github.com/windmeup/goreleaser@v1.21.95/internal/pipe/upload/upload_test.go (about)

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