Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion app/cli/cmd/attestation_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ package cmd

import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"time"

pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -65,6 +69,7 @@ func newAttestationPushCmd() *cobra.Command {
useAPIToken: "true",
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
info, err := executableInfo()
if err != nil {
return fmt.Errorf("getting executable information: %w", err)
Expand All @@ -91,7 +96,7 @@ func newAttestationPushCmd() *cobra.Command {
var res *action.AttestationResult
err = runWithBackoffRetry(
func() error {
res, err = a.Run(cmd.Context(), attestationID, annotations, bypassPolicyCheck)
res, err = a.Run(ctx, attestationID, annotations, bypassPolicyCheck)
return err
},
)
Expand All @@ -108,12 +113,23 @@ func newAttestationPushCmd() *cobra.Command {

res.Status.Digest = res.Digest

var attestationURL string
if !res.Status.DryRun && flagOutputFormat == output.FormatTable {
platformURL := fetchPlatformURL(ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this should come in the init and stored in the crafting state? That would be more in line on how the attestation process gets context today no?

attestationURL = buildAttestationViewURL(platformURL, res.Digest)
}

// Render the attestation status to a string
buf := &bytes.Buffer{}
if err := fullStatusTableWithWriter(res.Status, buf); err != nil {
return fmt.Errorf("failed to render output: %w", err)
}

// Append attestation URL if available
if attestationURL != "" {
fmt.Fprintf(buf, "\nView attestation details at: %s\n", attestationURL)
}

res.Status.TerminalOutput = buf.Bytes()

// Report to CI/CD platform if supported and not disabled
Expand Down Expand Up @@ -199,3 +215,35 @@ func NewGateError(policyName string) error {
func (e *GateError) Error() string {
return fmt.Sprintf("the policy %q is configured as a gate and has violations", e.PolicyName)
}

// fetchPlatformURL retrieves the platform URL from the control plane
// Returns empty string if not configured or if fetch fails
func fetchPlatformURL(ctx context.Context) string {
if ActionOpts.CPConnection == nil {
return ""
}

tmoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

client := pb.NewStatusServiceClient(ActionOpts.CPConnection)
resp, err := client.Infoz(tmoutCtx, &pb.InfozRequest{})
if err != nil {
logger.Debug().Err(err).Msg("failed to fetch platform URL")
return ""
}

return resp.PlatformUrl
}

// buildAttestationViewURL constructs the attestation view URL
// Returns empty string if platformURL is not configured
func buildAttestationViewURL(platformURL, digest string) string {
if platformURL == "" || digest == "" {
return ""
}

// Trim trailing slash from platform URL if present
platformURL = strings.TrimRight(platformURL, "/")
return fmt.Sprintf("%s/attestation/%s?tab=summary", platformURL, digest)
}
18 changes: 14 additions & 4 deletions app/controlplane/api/controlplane/v1/status.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/controlplane/api/controlplane/v1/status.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ message InfozResponse {
string chart_version = 3;
// Whether organization creation is restricted to admins
bool restricted_org_creation = 4;
// Link to the platform UI, if available
string platform_url = 5;
}

message StatuszResponse {}
17 changes: 16 additions & 1 deletion app/controlplane/api/gen/frontend/controlplane/v1/status.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ message Bootstrap {
string token = 2 [(buf.validate.field).string.min_len = 1];
}
}

// External URL of the platform UI, if available
string platform_external_url = 19;
}

message FederatedAuthentication {
Expand Down
1 change: 1 addition & 0 deletions app/controlplane/internal/service/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (s *StatusService) Statusz(ctx context.Context, r *pb.StatuszRequest) (*pb.
func (s *StatusService) Infoz(_ context.Context, _ *pb.InfozRequest) (*pb.InfozResponse, error) {
return &pb.InfozResponse{
LoginUrl: s.loginURL,
PlatformUrl: s.bootstrap.PlatformExternalUrl,
Version: s.version,
ChartVersion: os.Getenv("CHART_VERSION"),
RestrictedOrgCreation: s.bootstrap.RestrictOrgCreation,
Expand Down
2 changes: 1 addition & 1 deletion deployment/chainloop/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Chainloop is an open source software supply chain control plane, a

type: application
# Bump the patch (not minor, not major) version on each change in the Chart Source code
version: 1.321.0
version: 1.321.1
# Do not update appVersion, this is handled automatically by the release process
appVersion: v1.71.1

Expand Down
3 changes: 3 additions & 0 deletions deployment/chainloop/templates/controlplane/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ data:
{{- end }}
plugins_dir: {{ .Values.controlplane.pluginsDir }}
restrict_org_creation: {{ .Values.controlplane.restrictOrgCreation }}
{{- if .Values.controlplane.platformExternalUrl }}
platform_external_url: {{ .Values.controlplane.platformExternalUrl | quote }}
{{- end }}
referrer_shared_index:
{{- toYaml .Values.controlplane.referrerSharedIndex | nindent 6 }}
{{ if .Values.controlplane.onboarding }}
Expand Down
5 changes: 4 additions & 1 deletion deployment/chainloop/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ controlplane:

## @skip controlplane.restrictOrgCreation Restrict organization creation to instance admins
restrictOrgCreation: false


## @param controlplane.platformExternalUrl Optional external URL for the platform UI. If set, CLI will display attestation view links after push
platformExternalUrl: ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

platform might be overloaded, maybe uiDashboardURL?


## @extra controlplane.nats optional NATS configuration for events publishing.
## @param controlplane.nats.enabled Enable events publishing through a Nats stream
## @param controlplane.nats.host NATS Host
Expand Down
Loading