mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(cli): support description in create and presets list CLI commands (#19079)
## Description This PR improves the `coder templates presets` and `coder create` CLI commands to include preset descriptions. ## Changes * Added a `description` column to the `coder templates presets list` CLI command. * Fixed the `-o json` output for `coder templates presets list` to correctly include and format data. * Updated the `coder create` CLI command to display the preset's description in the selection menu. Follow-up from: * https://github.com/coder/coder/pull/18910 * https://github.com/coder/coder/pull/18912 * https://github.com/coder/coder/pull/18977
This commit is contained in:
+6
-1
@@ -472,7 +472,12 @@ func promptPresetSelection(inv *serpent.Invocation, presets []codersdk.Preset) (
|
||||
var presetOptions []string
|
||||
|
||||
for _, preset := range presets {
|
||||
option := preset.Name
|
||||
var option string
|
||||
if preset.Description == "" {
|
||||
option = preset.Name
|
||||
} else {
|
||||
option = fmt.Sprintf("%s: %s", preset.Name, preset.Description)
|
||||
}
|
||||
presetOptions = append(presetOptions, option)
|
||||
presetMap[option] = &preset
|
||||
}
|
||||
|
||||
+2
-1
@@ -882,7 +882,8 @@ func TestCreateWithPreset(t *testing.T) {
|
||||
|
||||
// Given: a template and a template version with two presets
|
||||
preset := proto.Preset{
|
||||
Name: "preset-test",
|
||||
Name: "preset-test",
|
||||
Description: "Preset Test.",
|
||||
Parameters: []*proto.PresetParameter{
|
||||
{Name: firstParameterName, Value: secondOptionalParameterValue},
|
||||
{Name: thirdParameterName, Value: thirdParameterValue},
|
||||
|
||||
+19
-10
@@ -41,12 +41,13 @@ func (r *RootCmd) templatePresets() *serpent.Command {
|
||||
func (r *RootCmd) templatePresetsList() *serpent.Command {
|
||||
defaultColumns := []string{
|
||||
"name",
|
||||
"description",
|
||||
"parameters",
|
||||
"default",
|
||||
"desired prebuild instances",
|
||||
}
|
||||
formatter := cliui.NewOutputFormatter(
|
||||
cliui.TableFormat([]templatePresetRow{}, defaultColumns),
|
||||
cliui.TableFormat([]TemplatePresetRow{}, defaultColumns),
|
||||
cliui.JSONFormat(),
|
||||
)
|
||||
client := new(codersdk.Client)
|
||||
@@ -108,10 +109,13 @@ func (r *RootCmd) templatePresetsList() *serpent.Command {
|
||||
return nil
|
||||
}
|
||||
|
||||
cliui.Infof(
|
||||
inv.Stdout,
|
||||
"Showing presets for template %q and template version %q.\n", template.Name, version.Name,
|
||||
)
|
||||
// Only display info message for table output
|
||||
if formatter.FormatID() == "table" {
|
||||
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 {
|
||||
@@ -128,12 +132,13 @@ func (r *RootCmd) templatePresetsList() *serpent.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
type templatePresetRow struct {
|
||||
// For json format:
|
||||
type TemplatePresetRow struct {
|
||||
// For json format
|
||||
TemplatePreset codersdk.Preset `table:"-"`
|
||||
|
||||
// For table format:
|
||||
Name string `json:"-" table:"name,default_sort"`
|
||||
Description string `json:"-" table:"description"`
|
||||
Parameters string `json:"-" table:"parameters"`
|
||||
Default bool `json:"-" table:"default"`
|
||||
DesiredPrebuildInstances string `json:"-" table:"desired prebuild instances"`
|
||||
@@ -149,15 +154,19 @@ func formatPresetParameters(params []codersdk.PresetParameter) string {
|
||||
|
||||
// templatePresetsToRows converts a list of presets to a list of rows
|
||||
// for outputting.
|
||||
func templatePresetsToRows(presets ...codersdk.Preset) []templatePresetRow {
|
||||
rows := make([]templatePresetRow, 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] = templatePresetRow{
|
||||
rows[i] = TemplatePresetRow{
|
||||
// For json format
|
||||
TemplatePreset: preset,
|
||||
// For table format
|
||||
Name: preset.Name,
|
||||
Description: preset.Description,
|
||||
Parameters: formatPresetParameters(preset.Parameters),
|
||||
Default: preset.Default,
|
||||
DesiredPrebuildInstances: prebuildInstances,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli"
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -84,8 +87,9 @@ func TestTemplatePresets(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "preset-prebuilds",
|
||||
Parameters: []*proto.PresetParameter{},
|
||||
Name: "preset-prebuilds",
|
||||
Description: "Preset without parameters and 2 prebuild instances.",
|
||||
Parameters: []*proto.PresetParameter{},
|
||||
Prebuild: &proto.Prebuild{
|
||||
Instances: 2,
|
||||
},
|
||||
@@ -117,7 +121,7 @@ func TestTemplatePresets(t *testing.T) {
|
||||
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`)
|
||||
pty.ExpectRegexMatch(`preset-prebuilds\s+Preset without parameters and 2 prebuild instances.\s+\s+false\s+2`)
|
||||
})
|
||||
|
||||
t.Run("ListsPresetsForSpecifiedTemplateVersion", func(t *testing.T) {
|
||||
@@ -158,8 +162,9 @@ func TestTemplatePresets(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "preset-prebuilds",
|
||||
Parameters: []*proto.PresetParameter{},
|
||||
Name: "preset-prebuilds",
|
||||
Description: "Preset without parameters and 2 prebuild instances.",
|
||||
Parameters: []*proto.PresetParameter{},
|
||||
Prebuild: &proto.Prebuild{
|
||||
Instances: 2,
|
||||
},
|
||||
@@ -208,7 +213,69 @@ func TestTemplatePresets(t *testing.T) {
|
||||
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`)
|
||||
pty.ExpectRegexMatch(`preset-prebuilds\s+Preset without parameters and 2 prebuild instances.\s+\s+false\s+2`)
|
||||
})
|
||||
|
||||
t.Run("ListsPresetsJSON", 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
|
||||
preset := proto.Preset{
|
||||
Name: "preset-default",
|
||||
Description: "Preset with parameters and 2 prebuild instances.",
|
||||
Icon: "/emojis/1f60e.png",
|
||||
Default: true,
|
||||
Parameters: []*proto.PresetParameter{
|
||||
{
|
||||
Name: "k1",
|
||||
Value: "v2",
|
||||
},
|
||||
},
|
||||
Prebuild: &proto.Prebuild{
|
||||
Instances: 2,
|
||||
},
|
||||
}
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithPresets([]*proto.Preset{&preset}))
|
||||
_ = 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, "-o", "json")
|
||||
clitest.SetupConfig(t, member, root)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
inv.Stdout = buf
|
||||
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 preset
|
||||
var jsonPresets []cli.TemplatePresetRow
|
||||
err := json.Unmarshal(buf.Bytes(), &jsonPresets)
|
||||
require.NoError(t, err, "unmarshal JSON output")
|
||||
require.Len(t, jsonPresets, 1)
|
||||
|
||||
jsonPreset := jsonPresets[0].TemplatePreset
|
||||
require.Equal(t, preset.Name, jsonPreset.Name)
|
||||
require.Equal(t, preset.Description, jsonPreset.Description)
|
||||
require.Equal(t, preset.Icon, jsonPreset.Icon)
|
||||
require.Equal(t, preset.Default, jsonPreset.Default)
|
||||
require.Equal(t, len(preset.Parameters), len(jsonPreset.Parameters))
|
||||
require.Equal(t, preset.Parameters[0].Name, jsonPreset.Parameters[0].Name)
|
||||
require.Equal(t, preset.Parameters[0].Value, jsonPreset.Parameters[0].Value)
|
||||
require.Equal(t, int(preset.Prebuild.Instances), *jsonPreset.DesiredPrebuildInstances)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ 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)
|
||||
-c, --column [name|description|parameters|default|desired prebuild instances] (default: name,description,parameters,default,desired prebuild instances)
|
||||
Columns to display in table output.
|
||||
|
||||
-o, --output table|json (default: table)
|
||||
|
||||
Reference in New Issue
Block a user