mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add --version flag to coder templates pull, default to active version (#10153)
Fixes https://github.com/coder/coder/issues/9837
This commit is contained in:
+58
-21
@@ -15,12 +15,15 @@ import (
|
||||
)
|
||||
|
||||
func (r *RootCmd) templatePull() *clibase.Cmd {
|
||||
var tarMode bool
|
||||
var (
|
||||
tarMode bool
|
||||
versionName string
|
||||
)
|
||||
|
||||
client := new(codersdk.Client)
|
||||
cmd := &clibase.Cmd{
|
||||
Use: "pull <name> [destination]",
|
||||
Short: "Download the latest version of a template to a path.",
|
||||
Short: "Download the active, latest, or specified version of a template to a path.",
|
||||
Middleware: clibase.Chain(
|
||||
clibase.RequireRangeArgs(1, 2),
|
||||
r.InitClient(client),
|
||||
@@ -36,39 +39,67 @@ func (r *RootCmd) templatePull() *clibase.Cmd {
|
||||
dest = inv.Args[1]
|
||||
}
|
||||
|
||||
// TODO(JonA): Do we need to add a flag for organization?
|
||||
organization, err := CurrentOrganization(inv, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("current organization: %w", err)
|
||||
return xerrors.Errorf("get current organization: %w", err)
|
||||
}
|
||||
|
||||
template, err := client.TemplateByName(ctx, organization.ID, templateName)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("template by name: %w", err)
|
||||
return xerrors.Errorf("get template by name: %w", err)
|
||||
}
|
||||
|
||||
// Pull the versions for the template. We'll find the latest
|
||||
// one and download the source.
|
||||
versions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
|
||||
TemplateID: template.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("template versions by template: %w", err)
|
||||
var latestVersion codersdk.TemplateVersion
|
||||
{
|
||||
// Determine the latest template version and compare with the
|
||||
// active version. If they aren't the same, warn the user.
|
||||
versions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
|
||||
TemplateID: template.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("template versions by template: %w", err)
|
||||
}
|
||||
|
||||
if len(versions) == 0 {
|
||||
return xerrors.Errorf("no template versions for template %q", templateName)
|
||||
}
|
||||
|
||||
// Sort the slice from newest to oldest template.
|
||||
sort.SliceStable(versions, func(i, j int) bool {
|
||||
return versions[i].CreatedAt.After(versions[j].CreatedAt)
|
||||
})
|
||||
|
||||
latestVersion = versions[0]
|
||||
}
|
||||
|
||||
if len(versions) == 0 {
|
||||
return xerrors.Errorf("no template versions for template %q", templateName)
|
||||
var templateVersion codersdk.TemplateVersion
|
||||
switch versionName {
|
||||
case "", "active":
|
||||
activeVersion, err := client.TemplateVersion(ctx, template.ActiveVersionID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get active template version: %w", err)
|
||||
}
|
||||
if versionName == "" && activeVersion.ID != latestVersion.ID {
|
||||
cliui.Warn(inv.Stderr,
|
||||
"A newer template version than the active version exists. Pulling the active version instead.",
|
||||
"Use "+cliui.Code("--template latest")+" to pull the latest version.",
|
||||
)
|
||||
}
|
||||
templateVersion = activeVersion
|
||||
case "latest":
|
||||
templateVersion = latestVersion
|
||||
default:
|
||||
version, err := client.TemplateVersionByName(ctx, template.ID, versionName)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get template version: %w", err)
|
||||
}
|
||||
templateVersion = version
|
||||
}
|
||||
|
||||
// Sort the slice from newest to oldest template.
|
||||
sort.SliceStable(versions, func(i, j int) bool {
|
||||
return versions[i].CreatedAt.After(versions[j].CreatedAt)
|
||||
})
|
||||
|
||||
latest := versions[0]
|
||||
cliui.Info(inv.Stderr, "Pulling template version "+cliui.Bold(templateVersion.Name)+"...")
|
||||
|
||||
// Download the tar archive.
|
||||
raw, ctype, err := client.Download(ctx, latest.Job.FileID)
|
||||
raw, ctype, err := client.Download(ctx, templateVersion.Job.FileID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("download template: %w", err)
|
||||
}
|
||||
@@ -121,6 +152,12 @@ func (r *RootCmd) templatePull() *clibase.Cmd {
|
||||
|
||||
Value: clibase.BoolOf(&tarMode),
|
||||
},
|
||||
{
|
||||
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.",
|
||||
Flag: "version",
|
||||
|
||||
Value: clibase.StringOf(&versionName),
|
||||
},
|
||||
cliui.SkipPromptOption(),
|
||||
}
|
||||
|
||||
|
||||
+126
-4
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/codeclysm/extract/v3"
|
||||
@@ -49,7 +50,7 @@ func TestTemplatePull_NoName(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// Stdout tests that 'templates pull' pulls down the latest template
|
||||
// Stdout tests that 'templates pull' pulls down the active template
|
||||
// and writes it to stdout.
|
||||
func TestTemplatePull_Stdout(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -78,6 +79,7 @@ func TestTemplatePull_Stdout(t *testing.T) {
|
||||
// are being sorted correctly.
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
|
||||
|
||||
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name)
|
||||
clitest.SetupConfig(t, templateAdmin, root)
|
||||
@@ -91,7 +93,123 @@ func TestTemplatePull_Stdout(t *testing.T) {
|
||||
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
|
||||
}
|
||||
|
||||
// ToDir tests that 'templates pull' pulls down the latest template
|
||||
// Stdout tests that 'templates pull' pulls down the non-latest active template
|
||||
// and writes it to stdout.
|
||||
func TestTemplatePull_ActiveOldStdout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
source1 := genTemplateVersionSource()
|
||||
source2 := genTemplateVersionSource()
|
||||
|
||||
expected, err := echo.Tar(source1)
|
||||
require.NoError(t, err)
|
||||
|
||||
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, source1)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
|
||||
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
|
||||
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
|
||||
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
||||
var buf bytes.Buffer
|
||||
inv.Stdout = &buf
|
||||
var stderr strings.Builder
|
||||
inv.Stderr = &stderr
|
||||
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
|
||||
require.Contains(t, stderr.String(), "A newer template version than the active version exists.")
|
||||
}
|
||||
|
||||
// Stdout tests that 'templates pull' pulls down the specified template and
|
||||
// writes it to stdout.
|
||||
func TestTemplatePull_SpecifiedStdout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
source1 := genTemplateVersionSource()
|
||||
source2 := genTemplateVersionSource()
|
||||
source3 := genTemplateVersionSource()
|
||||
|
||||
expected, err := echo.Tar(source1)
|
||||
require.NoError(t, err)
|
||||
|
||||
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, source1)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
|
||||
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
|
||||
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
|
||||
updatedVersion2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source3, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion2.ID)
|
||||
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion2.ID)
|
||||
|
||||
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name, "--version", version1.Name)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
||||
var buf bytes.Buffer
|
||||
inv.Stdout = &buf
|
||||
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
|
||||
}
|
||||
|
||||
// Stdout tests that 'templates pull' pulls down the latest template
|
||||
// and writes it to stdout.
|
||||
func TestTemplatePull_LatestStdout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
source1 := genTemplateVersionSource()
|
||||
source2 := genTemplateVersionSource()
|
||||
|
||||
expected, err := echo.Tar(source1)
|
||||
require.NoError(t, err)
|
||||
|
||||
version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, source1)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
|
||||
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version1.ID)
|
||||
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
|
||||
inv, root := clitest.New(t, "templates", "pull", "--tar", template.Name, "latest")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
||||
var buf bytes.Buffer
|
||||
inv.Stdout = &buf
|
||||
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, bytes.Equal(expected, buf.Bytes()), "tar files differ")
|
||||
}
|
||||
|
||||
// ToDir tests that 'templates pull' pulls down the active template
|
||||
// and writes it to the correct directory.
|
||||
func TestTemplatePull_ToDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -120,6 +238,7 @@ func TestTemplatePull_ToDir(t *testing.T) {
|
||||
// are being sorted correctly.
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
@@ -143,8 +262,9 @@ func TestTemplatePull_ToDir(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// ToDir tests that 'templates pull' pulls down the latest template
|
||||
// and writes it to a directory with the name of the template if the path is not implicitly supplied.
|
||||
// ToDir tests that 'templates pull' pulls down the active template and writes
|
||||
// it to a directory with the name of the template if the path is not implicitly
|
||||
// supplied.
|
||||
// nolint: paralleltest
|
||||
func TestTemplatePull_ToImplicit(t *testing.T) {
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
@@ -171,6 +291,7 @@ func TestTemplatePull_ToImplicit(t *testing.T) {
|
||||
// are being sorted correctly.
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
|
||||
|
||||
// create a tempdir and change the working directory to it for the duration of the test (cannot run in parallel)
|
||||
dir := t.TempDir()
|
||||
@@ -233,6 +354,7 @@ func TestTemplatePull_FolderConflict(t *testing.T) {
|
||||
// are being sorted correctly.
|
||||
updatedVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, source2, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, updatedVersion.ID)
|
||||
coderdtest.UpdateActiveTemplateVersion(t, client, template.ID, updatedVersion.ID)
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@ SUBCOMMANDS:
|
||||
edit Edit the metadata of a template by name.
|
||||
init Get started with a templated template.
|
||||
list List all the templates available for the organization
|
||||
pull Download the latest version of a template to a path.
|
||||
pull Download the active, latest, or specified version of a template
|
||||
to a path.
|
||||
push Push a new template version from the current directory or as
|
||||
specified by flag
|
||||
versions Manage different versions of the specified template
|
||||
|
||||
+6
-1
@@ -3,12 +3,17 @@ coder v0.0.0-devel
|
||||
USAGE:
|
||||
coder templates pull [flags] <name> [destination]
|
||||
|
||||
Download the latest version of a template to a path.
|
||||
Download the active, latest, or specified version of a template to a path.
|
||||
|
||||
OPTIONS:
|
||||
--tar bool
|
||||
Output the template as a tar archive to stdout.
|
||||
|
||||
--version string
|
||||
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.
|
||||
|
||||
-y, --yes bool
|
||||
Bypass prompts.
|
||||
|
||||
|
||||
@@ -753,11 +753,12 @@ func CreateTemplate(t *testing.T, client *codersdk.Client, organization uuid.UUI
|
||||
// UpdateTemplateVersion creates a new template version with the "echo" provisioner
|
||||
// and associates it with the given templateID.
|
||||
func UpdateTemplateVersion(t *testing.T, client *codersdk.Client, organizationID uuid.UUID, res *echo.Responses, templateID uuid.UUID) codersdk.TemplateVersion {
|
||||
ctx := context.Background()
|
||||
data, err := echo.Tar(res)
|
||||
require.NoError(t, err)
|
||||
file, err := client.Upload(context.Background(), codersdk.ContentTypeTar, bytes.NewReader(data))
|
||||
file, err := client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(data))
|
||||
require.NoError(t, err)
|
||||
templateVersion, err := client.CreateTemplateVersion(context.Background(), organizationID, codersdk.CreateTemplateVersionRequest{
|
||||
templateVersion, err := client.CreateTemplateVersion(ctx, organizationID, codersdk.CreateTemplateVersionRequest{
|
||||
TemplateID: templateID,
|
||||
FileID: file.ID,
|
||||
StorageMethod: codersdk.ProvisionerStorageMethodFile,
|
||||
@@ -767,6 +768,13 @@ func UpdateTemplateVersion(t *testing.T, client *codersdk.Client, organizationID
|
||||
return templateVersion
|
||||
}
|
||||
|
||||
func UpdateActiveTemplateVersion(t *testing.T, client *codersdk.Client, templateID, versionID uuid.UUID) {
|
||||
err := client.UpdateActiveTemplateVersion(context.Background(), templateID, codersdk.UpdateActiveTemplateVersion{
|
||||
ID: versionID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// AwaitTemplateVersionJobRunning waits for the build to be picked up by a provisioner.
|
||||
func AwaitTemplateVersionJobRunning(t *testing.T, client *codersdk.Client, version uuid.UUID) codersdk.TemplateVersion {
|
||||
t.Helper()
|
||||
|
||||
Generated
+1
-1
@@ -40,6 +40,6 @@ Templates are written in standard Terraform and describe the infrastructure for
|
||||
| [<code>edit</code>](./templates_edit.md) | Edit the metadata of a template by name. |
|
||||
| [<code>init</code>](./templates_init.md) | Get started with a templated template. |
|
||||
| [<code>list</code>](./templates_list.md) | List all the templates available for the organization |
|
||||
| [<code>pull</code>](./templates_pull.md) | Download the latest version of a template to a path. |
|
||||
| [<code>pull</code>](./templates_pull.md) | Download the active, latest, or specified version of a template to a path. |
|
||||
| [<code>push</code>](./templates_push.md) | Push a new template version from the current directory or as specified by flag |
|
||||
| [<code>versions</code>](./templates_versions.md) | Manage different versions of the specified template |
|
||||
|
||||
Generated
+9
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
# templates pull
|
||||
|
||||
Download the latest version of a template to a path.
|
||||
Download the active, latest, or specified version of a template to a path.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -20,6 +20,14 @@ coder templates pull [flags] <name> [destination]
|
||||
|
||||
Output the template as a tar archive to stdout.
|
||||
|
||||
### --version
|
||||
|
||||
| | |
|
||||
| ---- | ------------------- |
|
||||
| Type | <code>string</code> |
|
||||
|
||||
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.
|
||||
|
||||
### -y, --yes
|
||||
|
||||
| | |
|
||||
|
||||
+1
-1
@@ -838,7 +838,7 @@
|
||||
},
|
||||
{
|
||||
"title": "templates pull",
|
||||
"description": "Download the latest version of a template to a path.",
|
||||
"description": "Download the active, latest, or specified version of a template to a path.",
|
||||
"path": "cli/templates_pull.md"
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user