Skip to content

Commit 17132e6

Browse files
committed
Merge remote-tracking branch 'origin/main' into stevenmasley/archive_api_cli
2 parents 2a2de29 + 69d13f1 commit 17132e6

File tree

12 files changed

+233
-33
lines changed

12 files changed

+233
-33
lines changed

cli/templatepull.go

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ import (
1515
)
1616

1717
func (r *RootCmd) templatePull() *clibase.Cmd {
18-
var tarMode bool
18+
var (
19+
tarMode bool
20+
versionName string
21+
)
1922

2023
client := new(codersdk.Client)
2124
cmd := &clibase.Cmd{
2225
Use: "pull <name> [destination]",
23-
Short: "Download the latest version of a template to a path.",
26+
Short: "Download the active, latest, or specified version of a template to a path.",
2427
Middleware: clibase.Chain(
2528
clibase.RequireRangeArgs(1, 2),
2629
r.InitClient(client),
@@ -36,39 +39,67 @@ func (r *RootCmd) templatePull() *clibase.Cmd {
3639
dest = inv.Args[1]
3740
}
3841

39-
// TODO(JonA): Do we need to add a flag for organization?
4042
organization, err := CurrentOrganization(inv, client)
4143
if err != nil {
42-
return xerrors.Errorf("current organization: %w", err)
44+
return xerrors.Errorf("get current organization: %w", err)
4345
}
4446

4547
template, err := client.TemplateByName(ctx, organization.ID, templateName)
4648
if err != nil {
47-
return xerrors.Errorf("template by name: %w", err)
49+
return xerrors.Errorf("get template by name: %w", err)
4850
}
4951

50-
// Pull the versions for the template. We'll find the latest
51-
// one and download the source.
52-
versions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
53-
TemplateID: template.ID,
54-
})
55-
if err != nil {
56-
return xerrors.Errorf("template versions by template: %w", err)
57-
}
52+
var latestVersion codersdk.TemplateVersion
53+
{
54+
// Determine the latest template version and compare with the
55+
// active version. If they aren't the same, warn the user.
56+
versions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
57+
TemplateID: template.ID,
58+
})
59+
if err != nil {
60+
return xerrors.Errorf("template versions by template: %w", err)
61+
}
62+
63+
if len(versions) == 0 {
64+
return xerrors.Errorf("no template versions for template %q", templateName)
65+
}
66+
67+
// Sort the slice from newest to oldest template.
68+
sort.SliceStable(versions, func(i, j int) bool {
69+
return versions[i].CreatedAt.After(versions[j].CreatedAt)
70+
})
5871

59-
if len(versions) == 0 {
60-
return xerrors.Errorf("no template versions for template %q", templateName)
72+
latestVersion = versions[0]
6173
}
6274

63-
// Sort the slice from newest to oldest template.
64-
sort.SliceStable(versions, func(i, j int) bool {
65-
return versions[i].CreatedAt.After(versions[j].CreatedAt)
66-
})
75+
var templateVersion codersdk.TemplateVersion
76+
switch versionName {
77+
case "", "active":
78+
activeVersion, err := client.TemplateVersion(ctx, template.ActiveVersionID)
79+
if err != nil {
80+
return xerrors.Errorf("get active template version: %w", err)
81+
}
82+
if versionName == "" && activeVersion.ID != latestVersion.ID {
83+
cliui.Warn(inv.Stderr,
84+
"A newer template version than the active version exists. Pulling the active version instead.",
85+
"Use "+cliui.Code("--template latest")+" to pull the latest version.",
86+
)
87+
}
88+
templateVersion = activeVersion
89+
case "latest":
90+
templateVersion = latestVersion
91+
default:
92+
version, err := client.TemplateVersionByName(ctx, template.ID, versionName)
93+
if err != nil {
94+
return xerrors.Errorf("get template version: %w", err)
95+
}
96+
templateVersion = version
97+
}
6798

68-
latest := versions[0]
99+
cliui.Info(inv.Stderr, "Pulling template version "+cliui.Bold(templateVersion.Name)+"...")
69100

70101
// Download the tar archive.
71-
raw, ctype, err := client.Download(ctx, latest.Job.FileID)
102+
raw, ctype, err := client.Download(ctx, templateVersion.Job.FileID)
72103
if err != nil {
73104
return xerrors.Errorf("download template: %w", err)
74105
}
@@ -121,6 +152,12 @@ func (r *RootCmd) templatePull() *clibase.Cmd {
121152

122153
Value: clibase.BoolOf(&tarMode),
123154
},
155+
{
156+
Description: "The name of the template version to pull. Use 'active' to pull the active version, 'latest' to pull the latest version, or the name of the template version to pull.",
157+
Flag: "version",
158+
159+
Value: clibase.StringOf(&versionName),
160+
},
124161
cliui.SkipPromptOption(),
125162
}
126163

cli/templatepull_test.go

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/hex"
88
"os"
99
"path/filepath"
10+
"strings"
1011
"testing"
1112

1213
"github.com/codeclysm/extract/v3"
@@ -49,7 +50,7 @@ func TestTemplatePull_NoName(t *testing.T) {
4950
require.Error(t, err)
5051
}
5152

52-
// Stdout tests that 'templates pull' pulls down the latest template
53+
// Stdout tests that 'templates pull' pulls down the active template
5354
// and writes it to stdout.
5455
func TestTemplatePull_Stdout(t *testing.T) {
5556
t.Parallel()
@@ -78,6 +79,7 @@ func TestTemplatePull_Stdout(t *testing.T) {
7879
// are being sorted correctly.
7980
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
8081
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
82+
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
8183

8284
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name)
8385
clitest.SetupConfig(t, templateAdmin, root)
@@ -91,7 +93,123 @@ func TestTemplatePull_Stdout(t *testing.T) {
9193
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
9294
}
9395

94-
// ToDir tests that 'templates pull' pulls down the latest template
96+
// Stdout tests that 'templates pull' pulls down the non-latest active template
97+
// and writes it to stdout.
98+
func TestTemplatePull_ActiveOldStdout(t *testing.T) {
99+
t.Parallel()
100+
101+
client := coderdtest.New(t, &coderdtest.Options{
102+
IncludeProvisionerDaemon: true,
103+
})
104+
user := coderdtest.CreateFirstUser(t, client)
105+
106+
source1 := genTemplateVersionSource()
107+
source2 := genTemplateVersionSource()
108+
109+
expected, err := echo.Tar(source1)
110+
require.NoError(t, err)
111+
112+
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, source1)
113+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
114+
115+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
116+
117+
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source2, template.ID)
118+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
119+
120+
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name)
121+
clitest.SetupConfig(t, client, root)
122+
123+
var buf bytes.Buffer
124+
inv.Stdout = &buf
125+
var stderr strings.Builder
126+
inv.Stderr = &stderr
127+
128+
err = inv.Run()
129+
require.NoError(t, err)
130+
131+
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
132+
require.Contains(t, stderr.String(), "A newer template version than the active version exists.")
133+
}
134+
135+
// Stdout tests that 'templates pull' pulls down the specified template and
136+
// writes it to stdout.
137+
func TestTemplatePull_SpecifiedStdout(t *testing.T) {
138+
t.Parallel()
139+
140+
client := coderdtest.New(t, &coderdtest.Options{
141+
IncludeProvisionerDaemon: true,
142+
})
143+
user := coderdtest.CreateFirstUser(t, client)
144+
145+
source1 := genTemplateVersionSource()
146+
source2 := genTemplateVersionSource()
147+
source3 := genTemplateVersionSource()
148+
149+
expected, err := echo.Tar(source1)
150+
require.NoError(t, err)
151+
152+
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, source1)
153+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
154+
155+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
156+
157+
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source2, template.ID)
158+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
159+
160+
updatedVersion2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source3, template.ID)
161+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion2.ID)
162+
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion2.ID)
163+
164+
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name, "--version", version1.Name)
165+
clitest.SetupConfig(t, client, root)
166+
167+
var buf bytes.Buffer
168+
inv.Stdout = &buf
169+
170+
err = inv.Run()
171+
require.NoError(t, err)
172+
173+
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
174+
}
175+
176+
// Stdout tests that 'templates pull' pulls down the latest template
177+
// and writes it to stdout.
178+
func TestTemplatePull_LatestStdout(t *testing.T) {
179+
t.Parallel()
180+
181+
client := coderdtest.New(t, &coderdtest.Options{
182+
IncludeProvisionerDaemon: true,
183+
})
184+
user := coderdtest.CreateFirstUser(t, client)
185+
186+
source1 := genTemplateVersionSource()
187+
source2 := genTemplateVersionSource()
188+
189+
expected, err := echo.Tar(source1)
190+
require.NoError(t, err)
191+
192+
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, source1)
193+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
194+
195+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
196+
197+
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source2, template.ID)
198+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
199+
200+
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name, "latest")
201+
clitest.SetupConfig(t, client, root)
202+
203+
var buf bytes.Buffer
204+
inv.Stdout = &buf
205+
206+
err = inv.Run()
207+
require.NoError(t, err)
208+
209+
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
210+
}
211+
212+
// ToDir tests that 'templates pull' pulls down the active template
95213
// and writes it to the correct directory.
96214
func TestTemplatePull_ToDir(t *testing.T) {
97215
t.Parallel()
@@ -120,6 +238,7 @@ func TestTemplatePull_ToDir(t *testing.T) {
120238
// are being sorted correctly.
121239
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
122240
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
241+
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
123242

124243
dir := t.TempDir()
125244

@@ -143,8 +262,9 @@ func TestTemplatePull_ToDir(t *testing.T) {
143262
)
144263
}
145264

146-
// ToDir tests that 'templates pull' pulls down the latest template
147-
// and writes it to a directory with the name of the template if the path is not implicitly supplied.
265+
// ToDir tests that 'templates pull' pulls down the active template and writes
266+
// it to a directory with the name of the template if the path is not implicitly
267+
// supplied.
148268
// nolint: paralleltest
149269
func TestTemplatePull_ToImplicit(t *testing.T) {
150270
client := coderdtest.New(t, &coderdtest.Options{
@@ -171,6 +291,7 @@ func TestTemplatePull_ToImplicit(t *testing.T) {
171291
// are being sorted correctly.
172292
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
173293
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
294+
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
174295

175296
// create a tempdir and change the working directory to it for the duration of the test (cannot run in parallel)
176297
dir := t.TempDir()
@@ -233,6 +354,7 @@ func TestTemplatePull_FolderConflict(t *testing.T) {
233354
// are being sorted correctly.
234355
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
235356
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
357+
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
236358

237359
dir := t.TempDir()
238360

cli/testdata/coder_templates_--help.golden

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ SUBCOMMANDS:
3030
edit Edit the metadata of a template by name.
3131
init Get started with a templated template.
3232
list List all the templates available for the organization
33-
pull Download the latest version of a template to a path.
33+
pull Download the active, latest, or specified version of a template
34+
to a path.
3435
push Push a new template version from the current directory or as
3536
specified by flag
3637
versions Manage different versions of the specified template

cli/testdata/coder_templates_pull_--help.golden

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ coder v0.0.0-devel
33
USAGE:
44
coder templates pull [flags] <name> [destination]
55

6-
Download the latest version of a template to a path.
6+
Download the active, latest, or specified version of a template to a path.
77

88
OPTIONS:
99
--tar bool
1010
Output the template as a tar archive to stdout.
1111

12+
--version string
13+
The name of the template version to pull. Use 'active' to pull the
14+
active version, 'latest' to pull the latest version, or the name of
15+
the template version to pull.
16+
1217
-y, --yes bool
1318
Bypass prompts.
1419

coderd/coderdtest/coderdtest.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -753,11 +753,12 @@ func CreateTemplate(t *testing.T, client *codersdk.Client, organization uuid.UUI
753753
// UpdateTemplateVersion creates a new template version with the "echo" provisioner
754754
// and associates it with the given templateID.
755755
func UpdateTemplateVersion(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, res *echo.Responses, templateID uuid.UUID) codersdk.TemplateVersion {
756+
ctx := context.Background()
756757
data, err := echo.Tar(res)
757758
require.NoError(t, err)
758-
file, err := client.Upload(context.Background(), codersdk.ContentTypeTar, bytes.NewReader(data))
759+
file, err := client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(data))
759760
require.NoError(t, err)
760-
templateVersion, err := client.CreateTemplateVersion(context.Background(), organizationID, codersdk.CreateTemplateVersionRequest{
761+
templateVersion, err := client.CreateTemplateVersion(ctx, organizationID, codersdk.CreateTemplateVersionRequest{
761762
TemplateID: templateID,
762763
FileID: file.ID,
763764
StorageMethod: codersdk.ProvisionerStorageMethodFile,
@@ -767,6 +768,13 @@ func UpdateTemplateVersion(t *testing.T, client *codersdk.Client, organizationID
767768
return templateVersion
768769
}
769770

771+
func UpdateActiveTemplateVersion(t *testing.T, client *codersdk.Client, templateID, versionID uuid.UUID) {
772+
err := client.UpdateActiveTemplateVersion(context.Background(), templateID, codersdk.UpdateActiveTemplateVersion{
773+
ID: versionID,
774+
})
775+
require.NoError(t, err)
776+
}
777+
770778
// AwaitTemplateVersionJobRunning waits for the build to be picked up by a provisioner.
771779
func AwaitTemplateVersionJobRunning(t *testing.T, client *codersdk.Client, version uuid.UUID) codersdk.TemplateVersion {
772780
t.Helper()

docs/cli/templates.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/templates_pull.md

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)