Skip to content

Commit aca7cac

Browse files
authored
Merge pull request #5 from stuartleeks/sl/cmd-restructure
Restructure command code
2 parents 86b8208 + e3e40b3 commit aca7cac

File tree

3 files changed

+190
-155
lines changed

3 files changed

+190
-155
lines changed

cmd/devcontainer/completion.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func createCompleteCommand(rootCmd *cobra.Command) *cobra.Command {
12+
cmd := &cobra.Command{
13+
Use: "completion SHELL",
14+
Short: "Generates bash completion scripts",
15+
Long: `To load completion run
16+
17+
. <(devcontainer completion SHELL)
18+
19+
Valid values for SHELL are : bash, fish, powershell, zsh
20+
21+
For example, to configure your bash shell to load completions for each session add to your bashrc
22+
23+
# ~/.bashrc or ~/.profile
24+
. <(devcontainer completion)
25+
`,
26+
Run: func(cmd *cobra.Command, args []string) {
27+
if len(args) != 1 {
28+
cmd.Usage()
29+
os.Exit(1)
30+
}
31+
shell := args[0]
32+
switch strings.ToLower(shell) {
33+
case "bash":
34+
rootCmd.GenBashCompletion(os.Stdout)
35+
case "fish":
36+
rootCmd.GenFishCompletion(os.Stdout, true)
37+
case "powershell":
38+
rootCmd.GenPowerShellCompletion(os.Stdout)
39+
case "zsh":
40+
rootCmd.GenPowerShellCompletion(os.Stdout)
41+
default:
42+
fmt.Printf("Unsupported SHELL value: '%s'\n", shell)
43+
cmd.Usage()
44+
os.Exit(1)
45+
}
46+
},
47+
}
48+
return cmd
49+
}

cmd/devcontainer/devcontainer.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"sort"
8+
"text/tabwriter"
9+
10+
"github.com/spf13/cobra"
11+
"github.com/stuartleeks/devcontainer-cli/internal/pkg/devcontainers"
12+
)
13+
14+
func createListCommand() *cobra.Command {
15+
var listIncludeContainerNames bool
16+
var listVerbose bool
17+
cmdList := &cobra.Command{
18+
Use: "list",
19+
Short: "List devcontainers",
20+
Long: "Lists running devcontainers",
21+
Run: func(cmd *cobra.Command, args []string) {
22+
if listIncludeContainerNames && listVerbose {
23+
fmt.Println("Can't use both verbose and include-container-names")
24+
os.Exit(1)
25+
}
26+
devcontainers, err := devcontainers.ListDevcontainers()
27+
if err != nil {
28+
fmt.Printf("Error: %v", err)
29+
os.Exit(1)
30+
}
31+
if listVerbose {
32+
sort.Slice(devcontainers, func(i, j int) bool { return devcontainers[i].DevcontainerName < devcontainers[j].DevcontainerName })
33+
34+
w := new(tabwriter.Writer)
35+
// minwidth, tabwidth, padding, padchar, flags
36+
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
37+
defer w.Flush()
38+
39+
fmt.Fprintf(w, "%s\t%s\n", "DEVCONTAINER NAME", "CONTAINER NAME")
40+
fmt.Fprintf(w, "%s\t%s\n", "-----------------", "--------------")
41+
42+
for _, devcontainer := range devcontainers {
43+
fmt.Fprintf(w, "%s\t%s\n", devcontainer.DevcontainerName, devcontainer.ContainerName)
44+
}
45+
return
46+
}
47+
names := []string{}
48+
for _, devcontainer := range devcontainers {
49+
names = append(names, devcontainer.DevcontainerName)
50+
if listIncludeContainerNames {
51+
names = append(names, devcontainer.ContainerName)
52+
}
53+
}
54+
sort.Strings(names)
55+
for _, name := range names {
56+
fmt.Println(name)
57+
}
58+
},
59+
}
60+
cmdList.Flags().BoolVar(&listIncludeContainerNames, "include-container-names", false, "Also include container names in the list")
61+
cmdList.Flags().BoolVarP(&listVerbose, "verbose", "v", false, "Verbose output")
62+
return cmdList
63+
}
64+
65+
func createExecCommand() *cobra.Command {
66+
cmd := &cobra.Command{
67+
Use: "exec DEVCONTAINER_NAME COMMAND [args...]",
68+
Short: "Execute a command in a devcontainer",
69+
Long: "Execute a command in a devcontainer, similar to `docker exec`.",
70+
Run: func(cmd *cobra.Command, args []string) {
71+
72+
if len(args) < 2 {
73+
cmd.Usage()
74+
os.Exit(1)
75+
}
76+
77+
devcontainerName := args[0]
78+
devcontainers, err := devcontainers.ListDevcontainers()
79+
if err != nil {
80+
fmt.Printf("Error: %v", err)
81+
os.Exit(1)
82+
}
83+
84+
containerID := ""
85+
for _, devcontainer := range devcontainers {
86+
if devcontainer.ContainerName == devcontainerName || devcontainer.DevcontainerName == devcontainerName {
87+
containerID = devcontainer.ContainerID
88+
break
89+
}
90+
}
91+
if containerID == "" {
92+
cmd.Usage()
93+
if err != nil {
94+
fmt.Printf("Error: %v", err)
95+
}
96+
os.Exit(1)
97+
}
98+
99+
dockerArgs := []string{"exec", "-it", containerID}
100+
dockerArgs = append(dockerArgs, args[1:]...)
101+
102+
dockerCmd := exec.Command("docker", dockerArgs...)
103+
dockerCmd.Stdin = os.Stdin
104+
dockerCmd.Stdout = os.Stdout
105+
106+
err = dockerCmd.Start()
107+
if err != nil {
108+
fmt.Printf("Exec: start error: %s\n", err)
109+
}
110+
err = dockerCmd.Wait()
111+
if err != nil {
112+
fmt.Printf("Exec: wait error: %s\n", err)
113+
}
114+
},
115+
Args: cobra.ArbitraryArgs,
116+
DisableFlagParsing: true,
117+
DisableFlagsInUseLine: true,
118+
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
119+
// only completing the first arg (devcontainer name)
120+
if len(args) != 0 {
121+
return nil, cobra.ShellCompDirectiveNoFileComp
122+
}
123+
devcontainers, err := devcontainers.ListDevcontainers()
124+
if err != nil {
125+
fmt.Printf("Error: %v", err)
126+
os.Exit(1)
127+
}
128+
names := []string{}
129+
for _, devcontainer := range devcontainers {
130+
names = append(names, devcontainer.DevcontainerName)
131+
names = append(names, devcontainer.ContainerName)
132+
}
133+
sort.Strings(names)
134+
return names, cobra.ShellCompDirectiveNoFileComp
135+
},
136+
}
137+
return cmd
138+
}

cmd/devcontainer/main.go

Lines changed: 3 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,168 +1,16 @@
11
package main
22

33
import (
4-
"fmt"
5-
"os"
6-
"os/exec"
7-
"sort"
8-
"text/tabwriter"
9-
104
"github.com/spf13/cobra"
11-
"github.com/stuartleeks/devcontainer-cli/internal/pkg/devcontainers"
125
)
136

147
func main() {
15-
var listIncludeContainerNames bool
16-
var listVerbose bool
178

189
rootCmd := &cobra.Command{Use: "devcontainer"}
1910

20-
cmdList := &cobra.Command{
21-
Use: "list",
22-
Short: "List devcontainers",
23-
Long: "Lists devcontainers that are currently running",
24-
Run: func(cmd *cobra.Command, args []string) {
25-
runListCommand(cmd, args, listIncludeContainerNames, listVerbose)
26-
},
27-
}
28-
cmdList.Flags().BoolVar(&listIncludeContainerNames, "include-container-names", false, "Also include container names in the list")
29-
cmdList.Flags().BoolVarP(&listVerbose, "verbose", "v", false, "Verbose output")
30-
rootCmd.AddCommand(cmdList)
31-
32-
cmdExec := &cobra.Command{
33-
Use: "exec DEVCONTAINER_NAME COMMAND [args...]",
34-
Short: "Execute a command in a devcontainer",
35-
Long: "Execute a command in a devcontainer, similar to `docker exec`.",
36-
Run: func(cmd *cobra.Command, args []string) {
37-
runExecCommand(cmd, args)
38-
},
39-
Args: cobra.ArbitraryArgs,
40-
DisableFlagParsing: true,
41-
DisableFlagsInUseLine: true,
42-
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
43-
// only completing the first arg (devcontainer name)
44-
if len(args) != 0 {
45-
return nil, cobra.ShellCompDirectiveNoFileComp
46-
}
47-
devcontainers, err := devcontainers.ListDevcontainers()
48-
if err != nil {
49-
fmt.Printf("Error: %v", err)
50-
os.Exit(1)
51-
}
52-
names := []string{}
53-
for _, devcontainer := range devcontainers {
54-
names = append(names, devcontainer.DevcontainerName)
55-
names = append(names, devcontainer.ContainerName)
56-
}
57-
sort.Strings(names)
58-
return names, cobra.ShellCompDirectiveNoFileComp
59-
},
60-
}
61-
rootCmd.AddCommand(cmdExec)
62-
63-
cmdCompletion := &cobra.Command{
64-
Use: "completion",
65-
Short: "Generates bash completion scripts",
66-
Long: `To load completion run
67-
68-
. <(devcontainer completion)
69-
70-
To configure your bash shell to load completions for each session add to your bashrc
71-
72-
# ~/.bashrc or ~/.profile
73-
. <(devcontainer completion)
74-
`,
75-
Run: func(cmd *cobra.Command, args []string) {
76-
rootCmd.GenBashCompletion(os.Stdout)
77-
},
78-
}
79-
rootCmd.AddCommand(cmdCompletion)
11+
rootCmd.AddCommand(createListCommand())
12+
rootCmd.AddCommand(createExecCommand())
13+
rootCmd.AddCommand(createCompleteCommand(rootCmd))
8014

8115
rootCmd.Execute()
8216
}
83-
84-
func runListCommand(cmd *cobra.Command, args []string, listIncludeContainerNames bool, listVerbose bool) {
85-
if listIncludeContainerNames && listVerbose {
86-
fmt.Println("Can't use both verbose and include-container-names")
87-
os.Exit(1)
88-
}
89-
devcontainers, err := devcontainers.ListDevcontainers()
90-
if err != nil {
91-
fmt.Printf("Error: %v", err)
92-
os.Exit(1)
93-
}
94-
if listVerbose {
95-
sort.Slice(devcontainers, func(i, j int) bool { return devcontainers[i].DevcontainerName < devcontainers[j].DevcontainerName })
96-
97-
w := new(tabwriter.Writer)
98-
// minwidth, tabwidth, padding, padchar, flags
99-
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
100-
defer w.Flush()
101-
102-
fmt.Fprintf(w, "%s\t%s\n", "DEVCONTAINER NAME", "CONTAINER NAME")
103-
fmt.Fprintf(w, "%s\t%s\n", "-----------------", "--------------")
104-
105-
for _, devcontainer := range devcontainers {
106-
fmt.Fprintf(w, "%s\t%s\n", devcontainer.DevcontainerName, devcontainer.ContainerName)
107-
}
108-
return
109-
}
110-
names := []string{}
111-
for _, devcontainer := range devcontainers {
112-
names = append(names, devcontainer.DevcontainerName)
113-
if listIncludeContainerNames {
114-
names = append(names, devcontainer.ContainerName)
115-
}
116-
}
117-
sort.Strings(names)
118-
for _, name := range names {
119-
fmt.Println(name)
120-
}
121-
}
122-
123-
func runExecCommand(cmd *cobra.Command, args []string) {
124-
// TODO argument validation!
125-
126-
if len(args) < 2 {
127-
cmd.Usage()
128-
os.Exit(1)
129-
}
130-
131-
devcontainerName := args[0]
132-
devcontainers, err := devcontainers.ListDevcontainers()
133-
if err != nil {
134-
fmt.Printf("Error: %v", err)
135-
os.Exit(1)
136-
}
137-
138-
containerID := ""
139-
for _, devcontainer := range devcontainers {
140-
if devcontainer.ContainerName == devcontainerName || devcontainer.DevcontainerName == devcontainerName {
141-
containerID = devcontainer.ContainerID
142-
break
143-
}
144-
}
145-
if containerID == "" {
146-
cmd.Usage()
147-
if err != nil {
148-
fmt.Printf("Error: %v", err)
149-
}
150-
os.Exit(1)
151-
}
152-
153-
dockerArgs := []string{"exec", "-it", containerID}
154-
dockerArgs = append(dockerArgs, args[1:]...)
155-
156-
dockerCmd := exec.Command("docker", dockerArgs...)
157-
dockerCmd.Stdin = os.Stdin
158-
dockerCmd.Stdout = os.Stdout
159-
160-
err = dockerCmd.Start()
161-
if err != nil {
162-
fmt.Printf("Exec: start error: %s\n", err)
163-
}
164-
err = dockerCmd.Wait()
165-
if err != nil {
166-
fmt.Printf("Exec: wait error: %s\n", err)
167-
}
168-
}

0 commit comments

Comments
 (0)