@@ -2,9 +2,15 @@ package cli
2
2
3
3
import (
4
4
"bytes"
5
+ "errors"
5
6
"fmt"
7
+ "io"
6
8
"os"
7
9
"path/filepath"
10
+ "sort"
11
+
12
+ "golang.org/x/exp/maps"
13
+ "golang.org/x/xerrors"
8
14
9
15
"github.com/coder/coder/cli/clibase"
10
16
"github.com/coder/coder/cli/cliui"
@@ -14,38 +20,60 @@ import (
14
20
)
15
21
16
22
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 {
18
35
Use : "init [directory]" ,
19
36
Short : "Get started with a templated template." ,
20
37
Middleware : clibase .RequireRangeArgs (0 , 1 ),
21
38
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 ]
37
70
}
38
71
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 )
47
76
}
48
- selectedTemplate := exampleByName [option ]
49
77
archive , err := examples .Archive (selectedTemplate .ID )
50
78
if err != nil {
51
79
return err
@@ -81,4 +109,23 @@ func (*RootCmd) templateInit() *clibase.Cmd {
81
109
return nil
82
110
},
83
111
}
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
84
131
}
0 commit comments