1package commands
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "strconv"
8 "strings"
9
10 "github.com/spf13/cobra"
11
12 "github.com/MichaelMure/git-bug/bridge"
13 "github.com/MichaelMure/git-bug/bridge/core"
14 "github.com/MichaelMure/git-bug/bridge/core/auth"
15 "github.com/MichaelMure/git-bug/repository"
16)
17
18type bridgeConfigureOptions struct {
19 name string
20 target string
21 params core.BridgeParams
22 token string
23 tokenStdin bool
24}
25
26func newBridgeConfigureCommand() *cobra.Command {
27 env := newEnv()
28 options := bridgeConfigureOptions{}
29
30 cmd := &cobra.Command{
31 Use: "configure",
32 Short: "Configure a new bridge.",
33 Long: ` Configure a new bridge by passing flags or/and using interactive terminal prompts. You can avoid all the terminal prompts by passing all the necessary flags to configure your bridge.`,
34 Example: bridgeConfigureExample,
35 PreRunE: loadBackend(env),
36 PostRunE: closeBackend(env),
37 RunE: func(cmd *cobra.Command, args []string) error {
38 return runBridgeConfigure(env, options)
39 },
40 }
41
42 flags := cmd.Flags()
43 flags.SortFlags = false
44
45 flags.StringVarP(&options.name, "name", "n", "", "A distinctive name to identify the bridge")
46 flags.StringVarP(&options.target, "target", "t", "",
47 fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ",")))
48 flags.StringVarP(&options.params.URL, "url", "u", "", "The URL of the remote repository")
49 flags.StringVarP(&options.params.BaseURL, "base-url", "b", "", "The base URL of your remote issue tracker")
50 flags.StringVarP(&options.params.Login, "login", "l", "", "The login on your remote issue tracker")
51 flags.StringVarP(&options.params.CredPrefix, "credential", "c", "", "The identifier or prefix of an already known credential for your remote issue tracker (see \"git-bug bridge auth\")")
52 flags.StringVar(&options.token, "token", "", "A raw authentication token for the remote issue tracker")
53 flags.BoolVar(&options.tokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token")
54 flags.StringVarP(&options.params.Owner, "owner", "o", "", "The owner of the remote repository")
55 flags.StringVarP(&options.params.Project, "project", "p", "", "The name of the remote repository")
56
57 return cmd
58}
59
60func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error {
61 var err error
62
63 if (opts.tokenStdin || opts.token != "" || opts.params.CredPrefix != "") &&
64 (opts.name == "" || opts.target == "") {
65 return fmt.Errorf("you must provide a bridge name and target to configure a bridge with a credential")
66 }
67
68 // early fail
69 if opts.params.CredPrefix != "" {
70 if _, err := auth.LoadWithPrefix(env.repo, opts.params.CredPrefix); err != nil {
71 return err
72 }
73 }
74
75 switch {
76 case opts.tokenStdin:
77 reader := bufio.NewReader(os.Stdin)
78 token, err := reader.ReadString('\n')
79 if err != nil {
80 return fmt.Errorf("reading from stdin: %v", err)
81 }
82 opts.params.TokenRaw = strings.TrimSpace(token)
83 case opts.token != "":
84 opts.params.TokenRaw = opts.token
85 }
86
87 if opts.target == "" {
88 opts.target, err = promptTarget()
89 if err != nil {
90 return err
91 }
92 }
93
94 if opts.name == "" {
95 opts.name, err = promptName(env.repo)
96 if err != nil {
97 return err
98 }
99 }
100
101 b, err := bridge.NewBridge(env.backend, opts.target, opts.name)
102 if err != nil {
103 return err
104 }
105
106 err = b.Configure(opts.params)
107 if err != nil {
108 return err
109 }
110
111 env.out.Printf("Successfully configured bridge: %s\n", opts.name)
112 return nil
113}
114
115func promptTarget() (string, error) {
116 // TODO: use the reusable prompt from the input package
117 targets := bridge.Targets()
118
119 for {
120 for i, target := range targets {
121 fmt.Printf("[%d]: %s\n", i+1, target)
122 }
123 fmt.Printf("target: ")
124
125 line, err := bufio.NewReader(os.Stdin).ReadString('\n')
126
127 if err != nil {
128 return "", err
129 }
130
131 line = strings.TrimSpace(line)
132
133 index, err := strconv.Atoi(line)
134 if err != nil || index <= 0 || index > len(targets) {
135 fmt.Println("invalid input")
136 continue
137 }
138
139 return targets[index-1], nil
140 }
141}
142
143func promptName(repo repository.RepoConfig) (string, error) {
144 // TODO: use the reusable prompt from the input package
145 const defaultName = "default"
146
147 defaultExist := core.BridgeExist(repo, defaultName)
148
149 for {
150 if defaultExist {
151 fmt.Printf("name: ")
152 } else {
153 fmt.Printf("name [%s]: ", defaultName)
154 }
155
156 line, err := bufio.NewReader(os.Stdin).ReadString('\n')
157 if err != nil {
158 return "", err
159 }
160
161 line = strings.TrimSpace(line)
162
163 name := line
164 if defaultExist && name == "" {
165 continue
166 }
167
168 if name == "" {
169 name = defaultName
170 }
171
172 if !core.BridgeExist(repo, name) {
173 return name, nil
174 }
175
176 fmt.Println("a bridge with the same name already exist")
177 }
178}