github.com/joselitofilho/goreleaser@v0.155.1-0.20210123221854-e4891856c593/internal/pipe/docker/docker_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"syscall"
    12  	"testing"
    13  
    14  	"github.com/goreleaser/goreleaser/internal/artifact"
    15  	"github.com/goreleaser/goreleaser/internal/pipe"
    16  	"github.com/goreleaser/goreleaser/internal/testlib"
    17  	"github.com/goreleaser/goreleaser/pkg/config"
    18  	"github.com/goreleaser/goreleaser/pkg/context"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  var it = flag.Bool("it", false, "push images to docker hub")
    23  var registry = "localhost:5000/"
    24  var altRegistry = "localhost:5050/"
    25  
    26  func TestMain(m *testing.M) {
    27  	flag.Parse()
    28  	if *it {
    29  		registry = "docker.io/"
    30  	}
    31  	os.Exit(m.Run())
    32  }
    33  
    34  func start(t *testing.T) {
    35  	t.Helper()
    36  	if *it {
    37  		return
    38  	}
    39  	if out, err := exec.Command(
    40  		"docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2",
    41  	).CombinedOutput(); err != nil {
    42  		t.Log("failed to start docker registry", string(out), err)
    43  		t.FailNow()
    44  	}
    45  	if out, err := exec.Command(
    46  		"docker", "run", "-d", "-p", "5050:5000", "--name", "alt_registry", "registry:2",
    47  	).CombinedOutput(); err != nil {
    48  		t.Log("failed to start alternate docker registry", string(out), err)
    49  		t.FailNow()
    50  	}
    51  }
    52  
    53  func killAndRm(t *testing.T) {
    54  	t.Helper()
    55  	if *it {
    56  		return
    57  	}
    58  	t.Log("killing registry")
    59  	_ = exec.Command("docker", "kill", "registry").Run()
    60  	_ = exec.Command("docker", "rm", "registry").Run()
    61  	_ = exec.Command("docker", "kill", "alt_registry").Run()
    62  	_ = exec.Command("docker", "rm", "alt_registry").Run()
    63  }
    64  
    65  // TODO: this test is too big... split in smaller tests? Mainly the manifest ones...
    66  func TestRunPipe(t *testing.T) {
    67  	type errChecker func(*testing.T, error)
    68  	var shouldErr = func(msg string) errChecker {
    69  		return func(t *testing.T, err error) {
    70  			t.Helper()
    71  			require.Error(t, err)
    72  			require.Contains(t, err.Error(), msg)
    73  		}
    74  	}
    75  	var shouldNotErr = func(t *testing.T, err error) {
    76  		t.Helper()
    77  		require.NoError(t, err)
    78  	}
    79  	type imageLabelFinder func(*testing.T, int)
    80  	var shouldFindImagesWithLabels = func(image string, filters ...string) func(*testing.T, int) {
    81  		return func(t *testing.T, count int) {
    82  			t.Helper()
    83  			for _, filter := range filters {
    84  				output, err := exec.Command(
    85  					"docker", "images", "-q", "*/"+image,
    86  					"--filter", filter,
    87  				).CombinedOutput()
    88  				require.NoError(t, err)
    89  				lines := strings.Split(strings.TrimSpace(string(output)), "\n")
    90  				require.Equal(t, count, len(lines))
    91  			}
    92  		}
    93  
    94  	}
    95  	var noLabels = func(t *testing.T, count int) {
    96  		t.Helper()
    97  	}
    98  
    99  	var table = map[string]struct {
   100  		dockers             []config.Docker
   101  		manifests           []config.DockerManifest
   102  		env                 map[string]string
   103  		expect              []string
   104  		assertImageLabels   imageLabelFinder
   105  		assertError         errChecker
   106  		pubAssertError      errChecker
   107  		manifestAssertError errChecker
   108  		extraPrepare        func(ctx *context.Context)
   109  	}{
   110  		"multiarch": {
   111  			dockers: []config.Docker{
   112  				{
   113  					ImageTemplates:     []string{registry + "goreleaser/test_multiarch:test-amd64"},
   114  					Goos:               "linux",
   115  					Goarch:             "amd64",
   116  					Dockerfile:         "testdata/Dockerfile.arch",
   117  					BuildFlagTemplates: []string{"--build-arg", "ARCH=amd64"},
   118  				},
   119  				{
   120  					ImageTemplates:     []string{registry + "goreleaser/test_multiarch:test-arm64v8"},
   121  					Goos:               "linux",
   122  					Goarch:             "arm64",
   123  					Dockerfile:         "testdata/Dockerfile.arch",
   124  					BuildFlagTemplates: []string{"--build-arg", "ARCH=arm64v8"},
   125  				},
   126  			},
   127  			manifests: []config.DockerManifest{
   128  				{
   129  					// XXX: fails if :latest https://github.com/docker/distribution/issues/3100
   130  					NameTemplate: registry + "goreleaser/test_multiarch:test",
   131  					ImageTemplates: []string{
   132  						registry + "goreleaser/test_multiarch:test-amd64",
   133  						registry + "goreleaser/test_multiarch:test-arm64v8",
   134  					},
   135  					CreateFlags: []string{"--insecure"},
   136  					PushFlags:   []string{"--insecure"},
   137  				},
   138  			},
   139  			expect: []string{
   140  				registry + "goreleaser/test_multiarch:test-amd64",
   141  				registry + "goreleaser/test_multiarch:test-arm64v8",
   142  			},
   143  			assertError:         shouldNotErr,
   144  			pubAssertError:      shouldNotErr,
   145  			manifestAssertError: shouldNotErr,
   146  			assertImageLabels:   noLabels,
   147  		},
   148  		"multiarch with buildx": {
   149  			dockers: []config.Docker{
   150  				{
   151  					ImageTemplates:     []string{registry + "goreleaser/test_multiarch_buildx:amd64"},
   152  					Goos:               "linux",
   153  					Goarch:             "amd64",
   154  					Dockerfile:         "testdata/Dockerfile",
   155  					Buildx:             true,
   156  					BuildFlagTemplates: []string{"--platform=linux/amd64"},
   157  				},
   158  				{
   159  					ImageTemplates:     []string{registry + "goreleaser/test_multiarch_buildx:arm64v8"},
   160  					Goos:               "linux",
   161  					Goarch:             "arm64",
   162  					Dockerfile:         "testdata/Dockerfile",
   163  					Buildx:             true,
   164  					BuildFlagTemplates: []string{"--platform=linux/arm64"},
   165  				},
   166  			},
   167  			manifests: []config.DockerManifest{
   168  				{
   169  					NameTemplate: registry + "goreleaser/test_multiarch_buildx:test",
   170  					ImageTemplates: []string{
   171  						registry + "goreleaser/test_multiarch_buildx:amd64",
   172  						registry + "goreleaser/test_multiarch_buildx:arm64v8",
   173  					},
   174  					CreateFlags: []string{"--insecure"},
   175  					PushFlags:   []string{"--insecure"},
   176  				},
   177  			},
   178  			expect: []string{
   179  				registry + "goreleaser/test_multiarch_buildx:amd64",
   180  				registry + "goreleaser/test_multiarch_buildx:arm64v8",
   181  			},
   182  			assertError:         shouldNotErr,
   183  			pubAssertError:      shouldNotErr,
   184  			manifestAssertError: shouldNotErr,
   185  			assertImageLabels:   noLabels,
   186  		},
   187  		"multiarch image not found": {
   188  			dockers: []config.Docker{
   189  				{
   190  					ImageTemplates:     []string{registry + "goreleaser/test_multiarch_fail:latest-arm64v8"},
   191  					Goos:               "linux",
   192  					Goarch:             "arm64",
   193  					Dockerfile:         "testdata/Dockerfile.arch",
   194  					BuildFlagTemplates: []string{"--build-arg", "ARCH=arm64v8"},
   195  				},
   196  			},
   197  			manifests: []config.DockerManifest{
   198  				{
   199  					NameTemplate:   registry + "goreleaser/test_multiarch_fail:test",
   200  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_fail:latest-amd64"},
   201  					CreateFlags:    []string{"--insecure"},
   202  					PushFlags:      []string{"--insecure"},
   203  				},
   204  			},
   205  			expect:              []string{registry + "goreleaser/test_multiarch_fail:latest-arm64v8"},
   206  			assertError:         shouldNotErr,
   207  			pubAssertError:      shouldNotErr,
   208  			manifestAssertError: shouldErr("failed to create docker manifest: localhost:5000/goreleaser/test_multiarch_fail:test"),
   209  			assertImageLabels:   noLabels,
   210  		},
   211  		"multiarch manifest template error": {
   212  			dockers: []config.Docker{
   213  				{
   214  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"},
   215  					Goos:           "linux",
   216  					Goarch:         "arm64",
   217  					Dockerfile:     "testdata/Dockerfile",
   218  				},
   219  			},
   220  			manifests: []config.DockerManifest{
   221  				{
   222  					NameTemplate:   registry + "goreleaser/test_multiarch_manifest_tmpl_error:{{ .Goos }",
   223  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"},
   224  				},
   225  			},
   226  			expect:              []string{registry + "goreleaser/test_multiarch_manifest_tmpl_error"},
   227  			assertError:         shouldNotErr,
   228  			pubAssertError:      shouldNotErr,
   229  			manifestAssertError: shouldErr(`template: tmpl:1: unexpected "}" in operand`),
   230  			assertImageLabels:   noLabels,
   231  		},
   232  		"multiarch image template error": {
   233  			dockers: []config.Docker{
   234  				{
   235  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error"},
   236  					Goos:           "linux",
   237  					Goarch:         "arm64",
   238  					Dockerfile:     "testdata/Dockerfile",
   239  				},
   240  			},
   241  			manifests: []config.DockerManifest{
   242  				{
   243  					NameTemplate:   registry + "goreleaser/test_multiarch_img_tmpl_error",
   244  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_img_tmpl_error:{{ .Goos }"},
   245  				},
   246  			},
   247  			expect:              []string{registry + "goreleaser/test_multiarch_img_tmpl_error"},
   248  			assertError:         shouldNotErr,
   249  			pubAssertError:      shouldNotErr,
   250  			manifestAssertError: shouldErr(`template: tmpl:1: unexpected "}" in operand`),
   251  			assertImageLabels:   noLabels,
   252  		},
   253  		"multiarch missing manifest name": {
   254  			dockers: []config.Docker{
   255  				{
   256  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_manifest_name"},
   257  					Goos:           "linux",
   258  					Goarch:         "arm64",
   259  					Dockerfile:     "testdata/Dockerfile",
   260  				},
   261  			},
   262  			manifests: []config.DockerManifest{
   263  				{
   264  					NameTemplate:   "  ",
   265  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_manifest_name"},
   266  				},
   267  			},
   268  			expect:              []string{registry + "goreleaser/test_multiarch_no_manifest_name"},
   269  			assertError:         shouldNotErr,
   270  			pubAssertError:      shouldNotErr,
   271  			manifestAssertError: testlib.AssertSkipped,
   272  			assertImageLabels:   noLabels,
   273  		},
   274  		"multiarch missing images": {
   275  			dockers: []config.Docker{
   276  				{
   277  					ImageTemplates: []string{registry + "goreleaser/test_multiarch_no_manifest_images"},
   278  					Dockerfile:     "testdata/Dockerfile",
   279  					Goos:           "linux",
   280  					Goarch:         "arm64",
   281  				},
   282  			},
   283  			manifests: []config.DockerManifest{
   284  				{
   285  					NameTemplate:   "ignored",
   286  					ImageTemplates: []string{" ", "   ", ""},
   287  				},
   288  			},
   289  			expect:              []string{registry + "goreleaser/test_multiarch_no_manifest_images"},
   290  			assertError:         shouldNotErr,
   291  			pubAssertError:      shouldNotErr,
   292  			manifestAssertError: testlib.AssertSkipped,
   293  			assertImageLabels:   noLabels,
   294  		},
   295  		"valid": {
   296  			env: map[string]string{
   297  				"FOO": "123",
   298  			},
   299  			dockers: []config.Docker{
   300  				{
   301  					ImageTemplates: []string{
   302  						registry + "goreleaser/test_run_pipe:{{.Tag}}-{{.Env.FOO}}",
   303  						registry + "goreleaser/test_run_pipe:v{{.Major}}",
   304  						registry + "goreleaser/test_run_pipe:v{{.Major}}.{{.Minor}}",
   305  						registry + "goreleaser/test_run_pipe:commit-{{.Commit}}",
   306  						registry + "goreleaser/test_run_pipe:latest",
   307  						altRegistry + "goreleaser/test_run_pipe:{{.Tag}}-{{.Env.FOO}}",
   308  						altRegistry + "goreleaser/test_run_pipe:v{{.Major}}",
   309  						altRegistry + "goreleaser/test_run_pipe:v{{.Major}}.{{.Minor}}",
   310  						altRegistry + "goreleaser/test_run_pipe:commit-{{.Commit}}",
   311  						altRegistry + "goreleaser/test_run_pipe:latest",
   312  					},
   313  					Goos:       "linux",
   314  					Goarch:     "amd64",
   315  					Dockerfile: "testdata/Dockerfile",
   316  					BuildFlagTemplates: []string{
   317  						"--label=org.label-schema.schema-version=1.0",
   318  						"--label=org.label-schema.version={{.Version}}",
   319  						"--label=org.label-schema.vcs-ref={{.Commit}}",
   320  						"--label=org.label-schema.name={{.ProjectName}}",
   321  						"--build-arg=FRED={{.Tag}}",
   322  					},
   323  					Files: []string{
   324  						"testdata/extra_file.txt",
   325  					},
   326  				},
   327  			},
   328  			expect: []string{
   329  				registry + "goreleaser/test_run_pipe:v1.0.0-123",
   330  				registry + "goreleaser/test_run_pipe:v1",
   331  				registry + "goreleaser/test_run_pipe:v1.0",
   332  				registry + "goreleaser/test_run_pipe:commit-a1b2c3d4",
   333  				registry + "goreleaser/test_run_pipe:latest",
   334  				altRegistry + "goreleaser/test_run_pipe:v1.0.0-123",
   335  				altRegistry + "goreleaser/test_run_pipe:v1",
   336  				altRegistry + "goreleaser/test_run_pipe:v1.0",
   337  				altRegistry + "goreleaser/test_run_pipe:commit-a1b2c3d4",
   338  				altRegistry + "goreleaser/test_run_pipe:latest",
   339  			},
   340  			assertImageLabels: shouldFindImagesWithLabels(
   341  				"goreleaser/test_run_pipe",
   342  				"label=org.label-schema.schema-version=1.0",
   343  				"label=org.label-schema.version=1.0.0",
   344  				"label=org.label-schema.vcs-ref=a1b2c3d4",
   345  				"label=org.label-schema.name=mybin",
   346  			),
   347  			assertError:         shouldNotErr,
   348  			pubAssertError:      shouldNotErr,
   349  			manifestAssertError: shouldNotErr,
   350  		},
   351  		"empty image tag": {
   352  			dockers: []config.Docker{
   353  				{
   354  					ImageTemplates: []string{
   355  						"",
   356  						registry + "goreleaser/empty_tag:latest",
   357  					},
   358  					Goos:       "linux",
   359  					Goarch:     "amd64",
   360  					Dockerfile: "testdata/Dockerfile",
   361  				},
   362  			},
   363  			expect: []string{
   364  				registry + "goreleaser/empty_tag:latest",
   365  			},
   366  			assertImageLabels:   noLabels,
   367  			assertError:         shouldNotErr,
   368  			pubAssertError:      shouldNotErr,
   369  			manifestAssertError: shouldNotErr,
   370  		},
   371  		"no image tags": {
   372  			dockers: []config.Docker{
   373  				{
   374  					ImageTemplates: []string{
   375  						"",
   376  					},
   377  					Goos:       "linux",
   378  					Goarch:     "amd64",
   379  					Dockerfile: "testdata/Dockerfile",
   380  				},
   381  			},
   382  			expect:              []string{},
   383  			assertImageLabels:   noLabels,
   384  			assertError:         shouldErr("no image templates found"),
   385  			pubAssertError:      shouldNotErr,
   386  			manifestAssertError: shouldNotErr,
   387  		},
   388  		"valid with ids": {
   389  			dockers: []config.Docker{
   390  				{
   391  					ImageTemplates: []string{
   392  						registry + "goreleaser/test_run_pipe_build:latest",
   393  					},
   394  					Goos:       "linux",
   395  					Goarch:     "amd64",
   396  					Dockerfile: "testdata/Dockerfile",
   397  					IDs:        []string{"mybin"},
   398  				},
   399  			},
   400  			expect: []string{
   401  				registry + "goreleaser/test_run_pipe_build:latest",
   402  			},
   403  			assertImageLabels:   noLabels,
   404  			assertError:         shouldNotErr,
   405  			pubAssertError:      shouldNotErr,
   406  			manifestAssertError: shouldNotErr,
   407  		},
   408  		"multiple images with same extra file": {
   409  			dockers: []config.Docker{
   410  				{
   411  					ImageTemplates: []string{
   412  						registry + "goreleaser/multiplefiles1:latest",
   413  					},
   414  					Goos:       "linux",
   415  					Goarch:     "amd64",
   416  					Dockerfile: "testdata/Dockerfile",
   417  					Files:      []string{"testdata/extra_file.txt"},
   418  				},
   419  				{
   420  					ImageTemplates: []string{
   421  						registry + "goreleaser/multiplefiles2:latest",
   422  					},
   423  					Goos:       "linux",
   424  					Goarch:     "amd64",
   425  					Dockerfile: "testdata/Dockerfile",
   426  					Files:      []string{"testdata/extra_file.txt"},
   427  				},
   428  			},
   429  			expect: []string{
   430  				registry + "goreleaser/multiplefiles1:latest",
   431  				registry + "goreleaser/multiplefiles2:latest",
   432  			},
   433  			assertImageLabels:   noLabels,
   434  			assertError:         shouldNotErr,
   435  			pubAssertError:      shouldNotErr,
   436  			manifestAssertError: shouldNotErr,
   437  		},
   438  		"multiple images with same dockerfile": {
   439  			dockers: []config.Docker{
   440  				{
   441  					ImageTemplates: []string{
   442  						registry + "goreleaser/test_run_pipe:latest",
   443  					},
   444  					Goos:       "linux",
   445  					Goarch:     "amd64",
   446  					Dockerfile: "testdata/Dockerfile",
   447  				},
   448  				{
   449  					ImageTemplates: []string{
   450  						registry + "goreleaser/test_run_pipe2:latest",
   451  					},
   452  					Goos:       "linux",
   453  					Goarch:     "amd64",
   454  					Dockerfile: "testdata/Dockerfile",
   455  				},
   456  			},
   457  			assertImageLabels: noLabels,
   458  			expect: []string{
   459  				registry + "goreleaser/test_run_pipe:latest",
   460  				registry + "goreleaser/test_run_pipe2:latest",
   461  			},
   462  			assertError:         shouldNotErr,
   463  			pubAssertError:      shouldNotErr,
   464  			manifestAssertError: shouldNotErr,
   465  		},
   466  		"valid_skip_push": {
   467  			dockers: []config.Docker{
   468  				{
   469  					ImageTemplates: []string{
   470  						registry + "goreleaser/test_run_pipe:latest",
   471  					},
   472  					Goos:       "linux",
   473  					Goarch:     "amd64",
   474  					Dockerfile: "testdata/Dockerfile",
   475  					SkipPush:   "true",
   476  				},
   477  			},
   478  			expect: []string{
   479  				registry + "goreleaser/test_run_pipe:latest",
   480  			},
   481  			assertImageLabels: noLabels,
   482  			assertError:       testlib.AssertSkipped,
   483  		},
   484  		"one_img_error_with_skip_push": {
   485  			dockers: []config.Docker{
   486  				{
   487  					ImageTemplates: []string{
   488  						registry + "goreleaser/one_img_error_with_skip_push:true",
   489  					},
   490  					Goos:       "linux",
   491  					Goarch:     "amd64",
   492  					Dockerfile: "testdata/Dockerfile.true",
   493  					SkipPush:   "true",
   494  				},
   495  				{
   496  					ImageTemplates: []string{
   497  						registry + "goreleaser/one_img_error_with_skip_push:false",
   498  					},
   499  					Goos:       "linux",
   500  					Goarch:     "amd64",
   501  					Dockerfile: "testdata/Dockerfile.false",
   502  					SkipPush:   "true",
   503  				},
   504  			},
   505  			expect: []string{
   506  				registry + "goreleaser/one_img_error_with_skip_push:true",
   507  			},
   508  			assertImageLabels: noLabels,
   509  			assertError:       shouldErr("failed to build docker image"),
   510  		},
   511  		"valid_no_latest": {
   512  			dockers: []config.Docker{
   513  				{
   514  					ImageTemplates: []string{
   515  						registry + "goreleaser/test_run_pipe:{{.Version}}",
   516  					},
   517  					Goos:       "linux",
   518  					Goarch:     "amd64",
   519  					Dockerfile: "testdata/Dockerfile",
   520  				},
   521  			},
   522  			expect: []string{
   523  				registry + "goreleaser/test_run_pipe:1.0.0",
   524  			},
   525  			assertImageLabels:   noLabels,
   526  			assertError:         shouldNotErr,
   527  			pubAssertError:      shouldNotErr,
   528  			manifestAssertError: shouldNotErr,
   529  		},
   530  		"valid build args": {
   531  			dockers: []config.Docker{
   532  				{
   533  					ImageTemplates: []string{
   534  						registry + "goreleaser/test_build_args:latest",
   535  					},
   536  					Goos:       "linux",
   537  					Goarch:     "amd64",
   538  					Dockerfile: "testdata/Dockerfile",
   539  					BuildFlagTemplates: []string{
   540  						"--label=foo=bar",
   541  					},
   542  				},
   543  			},
   544  			expect: []string{
   545  				registry + "goreleaser/test_build_args:latest",
   546  			},
   547  			assertImageLabels:   noLabels,
   548  			assertError:         shouldNotErr,
   549  			pubAssertError:      shouldNotErr,
   550  			manifestAssertError: shouldNotErr,
   551  		},
   552  		"bad build args": {
   553  			dockers: []config.Docker{
   554  				{
   555  					ImageTemplates: []string{
   556  						registry + "goreleaser/test_build_args:latest",
   557  					},
   558  					Goos:       "linux",
   559  					Goarch:     "amd64",
   560  					Dockerfile: "testdata/Dockerfile",
   561  					BuildFlagTemplates: []string{
   562  						"--bad-flag",
   563  					},
   564  				},
   565  			},
   566  			assertImageLabels: noLabels,
   567  			assertError:       shouldErr("unknown flag: --bad-flag"),
   568  		},
   569  		"bad_dockerfile": {
   570  			dockers: []config.Docker{
   571  				{
   572  					ImageTemplates: []string{
   573  						registry + "goreleaser/bad_dockerfile:latest",
   574  					},
   575  					Goos:       "linux",
   576  					Goarch:     "amd64",
   577  					Dockerfile: "testdata/Dockerfile.bad",
   578  				},
   579  			},
   580  			assertImageLabels: noLabels,
   581  			assertError:       shouldErr("pull access denied for nope, repository does not exist"),
   582  		},
   583  		"tag_template_error": {
   584  			dockers: []config.Docker{
   585  				{
   586  					ImageTemplates: []string{
   587  						registry + "goreleaser/test_run_pipe:{{.Tag}",
   588  					},
   589  					Goos:       "linux",
   590  					Goarch:     "amd64",
   591  					Dockerfile: "testdata/Dockerfile",
   592  				},
   593  			},
   594  			assertImageLabels: noLabels,
   595  			assertError:       shouldErr(`template: tmpl:1: unexpected "}" in operand`),
   596  		},
   597  		"build_flag_template_error": {
   598  			dockers: []config.Docker{
   599  				{
   600  					ImageTemplates: []string{
   601  						registry + "goreleaser/test_run_pipe:latest",
   602  					},
   603  					Goos:       "linux",
   604  					Goarch:     "amd64",
   605  					Dockerfile: "testdata/Dockerfile",
   606  					BuildFlagTemplates: []string{
   607  						"--label=tag={{.Tag}",
   608  					},
   609  				},
   610  			},
   611  			assertImageLabels: noLabels,
   612  			assertError:       shouldErr(`template: tmpl:1: unexpected "}" in operand`),
   613  		},
   614  		"missing_env_on_tag_template": {
   615  			dockers: []config.Docker{
   616  				{
   617  					ImageTemplates: []string{
   618  						registry + "goreleaser/test_run_pipe:{{.Env.NOPE}}",
   619  					},
   620  					Goos:       "linux",
   621  					Goarch:     "amd64",
   622  					Dockerfile: "testdata/Dockerfile",
   623  				},
   624  			},
   625  			assertImageLabels: noLabels,
   626  			assertError:       shouldErr(`template: tmpl:1:46: executing "tmpl" at <.Env.NOPE>: map has no entry for key "NOPE"`),
   627  		},
   628  		"missing_env_on_build_flag_template": {
   629  			dockers: []config.Docker{
   630  				{
   631  					ImageTemplates: []string{
   632  						registry + "goreleaser/test_run_pipe:latest",
   633  					},
   634  					Goos:       "linux",
   635  					Goarch:     "amd64",
   636  					Dockerfile: "testdata/Dockerfile",
   637  					BuildFlagTemplates: []string{
   638  						"--label=nope={{.Env.NOPE}}",
   639  					},
   640  				},
   641  			},
   642  			assertImageLabels: noLabels,
   643  			assertError:       shouldErr(`template: tmpl:1:19: executing "tmpl" at <.Env.NOPE>: map has no entry for key "NOPE"`),
   644  		},
   645  		"image_has_projectname_template_variable": {
   646  			dockers: []config.Docker{
   647  				{
   648  					ImageTemplates: []string{
   649  						registry + "goreleaser/{{.ProjectName}}:{{.Tag}}-{{.Env.FOO}}",
   650  						registry + "goreleaser/{{.ProjectName}}:latest",
   651  					},
   652  					Goos:       "linux",
   653  					Goarch:     "amd64",
   654  					Dockerfile: "testdata/Dockerfile",
   655  					SkipPush:   "true",
   656  				},
   657  			},
   658  			env: map[string]string{
   659  				"FOO": "123",
   660  			},
   661  			expect: []string{
   662  				registry + "goreleaser/mybin:v1.0.0-123",
   663  				registry + "goreleaser/mybin:latest",
   664  			},
   665  			assertImageLabels: noLabels,
   666  			assertError:       testlib.AssertSkipped,
   667  		},
   668  		"no_permissions": {
   669  			dockers: []config.Docker{
   670  				{
   671  					ImageTemplates: []string{"docker.io/nope:latest"},
   672  					Goos:           "linux",
   673  					Goarch:         "amd64",
   674  					Dockerfile:     "testdata/Dockerfile",
   675  				},
   676  			},
   677  			expect: []string{
   678  				"docker.io/nope:latest",
   679  			},
   680  			assertImageLabels:   noLabels,
   681  			assertError:         shouldNotErr,
   682  			pubAssertError:      shouldErr(`requested access to the resource is denied`),
   683  			manifestAssertError: shouldNotErr,
   684  		},
   685  		"dockerfile_doesnt_exist": {
   686  			dockers: []config.Docker{
   687  				{
   688  					ImageTemplates: []string{"whatever:latest"},
   689  					Goos:           "linux",
   690  					Goarch:         "amd64",
   691  					Dockerfile:     "testdata/Dockerfilezzz",
   692  				},
   693  			},
   694  			assertImageLabels: noLabels,
   695  			assertError:       shouldErr(`failed to link dockerfile`),
   696  		},
   697  		"extra_file_doesnt_exist": {
   698  			dockers: []config.Docker{
   699  				{
   700  					ImageTemplates: []string{"whatever:latest"},
   701  					Goos:           "linux",
   702  					Goarch:         "amd64",
   703  					Dockerfile:     "testdata/Dockerfile",
   704  					Files: []string{
   705  						"testdata/nope.txt",
   706  					},
   707  				},
   708  			},
   709  			assertImageLabels: noLabels,
   710  			assertError:       shouldErr(`failed to link extra file 'testdata/nope.txt'`),
   711  		},
   712  		"binary doesnt exist": {
   713  			dockers: []config.Docker{
   714  				{
   715  					ImageTemplates: []string{"whatever:latest"},
   716  					Goos:           "linux",
   717  					Goarch:         "amd64",
   718  					Dockerfile:     "testdata/Dockerfile",
   719  					IDs:            []string{"nope"},
   720  				},
   721  			},
   722  			assertImageLabels: noLabels,
   723  			assertError:       shouldErr(`/wont-exist: no such file or directory`),
   724  			extraPrepare: func(ctx *context.Context) {
   725  				ctx.Artifacts.Add(&artifact.Artifact{
   726  					Name:   "wont-exist",
   727  					Path:   "wont-exist",
   728  					Goarch: "amd64",
   729  					Goos:   "linux",
   730  					Type:   artifact.Binary,
   731  					Extra: map[string]interface{}{
   732  						"ID": "nope",
   733  					},
   734  				})
   735  			},
   736  		},
   737  		"multiple_ids": {
   738  			dockers: []config.Docker{
   739  				{
   740  					ImageTemplates: []string{registry + "goreleaser/multiple:latest"},
   741  					Goos:           "darwin",
   742  					Goarch:         "amd64",
   743  					IDs:            []string{"mybin", "anotherbin"},
   744  					Dockerfile:     "testdata/Dockerfile.multiple",
   745  				},
   746  			},
   747  			assertImageLabels:   noLabels,
   748  			assertError:         shouldNotErr,
   749  			pubAssertError:      shouldNotErr,
   750  			manifestAssertError: shouldNotErr,
   751  			expect: []string{
   752  				registry + "goreleaser/multiple:latest",
   753  			},
   754  		},
   755  		"nfpm and multiple binaries": {
   756  			dockers: []config.Docker{
   757  				{
   758  					ImageTemplates: []string{registry + "goreleaser/nfpm:latest"},
   759  					Goos:           "linux",
   760  					Goarch:         "amd64",
   761  					IDs:            []string{"mybin", "anotherbin"},
   762  					Dockerfile:     "testdata/Dockerfile.nfpm",
   763  				},
   764  			},
   765  			assertImageLabels:   noLabels,
   766  			assertError:         shouldNotErr,
   767  			pubAssertError:      shouldNotErr,
   768  			manifestAssertError: shouldNotErr,
   769  			expect: []string{
   770  				registry + "goreleaser/nfpm:latest",
   771  			},
   772  		},
   773  		"nfpm and multiple binaries on arm64": {
   774  			dockers: []config.Docker{
   775  				{
   776  					ImageTemplates: []string{registry + "goreleaser/nfpm_arm:latest"},
   777  					Goos:           "linux",
   778  					Goarch:         "arm64",
   779  					IDs:            []string{"mybin", "anotherbin"},
   780  					Dockerfile:     "testdata/Dockerfile.nfpm",
   781  				},
   782  			},
   783  			assertImageLabels:   noLabels,
   784  			assertError:         shouldNotErr,
   785  			pubAssertError:      shouldNotErr,
   786  			manifestAssertError: shouldNotErr,
   787  			expect: []string{
   788  				registry + "goreleaser/nfpm_arm:latest",
   789  			},
   790  		},
   791  	}
   792  
   793  	killAndRm(t)
   794  	start(t)
   795  	defer killAndRm(t)
   796  
   797  	for name, docker := range table {
   798  		t.Run(name, func(t *testing.T) {
   799  			var folder = t.TempDir()
   800  			var dist = filepath.Join(folder, "dist")
   801  			require.NoError(t, os.Mkdir(dist, 0755))
   802  			require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
   803  			_, err := os.Create(filepath.Join(dist, "mybin", "mybin"))
   804  			require.NoError(t, err)
   805  			_, err = os.Create(filepath.Join(dist, "mybin", "anotherbin"))
   806  			require.NoError(t, err)
   807  			_, err = os.Create(filepath.Join(dist, "mynfpm.apk"))
   808  			require.NoError(t, err)
   809  			for _, arch := range []string{"amd64", "386", "arm64"} {
   810  				_, err = os.Create(filepath.Join(dist, fmt.Sprintf("mybin_%s.apk", arch)))
   811  				require.NoError(t, err)
   812  			}
   813  
   814  			var ctx = context.New(config.Project{
   815  				ProjectName:     "mybin",
   816  				Dist:            dist,
   817  				Dockers:         docker.dockers,
   818  				DockerManifests: docker.manifests,
   819  			})
   820  			ctx.Parallelism = 1
   821  			ctx.Env = docker.env
   822  			ctx.Version = "1.0.0"
   823  			ctx.Git = context.GitInfo{
   824  				CurrentTag: "v1.0.0",
   825  				Commit:     "a1b2c3d4",
   826  			}
   827  			ctx.Semver = context.Semver{
   828  				Major: 1,
   829  				Minor: 0,
   830  				Patch: 0,
   831  			}
   832  			for _, os := range []string{"linux", "darwin"} {
   833  				for _, arch := range []string{"amd64", "386", "arm64"} {
   834  					for _, bin := range []string{"mybin", "anotherbin"} {
   835  						ctx.Artifacts.Add(&artifact.Artifact{
   836  							Name:   bin,
   837  							Path:   filepath.Join(dist, "mybin", bin),
   838  							Goarch: arch,
   839  							Goos:   os,
   840  							Type:   artifact.Binary,
   841  							Extra: map[string]interface{}{
   842  								"ID": bin,
   843  							},
   844  						})
   845  					}
   846  				}
   847  			}
   848  			for _, arch := range []string{"amd64", "386", "arm64"} {
   849  				var name = fmt.Sprintf("mybin_%s.apk", arch)
   850  				ctx.Artifacts.Add(&artifact.Artifact{
   851  					Name:   name,
   852  					Path:   filepath.Join(dist, name),
   853  					Goarch: arch,
   854  					Goos:   "linux",
   855  					Type:   artifact.LinuxPackage,
   856  					Extra: map[string]interface{}{
   857  						"ID": "mybin",
   858  					},
   859  				})
   860  			}
   861  
   862  			if docker.extraPrepare != nil {
   863  				docker.extraPrepare(ctx)
   864  			}
   865  
   866  			// this might fail as the image doesnt exist yet, so lets ignore the error
   867  			for _, img := range docker.expect {
   868  				_ = exec.Command("docker", "rmi", img).Run()
   869  			}
   870  
   871  			err = Pipe{}.Run(ctx)
   872  			docker.assertError(t, err)
   873  			if err == nil {
   874  				docker.pubAssertError(t, Pipe{}.Publish(ctx))
   875  				docker.manifestAssertError(t, ManifestPipe{}.Publish(ctx))
   876  			}
   877  
   878  			for _, d := range docker.dockers {
   879  				docker.assertImageLabels(t, len(d.ImageTemplates))
   880  			}
   881  
   882  			// this might should not fail as the image should have been created when
   883  			// the step ran
   884  			for _, img := range docker.expect {
   885  				t.Log("removing docker image", img)
   886  				require.NoError(t, exec.Command("docker", "rmi", img).Run(), "could not delete image %s", img)
   887  			}
   888  
   889  		})
   890  	}
   891  }
   892  
   893  func TestBuildCommand(t *testing.T) {
   894  	images := []string{"goreleaser/test_build_flag", "goreleaser/test_multiple_tags"}
   895  	tests := []struct {
   896  		name   string
   897  		flags  []string
   898  		buildx bool
   899  		expect []string
   900  	}{
   901  		{
   902  			name:   "no flags",
   903  			flags:  []string{},
   904  			expect: []string{"build", ".", "-t", images[0], "-t", images[1]},
   905  		},
   906  		{
   907  			name:   "single flag",
   908  			flags:  []string{"--label=foo"},
   909  			expect: []string{"build", ".", "-t", images[0], "-t", images[1], "--label=foo"},
   910  		},
   911  		{
   912  			name:   "multiple flags",
   913  			flags:  []string{"--label=foo", "--build-arg=bar=baz"},
   914  			expect: []string{"build", ".", "-t", images[0], "-t", images[1], "--label=foo", "--build-arg=bar=baz"},
   915  		},
   916  		{
   917  			name:   "buildx",
   918  			buildx: true,
   919  			flags:  []string{"--label=foo", "--build-arg=bar=baz"},
   920  			expect: []string{"buildx", "build", ".", "--load", "-t", images[0], "-t", images[1], "--label=foo", "--build-arg=bar=baz"},
   921  		},
   922  	}
   923  	for _, tt := range tests {
   924  		t.Run(tt.name, func(t *testing.T) {
   925  			require.Equal(t, tt.expect, buildCommand(tt.buildx, images, tt.flags))
   926  		})
   927  	}
   928  }
   929  
   930  func TestDescription(t *testing.T) {
   931  	require.NotEmpty(t, Pipe{}.String())
   932  }
   933  
   934  func TestNoDockers(t *testing.T) {
   935  	require.True(t, pipe.IsSkip(Pipe{}.Run(context.New(config.Project{}))))
   936  }
   937  
   938  func TestNoDockerWithoutImageName(t *testing.T) {
   939  	require.True(t, pipe.IsSkip(Pipe{}.Run(context.New(config.Project{
   940  		Dockers: []config.Docker{
   941  			{
   942  				Goos: "linux",
   943  			},
   944  		},
   945  	}))))
   946  }
   947  
   948  func TestDockerNotInPath(t *testing.T) {
   949  	var path = os.Getenv("PATH")
   950  	defer func() {
   951  		require.NoError(t, os.Setenv("PATH", path))
   952  	}()
   953  	require.NoError(t, os.Setenv("PATH", ""))
   954  	var ctx = &context.Context{
   955  		Version: "1.0.0",
   956  		Config: config.Project{
   957  			Dockers: []config.Docker{
   958  				{
   959  					ImageTemplates: []string{"a/b"},
   960  				},
   961  			},
   962  		},
   963  	}
   964  	require.EqualError(t, Pipe{}.Run(ctx), ErrNoDocker.Error())
   965  }
   966  
   967  func TestDefault(t *testing.T) {
   968  	var ctx = &context.Context{
   969  		Config: config.Project{
   970  			Dockers: []config.Docker{
   971  				{
   972  					IDs:      []string{"aa"},
   973  					Builds:   []string{"foo"},
   974  					Binaries: []string{"aaa"},
   975  				},
   976  			},
   977  		},
   978  	}
   979  	require.NoError(t, Pipe{}.Default(ctx))
   980  	require.Len(t, ctx.Config.Dockers, 1)
   981  	var docker = ctx.Config.Dockers[0]
   982  	require.Equal(t, "linux", docker.Goos)
   983  	require.Equal(t, "amd64", docker.Goarch)
   984  	require.Equal(t, []string{"aa", "foo"}, docker.IDs)
   985  }
   986  
   987  func TestDefaultDockerfile(t *testing.T) {
   988  	var ctx = &context.Context{
   989  		Config: config.Project{
   990  			Builds: []config.Build{
   991  				{},
   992  			},
   993  			Dockers: []config.Docker{
   994  				{},
   995  				{},
   996  			},
   997  		},
   998  	}
   999  	require.NoError(t, Pipe{}.Default(ctx))
  1000  	require.Len(t, ctx.Config.Dockers, 2)
  1001  	require.Equal(t, "Dockerfile", ctx.Config.Dockers[0].Dockerfile)
  1002  	require.Equal(t, "Dockerfile", ctx.Config.Dockers[1].Dockerfile)
  1003  }
  1004  
  1005  func TestDefaultNoDockers(t *testing.T) {
  1006  	var ctx = &context.Context{
  1007  		Config: config.Project{
  1008  			Dockers: []config.Docker{},
  1009  		},
  1010  	}
  1011  	require.NoError(t, Pipe{}.Default(ctx))
  1012  	require.Empty(t, ctx.Config.Dockers)
  1013  }
  1014  
  1015  func TestDefaultFilesDot(t *testing.T) {
  1016  	var ctx = &context.Context{
  1017  		Config: config.Project{
  1018  			Dist: "/tmp/distt",
  1019  			Dockers: []config.Docker{
  1020  				{
  1021  					Files: []string{"./lala", "./lolsob", "."},
  1022  				},
  1023  			},
  1024  		},
  1025  	}
  1026  	require.EqualError(t, Pipe{}.Default(ctx), `invalid docker.files: can't be . or inside dist folder: .`)
  1027  }
  1028  
  1029  func TestDefaultFilesDis(t *testing.T) {
  1030  	var ctx = &context.Context{
  1031  		Config: config.Project{
  1032  			Dist: "/tmp/dist",
  1033  			Dockers: []config.Docker{
  1034  				{
  1035  					Files: []string{"./fooo", "/tmp/dist/asdasd/asd", "./bar"},
  1036  				},
  1037  			},
  1038  		},
  1039  	}
  1040  	require.EqualError(t, Pipe{}.Default(ctx), `invalid docker.files: can't be . or inside dist folder: /tmp/dist/asdasd/asd`)
  1041  }
  1042  
  1043  func TestDefaultSet(t *testing.T) {
  1044  	var ctx = &context.Context{
  1045  		Config: config.Project{
  1046  			Dockers: []config.Docker{
  1047  				{
  1048  					IDs:        []string{"foo"},
  1049  					Goos:       "windows",
  1050  					Goarch:     "i386",
  1051  					Dockerfile: "Dockerfile.foo",
  1052  				},
  1053  			},
  1054  		},
  1055  	}
  1056  	require.NoError(t, Pipe{}.Default(ctx))
  1057  	require.Len(t, ctx.Config.Dockers, 1)
  1058  	var docker = ctx.Config.Dockers[0]
  1059  	require.Equal(t, "windows", docker.Goos)
  1060  	require.Equal(t, "i386", docker.Goarch)
  1061  	require.Equal(t, []string{"foo"}, docker.IDs)
  1062  	require.Equal(t, "Dockerfile.foo", docker.Dockerfile)
  1063  }
  1064  
  1065  func Test_processImageTemplates(t *testing.T) {
  1066  	var ctx = &context.Context{
  1067  		Config: config.Project{
  1068  			Builds: []config.Build{
  1069  				{
  1070  					ID: "default",
  1071  				},
  1072  			},
  1073  			Dockers: []config.Docker{
  1074  				{
  1075  					Dockerfile: "Dockerfile.foo",
  1076  					ImageTemplates: []string{
  1077  						"user/image:{{.Tag}}",
  1078  						"gcr.io/image:{{.Tag}}-{{.Env.FOO}}",
  1079  						"gcr.io/image:v{{.Major}}.{{.Minor}}",
  1080  					},
  1081  					SkipPush: "true",
  1082  				},
  1083  			},
  1084  		},
  1085  	}
  1086  	ctx.SkipPublish = true
  1087  	ctx.Env = map[string]string{
  1088  		"FOO": "123",
  1089  	}
  1090  	ctx.Version = "1.0.0"
  1091  	ctx.Git = context.GitInfo{
  1092  		CurrentTag: "v1.0.0",
  1093  		Commit:     "a1b2c3d4",
  1094  	}
  1095  	ctx.Semver = context.Semver{
  1096  		Major: 1,
  1097  		Minor: 0,
  1098  		Patch: 0,
  1099  	}
  1100  
  1101  	require.NoError(t, Pipe{}.Default(ctx))
  1102  	require.Len(t, ctx.Config.Dockers, 1)
  1103  
  1104  	docker := ctx.Config.Dockers[0]
  1105  	require.Equal(t, "Dockerfile.foo", docker.Dockerfile)
  1106  
  1107  	images, err := processImageTemplates(ctx, docker)
  1108  	require.NoError(t, err)
  1109  	require.Equal(t, []string{
  1110  		"user/image:v1.0.0",
  1111  		"gcr.io/image:v1.0.0-123",
  1112  		"gcr.io/image:v1.0",
  1113  	}, images)
  1114  }
  1115  
  1116  func TestLinkFile(t *testing.T) {
  1117  	src, err := ioutil.TempFile(t.TempDir(), "src")
  1118  	require.NoError(t, err)
  1119  	require.NoError(t, src.Close())
  1120  	dst := filepath.Join(filepath.Dir(src.Name()), "dst")
  1121  	t.Cleanup(func() {
  1122  		os.Remove(src.Name())
  1123  		os.Remove(dst)
  1124  	})
  1125  	fmt.Println("src:", src.Name())
  1126  	fmt.Println("dst:", dst)
  1127  	require.NoError(t, ioutil.WriteFile(src.Name(), []byte("foo"), 0644))
  1128  	require.NoError(t, link(src.Name(), dst))
  1129  	require.Equal(t, inode(src.Name()), inode(dst))
  1130  }
  1131  
  1132  func TestLinkDirectory(t *testing.T) {
  1133  	var srcDir = t.TempDir()
  1134  	var dstDir = t.TempDir()
  1135  	const testFile = "test"
  1136  	require.NoError(t, ioutil.WriteFile(filepath.Join(srcDir, testFile), []byte("foo"), 0644))
  1137  	require.NoError(t, link(srcDir, dstDir))
  1138  	require.Equal(t, inode(filepath.Join(srcDir, testFile)), inode(filepath.Join(dstDir, testFile)))
  1139  }
  1140  
  1141  func TestLinkTwoLevelDirectory(t *testing.T) {
  1142  	var srcDir = t.TempDir()
  1143  	var dstDir = t.TempDir()
  1144  	var srcLevel2 = filepath.Join(srcDir, "level2")
  1145  	const testFile = "test"
  1146  
  1147  	require.NoError(t, os.Mkdir(srcLevel2, 0755))
  1148  	require.NoError(t, ioutil.WriteFile(filepath.Join(srcDir, testFile), []byte("foo"), 0644))
  1149  	require.NoError(t, ioutil.WriteFile(filepath.Join(srcLevel2, testFile), []byte("foo"), 0644))
  1150  
  1151  	require.NoError(t, link(srcDir, dstDir))
  1152  
  1153  	require.Equal(t, inode(filepath.Join(srcDir, testFile)), inode(filepath.Join(dstDir, testFile)))
  1154  	require.Equal(t, inode(filepath.Join(srcLevel2, testFile)), inode(filepath.Join(dstDir, "level2", testFile)))
  1155  }
  1156  
  1157  func inode(file string) uint64 {
  1158  	fileInfo, err := os.Stat(file)
  1159  	if err != nil {
  1160  		return 0
  1161  	}
  1162  	stat := fileInfo.Sys().(*syscall.Stat_t)
  1163  	return stat.Ino
  1164  }