github.com/windmeup/goreleaser@v1.21.95/internal/pipe/archive/archive_test.go (about)

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