Skip to content

Commit 4dcbc41

Browse files
authored
feat: rename default project version from empty string to "v0" (#3046)
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
1 parent 3592b7a commit 4dcbc41

13 files changed

Lines changed: 90 additions & 37 deletions

app/cli/cmd/attestation_status.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ func attestationStatusTableOutput(status *action.AttestationStatusResult, w io.W
102102
gt.AppendRow(table.Row{"Name", meta.Name})
103103
gt.AppendRow(table.Row{"Project", meta.Project})
104104
projectVersion := versionStringAttestation(meta.ProjectVersion, status.IsPushed)
105-
if projectVersion == "" {
106-
projectVersion = "none"
107-
}
108-
109105
gt.AppendRow(table.Row{"Version", projectVersion})
110106
gt.AppendRow(table.Row{"Contract", fmt.Sprintf("%s (revision %s)", meta.ContractName, meta.ContractRevision)})
111107
if status.RunnerContext.JobURL != "" {

app/cli/cmd/workflow_workflow_run_list.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2024-2025 The Chainloop Authors.
2+
// Copyright 2024-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.
@@ -130,13 +130,12 @@ func workflowRunListTableOutput(runs []*action.WorkflowRunItem) error {
130130
}
131131

132132
func versionString(p *action.ProjectVersion) string {
133-
versionString := p.Version
134-
if versionString == "" {
133+
if p.Version == "" {
135134
return ""
136135
}
137136

138137
if !p.Prerelease {
139-
return versionString
138+
return p.Version
140139
}
141140

142141
return fmt.Sprintf("%s (prerelease)", p.Version)

app/controlplane/pkg/biz/projectversion.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2024-2025 The Chainloop Authors.
2+
// Copyright 2024-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.
@@ -25,6 +25,9 @@ import (
2525
"github.com/google/uuid"
2626
)
2727

28+
// DefaultVersionName is the canonical name for the default/unversioned project version.
29+
const DefaultVersionName = "v0"
30+
2831
type ProjectVersion struct {
2932
// ID is the UUID of the project version.
3033
ID uuid.UUID
@@ -91,5 +94,14 @@ func (uc *ProjectVersionUseCase) Create(ctx context.Context, projectID, version
9194
return nil, NewErrInvalidUUID(err)
9295
}
9396

97+
// Treat empty version as the default for backward compatibility
98+
if version == "" {
99+
version = DefaultVersionName
100+
}
101+
102+
if err := ValidateVersion(version); err != nil {
103+
return nil, err
104+
}
105+
94106
return uc.projectRepo.Create(ctx, projectUUID, version, prerelease)
95107
}

app/controlplane/pkg/biz/version_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ type versionTestSuite struct {
2626
suite.Suite
2727
}
2828

29+
func (s *versionTestSuite) TestDefaultVersionNameIsValid() {
30+
s.NoError(biz.ValidateVersion(biz.DefaultVersionName))
31+
}
32+
2933
func (s *versionTestSuite) TestValidateVersion() {
3034
testCases := []struct {
3135
name string

app/controlplane/pkg/biz/workflow_integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func (s *workflowIntegrationTestSuite) TestCreate() {
210210
s.NotEmpty(got.ContractID)
211211
s.NotEmpty(got.ContractName)
212212
// There is a project version created
213-
pv, err := s.ProjectVersion.FindByProjectAndVersion(ctx, got.ProjectID.String(), "")
213+
pv, err := s.ProjectVersion.FindByProjectAndVersion(ctx, got.ProjectID.String(), biz.DefaultVersionName)
214214
s.NoError(err)
215215
s.NotNil(pv)
216216
})

app/controlplane/pkg/biz/workflowrun.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ func (uc *WorkflowRunUseCase) Create(ctx context.Context, opts *WorkflowRunCreat
255255
return nil, NewErrValidationStr("cannot specify both a project version and use-latest-version")
256256
}
257257

258+
// Treat empty version as the default for backward compatibility with old clients
259+
if opts.ProjectVersion == "" && !opts.UseLatestVersion {
260+
opts.ProjectVersion = DefaultVersionName
261+
}
262+
258263
if opts.ProjectVersion != "" {
259264
if err := ValidateVersion(opts.ProjectVersion); err != nil {
260265
return nil, err

app/controlplane/pkg/biz/workflowrun_integration_test.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ func (s *workflowRunIntegrationTestSuite) TestCreate() {
310310
RunnerType: "runnerType", RunnerRunURL: "runURL",
311311
})
312312
s.Require().NoError(err)
313-
// Load project version
314-
pv, err := s.ProjectVersion.FindByProjectAndVersion(ctx, s.workflowOrg1.ProjectID.String(), "")
313+
// Load project version — empty version is translated to DefaultVersionName
314+
pv, err := s.ProjectVersion.FindByProjectAndVersion(ctx, s.workflowOrg1.ProjectID.String(), biz.DefaultVersionName)
315315
s.Require().NoError(err)
316316
s.Equal("runnerType", run.RunnerType)
317317
s.Equal("runURL", run.RunURL)
@@ -321,26 +321,46 @@ func (s *workflowRunIntegrationTestSuite) TestCreate() {
321321

322322
s.T().Run("find or create version", func(_ *testing.T) {
323323
testCases := []struct {
324-
version string
324+
name string
325+
version string
326+
expectedVersion string
325327
}{
326-
{version: ""},
327-
{version: "custom"},
328+
{name: "empty string maps to default", version: "", expectedVersion: biz.DefaultVersionName},
329+
{name: "custom version", version: "custom", expectedVersion: "custom"},
328330
}
329331

330332
for _, tc := range testCases {
331-
run, err := s.WorkflowRun.Create(ctx, &biz.WorkflowRunCreateOpts{
332-
WorkflowID: s.workflowOrg1.ID.String(), ContractRevision: s.contractVersion, CASBackendID: s.casBackend.ID,
333-
RunnerType: "runnerType", RunnerRunURL: "runURL", ProjectVersion: tc.version,
333+
s.T().Run(tc.name, func(_ *testing.T) {
334+
run, err := s.WorkflowRun.Create(ctx, &biz.WorkflowRunCreateOpts{
335+
WorkflowID: s.workflowOrg1.ID.String(), ContractRevision: s.contractVersion, CASBackendID: s.casBackend.ID,
336+
RunnerType: "runnerType", RunnerRunURL: "runURL", ProjectVersion: tc.version,
337+
})
338+
s.Require().NoError(err)
339+
s.Equal(tc.expectedVersion, run.ProjectVersion.Version)
340+
pv, err := s.ProjectVersion.FindByProjectAndVersion(ctx, s.workflowOrg1.ProjectID.String(), tc.expectedVersion)
341+
s.Require().NoError(err)
342+
s.Equal(pv.ID, run.ProjectVersion.ID)
334343
})
335-
s.Require().NoError(err)
336-
// Load project version
337-
s.Equal(tc.version, run.ProjectVersion.Version)
338-
pv, err := s.ProjectVersion.FindByProjectAndVersion(ctx, s.workflowOrg1.ProjectID.String(), tc.version)
339-
s.Require().NoError(err)
340-
s.Equal(pv.ID, run.ProjectVersion.ID)
341344
}
342345
})
343346

347+
s.T().Run("explicit v0 uses same default version", func(_ *testing.T) {
348+
// First create a run without version (gets default "v0")
349+
runDefault, err := s.WorkflowRun.Create(ctx, &biz.WorkflowRunCreateOpts{
350+
WorkflowID: s.workflowOrg1.ID.String(), ContractRevision: s.contractVersion, CASBackendID: s.casBackend.ID,
351+
})
352+
s.Require().NoError(err)
353+
s.Equal(biz.DefaultVersionName, runDefault.ProjectVersion.Version)
354+
355+
// Now explicitly specify "v0" — should find the same version record
356+
runExplicit, err := s.WorkflowRun.Create(ctx, &biz.WorkflowRunCreateOpts{
357+
WorkflowID: s.workflowOrg1.ID.String(), ContractRevision: s.contractVersion, CASBackendID: s.casBackend.ID,
358+
ProjectVersion: biz.DefaultVersionName,
359+
})
360+
s.Require().NoError(err)
361+
s.Equal(runDefault.ProjectVersion.ID, runExplicit.ProjectVersion.ID)
362+
})
363+
344364
s.T().Run("use-latest-version resolves to version with latest=true", func(_ *testing.T) {
345365
// Create a named version first so we know which one is latest.
346366
namedRun, err := s.WorkflowRun.Create(ctx, &biz.WorkflowRunCreateOpts{
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- atlas:txmode none
2+
3+
-- Step 1: Rename any existing user-created "v0" versions to "v0.0"
4+
-- (avoids conflict when the empty-string default is renamed to "v0")
5+
UPDATE project_versions
6+
SET version = 'v0.0'
7+
WHERE version = 'v0'
8+
AND deleted_at IS NULL;
9+
10+
-- Step 2: Rename all default "" versions to "v0"
11+
UPDATE project_versions
12+
SET version = 'v0'
13+
WHERE version = ''
14+
AND deleted_at IS NULL;
15+
16+
-- Step 3: Change column default from '' to 'v0'
17+
ALTER TABLE "project_versions" ALTER COLUMN "version" SET DEFAULT 'v0';

app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
h1:/HATckRi5Q/sEHMczVjDJJ9VOwgJWiAp0Lpk8tmRVWk=
1+
h1:sNY7GgdTnqEyDG2nzVPtN7Vb4WM2agXX+GKsQJQvnCg=
22
20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M=
33
20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g=
44
20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI=
@@ -128,3 +128,4 @@ h1:/HATckRi5Q/sEHMczVjDJJ9VOwgJWiAp0Lpk8tmRVWk=
128128
20260303120000.sql h1:msXy2MRkzMOGxWbG1NOHh+PN5qjaBZcRzVT+7SFIwaA=
129129
20260318160301.sql h1:kH88s6pOi7Vprydb7xrzgY55JhMxfzY32txpQ8a1wEE=
130130
20260408122048.sql h1:imfswpfmBlpP1l149/wCLN5HkN3/sGIQ3GnxaSnwOZE=
131+
20260416153232.sql h1:xjEfZuMOo1lgZm3VUYGHpNOhpJixncVZuMRg0jiH+7A=

app/controlplane/pkg/data/ent/migrate/schema.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ var (
497497
// ProjectVersionsColumns holds the columns for the "project_versions" table.
498498
ProjectVersionsColumns = []*schema.Column{
499499
{Name: "id", Type: field.TypeUUID, Unique: true},
500-
{Name: "version", Type: field.TypeString, Default: ""},
500+
{Name: "version", Type: field.TypeString, Default: "v0"},
501501
{Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"},
502502
{Name: "updated_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"},
503503
{Name: "deleted_at", Type: field.TypeTime, Nullable: true},

0 commit comments

Comments
 (0)