Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit a171882

Browse files
authored
Error coder sh commands if the env requires a rebuild (#163)
1 parent 03d2d8b commit a171882

File tree

12 files changed

+82
-70
lines changed

12 files changed

+82
-70
lines changed

coder-sdk/env.go

-4
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@ type RebuildMessage struct {
3939
Text string `json:"text"`
4040
Required bool `json:"required"`
4141
AutoOffThreshold xjson.MSDuration `json:"auto_off_threshold" tab:"-"`
42-
RebuildMessages []struct {
43-
Text string `json:"text"`
44-
Required bool `json:"required"`
45-
} `json:"rebuild_messages" tab:"-"`
4642
}
4743

4844
// EnvironmentStat represents the state of an environment

docs/coder_sh.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ coder sh [environment_name] [<command [args...]>] [flags]
1414

1515
```
1616
coder sh backend-env
17+
coder sh front-end-dev cat ~/config.json
1718
```
1819

1920
### Options

internal/cmd/cmd.go

+34-32
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,24 @@ func Make() *cobra.Command {
2121
}
2222

2323
app.AddCommand(
24-
makeLoginCmd(),
25-
makeLogoutCmd(),
26-
makeShellCmd(),
27-
makeUsersCmd(),
28-
makeConfigSSHCmd(),
29-
makeSecretsCmd(),
30-
envsCommand(),
31-
makeSyncCmd(),
32-
makeURLCmd(),
24+
loginCmd(),
25+
logoutCmd(),
26+
shCmd(),
27+
usersCmd(),
28+
configSSHCmd(),
29+
secretsCmd(),
30+
envsCmd(),
31+
syncCmd(),
32+
urlCmd(),
3333
resourceCmd(),
34-
completionCmd,
35-
genDocs(app),
34+
completionCmd(),
35+
genDocsCmd(app),
3636
)
3737
app.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show verbose output")
3838
return app
3939
}
4040

41-
func genDocs(rootCmd *cobra.Command) *cobra.Command {
41+
func genDocsCmd(rootCmd *cobra.Command) *cobra.Command {
4242
return &cobra.Command{
4343
Use: "gen-docs [dir_path]",
4444
Short: "Generate a markdown documentation tree for the root command.",
@@ -52,17 +52,18 @@ func genDocs(rootCmd *cobra.Command) *cobra.Command {
5252
}
5353

5454
// reference: https://github.com/spf13/cobra/blob/master/shell_completions.md
55-
var completionCmd = &cobra.Command{
56-
Use: "completion [bash|zsh|fish|powershell]",
57-
Short: "Generate completion script",
58-
Example: `coder completion fish > ~/.config/fish/completions/coder.fish
55+
func completionCmd() *cobra.Command {
56+
return &cobra.Command{
57+
Use: "completion [bash|zsh|fish|powershell]",
58+
Short: "Generate completion script",
59+
Example: `coder completion fish > ~/.config/fish/completions/coder.fish
5960
coder completion zsh > "${fpath[1]}/_coder"
6061
6162
Linux:
6263
$ coder completion bash > /etc/bash_completion.d/coder
6364
MacOS:
6465
$ coder completion bash > /usr/local/etc/bash_completion.d/coder`,
65-
Long: `To load completions:
66+
Long: `To load completions:
6667
6768
Bash:
6869
@@ -93,19 +94,20 @@ $ coder completion fish | source
9394
To load completions for each session, execute once:
9495
$ coder completion fish > ~/.config/fish/completions/coder.fish
9596
`,
96-
DisableFlagsInUseLine: true,
97-
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
98-
Args: cobra.ExactValidArgs(1),
99-
Run: func(cmd *cobra.Command, args []string) {
100-
switch args[0] {
101-
case "bash":
102-
_ = cmd.Root().GenBashCompletion(os.Stdout) // Best effort.
103-
case "zsh":
104-
_ = cmd.Root().GenZshCompletion(os.Stdout) // Best effort.
105-
case "fish":
106-
_ = cmd.Root().GenFishCompletion(os.Stdout, true) // Best effort.
107-
case "powershell":
108-
_ = cmd.Root().GenPowerShellCompletion(os.Stdout) // Best effort.
109-
}
110-
},
97+
DisableFlagsInUseLine: true,
98+
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
99+
Args: cobra.ExactValidArgs(1),
100+
Run: func(cmd *cobra.Command, args []string) {
101+
switch args[0] {
102+
case "bash":
103+
_ = cmd.Root().GenBashCompletion(os.Stdout) // Best effort.
104+
case "zsh":
105+
_ = cmd.Root().GenZshCompletion(os.Stdout) // Best effort.
106+
case "fish":
107+
_ = cmd.Root().GenFishCompletion(os.Stdout, true) // Best effort.
108+
case "powershell":
109+
_ = cmd.Root().GenPowerShellCompletion(os.Stdout) // Best effort.
110+
}
111+
},
112+
}
111113
}

internal/cmd/configssh.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"golang.org/x/xerrors"
1919
)
2020

21-
func makeConfigSSHCmd() *cobra.Command {
21+
func configSSHCmd() *cobra.Command {
2222
var (
2323
configpath string
2424
remove = false

internal/cmd/envs.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717

1818
const defaultImgTag = "latest"
1919

20-
func envsCommand() *cobra.Command {
20+
func envsCmd() *cobra.Command {
2121
var user string
2222
cmd := &cobra.Command{
2323
Use: "envs",
@@ -28,12 +28,12 @@ func envsCommand() *cobra.Command {
2828

2929
cmd.AddCommand(
3030
lsEnvsCommand(&user),
31-
stopEnvsCommand(&user),
32-
rmEnvsCommand(&user),
31+
stopEnvsCmd(&user),
32+
rmEnvsCmd(&user),
3333
watchBuildLogCommand(&user),
3434
rebuildEnvCommand(&user),
35-
createEnvCommand(&user),
36-
editEnvCommand(&user),
35+
createEnvCmd(&user),
36+
editEnvCmd(&user),
3737
)
3838
return cmd
3939
}
@@ -84,7 +84,7 @@ func lsEnvsCommand(user *string) *cobra.Command {
8484
return cmd
8585
}
8686

87-
func stopEnvsCommand(user *string) *cobra.Command {
87+
func stopEnvsCmd(user *string) *cobra.Command {
8888
return &cobra.Command{
8989
Use: "stop [...environment_names]",
9090
Short: "stop Coder environments by name",
@@ -131,7 +131,7 @@ coder envs --user charlie@coder.com ls -o json \
131131
}
132132
}
133133

134-
func createEnvCommand(user *string) *cobra.Command {
134+
func createEnvCmd(user *string) *cobra.Command {
135135
var (
136136
org string
137137
img string
@@ -239,7 +239,7 @@ coder envs create --cpu 4 --disk 100 --memory 8 --image 5f443b16-30652892427b955
239239
return cmd
240240
}
241241

242-
func editEnvCommand(user *string) *cobra.Command {
242+
func editEnvCmd(user *string) *cobra.Command {
243243
var (
244244
org string
245245
img string
@@ -336,7 +336,7 @@ coder envs edit back-end-env --disk 20`,
336336
return cmd
337337
}
338338

339-
func rmEnvsCommand(user *string) *cobra.Command {
339+
func rmEnvsCmd(user *string) *cobra.Command {
340340
var force bool
341341
cmd := &cobra.Command{
342342
Use: "rm [...environment_names]",

internal/cmd/login.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"golang.org/x/xerrors"
1919
)
2020

21-
func makeLoginCmd() *cobra.Command {
21+
func loginCmd() *cobra.Command {
2222
return &cobra.Command{
2323
Use: "login [Coder Enterprise URL eg. https://my.coder.domain/]",
2424
Short: "Authenticate this client for future operations",

internal/cmd/logout.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"golang.org/x/xerrors"
1010
)
1111

12-
func makeLogoutCmd() *cobra.Command {
12+
func logoutCmd() *cobra.Command {
1313
return &cobra.Command{
1414
Use: "logout",
1515
Short: "Remove local authentication credentials if any exist",

internal/cmd/secrets.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"cdr.dev/coder-cli/internal/x/xtabwriter"
1515
)
1616

17-
func makeSecretsCmd() *cobra.Command {
17+
func secretsCmd() *cobra.Command {
1818
var user string
1919
cmd := &cobra.Command{
2020
Use: "secrets",
@@ -26,28 +26,28 @@ func makeSecretsCmd() *cobra.Command {
2626
&cobra.Command{
2727
Use: "ls",
2828
Short: "List all secrets owned by the active user",
29-
RunE: listSecrets(&user),
29+
RunE: listSecretsCmd(&user),
3030
},
31-
makeCreateSecret(&user),
31+
createSecretCmd(&user),
3232
&cobra.Command{
3333
Use: "rm [...secret_name]",
3434
Short: "Remove one or more secrets by name",
3535
Args: cobra.MinimumNArgs(1),
36-
RunE: makeRemoveSecrets(&user),
36+
RunE: removeSecretsCmd(&user),
3737
Example: "coder secrets rm mysql-password mysql-user",
3838
},
3939
&cobra.Command{
4040
Use: "view [secret_name]",
4141
Short: "View a secret by name",
4242
Args: cobra.ExactArgs(1),
43-
RunE: makeViewSecret(&user),
43+
RunE: viewSecretCmd(&user),
4444
Example: "coder secrets view mysql-password",
4545
},
4646
)
4747
return cmd
4848
}
4949

50-
func makeCreateSecret(userEmail *string) *cobra.Command {
50+
func createSecretCmd(userEmail *string) *cobra.Command {
5151
var (
5252
fromFile string
5353
fromLiteral string
@@ -136,7 +136,7 @@ coder secrets create aws-credentials --from-file ./credentials.json`,
136136
return cmd
137137
}
138138

139-
func listSecrets(userEmail *string) func(cmd *cobra.Command, _ []string) error {
139+
func listSecretsCmd(userEmail *string) func(cmd *cobra.Command, _ []string) error {
140140
return func(cmd *cobra.Command, _ []string) error {
141141
client, err := newClient()
142142
if err != nil {
@@ -169,7 +169,7 @@ func listSecrets(userEmail *string) func(cmd *cobra.Command, _ []string) error {
169169
}
170170
}
171171

172-
func makeViewSecret(userEmail *string) func(cmd *cobra.Command, args []string) error {
172+
func viewSecretCmd(userEmail *string) func(cmd *cobra.Command, args []string) error {
173173
return func(cmd *cobra.Command, args []string) error {
174174
var (
175175
name = args[0]
@@ -196,7 +196,7 @@ func makeViewSecret(userEmail *string) func(cmd *cobra.Command, args []string) e
196196
}
197197
}
198198

199-
func makeRemoveSecrets(userEmail *string) func(c *cobra.Command, args []string) error {
199+
func removeSecretsCmd(userEmail *string) func(c *cobra.Command, args []string) error {
200200
return func(cmd *cobra.Command, args []string) error {
201201
client, err := newClient()
202202
if err != nil {

internal/cmd/shell.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import (
2323

2424
func getEnvsForCompletion(user string) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
2525
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
26+
ctx := cmd.Context()
2627
client, err := newClient()
2728
if err != nil {
2829
return nil, cobra.ShellCompDirectiveDefault
2930
}
30-
envs, err := getEnvs(context.TODO(), client, user)
31+
envs, err := getEnvs(ctx, client, user)
3132
if err != nil {
3233
return nil, cobra.ShellCompDirectiveDefault
3334
}
@@ -40,7 +41,7 @@ func getEnvsForCompletion(user string) func(cmd *cobra.Command, args []string, t
4041
}
4142
}
4243

43-
func makeShellCmd() *cobra.Command {
44+
func shCmd() *cobra.Command {
4445
return &cobra.Command{
4546
Use: "sh [environment_name] [<command [args...]>]",
4647
Short: "Open a shell and execute commands in a Coder environment",
@@ -49,13 +50,13 @@ func makeShellCmd() *cobra.Command {
4950
DisableFlagParsing: true,
5051
ValidArgsFunction: getEnvsForCompletion(coder.Me),
5152
RunE: shell,
52-
Example: "coder sh backend-env",
53+
Example: `coder sh backend-env
54+
coder sh front-end-dev cat ~/config.json`,
5355
}
5456
}
5557

56-
func shell(_ *cobra.Command, cmdArgs []string) error {
57-
ctx := context.Background()
58-
58+
func shell(cmd *cobra.Command, cmdArgs []string) error {
59+
ctx := cmd.Context()
5960
command := "sh"
6061
args := []string{"-c"}
6162
if len(cmdArgs) > 1 {
@@ -106,6 +107,18 @@ func runCommand(ctx context.Context, envName, command string, args []string) err
106107
return xerrors.Errorf("find environment: %w", err)
107108
}
108109

110+
// check if a rebuild is required before attempting to open a shell
111+
for _, r := range env.RebuildMessages {
112+
// use the first rebuild message that is required
113+
if r.Required {
114+
return clog.Error(
115+
fmt.Sprintf(`environment "%s" requires a rebuild`, env.Name),
116+
clog.Causef(r.Text), clog.BlankLine,
117+
clog.Tipf(`run "coder envs rebuild %s" to rebuild`, env.Name),
118+
)
119+
}
120+
}
121+
109122
termFD := os.Stdout.Fd()
110123

111124
isInteractive := terminal.IsTerminal(int(termFD))

internal/cmd/sync.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"golang.org/x/xerrors"
1616
)
1717

18-
func makeSyncCmd() *cobra.Command {
18+
func syncCmd() *cobra.Command {
1919
var init bool
2020
cmd := &cobra.Command{
2121
Use: "sync [local directory] [<env name>:<remote directory>]",

internal/cmd/urls.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"cdr.dev/coder-cli/internal/x/xtabwriter"
1919
)
2020

21-
func makeURLCmd() *cobra.Command {
21+
func urlCmd() *cobra.Command {
2222
var outputFmt string
2323
cmd := &cobra.Command{
2424
Use: "urls",
@@ -29,7 +29,7 @@ func makeURLCmd() *cobra.Command {
2929
Short: "List all DevURLs for an environment",
3030
Args: cobra.ExactArgs(1),
3131
ValidArgsFunction: getEnvsForCompletion(coder.Me),
32-
RunE: makeListDevURLs(&outputFmt),
32+
RunE: listDevURLsCmd(&outputFmt),
3333
}
3434
lsCmd.Flags().StringVarP(&outputFmt, "output", "o", "human", "human|json")
3535

@@ -43,7 +43,7 @@ func makeURLCmd() *cobra.Command {
4343
cmd.AddCommand(
4444
lsCmd,
4545
rmCmd,
46-
makeCreateDevURL(),
46+
createDevURLCmd(),
4747
)
4848

4949
return cmd
@@ -89,7 +89,7 @@ func accessLevelIsValid(level string) bool {
8989

9090
// Run gets the list of active devURLs from the cemanager for the
9191
// specified environment and outputs info to stdout.
92-
func makeListDevURLs(outputFmt *string) func(cmd *cobra.Command, args []string) error {
92+
func listDevURLsCmd(outputFmt *string) func(cmd *cobra.Command, args []string) error {
9393
return func(cmd *cobra.Command, args []string) error {
9494
envName := args[0]
9595
devURLs, err := urlList(cmd.Context(), envName)
@@ -120,7 +120,7 @@ func makeListDevURLs(outputFmt *string) func(cmd *cobra.Command, args []string)
120120
}
121121
}
122122

123-
func makeCreateDevURL() *cobra.Command {
123+
func createDevURLCmd() *cobra.Command {
124124
var (
125125
access string
126126
urlname string

internal/cmd/users.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"cdr.dev/coder-cli/internal/x/xtabwriter"
1111
)
1212

13-
func makeUsersCmd() *cobra.Command {
13+
func usersCmd() *cobra.Command {
1414
cmd := &cobra.Command{
1515
Use: "users",
1616
Short: "Interact with Coder user accounts",

0 commit comments

Comments
 (0)