-
-
Notifications
You must be signed in to change notification settings - Fork 912
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
this adds a new root-level `upx` config, so users can pack their binaries with upx :) --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
- Loading branch information
Showing
9 changed files
with
315 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package upx | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
|
||
"github.com/caarlos0/log" | ||
"github.com/docker/go-units" | ||
"github.com/goreleaser/goreleaser/internal/artifact" | ||
"github.com/goreleaser/goreleaser/internal/pipe" | ||
"github.com/goreleaser/goreleaser/internal/semerrgroup" | ||
"github.com/goreleaser/goreleaser/pkg/config" | ||
"github.com/goreleaser/goreleaser/pkg/context" | ||
) | ||
|
||
type Pipe struct{} | ||
|
||
func (Pipe) String() string { return "upx" } | ||
func (Pipe) Default(ctx *context.Context) error { | ||
for i := range ctx.Config.UPXs { | ||
upx := &ctx.Config.UPXs[i] | ||
if upx.Binary == "" { | ||
upx.Binary = "upx" | ||
} | ||
} | ||
return nil | ||
} | ||
func (Pipe) Skip(ctx *context.Context) bool { return len(ctx.Config.UPXs) == 0 } | ||
func (Pipe) Run(ctx *context.Context) error { | ||
g := semerrgroup.NewSkipAware(semerrgroup.New(ctx.Parallelism)) | ||
for _, upx := range ctx.Config.UPXs { | ||
upx := upx | ||
if !upx.Enabled { | ||
return pipe.Skip("upx is not enabled") | ||
} | ||
if _, err := exec.LookPath(upx.Binary); err != nil { | ||
return pipe.Skipf("%s not found in PATH", upx.Binary) | ||
} | ||
for _, bin := range findBinaries(ctx, upx) { | ||
bin := bin | ||
g.Go(func() error { | ||
sizeBefore := sizeOf(bin.Path) | ||
args := []string{ | ||
"--quiet", | ||
} | ||
switch upx.Compress { | ||
case "best": | ||
args = append(args, "--best") | ||
case "": | ||
default: | ||
args = append(args, "-"+upx.Compress) | ||
} | ||
if upx.LZMA { | ||
args = append(args, "--lzma") | ||
} | ||
if upx.Brute { | ||
args = append(args, "--brute") | ||
} | ||
args = append(args, bin.Path) | ||
out, err := exec.CommandContext(ctx, "upx", args...).CombinedOutput() | ||
if err != nil { | ||
for _, ke := range knownExceptions { | ||
if strings.Contains(string(out), ke) { | ||
log.WithField("binary", bin.Path). | ||
WithField("exception", ke). | ||
Warn("could not pack") | ||
return nil | ||
} | ||
} | ||
return fmt.Errorf("could not pack %s: %w: %s", bin.Path, err, string(out)) | ||
} | ||
|
||
sizeAfter := sizeOf(bin.Path) | ||
|
||
log. | ||
WithField("before", units.HumanSize(float64(sizeBefore))). | ||
WithField("after", units.HumanSize(float64(sizeAfter))). | ||
WithField("ratio", fmt.Sprintf("%d%%", (sizeAfter*100)/sizeBefore)). | ||
WithField("binary", bin.Path). | ||
Info("packed") | ||
|
||
return nil | ||
}) | ||
} | ||
} | ||
return g.Wait() | ||
} | ||
|
||
var knownExceptions = []string{ | ||
"CantPackException", | ||
"AlreadyPackedException", | ||
"NotCompressibleException", | ||
} | ||
|
||
func findBinaries(ctx *context.Context, upx config.UPX) []*artifact.Artifact { | ||
filters := []artifact.Filter{ | ||
artifact.Or( | ||
artifact.ByType(artifact.Binary), | ||
artifact.ByType(artifact.UniversalBinary), | ||
), | ||
} | ||
if len(upx.IDs) > 0 { | ||
filters = append(filters, artifact.ByIDs(upx.IDs...)) | ||
} | ||
return ctx.Artifacts.Filter(artifact.And(filters...)).List() | ||
} | ||
|
||
func sizeOf(name string) int64 { | ||
st, err := os.Stat(name) | ||
if err != nil { | ||
return 0 | ||
} | ||
return st.Size() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package upx | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/goreleaser/goreleaser/internal/artifact" | ||
"github.com/goreleaser/goreleaser/internal/testctx" | ||
"github.com/goreleaser/goreleaser/internal/testlib" | ||
"github.com/goreleaser/goreleaser/pkg/config" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestStringer(t *testing.T) { | ||
require.NotEmpty(t, Pipe{}.String()) | ||
} | ||
|
||
func TestDefault(t *testing.T) { | ||
ctx := testctx.NewWithCfg(config.Project{ | ||
UPXs: []config.UPX{ | ||
{}, | ||
}, | ||
}) | ||
require.NoError(t, Pipe{}.Default(ctx)) | ||
require.Len(t, ctx.Config.UPXs, 1) | ||
require.Equal(t, "upx", ctx.Config.UPXs[0].Binary) | ||
} | ||
|
||
func TestSkip(t *testing.T) { | ||
t.Run("skip", func(t *testing.T) { | ||
ctx := testctx.NewWithCfg(config.Project{ | ||
UPXs: []config.UPX{}, | ||
}) | ||
require.True(t, Pipe{}.Skip(ctx)) | ||
}) | ||
t.Run("do not skip", func(t *testing.T) { | ||
ctx := testctx.NewWithCfg(config.Project{ | ||
UPXs: []config.UPX{ | ||
{}, | ||
}, | ||
}) | ||
require.False(t, Pipe{}.Skip(ctx)) | ||
}) | ||
} | ||
|
||
func TestRun(t *testing.T) { | ||
ctx := testctx.NewWithCfg(config.Project{ | ||
UPXs: []config.UPX{ | ||
{ | ||
Enabled: true, | ||
IDs: []string{"1"}, | ||
}, | ||
{ | ||
Enabled: true, | ||
IDs: []string{"2"}, | ||
Compress: "best", | ||
}, | ||
{ | ||
Enabled: true, | ||
IDs: []string{"3"}, | ||
Compress: "9", | ||
}, | ||
{ | ||
Enabled: true, | ||
IDs: []string{"4"}, | ||
Compress: "8", | ||
LZMA: true, | ||
}, | ||
{ | ||
Enabled: true, | ||
IDs: []string{"5"}, | ||
Brute: true, | ||
}, | ||
}, | ||
}) | ||
|
||
tmp := t.TempDir() | ||
main := filepath.Join(tmp, "main.go") | ||
require.NoError(t, os.WriteFile(main, []byte("package main\nfunc main(){ println(1) }"), 0o644)) | ||
|
||
for _, goos := range []string{"linux", "windows", "darwin"} { | ||
for _, goarch := range []string{"386", "amd64", "arm64"} { | ||
ext := "" | ||
if goos == "windows" { | ||
ext = ".exe" | ||
} | ||
path := filepath.Join(tmp, fmt.Sprintf("bin_%s_%s%s", goos, goarch, ext)) | ||
cmd := exec.Command("go", "build", "-o", path, main) | ||
cmd.Env = append([]string{ | ||
"CGO_ENABLED=0", | ||
"GOOS=" + goos, | ||
"GOARCH=" + goarch, | ||
}, cmd.Environ()...) | ||
if cmd.Run() != nil { | ||
// ignore unsupported arches | ||
continue | ||
} | ||
|
||
for i := 1; i <= 5; i++ { | ||
ctx.Artifacts.Add(&artifact.Artifact{ | ||
Name: "bin", | ||
Path: path, | ||
Goos: goos, | ||
Goarch: goarch, | ||
Type: artifact.Binary, | ||
Extra: map[string]any{ | ||
artifact.ExtraID: fmt.Sprintf("%d", i), | ||
}, | ||
}) | ||
} | ||
|
||
} | ||
} | ||
|
||
require.NoError(t, Pipe{}.Default(ctx)) | ||
require.NoError(t, Pipe{}.Run(ctx)) | ||
} | ||
|
||
func TestDisabled(t *testing.T) { | ||
ctx := testctx.NewWithCfg(config.Project{ | ||
UPXs: []config.UPX{ | ||
{}, | ||
}, | ||
}) | ||
testlib.AssertSkipped(t, Pipe{}.Run(ctx)) | ||
} | ||
|
||
func TestUpxNotInstalled(t *testing.T) { | ||
ctx := testctx.NewWithCfg(config.Project{ | ||
UPXs: []config.UPX{ | ||
{ | ||
Enabled: true, | ||
Binary: "fakeupx", | ||
}, | ||
}, | ||
}) | ||
testlib.AssertSkipped(t, Pipe{}.Run(ctx)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# UPX | ||
|
||
> Since: v1.18 | ||
Having small binary sizes are important, and Go is known for generating rather | ||
big binaries. | ||
|
||
GoReleaser has had `-s -w` as default `ldflags` since the beginning, which help | ||
shaving off some bytes, but if you want to shave it even more, [`upx`][upx] is | ||
the _de facto_ tool for the job. | ||
|
||
[upx]: https://upx.github.io/ | ||
|
||
GoReleaser has been able to integrate with it via custom [build hooks][bhooks], | ||
and now UPX has its own configuration section: | ||
|
||
```yaml | ||
# .goreleaser.yaml | ||
upx: | ||
- | ||
# Whether to enable it or not. | ||
enabled: true | ||
|
||
# Filter by build ID. | ||
ids: [ build1, build2 ] | ||
|
||
# Compress argument. | ||
# Valid options are from '1' (faster) to '9' (better), and 'best'. | ||
compress: best | ||
|
||
# Whether to try LZMA (slower). | ||
lzma: true | ||
|
||
# Whether to try all methods and filters (slow). | ||
brute: true | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters