Skip to content

Commit 5ebc748

Browse files
joobisbmatifali
andauthored
feat: allow promoting an existing template version to active from CLI (coder#15051)
Co-authored-by: Muhammad Atif Ali <atif@coder.com>
1 parent 46cce33 commit 5ebc748

7 files changed

+225
-0
lines changed

cli/templateversions.go

+64
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func (r *RootCmd) templateVersions() *serpent.Command {
3232
r.templateVersionsList(),
3333
r.archiveTemplateVersion(),
3434
r.unarchiveTemplateVersion(),
35+
r.templateVersionsPromote(),
3536
},
3637
}
3738

@@ -169,3 +170,66 @@ func templateVersionsToRows(activeVersionID uuid.UUID, templateVersions ...coder
169170

170171
return rows
171172
}
173+
174+
func (r *RootCmd) templateVersionsPromote() *serpent.Command {
175+
var (
176+
templateName string
177+
templateVersionName string
178+
orgContext = NewOrganizationContext()
179+
)
180+
client := new(codersdk.Client)
181+
cmd := &serpent.Command{
182+
Use: "promote --template=<template_name> --template-version=<template_version_name>",
183+
Short: "Promote a template version to active.",
184+
Long: "Promote an existing template version to be the active version for the specified template.",
185+
Middleware: serpent.Chain(
186+
r.InitClient(client),
187+
),
188+
Handler: func(inv *serpent.Invocation) error {
189+
organization, err := orgContext.Selected(inv, client)
190+
if err != nil {
191+
return err
192+
}
193+
194+
template, err := client.TemplateByName(inv.Context(), organization.ID, templateName)
195+
if err != nil {
196+
return xerrors.Errorf("get template by name: %w", err)
197+
}
198+
199+
version, err := client.TemplateVersionByName(inv.Context(), template.ID, templateVersionName)
200+
if err != nil {
201+
return xerrors.Errorf("get template version by name: %w", err)
202+
}
203+
204+
err = client.UpdateActiveTemplateVersion(inv.Context(), template.ID, codersdk.UpdateActiveTemplateVersion{
205+
ID: version.ID,
206+
})
207+
if err != nil {
208+
return xerrors.Errorf("update active template version: %w", err)
209+
}
210+
211+
_, _ = fmt.Fprintf(inv.Stdout, "Successfully promoted version %q to active for template %q\n", templateVersionName, templateName)
212+
return nil
213+
},
214+
}
215+
216+
cmd.Options = serpent.OptionSet{
217+
{
218+
Flag: "template",
219+
FlagShorthand: "t",
220+
Env: "CODER_TEMPLATE_NAME",
221+
Description: "Specify the template name.",
222+
Required: true,
223+
Value: serpent.StringOf(&templateName),
224+
},
225+
{
226+
Flag: "template-version",
227+
Description: "Specify the template version name to promote.",
228+
Env: "CODER_TEMPLATE_VERSION_NAME",
229+
Required: true,
230+
Value: serpent.StringOf(&templateVersionName),
231+
},
232+
}
233+
orgContext.AttachOptions(cmd)
234+
return cmd
235+
}

cli/templateversions_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package cli_test
22

33
import (
4+
"context"
45
"testing"
56

7+
"github.com/stretchr/testify/assert"
68
"github.com/stretchr/testify/require"
79

810
"github.com/coder/coder/v2/cli/clitest"
911
"github.com/coder/coder/v2/coderd/coderdtest"
12+
"github.com/coder/coder/v2/codersdk"
1013
"github.com/coder/coder/v2/pty/ptytest"
1114
)
1215

@@ -38,3 +41,85 @@ func TestTemplateVersions(t *testing.T) {
3841
pty.ExpectMatch("Active")
3942
})
4043
}
44+
45+
func TestTemplateVersionsPromote(t *testing.T) {
46+
t.Parallel()
47+
48+
t.Run("PromoteVersion", func(t *testing.T) {
49+
t.Parallel()
50+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
51+
owner := coderdtest.CreateFirstUser(t, client)
52+
53+
// Create a template with two versions
54+
version1 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, completeWithAgent())
55+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
56+
57+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version1.ID)
58+
59+
version2 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, completeWithAgent(), func(ctvr *codersdk.CreateTemplateVersionRequest) {
60+
ctvr.TemplateID = template.ID
61+
ctvr.Name = "2.0.0"
62+
})
63+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version2.ID)
64+
65+
// Ensure version1 is active
66+
updatedTemplate, err := client.Template(context.Background(), template.ID)
67+
assert.NoError(t, err)
68+
assert.Equal(t, version1.ID, updatedTemplate.ActiveVersionID)
69+
70+
args := []string{
71+
"templates",
72+
"versions",
73+
"promote",
74+
"--template", template.Name,
75+
"--template-version", version2.Name,
76+
}
77+
78+
inv, root := clitest.New(t, args...)
79+
//nolint:gocritic // Creating a workspace for another user requires owner permissions.
80+
clitest.SetupConfig(t, client, root)
81+
errC := make(chan error)
82+
go func() {
83+
errC <- inv.Run()
84+
}()
85+
86+
require.NoError(t, <-errC)
87+
88+
// Verify that version2 is now the active version
89+
updatedTemplate, err = client.Template(context.Background(), template.ID)
90+
require.NoError(t, err)
91+
assert.Equal(t, version2.ID, updatedTemplate.ActiveVersionID)
92+
})
93+
94+
t.Run("PromoteNonExistentVersion", func(t *testing.T) {
95+
t.Parallel()
96+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
97+
owner := coderdtest.CreateFirstUser(t, client)
98+
member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
99+
100+
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
101+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
102+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
103+
104+
inv, root := clitest.New(t, "templates", "versions", "promote", "--template", template.Name, "--template-version", "non-existent-version")
105+
clitest.SetupConfig(t, member, root)
106+
107+
err := inv.Run()
108+
require.Error(t, err)
109+
require.Contains(t, err.Error(), "get template version by name")
110+
})
111+
112+
t.Run("PromoteVersionInvalidTemplate", func(t *testing.T) {
113+
t.Parallel()
114+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
115+
owner := coderdtest.CreateFirstUser(t, client)
116+
member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
117+
118+
inv, root := clitest.New(t, "templates", "versions", "promote", "--template", "non-existent-template", "--template-version", "some-version")
119+
clitest.SetupConfig(t, member, root)
120+
121+
err := inv.Run()
122+
require.Error(t, err)
123+
require.Contains(t, err.Error(), "get template by name")
124+
})
125+
}

cli/testdata/coder_templates_versions_--help.golden

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ USAGE:
1414
SUBCOMMANDS:
1515
archive Archive a template version(s).
1616
list List all the versions of the specified template
17+
promote Promote a template version to active.
1718
unarchive Unarchive a template version(s).
1819

1920
———
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
coder v0.0.0-devel
2+
3+
USAGE:
4+
coder templates versions promote [flags] --template=<template_name>
5+
--template-version=<template_version_name>
6+
7+
Promote a template version to active.
8+
9+
Promote an existing template version to be the active version for the
10+
specified template.
11+
12+
OPTIONS:
13+
-O, --org string, $CODER_ORGANIZATION
14+
Select which organization (uuid or name) to use.
15+
16+
-t, --template string, $CODER_TEMPLATE_NAME
17+
Specify the template name.
18+
19+
--template-version string, $CODER_TEMPLATE_VERSION_NAME
20+
Specify the template version name to promote.
21+
22+
———
23+
Run `coder --help` for a list of global options.

docs/manifest.json

+5
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,11 @@
13141314
"description": "List all the versions of the specified template",
13151315
"path": "reference/cli/templates_versions_list.md"
13161316
},
1317+
{
1318+
"title": "templates versions promote",
1319+
"description": "Promote a template version to active.",
1320+
"path": "reference/cli/templates_versions_promote.md"
1321+
},
13171322
{
13181323
"title": "templates versions unarchive",
13191324
"description": "Unarchive a template version(s).",

docs/reference/cli/templates_versions.md

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/cli/templates_versions_promote.md

+46
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)