diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c22ddcaf..e008335d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,6 @@ jobs: make e2e-covdata fi - name: Upload coverage to codecov.io - uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # v5.1.1 + uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release-github.yml b/.github/workflows/release-github.yml index f95474ad5..6f59e9c12 100644 --- a/.github/workflows/release-github.yml +++ b/.github/workflows/release-github.yml @@ -26,7 +26,7 @@ jobs: permissions: contents: write # for goreleaser/goreleaser-action to create a GitHub release name: Release Notation Binaries - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: matrix: go-version: ['1.23'] diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 2813f6762..6fe05969c 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -54,7 +54,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # tag=v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # tag=v4.6.0 with: name: SARIF file path: results.sarif diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index 196768b2c..a2bed995b 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -44,14 +44,14 @@ var ( Usage: "signature envelope format, options: \"jws\", \"cose\"", } SetPflagSignatureFormat = func(fs *pflag.FlagSet, p *string) { - defaultSignatureFormat := envelope.JWS - // load config to get signatureFormat config, err := configutil.LoadConfigOnce() - if err == nil && config.SignatureFormat != "" { - defaultSignatureFormat = config.SignatureFormat + if err != nil || config.SignatureFormat == "" { + fs.StringVar(p, PflagSignatureFormat.Name, envelope.JWS, PflagSignatureFormat.Usage) + return } - fs.StringVar(p, PflagSignatureFormat.Name, defaultSignatureFormat, PflagSignatureFormat.Usage) + // set signatureFormat from config + fs.StringVar(p, PflagSignatureFormat.Name, config.SignatureFormat, PflagSignatureFormat.Usage) } PflagID = &pflag.Flag{ diff --git a/pkg/configutil/once.go b/pkg/configutil/once.go index 00e2a2a46..f5e1d96e5 100644 --- a/pkg/configutil/once.go +++ b/pkg/configutil/once.go @@ -21,28 +21,28 @@ import ( "github.com/notaryproject/notation/internal/envelope" ) -var ( - // configInfo is the config.json data - configInfo *config.Config - configOnce sync.Once -) +// loadConfigOnce is a function that invokes loadConfig only once. +var loadConfigOnce = sync.OnceValues(loadConfig) // LoadConfigOnce returns the previously read config file. // If previous config file does not exist, it reads the config from file // or return a default config if not found. // The returned config is only suitable for read only scenarios for short-lived processes. func LoadConfigOnce() (*config.Config, error) { - var err error - configOnce.Do(func() { - configInfo, err = config.LoadConfig() - if err != nil { - return - } - // set default value - configInfo.SignatureFormat = strings.ToLower(configInfo.SignatureFormat) - if configInfo.SignatureFormat == "" { - configInfo.SignatureFormat = envelope.JWS - } - }) - return configInfo, err + return loadConfigOnce() +} + +// loadConfig reads the config from file or return a default config if not +// found. +func loadConfig() (*config.Config, error) { + configInfo, err := config.LoadConfig() + if err != nil { + return nil, err + } + // set default value + configInfo.SignatureFormat = strings.ToLower(configInfo.SignatureFormat) + if configInfo.SignatureFormat == "" { + configInfo.SignatureFormat = envelope.JWS + } + return configInfo, nil } diff --git a/pkg/configutil/once_test.go b/pkg/configutil/once_test.go index d218669dd..bd4069199 100644 --- a/pkg/configutil/once_test.go +++ b/pkg/configutil/once_test.go @@ -14,10 +14,19 @@ package configutil import ( + "os" + "path/filepath" + "strings" + "sync" "testing" + + "github.com/notaryproject/notation-go/dir" ) func TestLoadConfigOnce(t *testing.T) { + defer func() { + loadConfigOnce = sync.OnceValues(loadConfig) + }() config1, err := LoadConfigOnce() if err != nil { t.Fatal("LoadConfigOnce failed.") @@ -27,6 +36,26 @@ func TestLoadConfigOnce(t *testing.T) { t.Fatal("LoadConfigOnce failed.") } if config1 != config2 { - t.Fatal("LoadConfigOnce is invalid.") + t.Fatal("LoadConfigOnce should return the same config.") + } +} + +func TestLoadConfigOnceError(t *testing.T) { + dir.UserConfigDir = t.TempDir() + defer func() { + dir.UserConfigDir = "" + loadConfigOnce = sync.OnceValues(loadConfig) + }() + if err := os.WriteFile(filepath.Join(dir.UserConfigDir, dir.PathConfigFile), []byte("invalid json"), 0600); err != nil { + t.Fatal("Failed to create file.") + } + + _, err := LoadConfigOnce() + if err == nil || !strings.Contains(err.Error(), "invalid character") { + t.Fatal("LoadConfigOnce should fail.") + } + _, err2 := LoadConfigOnce() + if err != err2 { + t.Fatal("LoadConfigOnce should return the same error.") } } diff --git a/pkg/configutil/util_test.go b/pkg/configutil/util_test.go index cac87584f..1570996de 100644 --- a/pkg/configutil/util_test.go +++ b/pkg/configutil/util_test.go @@ -25,11 +25,10 @@ import ( ) func TestIsRegistryInsecure(t *testing.T) { - configOnce = sync.Once{} // for restore dir defer func(oldDir string) { dir.UserConfigDir = oldDir - configOnce = sync.Once{} + loadConfigOnce = sync.OnceValues(loadConfig) }(dir.UserConfigDir) // update config dir dir.UserConfigDir = "testdata" @@ -56,11 +55,10 @@ func TestIsRegistryInsecure(t *testing.T) { } func TestIsRegistryInsecureMissingConfig(t *testing.T) { - configOnce = sync.Once{} // for restore dir defer func(oldDir string) { dir.UserConfigDir = oldDir - configOnce = sync.Once{} + loadConfigOnce = sync.OnceValues(loadConfig) }(dir.UserConfigDir) // update config dir dir.UserConfigDir = "./testdata2" @@ -93,7 +91,7 @@ func TestIsRegistryInsecureConfigPermissionError(t *testing.T) { defer func(oldDir string) error { // restore permission dir.UserConfigDir = oldDir - configOnce = sync.Once{} + loadConfigOnce = sync.OnceValues(loadConfig) return os.Chmod(filepath.Join(configDir, "config.json"), 0644) }(dir.UserConfigDir) @@ -113,6 +111,7 @@ func TestIsRegistryInsecureConfigPermissionError(t *testing.T) { func TestResolveKey(t *testing.T) { defer func(oldDir string) { dir.UserConfigDir = oldDir + loadConfigOnce = sync.OnceValues(loadConfig) }(dir.UserConfigDir) t.Run("valid e2e key", func(t *testing.T) {