Skip to content

Commit d923e1b

Browse files
committed
Merge branch 'main' into bq/refactor-workspace-header
2 parents ebf0011 + 87fe16c commit d923e1b

File tree

6 files changed

+113
-30
lines changed

6 files changed

+113
-30
lines changed

cli/templateinit.go

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@ package cli
22

33
import (
44
"bytes"
5+
"errors"
56
"fmt"
7+
"io"
68
"os"
79
"path/filepath"
10+
"sort"
11+
12+
"golang.org/x/exp/maps"
13+
"golang.org/x/xerrors"
814

915
"github.com/coder/coder/cli/clibase"
1016
"github.com/coder/coder/cli/cliui"
@@ -14,38 +20,60 @@ import (
1420
)
1521

1622
func (*RootCmd) templateInit() *clibase.Cmd {
17-
return &clibase.Cmd{
23+
var templateID string
24+
exampleList, err := examples.List()
25+
if err != nil {
26+
// This should not happen. If it does, something is very wrong.
27+
panic(err)
28+
}
29+
var templateIDs []string
30+
for _, ex := range exampleList {
31+
templateIDs = append(templateIDs, ex.ID)
32+
}
33+
sort.Strings(templateIDs)
34+
cmd := &clibase.Cmd{
1835
Use: "init [directory]",
1936
Short: "Get started with a templated template.",
2037
Middleware: clibase.RequireRangeArgs(0, 1),
2138
Handler: func(inv *clibase.Invocation) error {
22-
exampleList, err := examples.List()
23-
if err != nil {
24-
return err
25-
}
26-
exampleNames := []string{}
27-
exampleByName := map[string]codersdk.TemplateExample{}
28-
for _, example := range exampleList {
29-
name := fmt.Sprintf(
30-
"%s\n%s\n%s\n",
31-
cliui.Styles.Bold.Render(example.Name),
32-
cliui.Styles.Wrap.Copy().PaddingLeft(6).Render(example.Description),
33-
cliui.Styles.Keyword.Copy().PaddingLeft(6).Render(example.URL),
34-
)
35-
exampleNames = append(exampleNames, name)
36-
exampleByName[name] = example
39+
// If the user didn't specify any template, prompt them to select one.
40+
if templateID == "" {
41+
optsToID := map[string]string{}
42+
for _, example := range exampleList {
43+
name := fmt.Sprintf(
44+
"%s\n%s\n%s\n",
45+
cliui.Styles.Bold.Render(example.Name),
46+
cliui.Styles.Wrap.Copy().PaddingLeft(6).Render(example.Description),
47+
cliui.Styles.Keyword.Copy().PaddingLeft(6).Render(example.URL),
48+
)
49+
optsToID[name] = example.ID
50+
}
51+
opts := maps.Keys(optsToID)
52+
sort.Strings(opts)
53+
_, _ = fmt.Fprintln(inv.Stdout, cliui.Styles.Wrap.Render(
54+
"A template defines infrastructure as code to be provisioned "+
55+
"for individual developer workspaces. Select an example to be copied to the active directory:\n"))
56+
selected, err := cliui.Select(inv, cliui.SelectOptions{
57+
Options: opts,
58+
})
59+
if err != nil {
60+
if errors.Is(err, io.EOF) {
61+
return xerrors.Errorf(
62+
"Couldn't find a matching template!\n" +
63+
"Tip: if you're trying to automate template creation, try\n" +
64+
"coder templates init --id <template_id> instead!",
65+
)
66+
}
67+
return err
68+
}
69+
templateID = optsToID[selected]
3770
}
3871

39-
_, _ = fmt.Fprintln(inv.Stdout, cliui.Styles.Wrap.Render(
40-
"A template defines infrastructure as code to be provisioned "+
41-
"for individual developer workspaces. Select an example to be copied to the active directory:\n"))
42-
option, err := cliui.Select(inv, cliui.SelectOptions{
43-
Options: exampleNames,
44-
})
45-
if err != nil {
46-
return err
72+
selectedTemplate, ok := templateByID(templateID, exampleList)
73+
if !ok {
74+
// clibase.EnumOf would normally handle this.
75+
return xerrors.Errorf("template not found: %q", templateID)
4776
}
48-
selectedTemplate := exampleByName[option]
4977
archive, err := examples.Archive(selectedTemplate.ID)
5078
if err != nil {
5179
return err
@@ -81,4 +109,23 @@ func (*RootCmd) templateInit() *clibase.Cmd {
81109
return nil
82110
},
83111
}
112+
113+
cmd.Options = clibase.OptionSet{
114+
{
115+
Flag: "id",
116+
Description: "Specify a given example template by ID.",
117+
Value: clibase.EnumOf(&templateID, templateIDs...),
118+
},
119+
}
120+
121+
return cmd
122+
}
123+
124+
func templateByID(templateID string, tes []codersdk.TemplateExample) (codersdk.TemplateExample, bool) {
125+
for _, te := range tes {
126+
if te.ID == templateID {
127+
return te, true
128+
}
129+
}
130+
return codersdk.TemplateExample{}, false
84131
}

cli/templateinit_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,27 @@ func TestTemplateInit(t *testing.T) {
2222
require.NoError(t, err)
2323
require.Greater(t, len(files), 0)
2424
})
25+
26+
t.Run("ExtractSpecific", func(t *testing.T) {
27+
t.Parallel()
28+
tempDir := t.TempDir()
29+
inv, _ := clitest.New(t, "templates", "init", "--id", "docker", tempDir)
30+
ptytest.New(t).Attach(inv)
31+
clitest.Run(t, inv)
32+
files, err := os.ReadDir(tempDir)
33+
require.NoError(t, err)
34+
require.Greater(t, len(files), 0)
35+
})
36+
37+
t.Run("NotFound", func(t *testing.T) {
38+
t.Parallel()
39+
tempDir := t.TempDir()
40+
inv, _ := clitest.New(t, "templates", "init", "--id", "thistemplatedoesnotexist", tempDir)
41+
ptytest.New(t).Attach(inv)
42+
err := inv.Run()
43+
require.ErrorContains(t, err, "invalid choice: thistemplatedoesnotexist, should be one of")
44+
files, err := os.ReadDir(tempDir)
45+
require.NoError(t, err)
46+
require.Empty(t, files)
47+
})
2548
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
Usage: coder templates init [directory]
1+
Usage: coder templates init [flags] [directory]
22

33
Get started with a templated template.
44

5+
Options
6+
--id aws-ecs-container|aws-linux|aws-windows|azure-linux|do-linux|docker|docker-with-dotfiles|fly-docker-image|gcp-linux|gcp-vm-container|gcp-windows|kubernetes
7+
Specify a given example template by ID.
8+
59
---
610
Run `coder --help` for a list of global options.

docs/cli/templates_init.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,15 @@ Get started with a templated template.
77
## Usage
88

99
```console
10-
coder templates init [directory]
10+
coder templates init [flags] [directory]
1111
```
12+
13+
## Options
14+
15+
### --id
16+
17+
| | |
18+
| ---- | ---------------------------- | --------- | ----------- | ----------- | -------- | ------ | -------------------- | ---------------- | --------- | ---------------- | ----------- | ------------------ |
19+
| Type | <code>enum[aws-ecs-container | aws-linux | aws-windows | azure-linux | do-linux | docker | docker-with-dotfiles | fly-docker-image | gcp-linux | gcp-vm-container | gcp-windows | kubernetes]</code> |
20+
21+
Specify a given example template by ID.

examples/lima/coder.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ provision:
9696
[ ! -e ~/.config/coderv2/session ] && coder login http://localhost:3000 --first-user-username admin --first-user-email admin@coder.com --first-user-password $(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c12 | tee ${HOME}/.config/coderv2/password)
9797
# Create an initial template
9898
temp_template_dir=$(mktemp -d)
99-
echo code-server | coder templates init "${temp_template_dir}"
99+
coder templates init --id docker "${temp_template_dir}"
100100
DOCKER_ARCH="amd64"
101101
if [ "$(arch)" = "aarch64" ]; then
102102
DOCKER_ARCH="arm64"

scripts/develop.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,14 @@ fatal() {
151151

152152
# If we have docker available and the "docker" template doesn't already
153153
# exist, then let's try to create a template!
154-
example_template="code-server"
155154
template_name="docker"
156155
if docker info >/dev/null 2>&1 && ! "${CODER_DEV_SHIM}" templates versions list "${template_name}" >/dev/null 2>&1; then
157156
# sometimes terraform isn't installed yet when we go to create the
158157
# template
159158
sleep 5
160159

161160
temp_template_dir="$(mktemp -d)"
162-
echo "${example_template}" | "${CODER_DEV_SHIM}" templates init "${temp_template_dir}"
161+
"${CODER_DEV_SHIM}" templates init --id "${template_name}" "${temp_template_dir}"
163162

164163
DOCKER_HOST="$(docker context inspect --format '{{ .Endpoints.docker.Host }}')"
165164
printf 'docker_arch: "%s"\ndocker_host: "%s"\n' "${GOARCH}" "${DOCKER_HOST}" >"${temp_template_dir}/params.yaml"

0 commit comments

Comments
 (0)