diff --git a/README.md b/README.md index 74bb1f4b6..c3e2a6b70 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,9 @@ archive: # The default is `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}` name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - # Archive format. Valid options are `tar.gz` and `zip`. + # Archive format. Valid options are `tar.gz`, `zip` and `binary`. + # If format is `binary` no archives are created and the binaries are instead uploaded directly. + # In that case name_template the below specified files are ignored. # Default is `tar.gz` format: zip diff --git a/context/context.go b/context/context.go index d36195401..be6bb2a6e 100644 --- a/context/context.go +++ b/context/context.go @@ -43,8 +43,7 @@ var foldersLock sync.Mutex func (ctx *Context) AddArtifact(file string) { artifactsLock.Lock() defer artifactsLock.Unlock() - file = strings.TrimPrefix(file, ctx.Config.Dist) - file = strings.Replace(file, "/", "", -1) + file = strings.TrimPrefix(file, ctx.Config.Dist+"/") ctx.Artifacts = append(ctx.Artifacts, file) log.WithField("artifact", file).Info("new artifact") } diff --git a/internal/name/name.go b/internal/name/name.go index 7bf006ee7..a58b0b8c9 100644 --- a/internal/name/name.go +++ b/internal/name/name.go @@ -6,6 +6,7 @@ import ( "bytes" "text/template" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" ) @@ -19,20 +20,42 @@ type nameData struct { ProjectName string } +// ForBuild return the name for the given context, goos, goarch, goarm and +// build, using the build.Binary property instead of project_name. +func ForBuild(ctx *context.Context, build config.Build, goos, goarch, goarm string) (string, error) { + return apply( + nameData{ + Os: replace(ctx.Config.Archive.Replacements, goos), + Arch: replace(ctx.Config.Archive.Replacements, goarch), + Arm: replace(ctx.Config.Archive.Replacements, goarm), + Version: ctx.Version, + Tag: ctx.Git.CurrentTag, + Binary: build.Binary, + ProjectName: build.Binary, + }, + ctx.Config.Archive.NameTemplate, + ) +} + // For returns the name for the given context, goos, goarch and goarm. func For(ctx *context.Context, goos, goarch, goarm string) (string, error) { - var data = nameData{ - Os: replace(ctx.Config.Archive.Replacements, goos), - Arch: replace(ctx.Config.Archive.Replacements, goarch), - Arm: replace(ctx.Config.Archive.Replacements, goarm), - Version: ctx.Version, - Tag: ctx.Git.CurrentTag, - Binary: ctx.Config.ProjectName, - ProjectName: ctx.Config.ProjectName, - } + return apply( + nameData{ + Os: replace(ctx.Config.Archive.Replacements, goos), + Arch: replace(ctx.Config.Archive.Replacements, goarch), + Arm: replace(ctx.Config.Archive.Replacements, goarm), + Version: ctx.Version, + Tag: ctx.Git.CurrentTag, + Binary: ctx.Config.ProjectName, + ProjectName: ctx.Config.ProjectName, + }, + ctx.Config.Archive.NameTemplate, + ) +} +func apply(data nameData, templateStr string) (string, error) { var out bytes.Buffer - t, err := template.New(data.Binary).Parse(ctx.Config.Archive.NameTemplate) + t, err := template.New(data.ProjectName).Parse(templateStr) if err != nil { return "", err } diff --git a/internal/name/name_test.go b/internal/name/name_test.go index 80c5f7eff..a303a5921 100644 --- a/internal/name/name_test.go +++ b/internal/name/name_test.go @@ -35,6 +35,31 @@ func TestNameFor(t *testing.T) { assert.Equal("test_Darwin_x86_64_v1.2.3_1.2.3", name) } +func TestNameForBuild(t *testing.T) { + assert := assert.New(t) + + var ctx = &context.Context{ + Config: config.Project{ + Archive: config.Archive{ + NameTemplate: "{{.Binary}}_{{.Os}}_{{.Arch}}_{{.Tag}}_{{.Version}}", + Replacements: map[string]string{ + "darwin": "Darwin", + "amd64": "x86_64", + }, + }, + ProjectName: "test", + }, + Version: "1.2.3", + Git: context.GitInfo{ + CurrentTag: "v1.2.3", + }, + } + + name, err := ForBuild(ctx, config.Build{Binary: "foo"}, "darwin", "amd64", "") + assert.NoError(err) + assert.Equal("foo_Darwin_x86_64_v1.2.3_1.2.3", name) +} + func TestInvalidNameTemplate(t *testing.T) { var assert = assert.New(t) var ctx = &context.Context{ diff --git a/pipeline/archive/archive.go b/pipeline/archive/archive.go index 1806538a6..707b63b26 100644 --- a/pipeline/archive/archive.go +++ b/pipeline/archive/archive.go @@ -31,6 +31,9 @@ func (Pipe) Run(ctx *context.Context) error { folder := folder platform := platform g.Go(func() error { + if ctx.Config.Archive.Format == "binary" { + return skip(ctx, platform, folder) + } return create(ctx, platform, folder) }) } @@ -72,6 +75,20 @@ func create(ctx *context.Context, platform, name string) error { return nil } +func skip(ctx *context.Context, platform, name string) error { + var path = filepath.Join(ctx.Config.Dist, name) + binaries, err := ioutil.ReadDir(path) + if err != nil { + return err + } + log.WithField("platform", platform).Debugf("found %v binaries", len(binaries)) + for _, binary := range binaries { + log.WithField("binary", binary.Name()).Info("skip archiving") + ctx.AddArtifact(filepath.Join(path+"/", binary.Name())) + } + return nil +} + func findFiles(ctx *context.Context) (result []string, err error) { for _, glob := range ctx.Config.Archive.Files { files, err := zglob.Glob(glob) diff --git a/pipeline/archive/archive_test.go b/pipeline/archive/archive_test.go index 38e2a5d95..c66431516 100644 --- a/pipeline/archive/archive_test.go +++ b/pipeline/archive/archive_test.go @@ -63,6 +63,47 @@ func TestRunPipe(t *testing.T) { } } +func TestRunPipeBinary(t *testing.T) { + var assert = assert.New(t) + folder, err := ioutil.TempDir("", "archivetest") + assert.NoError(err) + current, err := os.Getwd() + assert.NoError(err) + assert.NoError(os.Chdir(folder)) + defer func() { + assert.NoError(os.Chdir(current)) + }() + var dist = filepath.Join(folder, "dist") + assert.NoError(os.Mkdir(dist, 0755)) + assert.NoError(os.Mkdir(filepath.Join(dist, "mybin_darwin"), 0755)) + assert.NoError(os.Mkdir(filepath.Join(dist, "mybin_win"), 0755)) + _, err = os.Create(filepath.Join(dist, "mybin_darwin", "mybin")) + assert.NoError(err) + _, err = os.Create(filepath.Join(dist, "mybin_win", "mybin.exe")) + assert.NoError(err) + _, err = os.Create(filepath.Join(folder, "README.md")) + assert.NoError(err) + var ctx = &context.Context{ + Folders: map[string]string{ + "darwinamd64": "mybin_darwin", + "windowsamd64": "mybin_win", + }, + Config: config.Project{ + Dist: dist, + Builds: []config.Build{ + {Binary: "mybin"}, + }, + Archive: config.Archive{ + Format: "binary", + }, + }, + } + assert.NoError(Pipe{}.Run(ctx)) + assert.Contains(ctx.Artifacts, "mybin_darwin/mybin") + assert.Contains(ctx.Artifacts, "mybin_win/mybin.exe") + assert.Len(ctx.Artifacts, 2) +} + func TestRunPipeDistRemoved(t *testing.T) { var assert = assert.New(t) var ctx = &context.Context{ diff --git a/pipeline/brew/brew.go b/pipeline/brew/brew.go index 5aceddeb6..4e47df370 100644 --- a/pipeline/brew/brew.go +++ b/pipeline/brew/brew.go @@ -107,6 +107,10 @@ func doRun(ctx *context.Context, client client.Client) error { log.Warn("skipped because release is marked as draft") return nil } + if ctx.Config.Archive.Format == "binary" { + log.Info("skipped because archive format is binary") + return nil + } var path = filepath.Join(ctx.Config.Brew.Folder, ctx.Config.ProjectName+".rb") log.WithField("formula", path). WithField("repo", ctx.Config.Brew.GitHub.String()). diff --git a/pipeline/brew/brew_test.go b/pipeline/brew/brew_test.go index e9392f1c6..8b3aa4b5e 100644 --- a/pipeline/brew/brew_test.go +++ b/pipeline/brew/brew_test.go @@ -208,9 +208,25 @@ func TestRunPipeBrewNotSetup(t *testing.T) { assert.False(client.CreatedFile) } -func TestRunPipeNoDarwinBuild(t *testing.T) { +func TestRunPipeBinaryRelease(t *testing.T) { assert := assert.New(t) - var ctx = &context.Context{} + var ctx = &context.Context{ + Publish: true, + Config: config.Project{ + Archive: config.Archive{ + Format: "binary", + }, + Brew: config.Homebrew{ + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, + }, + }, + Folders: map[string]string{ + "darwinamd64": "bin", + }, + } client := &DummyClient{} assert.NoError(doRun(ctx, client)) assert.False(client.CreatedFile) @@ -247,6 +263,20 @@ func TestRunPipeDraftRelease(t *testing.T) { assert.False(client.CreatedFile) } +func TestRunPipeFormatBinary(t *testing.T) { + assert := assert.New(t) + var ctx = &context.Context{ + Config: config.Project{ + Archive: config.Archive{ + Format: "binary", + }, + }, + } + client := &DummyClient{} + assert.NoError(doRun(ctx, client)) + assert.False(client.CreatedFile) +} + type DummyClient struct { CreatedFile bool Content string diff --git a/pipeline/build/build.go b/pipeline/build/build.go index 49a1e4874..a8db2ba89 100644 --- a/pipeline/build/build.go +++ b/pipeline/build/build.go @@ -79,6 +79,17 @@ func doBuild(ctx *context.Context, build config.Build, target buildTarget) error folder, build.Binary+ext.For(target.goos), ) + if ctx.Config.Archive.Format == "binary" { + bin, err := name.ForBuild(ctx, build, target.goos, target.goarch, target.goarm) + if err != nil { + return err + } + binary = filepath.Join( + ctx.Config.Dist, + folder, + bin+ext.For(target.goos), + ) + } log.WithField("binary", binary).Info("building") cmd := []string{"go", "build"} if build.Flags != "" { diff --git a/pipeline/build/build_test.go b/pipeline/build/build_test.go index e2f1938b9..7cf6d74f9 100644 --- a/pipeline/build/build_test.go +++ b/pipeline/build/build_test.go @@ -81,6 +81,38 @@ func TestRunFullPipe(t *testing.T) { assert.True(exists(post), post) } +func TestRunPipeFormatBinary(t *testing.T) { + assert := assert.New(t) + folder, err := ioutil.TempDir("", "goreleasertest") + assert.NoError(err) + var binary = filepath.Join(folder, "binary-testing") + var config = config.Project{ + ProjectName: "testing", + Dist: folder, + Builds: []config.Build{ + { + Binary: "testing", + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, + }, + }, + Archive: config.Archive{ + Format: "binary", + NameTemplate: "binary-{{.Binary}}", + }, + } + var ctx = &context.Context{ + Config: config, + Folders: map[string]string{}, + } + assert.NoError(Pipe{}.Run(ctx)) + assert.True(exists(binary)) +} + func TestRunPipeArmBuilds(t *testing.T) { assert := assert.New(t) folder, err := ioutil.TempDir("", "goreleasertest") @@ -160,26 +192,30 @@ func TestRunPipeWithInvalidOS(t *testing.T) { func TestRunInvalidNametemplate(t *testing.T) { var assert = assert.New(t) - var ctx = &context.Context{ - Config: config.Project{ - Builds: []config.Build{ - { - Binary: "nametest", - Flags: "-v", - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, + for _, format := range []string{"tar.gz", "zip", "binary"} { + var ctx = &context.Context{ + Config: config.Project{ + ProjectName: "nameeeee", + Builds: []config.Build{ + { + Binary: "namet{{.est}", + Flags: "-v", + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, }, }, + Archive: config.Archive{ + Format: format, + NameTemplate: "{{.Binary}", + }, }, - Archive: config.Archive{ - NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}", - }, - }, + } + assert.Error(Pipe{}.Run(ctx)) } - assert.Error(Pipe{}.Run(ctx)) } func TestRunInvalidLdflags(t *testing.T) { diff --git a/pipeline/fpm/fpm.go b/pipeline/fpm/fpm.go index 0f8c8c1cf..4a99bab9a 100644 --- a/pipeline/fpm/fpm.go +++ b/pipeline/fpm/fpm.go @@ -31,6 +31,10 @@ func (Pipe) Run(ctx *context.Context) error { log.Info("no output formats configured, skipping") return nil } + if ctx.Config.Archive.Format == "binary" { + log.Info("skipped because archive format is binary") + return nil + } _, err := exec.LookPath("fpm") if err != nil { return ErrNoFPM diff --git a/pipeline/fpm/fpm_test.go b/pipeline/fpm/fpm_test.go index e376c6614..5bad8b24a 100644 --- a/pipeline/fpm/fpm_test.go +++ b/pipeline/fpm/fpm_test.go @@ -23,6 +23,21 @@ func TestRunPipeNoFormats(t *testing.T) { assert.NoError(Pipe{}.Run(ctx)) } +func TestRunPipeFormatBinary(t *testing.T) { + var assert = assert.New(t) + var ctx = &context.Context{ + Config: config.Project{ + FPM: config.FPM{ + Formats: []string{"deb"}, + }, + Archive: config.Archive{ + Format: "binary", + }, + }, + } + assert.NoError(Pipe{}.Run(ctx)) +} + func TestRunPipe(t *testing.T) { var assert = assert.New(t) folder, err := ioutil.TempDir("", "archivetest") diff --git a/pipeline/release/release.go b/pipeline/release/release.go index 1e4ccfaaa..23a1fe92a 100644 --- a/pipeline/release/release.go +++ b/pipeline/release/release.go @@ -58,6 +58,7 @@ func upload(ctx *context.Context, client client.Client, releaseID int, artifact return err } defer func() { _ = file.Close() }() - log.WithField("file", file.Name()).Info("uploading") - return client.Upload(ctx, releaseID, artifact, file) + _, name := filepath.Split(path) + log.WithField("file", file.Name()).WithField("name", name).Info("uploading to release") + return client.Upload(ctx, releaseID, name, file) } diff --git a/pipeline/release/release_test.go b/pipeline/release/release_test.go index 5810ee63b..067cbf552 100644 --- a/pipeline/release/release_test.go +++ b/pipeline/release/release_test.go @@ -46,6 +46,8 @@ func TestRunPipe(t *testing.T) { assert.NoError(doRun(ctx, client)) assert.True(client.CreatedRelease) assert.True(client.UploadedFile) + assert.Contains("bin.deb", client.UploadedFileNames) + assert.Contains("bin.tar.gz", client.UploadedFileNames) } func TestRunPipeReleaseCreationFailed(t *testing.T) { @@ -141,6 +143,7 @@ type DummyClient struct { FailToUpload bool CreatedRelease bool UploadedFile bool + UploadedFileNames []string } func (client *DummyClient) CreateRelease(ctx *context.Context, body string) (releaseID int, err error) { @@ -160,5 +163,6 @@ func (client *DummyClient) Upload(ctx *context.Context, releaseID int, name stri return errors.New("upload failed") } client.UploadedFile = true + client.UploadedFileNames = append(client.UploadedFileNames, name) return }