Skip to content

Commit b6f5f04

Browse files
authored
feat: expose prerelease info (#1472)
Signed-off-by: Miguel Martinez <miguel@chainloop.dev>
1 parent 9f7e746 commit b6f5f04

47 files changed

Lines changed: 1208 additions & 377 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/cli/cmd/attestation_init.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func newAttestationInitCmd() *cobra.Command {
3131
workflowName string
3232
projectName string
3333
projectVersion string
34+
projectVersionRelease bool
3435
newWorkflowcontractName string
3536
)
3637

@@ -60,6 +61,10 @@ func newAttestationInitCmd() *cobra.Command {
6061
projectVersion = cfg.ProjectVersion
6162
}
6263

64+
if projectVersion == "" && projectVersionRelease {
65+
return errors.New("project version is required when using --release")
66+
}
67+
6368
return nil
6469
},
6570
RunE: func(cmd *cobra.Command, _ []string) error {
@@ -77,11 +82,12 @@ func newAttestationInitCmd() *cobra.Command {
7782

7883
// Initialize it
7984
attestationID, err := a.Run(cmd.Context(), &action.AttestationInitRunOpts{
80-
ContractRevision: contractRevision,
81-
ProjectName: projectName,
82-
ProjectVersion: projectVersion,
83-
WorkflowName: workflowName,
84-
NewWorkflowContractName: newWorkflowcontractName,
85+
ContractRevision: contractRevision,
86+
ProjectName: projectName,
87+
ProjectVersion: projectVersion,
88+
WorkflowName: workflowName,
89+
NewWorkflowContractName: newWorkflowcontractName,
90+
ProjectVersionMarkAsReleased: projectVersionRelease,
8591
})
8692

8793
if err != nil {
@@ -136,6 +142,7 @@ func newAttestationInitCmd() *cobra.Command {
136142
cmd.Flags().StringVar(&newWorkflowcontractName, "contract", "", "name of an existing contract to attach it to the auto-created workflow (it doesn't update an existing one)")
137143

138144
cmd.Flags().StringVar(&projectVersion, "version", "", "project version, i.e 0.1.0")
145+
cmd.Flags().BoolVar(&projectVersionRelease, "release", false, "promote the provided version as a release")
139146

140147
return cmd
141148
}

app/cli/cmd/attestation_status.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ func attestationStatusTableOutput(status *action.AttestationStatusResult, full b
8484
gt.AppendRow(table.Row{"Organization", meta.Organization})
8585
gt.AppendRow(table.Row{"Name", meta.Name})
8686
gt.AppendRow(table.Row{"Project", meta.Project})
87-
projectVersion := "none"
88-
if meta.ProjectVersion != "" {
89-
projectVersion = meta.ProjectVersion
87+
projectVersion := versionStringAttestation(meta.ProjectVersion)
88+
if projectVersion == "" {
89+
projectVersion = "none"
9090
}
9191

9292
gt.AppendRow(table.Row{"Version", projectVersion})
@@ -206,3 +206,22 @@ func hBool(b bool) string {
206206

207207
return "No"
208208
}
209+
210+
// Version information to be shown during the attestation process
211+
func versionStringAttestation(p *action.ProjectVersion) string {
212+
if p.Version == "" {
213+
return ""
214+
}
215+
216+
// We are releasing a pre-release at the end of the attestation
217+
if p.MarkAsReleased && p.Prerelease {
218+
return fmt.Sprintf("%s (will release)", p.Version)
219+
}
220+
221+
// The version loaded is a already released version
222+
if !p.Prerelease {
223+
return fmt.Sprintf("%s (released)", p.Version)
224+
}
225+
226+
return fmt.Sprintf("%s (prerelease)", p.Version)
227+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2024 The Chainloop Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmd
16+
17+
import (
18+
"testing"
19+
20+
"github.com/chainloop-dev/chainloop/app/cli/internal/action"
21+
"github.com/stretchr/testify/assert"
22+
)
23+
24+
func TestVersionStringAttestation(t *testing.T) {
25+
testCases := []struct {
26+
name string
27+
version *action.ProjectVersion
28+
expected string
29+
}{
30+
{
31+
name: "empty version",
32+
version: &action.ProjectVersion{
33+
Version: "",
34+
},
35+
expected: "",
36+
},
37+
{
38+
name: "prerelease version to be released",
39+
version: &action.ProjectVersion{
40+
Version: "1.0.0",
41+
Prerelease: true,
42+
MarkAsReleased: true,
43+
},
44+
expected: "1.0.0 (will release)",
45+
},
46+
{
47+
name: "already released version",
48+
version: &action.ProjectVersion{
49+
Version: "1.0.0",
50+
Prerelease: false,
51+
},
52+
expected: "1.0.0 (released)",
53+
},
54+
{
55+
name: "prerelease version",
56+
version: &action.ProjectVersion{
57+
Version: "1.0.0-rc1",
58+
Prerelease: true,
59+
},
60+
expected: "1.0.0-rc1 (prerelease)",
61+
},
62+
{
63+
name: "prerelease version not marked for release",
64+
version: &action.ProjectVersion{
65+
Version: "2.0.0-beta",
66+
Prerelease: true,
67+
MarkAsReleased: false,
68+
},
69+
expected: "2.0.0-beta (prerelease)",
70+
},
71+
}
72+
73+
for _, tc := range testCases {
74+
t.Run(tc.name, func(t *testing.T) {
75+
result := versionStringAttestation(tc.version)
76+
assert.Equal(t, tc.expected, result)
77+
})
78+
}
79+
}

app/cli/cmd/workflow_workflow_run_list.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func workflowRunListTableOutput(runs []*action.WorkflowRunItem) error {
9595
return nil
9696
}
9797

98-
header := table.Row{"ID", "Workflow", "Version", "State", "Created At", "Runner"}
98+
header := table.Row{"ID", "Workflow", "Version", "Prerelease", "State", "Created At", "Runner"}
9999
if full {
100100
header = append(header, "Finished At", "Failure reason")
101101
}
@@ -105,7 +105,7 @@ func workflowRunListTableOutput(runs []*action.WorkflowRunItem) error {
105105

106106
for _, p := range runs {
107107
wf := p.Workflow
108-
r := table.Row{p.ID, wf.NamespacedName(), p.ProjectVersion.Version, p.State, p.CreatedAt.Format(time.RFC822), p.RunnerType}
108+
r := table.Row{p.ID, wf.NamespacedName(), versionString(p.ProjectVersion), p.State, p.CreatedAt.Format(time.RFC822), p.RunnerType}
109109

110110
if full {
111111
var finishedAt string
@@ -121,6 +121,19 @@ func workflowRunListTableOutput(runs []*action.WorkflowRunItem) error {
121121
return nil
122122
}
123123

124+
func versionString(p *action.ProjectVersion) string {
125+
versionString := p.Version
126+
if versionString == "" {
127+
return ""
128+
}
129+
130+
if !p.Prerelease {
131+
return versionString
132+
}
133+
134+
return fmt.Sprintf("%s (prerelease)", p.Version)
135+
}
136+
124137
// listAvailableWorkflowStatusFlag returns a list of available workflow status flags
125138
func listAvailableWorkflowStatusFlag() []string {
126139
m := action.WorkflowRunStatus()

app/cli/internal/action/attestation_init.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ func NewAttestationInit(cfg *AttestationInitOpts) (*AttestationInit, error) {
7474

7575
// returns the attestation ID
7676
type AttestationInitRunOpts struct {
77-
ContractRevision int
78-
ProjectName string
79-
ProjectVersion string
80-
WorkflowName string
81-
NewWorkflowContractName string
77+
ContractRevision int
78+
ProjectName string
79+
ProjectVersion string
80+
ProjectVersionMarkAsReleased bool
81+
WorkflowName string
82+
NewWorkflowContractName string
8283
}
8384

8485
func (action *AttestationInit) Run(ctx context.Context, opts *AttestationInitRunOpts) (string, error) {
@@ -124,7 +125,10 @@ func (action *AttestationInit) Run(ctx context.Context, opts *AttestationInitRun
124125
Project: workflow.GetProject(),
125126
Team: workflow.GetTeam(),
126127
SchemaRevision: strconv.Itoa(int(contractVersion.GetRevision())),
127-
ProjectVersion: opts.ProjectVersion,
128+
ProjectVersion: &clientAPI.ProjectVersion{
129+
Version: opts.ProjectVersion,
130+
MarkAsReleased: opts.ProjectVersionMarkAsReleased,
131+
},
128132
}
129133

130134
action.Logger.Debug().Msg("workflow contract and metadata retrieved from the control plane")
@@ -165,6 +169,7 @@ func (action *AttestationInit) Run(ctx context.Context, opts *AttestationInitRun
165169
workflowRun := runResp.GetResult().GetWorkflowRun()
166170
workflowMeta.WorkflowRunId = workflowRun.GetId()
167171
workflowMeta.Organization = runResp.GetResult().GetOrganization()
172+
workflowMeta.ProjectVersion.Prerelease = runResp.GetResult().GetWorkflowRun().Version.GetPrerelease()
168173
action.Logger.Debug().Str("workflow-run-id", workflowRun.GetId()).Msg("attestation initialized in the control plane")
169174
attestationID = workflowRun.GetId()
170175
}

app/cli/internal/action/attestation_push.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru
7878
}
7979

8080
// Retrieve attestation status
81-
statusAction, err := NewAttestationStatus(&AttestationStatusOpts{ActionsOpts: action.ActionsOpts, UseAttestationRemoteState: useRemoteState})
81+
statusAction, err := NewAttestationStatus(&AttestationStatusOpts{
82+
ActionsOpts: action.ActionsOpts, UseAttestationRemoteState: useRemoteState, SkipReleaseInfo: true,
83+
})
84+
8285
if err != nil {
8386
return nil, fmt.Errorf("creating status action: %w", err)
8487
}
@@ -172,7 +175,9 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru
172175
return attestationResult, nil
173176
}
174177

175-
attestationResult.Digest, err = pushToControlPlane(ctx, action.ActionsOpts.CPConnection, envelope, crafter.CraftingState.Attestation.GetWorkflow().GetWorkflowRunId())
178+
workflow := crafter.CraftingState.Attestation.GetWorkflow()
179+
180+
attestationResult.Digest, err = pushToControlPlane(ctx, action.ActionsOpts.CPConnection, envelope, workflow.GetWorkflowRunId(), workflow.GetProjectVersion().GetMarkAsReleased())
176181
if err != nil {
177182
return nil, fmt.Errorf("pushing to control plane: %w", err)
178183
}
@@ -187,16 +192,17 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru
187192
return attestationResult, nil
188193
}
189194

190-
func pushToControlPlane(ctx context.Context, conn *grpc.ClientConn, envelope *dsse.Envelope, workflowRunID string) (string, error) {
195+
func pushToControlPlane(ctx context.Context, conn *grpc.ClientConn, envelope *dsse.Envelope, workflowRunID string, markVersionAsReleased bool) (string, error) {
191196
encodedAttestation, err := encodeEnvelope(envelope)
192197
if err != nil {
193198
return "", fmt.Errorf("encoding attestation: %w", err)
194199
}
195200

196201
client := pb.NewAttestationServiceClient(conn)
197202
resp, err := client.Store(ctx, &pb.AttestationServiceStoreRequest{
198-
Attestation: encodedAttestation,
199-
WorkflowRunId: workflowRunID,
203+
Attestation: encodedAttestation,
204+
WorkflowRunId: workflowRunID,
205+
MarkVersionAsReleased: &markVersionAsReleased,
200206
})
201207

202208
if err != nil {

app/cli/internal/action/attestation_status.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ import (
2828
type AttestationStatusOpts struct {
2929
*ActionsOpts
3030
UseAttestationRemoteState bool
31+
SkipReleaseInfo bool
3132
}
3233

3334
type AttestationStatus struct {
3435
*ActionsOpts
3536
c *crafter.Crafter
37+
// Do not show information about the project version release status
38+
skipReleaseInfo bool
3639
}
3740

3841
type AttestationStatusResult struct {
@@ -52,7 +55,8 @@ type AttestationResultRunnerContext struct {
5255
}
5356

5457
type AttestationStatusWorkflowMeta struct {
55-
WorkflowID, Name, Team, Project, ContractRevision, Organization, ProjectVersion string
58+
WorkflowID, Name, Team, Project, ContractRevision, Organization string
59+
ProjectVersion *ProjectVersion
5660
}
5761

5862
type AttestationStatusResultMaterial struct {
@@ -67,8 +71,9 @@ func NewAttestationStatus(cfg *AttestationStatusOpts) (*AttestationStatus, error
6771
}
6872

6973
return &AttestationStatus{
70-
ActionsOpts: cfg.ActionsOpts,
71-
c: c,
74+
ActionsOpts: cfg.ActionsOpts,
75+
c: c,
76+
skipReleaseInfo: cfg.SkipReleaseInfo,
7277
}, nil
7378
}
7479

@@ -98,13 +103,20 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string)
98103
Project: workflowMeta.GetProject(),
99104
Team: workflowMeta.GetTeam(),
100105
ContractRevision: workflowMeta.GetSchemaRevision(),
101-
ProjectVersion: workflowMeta.GetProjectVersion(),
106+
ProjectVersion: &ProjectVersion{
107+
Version: workflowMeta.GetProjectVersion().GetVersion(),
108+
},
102109
},
103110
InitializedAt: toTimePtr(att.InitializedAt.AsTime()),
104111
DryRun: c.CraftingState.DryRun,
105112
Annotations: pbAnnotationsToAction(c.CraftingState.InputSchema.GetAnnotations()),
106113
}
107114

115+
if !action.skipReleaseInfo {
116+
res.WorkflowMeta.ProjectVersion.Prerelease = workflowMeta.ProjectVersion.Prerelease
117+
res.WorkflowMeta.ProjectVersion.MarkAsReleased = workflowMeta.ProjectVersion.MarkAsReleased
118+
}
119+
108120
// Let's perform the following steps in order to show all possible materials:
109121
// 1. Populate the materials that are defined in the contract schema
110122
// 2. Populate the materials that are not defined in the contract schema, added inline in the attestation

app/cli/internal/action/workflow_run_list.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ type WorkflowRunItem struct {
5555
}
5656

5757
type ProjectVersion struct {
58-
ID string `json:"id"`
59-
Version string `json:"version,omitempty"`
58+
ID string `json:"id"`
59+
Version string `json:"version,omitempty"`
60+
Prerelease bool `json:"prerelease"`
61+
MarkAsReleased bool `json:"markAsRelease"`
6062
}
6163

6264
type PaginatedWorkflowRunItem struct {
@@ -126,8 +128,9 @@ func pbWorkflowRunItemToAction(in *pb.WorkflowRunItem) *WorkflowRunItem {
126128
ContractRevisionUsed: int(in.GetContractRevisionUsed()),
127129
ContractRevisionLatest: int(in.GetContractRevisionLatest()),
128130
ProjectVersion: &ProjectVersion{
129-
ID: in.GetVersion().GetId(),
130-
Version: in.GetVersion().GetVersion(),
131+
ID: in.GetVersion().GetId(),
132+
Version: in.GetVersion().GetVersion(),
133+
Prerelease: in.GetVersion().GetPrerelease(),
131134
},
132135
}
133136

0 commit comments

Comments
 (0)