Skip to content

Commit 4a060c5

Browse files
authored
feat(attestation): Include attestation output automatically on GitHub and GitLab reports (#2668)
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
1 parent 2107f17 commit 4a060c5

15 files changed

Lines changed: 119 additions & 10 deletions

app/cli/cmd/attestation_push.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222

23+
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2324
"github.com/spf13/cobra"
2425
"google.golang.org/grpc/codes"
2526
"google.golang.org/grpc/status"
@@ -37,6 +38,7 @@ func newAttestationPushCmd() *cobra.Command {
3738
signServerAuthCertPath string
3839
signServerAuthCertPass string
3940
bypassPolicyCheck bool
41+
deactivateCIReport bool
4042
)
4143

4244
cmd := &cobra.Command{
@@ -106,15 +108,29 @@ func newAttestationPushCmd() *cobra.Command {
106108

107109
res.Status.Digest = res.Digest
108110

109-
// If we are returning the json format, we also want to render the attestation table as one property so it can also be consumed
110-
if flagOutputFormat == output.FormatJSON {
111-
// Render the attestation status to a string
112-
buf := &bytes.Buffer{}
113-
if err := fullStatusTableWithWriter(res.Status, buf); err != nil {
114-
return fmt.Errorf("failed to render output: %w", err)
115-
}
111+
// Render the attestation status to a string
112+
buf := &bytes.Buffer{}
113+
if err := fullStatusTableWithWriter(res.Status, buf); err != nil {
114+
return fmt.Errorf("failed to render output: %w", err)
115+
}
116116

117-
res.Status.TerminalOutput = buf.Bytes()
117+
res.Status.TerminalOutput = buf.Bytes()
118+
119+
// Report to CI/CD platform if supported and not disabled
120+
if !deactivateCIReport && !res.Status.DryRun && res.Status.RunnerContext != nil {
121+
// Clean up possible ANSI characters from the output
122+
sanitizedOutput := removeAnsiCharactersFromBytes(res.Status.TerminalOutput)
123+
if err := res.Status.RunnerContext.RawRunner.Report(sanitizedOutput); err != nil {
124+
logger.Warn().Err(err).Msg("failed to write CI/CD platform report")
125+
} else {
126+
// Log success message based on runner type
127+
switch res.Status.RunnerContext.RawRunner.ID() {
128+
case schemaapi.CraftingSchema_Runner_GITHUB_ACTION:
129+
logger.Info().Msg("attestation report written to GitHub step summary")
130+
case schemaapi.CraftingSchema_Runner_GITLAB_PIPELINE:
131+
logger.Info().Msg("attestation report written to chainloop-attestation-report.txt artifact")
132+
}
133+
}
118134
}
119135

120136
// In TABLE format, we render the attestation status
@@ -160,6 +176,7 @@ func newAttestationPushCmd() *cobra.Command {
160176
cmd.Flags().StringVar(&signServerAuthCertPath, "signserver-client-cert", "", "path to client certificate in PEM format for authenticated SignServer TLS connection")
161177
cmd.Flags().StringVar(&signServerAuthCertPass, "signserver-client-pass", "", "certificate passphrase for authenticated SignServer TLS connection")
162178
cmd.Flags().BoolVar(&bypassPolicyCheck, exceptionFlagName, false, "do not fail this command on policy violations enforcement")
179+
cmd.Flags().BoolVar(&deactivateCIReport, "deactivate-ci-report", false, "deactivate automatic attestation report to CI/CD platform")
163180

164181
return cmd
165182
}

app/cli/cmd/attestation_status.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"io"
2121
"os"
22+
"regexp"
2223
"slices"
2324
"strings"
2425
"time"
@@ -302,3 +303,11 @@ func versionStringAttFinal(p *action.ProjectVersion) string {
302303

303304
return p.Version
304305
}
306+
307+
// removeAnsiCharactersFromBytes removes ANSI escape codes from bytes slices.
308+
// Credits to: https://github.com/acarl005/stripansi
309+
func removeAnsiCharactersFromBytes(input []byte) []byte {
310+
const ansiPattern = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
311+
re := regexp.MustCompile(ansiPattern)
312+
return re.ReplaceAll(input, []byte(""))
313+
}

app/cli/documentation/cli-reference.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ Options
341341
--annotation strings additional annotation in the format of key=value
342342
--attestation-id string Unique identifier of the in-progress attestation
343343
--bundle string output a Sigstore bundle to the provided path
344+
--deactivate-ci-report deactivate automatic attestation report to CI/CD platform
344345
--exception-bypass-policy-check do not fail this command on policy violations enforcement
345346
-h, --help help for push
346347
-k, --key string reference (path or env variable name) to the cosign or KMS key that will be used to sign the attestation

app/cli/pkg/action/attestation_push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2024 The Chainloop Authors.
2+
// Copyright 2024-2025 The Chainloop Authors.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.

app/cli/pkg/action/attestation_status.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type AttestationStatusResult struct {
6666
type AttestationResultRunnerContext struct {
6767
EnvVars map[string]string
6868
JobURL, RunnerType string
69+
RawRunner crafter.SupportedRunner
6970
}
7071

7172
type AttestationStatusWorkflowMeta struct {
@@ -202,6 +203,7 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string,
202203
EnvVars: runnerEnvVars,
203204
RunnerType: att.RunnerType.String(),
204205
JobURL: att.RunnerUrl,
206+
RawRunner: c.Runner,
205207
}
206208

207209
return res, nil

pkg/attestation/crafter/runner.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ type SupportedRunner interface {
5858
// Returns nil if verification is not supported or not applicable for this runner.
5959
// Non-blocking: errors are logged and returned as unavailable status.
6060
VerifyCommitSignature(ctx context.Context, commitHash string) *commitverification.CommitVerification
61+
62+
// Report writes attestation table output to platform-specific location.
63+
// Returns nil if platform doesn't support reporting.
64+
Report(tableOutput []byte) error
6165
}
6266

6367
type RunnerM map[schemaapi.CraftingSchema_Runner_RunnerType]SupportedRunner

pkg/attestation/crafter/runners/azurepipeline.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,7 @@ func (r *AzurePipeline) Environment() RunnerEnvironment {
101101
func (r *AzurePipeline) VerifyCommitSignature(_ context.Context, _ string) *commitverification.CommitVerification {
102102
return nil // Not supported for this runner
103103
}
104+
105+
func (r *AzurePipeline) Report(_ []byte) error {
106+
return nil
107+
}

pkg/attestation/crafter/runners/circleci_build.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,7 @@ func (r *CircleCIBuild) Environment() RunnerEnvironment {
8181
func (r *CircleCIBuild) VerifyCommitSignature(_ context.Context, _ string) *commitverification.CommitVerification {
8282
return nil // Not supported for this runner
8383
}
84+
85+
func (r *CircleCIBuild) Report(_ []byte) error {
86+
return nil
87+
}

pkg/attestation/crafter/runners/daggerpipeline.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,7 @@ func (r *DaggerPipeline) verifyCommitViaGitLab(ctx context.Context, commitHash s
181181
// Call GitLab API to verify commit
182182
return commitverification.VerifyGitLabCommit(ctx, baseURL, projectPath, commitHash, token, r.logger)
183183
}
184+
185+
func (r *DaggerPipeline) Report(_ []byte) error {
186+
return nil
187+
}

pkg/attestation/crafter/runners/generic.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2023 The Chainloop Authors.
2+
// Copyright 2023-2026 The Chainloop Authors.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
@@ -65,3 +65,7 @@ func (r *Generic) Environment() RunnerEnvironment {
6565
func (r *Generic) VerifyCommitSignature(_ context.Context, _ string) *commitverification.CommitVerification {
6666
return nil // Not supported for this runner
6767
}
68+
69+
func (r *Generic) Report(_ []byte) error {
70+
return nil
71+
}

0 commit comments

Comments
 (0)