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