mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-16 03:52:12 +02:00
feat: support setting mod_timestamp in universalbinary (#4172)
refs #4167 --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
52792f4a15
commit
3c1ebe82cd
@ -7,6 +7,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/caarlos0/go-shellwords"
|
||||
"github.com/caarlos0/log"
|
||||
@ -134,10 +136,13 @@ const (
|
||||
|
||||
// heavily based on https://github.com/randall77/makefat
|
||||
func makeUniversalBinary(ctx *context.Context, opts *build.Options, unibin config.UniversalBinary) error {
|
||||
name, err := tmpl.New(ctx).Apply(unibin.NameTemplate)
|
||||
if err != nil {
|
||||
if err := tmpl.New(ctx).ApplyAll(
|
||||
&unibin.NameTemplate,
|
||||
&unibin.ModTimestamp,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
name := unibin.NameTemplate
|
||||
opts.Name = name
|
||||
|
||||
path := filepath.Join(ctx.Config.Dist, unibin.ID+"_darwin_all", name)
|
||||
@ -221,6 +226,17 @@ func makeUniversalBinary(ctx *context.Context, opts *build.Options, unibin confi
|
||||
return fmt.Errorf("failed to close file: %w", err)
|
||||
}
|
||||
|
||||
if unibin.ModTimestamp != "" {
|
||||
modUnix, err := strconv.ParseInt(unibin.ModTimestamp, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modTime := time.Unix(modUnix, 0)
|
||||
if err := os.Chtimes(path, modTime, modTime); err != nil {
|
||||
return fmt.Errorf("failed to change times for %s: %w", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
extra := map[string]interface{}{}
|
||||
for k, v := range binaries[0].Extra {
|
||||
extra[k] = v
|
||||
|
@ -2,9 +2,11 @@ package universalbinary
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -194,6 +196,28 @@ func TestRun(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
modTime := time.Now().AddDate(-1, 0, 0).Round(1 * time.Second).UTC()
|
||||
ctx7 := testctx.NewWithCfg(config.Project{
|
||||
Dist: dist,
|
||||
UniversalBinaries: []config.UniversalBinary{
|
||||
{
|
||||
ID: "foo",
|
||||
IDs: []string{"foo"},
|
||||
NameTemplate: "foo",
|
||||
ModTimestamp: fmt.Sprintf("%d", modTime.Unix()),
|
||||
Hooks: config.BuildHookConfig{
|
||||
Pre: []config.Hook{
|
||||
{Cmd: "touch " + pre},
|
||||
},
|
||||
Post: []config.Hook{
|
||||
{Cmd: "touch " + post},
|
||||
{Cmd: `sh -c 'echo "{{ .Name }} {{ .Os }} {{ .Arch }} {{ .Arm }} {{ .Target }} {{ .Ext }}" > {{ .Path }}.post'`, Output: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
for arch, path := range paths {
|
||||
cmd := exec.Command("go", "build", "-o", path, src)
|
||||
cmd.Env = append(os.Environ(), "GOOS=darwin", "GOARCH="+arch)
|
||||
@ -218,6 +242,7 @@ func TestRun(t *testing.T) {
|
||||
ctx2.Artifacts.Add(&art)
|
||||
ctx5.Artifacts.Add(&art)
|
||||
ctx6.Artifacts.Add(&art)
|
||||
ctx7.Artifacts.Add(&art)
|
||||
ctx4.Artifacts.Add(&artifact.Artifact{
|
||||
Name: "fake",
|
||||
Path: path + "wrong",
|
||||
@ -342,6 +367,24 @@ func TestRun(t *testing.T) {
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Post = []config.Hook{}
|
||||
testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
|
||||
})
|
||||
|
||||
t.Run("mod timestamp", func(t *testing.T) {
|
||||
ctx := ctx7
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
unibins := ctx.Artifacts.Filter(artifact.ByType(artifact.UniversalBinary)).List()
|
||||
require.Len(t, unibins, 1)
|
||||
stat, err := os.Stat(unibins[0].Path)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, modTime.Unix(), stat.ModTime().Unix())
|
||||
})
|
||||
|
||||
t.Run("bad mod timestamp", func(t *testing.T) {
|
||||
ctx := ctx5
|
||||
ctx.Config.UniversalBinaries[0].ModTimestamp = "not a number"
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Pre = []config.Hook{}
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Post = []config.Hook{}
|
||||
require.ErrorIs(t, Pipe{}.Run(ctx), strconv.ErrSyntax)
|
||||
})
|
||||
}
|
||||
|
||||
func checkUniversalBinary(tb testing.TB, unibin *artifact.Artifact) {
|
||||
|
@ -609,6 +609,7 @@ type UniversalBinary struct {
|
||||
NameTemplate string `yaml:"name_template,omitempty" json:"name_template,omitempty"`
|
||||
Replace bool `yaml:"replace,omitempty" json:"replace,omitempty"`
|
||||
Hooks BuildHookConfig `yaml:"hooks,omitempty" json:"hooks,omitempty"`
|
||||
ModTimestamp string `yaml:"mod_timestamp,omitempty" json:"mod_timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// UPX allows to compress binaries with `upx`.
|
||||
|
@ -183,6 +183,8 @@ builds:
|
||||
# Set the modified timestamp on the output binary, typically
|
||||
# you would do this to ensure a build was reproducible. Pass
|
||||
# empty string to skip modifying the output.
|
||||
#
|
||||
# Templates: allowed.
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
|
||||
# Hooks can be used to customize the final binary,
|
||||
|
@ -9,52 +9,61 @@ Here's how to use it:
|
||||
```yaml
|
||||
# .goreleaser.yaml
|
||||
universal_binaries:
|
||||
-
|
||||
# ID of resulting universal binary.
|
||||
#
|
||||
# Default: the project name
|
||||
id: foo
|
||||
- # ID of resulting universal binary.
|
||||
#
|
||||
# Default: the project name
|
||||
id: foo
|
||||
|
||||
# IDs to use to filter the built binaries.
|
||||
#
|
||||
# Default: the value of the id field
|
||||
# Since: v1.3
|
||||
ids:
|
||||
- build1
|
||||
- build2
|
||||
# IDs to use to filter the built binaries.
|
||||
#
|
||||
# Default: the value of the id field
|
||||
# Since: v1.3
|
||||
ids:
|
||||
- build1
|
||||
- build2
|
||||
|
||||
# Universal binary name.
|
||||
#
|
||||
# You will want to change this if you have multiple builds!
|
||||
#
|
||||
# Default: '{{ .ProjectName }}'
|
||||
# Templates: allowed
|
||||
name_template: '{{.ProjectName}}_{{.Version}}'
|
||||
# Universal binary name.
|
||||
#
|
||||
# You will want to change this if you have multiple builds!
|
||||
#
|
||||
# Default: '{{ .ProjectName }}'
|
||||
# Templates: allowed
|
||||
name_template: "{{.ProjectName}}_{{.Version}}"
|
||||
|
||||
# Whether to remove the previous single-arch binaries from the artifact list.
|
||||
# If left as false, your end release might have both several macOS archives:
|
||||
# amd64, arm64 and all.
|
||||
replace: true
|
||||
# Whether to remove the previous single-arch binaries from the artifact list.
|
||||
# If left as false, your end release might have both several macOS archives:
|
||||
# amd64, arm64 and all.
|
||||
replace: true
|
||||
|
||||
# Hooks can be used to customize the final binary,
|
||||
# for example, to run generators.
|
||||
#
|
||||
# Templates: allowed
|
||||
hooks:
|
||||
pre: rice embed-go
|
||||
post: ./script.sh {{ .Path }}
|
||||
# Set the modified timestamp on the output binary, typically
|
||||
# you would do this to ensure a build was reproducible. Pass
|
||||
# empty string to skip modifying the output.
|
||||
#
|
||||
# Templates: allowed.
|
||||
# Since: v1.20.
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
|
||||
# Hooks can be used to customize the final binary,
|
||||
# for example, to run generators.
|
||||
#
|
||||
# Templates: allowed
|
||||
hooks:
|
||||
pre: rice embed-go
|
||||
post: ./script.sh {{ .Path }}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
Learn more about the [name template engine](/customization/templates/).
|
||||
|
||||
For more info about hooks, see the [build section](/customization/build/#build-hooks).
|
||||
|
||||
The minimal configuration for most setups would look like this:
|
||||
|
||||
```yaml
|
||||
# .goreleaser.yml
|
||||
universal_binaries:
|
||||
- replace: true
|
||||
- replace: true
|
||||
```
|
||||
|
||||
That config will join your default build macOS binaries into a Universal Binary,
|
||||
@ -64,6 +73,7 @@ From there, the `Arch` template variable for this file will be `all`.
|
||||
You can use the Go template engine to remove it if you'd like.
|
||||
|
||||
!!! warning
|
||||
|
||||
You'll want to change `name_template` for each `id` you add in universal
|
||||
binaries, otherwise they'll have the same name.
|
||||
|
||||
@ -79,21 +89,22 @@ You can use the Go template engine to remove it if you'd like.
|
||||
|
||||
## Naming templates
|
||||
|
||||
Most fields that support [templating](/customization/templates/) will also
|
||||
Most fields that support [templates](/customization/templates/) will also
|
||||
support the following build details:
|
||||
|
||||
<!-- to format the tables, use: https://tabletomarkdown.com/format-markdown-table/ -->
|
||||
|
||||
Key |Description
|
||||
-------|---------------------------------
|
||||
.Os |`GOOS`, always `darwin`
|
||||
.Arch |`GOARCH`, always `all`
|
||||
.Arm |`GOARM`, always empty
|
||||
.Ext |Extension, always empty
|
||||
.Target|Build target, always `darwin_all`
|
||||
.Path |The binary path
|
||||
.Name |The binary name
|
||||
| Key | Description |
|
||||
| ------- | --------------------------------- |
|
||||
| .Os | `GOOS`, always `darwin` |
|
||||
| .Arch | `GOARCH`, always `all` |
|
||||
| .Arm | `GOARM`, always empty |
|
||||
| .Ext | Extension, always empty |
|
||||
| .Target | Build target, always `darwin_all` |
|
||||
| .Path | The binary path |
|
||||
| .Name | The binary name |
|
||||
|
||||
!!! tip
|
||||
|
||||
Notice that `.Path` and `.Name` will only be available after they are
|
||||
evaluated, so they are mostly only useful in the `post` hooks.
|
||||
|
Loading…
Reference in New Issue
Block a user