github.com/triarius/goreleaser@v1.12.5/internal/pipe/archive/archive_test.go (about)

     1  package archive
     2  
     3  import (
     4  	"archive/tar"
     5  	"archive/zip"
     6  	"compress/gzip"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/triarius/goreleaser/internal/artifact"
    14  	"github.com/triarius/goreleaser/internal/testlib"
    15  	"github.com/triarius/goreleaser/pkg/archive"
    16  	"github.com/triarius/goreleaser/pkg/config"
    17  	"github.com/triarius/goreleaser/pkg/context"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestDescription(t *testing.T) {
    22  	require.NotEmpty(t, Pipe{}.String())
    23  }
    24  
    25  func createFakeBinary(t *testing.T, dist, arch, bin string) {
    26  	t.Helper()
    27  	path := filepath.Join(dist, arch, bin)
    28  	require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
    29  	f, err := os.Create(path)
    30  	require.NoError(t, err)
    31  	require.NoError(t, f.Close())
    32  }
    33  
    34  func TestRunPipe(t *testing.T) {
    35  	folder := testlib.Mktmp(t)
    36  	for _, dets := range []struct {
    37  		Format string
    38  		Strip  bool
    39  	}{
    40  		{
    41  			Format: "tar.gz",
    42  			Strip:  true,
    43  		},
    44  		{
    45  			Format: "tar.gz",
    46  			Strip:  false,
    47  		},
    48  
    49  		{
    50  			Format: "zip",
    51  			Strip:  true,
    52  		},
    53  		{
    54  			Format: "zip",
    55  			Strip:  false,
    56  		},
    57  	} {
    58  		format := dets.Format
    59  		name := "archive." + format
    60  		if dets.Strip {
    61  			name = "strip_" + name
    62  		}
    63  		t.Run(name, func(t *testing.T) {
    64  			dist := filepath.Join(folder, name+"_dist")
    65  			require.NoError(t, os.Mkdir(dist, 0o755))
    66  			for _, arch := range []string{"darwinamd64v1", "darwinall", "linux386", "linuxarm7", "linuxmipssoftfloat", "linuxamd64v3"} {
    67  				createFakeBinary(t, dist, arch, "bin/mybin")
    68  			}
    69  			createFakeBinary(t, dist, "windowsamd64", "bin/mybin.exe")
    70  			for _, tt := range []string{"darwin", "linux", "windows"} {
    71  				f, err := os.Create(filepath.Join(folder, fmt.Sprintf("README.%s.md", tt)))
    72  				require.NoError(t, err)
    73  				require.NoError(t, f.Close())
    74  			}
    75  			require.NoError(t, os.MkdirAll(filepath.Join(folder, "foo", "bar", "foobar"), 0o755))
    76  			f, err := os.Create(filepath.Join(filepath.Join(folder, "foo", "bar", "foobar", "blah.txt")))
    77  			require.NoError(t, err)
    78  			require.NoError(t, f.Close())
    79  			ctx := context.New(
    80  				config.Project{
    81  					Dist:        dist,
    82  					ProjectName: "foobar",
    83  					Archives: []config.Archive{
    84  						{
    85  							ID:                      "myid",
    86  							Builds:                  []string{"default"},
    87  							NameTemplate:            defaultNameTemplate,
    88  							StripParentBinaryFolder: dets.Strip,
    89  							Files: []config.File{
    90  								{Source: "README.{{.Os}}.*"},
    91  								{Source: "./foo/**/*"},
    92  							},
    93  							FormatOverrides: []config.FormatOverride{
    94  								{
    95  									Goos:   "windows",
    96  									Format: "zip",
    97  								},
    98  							},
    99  						},
   100  					},
   101  				},
   102  			)
   103  			darwinUniversalBinary := &artifact.Artifact{
   104  				Goos:   "darwin",
   105  				Goarch: "all",
   106  				Name:   "bin/mybin",
   107  				Path:   filepath.Join(dist, "darwinall", "bin", "mybin"),
   108  				Type:   artifact.UniversalBinary,
   109  				Extra: map[string]interface{}{
   110  					artifact.ExtraBinary:   "bin/mybin",
   111  					artifact.ExtraID:       "default",
   112  					artifact.ExtraReplaces: true,
   113  				},
   114  			}
   115  			darwinBuild := &artifact.Artifact{
   116  				Goos:    "darwin",
   117  				Goarch:  "amd64",
   118  				Goamd64: "v1",
   119  				Name:    "bin/mybin",
   120  				Path:    filepath.Join(dist, "darwinamd64v1", "bin", "mybin"),
   121  				Type:    artifact.Binary,
   122  				Extra: map[string]interface{}{
   123  					artifact.ExtraBinary: "bin/mybin",
   124  					artifact.ExtraID:     "default",
   125  				},
   126  			}
   127  			linux386Build := &artifact.Artifact{
   128  				Goos:   "linux",
   129  				Goarch: "386",
   130  				Name:   "bin/mybin",
   131  				Path:   filepath.Join(dist, "linux386", "bin", "mybin"),
   132  				Type:   artifact.Binary,
   133  				Extra: map[string]interface{}{
   134  					artifact.ExtraBinary: "bin/mybin",
   135  					artifact.ExtraID:     "default",
   136  				},
   137  			}
   138  			linuxArmBuild := &artifact.Artifact{
   139  				Goos:   "linux",
   140  				Goarch: "arm",
   141  				Goarm:  "7",
   142  				Name:   "bin/mybin",
   143  				Path:   filepath.Join(dist, "linuxarm7", "bin", "mybin"),
   144  				Type:   artifact.Binary,
   145  				Extra: map[string]interface{}{
   146  					artifact.ExtraBinary: "bin/mybin",
   147  					artifact.ExtraID:     "default",
   148  				},
   149  			}
   150  			linuxMipsBuild := &artifact.Artifact{
   151  				Goos:   "linux",
   152  				Goarch: "mips",
   153  				Gomips: "softfloat",
   154  				Name:   "bin/mybin",
   155  				Path:   filepath.Join(dist, "linuxmipssoftfloat", "bin", "mybin"),
   156  				Type:   artifact.Binary,
   157  				Extra: map[string]interface{}{
   158  					artifact.ExtraBinary: "mybin",
   159  					artifact.ExtraID:     "default",
   160  				},
   161  			}
   162  			windowsBuild := &artifact.Artifact{
   163  				Goos:    "windows",
   164  				Goarch:  "amd64",
   165  				Goamd64: "v1",
   166  				Name:    "bin/mybin.exe",
   167  				Path:    filepath.Join(dist, "windowsamd64", "bin", "mybin.exe"),
   168  				Type:    artifact.Binary,
   169  				Extra: map[string]interface{}{
   170  					artifact.ExtraBinary: "mybin",
   171  					artifact.ExtraExt:    ".exe",
   172  					artifact.ExtraID:     "default",
   173  				},
   174  			}
   175  			linuxAmd64Build := &artifact.Artifact{
   176  				Goos:    "linux",
   177  				Goarch:  "amd64",
   178  				Goamd64: "v3",
   179  				Name:    "bin/mybin",
   180  				Path:    filepath.Join(dist, "linuxamd64v3", "bin", "mybin"),
   181  				Type:    artifact.Binary,
   182  				Extra: map[string]interface{}{
   183  					artifact.ExtraBinary: "mybin",
   184  					artifact.ExtraID:     "default",
   185  				},
   186  			}
   187  			ctx.Artifacts.Add(darwinBuild)
   188  			ctx.Artifacts.Add(darwinUniversalBinary)
   189  			ctx.Artifacts.Add(linux386Build)
   190  			ctx.Artifacts.Add(linuxArmBuild)
   191  			ctx.Artifacts.Add(linuxMipsBuild)
   192  			ctx.Artifacts.Add(windowsBuild)
   193  			ctx.Artifacts.Add(linuxAmd64Build)
   194  			ctx.Version = "0.0.1"
   195  			ctx.Git.CurrentTag = "v0.0.1"
   196  			ctx.Config.Archives[0].Format = format
   197  			require.NoError(t, Pipe{}.Run(ctx))
   198  			archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)).List()
   199  
   200  			for _, arch := range archives {
   201  				expectBin := "bin/mybin"
   202  				if arch.Goos == "windows" {
   203  					expectBin += ".exe"
   204  				}
   205  				require.Equal(t, "myid", arch.ID(), "all archives must have the archive ID set")
   206  				require.Equal(t, []string{expectBin}, artifact.ExtraOr(*arch, artifact.ExtraBinaries, []string{}))
   207  				require.Equal(t, "", artifact.ExtraOr(*arch, artifact.ExtraBinary, ""))
   208  			}
   209  			require.Len(t, archives, 7)
   210  			// TODO: should verify the artifact fields here too
   211  
   212  			expectBin := "bin/mybin"
   213  			if dets.Strip {
   214  				expectBin = "mybin"
   215  			}
   216  
   217  			if format == "tar.gz" {
   218  				// Check archive contents
   219  				for name, os := range map[string]string{
   220  					"foobar_0.0.1_darwin_amd64.tar.gz":         "darwin",
   221  					"foobar_0.0.1_darwin_all.tar.gz":           "darwin",
   222  					"foobar_0.0.1_linux_386.tar.gz":            "linux",
   223  					"foobar_0.0.1_linux_armv7.tar.gz":          "linux",
   224  					"foobar_0.0.1_linux_mips_softfloat.tar.gz": "linux",
   225  					"foobar_0.0.1_linux_amd64v3.tar.gz":        "linux",
   226  				} {
   227  					require.Equal(
   228  						t,
   229  						[]string{
   230  							fmt.Sprintf("README.%s.md", os),
   231  							"foo/bar/foobar/blah.txt",
   232  							expectBin,
   233  						},
   234  						tarFiles(t, filepath.Join(dist, name)),
   235  					)
   236  				}
   237  			}
   238  			if format == "zip" {
   239  				require.Equal(
   240  					t,
   241  					[]string{
   242  						"README.windows.md",
   243  						"foo/bar/foobar/blah.txt",
   244  						expectBin + ".exe",
   245  					},
   246  					zipFiles(t, filepath.Join(dist, "foobar_0.0.1_windows_amd64.zip")),
   247  				)
   248  			}
   249  		})
   250  	}
   251  }
   252  
   253  func TestRunPipeDifferentBinaryCount(t *testing.T) {
   254  	folder := testlib.Mktmp(t)
   255  	dist := filepath.Join(folder, "dist")
   256  	require.NoError(t, os.Mkdir(dist, 0o755))
   257  	for _, arch := range []string{"darwinamd64", "linuxamd64"} {
   258  		createFakeBinary(t, dist, arch, "bin/mybin")
   259  	}
   260  	createFakeBinary(t, dist, "darwinamd64", "bin/foobar")
   261  	ctx := context.New(config.Project{
   262  		Dist:        dist,
   263  		ProjectName: "foobar",
   264  		Archives: []config.Archive{
   265  			{
   266  				ID:           "myid",
   267  				Format:       "tar.gz",
   268  				Builds:       []string{"default", "foobar"},
   269  				NameTemplate: defaultNameTemplate,
   270  			},
   271  		},
   272  	})
   273  	darwinBuild := &artifact.Artifact{
   274  		Goos:   "darwin",
   275  		Goarch: "amd64",
   276  		Name:   "bin/mybin",
   277  		Path:   filepath.Join(dist, "darwinamd64", "bin", "mybin"),
   278  		Type:   artifact.Binary,
   279  		Extra: map[string]interface{}{
   280  			artifact.ExtraBinary: "bin/mybin",
   281  			artifact.ExtraID:     "default",
   282  		},
   283  	}
   284  	darwinBuild2 := &artifact.Artifact{
   285  		Goos:   "darwin",
   286  		Goarch: "amd64",
   287  		Name:   "bin/foobar",
   288  		Path:   filepath.Join(dist, "darwinamd64", "bin", "foobar"),
   289  		Type:   artifact.Binary,
   290  		Extra: map[string]interface{}{
   291  			artifact.ExtraBinary: "bin/foobar",
   292  			artifact.ExtraID:     "foobar",
   293  		},
   294  	}
   295  	linuxArmBuild := &artifact.Artifact{
   296  		Goos:   "linux",
   297  		Goarch: "amd64",
   298  		Name:   "bin/mybin",
   299  		Path:   filepath.Join(dist, "linuxamd64", "bin", "mybin"),
   300  		Type:   artifact.Binary,
   301  		Extra: map[string]interface{}{
   302  			artifact.ExtraBinary: "bin/mybin",
   303  			artifact.ExtraID:     "default",
   304  		},
   305  	}
   306  
   307  	ctx.Artifacts.Add(darwinBuild)
   308  	ctx.Artifacts.Add(darwinBuild2)
   309  	ctx.Artifacts.Add(linuxArmBuild)
   310  	ctx.Version = "0.0.1"
   311  	ctx.Git.CurrentTag = "v0.0.1"
   312  
   313  	t.Run("check enabled", func(t *testing.T) {
   314  		ctx.Config.Archives[0].AllowDifferentBinaryCount = false
   315  		require.EqualError(t, Pipe{}.Run(ctx), "invalid archive: 0: "+ErrArchiveDifferentBinaryCount.Error())
   316  	})
   317  
   318  	t.Run("check disabled", func(t *testing.T) {
   319  		ctx.Config.Archives[0].AllowDifferentBinaryCount = true
   320  		require.NoError(t, Pipe{}.Run(ctx))
   321  	})
   322  }
   323  
   324  func TestRunPipeNoBinaries(t *testing.T) {
   325  	folder := testlib.Mktmp(t)
   326  	dist := filepath.Join(folder, "dist")
   327  	require.NoError(t, os.Mkdir(dist, 0o755))
   328  	ctx := context.New(config.Project{
   329  		Dist:        dist,
   330  		ProjectName: "foobar",
   331  		Archives: []config.Archive{{
   332  			Builds: []string{"not-default"},
   333  		}},
   334  	})
   335  	ctx.Version = "0.0.1"
   336  	ctx.Git.CurrentTag = "v0.0.1"
   337  	ctx.Artifacts.Add(&artifact.Artifact{
   338  		Goos:   "linux",
   339  		Goarch: "amd64",
   340  		Name:   "bin/mybin",
   341  		Path:   filepath.Join(dist, "linuxamd64", "bin", "mybin"),
   342  		Type:   artifact.Binary,
   343  		Extra: map[string]interface{}{
   344  			artifact.ExtraBinary: "bin/mybin",
   345  			artifact.ExtraID:     "default",
   346  		},
   347  	})
   348  	require.NoError(t, Pipe{}.Run(ctx))
   349  }
   350  
   351  func zipFiles(t *testing.T, path string) []string {
   352  	t.Helper()
   353  	f, err := os.Open(path)
   354  	require.NoError(t, err)
   355  	info, err := f.Stat()
   356  	require.NoError(t, err)
   357  	r, err := zip.NewReader(f, info.Size())
   358  	require.NoError(t, err)
   359  	paths := make([]string, len(r.File))
   360  	for i, zf := range r.File {
   361  		paths[i] = zf.Name
   362  	}
   363  	return paths
   364  }
   365  
   366  func tarFiles(t *testing.T, path string) []string {
   367  	t.Helper()
   368  	f, err := os.Open(path)
   369  	require.NoError(t, err)
   370  	defer f.Close()
   371  	gr, err := gzip.NewReader(f)
   372  	require.NoError(t, err)
   373  	defer gr.Close()
   374  	r := tar.NewReader(gr)
   375  	var paths []string
   376  	for {
   377  		next, err := r.Next()
   378  		if err == io.EOF {
   379  			break
   380  		}
   381  		require.NoError(t, err)
   382  		paths = append(paths, next.Name)
   383  	}
   384  	return paths
   385  }
   386  
   387  func TestRunPipeBinary(t *testing.T) {
   388  	folder := testlib.Mktmp(t)
   389  	dist := filepath.Join(folder, "dist")
   390  	require.NoError(t, os.Mkdir(dist, 0o755))
   391  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   392  	require.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0o755))
   393  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   394  	require.NoError(t, err)
   395  	require.NoError(t, f.Close())
   396  	f, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe"))
   397  	require.NoError(t, err)
   398  	require.NoError(t, f.Close())
   399  	f, err = os.Create(filepath.Join(dist, "windowsamd64", "myotherbin"))
   400  	require.NoError(t, err)
   401  	require.NoError(t, f.Close())
   402  	f, err = os.Create(filepath.Join(folder, "README.md"))
   403  	require.NoError(t, err)
   404  	require.NoError(t, f.Close())
   405  	ctx := context.New(
   406  		config.Project{
   407  			Dist: dist,
   408  			Archives: []config.Archive{
   409  				{
   410  					Format:       "binary",
   411  					NameTemplate: defaultBinaryNameTemplate,
   412  					Builds:       []string{"default", "default2"},
   413  				},
   414  			},
   415  		},
   416  	)
   417  	ctx.Version = "0.0.1"
   418  	ctx.Git.CurrentTag = "v0.0.1"
   419  	ctx.Artifacts.Add(&artifact.Artifact{
   420  		Goos:   "darwin",
   421  		Goarch: "amd64",
   422  		Name:   "mybin",
   423  		Path:   filepath.Join(dist, "darwinamd64", "mybin"),
   424  		Type:   artifact.Binary,
   425  		Extra: map[string]interface{}{
   426  			artifact.ExtraBinary: "mybin",
   427  			artifact.ExtraID:     "default",
   428  		},
   429  	})
   430  	ctx.Artifacts.Add(&artifact.Artifact{
   431  		Goos:   "darwin",
   432  		Goarch: "all",
   433  		Name:   "myunibin",
   434  		Path:   filepath.Join(dist, "darwinamd64", "mybin"),
   435  		Type:   artifact.UniversalBinary,
   436  		Extra: map[string]interface{}{
   437  			artifact.ExtraBinary:   "myunibin",
   438  			artifact.ExtraID:       "default",
   439  			artifact.ExtraReplaces: true,
   440  		},
   441  	})
   442  	ctx.Artifacts.Add(&artifact.Artifact{
   443  		Goos:   "windows",
   444  		Goarch: "amd64",
   445  		Name:   "mybin.exe",
   446  		Path:   filepath.Join(dist, "windowsamd64", "mybin.exe"),
   447  		Type:   artifact.Binary,
   448  		Extra: map[string]interface{}{
   449  			artifact.ExtraBinary: "mybin",
   450  			artifact.ExtraExt:    ".exe",
   451  			artifact.ExtraID:     "default",
   452  		},
   453  	})
   454  	ctx.Artifacts.Add(&artifact.Artifact{
   455  		Goos:   "windows",
   456  		Goarch: "amd64",
   457  		Name:   "myotherbin.exe",
   458  		Path:   filepath.Join(dist, "windowsamd64", "myotherbin.exe"),
   459  		Type:   artifact.Binary,
   460  		Extra: map[string]interface{}{
   461  			artifact.ExtraBinary: "myotherbin",
   462  			artifact.ExtraExt:    ".exe",
   463  			artifact.ExtraID:     "default2",
   464  		},
   465  	})
   466  
   467  	require.NoError(t, Pipe{}.Run(ctx))
   468  	binaries := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary))
   469  	require.Len(t, binaries.List(), 4)
   470  	darwinThin := binaries.Filter(artifact.And(
   471  		artifact.ByGoos("darwin"),
   472  		artifact.ByGoarch("amd64"),
   473  	)).List()[0]
   474  	darwinUniversal := binaries.Filter(artifact.And(
   475  		artifact.ByGoos("darwin"),
   476  		artifact.ByGoarch("all"),
   477  	)).List()[0]
   478  	require.True(t, artifact.ExtraOr(*darwinUniversal, artifact.ExtraReplaces, false))
   479  	windows := binaries.Filter(artifact.ByGoos("windows")).List()[0]
   480  	windows2 := binaries.Filter(artifact.ByGoos("windows")).List()[1]
   481  	require.Equal(t, "mybin_0.0.1_darwin_amd64", darwinThin.Name)
   482  	require.Equal(t, "mybin", artifact.ExtraOr(*darwinThin, artifact.ExtraBinary, ""))
   483  	require.Equal(t, "myunibin_0.0.1_darwin_all", darwinUniversal.Name)
   484  	require.Equal(t, "myunibin", artifact.ExtraOr(*darwinUniversal, artifact.ExtraBinary, ""))
   485  	require.Equal(t, "mybin_0.0.1_windows_amd64.exe", windows.Name)
   486  	require.Equal(t, "mybin.exe", artifact.ExtraOr(*windows, artifact.ExtraBinary, ""))
   487  	require.Equal(t, "myotherbin_0.0.1_windows_amd64.exe", windows2.Name)
   488  	require.Equal(t, "myotherbin.exe", artifact.ExtraOr(*windows2, artifact.ExtraBinary, ""))
   489  }
   490  
   491  func TestRunPipeDistRemoved(t *testing.T) {
   492  	ctx := context.New(
   493  		config.Project{
   494  			Dist: "/tmp/path/to/nope",
   495  			Archives: []config.Archive{
   496  				{
   497  					NameTemplate: "nope",
   498  					Format:       "zip",
   499  					Builds:       []string{"default"},
   500  				},
   501  			},
   502  		},
   503  	)
   504  	ctx.Git.CurrentTag = "v0.0.1"
   505  	ctx.Artifacts.Add(&artifact.Artifact{
   506  		Goos:   "windows",
   507  		Goarch: "amd64",
   508  		Name:   "mybin.exe",
   509  		Path:   filepath.Join("/tmp/path/to/nope", "windowsamd64", "mybin.exe"),
   510  		Type:   artifact.Binary,
   511  		Extra: map[string]interface{}{
   512  			artifact.ExtraBinary: "mybin",
   513  			artifact.ExtraExt:    ".exe",
   514  			artifact.ExtraID:     "default",
   515  		},
   516  	})
   517  	// not checking on error msg because it may change depending on OS/version
   518  	require.Error(t, Pipe{}.Run(ctx))
   519  }
   520  
   521  func TestRunPipeInvalidGlob(t *testing.T) {
   522  	folder := testlib.Mktmp(t)
   523  	dist := filepath.Join(folder, "dist")
   524  	require.NoError(t, os.Mkdir(dist, 0o755))
   525  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   526  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   527  	require.NoError(t, err)
   528  	require.NoError(t, f.Close())
   529  	ctx := context.New(
   530  		config.Project{
   531  			Dist: dist,
   532  			Archives: []config.Archive{
   533  				{
   534  					Builds:       []string{"default"},
   535  					NameTemplate: "foo",
   536  					Format:       "zip",
   537  					Files: []config.File{
   538  						{Source: "[x-]"},
   539  					},
   540  				},
   541  			},
   542  		},
   543  	)
   544  	ctx.Git.CurrentTag = "v0.0.1"
   545  	ctx.Artifacts.Add(&artifact.Artifact{
   546  		Goos:   "darwin",
   547  		Goarch: "amd64",
   548  		Name:   "mybin",
   549  		Path:   filepath.Join("dist", "darwinamd64", "mybin"),
   550  		Type:   artifact.Binary,
   551  		Extra: map[string]interface{}{
   552  			artifact.ExtraBinary: "mybin",
   553  			artifact.ExtraID:     "default",
   554  		},
   555  	})
   556  	require.EqualError(t, Pipe{}.Run(ctx), `failed to find files to archive: globbing failed for pattern [x-]: compile glob pattern: unexpected end of input`)
   557  }
   558  
   559  func TestRunPipeInvalidNameTemplate(t *testing.T) {
   560  	folder := testlib.Mktmp(t)
   561  	dist := filepath.Join(folder, "dist")
   562  	require.NoError(t, os.Mkdir(dist, 0o755))
   563  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   564  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   565  	require.NoError(t, err)
   566  	require.NoError(t, f.Close())
   567  	ctx := context.New(
   568  		config.Project{
   569  			Dist: dist,
   570  			Archives: []config.Archive{
   571  				{
   572  					Builds:       []string{"default"},
   573  					NameTemplate: "foo{{ .fff }",
   574  					Format:       "zip",
   575  				},
   576  			},
   577  		},
   578  	)
   579  	ctx.Git.CurrentTag = "v0.0.1"
   580  	ctx.Artifacts.Add(&artifact.Artifact{
   581  		Goos:   "darwin",
   582  		Goarch: "amd64",
   583  		Name:   "mybin",
   584  		Path:   filepath.Join("dist", "darwinamd64", "mybin"),
   585  		Type:   artifact.Binary,
   586  		Extra: map[string]interface{}{
   587  			artifact.ExtraBinary: "mybin",
   588  			artifact.ExtraID:     "default",
   589  		},
   590  	})
   591  	testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
   592  }
   593  
   594  func TestRunPipeInvalidFilesNameTemplate(t *testing.T) {
   595  	folder := testlib.Mktmp(t)
   596  	dist := filepath.Join(folder, "dist")
   597  	require.NoError(t, os.Mkdir(dist, 0o755))
   598  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   599  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   600  	require.NoError(t, err)
   601  	require.NoError(t, f.Close())
   602  	ctx := context.New(
   603  		config.Project{
   604  			Dist: dist,
   605  			Archives: []config.Archive{
   606  				{
   607  					Builds:       []string{"default"},
   608  					NameTemplate: "foo",
   609  					Format:       "zip",
   610  					Files: []config.File{
   611  						{Source: "{{.asdsd}"},
   612  					},
   613  				},
   614  			},
   615  		},
   616  	)
   617  	ctx.Git.CurrentTag = "v0.0.1"
   618  	ctx.Artifacts.Add(&artifact.Artifact{
   619  		Goos:   "darwin",
   620  		Goarch: "amd64",
   621  		Name:   "mybin",
   622  		Path:   filepath.Join("dist", "darwinamd64", "mybin"),
   623  		Type:   artifact.Binary,
   624  		Extra: map[string]interface{}{
   625  			artifact.ExtraBinary: "mybin",
   626  			artifact.ExtraID:     "default",
   627  		},
   628  	})
   629  	testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
   630  }
   631  
   632  func TestRunPipeInvalidWrapInDirectoryTemplate(t *testing.T) {
   633  	folder := testlib.Mktmp(t)
   634  	dist := filepath.Join(folder, "dist")
   635  	require.NoError(t, os.Mkdir(dist, 0o755))
   636  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   637  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   638  	require.NoError(t, err)
   639  	require.NoError(t, f.Close())
   640  	ctx := context.New(
   641  		config.Project{
   642  			Dist: dist,
   643  			Archives: []config.Archive{
   644  				{
   645  					Builds:          []string{"default"},
   646  					NameTemplate:    "foo",
   647  					WrapInDirectory: "foo{{ .fff }",
   648  					Format:          "zip",
   649  				},
   650  			},
   651  		},
   652  	)
   653  	ctx.Git.CurrentTag = "v0.0.1"
   654  	ctx.Artifacts.Add(&artifact.Artifact{
   655  		Goos:   "darwin",
   656  		Goarch: "amd64",
   657  		Name:   "mybin",
   658  		Path:   filepath.Join("dist", "darwinamd64", "mybin"),
   659  		Type:   artifact.Binary,
   660  		Extra: map[string]interface{}{
   661  			artifact.ExtraBinary: "mybin",
   662  			artifact.ExtraID:     "default",
   663  		},
   664  	})
   665  	require.EqualError(t, Pipe{}.Run(ctx), `template: tmpl:1: unexpected "}" in operand`)
   666  }
   667  
   668  func TestRunPipeWrap(t *testing.T) {
   669  	folder := testlib.Mktmp(t)
   670  	dist := filepath.Join(folder, "dist")
   671  	require.NoError(t, os.Mkdir(dist, 0o755))
   672  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   673  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   674  	require.NoError(t, err)
   675  	require.NoError(t, f.Close())
   676  	f, err = os.Create(filepath.Join(folder, "README.md"))
   677  	require.NoError(t, err)
   678  	require.NoError(t, f.Close())
   679  	ctx := context.New(
   680  		config.Project{
   681  			Dist: dist,
   682  			Archives: []config.Archive{
   683  				{
   684  					Builds:          []string{"default"},
   685  					NameTemplate:    "foo",
   686  					WrapInDirectory: "foo_{{ .Os }}",
   687  					Format:          "tar.gz",
   688  					Replacements: map[string]string{
   689  						"darwin": "macOS",
   690  					},
   691  					Files: []config.File{
   692  						{Source: "README.*"},
   693  					},
   694  				},
   695  			},
   696  		},
   697  	)
   698  	ctx.Git.CurrentTag = "v0.0.1"
   699  	ctx.Artifacts.Add(&artifact.Artifact{
   700  		Goos:   "darwin",
   701  		Goarch: "amd64",
   702  		Name:   "mybin",
   703  		Path:   filepath.Join("dist", "darwinamd64", "mybin"),
   704  		Type:   artifact.Binary,
   705  		Extra: map[string]interface{}{
   706  			artifact.ExtraBinary: "mybin",
   707  			artifact.ExtraID:     "default",
   708  		},
   709  	})
   710  	require.NoError(t, Pipe{}.Run(ctx))
   711  
   712  	archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)).List()
   713  	require.Len(t, archives, 1)
   714  	require.Equal(t, "foo_macOS", artifact.ExtraOr(*archives[0], artifact.ExtraWrappedIn, ""))
   715  
   716  	// Check archive contents
   717  	f, err = os.Open(filepath.Join(dist, "foo.tar.gz"))
   718  	require.NoError(t, err)
   719  	defer func() { require.NoError(t, f.Close()) }()
   720  	gr, err := gzip.NewReader(f)
   721  	require.NoError(t, err)
   722  	defer func() { require.NoError(t, gr.Close()) }()
   723  	r := tar.NewReader(gr)
   724  	for _, n := range []string{"README.md", "mybin"} {
   725  		h, err := r.Next()
   726  		if err == io.EOF {
   727  			break
   728  		}
   729  		require.NoError(t, err)
   730  		require.Equal(t, filepath.Join("foo_macOS", n), h.Name)
   731  	}
   732  }
   733  
   734  func TestDefault(t *testing.T) {
   735  	ctx := &context.Context{
   736  		Config: config.Project{
   737  			Archives: []config.Archive{},
   738  		},
   739  	}
   740  	require.NoError(t, Pipe{}.Default(ctx))
   741  	require.NotEmpty(t, ctx.Config.Archives[0].NameTemplate)
   742  	require.Equal(t, "tar.gz", ctx.Config.Archives[0].Format)
   743  	require.NotEmpty(t, ctx.Config.Archives[0].Files)
   744  }
   745  
   746  func TestDefaultSet(t *testing.T) {
   747  	ctx := &context.Context{
   748  		Config: config.Project{
   749  			Archives: []config.Archive{
   750  				{
   751  					Builds:       []string{"default"},
   752  					NameTemplate: "foo",
   753  					Format:       "zip",
   754  					Files: []config.File{
   755  						{Source: "foo"},
   756  					},
   757  				},
   758  			},
   759  		},
   760  	}
   761  	require.NoError(t, Pipe{}.Default(ctx))
   762  	require.Equal(t, "foo", ctx.Config.Archives[0].NameTemplate)
   763  	require.Equal(t, "zip", ctx.Config.Archives[0].Format)
   764  	require.Equal(t, config.File{Source: "foo"}, ctx.Config.Archives[0].Files[0])
   765  }
   766  
   767  func TestDefaultFormatBinary(t *testing.T) {
   768  	ctx := &context.Context{
   769  		Config: config.Project{
   770  			Archives: []config.Archive{
   771  				{
   772  					Format: "binary",
   773  				},
   774  			},
   775  		},
   776  	}
   777  	require.NoError(t, Pipe{}.Default(ctx))
   778  	require.Equal(t, defaultBinaryNameTemplate, ctx.Config.Archives[0].NameTemplate)
   779  }
   780  
   781  func TestFormatFor(t *testing.T) {
   782  	ctx := &context.Context{
   783  		Config: config.Project{
   784  			Archives: []config.Archive{
   785  				{
   786  					Builds: []string{"default"},
   787  					Format: "tar.gz",
   788  					FormatOverrides: []config.FormatOverride{
   789  						{
   790  							Goos:   "windows",
   791  							Format: "zip",
   792  						},
   793  					},
   794  				},
   795  			},
   796  		},
   797  	}
   798  	require.Equal(t, "zip", packageFormat(ctx.Config.Archives[0], "windows"))
   799  	require.Equal(t, "tar.gz", packageFormat(ctx.Config.Archives[0], "linux"))
   800  }
   801  
   802  func TestBinaryOverride(t *testing.T) {
   803  	folder := testlib.Mktmp(t)
   804  	dist := filepath.Join(folder, "dist")
   805  	require.NoError(t, os.Mkdir(dist, 0o755))
   806  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   807  	require.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0o755))
   808  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   809  	require.NoError(t, err)
   810  	require.NoError(t, f.Close())
   811  	f, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe"))
   812  	require.NoError(t, err)
   813  	require.NoError(t, f.Close())
   814  	f, err = os.Create(filepath.Join(folder, "README.md"))
   815  	require.NoError(t, err)
   816  	require.NoError(t, f.Close())
   817  	for _, format := range []string{"tar.gz", "zip"} {
   818  		t.Run("Archive format "+format, func(t *testing.T) {
   819  			ctx := context.New(
   820  				config.Project{
   821  					Dist:        dist,
   822  					ProjectName: "foobar",
   823  					Archives: []config.Archive{
   824  						{
   825  							Builds:       []string{"default"},
   826  							NameTemplate: defaultNameTemplate,
   827  							Files: []config.File{
   828  								{Source: "README.*"},
   829  							},
   830  							FormatOverrides: []config.FormatOverride{
   831  								{
   832  									Goos:   "windows",
   833  									Format: "binary",
   834  								},
   835  							},
   836  						},
   837  					},
   838  				},
   839  			)
   840  			ctx.Git.CurrentTag = "v0.0.1"
   841  			ctx.Artifacts.Add(&artifact.Artifact{
   842  				Goos:   "darwin",
   843  				Goarch: "amd64",
   844  				Name:   "mybin",
   845  				Path:   filepath.Join(dist, "darwinamd64", "mybin"),
   846  				Type:   artifact.Binary,
   847  				Extra: map[string]interface{}{
   848  					artifact.ExtraBinary: "mybin",
   849  					artifact.ExtraID:     "default",
   850  				},
   851  			})
   852  			ctx.Artifacts.Add(&artifact.Artifact{
   853  				Goos:   "windows",
   854  				Goarch: "amd64",
   855  				Name:   "mybin.exe",
   856  				Path:   filepath.Join(dist, "windowsamd64", "mybin.exe"),
   857  				Type:   artifact.Binary,
   858  				Extra: map[string]interface{}{
   859  					artifact.ExtraBinary: "mybin",
   860  					artifact.ExtraExt:    ".exe",
   861  					artifact.ExtraID:     "default",
   862  				},
   863  			})
   864  			ctx.Version = "0.0.1"
   865  			ctx.Config.Archives[0].Format = format
   866  
   867  			require.NoError(t, Pipe{}.Run(ctx))
   868  			archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive))
   869  			darwin := archives.Filter(artifact.ByGoos("darwin")).List()[0]
   870  			require.Equal(t, "foobar_0.0.1_darwin_amd64."+format, darwin.Name)
   871  			require.Equal(t, format, darwin.Format())
   872  			require.Empty(t, artifact.ExtraOr(*darwin, artifact.ExtraWrappedIn, ""))
   873  
   874  			archives = ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary))
   875  			windows := archives.Filter(artifact.ByGoos("windows")).List()[0]
   876  			require.Equal(t, "foobar_0.0.1_windows_amd64.exe", windows.Name)
   877  			require.Empty(t, artifact.ExtraOr(*windows, artifact.ExtraWrappedIn, ""))
   878  			require.Equal(t, "mybin.exe", artifact.ExtraOr(*windows, artifact.ExtraBinary, ""))
   879  		})
   880  	}
   881  }
   882  
   883  func TestRunPipeSameArchiveFilename(t *testing.T) {
   884  	folder := testlib.Mktmp(t)
   885  	dist := filepath.Join(folder, "dist")
   886  	require.NoError(t, os.Mkdir(dist, 0o755))
   887  	require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755))
   888  	require.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0o755))
   889  	f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin"))
   890  	require.NoError(t, err)
   891  	require.NoError(t, f.Close())
   892  	f, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe"))
   893  	require.NoError(t, err)
   894  	require.NoError(t, f.Close())
   895  	ctx := context.New(
   896  		config.Project{
   897  			Dist:        dist,
   898  			ProjectName: "foobar",
   899  			Archives: []config.Archive{
   900  				{
   901  					Builds:       []string{"default"},
   902  					NameTemplate: "same-filename",
   903  					Files: []config.File{
   904  						{Source: "README.*"},
   905  						{Source: "./foo/**/*"},
   906  					},
   907  					Format: "tar.gz",
   908  				},
   909  			},
   910  		},
   911  	)
   912  	ctx.Artifacts.Add(&artifact.Artifact{
   913  		Goos:   "darwin",
   914  		Goarch: "amd64",
   915  		Name:   "mybin",
   916  		Path:   filepath.Join(dist, "darwinamd64", "mybin"),
   917  		Type:   artifact.Binary,
   918  		Extra: map[string]interface{}{
   919  			artifact.ExtraBinary: "mybin",
   920  			artifact.ExtraID:     "default",
   921  		},
   922  	})
   923  	ctx.Artifacts.Add(&artifact.Artifact{
   924  		Goos:   "windows",
   925  		Goarch: "amd64",
   926  		Name:   "mybin.exe",
   927  		Path:   filepath.Join(dist, "windowsamd64", "mybin.exe"),
   928  		Type:   artifact.Binary,
   929  		Extra: map[string]interface{}{
   930  			artifact.ExtraBinary: "mybin",
   931  			artifact.ExtraExt:    ".exe",
   932  			artifact.ExtraID:     "default",
   933  		},
   934  	})
   935  	ctx.Version = "0.0.1"
   936  	ctx.Git.CurrentTag = "v0.0.1"
   937  	err = Pipe{}.Run(ctx)
   938  	require.Error(t, err)
   939  	require.Contains(t, err.Error(), "same-filename.tar.gz already exists. Check your archive name template")
   940  }
   941  
   942  func TestDuplicateFilesInsideArchive(t *testing.T) {
   943  	folder := t.TempDir()
   944  
   945  	f, err := os.CreateTemp(folder, "")
   946  	require.NoError(t, err)
   947  	t.Cleanup(func() {
   948  		require.NoError(t, f.Close())
   949  	})
   950  
   951  	ff, err := os.CreateTemp(folder, "")
   952  	require.NoError(t, err)
   953  	require.NoError(t, ff.Close())
   954  	a, err := archive.New(f, "tar.gz")
   955  	require.NoError(t, err)
   956  	a = NewEnhancedArchive(a, "")
   957  	t.Cleanup(func() {
   958  		require.NoError(t, a.Close())
   959  	})
   960  
   961  	require.NoError(t, a.Add(config.File{
   962  		Source:      ff.Name(),
   963  		Destination: "foo",
   964  	}))
   965  	require.EqualError(t, a.Add(config.File{
   966  		Source:      ff.Name(),
   967  		Destination: "foo",
   968  	}), "file foo already exists in the archive")
   969  }
   970  
   971  func TestWrapInDirectory(t *testing.T) {
   972  	t.Run("false", func(t *testing.T) {
   973  		require.Equal(t, "", wrapFolder(config.Archive{
   974  			WrapInDirectory: "false",
   975  		}))
   976  	})
   977  	t.Run("true", func(t *testing.T) {
   978  		require.Equal(t, "foo", wrapFolder(config.Archive{
   979  			WrapInDirectory: "true",
   980  			NameTemplate:    "foo",
   981  		}))
   982  	})
   983  	t.Run("custom", func(t *testing.T) {
   984  		require.Equal(t, "foobar", wrapFolder(config.Archive{
   985  			WrapInDirectory: "foobar",
   986  		}))
   987  	})
   988  }
   989  
   990  func TestSeveralArchivesWithTheSameID(t *testing.T) {
   991  	ctx := &context.Context{
   992  		Config: config.Project{
   993  			Archives: []config.Archive{
   994  				{
   995  					ID: "a",
   996  				},
   997  				{
   998  					ID: "a",
   999  				},
  1000  			},
  1001  		},
  1002  	}
  1003  	require.EqualError(t, Pipe{}.Default(ctx), "found 2 archives with the ID 'a', please fix your config")
  1004  }
  1005  
  1006  func TestArchive_globbing(t *testing.T) {
  1007  	assertGlob := func(t *testing.T, files []config.File, expected []string) {
  1008  		t.Helper()
  1009  		bin, err := os.CreateTemp(t.TempDir(), "binary")
  1010  		require.NoError(t, err)
  1011  		dist := t.TempDir()
  1012  		ctx := context.New(config.Project{
  1013  			Dist: dist,
  1014  			Archives: []config.Archive{
  1015  				{
  1016  					Builds:       []string{"default"},
  1017  					Format:       "tar.gz",
  1018  					NameTemplate: "foo",
  1019  					Files:        files,
  1020  				},
  1021  			},
  1022  		})
  1023  
  1024  		ctx.Artifacts.Add(&artifact.Artifact{
  1025  			Goos:   "darwin",
  1026  			Goarch: "amd64",
  1027  			Name:   "foobin",
  1028  			Path:   bin.Name(),
  1029  			Type:   artifact.Binary,
  1030  			Extra: map[string]interface{}{
  1031  				artifact.ExtraID: "default",
  1032  			},
  1033  		})
  1034  
  1035  		require.NoError(t, Pipe{}.Run(ctx))
  1036  		require.Equal(t, append(expected, "foobin"), tarFiles(t, filepath.Join(dist, "foo.tar.gz")))
  1037  	}
  1038  
  1039  	t.Run("exact src file", func(t *testing.T) {
  1040  		assertGlob(t, []config.File{{Source: "testdata/a/a.txt"}}, []string{"testdata/a/a.txt"})
  1041  	})
  1042  
  1043  	t.Run("exact src file with dst", func(t *testing.T) {
  1044  		assertGlob(t, []config.File{
  1045  			{
  1046  				Source:      "testdata/a/a.txt",
  1047  				Destination: "foo/",
  1048  			},
  1049  		}, []string{"foo/testdata/a/a.txt"})
  1050  	})
  1051  
  1052  	t.Run("glob src", func(t *testing.T) {
  1053  		assertGlob(t, []config.File{
  1054  			{Source: "testdata/**/*.txt"},
  1055  		}, []string{
  1056  			"testdata/a/a.txt",
  1057  			"testdata/a/b/a.txt",
  1058  			"testdata/a/b/c/d.txt",
  1059  		})
  1060  	})
  1061  
  1062  	t.Run("glob src with dst", func(t *testing.T) {
  1063  		assertGlob(t, []config.File{
  1064  			{
  1065  				Source:      "testdata/**/*.txt",
  1066  				Destination: "var/yada",
  1067  			},
  1068  		}, []string{
  1069  			"var/yada/testdata/a/a.txt",
  1070  			"var/yada/testdata/a/b/a.txt",
  1071  			"var/yada/testdata/a/b/c/d.txt",
  1072  		})
  1073  	})
  1074  
  1075  	t.Run("glob src with dst stripping parent", func(t *testing.T) {
  1076  		assertGlob(t, []config.File{
  1077  			{
  1078  				Source:      "testdata/**/*.txt",
  1079  				Destination: "var/yada",
  1080  				StripParent: true,
  1081  			},
  1082  		}, []string{
  1083  			"var/yada/a.txt",
  1084  			"var/yada/d.txt",
  1085  		})
  1086  	})
  1087  }
  1088  
  1089  func TestInvalidFormat(t *testing.T) {
  1090  	ctx := context.New(config.Project{
  1091  		Dist: t.TempDir(),
  1092  		Archives: []config.Archive{
  1093  			{
  1094  				ID:           "foo",
  1095  				NameTemplate: "foo",
  1096  				Meta:         true,
  1097  				Format:       "7z",
  1098  			},
  1099  		},
  1100  	})
  1101  	require.EqualError(t, Pipe{}.Run(ctx), "invalid archive format: 7z")
  1102  }