@@ -10,6 +10,7 @@ import (
10
10
"path/filepath"
11
11
"strings"
12
12
"time"
13
+ "unicode/utf8"
13
14
14
15
"github.com/briandowns/spinner"
15
16
"github.com/google/uuid"
@@ -22,140 +23,6 @@ import (
22
23
"github.com/coder/pretty"
23
24
)
24
25
25
- // templateUploadFlags is shared by `templates create` and `templates push`.
26
- type templateUploadFlags struct {
27
- directory string
28
- ignoreLockfile bool
29
- message string
30
- }
31
-
32
- func (pf * templateUploadFlags ) options () []clibase.Option {
33
- return []clibase.Option {{
34
- Flag : "directory" ,
35
- FlagShorthand : "d" ,
36
- Description : "Specify the directory to create from, use '-' to read tar from stdin." ,
37
- Default : "." ,
38
- Value : clibase .StringOf (& pf .directory ),
39
- }, {
40
- Flag : "ignore-lockfile" ,
41
- Description : "Ignore warnings about not having a .terraform.lock.hcl file present in the template." ,
42
- Default : "false" ,
43
- Value : clibase .BoolOf (& pf .ignoreLockfile ),
44
- }, {
45
- Flag : "message" ,
46
- FlagShorthand : "m" ,
47
- Description : "Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as truncated." ,
48
- Value : clibase .StringOf (& pf .message ),
49
- }}
50
- }
51
-
52
- func (pf * templateUploadFlags ) setWorkdir (wd string ) {
53
- if wd == "" {
54
- return
55
- }
56
- if pf .directory == "" || pf .directory == "." {
57
- pf .directory = wd
58
- } else if ! filepath .IsAbs (pf .directory ) {
59
- pf .directory = filepath .Join (wd , pf .directory )
60
- }
61
- }
62
-
63
- func (pf * templateUploadFlags ) stdin () bool {
64
- return pf .directory == "-"
65
- }
66
-
67
- func (pf * templateUploadFlags ) upload (inv * clibase.Invocation , client * codersdk.Client ) (* codersdk.UploadResponse , error ) {
68
- var content io.Reader
69
- if pf .stdin () {
70
- content = inv .Stdin
71
- } else {
72
- prettyDir := prettyDirectoryPath (pf .directory )
73
- _ , err := cliui .Prompt (inv , cliui.PromptOptions {
74
- Text : fmt .Sprintf ("Upload %q?" , prettyDir ),
75
- IsConfirm : true ,
76
- Default : cliui .ConfirmYes ,
77
- })
78
- if err != nil {
79
- return nil , err
80
- }
81
-
82
- pipeReader , pipeWriter := io .Pipe ()
83
- go func () {
84
- err := provisionersdk .Tar (pipeWriter , inv .Logger , pf .directory , provisionersdk .TemplateArchiveLimit )
85
- _ = pipeWriter .CloseWithError (err )
86
- }()
87
- defer pipeReader .Close ()
88
- content = pipeReader
89
- }
90
-
91
- spin := spinner .New (spinner .CharSets [5 ], 100 * time .Millisecond )
92
- spin .Writer = inv .Stdout
93
- spin .Suffix = pretty .Sprint (cliui .DefaultStyles .Keyword , " Uploading directory..." )
94
- spin .Start ()
95
- defer spin .Stop ()
96
-
97
- resp , err := client .Upload (inv .Context (), codersdk .ContentTypeTar , bufio .NewReader (content ))
98
- if err != nil {
99
- return nil , xerrors .Errorf ("upload: %w" , err )
100
- }
101
- return & resp , nil
102
- }
103
-
104
- func (pf * templateUploadFlags ) checkForLockfile (inv * clibase.Invocation ) error {
105
- if pf .stdin () || pf .ignoreLockfile {
106
- // Just assume there's a lockfile if reading from stdin.
107
- return nil
108
- }
109
-
110
- hasLockfile , err := provisionersdk .DirHasLockfile (pf .directory )
111
- if err != nil {
112
- return xerrors .Errorf ("dir has lockfile: %w" , err )
113
- }
114
-
115
- if ! hasLockfile {
116
- cliui .Warn (inv .Stdout , "No .terraform.lock.hcl file found" ,
117
- "When provisioning, Coder will be unable to cache providers without a lockfile and must download them from the internet each time." ,
118
- "Create one by running " + pretty .Sprint (cliui .DefaultStyles .Code , "terraform init" )+ " in your template directory." ,
119
- )
120
- }
121
- return nil
122
- }
123
-
124
- func (pf * templateUploadFlags ) templateMessage (inv * clibase.Invocation ) string {
125
- title := strings .SplitN (pf .message , "\n " , 2 )[0 ]
126
- if len (title ) > 72 {
127
- cliui .Warn (inv .Stdout , "Template message is longer than 72 characters, it will be displayed as truncated." )
128
- }
129
- if title != pf .message {
130
- cliui .Warn (inv .Stdout , "Template message contains newlines, only the first line will be displayed." )
131
- }
132
- if pf .message != "" {
133
- return pf .message
134
- }
135
- return "Uploaded from the CLI"
136
- }
137
-
138
- func (pf * templateUploadFlags ) templateName (args []string ) (string , error ) {
139
- if pf .stdin () {
140
- // Can't infer name from directory if none provided.
141
- if len (args ) == 0 {
142
- return "" , xerrors .New ("template name argument must be provided" )
143
- }
144
- return args [0 ], nil
145
- }
146
-
147
- if len (args ) > 0 {
148
- return args [0 ], nil
149
- }
150
- // Have to take absPath to resolve "." and "..".
151
- absPath , err := filepath .Abs (pf .directory )
152
- if err != nil {
153
- return "" , err
154
- }
155
- // If no name is provided, use the directory name.
156
- return filepath .Base (absPath ), nil
157
- }
158
-
159
26
func (r * RootCmd ) templatePush () * clibase.Cmd {
160
27
var (
161
28
versionName string
@@ -189,6 +56,10 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
189
56
return err
190
57
}
191
58
59
+ if utf8 .RuneCountInString (name ) >= 32 {
60
+ return xerrors .Errorf ("Template name must be less than 32 characters" )
61
+ }
62
+
192
63
var createTemplate bool
193
64
template , err := client .TemplateByName (inv .Context (), organization .ID , name )
194
65
if err != nil {
@@ -335,21 +206,137 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
335
206
return cmd
336
207
}
337
208
338
- // prettyDirectoryPath returns a prettified path when inside the users
339
- // home directory. Falls back to dir if the users home directory cannot
340
- // discerned. This function calls filepath.Clean on the result.
341
- func prettyDirectoryPath (dir string ) string {
342
- dir = filepath .Clean (dir )
343
- homeDir , err := os .UserHomeDir ()
209
+ type templateUploadFlags struct {
210
+ directory string
211
+ ignoreLockfile bool
212
+ message string
213
+ }
214
+
215
+ func (pf * templateUploadFlags ) options () []clibase.Option {
216
+ return []clibase.Option {{
217
+ Flag : "directory" ,
218
+ FlagShorthand : "d" ,
219
+ Description : "Specify the directory to create from, use '-' to read tar from stdin." ,
220
+ Default : "." ,
221
+ Value : clibase .StringOf (& pf .directory ),
222
+ }, {
223
+ Flag : "ignore-lockfile" ,
224
+ Description : "Ignore warnings about not having a .terraform.lock.hcl file present in the template." ,
225
+ Default : "false" ,
226
+ Value : clibase .BoolOf (& pf .ignoreLockfile ),
227
+ }, {
228
+ Flag : "message" ,
229
+ FlagShorthand : "m" ,
230
+ Description : "Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as truncated." ,
231
+ Value : clibase .StringOf (& pf .message ),
232
+ }}
233
+ }
234
+
235
+ func (pf * templateUploadFlags ) setWorkdir (wd string ) {
236
+ if wd == "" {
237
+ return
238
+ }
239
+ if pf .directory == "" || pf .directory == "." {
240
+ pf .directory = wd
241
+ } else if ! filepath .IsAbs (pf .directory ) {
242
+ pf .directory = filepath .Join (wd , pf .directory )
243
+ }
244
+ }
245
+
246
+ func (pf * templateUploadFlags ) stdin () bool {
247
+ return pf .directory == "-"
248
+ }
249
+
250
+ func (pf * templateUploadFlags ) upload (inv * clibase.Invocation , client * codersdk.Client ) (* codersdk.UploadResponse , error ) {
251
+ var content io.Reader
252
+ if pf .stdin () {
253
+ content = inv .Stdin
254
+ } else {
255
+ prettyDir := prettyDirectoryPath (pf .directory )
256
+ _ , err := cliui .Prompt (inv , cliui.PromptOptions {
257
+ Text : fmt .Sprintf ("Upload %q?" , prettyDir ),
258
+ IsConfirm : true ,
259
+ Default : cliui .ConfirmYes ,
260
+ })
261
+ if err != nil {
262
+ return nil , err
263
+ }
264
+
265
+ pipeReader , pipeWriter := io .Pipe ()
266
+ go func () {
267
+ err := provisionersdk .Tar (pipeWriter , inv .Logger , pf .directory , provisionersdk .TemplateArchiveLimit )
268
+ _ = pipeWriter .CloseWithError (err )
269
+ }()
270
+ defer pipeReader .Close ()
271
+ content = pipeReader
272
+ }
273
+
274
+ spin := spinner .New (spinner .CharSets [5 ], 100 * time .Millisecond )
275
+ spin .Writer = inv .Stdout
276
+ spin .Suffix = pretty .Sprint (cliui .DefaultStyles .Keyword , " Uploading directory..." )
277
+ spin .Start ()
278
+ defer spin .Stop ()
279
+
280
+ resp , err := client .Upload (inv .Context (), codersdk .ContentTypeTar , bufio .NewReader (content ))
344
281
if err != nil {
345
- return dir
282
+ return nil , xerrors . Errorf ( "upload: %w" , err )
346
283
}
347
- prettyDir := dir
348
- if strings .HasPrefix (prettyDir , homeDir ) {
349
- prettyDir = strings .TrimPrefix (prettyDir , homeDir )
350
- prettyDir = "~" + prettyDir
284
+ return & resp , nil
285
+ }
286
+
287
+ func (pf * templateUploadFlags ) checkForLockfile (inv * clibase.Invocation ) error {
288
+ if pf .stdin () || pf .ignoreLockfile {
289
+ // Just assume there's a lockfile if reading from stdin.
290
+ return nil
351
291
}
352
- return prettyDir
292
+
293
+ hasLockfile , err := provisionersdk .DirHasLockfile (pf .directory )
294
+ if err != nil {
295
+ return xerrors .Errorf ("dir has lockfile: %w" , err )
296
+ }
297
+
298
+ if ! hasLockfile {
299
+ cliui .Warn (inv .Stdout , "No .terraform.lock.hcl file found" ,
300
+ "When provisioning, Coder will be unable to cache providers without a lockfile and must download them from the internet each time." ,
301
+ "Create one by running " + pretty .Sprint (cliui .DefaultStyles .Code , "terraform init" )+ " in your template directory." ,
302
+ )
303
+ }
304
+ return nil
305
+ }
306
+
307
+ func (pf * templateUploadFlags ) templateMessage (inv * clibase.Invocation ) string {
308
+ title := strings .SplitN (pf .message , "\n " , 2 )[0 ]
309
+ if len (title ) > 72 {
310
+ cliui .Warn (inv .Stdout , "Template message is longer than 72 characters, it will be displayed as truncated." )
311
+ }
312
+ if title != pf .message {
313
+ cliui .Warn (inv .Stdout , "Template message contains newlines, only the first line will be displayed." )
314
+ }
315
+ if pf .message != "" {
316
+ return pf .message
317
+ }
318
+ return "Uploaded from the CLI"
319
+ }
320
+
321
+ func (pf * templateUploadFlags ) templateName (args []string ) (string , error ) {
322
+ if pf .stdin () {
323
+ // Can't infer name from directory if none provided.
324
+ if len (args ) == 0 {
325
+ return "" , xerrors .New ("template name argument must be provided" )
326
+ }
327
+ return args [0 ], nil
328
+ }
329
+
330
+ if len (args ) > 0 {
331
+ return args [0 ], nil
332
+ }
333
+ // Have to take absPath to resolve "." and "..".
334
+ absPath , err := filepath .Abs (pf .directory )
335
+ if err != nil {
336
+ return "" , err
337
+ }
338
+ // If no name is provided, use the directory name.
339
+ return filepath .Base (absPath ), nil
353
340
}
354
341
355
342
type createValidTemplateVersionArgs struct {
@@ -455,3 +442,20 @@ func ParseProvisionerTags(rawTags []string) (map[string]string, error) {
455
442
}
456
443
return tags , nil
457
444
}
445
+
446
+ // prettyDirectoryPath returns a prettified path when inside the users
447
+ // home directory. Falls back to dir if the users home directory cannot
448
+ // discerned. This function calls filepath.Clean on the result.
449
+ func prettyDirectoryPath (dir string ) string {
450
+ dir = filepath .Clean (dir )
451
+ homeDir , err := os .UserHomeDir ()
452
+ if err != nil {
453
+ return dir
454
+ }
455
+ prettyDir := dir
456
+ if strings .HasPrefix (prettyDir , homeDir ) {
457
+ prettyDir = strings .TrimPrefix (prettyDir , homeDir )
458
+ prettyDir = "~" + prettyDir
459
+ }
460
+ return prettyDir
461
+ }
0 commit comments