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