Content-Length: 73875 | pFad | http://github.com/coder/coder/pull/18910.patch
thub.com
From 356fb39ba4c6afeb19540a66f4c1e07e37356489 Mon Sep 17 00:00:00 2001
From: Susana Ferreira
Date: Thu, 17 Jul 2025 08:49:55 +0000
Subject: [PATCH 1/5] feat(cli): add CLI support for listing presets
---
cli/templateversionpresets.go | 137 ++++++++++++++++++
cli/templateversionpresets_test.go | 137 ++++++++++++++++++
cli/templateversions.go | 1 +
.../coder_templates_versions_--help.golden | 1 +
...r_templates_versions_presets_--help.golden | 18 +++
...ates_versions_presets_--help_--help.golden | 18 +++
coderd/apidoc/docs.go | 3 +
coderd/apidoc/swagger.json | 3 +
coderd/presets.go | 16 +-
codersdk/presets.go | 1 +
docs/manifest.json | 10 ++
docs/reference/api/schemas.md | 4 +-
docs/reference/api/templates.md | 4 +-
docs/reference/cli/templates_versions.md | 13 +-
.../cli/templates_versions_presets.md | 28 ++++
.../cli/templates_versions_presets_list.md | 39 +++++
site/src/api/typesGenerated.ts | 1 +
.../CreateWorkspacePageView.stories.tsx | 4 +
18 files changed, 427 insertions(+), 11 deletions(-)
create mode 100644 cli/templateversionpresets.go
create mode 100644 cli/templateversionpresets_test.go
create mode 100644 cli/testdata/coder_templates_versions_presets_--help.golden
create mode 100644 cli/testdata/coder_templates_versions_presets_--help_--help.golden
create mode 100644 docs/reference/cli/templates_versions_presets.md
create mode 100644 docs/reference/cli/templates_versions_presets_list.md
diff --git a/cli/templateversionpresets.go b/cli/templateversionpresets.go
new file mode 100644
index 0000000000000..984ffec331566
--- /dev/null
+++ b/cli/templateversionpresets.go
@@ -0,0 +1,137 @@
+package cli
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "golang.org/x/xerrors"
+
+ "github.com/coder/coder/v2/cli/cliui"
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/serpent"
+)
+
+func (r *RootCmd) templateVersionPresets() *serpent.Command {
+ cmd := &serpent.Command{
+ Use: "presets",
+ Short: "Manage presets of the specified template version",
+ Aliases: []string{"preset"},
+ Long: FormatExamples(
+ Example{
+ Description: "List presets of a specific template version",
+ Command: "coder templates versions presets list my-template my-template-version",
+ },
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ return inv.Command.HelpHandler(inv)
+ },
+ Children: []*serpent.Command{
+ r.templateVersionPresetsList(),
+ },
+ }
+
+ return cmd
+}
+
+func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
+ defaultColumns := []string{
+ "name",
+ "parameters",
+ "default",
+ "prebuilds",
+ }
+ formatter := cliui.NewOutputFormatter(
+ cliui.TableFormat([]templateVersionPresetRow{}, defaultColumns),
+ cliui.JSONFormat(),
+ )
+ client := new(codersdk.Client)
+ orgContext := NewOrganizationContext()
+
+ cmd := &serpent.Command{
+ Use: "list ",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(2),
+ r.InitClient(client),
+ ),
+ Short: "List all the presets of the specified template version",
+ Options: serpent.OptionSet{},
+ Handler: func(inv *serpent.Invocation) error {
+ organization, err := orgContext.Selected(inv, client)
+ if err != nil {
+ return xerrors.Errorf("get current organization: %w", err)
+ }
+
+ template, err := client.TemplateByName(inv.Context(), organization.ID, inv.Args[0])
+ if err != nil {
+ return xerrors.Errorf("get template by name: %w", err)
+ }
+
+ version, err := client.TemplateVersionByName(inv.Context(), template.ID, inv.Args[1])
+ if err != nil {
+ return xerrors.Errorf("get template version by name: %w", err)
+ }
+
+ presets, err := client.TemplateVersionPresets(inv.Context(), version.ID)
+ if err != nil {
+ return xerrors.Errorf("get template versions presets by template version: %w", err)
+ }
+
+ if len(presets) == 0 {
+ return xerrors.Errorf("no presets found for template %q and template-version %q", template.Name, version.Name)
+ }
+
+ rows := templateVersionPresetsToRows(presets...)
+ out, err := formatter.Format(inv.Context(), rows)
+ if err != nil {
+ return xerrors.Errorf("render table: %w", err)
+ }
+
+ _, err = fmt.Fprintln(inv.Stdout, out)
+ return err
+ },
+ }
+
+ orgContext.AttachOptions(cmd)
+ formatter.AttachOptions(&cmd.Options)
+ return cmd
+}
+
+type templateVersionPresetRow struct {
+ // For json format:
+ TemplateVersionPreset codersdk.Preset `table:"-"`
+
+ // For table format:
+ Name string `json:"-" table:"name,default_sort"`
+ Parameters string `json:"-" table:"parameters"`
+ Default bool `json:"-" table:"default"`
+ Prebuilds string `json:"-" table:"prebuilds"`
+}
+
+func formatPresetParameters(params []codersdk.PresetParameter) string {
+ var paramsStr []string
+ for _, p := range params {
+ paramsStr = append(paramsStr, fmt.Sprintf("%s=%s", p.Name, p.Value))
+ }
+ return strings.Join(paramsStr, ",")
+}
+
+// templateVersionPresetsToRows converts a list of presets to a list of rows
+// for outputting.
+func templateVersionPresetsToRows(presets ...codersdk.Preset) []templateVersionPresetRow {
+ rows := make([]templateVersionPresetRow, len(presets))
+ for i, preset := range presets {
+ prebuilds := "-"
+ if preset.Prebuilds != nil {
+ prebuilds = strconv.Itoa(*preset.Prebuilds)
+ }
+ rows[i] = templateVersionPresetRow{
+ Name: preset.Name,
+ Parameters: formatPresetParameters(preset.Parameters),
+ Default: preset.Default,
+ Prebuilds: prebuilds,
+ }
+ }
+
+ return rows
+}
diff --git a/cli/templateversionpresets_test.go b/cli/templateversionpresets_test.go
new file mode 100644
index 0000000000000..36b955f366a30
--- /dev/null
+++ b/cli/templateversionpresets_test.go
@@ -0,0 +1,137 @@
+package cli_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/coder/coder/v2/provisioner/echo"
+ "github.com/coder/coder/v2/provisionersdk/proto"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/coder/coder/v2/cli/clitest"
+ "github.com/coder/coder/v2/coderd/coderdtest"
+ "github.com/coder/coder/v2/pty/ptytest"
+)
+
+func TestTemplateVersionPresets(t *testing.T) {
+ t.Parallel()
+
+ t.Run("ListPresets", func(t *testing.T) {
+ t.Parallel()
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
+ owner := coderdtest.CreateFirstUser(t, client)
+ member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
+
+ // Given: a template version that includes presets
+ presets := []*proto.Preset{
+ {
+ Name: "preset-multiple-params",
+ Parameters: []*proto.PresetParameter{
+ {
+ Name: "k1",
+ Value: "v1",
+ }, {
+ Name: "k2",
+ Value: "v2",
+ },
+ },
+ },
+ {
+ Name: "preset-default",
+ Default: true,
+ Parameters: []*proto.PresetParameter{
+ {
+ Name: "k1",
+ Value: "v2",
+ },
+ },
+ Prebuild: &proto.Prebuild{
+ Instances: 0,
+ },
+ },
+ {
+ Name: "preset-prebuilds",
+ Parameters: []*proto.PresetParameter{},
+ Prebuild: &proto.Prebuild{
+ Instances: 2,
+ },
+ },
+ }
+ version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets(presets))
+ _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
+
+ // When: listing presets for that template and template version
+ inv, root := clitest.New(t, "templates", "versions", "presets", "list", template.Name, version.Name)
+ clitest.SetupConfig(t, member, root)
+
+ pty := ptytest.New(t).Attach(inv)
+ doneChan := make(chan struct{})
+ var runErr error
+ go func() {
+ defer close(doneChan)
+ runErr = inv.Run()
+ }()
+
+ <-doneChan
+ require.NoError(t, runErr)
+
+ // Should: return the presets sorted by name
+ pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
+ // The parameter order is not guaranteed in the output, so we match both possible orders
+ pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
+ pty.ExpectRegexMatch(`preset-prebuilds\s+\s+false\s+2`)
+ })
+
+ t.Run("NoPresets", func(t *testing.T) {
+ t.Parallel()
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
+ owner := coderdtest.CreateFirstUser(t, client)
+ member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
+
+ // Given: a template version without presets
+ version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets([]*proto.Preset{}))
+ _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
+
+ // When: listing presets for that template and template version
+ inv, root := clitest.New(t, "templates", "versions", "presets", "list", template.Name, version.Name)
+ clitest.SetupConfig(t, member, root)
+
+ ptytest.New(t).Attach(inv)
+ doneChan := make(chan struct{})
+ var runErr error
+ go func() {
+ defer close(doneChan)
+ runErr = inv.Run()
+ }()
+ <-doneChan
+
+ // Should return an error when no presets are found for the given template and version.
+ require.Error(t, runErr)
+ expectedErr := fmt.Sprintf(
+ "no presets found for template %q and template-version %q",
+ template.Name,
+ version.Name,
+ )
+ require.Contains(t, runErr.Error(), expectedErr)
+ })
+}
+
+func templateWithPresets(presets []*proto.Preset) *echo.Responses {
+ return &echo.Responses{
+ Parse: echo.ParseComplete,
+ ProvisionPlan: []*proto.Response{
+ {
+ Type: &proto.Response_Plan{
+ Plan: &proto.PlanComplete{
+ Presets: presets,
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/cli/templateversions.go b/cli/templateversions.go
index c90903a7c4f93..fa80f075a1601 100644
--- a/cli/templateversions.go
+++ b/cli/templateversions.go
@@ -33,6 +33,7 @@ func (r *RootCmd) templateVersions() *serpent.Command {
r.archiveTemplateVersion(),
r.unarchiveTemplateVersion(),
r.templateVersionsPromote(),
+ r.templateVersionPresets(),
},
}
diff --git a/cli/testdata/coder_templates_versions_--help.golden b/cli/testdata/coder_templates_versions_--help.golden
index fa276999563d2..8468395627dbb 100644
--- a/cli/testdata/coder_templates_versions_--help.golden
+++ b/cli/testdata/coder_templates_versions_--help.golden
@@ -14,6 +14,7 @@ USAGE:
SUBCOMMANDS:
archive Archive a template version(s).
list List all the versions of the specified template
+ presets Manage presets of the specified template version
promote Promote a template version to active.
unarchive Unarchive a template version(s).
diff --git a/cli/testdata/coder_templates_versions_presets_--help.golden b/cli/testdata/coder_templates_versions_presets_--help.golden
new file mode 100644
index 0000000000000..8967c85d34881
--- /dev/null
+++ b/cli/testdata/coder_templates_versions_presets_--help.golden
@@ -0,0 +1,18 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder templates versions presets
+
+ Manage presets of the specified template version
+
+ Aliases: preset
+
+ - List presets of a specific template version:
+
+ $ coder templates versions presets list my-template my-template-version
+
+SUBCOMMANDS:
+ list List all the presets of the specified template version
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_templates_versions_presets_--help_--help.golden b/cli/testdata/coder_templates_versions_presets_--help_--help.golden
new file mode 100644
index 0000000000000..8967c85d34881
--- /dev/null
+++ b/cli/testdata/coder_templates_versions_presets_--help_--help.golden
@@ -0,0 +1,18 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder templates versions presets
+
+ Manage presets of the specified template version
+
+ Aliases: preset
+
+ - List presets of a specific template version:
+
+ $ coder templates versions presets list my-template my-template-version
+
+SUBCOMMANDS:
+ list List all the presets of the specified template version
+
+———
+Run `coder --help` for a list of global options.
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index 7a3bd8a0d913a..c7d9ce4064a6f 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -14837,6 +14837,9 @@ const docTemplate = `{
"items": {
"$ref": "#/definitions/codersdk.PresetParameter"
}
+ },
+ "prebuilds": {
+ "type": "integer"
}
}
},
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index ded07f40f1163..9d697b714452d 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -13439,6 +13439,9 @@
"items": {
"$ref": "#/definitions/codersdk.PresetParameter"
}
+ },
+ "prebuilds": {
+ "type": "integer"
}
}
},
diff --git a/coderd/presets.go b/coderd/presets.go
index 151a1e7d5a904..a87163dd115e1 100644
--- a/coderd/presets.go
+++ b/coderd/presets.go
@@ -1,6 +1,7 @@
package coderd
import (
+ "database/sql"
"net/http"
"github.com/coder/coder/v2/coderd/httpapi"
@@ -38,12 +39,21 @@ func (api *API) templateVersionPresets(rw http.ResponseWriter, r *http.Request)
return
}
+ getPrebuildInstances := func(desiredInstances sql.NullInt32) *int {
+ if desiredInstances.Valid {
+ value := int(desiredInstances.Int32)
+ return &value
+ }
+ return nil
+ }
+
var res []codersdk.Preset
for _, preset := range presets {
sdkPreset := codersdk.Preset{
- ID: preset.ID,
- Name: preset.Name,
- Default: preset.IsDefault,
+ ID: preset.ID,
+ Name: preset.Name,
+ Default: preset.IsDefault,
+ Prebuilds: getPrebuildInstances(preset.DesiredInstances),
}
for _, presetParam := range presetParams {
if presetParam.TemplateVersionPresetID != preset.ID {
diff --git a/codersdk/presets.go b/codersdk/presets.go
index 60253d6595c51..4cfb91afa5eef 100644
--- a/codersdk/presets.go
+++ b/codersdk/presets.go
@@ -15,6 +15,7 @@ type Preset struct {
Name string
Parameters []PresetParameter
Default bool
+ Prebuilds *int
}
type PresetParameter struct {
diff --git a/docs/manifest.json b/docs/manifest.json
index 93f8282c26c4a..1e216f02cdde4 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -1664,6 +1664,16 @@
"description": "List all the versions of the specified template",
"path": "reference/cli/templates_versions_list.md"
},
+ {
+ "title": "templates versions presets",
+ "description": "Manage presets of the specified template version",
+ "path": "reference/cli/templates_versions_presets.md"
+ },
+ {
+ "title": "templates versions presets list",
+ "description": "List all the presets of the specified template version",
+ "path": "reference/cli/templates_versions_presets_list.md"
+ },
{
"title": "templates versions promote",
"description": "Promote a template version to active.",
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index 053a738413060..9cb1d5f48fa12 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -5468,7 +5468,8 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
"name": "string",
"value": "string"
}
- ]
+ ],
+ "prebuilds": 0
}
```
@@ -5480,6 +5481,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
| `id` | string | false | | |
| `name` | string | false | | |
| `parameters` | array of [codersdk.PresetParameter](#codersdkpresetparameter) | false | | |
+| `prebuilds` | integer | false | | |
## codersdk.PresetParameter
diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md
index 4c21b3644be2d..06959fefee9a4 100644
--- a/docs/reference/api/templates.md
+++ b/docs/reference/api/templates.md
@@ -2921,7 +2921,8 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/p
"name": "string",
"value": "string"
}
- ]
+ ],
+ "prebuilds": 0
}
]
```
@@ -2945,6 +2946,7 @@ Status Code **200**
| `» parameters` | array | false | | |
| `»» name` | string | false | | |
| `»» value` | string | false | | |
+| `» prebuilds` | integer | false | | |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
diff --git a/docs/reference/cli/templates_versions.md b/docs/reference/cli/templates_versions.md
index 8eb927967d162..6a65c7ddf33e5 100644
--- a/docs/reference/cli/templates_versions.md
+++ b/docs/reference/cli/templates_versions.md
@@ -23,9 +23,10 @@ coder templates versions
## Subcommands
-| Name | Purpose |
-|-------------------------------------------------------------|-------------------------------------------------|
-| [list
](./templates_versions_list.md) | List all the versions of the specified template |
-| [archive
](./templates_versions_archive.md) | Archive a template version(s). |
-| [unarchive
](./templates_versions_unarchive.md) | Unarchive a template version(s). |
-| [promote
](./templates_versions_promote.md) | Promote a template version to active. |
+| Name | Purpose |
+|-------------------------------------------------------------|--------------------------------------------------|
+| [list
](./templates_versions_list.md) | List all the versions of the specified template |
+| [archive
](./templates_versions_archive.md) | Archive a template version(s). |
+| [unarchive
](./templates_versions_unarchive.md) | Unarchive a template version(s). |
+| [promote
](./templates_versions_promote.md) | Promote a template version to active. |
+| [presets
](./templates_versions_presets.md) | Manage presets of the specified template version |
diff --git a/docs/reference/cli/templates_versions_presets.md b/docs/reference/cli/templates_versions_presets.md
new file mode 100644
index 0000000000000..6dc404dc0fc01
--- /dev/null
+++ b/docs/reference/cli/templates_versions_presets.md
@@ -0,0 +1,28 @@
+
+# templates versions presets
+
+Manage presets of the specified template version
+
+Aliases:
+
+* preset
+
+## Usage
+
+```console
+coder templates versions presets
+```
+
+## Description
+
+```console
+ - List presets of a specific template version:
+
+ $ coder templates versions presets list my-template my-template-version
+```
+
+## Subcommands
+
+| Name | Purpose |
+|-----------------------------------------------------------|--------------------------------------------------------|
+| [list
](./templates_versions_presets_list.md) | List all the presets of the specified template version |
diff --git a/docs/reference/cli/templates_versions_presets_list.md b/docs/reference/cli/templates_versions_presets_list.md
new file mode 100644
index 0000000000000..9874b158383c5
--- /dev/null
+++ b/docs/reference/cli/templates_versions_presets_list.md
@@ -0,0 +1,39 @@
+
+# templates versions presets list
+
+List all the presets of the specified template version
+
+## Usage
+
+```console
+coder templates versions presets list [flags]
+```
+
+## Options
+
+### -O, --org
+
+| | |
+|-------------|----------------------------------|
+| Type | string
|
+| Environment | $CODER_ORGANIZATION
|
+
+Select which organization (uuid or name) to use.
+
+### -c, --column
+
+| | |
+|---------|-----------------------------------------------------|
+| Type | [name\|parameters\|default\|prebuilds]
|
+| Default | name,parameters,default,prebuilds
|
+
+Columns to display in table output.
+
+### -o, --output
+
+| | |
+|---------|--------------------------|
+| Type | table\|json
|
+| Default | table
|
+
+Output format.
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index 47a2984d374a2..c2ad11b37fcff 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -1962,6 +1962,7 @@ export interface Preset {
readonly Name: string;
readonly Parameters: readonly PresetParameter[];
readonly Default: boolean;
+ readonly Prebuilds: number | null;
}
// From codersdk/presets.go
diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx
index f085c74c57073..0f1c993c122d8 100644
--- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx
+++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx
@@ -132,6 +132,7 @@ export const PresetsButNoneSelected: Story = {
Value: "preset 1 override",
},
],
+ Prebuilds: null,
},
{
ID: "preset-2",
@@ -143,6 +144,7 @@ export const PresetsButNoneSelected: Story = {
Value: "42",
},
],
+ Prebuilds: null,
},
],
parameters: [
@@ -256,6 +258,7 @@ export const PresetsWithDefault: Story = {
Value: "preset 1 override",
},
],
+ Prebuilds: null,
},
{
ID: "preset-2",
@@ -267,6 +270,7 @@ export const PresetsWithDefault: Story = {
Value: "150189",
},
],
+ Prebuilds: null,
},
],
parameters: [
From 641dc96e63dccee8c2984b3d946231269cc50584 Mon Sep 17 00:00:00 2001
From: Susana Ferreira
Date: Tue, 22 Jul 2025 17:56:28 +0000
Subject: [PATCH 2/5] chore: address comments
---
cli/templateversionpresets.go | 29 ++++++++++---------
cli/templateversionpresets_test.go | 14 ++++-----
coderd/presets.go | 10 +++----
codersdk/presets.go | 10 +++----
site/src/api/typesGenerated.ts | 2 +-
.../CreateWorkspacePageView.stories.tsx | 8 ++---
6 files changed, 36 insertions(+), 37 deletions(-)
diff --git a/cli/templateversionpresets.go b/cli/templateversionpresets.go
index 984ffec331566..d698ef0581260 100644
--- a/cli/templateversionpresets.go
+++ b/cli/templateversionpresets.go
@@ -39,7 +39,7 @@ func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
"name",
"parameters",
"default",
- "prebuilds",
+ "desired prebuild instances",
}
formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]templateVersionPresetRow{}, defaultColumns),
@@ -78,7 +78,10 @@ func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
}
if len(presets) == 0 {
- return xerrors.Errorf("no presets found for template %q and template-version %q", template.Name, version.Name)
+ cliui.Infof(
+ inv.Stdout,
+ "No presets found for template %q and template-version %q.\n", template.Name, version.Name,
+ )
}
rows := templateVersionPresetsToRows(presets...)
@@ -102,10 +105,10 @@ type templateVersionPresetRow struct {
TemplateVersionPreset codersdk.Preset `table:"-"`
// For table format:
- Name string `json:"-" table:"name,default_sort"`
- Parameters string `json:"-" table:"parameters"`
- Default bool `json:"-" table:"default"`
- Prebuilds string `json:"-" table:"prebuilds"`
+ Name string `json:"-" table:"name,default_sort"`
+ Parameters string `json:"-" table:"parameters"`
+ Default bool `json:"-" table:"default"`
+ DesiredPrebuildInstances string `json:"-" table:"desired prebuild instances"`
}
func formatPresetParameters(params []codersdk.PresetParameter) string {
@@ -121,15 +124,15 @@ func formatPresetParameters(params []codersdk.PresetParameter) string {
func templateVersionPresetsToRows(presets ...codersdk.Preset) []templateVersionPresetRow {
rows := make([]templateVersionPresetRow, len(presets))
for i, preset := range presets {
- prebuilds := "-"
- if preset.Prebuilds != nil {
- prebuilds = strconv.Itoa(*preset.Prebuilds)
+ prebuildInstances := "-"
+ if preset.DesiredPrebuildInstances != nil {
+ prebuildInstances = strconv.Itoa(*preset.DesiredPrebuildInstances)
}
rows[i] = templateVersionPresetRow{
- Name: preset.Name,
- Parameters: formatPresetParameters(preset.Parameters),
- Default: preset.Default,
- Prebuilds: prebuilds,
+ Name: preset.Name,
+ Parameters: formatPresetParameters(preset.Parameters),
+ Default: preset.Default,
+ DesiredPrebuildInstances: prebuildInstances,
}
}
diff --git a/cli/templateversionpresets_test.go b/cli/templateversionpresets_test.go
index 36b955f366a30..033fb111e7377 100644
--- a/cli/templateversionpresets_test.go
+++ b/cli/templateversionpresets_test.go
@@ -101,7 +101,7 @@ func TestTemplateVersionPresets(t *testing.T) {
inv, root := clitest.New(t, "templates", "versions", "presets", "list", template.Name, version.Name)
clitest.SetupConfig(t, member, root)
- ptytest.New(t).Attach(inv)
+ pty := ptytest.New(t).Attach(inv)
doneChan := make(chan struct{})
var runErr error
go func() {
@@ -109,15 +109,11 @@ func TestTemplateVersionPresets(t *testing.T) {
runErr = inv.Run()
}()
<-doneChan
+ require.NoError(t, runErr)
- // Should return an error when no presets are found for the given template and version.
- require.Error(t, runErr)
- expectedErr := fmt.Sprintf(
- "no presets found for template %q and template-version %q",
- template.Name,
- version.Name,
- )
- require.Contains(t, runErr.Error(), expectedErr)
+ // Should return a message when no presets are found for the given template and version.
+ notFoundMessage := fmt.Sprintf("No presets found for template %q and template-version %q.", template.Name, version.Name)
+ pty.ExpectRegexMatch(notFoundMessage)
})
}
diff --git a/coderd/presets.go b/coderd/presets.go
index a87163dd115e1..c8d84baec4bf3 100644
--- a/coderd/presets.go
+++ b/coderd/presets.go
@@ -39,7 +39,7 @@ func (api *API) templateVersionPresets(rw http.ResponseWriter, r *http.Request)
return
}
- getPrebuildInstances := func(desiredInstances sql.NullInt32) *int {
+ convertPrebuildInstances := func(desiredInstances sql.NullInt32) *int {
if desiredInstances.Valid {
value := int(desiredInstances.Int32)
return &value
@@ -50,10 +50,10 @@ func (api *API) templateVersionPresets(rw http.ResponseWriter, r *http.Request)
var res []codersdk.Preset
for _, preset := range presets {
sdkPreset := codersdk.Preset{
- ID: preset.ID,
- Name: preset.Name,
- Default: preset.IsDefault,
- Prebuilds: getPrebuildInstances(preset.DesiredInstances),
+ ID: preset.ID,
+ Name: preset.Name,
+ Default: preset.IsDefault,
+ DesiredPrebuildInstances: convertPrebuildInstances(preset.DesiredInstances),
}
for _, presetParam := range presetParams {
if presetParam.TemplateVersionPresetID != preset.ID {
diff --git a/codersdk/presets.go b/codersdk/presets.go
index 4cfb91afa5eef..2d94aa3baabb6 100644
--- a/codersdk/presets.go
+++ b/codersdk/presets.go
@@ -11,11 +11,11 @@ import (
)
type Preset struct {
- ID uuid.UUID
- Name string
- Parameters []PresetParameter
- Default bool
- Prebuilds *int
+ ID uuid.UUID
+ Name string
+ Parameters []PresetParameter
+ Default bool
+ DesiredPrebuildInstances *int
}
type PresetParameter struct {
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index c2ad11b37fcff..caaa851c8a67e 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -1962,7 +1962,7 @@ export interface Preset {
readonly Name: string;
readonly Parameters: readonly PresetParameter[];
readonly Default: boolean;
- readonly Prebuilds: number | null;
+ readonly DesiredPrebuildInstances: number | null;
}
// From codersdk/presets.go
diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx
index 0f1c993c122d8..bea7beb7d61f9 100644
--- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx
+++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx
@@ -132,7 +132,7 @@ export const PresetsButNoneSelected: Story = {
Value: "preset 1 override",
},
],
- Prebuilds: null,
+ DesiredPrebuildInstances: null,
},
{
ID: "preset-2",
@@ -144,7 +144,7 @@ export const PresetsButNoneSelected: Story = {
Value: "42",
},
],
- Prebuilds: null,
+ DesiredPrebuildInstances: null,
},
],
parameters: [
@@ -258,7 +258,7 @@ export const PresetsWithDefault: Story = {
Value: "preset 1 override",
},
],
- Prebuilds: null,
+ DesiredPrebuildInstances: null,
},
{
ID: "preset-2",
@@ -270,7 +270,7 @@ export const PresetsWithDefault: Story = {
Value: "150189",
},
],
- Prebuilds: null,
+ DesiredPrebuildInstances: null,
},
],
parameters: [
From b0b83fa0fc9918cd5915b3e999ba73c1fb350148 Mon Sep 17 00:00:00 2001
From: Susana Ferreira
Date: Tue, 22 Jul 2025 18:00:22 +0000
Subject: [PATCH 3/5] chore: run make gen
---
coderd/apidoc/docs.go | 6 ++---
coderd/apidoc/swagger.json | 6 ++---
docs/reference/api/schemas.md | 18 +++++++-------
docs/reference/api/templates.md | 24 +++++++++----------
.../cli/templates_versions_presets_list.md | 8 +++----
5 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index c7d9ce4064a6f..730cb3a5ab718 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -14826,6 +14826,9 @@ const docTemplate = `{
"default": {
"type": "boolean"
},
+ "desiredPrebuildInstances": {
+ "type": "integer"
+ },
"id": {
"type": "string"
},
@@ -14837,9 +14840,6 @@ const docTemplate = `{
"items": {
"$ref": "#/definitions/codersdk.PresetParameter"
}
- },
- "prebuilds": {
- "type": "integer"
}
}
},
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index 9d697b714452d..a3f884631f428 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -13428,6 +13428,9 @@
"default": {
"type": "boolean"
},
+ "desiredPrebuildInstances": {
+ "type": "integer"
+ },
"id": {
"type": "string"
},
@@ -13439,9 +13442,6 @@
"items": {
"$ref": "#/definitions/codersdk.PresetParameter"
}
- },
- "prebuilds": {
- "type": "integer"
}
}
},
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index 9cb1d5f48fa12..42240796366ea 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -5461,6 +5461,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
```json
{
"default": true,
+ "desiredPrebuildInstances": 0,
"id": "string",
"name": "string",
"parameters": [
@@ -5468,20 +5469,19 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
"name": "string",
"value": "string"
}
- ],
- "prebuilds": 0
+ ]
}
```
### Properties
-| Name | Type | Required | Restrictions | Description |
-|--------------|---------------------------------------------------------------|----------|--------------|-------------|
-| `default` | boolean | false | | |
-| `id` | string | false | | |
-| `name` | string | false | | |
-| `parameters` | array of [codersdk.PresetParameter](#codersdkpresetparameter) | false | | |
-| `prebuilds` | integer | false | | |
+| Name | Type | Required | Restrictions | Description |
+|----------------------------|---------------------------------------------------------------|----------|--------------|-------------|
+| `default` | boolean | false | | |
+| `desiredPrebuildInstances` | integer | false | | |
+| `id` | string | false | | |
+| `name` | string | false | | |
+| `parameters` | array of [codersdk.PresetParameter](#codersdkpresetparameter) | false | | |
## codersdk.PresetParameter
diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md
index 06959fefee9a4..8d84623a15c8e 100644
--- a/docs/reference/api/templates.md
+++ b/docs/reference/api/templates.md
@@ -2914,6 +2914,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/p
[
{
"default": true,
+ "desiredPrebuildInstances": 0,
"id": "string",
"name": "string",
"parameters": [
@@ -2921,8 +2922,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/p
"name": "string",
"value": "string"
}
- ],
- "prebuilds": 0
+ ]
}
]
```
@@ -2937,16 +2937,16 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/p
Status Code **200**
-| Name | Type | Required | Restrictions | Description |
-|----------------|---------|----------|--------------|-------------|
-| `[array item]` | array | false | | |
-| `» default` | boolean | false | | |
-| `» id` | string | false | | |
-| `» name` | string | false | | |
-| `» parameters` | array | false | | |
-| `»» name` | string | false | | |
-| `»» value` | string | false | | |
-| `» prebuilds` | integer | false | | |
+| Name | Type | Required | Restrictions | Description |
+|------------------------------|---------|----------|--------------|-------------|
+| `[array item]` | array | false | | |
+| `» default` | boolean | false | | |
+| `» desiredPrebuildInstances` | integer | false | | |
+| `» id` | string | false | | |
+| `» name` | string | false | | |
+| `» parameters` | array | false | | |
+| `»» name` | string | false | | |
+| `»» value` | string | false | | |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
diff --git a/docs/reference/cli/templates_versions_presets_list.md b/docs/reference/cli/templates_versions_presets_list.md
index 9874b158383c5..ffdbf79b0b9c7 100644
--- a/docs/reference/cli/templates_versions_presets_list.md
+++ b/docs/reference/cli/templates_versions_presets_list.md
@@ -22,10 +22,10 @@ Select which organization (uuid or name) to use.
### -c, --column
-| | |
-|---------|-----------------------------------------------------|
-| Type | [name\|parameters\|default\|prebuilds]
|
-| Default | name,parameters,default,prebuilds
|
+| | |
+|---------|----------------------------------------------------------------------|
+| Type | [name\|parameters\|default\|desired prebuild instances]
|
+| Default | name,parameters,default,desired prebuild instances
|
Columns to display in table output.
From 37c1960086e8c4b83bb3fec77746bb266cf81708 Mon Sep 17 00:00:00 2001
From: Susana Ferreira
Date: Tue, 22 Jul 2025 19:13:47 +0000
Subject: [PATCH 4/5] chore: simplify presets listing under coder templates
presets
---
...teversionpresets.go => templatepresets.go} | 65 +++--
cli/templatepresets_test.go | 226 ++++++++++++++++++
cli/templates.go | 1 +
cli/templateversionpresets_test.go | 133 -----------
cli/templateversions.go | 1 -
cli/testdata/coder_templates_--help.golden | 1 +
.../coder_templates_presets_--help.golden | 24 ++
...coder_templates_presets_list_--help.golden | 24 ++
.../coder_templates_versions_--help.golden | 1 -
...r_templates_versions_presets_--help.golden | 18 --
...ates_versions_presets_--help_--help.golden | 18 --
docs/manifest.json | 20 +-
docs/reference/cli/templates.md | 1 +
docs/reference/cli/templates_presets.md | 32 +++
...sets_list.md => templates_presets_list.md} | 14 +-
docs/reference/cli/templates_versions.md | 13 +-
.../cli/templates_versions_presets.md | 28 ---
17 files changed, 380 insertions(+), 240 deletions(-)
rename cli/{templateversionpresets.go => templatepresets.go} (58%)
create mode 100644 cli/templatepresets_test.go
delete mode 100644 cli/templateversionpresets_test.go
create mode 100644 cli/testdata/coder_templates_presets_--help.golden
create mode 100644 cli/testdata/coder_templates_presets_list_--help.golden
delete mode 100644 cli/testdata/coder_templates_versions_presets_--help.golden
delete mode 100644 cli/testdata/coder_templates_versions_presets_--help_--help.golden
create mode 100644 docs/reference/cli/templates_presets.md
rename docs/reference/cli/{templates_versions_presets_list.md => templates_presets_list.md} (72%)
delete mode 100644 docs/reference/cli/templates_versions_presets.md
diff --git a/cli/templateversionpresets.go b/cli/templatepresets.go
similarity index 58%
rename from cli/templateversionpresets.go
rename to cli/templatepresets.go
index d698ef0581260..430aaab1ef24d 100644
--- a/cli/templateversionpresets.go
+++ b/cli/templatepresets.go
@@ -12,29 +12,33 @@ import (
"github.com/coder/serpent"
)
-func (r *RootCmd) templateVersionPresets() *serpent.Command {
+func (r *RootCmd) templatePresets() *serpent.Command {
cmd := &serpent.Command{
Use: "presets",
- Short: "Manage presets of the specified template version",
+ Short: "Manage presets of the specified template",
Aliases: []string{"preset"},
Long: FormatExamples(
Example{
- Description: "List presets of a specific template version",
- Command: "coder templates versions presets list my-template my-template-version",
+ Description: "List presets for the active version of a template",
+ Command: "coder templates presets list my-template",
+ },
+ Example{
+ Description: "List presets for a specific version of a template",
+ Command: "coder templates presets list my-template --template-version my-template-version",
},
),
Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv)
},
Children: []*serpent.Command{
- r.templateVersionPresetsList(),
+ r.templatePresetsList(),
},
}
return cmd
}
-func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
+func (r *RootCmd) templatePresetsList() *serpent.Command {
defaultColumns := []string{
"name",
"parameters",
@@ -42,20 +46,29 @@ func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
"desired prebuild instances",
}
formatter := cliui.NewOutputFormatter(
- cliui.TableFormat([]templateVersionPresetRow{}, defaultColumns),
+ cliui.TableFormat([]templatePresetRow{}, defaultColumns),
cliui.JSONFormat(),
)
client := new(codersdk.Client)
orgContext := NewOrganizationContext()
+ var templateVersion string
+
cmd := &serpent.Command{
- Use: "list ",
+ Use: "list ",
Middleware: serpent.Chain(
- serpent.RequireNArgs(2),
+ serpent.RequireNArgs(1),
r.InitClient(client),
),
- Short: "List all the presets of the specified template version",
- Options: serpent.OptionSet{},
+ Short: "List all presets of the specified template. Defaults to the active template version.",
+ Options: serpent.OptionSet{
+ {
+ Name: "template-version",
+ Description: "Specify a template version to list presets for. Defaults to the active version.",
+ Flag: "template-version",
+ Value: serpent.StringOf(&templateVersion),
+ },
+ },
Handler: func(inv *serpent.Invocation) error {
organization, err := orgContext.Selected(inv, client)
if err != nil {
@@ -67,9 +80,19 @@ func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
return xerrors.Errorf("get template by name: %w", err)
}
- version, err := client.TemplateVersionByName(inv.Context(), template.ID, inv.Args[1])
- if err != nil {
- return xerrors.Errorf("get template version by name: %w", err)
+ // If a template version is specified via flag, fetch that version by name
+ var version codersdk.TemplateVersion
+ if len(templateVersion) > 0 {
+ version, err = client.TemplateVersionByName(inv.Context(), template.ID, templateVersion)
+ if err != nil {
+ return xerrors.Errorf("get template version by name: %w", err)
+ }
+ } else {
+ // Otherwise, use the template's active version
+ version, err = client.TemplateVersion(inv.Context(), template.ActiveVersionID)
+ if err != nil {
+ return xerrors.Errorf("get active template version: %w", err)
+ }
}
presets, err := client.TemplateVersionPresets(inv.Context(), version.ID)
@@ -84,7 +107,7 @@ func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
)
}
- rows := templateVersionPresetsToRows(presets...)
+ rows := templatePresetsToRows(presets...)
out, err := formatter.Format(inv.Context(), rows)
if err != nil {
return xerrors.Errorf("render table: %w", err)
@@ -100,9 +123,9 @@ func (r *RootCmd) templateVersionPresetsList() *serpent.Command {
return cmd
}
-type templateVersionPresetRow struct {
+type templatePresetRow struct {
// For json format:
- TemplateVersionPreset codersdk.Preset `table:"-"`
+ TemplatePreset codersdk.Preset `table:"-"`
// For table format:
Name string `json:"-" table:"name,default_sort"`
@@ -119,16 +142,16 @@ func formatPresetParameters(params []codersdk.PresetParameter) string {
return strings.Join(paramsStr, ",")
}
-// templateVersionPresetsToRows converts a list of presets to a list of rows
+// templatePresetsToRows converts a list of presets to a list of rows
// for outputting.
-func templateVersionPresetsToRows(presets ...codersdk.Preset) []templateVersionPresetRow {
- rows := make([]templateVersionPresetRow, len(presets))
+func templatePresetsToRows(presets ...codersdk.Preset) []templatePresetRow {
+ rows := make([]templatePresetRow, len(presets))
for i, preset := range presets {
prebuildInstances := "-"
if preset.DesiredPrebuildInstances != nil {
prebuildInstances = strconv.Itoa(*preset.DesiredPrebuildInstances)
}
- rows[i] = templateVersionPresetRow{
+ rows[i] = templatePresetRow{
Name: preset.Name,
Parameters: formatPresetParameters(preset.Parameters),
Default: preset.Default,
diff --git a/cli/templatepresets_test.go b/cli/templatepresets_test.go
new file mode 100644
index 0000000000000..fa62fe865bf0b
--- /dev/null
+++ b/cli/templatepresets_test.go
@@ -0,0 +1,226 @@
+package cli_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/coder/v2/testutil"
+
+ "github.com/coder/coder/v2/provisioner/echo"
+ "github.com/coder/coder/v2/provisionersdk/proto"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/coder/coder/v2/cli/clitest"
+ "github.com/coder/coder/v2/coderd/coderdtest"
+ "github.com/coder/coder/v2/pty/ptytest"
+)
+
+func TestTemplatePresets(t *testing.T) {
+ t.Parallel()
+
+ t.Run("NoPresets", func(t *testing.T) {
+ t.Parallel()
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
+ owner := coderdtest.CreateFirstUser(t, client)
+ member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
+
+ // Given: a template version without presets
+ version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets([]*proto.Preset{}))
+ _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
+
+ // When: listing presets for that template
+ inv, root := clitest.New(t, "templates", "presets", "list", template.Name)
+ clitest.SetupConfig(t, member, root)
+
+ pty := ptytest.New(t).Attach(inv)
+ doneChan := make(chan struct{})
+ var runErr error
+ go func() {
+ defer close(doneChan)
+ runErr = inv.Run()
+ }()
+ <-doneChan
+ require.NoError(t, runErr)
+
+ // Should return a message when no presets are found for the given template and version.
+ notFoundMessage := fmt.Sprintf("No presets found for template %q and template-version %q.", template.Name, version.Name)
+ pty.ExpectRegexMatch(notFoundMessage)
+ })
+
+ t.Run("ListsPresetsForDefaultTemplateVersion", func(t *testing.T) {
+ t.Parallel()
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
+ owner := coderdtest.CreateFirstUser(t, client)
+ member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
+
+ // Given: an active template version that includes presets
+ presets := []*proto.Preset{
+ {
+ Name: "preset-multiple-params",
+ Parameters: []*proto.PresetParameter{
+ {
+ Name: "k1",
+ Value: "v1",
+ }, {
+ Name: "k2",
+ Value: "v2",
+ },
+ },
+ },
+ {
+ Name: "preset-default",
+ Default: true,
+ Parameters: []*proto.PresetParameter{
+ {
+ Name: "k1",
+ Value: "v2",
+ },
+ },
+ Prebuild: &proto.Prebuild{
+ Instances: 0,
+ },
+ },
+ {
+ Name: "preset-prebuilds",
+ Parameters: []*proto.PresetParameter{},
+ Prebuild: &proto.Prebuild{
+ Instances: 2,
+ },
+ },
+ }
+ version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets(presets))
+ _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
+ require.Equal(t, version.ID, template.ActiveVersionID)
+
+ // When: listing presets for that template
+ inv, root := clitest.New(t, "templates", "presets", "list", template.Name)
+ clitest.SetupConfig(t, member, root)
+
+ pty := ptytest.New(t).Attach(inv)
+ doneChan := make(chan struct{})
+ var runErr error
+ go func() {
+ defer close(doneChan)
+ runErr = inv.Run()
+ }()
+
+ <-doneChan
+ require.NoError(t, runErr)
+
+ // Should: return the active version's presets sorted by name
+ pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
+ // The parameter order is not guaranteed in the output, so we match both possible orders
+ pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
+ pty.ExpectRegexMatch(`preset-prebuilds\s+\s+false\s+2`)
+ })
+
+ t.Run("ListsPresetsForSpecifiedTemplateVersion", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitMedium)
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
+ owner := coderdtest.CreateFirstUser(t, client)
+ member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
+
+ // Given: a template with an active version that has no presets,
+ // and another template version that includes presets
+ presets := []*proto.Preset{
+ {
+ Name: "preset-multiple-params",
+ Parameters: []*proto.PresetParameter{
+ {
+ Name: "k1",
+ Value: "v1",
+ }, {
+ Name: "k2",
+ Value: "v2",
+ },
+ },
+ },
+ {
+ Name: "preset-default",
+ Default: true,
+ Parameters: []*proto.PresetParameter{
+ {
+ Name: "k1",
+ Value: "v2",
+ },
+ },
+ Prebuild: &proto.Prebuild{
+ Instances: 0,
+ },
+ },
+ {
+ Name: "preset-prebuilds",
+ Parameters: []*proto.PresetParameter{},
+ Prebuild: &proto.Prebuild{
+ Instances: 2,
+ },
+ },
+ }
+ // Given: first template version with presets
+ version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets(presets))
+ _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
+ // Given: second template version without presets
+ activeVersion := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets([]*proto.Preset{}), template.ID)
+ _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, activeVersion.ID)
+ // Given: second template version is the active version
+ err := client.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{
+ ID: activeVersion.ID,
+ })
+ require.NoError(t, err)
+ updatedTemplate, err := client.Template(ctx, template.ID)
+ require.NoError(t, err)
+ require.Equal(t, activeVersion.ID, updatedTemplate.ActiveVersionID)
+ // Given: template has two versions
+ templateVersions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
+ TemplateID: updatedTemplate.ID,
+ })
+ require.NoError(t, err)
+ require.Len(t, templateVersions, 2)
+
+ // When: listing presets for a specific template and its specified version
+ inv, root := clitest.New(t, "templates", "presets", "list", updatedTemplate.Name, "--template-version", version.Name)
+ clitest.SetupConfig(t, member, root)
+
+ pty := ptytest.New(t).Attach(inv)
+ doneChan := make(chan struct{})
+ var runErr error
+ go func() {
+ defer close(doneChan)
+ runErr = inv.Run()
+ }()
+
+ <-doneChan
+ require.NoError(t, runErr)
+
+ // Should: return the specified version's presets sorted by name
+ pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
+ // The parameter order is not guaranteed in the output, so we match both possible orders
+ pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
+ pty.ExpectRegexMatch(`preset-prebuilds\s+\s+false\s+2`)
+ })
+}
+
+func templateWithPresets(presets []*proto.Preset) *echo.Responses {
+ return &echo.Responses{
+ Parse: echo.ParseComplete,
+ ProvisionPlan: []*proto.Response{
+ {
+ Type: &proto.Response_Plan{
+ Plan: &proto.PlanComplete{
+ Presets: presets,
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/cli/templates.go b/cli/templates.go
index 4843ca89deeef..3eca3df99c10e 100644
--- a/cli/templates.go
+++ b/cli/templates.go
@@ -33,6 +33,7 @@ func (r *RootCmd) templates() *serpent.Command {
r.templateList(),
r.templatePush(),
r.templateVersions(),
+ r.templatePresets(),
r.templateDelete(),
r.templatePull(),
r.archiveTemplateVersions(),
diff --git a/cli/templateversionpresets_test.go b/cli/templateversionpresets_test.go
deleted file mode 100644
index 033fb111e7377..0000000000000
--- a/cli/templateversionpresets_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package cli_test
-
-import (
- "fmt"
- "testing"
-
- "github.com/coder/coder/v2/provisioner/echo"
- "github.com/coder/coder/v2/provisionersdk/proto"
-
- "github.com/stretchr/testify/require"
-
- "github.com/coder/coder/v2/cli/clitest"
- "github.com/coder/coder/v2/coderd/coderdtest"
- "github.com/coder/coder/v2/pty/ptytest"
-)
-
-func TestTemplateVersionPresets(t *testing.T) {
- t.Parallel()
-
- t.Run("ListPresets", func(t *testing.T) {
- t.Parallel()
-
- client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
- owner := coderdtest.CreateFirstUser(t, client)
- member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
-
- // Given: a template version that includes presets
- presets := []*proto.Preset{
- {
- Name: "preset-multiple-params",
- Parameters: []*proto.PresetParameter{
- {
- Name: "k1",
- Value: "v1",
- }, {
- Name: "k2",
- Value: "v2",
- },
- },
- },
- {
- Name: "preset-default",
- Default: true,
- Parameters: []*proto.PresetParameter{
- {
- Name: "k1",
- Value: "v2",
- },
- },
- Prebuild: &proto.Prebuild{
- Instances: 0,
- },
- },
- {
- Name: "preset-prebuilds",
- Parameters: []*proto.PresetParameter{},
- Prebuild: &proto.Prebuild{
- Instances: 2,
- },
- },
- }
- version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets(presets))
- _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
- template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
-
- // When: listing presets for that template and template version
- inv, root := clitest.New(t, "templates", "versions", "presets", "list", template.Name, version.Name)
- clitest.SetupConfig(t, member, root)
-
- pty := ptytest.New(t).Attach(inv)
- doneChan := make(chan struct{})
- var runErr error
- go func() {
- defer close(doneChan)
- runErr = inv.Run()
- }()
-
- <-doneChan
- require.NoError(t, runErr)
-
- // Should: return the presets sorted by name
- pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
- // The parameter order is not guaranteed in the output, so we match both possible orders
- pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
- pty.ExpectRegexMatch(`preset-prebuilds\s+\s+false\s+2`)
- })
-
- t.Run("NoPresets", func(t *testing.T) {
- t.Parallel()
-
- client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
- owner := coderdtest.CreateFirstUser(t, client)
- member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
-
- // Given: a template version without presets
- version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets([]*proto.Preset{}))
- _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
- template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
-
- // When: listing presets for that template and template version
- inv, root := clitest.New(t, "templates", "versions", "presets", "list", template.Name, version.Name)
- clitest.SetupConfig(t, member, root)
-
- pty := ptytest.New(t).Attach(inv)
- doneChan := make(chan struct{})
- var runErr error
- go func() {
- defer close(doneChan)
- runErr = inv.Run()
- }()
- <-doneChan
- require.NoError(t, runErr)
-
- // Should return a message when no presets are found for the given template and version.
- notFoundMessage := fmt.Sprintf("No presets found for template %q and template-version %q.", template.Name, version.Name)
- pty.ExpectRegexMatch(notFoundMessage)
- })
-}
-
-func templateWithPresets(presets []*proto.Preset) *echo.Responses {
- return &echo.Responses{
- Parse: echo.ParseComplete,
- ProvisionPlan: []*proto.Response{
- {
- Type: &proto.Response_Plan{
- Plan: &proto.PlanComplete{
- Presets: presets,
- },
- },
- },
- },
- }
-}
diff --git a/cli/templateversions.go b/cli/templateversions.go
index fa80f075a1601..c90903a7c4f93 100644
--- a/cli/templateversions.go
+++ b/cli/templateversions.go
@@ -33,7 +33,6 @@ func (r *RootCmd) templateVersions() *serpent.Command {
r.archiveTemplateVersion(),
r.unarchiveTemplateVersion(),
r.templateVersionsPromote(),
- r.templateVersionPresets(),
},
}
diff --git a/cli/testdata/coder_templates_--help.golden b/cli/testdata/coder_templates_--help.golden
index a198a6772313f..2c09f2951d999 100644
--- a/cli/testdata/coder_templates_--help.golden
+++ b/cli/testdata/coder_templates_--help.golden
@@ -23,6 +23,7 @@ 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
+ presets Manage presets of the specified template
pull Download the active, latest, or specified version of a template
to a path.
push Create or update a template from the current directory or as
diff --git a/cli/testdata/coder_templates_presets_--help.golden b/cli/testdata/coder_templates_presets_--help.golden
new file mode 100644
index 0000000000000..0aad71383edd4
--- /dev/null
+++ b/cli/testdata/coder_templates_presets_--help.golden
@@ -0,0 +1,24 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder templates presets
+
+ Manage presets of the specified template
+
+ Aliases: preset
+
+ - List presets for the active version of a template:
+
+ $ coder templates presets list my-template
+
+ - List presets for a specific version of a template:
+
+ $ coder templates presets list my-template --template-version
+ my-template-version
+
+SUBCOMMANDS:
+ list List all presets of the specified template. Defaults to the active
+ template version.
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_templates_presets_list_--help.golden b/cli/testdata/coder_templates_presets_list_--help.golden
new file mode 100644
index 0000000000000..81445df03cc97
--- /dev/null
+++ b/cli/testdata/coder_templates_presets_list_--help.golden
@@ -0,0 +1,24 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder templates presets list [flags]
+
+ List all presets of the specified template. Defaults to the active template
+ version.
+
+OPTIONS:
+ -O, --org string, $CODER_ORGANIZATION
+ Select which organization (uuid or name) to use.
+
+ -c, --column [name|parameters|default|desired prebuild instances] (default: name,parameters,default,desired prebuild instances)
+ Columns to display in table output.
+
+ -o, --output table|json (default: table)
+ Output format.
+
+ --template-version string
+ Specify a template version to list presets for. Defaults to the active
+ version.
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_templates_versions_--help.golden b/cli/testdata/coder_templates_versions_--help.golden
index 8468395627dbb..fa276999563d2 100644
--- a/cli/testdata/coder_templates_versions_--help.golden
+++ b/cli/testdata/coder_templates_versions_--help.golden
@@ -14,7 +14,6 @@ USAGE:
SUBCOMMANDS:
archive Archive a template version(s).
list List all the versions of the specified template
- presets Manage presets of the specified template version
promote Promote a template version to active.
unarchive Unarchive a template version(s).
diff --git a/cli/testdata/coder_templates_versions_presets_--help.golden b/cli/testdata/coder_templates_versions_presets_--help.golden
deleted file mode 100644
index 8967c85d34881..0000000000000
--- a/cli/testdata/coder_templates_versions_presets_--help.golden
+++ /dev/null
@@ -1,18 +0,0 @@
-coder v0.0.0-devel
-
-USAGE:
- coder templates versions presets
-
- Manage presets of the specified template version
-
- Aliases: preset
-
- - List presets of a specific template version:
-
- $ coder templates versions presets list my-template my-template-version
-
-SUBCOMMANDS:
- list List all the presets of the specified template version
-
-———
-Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_templates_versions_presets_--help_--help.golden b/cli/testdata/coder_templates_versions_presets_--help_--help.golden
deleted file mode 100644
index 8967c85d34881..0000000000000
--- a/cli/testdata/coder_templates_versions_presets_--help_--help.golden
+++ /dev/null
@@ -1,18 +0,0 @@
-coder v0.0.0-devel
-
-USAGE:
- coder templates versions presets
-
- Manage presets of the specified template version
-
- Aliases: preset
-
- - List presets of a specific template version:
-
- $ coder templates versions presets list my-template my-template-version
-
-SUBCOMMANDS:
- list List all the presets of the specified template version
-
-———
-Run `coder --help` for a list of global options.
diff --git a/docs/manifest.json b/docs/manifest.json
index 4df04d54e6ce0..0305105c029fd 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -1651,6 +1651,16 @@
"description": "List all the templates available for the organization",
"path": "reference/cli/templates_list.md"
},
+ {
+ "title": "templates presets",
+ "description": "Manage presets of the specified template",
+ "path": "reference/cli/templates_presets.md"
+ },
+ {
+ "title": "templates presets list",
+ "description": "List all presets of the specified template. Defaults to the active template version.",
+ "path": "reference/cli/templates_presets_list.md"
+ },
{
"title": "templates pull",
"description": "Download the active, latest, or specified version of a template to a path.",
@@ -1676,16 +1686,6 @@
"description": "List all the versions of the specified template",
"path": "reference/cli/templates_versions_list.md"
},
- {
- "title": "templates versions presets",
- "description": "Manage presets of the specified template version",
- "path": "reference/cli/templates_versions_presets.md"
- },
- {
- "title": "templates versions presets list",
- "description": "List all the presets of the specified template version",
- "path": "reference/cli/templates_versions_presets_list.md"
- },
{
"title": "templates versions promote",
"description": "Promote a template version to active.",
diff --git a/docs/reference/cli/templates.md b/docs/reference/cli/templates.md
index 99052aa6c3e20..e1141f5db8571 100644
--- a/docs/reference/cli/templates.md
+++ b/docs/reference/cli/templates.md
@@ -33,6 +33,7 @@ workspaces:
| [list
](./templates_list.md) | List all the templates available for the organization |
| [push
](./templates_push.md) | Create or update a template from the current directory or as specified by flag |
| [versions
](./templates_versions.md) | Manage different versions of the specified template |
+| [presets
](./templates_presets.md) | Manage presets of the specified template |
| [delete
](./templates_delete.md) | Delete templates |
| [pull
](./templates_pull.md) | Download the active, latest, or specified version of a template to a path. |
| [archive
](./templates_archive.md) | Archive unused or failed template versions from a given template(s) |
diff --git a/docs/reference/cli/templates_presets.md b/docs/reference/cli/templates_presets.md
new file mode 100644
index 0000000000000..a03f206366f20
--- /dev/null
+++ b/docs/reference/cli/templates_presets.md
@@ -0,0 +1,32 @@
+
+# templates presets
+
+Manage presets of the specified template
+
+Aliases:
+
+* preset
+
+## Usage
+
+```console
+coder templates presets
+```
+
+## Description
+
+```console
+ - List presets for the active version of a template:
+
+ $ coder templates presets list my-template
+
+ - List presets for a specific version of a template:
+
+ $ coder templates presets list my-template --template-version my-template-version
+```
+
+## Subcommands
+
+| Name | Purpose |
+|--------------------------------------------------|--------------------------------------------------------------------------------------|
+| [list
](./templates_presets_list.md) | List all presets of the specified template. Defaults to the active template version. |
diff --git a/docs/reference/cli/templates_versions_presets_list.md b/docs/reference/cli/templates_presets_list.md
similarity index 72%
rename from docs/reference/cli/templates_versions_presets_list.md
rename to docs/reference/cli/templates_presets_list.md
index ffdbf79b0b9c7..69dd12faadc7b 100644
--- a/docs/reference/cli/templates_versions_presets_list.md
+++ b/docs/reference/cli/templates_presets_list.md
@@ -1,16 +1,24 @@
-# templates versions presets list
+# templates presets list
-List all the presets of the specified template version
+List all presets of the specified template. Defaults to the active template version.
## Usage
```console
-coder templates versions presets list [flags]
+coder templates presets list [flags]
```
## Options
+### --template-version
+
+| | |
+|------|---------------------|
+| Type | string
|
+
+Specify a template version to list presets for. Defaults to the active version.
+
### -O, --org
| | |
diff --git a/docs/reference/cli/templates_versions.md b/docs/reference/cli/templates_versions.md
index 6a65c7ddf33e5..8eb927967d162 100644
--- a/docs/reference/cli/templates_versions.md
+++ b/docs/reference/cli/templates_versions.md
@@ -23,10 +23,9 @@ coder templates versions
## Subcommands
-| Name | Purpose |
-|-------------------------------------------------------------|--------------------------------------------------|
-| [list
](./templates_versions_list.md) | List all the versions of the specified template |
-| [archive
](./templates_versions_archive.md) | Archive a template version(s). |
-| [unarchive
](./templates_versions_unarchive.md) | Unarchive a template version(s). |
-| [promote
](./templates_versions_promote.md) | Promote a template version to active. |
-| [presets
](./templates_versions_presets.md) | Manage presets of the specified template version |
+| Name | Purpose |
+|-------------------------------------------------------------|-------------------------------------------------|
+| [list
](./templates_versions_list.md) | List all the versions of the specified template |
+| [archive
](./templates_versions_archive.md) | Archive a template version(s). |
+| [unarchive
](./templates_versions_unarchive.md) | Unarchive a template version(s). |
+| [promote
](./templates_versions_promote.md) | Promote a template version to active. |
diff --git a/docs/reference/cli/templates_versions_presets.md b/docs/reference/cli/templates_versions_presets.md
deleted file mode 100644
index 6dc404dc0fc01..0000000000000
--- a/docs/reference/cli/templates_versions_presets.md
+++ /dev/null
@@ -1,28 +0,0 @@
-
-# templates versions presets
-
-Manage presets of the specified template version
-
-Aliases:
-
-* preset
-
-## Usage
-
-```console
-coder templates versions presets
-```
-
-## Description
-
-```console
- - List presets of a specific template version:
-
- $ coder templates versions presets list my-template my-template-version
-```
-
-## Subcommands
-
-| Name | Purpose |
-|-----------------------------------------------------------|--------------------------------------------------------|
-| [list
](./templates_versions_presets_list.md) | List all the presets of the specified template version |
From 348148ea577e293ff2aa2c6c4bfdc34d6070787d Mon Sep 17 00:00:00 2001
From: Susana Ferreira
Date: Tue, 22 Jul 2025 19:36:21 +0000
Subject: [PATCH 5/5] chore: add message describing used template and
template-version
---
cli/templatepresets.go | 5 +++++
cli/templatepresets_test.go | 14 ++++++++------
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/cli/templatepresets.go b/cli/templatepresets.go
index 430aaab1ef24d..ab0d49725b99a 100644
--- a/cli/templatepresets.go
+++ b/cli/templatepresets.go
@@ -105,8 +105,13 @@ func (r *RootCmd) templatePresetsList() *serpent.Command {
inv.Stdout,
"No presets found for template %q and template-version %q.\n", template.Name, version.Name,
)
+ return nil
}
+ cliui.Infof(
+ inv.Stdout,
+ "Showing presets for template %q and template version %q.\n", template.Name, version.Name,
+ )
rows := templatePresetsToRows(presets...)
out, err := formatter.Format(inv.Context(), rows)
if err != nil {
diff --git a/cli/templatepresets_test.go b/cli/templatepresets_test.go
index fa62fe865bf0b..47d34af2dcf2d 100644
--- a/cli/templatepresets_test.go
+++ b/cli/templatepresets_test.go
@@ -4,17 +4,15 @@ import (
"fmt"
"testing"
- "github.com/coder/coder/v2/codersdk"
- "github.com/coder/coder/v2/testutil"
-
- "github.com/coder/coder/v2/provisioner/echo"
- "github.com/coder/coder/v2/provisionersdk/proto"
-
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest"
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/coder/v2/provisioner/echo"
+ "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/pty/ptytest"
+ "github.com/coder/coder/v2/testutil"
)
func TestTemplatePresets(t *testing.T) {
@@ -114,6 +112,8 @@ func TestTemplatePresets(t *testing.T) {
require.NoError(t, runErr)
// Should: return the active version's presets sorted by name
+ message := fmt.Sprintf("Showing presets for template %q and template version %q.", template.Name, version.Name)
+ pty.ExpectMatch(message)
pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
// The parameter order is not guaranteed in the output, so we match both possible orders
pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
@@ -203,6 +203,8 @@ func TestTemplatePresets(t *testing.T) {
require.NoError(t, runErr)
// Should: return the specified version's presets sorted by name
+ message := fmt.Sprintf("Showing presets for template %q and template version %q.", template.Name, version.Name)
+ pty.ExpectMatch(message)
pty.ExpectRegexMatch(`preset-default\s+k1=v2\s+true\s+0`)
// The parameter order is not guaranteed in the output, so we match both possible orders
pty.ExpectRegexMatch(`preset-multiple-params\s+(k1=v1,k2=v2)|(k2=v2,k1=v1)\s+false\s+-`)
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/coder/coder/pull/18910.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy