github.com/triarius/goreleaser@v1.12.5/internal/pipe/upload/upload_test.go (about)

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