github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/artifactory/artifactory_test.go (about)

     1  package artifactory
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"os"
     8  	"path/filepath"
     9  	"sync"
    10  	"syscall"
    11  	"testing"
    12  
    13  	"github.com/goreleaser/goreleaser/internal/artifact"
    14  	"github.com/goreleaser/goreleaser/internal/testctx"
    15  	"github.com/goreleaser/goreleaser/internal/testlib"
    16  	"github.com/goreleaser/goreleaser/pkg/config"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  var (
    21  	// mux is the HTTP request multiplexer used with the test server.
    22  	mux *http.ServeMux
    23  
    24  	// server is a test HTTP server used to provide mock API responses.
    25  	server *httptest.Server
    26  )
    27  
    28  func setup() {
    29  	// test server
    30  	mux = http.NewServeMux()
    31  	server = httptest.NewServer(mux)
    32  }
    33  
    34  // teardown closes the test HTTP server.
    35  func teardown() {
    36  	server.Close()
    37  }
    38  
    39  func requireMethodPut(t *testing.T, r *http.Request) {
    40  	t.Helper()
    41  	require.Equal(t, http.MethodPut, r.Method)
    42  }
    43  
    44  func requireHeader(t *testing.T, r *http.Request, header, want string) {
    45  	t.Helper()
    46  	require.Equal(t, want, r.Header.Get(header))
    47  }
    48  
    49  // TODO: improve all tests below by checking whether the mocked handlers
    50  // were called or not.
    51  
    52  func TestRunPipe_ModeBinary(t *testing.T) {
    53  	setup()
    54  	defer teardown()
    55  
    56  	folder := t.TempDir()
    57  	dist := filepath.Join(folder, "dist")
    58  	require.NoError(t, os.Mkdir(dist, 0o755))
    59  	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0o755))
    60  	binPath := filepath.Join(dist, "mybin", "mybin")
    61  	d1 := []byte("hello\ngo\n")
    62  	require.NoError(t, os.WriteFile(binPath, d1, 0o666))
    63  
    64  	// Dummy artifactories
    65  	mux.HandleFunc("/example-repo-local/mybin/darwin/amd64/mybin", func(w http.ResponseWriter, r *http.Request) {
    66  		requireMethodPut(t, r)
    67  		requireHeader(t, r, "Content-Length", "9")
    68  		// Basic auth of user "deployuser" with secret "deployuser-secret"
    69  		requireHeader(t, r, "Authorization", "Basic ZGVwbG95dXNlcjpkZXBsb3l1c2VyLXNlY3JldA==")
    70  
    71  		w.WriteHeader(http.StatusCreated)
    72  		fmt.Fprint(w, `{
    73  			"repo" : "example-repo-local",
    74  			"path" : "/mybin/darwin/amd64/mybin",
    75  			"created" : "2017-12-02T19:30:45.436Z",
    76  			"createdBy" : "deployuser",
    77  			"downloadUri" : "http://127.0.0.1:56563/example-repo-local/mybin/darwin/amd64/mybin",
    78  			"mimeType" : "application/octet-stream",
    79  			"size" : "9",
    80  			"checksums" : {
    81  			  "sha1" : "65d01857a69f14ade727fe1ceee0f52a264b6e57",
    82  			  "md5" : "a55e303e7327dc871a8e2a84f30b9983",
    83  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
    84  			},
    85  			"originalChecksums" : {
    86  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
    87  			},
    88  			"uri" : "http://127.0.0.1:56563/example-repo-local/mybin/darwin/amd64/mybin"
    89  		  }`)
    90  	})
    91  	mux.HandleFunc("/example-repo-local/mybin/linux/amd64/mybin", func(w http.ResponseWriter, r *http.Request) {
    92  		requireMethodPut(t, r)
    93  		requireHeader(t, r, "Content-Length", "9")
    94  		// Basic auth of user "deployuser" with secret "deployuser-secret"
    95  		requireHeader(t, r, "Authorization", "Basic ZGVwbG95dXNlcjpkZXBsb3l1c2VyLXNlY3JldA==")
    96  
    97  		w.WriteHeader(http.StatusCreated)
    98  		fmt.Fprint(w, `{
    99  			"repo" : "example-repo-local",
   100  			"path" : "mybin/linux/amd64/mybin",
   101  			"created" : "2017-12-02T19:30:46.436Z",
   102  			"createdBy" : "deployuser",
   103  			"downloadUri" : "http://127.0.0.1:56563/example-repo-local/mybin/linux/amd64/mybin",
   104  			"mimeType" : "application/octet-stream",
   105  			"size" : "9",
   106  			"checksums" : {
   107  			  "sha1" : "65d01857a69f14ade727fe1ceee0f52a264b6e57",
   108  			  "md5" : "a55e303e7327dc871a8e2a84f30b9983",
   109  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   110  			},
   111  			"originalChecksums" : {
   112  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   113  			},
   114  			"uri" : "http://127.0.0.1:56563/example-repo-local/mybin/linux/amd64/mybin"
   115  		  }`)
   116  	})
   117  	mux.HandleFunc("/production-repo-remote/mybin/darwin/amd64/mybin", func(w http.ResponseWriter, r *http.Request) {
   118  		requireMethodPut(t, r)
   119  		requireHeader(t, r, "Content-Length", "9")
   120  		// Basic auth of user "productionuser" with secret "productionuser-apikey"
   121  		requireHeader(t, r, "Authorization", "Basic cHJvZHVjdGlvbnVzZXI6cHJvZHVjdGlvbnVzZXItYXBpa2V5")
   122  
   123  		w.WriteHeader(http.StatusCreated)
   124  		fmt.Fprint(w, `{
   125  			"repo" : "production-repo-remote",
   126  			"path" : "mybin/darwin/amd64/mybin",
   127  			"created" : "2017-12-02T19:30:46.436Z",
   128  			"createdBy" : "productionuser",
   129  			"downloadUri" : "http://127.0.0.1:56563/production-repo-remote/mybin/darwin/amd64/mybin",
   130  			"mimeType" : "application/octet-stream",
   131  			"size" : "9",
   132  			"checksums" : {
   133  			  "sha1" : "65d01857a69f14ade727fe1ceee0f52a264b6e57",
   134  			  "md5" : "a55e303e7327dc871a8e2a84f30b9983",
   135  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   136  			},
   137  			"originalChecksums" : {
   138  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   139  			},
   140  			"uri" : "http://127.0.0.1:56563/production-repo-remote/mybin/darwin/amd64/mybin"
   141  		  }`)
   142  	})
   143  	mux.HandleFunc("/production-repo-remote/mybin/linux/amd64/mybin", func(w http.ResponseWriter, r *http.Request) {
   144  		requireMethodPut(t, r)
   145  		requireHeader(t, r, "Content-Length", "9")
   146  		// Basic auth of user "productionuser" with secret "productionuser-apikey"
   147  		requireHeader(t, r, "Authorization", "Basic cHJvZHVjdGlvbnVzZXI6cHJvZHVjdGlvbnVzZXItYXBpa2V5")
   148  
   149  		w.WriteHeader(http.StatusCreated)
   150  		fmt.Fprint(w, `{
   151  			"repo" : "production-repo-remote",
   152  			"path" : "mybin/linux/amd64/mybin",
   153  			"created" : "2017-12-02T19:30:46.436Z",
   154  			"createdBy" : "productionuser",
   155  			"downloadUri" : "http://127.0.0.1:56563/production-repo-remote/mybin/linux/amd64/mybin",
   156  			"mimeType" : "application/octet-stream",
   157  			"size" : "9",
   158  			"checksums" : {
   159  			  "sha1" : "65d01857a69f14ade727fe1ceee0f52a264b6e57",
   160  			  "md5" : "a55e303e7327dc871a8e2a84f30b9983",
   161  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   162  			},
   163  			"originalChecksums" : {
   164  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   165  			},
   166  			"uri" : "http://127.0.0.1:56563/production-repo-remote/mybin/linux/amd64/mybin"
   167  		  }`)
   168  	})
   169  
   170  	ctx := testctx.NewWithCfg(config.Project{
   171  		ProjectName: "mybin",
   172  		Dist:        dist,
   173  		Artifactories: []config.Upload{
   174  			{
   175  				Name:     "production-us",
   176  				Mode:     "binary",
   177  				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
   178  				Username: "deployuser",
   179  			},
   180  			{
   181  				Name:     "production-eu",
   182  				Mode:     "binary",
   183  				Target:   fmt.Sprintf("%s/production-repo-remote/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
   184  				Username: "productionuser",
   185  			},
   186  		},
   187  		Archives: []config.Archive{{}},
   188  		Env: []string{
   189  			"ARTIFACTORY_PRODUCTION-US_SECRET=deployuser-secret",
   190  			"ARTIFACTORY_PRODUCTION-EU_SECRET=productionuser-apikey",
   191  		},
   192  	})
   193  	for _, goos := range []string{"linux", "darwin"} {
   194  		ctx.Artifacts.Add(&artifact.Artifact{
   195  			Name:   "mybin",
   196  			Path:   binPath,
   197  			Goarch: "amd64",
   198  			Goos:   goos,
   199  			Type:   artifact.UploadableBinary,
   200  		})
   201  	}
   202  
   203  	require.NoError(t, Pipe{}.Default(ctx))
   204  	require.NoError(t, Pipe{}.Publish(ctx))
   205  }
   206  
   207  func TestRunPipe_ModeArchive(t *testing.T) {
   208  	setup()
   209  	defer teardown()
   210  
   211  	folder := t.TempDir()
   212  	tarfile, err := os.Create(filepath.Join(folder, "bin.tar.gz"))
   213  	require.NoError(t, err)
   214  	require.NoError(t, tarfile.Close())
   215  	debfile, err := os.Create(filepath.Join(folder, "bin.deb"))
   216  	require.NoError(t, err)
   217  	require.NoError(t, debfile.Close())
   218  
   219  	ctx := testctx.NewWithCfg(config.Project{
   220  		ProjectName: "goreleaser",
   221  		Dist:        folder,
   222  		Artifactories: []config.Upload{
   223  			{
   224  				Name:     "production",
   225  				Mode:     "archive",
   226  				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Version }}/", server.URL),
   227  				Username: "deployuser",
   228  			},
   229  		},
   230  		Archives: []config.Archive{{}},
   231  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   232  	}, testctx.WithVersion("1.0.0"))
   233  	ctx.Artifacts.Add(&artifact.Artifact{
   234  		Type: artifact.UploadableArchive,
   235  		Name: "bin.tar.gz",
   236  		Path: tarfile.Name(),
   237  	})
   238  	ctx.Artifacts.Add(&artifact.Artifact{
   239  		Type: artifact.LinuxPackage,
   240  		Name: "bin.deb",
   241  		Path: debfile.Name(),
   242  	})
   243  
   244  	var uploads sync.Map
   245  
   246  	// Dummy artifactories
   247  	mux.HandleFunc("/example-repo-local/goreleaser/1.0.0/bin.tar.gz", func(w http.ResponseWriter, r *http.Request) {
   248  		requireMethodPut(t, r)
   249  		// Basic auth of user "deployuser" with secret "deployuser-secret"
   250  		requireHeader(t, r, "Authorization", "Basic ZGVwbG95dXNlcjpkZXBsb3l1c2VyLXNlY3JldA==")
   251  
   252  		w.WriteHeader(http.StatusCreated)
   253  		fmt.Fprint(w, `{
   254  			"repo" : "example-repo-local",
   255  			"path" : "/goreleaser/bin.tar.gz",
   256  			"created" : "2017-12-02T19:30:45.436Z",
   257  			"createdBy" : "deployuser",
   258  			"downloadUri" : "http://127.0.0.1:56563/example-repo-local/goreleaser/bin.tar.gz",
   259  			"mimeType" : "application/octet-stream",
   260  			"size" : "9",
   261  			"checksums" : {
   262  			  "sha1" : "65d01857a69f14ade727fe1ceee0f52a264b6e57",
   263  			  "md5" : "a55e303e7327dc871a8e2a84f30b9983",
   264  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   265  			},
   266  			"originalChecksums" : {
   267  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   268  			},
   269  			"uri" : "http://127.0.0.1:56563/example-repo-local/goreleaser/bin.tar.gz"
   270  		  }`)
   271  		uploads.Store("targz", true)
   272  	})
   273  	mux.HandleFunc("/example-repo-local/goreleaser/1.0.0/bin.deb", func(w http.ResponseWriter, r *http.Request) {
   274  		requireMethodPut(t, r)
   275  		// Basic auth of user "deployuser" with secret "deployuser-secret"
   276  		requireHeader(t, r, "Authorization", "Basic ZGVwbG95dXNlcjpkZXBsb3l1c2VyLXNlY3JldA==")
   277  
   278  		w.WriteHeader(http.StatusCreated)
   279  		fmt.Fprint(w, `{
   280  			"repo" : "example-repo-local",
   281  			"path" : "goreleaser/bin.deb",
   282  			"created" : "2017-12-02T19:30:46.436Z",
   283  			"createdBy" : "deployuser",
   284  			"downloadUri" : "http://127.0.0.1:56563/example-repo-local/goreleaser/bin.deb",
   285  			"mimeType" : "application/octet-stream",
   286  			"size" : "9",
   287  			"checksums" : {
   288  			  "sha1" : "65d01857a69f14ade727fe1ceee0f52a264b6e57",
   289  			  "md5" : "a55e303e7327dc871a8e2a84f30b9983",
   290  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   291  			},
   292  			"originalChecksums" : {
   293  			  "sha256" : "ead9b172aec5c24ca6c12e85a1e6fc48dd341d8fac38c5ba00a78881eabccf0e"
   294  			},
   295  			"uri" : "http://127.0.0.1:56563/example-repo-local/goreleaser/bin.deb"
   296  		  }`)
   297  		uploads.Store("deb", true)
   298  	})
   299  
   300  	require.NoError(t, Pipe{}.Default(ctx))
   301  	require.NoError(t, Pipe{}.Publish(ctx))
   302  	_, ok := uploads.Load("targz")
   303  	require.True(t, ok, "tar.gz file was not uploaded")
   304  	_, ok = uploads.Load("deb")
   305  	require.True(t, ok, "deb file was not uploaded")
   306  }
   307  
   308  func TestRunPipe_ArtifactoryDown(t *testing.T) {
   309  	folder := t.TempDir()
   310  	tarfile, err := os.Create(filepath.Join(folder, "bin.tar.gz"))
   311  	require.NoError(t, err)
   312  	require.NoError(t, tarfile.Close())
   313  
   314  	ctx := testctx.NewWithCfg(config.Project{
   315  		ProjectName: "goreleaser",
   316  		Dist:        folder,
   317  		Artifactories: []config.Upload{
   318  			{
   319  				Name:     "production",
   320  				Mode:     "archive",
   321  				Target:   "http://localhost:1234/example-repo-local/{{ .ProjectName }}/{{ .Version }}/",
   322  				Username: "deployuser",
   323  			},
   324  		},
   325  		Env: []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   326  	}, testctx.WithVersion("2.0.0"))
   327  	ctx.Artifacts.Add(&artifact.Artifact{
   328  		Type: artifact.UploadableArchive,
   329  		Name: "bin.tar.gz",
   330  		Path: tarfile.Name(),
   331  	})
   332  
   333  	require.NoError(t, Pipe{}.Default(ctx))
   334  	require.ErrorIs(t, Pipe{}.Publish(ctx), syscall.ECONNREFUSED)
   335  }
   336  
   337  func TestRunPipe_TargetTemplateError(t *testing.T) {
   338  	folder := t.TempDir()
   339  	dist := filepath.Join(folder, "dist")
   340  	binPath := filepath.Join(dist, "mybin", "mybin")
   341  
   342  	ctx := testctx.NewWithCfg(config.Project{
   343  		ProjectName: "mybin",
   344  		Dist:        dist,
   345  		Artifactories: []config.Upload{
   346  			{
   347  				Name: "production",
   348  				Mode: "binary",
   349  				// This template is not correct and should fail
   350  				Target:   "http://storage.company.com/example-repo-local/{{.Name}",
   351  				Username: "deployuser",
   352  			},
   353  		},
   354  		Archives: []config.Archive{{}},
   355  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   356  	})
   357  	ctx.Artifacts.Add(&artifact.Artifact{
   358  		Name:   "mybin",
   359  		Path:   binPath,
   360  		Goarch: "amd64",
   361  		Goos:   "darwin",
   362  		Type:   artifact.UploadableBinary,
   363  	})
   364  
   365  	require.NoError(t, Pipe{}.Default(ctx))
   366  	testlib.RequireTemplateError(t, Pipe{}.Publish(ctx))
   367  }
   368  
   369  func TestRunPipe_BadCredentials(t *testing.T) {
   370  	setup()
   371  	defer teardown()
   372  
   373  	folder := t.TempDir()
   374  	dist := filepath.Join(folder, "dist")
   375  	require.NoError(t, os.Mkdir(dist, 0o755))
   376  	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0o755))
   377  	binPath := filepath.Join(dist, "mybin", "mybin")
   378  	d1 := []byte("hello\ngo\n")
   379  	require.NoError(t, os.WriteFile(binPath, d1, 0o666))
   380  
   381  	// Dummy artifactories
   382  	mux.HandleFunc("/example-repo-local/mybin/darwin/amd64/mybin", func(w http.ResponseWriter, r *http.Request) {
   383  		requireMethodPut(t, r)
   384  		requireHeader(t, r, "Content-Length", "9")
   385  		// Basic auth of user "deployuser" with secret "deployuser-secret"
   386  		requireHeader(t, r, "Authorization", "Basic ZGVwbG95dXNlcjpkZXBsb3l1c2VyLXNlY3JldA==")
   387  
   388  		w.WriteHeader(http.StatusUnauthorized)
   389  		fmt.Fprint(w, `{
   390  			"errors" : [ {
   391  			  "status" : 401,
   392  			  "message" : "Bad credentials"
   393  			} ]
   394  		  }`)
   395  	})
   396  
   397  	ctx := testctx.NewWithCfg(config.Project{
   398  		ProjectName: "mybin",
   399  		Dist:        dist,
   400  		Artifactories: []config.Upload{
   401  			{
   402  				Name:     "production",
   403  				Mode:     "binary",
   404  				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
   405  				Username: "deployuser",
   406  			},
   407  		},
   408  		Archives: []config.Archive{{}},
   409  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   410  	})
   411  	ctx.Artifacts.Add(&artifact.Artifact{
   412  		Name:   "mybin",
   413  		Path:   binPath,
   414  		Goarch: "amd64",
   415  		Goos:   "darwin",
   416  		Type:   artifact.UploadableBinary,
   417  	})
   418  
   419  	require.NoError(t, Pipe{}.Default(ctx))
   420  	err := Pipe{}.Publish(ctx)
   421  	require.ErrorContains(t, err, "Bad credentials")
   422  }
   423  
   424  func TestRunPipe_UnparsableErrorResponse(t *testing.T) {
   425  	setup()
   426  	defer teardown()
   427  
   428  	folder := t.TempDir()
   429  	dist := filepath.Join(folder, "dist")
   430  	require.NoError(t, os.Mkdir(dist, 0o755))
   431  	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0o755))
   432  	binPath := filepath.Join(dist, "mybin", "mybin")
   433  	d1 := []byte("hello\ngo\n")
   434  	require.NoError(t, os.WriteFile(binPath, d1, 0o666))
   435  
   436  	// Dummy artifactories
   437  	mux.HandleFunc("/example-repo-local/mybin/darwin/amd64/mybin", func(w http.ResponseWriter, r *http.Request) {
   438  		requireMethodPut(t, r)
   439  		requireHeader(t, r, "Content-Length", "9")
   440  		// Basic auth of user "deployuser" with secret "deployuser-secret"
   441  		requireHeader(t, r, "Authorization", "Basic ZGVwbG95dXNlcjpkZXBsb3l1c2VyLXNlY3JldA==")
   442  
   443  		w.WriteHeader(http.StatusUnauthorized)
   444  		fmt.Fprint(w, `<body><h1>error</h1></body>`)
   445  	})
   446  
   447  	ctx := testctx.NewWithCfg(config.Project{
   448  		ProjectName: "mybin",
   449  		Dist:        dist,
   450  		Artifactories: []config.Upload{
   451  			{
   452  				Name:     "production",
   453  				Mode:     "binary",
   454  				Target:   fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL),
   455  				Username: "deployuser",
   456  			},
   457  		},
   458  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   459  		Archives: []config.Archive{{}},
   460  	})
   461  	ctx.Artifacts.Add(&artifact.Artifact{
   462  		Name:   "mybin",
   463  		Path:   binPath,
   464  		Goarch: "amd64",
   465  		Goos:   "darwin",
   466  		Type:   artifact.UploadableBinary,
   467  	})
   468  
   469  	require.NoError(t, Pipe{}.Default(ctx))
   470  	require.EqualError(t, Pipe{}.Publish(ctx), `production: artifactory: upload failed: unexpected error: invalid character '<' looking for beginning of value: <body><h1>error</h1></body>`)
   471  }
   472  
   473  func TestRunPipe_FileNotFound(t *testing.T) {
   474  	ctx := testctx.NewWithCfg(config.Project{
   475  		ProjectName: "mybin",
   476  		Dist:        "archivetest/dist",
   477  		Artifactories: []config.Upload{
   478  			{
   479  				Name:     "production",
   480  				Mode:     "binary",
   481  				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   482  				Username: "deployuser",
   483  			},
   484  		},
   485  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   486  		Archives: []config.Archive{{}},
   487  	})
   488  	ctx.Artifacts.Add(&artifact.Artifact{
   489  		Name:   "mybin",
   490  		Path:   "archivetest/dist/mybin/mybin",
   491  		Goarch: "amd64",
   492  		Goos:   "darwin",
   493  		Type:   artifact.UploadableBinary,
   494  	})
   495  
   496  	require.NoError(t, Pipe{}.Default(ctx))
   497  	require.ErrorIs(t, Pipe{}.Publish(ctx), os.ErrNotExist)
   498  }
   499  
   500  func TestRunPipe_UnparsableTarget(t *testing.T) {
   501  	folder := t.TempDir()
   502  	dist := filepath.Join(folder, "dist")
   503  	require.NoError(t, os.Mkdir(dist, 0o755))
   504  	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0o755))
   505  	binPath := filepath.Join(dist, "mybin", "mybin")
   506  	d1 := []byte("hello\ngo\n")
   507  	require.NoError(t, os.WriteFile(binPath, d1, 0o666))
   508  
   509  	ctx := testctx.NewWithCfg(config.Project{
   510  		ProjectName: "mybin",
   511  		Dist:        dist,
   512  		Artifactories: []config.Upload{
   513  			{
   514  				Name:     "production",
   515  				Mode:     "binary",
   516  				Target:   "://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   517  				Username: "deployuser",
   518  			},
   519  		},
   520  		Archives: []config.Archive{{}},
   521  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   522  	})
   523  	ctx.Artifacts.Add(&artifact.Artifact{
   524  		Name:   "mybin",
   525  		Path:   binPath,
   526  		Goarch: "amd64",
   527  		Goos:   "darwin",
   528  		Type:   artifact.UploadableBinary,
   529  	})
   530  
   531  	require.NoError(t, Pipe{}.Default(ctx))
   532  	require.EqualError(t, Pipe{}.Publish(ctx), `production: artifactory: upload failed: parse "://artifacts.company.com/example-repo-local/mybin/darwin/amd64/mybin": missing protocol scheme`)
   533  }
   534  
   535  func TestRunPipe_DirUpload(t *testing.T) {
   536  	folder := t.TempDir()
   537  	dist := filepath.Join(folder, "dist")
   538  	require.NoError(t, os.Mkdir(dist, 0o755))
   539  	require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0o755))
   540  	binPath := filepath.Join(dist, "mybin")
   541  
   542  	ctx := testctx.NewWithCfg(config.Project{
   543  		ProjectName: "mybin",
   544  		Dist:        dist,
   545  		Artifactories: []config.Upload{
   546  			{
   547  				Name:     "production",
   548  				Mode:     "binary",
   549  				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   550  				Username: "deployuser",
   551  			},
   552  		},
   553  		Archives: []config.Archive{{}},
   554  		Env:      []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   555  	})
   556  	ctx.Artifacts.Add(&artifact.Artifact{
   557  		Name:   "mybin",
   558  		Path:   filepath.Dir(binPath),
   559  		Goarch: "amd64",
   560  		Goos:   "darwin",
   561  		Type:   artifact.UploadableBinary,
   562  	})
   563  
   564  	require.NoError(t, Pipe{}.Default(ctx))
   565  	require.EqualError(t, Pipe{}.Publish(ctx), `artifactory: upload failed: the asset to upload can't be a directory`)
   566  }
   567  
   568  func TestDescription(t *testing.T) {
   569  	require.NotEmpty(t, Pipe{}.String())
   570  }
   571  
   572  func TestArtifactoriesWithoutTarget(t *testing.T) {
   573  	ctx := testctx.NewWithCfg(config.Project{
   574  		Artifactories: []config.Upload{
   575  			{
   576  				Name:     "production",
   577  				Username: "deployuser",
   578  			},
   579  		},
   580  		Env: []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   581  	})
   582  	require.NoError(t, Pipe{}.Default(ctx))
   583  	testlib.AssertSkipped(t, Pipe{}.Publish(ctx))
   584  }
   585  
   586  func TestArtifactoriesWithoutUsername(t *testing.T) {
   587  	ctx := testctx.NewWithCfg(config.Project{
   588  		Artifactories: []config.Upload{
   589  			{
   590  				Name:   "production",
   591  				Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   592  			},
   593  		},
   594  		Env: []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   595  	})
   596  	require.NoError(t, Pipe{}.Default(ctx))
   597  	testlib.AssertSkipped(t, Pipe{}.Publish(ctx))
   598  }
   599  
   600  func TestArtifactoriesWithoutName(t *testing.T) {
   601  	ctx := testctx.NewWithCfg(config.Project{
   602  		Artifactories: []config.Upload{
   603  			{
   604  				Username: "deployuser",
   605  				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   606  			},
   607  		},
   608  	})
   609  	require.NoError(t, Pipe{}.Default(ctx))
   610  	testlib.AssertSkipped(t, Pipe{}.Publish(ctx))
   611  }
   612  
   613  func TestArtifactoriesWithoutSecret(t *testing.T) {
   614  	ctx := testctx.NewWithCfg(config.Project{
   615  		Artifactories: []config.Upload{
   616  			{
   617  				Name:     "production",
   618  				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   619  				Username: "deployuser",
   620  			},
   621  		},
   622  	})
   623  	require.NoError(t, Pipe{}.Default(ctx))
   624  	testlib.AssertSkipped(t, Pipe{}.Publish(ctx))
   625  }
   626  
   627  func TestArtifactoriesWithInvalidMode(t *testing.T) {
   628  	ctx := testctx.NewWithCfg(config.Project{
   629  		Artifactories: []config.Upload{
   630  			{
   631  				Name:     "production",
   632  				Mode:     "does-not-exists",
   633  				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   634  				Username: "deployuser",
   635  			},
   636  		},
   637  		Env: []string{"ARTIFACTORY_PRODUCTION_SECRET=deployuser-secret"},
   638  	})
   639  	require.NoError(t, Pipe{}.Default(ctx))
   640  	require.Error(t, Pipe{}.Publish(ctx))
   641  }
   642  
   643  func TestDefault(t *testing.T) {
   644  	ctx := testctx.NewWithCfg(config.Project{
   645  		Artifactories: []config.Upload{
   646  			{
   647  				Name:     "production",
   648  				Target:   "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}",
   649  				Username: "deployuser",
   650  			},
   651  		},
   652  	})
   653  
   654  	require.NoError(t, Pipe{}.Default(ctx))
   655  	require.Len(t, ctx.Config.Artifactories, 1)
   656  	artifactory := ctx.Config.Artifactories[0]
   657  	require.Equal(t, "archive", artifactory.Mode)
   658  }
   659  
   660  func TestDefaultNoArtifactories(t *testing.T) {
   661  	ctx := testctx.NewWithCfg(config.Project{
   662  		Artifactories: []config.Upload{},
   663  	})
   664  	require.NoError(t, Pipe{}.Default(ctx))
   665  	require.Empty(t, ctx.Config.Artifactories)
   666  }
   667  
   668  func TestDefaultSet(t *testing.T) {
   669  	ctx := testctx.NewWithCfg(config.Project{
   670  		Artifactories: []config.Upload{
   671  			{
   672  				Mode:           "custom",
   673  				ChecksumHeader: "foo",
   674  			},
   675  		},
   676  	})
   677  	require.NoError(t, Pipe{}.Default(ctx))
   678  	require.Len(t, ctx.Config.Artifactories, 1)
   679  	artifactory := ctx.Config.Artifactories[0]
   680  	require.Equal(t, "custom", artifactory.Mode)
   681  	require.Equal(t, "foo", artifactory.ChecksumHeader)
   682  }
   683  
   684  func TestSkip(t *testing.T) {
   685  	t.Run("skip", func(t *testing.T) {
   686  		require.True(t, Pipe{}.Skip(testctx.New()))
   687  	})
   688  
   689  	t.Run("dont skip", func(t *testing.T) {
   690  		ctx := testctx.NewWithCfg(config.Project{
   691  			Artifactories: []config.Upload{{}},
   692  		})
   693  		require.False(t, Pipe{}.Skip(ctx))
   694  	})
   695  }