You've already forked goreleaser
							
							
				mirror of
				https://github.com/goreleaser/goreleaser.git
				synced 2025-10-30 23:58:09 +02:00 
			
		
		
		
	feat: metadata mod_timestamp (#4188)
closes #4167 --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							b9a08c4dc9
						
					
				
				
					commit
					609e7c5657
				
			| @@ -17,6 +17,9 @@ gomod: | ||||
|  | ||||
| report_sizes: true | ||||
|  | ||||
| metadata: | ||||
|   mod_timestamp: "{{ .CommitTimestamp }}" | ||||
|  | ||||
| builds: | ||||
|   - env: | ||||
|       - CGO_ENABLED=0 | ||||
|   | ||||
| @@ -9,6 +9,8 @@ import ( | ||||
|  | ||||
| 	"github.com/caarlos0/log" | ||||
| 	"github.com/goreleaser/goreleaser/internal/artifact" | ||||
| 	"github.com/goreleaser/goreleaser/internal/gio" | ||||
| 	"github.com/goreleaser/goreleaser/internal/tmpl" | ||||
| 	"github.com/goreleaser/goreleaser/pkg/context" | ||||
| ) | ||||
|  | ||||
| @@ -20,6 +22,11 @@ func (Pipe) Skip(_ *context.Context) bool { return false } | ||||
|  | ||||
| // Run the pipe. | ||||
| func (Pipe) Run(ctx *context.Context) error { | ||||
| 	if err := tmpl.New(ctx).ApplyAll( | ||||
| 		&ctx.Config.Metadata.ModTimestamp, | ||||
| 	); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := writeArtifacts(ctx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -57,7 +64,11 @@ func writeJSON(ctx *context.Context, j interface{}, name string) error { | ||||
| 	} | ||||
| 	path := filepath.Join(ctx.Config.Dist, name) | ||||
| 	log.Log.WithField("file", path).Info("writing") | ||||
| 	return os.WriteFile(path, bts, 0o644) | ||||
| 	if err := os.WriteFile(path, bts, 0o644); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return gio.Chtimes(path, ctx.Config.Metadata.ModTimestamp) | ||||
| } | ||||
|  | ||||
| type metadata struct { | ||||
|   | ||||
| @@ -1,15 +1,19 @@ | ||||
| package metadata | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/goreleaser/goreleaser/internal/artifact" | ||||
| 	"github.com/goreleaser/goreleaser/internal/golden" | ||||
| 	"github.com/goreleaser/goreleaser/internal/testctx" | ||||
| 	"github.com/goreleaser/goreleaser/internal/testlib" | ||||
| 	"github.com/goreleaser/goreleaser/pkg/config" | ||||
| 	"github.com/goreleaser/goreleaser/pkg/context" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
|  | ||||
| @@ -22,42 +26,74 @@ func TestRunWithError(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestRun(t *testing.T) { | ||||
| 	tmp := t.TempDir() | ||||
| 	ctx := testctx.NewWithCfg( | ||||
| 		config.Project{ | ||||
| 			Dist:        tmp, | ||||
| 			ProjectName: "name", | ||||
| 		}, | ||||
| 		testctx.WithPreviousTag("v1.2.2"), | ||||
| 		testctx.WithCurrentTag("v1.2.3"), | ||||
| 		testctx.WithCommit("aef34a"), | ||||
| 		testctx.WithVersion("1.2.3"), | ||||
| 		testctx.WithDate(time.Date(2022, 0o1, 22, 10, 12, 13, 0, time.UTC)), | ||||
| 		testctx.WithFakeRuntime, | ||||
| 	) | ||||
| 	ctx.Artifacts.Add(&artifact.Artifact{ | ||||
| 		Name:   "foo", | ||||
| 		Path:   "foo.txt", | ||||
| 		Type:   artifact.Binary, | ||||
| 		Goos:   "darwin", | ||||
| 		Goarch: "amd64", | ||||
| 		Goarm:  "7", | ||||
| 		Extra: map[string]interface{}{ | ||||
| 			"foo": "bar", | ||||
| 		}, | ||||
| 	}) | ||||
| 	modTime := time.Now().AddDate(-1, 0, 0).Round(1 * time.Second).UTC() | ||||
|  | ||||
| 	getCtx := func(tmp string) *context.Context { | ||||
| 		ctx := testctx.NewWithCfg( | ||||
| 			config.Project{ | ||||
| 				Dist:        tmp, | ||||
| 				ProjectName: "name", | ||||
| 				Metadata: config.ProjectMetadata{ | ||||
| 					ModTimestamp: "{{.Env.MOD_TS}}", | ||||
| 				}, | ||||
| 			}, | ||||
| 			testctx.WithPreviousTag("v1.2.2"), | ||||
| 			testctx.WithCurrentTag("v1.2.3"), | ||||
| 			testctx.WithCommit("aef34a"), | ||||
| 			testctx.WithVersion("1.2.3"), | ||||
| 			testctx.WithDate(time.Date(2022, 0o1, 22, 10, 12, 13, 0, time.UTC)), | ||||
| 			testctx.WithFakeRuntime, | ||||
| 			testctx.WithEnv(map[string]string{ | ||||
| 				"MOD_TS": fmt.Sprintf("%d", modTime.Unix()), | ||||
| 			}), | ||||
| 		) | ||||
| 		ctx.Artifacts.Add(&artifact.Artifact{ | ||||
| 			Name:   "foo", | ||||
| 			Path:   "foo.txt", | ||||
| 			Type:   artifact.Binary, | ||||
| 			Goos:   "darwin", | ||||
| 			Goarch: "amd64", | ||||
| 			Goarm:  "7", | ||||
| 			Extra: map[string]interface{}{ | ||||
| 				"foo": "bar", | ||||
| 			}, | ||||
| 		}) | ||||
| 		return ctx | ||||
| 	} | ||||
|  | ||||
| 	require.NoError(t, Pipe{}.Run(ctx)) | ||||
| 	t.Run("artifacts", func(t *testing.T) { | ||||
| 		requireEqualJSONFile(t, tmp, "artifacts.json") | ||||
| 		tmp := t.TempDir() | ||||
| 		ctx := getCtx(tmp) | ||||
| 		require.NoError(t, Pipe{}.Run(ctx)) | ||||
| 		requireEqualJSONFile(t, tmp, "artifacts.json", modTime) | ||||
| 	}) | ||||
| 	t.Run("metadata", func(t *testing.T) { | ||||
| 		requireEqualJSONFile(t, tmp, "metadata.json") | ||||
| 		tmp := t.TempDir() | ||||
| 		ctx := getCtx(tmp) | ||||
| 		require.NoError(t, Pipe{}.Run(ctx)) | ||||
| 		requireEqualJSONFile(t, tmp, "metadata.json", modTime) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("invalid mod metadata", func(t *testing.T) { | ||||
| 		tmp := t.TempDir() | ||||
| 		ctx := getCtx(tmp) | ||||
| 		ctx.Config.Metadata.ModTimestamp = "not a number" | ||||
| 		require.ErrorIs(t, Pipe{}.Run(ctx), strconv.ErrSyntax) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("invalid mod metadata tmpl", func(t *testing.T) { | ||||
| 		tmp := t.TempDir() | ||||
| 		ctx := getCtx(tmp) | ||||
| 		ctx.Config.Metadata.ModTimestamp = "{{.Nope}}" | ||||
| 		testlib.RequireTemplateError(t, Pipe{}.Run(ctx)) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func requireEqualJSONFile(tb testing.TB, tmp, s string) { | ||||
| func requireEqualJSONFile(tb testing.TB, tmp, s string, modTime time.Time) { | ||||
| 	tb.Helper() | ||||
| 	path := filepath.Join(tmp, s) | ||||
| 	golden.RequireEqualJSON(tb, golden.RequireReadFile(tb, path)) | ||||
| 	stat, err := os.Stat(path) | ||||
| 	require.NoError(tb, err) | ||||
| 	require.Equal(tb, modTime.Unix(), stat.ModTime().Unix()) | ||||
| } | ||||
|   | ||||
| @@ -17,9 +17,7 @@ func TestString(t *testing.T) { | ||||
|  | ||||
| func TestSkip(t *testing.T) { | ||||
| 	t.Run("skip", func(t *testing.T) { | ||||
| 		require.True(t, Pipe{}.Skip(testctx.NewWithCfg(config.Project{ | ||||
| 			ReportSizes: false, | ||||
| 		}))) | ||||
| 		require.True(t, Pipe{}.Skip(testctx.New())) | ||||
| 	}) | ||||
| 	t.Run("dont skip", func(t *testing.T) { | ||||
| 		require.False(t, Pipe{}.Skip(testctx.NewWithCfg(config.Project{ | ||||
|   | ||||
| @@ -1101,6 +1101,7 @@ type Project struct { | ||||
| 	Chocolateys     []Chocolatey     `yaml:"chocolateys,omitempty" json:"chocolateys,omitempty"` | ||||
| 	Git             Git              `yaml:"git,omitempty" json:"git,omitempty"` | ||||
| 	ReportSizes     bool             `yaml:"report_sizes,omitempty" json:"report_sizes,omitempty"` | ||||
| 	Metadata        ProjectMetadata  `yaml:"metadata,omitempty" json:"metadata,omitempty"` | ||||
|  | ||||
| 	UniversalBinaries []UniversalBinary `yaml:"universal_binaries,omitempty" json:"universal_binaries,omitempty"` | ||||
| 	UPXs              []UPX             `yaml:"upx,omitempty" json:"upx,omitempty"` | ||||
| @@ -1120,6 +1121,10 @@ type Project struct { | ||||
| 	GiteaURLs GiteaURLs `yaml:"gitea_urls,omitempty" json:"gitea_urls,omitempty"` | ||||
| } | ||||
|  | ||||
| type ProjectMetadata struct { | ||||
| 	ModTimestamp string `yaml:"mod_timestamp,omitempty" json:"mod_timestamp,omitempty"` | ||||
| } | ||||
|  | ||||
| type GoMod struct { | ||||
| 	Proxy    bool     `yaml:"proxy,omitempty" json:"proxy,omitempty"` | ||||
| 	Env      []string `yaml:"env,omitempty" json:"env,omitempty"` | ||||
|   | ||||
							
								
								
									
										18
									
								
								www/docs/customization/metadata.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								www/docs/customization/metadata.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # Metadata | ||||
|  | ||||
| > Since v1.20 | ||||
|  | ||||
| GoReleaser creates some metadata files in the `dist` folder before it finishes | ||||
| running. | ||||
|  | ||||
| These are the options available: | ||||
|  | ||||
| ```yaml | ||||
| # .goreleaser.yaml | ||||
| # | ||||
| metadata: | ||||
|   # Set the modified timestamp on the metadata files. | ||||
|   # | ||||
|   # Templates: allowed. | ||||
|   mod_timestamp: "{{ .CommitTimestamp }}" | ||||
| ``` | ||||
| @@ -120,6 +120,7 @@ nav: | ||||
|           - customization/ko.md | ||||
|       - customization/sbom.md | ||||
|       - customization/reportsizes.md | ||||
|       - customization/metadata.md | ||||
|       - Signing: | ||||
|           - Checksums and artifacts: customization/sign.md | ||||
|           - Docker Images and Manifests: customization/docker_sign.md | ||||
|   | ||||
		Reference in New Issue
	
	Block a user