From 06785ca40fdca8afe07afb3dac9e6aa11893bc8f Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Fri, 9 Oct 2026 18:02:38 +0200 Subject: [PATCH 1/2] fix(cli): stop printing repository config lookup debug line Attestation commands resolved the repository organization before the root command set the log level, so a debug line about a missing .chainloop.yml was printed on every run even without --debug. Set the log level before resolving the organization, ignore a missing .chainloop.yml, and warn when the file exists but cannot be loaded. Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino Chainloop-Trace-Sessions: 256841e0-47e6-4647-b095-1cce91a60480 --- app/cli/cmd/attestation.go | 14 ++++++++-- app/cli/cmd/attestation_test.go | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/app/cli/cmd/attestation.go b/app/cli/cmd/attestation.go index 7f7240d5b..a80288b33 100644 --- a/app/cli/cmd/attestation.go +++ b/app/cli/cmd/attestation.go @@ -16,6 +16,7 @@ package cmd import ( + "errors" "fmt" "os" "strings" @@ -55,6 +56,14 @@ func newAttestationCmd() *cobra.Command { Short: "Craft Software Supply Chain Attestations", Example: "Refer to https://docs.chainloop.dev/getting-started/attestation-crafting", PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // The organization must be resolved before the root PersistentPreRunE, + // which uses it to build the control plane connection. Set the log + // level first so that resolving it honors --debug. + var err error + if logger, err = initLogger(logger); err != nil { + return err + } + if err := resolveAttestationOrganization(cmd); err != nil { return err } @@ -120,8 +129,9 @@ func resolveAttestationOrganization(cmd *cobra.Command) error { } return nil } - if err != nil { - logger.Debug().Err(err).Msg("failed to load repository organization") + // A missing .chainloop.yml is the common case, not a problem. + if err != nil && !errors.Is(err, repositoryconfig.ErrChainloopYMLNotFound) { + logger.Warn().Err(err).Msg("failed to load repository organization") } } diff --git a/app/cli/cmd/attestation_test.go b/app/cli/cmd/attestation_test.go index 97029bc45..78ff84964 100644 --- a/app/cli/cmd/attestation_test.go +++ b/app/cli/cmd/attestation_test.go @@ -281,6 +281,52 @@ func TestAttestationRepositoryOrganizationNotice(t *testing.T) { assert.Equal(t, "saved-org", viper.GetString(confOptions.organization.viperKey)) } +func TestAttestationRepositoryConfigLookupLogging(t *testing.T) { + for _, tc := range []struct { + name string + config string + debug bool + wantWarn bool + }{ + {name: "missing config"}, + {name: "missing config with debug", debug: true}, + {name: "invalid config", config: "organization: [\n", wantWarn: true}, + {name: "invalid config with debug", config: "organization: [\n", debug: true, wantWarn: true}, + } { + t.Run(tc.name, func(t *testing.T) { + dir := t.TempDir() + if tc.config != "" { + require.NoError(t, os.WriteFile(filepath.Join(dir, ".chainloop.yml"), []byte(tc.config), 0o600)) + } + t.Chdir(dir) + + _, cmd := newAttestationOrganizationTestCommand(t, "init", "saved-org") + previousDebug := flagDebug + flagDebug = tc.debug + t.Cleanup(func() { flagDebug = previousDebug }) + + // Same as the logger seeded in main.go: no level set until the root + // PersistentPreRunE runs. + var output bytes.Buffer + logger = zerolog.New(&output) + + require.NoError(t, cmd.Parent().PersistentPreRunE(cmd, nil)) + if tc.wantWarn { + assert.Contains(t, output.String(), `"level":"warn"`) + assert.NotContains(t, output.String(), `"level":"debug"`) + } else { + assert.Empty(t, output.String()) + } + + wantLevel := zerolog.InfoLevel + if tc.debug { + wantLevel = zerolog.DebugLevel + } + assert.Equal(t, wantLevel, logger.GetLevel()) + }) + } +} + func newAttestationOrganizationTestCommand(t *testing.T, command, savedOrganization string) (*cobra.Command, *cobra.Command) { t.Helper() From 34a2ac772e5756bbe113b662b64f48676302b6df Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Fri, 9 Oct 2026 18:19:16 +0200 Subject: [PATCH 2/2] fix(cli): skip missing .chainloop.yml in attestation init project lookup attestation init reads .chainloop.yml a second time for project metadata and logged a debug line when the file was missing. A missing file is the normal case, so do not log it. Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino Chainloop-Trace-Sessions: 256841e0-47e6-4647-b095-1cce91a60480 --- app/cli/cmd/attestation_init.go | 6 ++++-- app/cli/cmd/attestation_test.go | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/cli/cmd/attestation_init.go b/app/cli/cmd/attestation_init.go index 483d9d9d5..7ca50bc36 100644 --- a/app/cli/cmd/attestation_init.go +++ b/app/cli/cmd/attestation_init.go @@ -61,9 +61,11 @@ func newAttestationInitCmd() *cobra.Command { // Load unresolved project metadata from the repository config. if projectName == "" || (projectVersion == "" && !useLatestVersion) { cfg, path, err := repositoryconfig.LoadChainloopYML(".") - if err != nil { + switch { + case errors.Is(err, repositoryconfig.ErrChainloopYMLNotFound): + case err != nil: logger.Debug().Msgf("failed to load chainloop config: %s", err) - } else { + default: logger.Debug().Msgf("loaded project metadata from config file %s", path) if projectName == "" { projectName = cfg.ProjectName diff --git a/app/cli/cmd/attestation_test.go b/app/cli/cmd/attestation_test.go index 78ff84964..b6f8262dd 100644 --- a/app/cli/cmd/attestation_test.go +++ b/app/cli/cmd/attestation_test.go @@ -311,12 +311,20 @@ func TestAttestationRepositoryConfigLookupLogging(t *testing.T) { logger = zerolog.New(&output) require.NoError(t, cmd.Parent().PersistentPreRunE(cmd, nil)) + // init reads .chainloop.yml again for the project metadata. --release + // without a version makes PreRunE stop right after that lookup. + require.NoError(t, cmd.Flags().Set("workflow", "build")) + require.NoError(t, cmd.Flags().Set("release", "true")) + require.EqualError(t, cmd.PreRunE(cmd, nil), "project version is required when using --release") + if tc.wantWarn { assert.Contains(t, output.String(), `"level":"warn"`) - assert.NotContains(t, output.String(), `"level":"debug"`) } else { assert.Empty(t, output.String()) } + if !tc.debug { + assert.NotContains(t, output.String(), `"level":"debug"`) + } wantLevel := zerolog.InfoLevel if tc.debug {