Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package melange for openSUSE:Factory checked in at 2026-10-08 11:59:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/melange (Old) and /work/SRC/openSUSE:Factory/.melange.new.3698736 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "melange" Thu Oct 8 11:59:04 2026 rev:183 rq:1383170 version:0.62.0 Changes: -------- --- /work/SRC/openSUSE:Factory/melange/melange.changes 2026-10-06 19:18:02.544595215 +0200 +++ /work/SRC/openSUSE:Factory/.melange.new.3698736/melange.changes 2026-10-08 12:00:00.433518526 +0200 @@ -1,0 +2,7 @@ +Thu Oct 08 05:26:37 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.62.0: + * feat(build): add --exclude-env-from-recorded-config flag + (#2688) + +------------------------------------------------------------------- Old: ---- melange-0.61.4.obscpio New: ---- melange-0.62.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ melange.spec ++++++ --- /var/tmp/diff_new_pack.nZgxt8/_old 2026-10-08 12:00:02.919622660 +0200 +++ /var/tmp/diff_new_pack.nZgxt8/_new 2026-10-08 12:00:02.921622743 +0200 @@ -17,7 +17,7 @@ Name: melange -Version: 0.61.4 +Version: 0.62.0 Release: 0 Summary: Build APKs from source code License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.nZgxt8/_old 2026-10-08 12:00:02.985625424 +0200 +++ /var/tmp/diff_new_pack.nZgxt8/_new 2026-10-08 12:00:03.000626053 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/chainguard-dev/melange.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">refs/tags/v0.61.4</param> + <param name="revision">refs/tags/v0.62.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.nZgxt8/_old 2026-10-08 12:00:03.053628273 +0200 +++ /var/tmp/diff_new_pack.nZgxt8/_new 2026-10-08 12:00:03.063628691 +0200 @@ -3,6 +3,6 @@ <param name="url">https://github.com/chainguard-dev/melange</param> <param name="changesrevision">3f6115b820985d70ca3c93cdf8519c1b3b4cfe81</param></service><service name="tar_scm"> <param name="url">https://github.com/chainguard-dev/melange.git</param> - <param name="changesrevision">036337ffd1d1bab54fe90e2ea943385f4a72f4d3</param></service></servicedata> + <param name="changesrevision">0ae0a5c52b587563b72755df1c7520b082712517</param></service></servicedata> (No newline at EOF) ++++++ melange-0.61.4.obscpio -> melange-0.62.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/docs/md/melange_build.md new/melange-0.62.0/docs/md/melange_build.md --- old/melange-0.61.4/docs/md/melange_build.md 2026-10-06 02:51:28.000000000 +0200 +++ new/melange-0.62.0/docs/md/melange_build.md 2026-10-07 22:14:27.000000000 +0200 @@ -44,6 +44,7 @@ --disk string disk size to use for builds --empty-workspace whether the build workspace should be empty --env-file strings files to use for preloaded environment variables + --exclude-env-from-recorded-config strings environment variable keys to omit from the build configuration recorded in built packages (the variables are still set in the build environment) --generate-index whether to generate APKINDEX.tar.gz (default true) --generate-provenance generate SLSA provenance for builds (included in a separate .attest.tar.gz file next to the APK) --git-commit string commit hash of the git repository containing the build config file (defaults to detecting HEAD) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/build/build.go new/melange-0.62.0/pkg/build/build.go --- old/melange-0.61.4/pkg/build/build.go 2026-10-06 02:51:28.000000000 +0200 +++ new/melange-0.62.0/pkg/build/build.go 2026-10-07 22:14:27.000000000 +0200 @@ -137,6 +137,11 @@ Auth map[string]options.Auth IgnoreSignatures bool + // ExcludedEnvFromRecordedConfig lists environment variable keys to omit + // from the configuration recorded into built artifacts. The variables + // are still set in the build environment. + ExcludedEnvFromRecordedConfig []string + EnabledBuildOptions []string // SBOMGenerator is the generator used to create SBOMs for this build. @@ -159,6 +164,25 @@ PkgResolver *apk.PkgResolver } +// recordedConfiguration returns the build configuration as it should be +// persisted into built artifacts: the .melange.yaml embedded in each APK +// and the SLSA provenance. Environment variable keys listed in +// ExcludedEnvFromRecordedConfig are removed from a copy, leaving the +// configuration used for the build environment itself untouched. +func (b *Build) recordedConfiguration() *config.Configuration { + if len(b.ExcludedEnvFromRecordedConfig) == 0 { + return b.Configuration + } + + cfg := *b.Configuration + cfg.Environment.Environment = maps.Clone(cfg.Environment.Environment) + for _, key := range b.ExcludedEnvFromRecordedConfig { + delete(cfg.Environment.Environment, key) + } + + return &cfg +} + func New(ctx context.Context, opts ...Option) (*Build, error) { b := Build{ WorkspaceIgnore: ".melangeignore", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/build/options.go new/melange-0.62.0/pkg/build/options.go --- old/melange-0.61.4/pkg/build/options.go 2026-10-06 02:51:28.000000000 +0200 +++ new/melange-0.62.0/pkg/build/options.go 2026-10-07 22:14:27.000000000 +0200 @@ -263,6 +263,24 @@ } } +// WithExcludedEnvFromRecordedConfig specifies environment variable keys to +// omit from the build configuration recorded into built artifacts (the +// .melange.yaml embedded in each APK and the SLSA provenance). The +// variables are still exported into the build environment. This is useful +// when a build system injects environment variables describing build +// infrastructure (such as paths to injected configuration files) that +// should not be persisted in published packages. +func WithExcludedEnvFromRecordedConfig(keys []string) Option { + return func(b *Build) error { + for _, key := range keys { + if key != "" { + b.ExcludedEnvFromRecordedConfig = append(b.ExcludedEnvFromRecordedConfig, key) + } + } + return nil + } +} + // WithVarsFile specifies a variables file to use to populate the build // configuration variables block. func WithVarsFile(varsFile string) Option { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/build/package.go new/melange-0.62.0/pkg/build/package.go --- old/melange-0.61.4/pkg/build/package.go 2026-10-06 02:51:28.000000000 +0200 +++ new/melange-0.62.0/pkg/build/package.go 2026-10-07 22:14:27.000000000 +0200 @@ -225,7 +225,7 @@ enc := yaml.NewEncoder(&melangeBuf) enc.SetIndent(2) // To align with `yam` a little better. - if err := enc.Encode(pc.Build.Configuration); err != nil { + if err := enc.Encode(pc.Build.recordedConfiguration()); err != nil { return nil, fmt.Errorf("marshalling config: %w", err) } if err := fsys.WriteFile(".melange.yaml", melangeBuf.Bytes(), 0o644); err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/build/recorded_config_test.go new/melange-0.62.0/pkg/build/recorded_config_test.go --- old/melange-0.61.4/pkg/build/recorded_config_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/melange-0.62.0/pkg/build/recorded_config_test.go 2026-10-07 22:14:27.000000000 +0200 @@ -0,0 +1,197 @@ +// Copyright 2026 Chainguard, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package build + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "io" + "os" + "path/filepath" + "testing" + + "chainguard.dev/melange/pkg/config" + + apko_types "chainguard.dev/apko/pkg/build/types" + "github.com/chainguard-dev/clog/slogtest" + "github.com/stretchr/testify/require" +) + +func TestRecordedConfiguration(t *testing.T) { + buildEnv := map[string]string{ + "CONFIG_PATH": "/run/injected/config", + "CFLAGS": "-O2", + } + + tests := []struct { + name string + excluded []string + want map[string]string + }{ + { + name: "no exclusions records all environment variables", + excluded: nil, + want: map[string]string{ + "CONFIG_PATH": "/run/injected/config", + "CFLAGS": "-O2", + }, + }, + { + name: "excluded key is omitted from the recorded config", + excluded: []string{"CONFIG_PATH"}, + want: map[string]string{ + "CFLAGS": "-O2", + }, + }, + { + name: "excluding an unset key changes nothing", + excluded: []string{"NOT_SET"}, + want: map[string]string{ + "CONFIG_PATH": "/run/injected/config", + "CFLAGS": "-O2", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := &Build{ + Configuration: &config.Configuration{ + Environment: apko_types.ImageConfiguration{ + Environment: map[string]string{ + "CONFIG_PATH": "/run/injected/config", + "CFLAGS": "-O2", + }, + }, + }, + ExcludedEnvFromRecordedConfig: tt.excluded, + } + + got := b.recordedConfiguration() + require.Equal(t, tt.want, got.Environment.Environment) + + // The configuration driving the build environment keeps + // every variable regardless of exclusions. + require.Equal(t, buildEnv, b.Configuration.Environment.Environment) + }) + } +} + +// TestGenerateControlSection_ExcludedEnv proves the full path from an +// --env-file through config parsing to the .melange.yaml embedded in the +// control section: an excluded variable reaches the configuration used for +// the build environment, but never the recorded configuration. +func TestGenerateControlSection_ExcludedEnv(t *testing.T) { + ctx := slogtest.Context(t) + dir := t.TempDir() + + cfgPath := filepath.Join(dir, "test.melange.yaml") + require.NoError(t, os.WriteFile(cfgPath, []byte(` +package: + name: hello + version: "1.0.0" + epoch: 0 +`), 0o644)) + + envPath := filepath.Join(dir, "build.env") + require.NoError(t, os.WriteFile(envPath, []byte("CONFIG_PATH=/run/injected/config\nCFLAGS=-O2\n"), 0o644)) + + cfg, err := config.ParseConfiguration(ctx, cfgPath, config.WithEnvFilesForParsing([]string{envPath})) + require.NoError(t, err) + + // Both env-file variables reach the configuration that is used to set + // up the build environment. + require.Equal(t, "/run/injected/config", cfg.Environment.Environment["CONFIG_PATH"]) + require.Equal(t, "-O2", cfg.Environment.Environment["CFLAGS"]) + + pc := &PackageBuild{ + Build: &Build{ + Configuration: cfg, + ExcludedEnvFromRecordedConfig: []string{"CONFIG_PATH"}, + }, + Origin: &cfg.Package, + PackageName: cfg.Package.Name, + } + + controlSection, err := pc.generateControlSection(ctx) + require.NoError(t, err) + + embedded := readControlFile(t, controlSection, ".melange.yaml") + require.NotContains(t, embedded, "CONFIG_PATH") + require.NotContains(t, embedded, "/run/injected/config") + require.Contains(t, embedded, "CFLAGS: -O2") + + // The configuration used for the build environment is untouched. + require.Equal(t, "/run/injected/config", cfg.Environment.Environment["CONFIG_PATH"]) +} + +func TestGenerateSLSA_ExcludedEnv(t *testing.T) { + pc := &PackageBuild{ + Build: &Build{ + Configuration: &config.Configuration{ + Package: config.Package{ + Name: "hello", + Version: "1.0.0", + }, + Environment: apko_types.ImageConfiguration{ + Environment: map[string]string{ + "CONFIG_PATH": "/run/injected/config", + "CFLAGS": "-O2", + }, + }, + }, + ExcludedEnvFromRecordedConfig: []string{"CONFIG_PATH"}, + }, + PackageName: "hello", + Origin: &config.Package{ + Name: "hello", + Version: "1.0.0", + }, + DataHash: "abcdef1234567890", + } + + result, err := pc.generateSLSA() + require.NoError(t, err) + + provenance := string(result) + require.NotContains(t, provenance, "CONFIG_PATH") + require.NotContains(t, provenance, "/run/injected/config") + require.Contains(t, provenance, "CFLAGS") +} + +// readControlFile extracts a single file from a gzipped control section +// tarball. +func readControlFile(t *testing.T, controlSection []byte, name string) string { + t.Helper() + + zr, err := gzip.NewReader(bytes.NewReader(controlSection)) + require.NoError(t, err) + tr := tar.NewReader(zr) + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + if hdr.Name == name { + contents, err := io.ReadAll(tr) + require.NoError(t, err) + return string(contents) + } + } + t.Fatalf("%s not found in control section", name) + return "" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/build/slsa.go new/melange-0.62.0/pkg/build/slsa.go --- old/melange-0.61.4/pkg/build/slsa.go 2026-10-06 02:51:28.000000000 +0200 +++ new/melange-0.62.0/pkg/build/slsa.go 2026-10-07 22:14:27.000000000 +0200 @@ -41,7 +41,7 @@ }, } - cfg, err := structToMap(pc.Build.Configuration) + cfg, err := structToMap(pc.Build.recordedConfiguration()) if err != nil { return nil, fmt.Errorf("converting contents to generic map: %w", err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/cli/build.go new/melange-0.62.0/pkg/cli/build.go --- old/melange-0.61.4/pkg/cli/build.go 2026-10-06 02:51:28.000000000 +0200 +++ new/melange-0.62.0/pkg/cli/build.go 2026-10-07 22:14:27.000000000 +0200 @@ -54,6 +54,7 @@ fs.StringVar(&flags.ApkCacheDir, "apk-cache-dir", "", "directory used for cached apk packages (default is system-defined cache directory)") fs.StringVar(&flags.SigningKey, "signing-key", "", "key to use for signing") fs.StringSliceVar(&flags.EnvFiles, "env-file", []string{}, "files to use for preloaded environment variables") + fs.StringSliceVar(&flags.ExcludeEnvFromRecordedConfig, "exclude-env-from-recorded-config", []string{}, "environment variable keys to omit from the build configuration recorded in built packages (the variables are still set in the build environment)") fs.StringVar(&flags.VarsFile, "vars-file", "", "file to use for preloaded build configuration variables") fs.BoolVar(&flags.GenerateIndex, "generate-index", true, "whether to generate APKINDEX.tar.gz") fs.BoolVar(&flags.EmptyWorkspace, "empty-workspace", false, "whether the build workspace should be empty") @@ -139,6 +140,10 @@ ConfigFileLicense string GenerateProvenance bool TraceFile string + + // ExcludeEnvFromRecordedConfig lists environment variable keys to omit + // from the build configuration recorded in built packages. + ExcludeEnvFromRecordedConfig []string } // ParseBuildFlags parses build flags from the provided args and returns a BuildFlags struct @@ -210,6 +215,7 @@ build.WithDependencyLog(flags.DependencyLog), build.WithStripOriginName(flags.StripOriginName), build.WithEnvFiles(flags.EnvFiles), + build.WithExcludedEnvFromRecordedConfig(flags.ExcludeEnvFromRecordedConfig), build.WithVarsFile(flags.VarsFile), build.WithNamespace(flags.PurlNamespace), build.WithEnabledBuildOptions(flags.BuildOption), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.61.4/pkg/cli/build_test.go new/melange-0.62.0/pkg/cli/build_test.go --- old/melange-0.61.4/pkg/cli/build_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/melange-0.62.0/pkg/cli/build_test.go 2026-10-07 22:14:27.000000000 +0200 @@ -0,0 +1,65 @@ +// Copyright 2026 Chainguard, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "testing" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/require" +) + +func TestAddBuildFlags_ExcludeEnvFromRecordedConfig(t *testing.T) { + tests := []struct { + name string + args []string + want []string + }{ + { + name: "defaults to empty", + args: nil, + want: []string{}, + }, + { + name: "single key", + args: []string{"--exclude-env-from-recorded-config=CONFIG_PATH"}, + want: []string{"CONFIG_PATH"}, + }, + { + name: "repeated flag accumulates keys", + args: []string{ + "--exclude-env-from-recorded-config=CONFIG_PATH", + "--exclude-env-from-recorded-config=CREDENTIALS_FILE", + }, + want: []string{"CONFIG_PATH", "CREDENTIALS_FILE"}, + }, + { + name: "comma-separated keys", + args: []string{"--exclude-env-from-recorded-config=CONFIG_PATH,CREDENTIALS_FILE"}, + want: []string{"CONFIG_PATH", "CREDENTIALS_FILE"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := pflag.NewFlagSet("build", pflag.ContinueOnError) + flags := &BuildFlags{} + addBuildFlags(fs, flags) + + require.NoError(t, fs.Parse(tt.args)) + require.Equal(t, tt.want, flags.ExcludeEnvFromRecordedConfig) + }) + } +} ++++++ melange.obsinfo ++++++ --- /var/tmp/diff_new_pack.nZgxt8/_old 2026-10-08 12:00:05.426727673 +0200 +++ /var/tmp/diff_new_pack.nZgxt8/_new 2026-10-08 12:00:05.433727966 +0200 @@ -1,5 +1,5 @@ name: melange -version: 0.61.4 -mtime: 1791247888 -commit: 036337ffd1d1bab54fe90e2ea943385f4a72f4d3 +version: 0.62.0 +mtime: 1791404067 +commit: 0ae0a5c52b587563b72755df1c7520b082712517 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/melange/vendor.tar.gz /work/SRC/openSUSE:Factory/.melange.new.3698736/vendor.tar.gz differ: char 30, line 1
