1package commands
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "reflect"
8 "strconv"
9 "strings"
10
11 "github.com/spf13/cobra"
12
13 "github.com/MichaelMure/git-bug/bridge"
14 "github.com/MichaelMure/git-bug/bridge/core"
15 "github.com/MichaelMure/git-bug/bridge/core/auth"
16 "github.com/MichaelMure/git-bug/repository"
17)
18
19type bridgeConfigureOptions struct {
20 name string
21 target string
22 params core.BridgeParams
23 token string
24 tokenStdin bool
25}
26
27func newBridgeConfigureCommand() *cobra.Command {
28 env := newEnv()
29 options := bridgeConfigureOptions{}
30
31 targetDocs := ""
32 for _, v := range core.TargetTypes() {
33 targetDocs += fmt.Sprintf("# For %s:\ngit bug bridge configure \\\n", strings.Title(strings.Split(v.String(), ".")[0]))
34 b := reflect.New(v).Interface().(core.BridgeImpl)
35 for param := range b.ValidParams() {
36 targetDocs += fmt.Sprintf(" --%s=Placeholder Text \\\n", strings.ToLower(param))
37 }
38 targetDocs += "\n"
39 }
40
41 cmd := &cobra.Command{
42 Use: "configure",
43 Short: "Configure a new bridge.",
44 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.`,
45 Example: fmt.Sprintf(`# Interactive example
46[1]: github
47[2]: gitlab
48[3]: jira
49[4]: launchpad-preview
50
51target: 1
52name [default]: default
53
54Detected projects:
55[1]: github.com/a-hilaly/git-bug
56[2]: github.com/MichaelMure/git-bug
57
58[0]: Another project
59
60Select option: 1
61
62[1]: user provided token
63[2]: interactive token creation
64Select option: 1
65
66You can generate a new token by visiting https://github.com/settings/tokens.
67Choose 'Generate new token' and set the necessary access scope for your repository.
68
69The access scope depend on the type of repository.
70Public:
71 - 'public_repo': to be able to read public repositories
72Private:
73 - 'repo' : to be able to read private repositories
74
75Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700
76Successfully configured bridge: default
77
78%s`, targetDocs),
79 PreRunE: loadBackend(env),
80 PostRunE: closeBackend(env),
81 RunE: func(cmd *cobra.Command, args []string) error {
82 return runBridgeConfigure(env, options)
83 },
84 }
85
86 flags := cmd.Flags()
87 flags.SortFlags = false
88
89 flags.StringVarP(&options.name, "name", "n", "", "A distinctive name to identify the bridge")
90 flags.StringVarP(&options.target, "target", "t", "",
91 fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ",")))
92 flags.StringVarP(&options.params.URL, "url", "u", "", "The URL of the remote repository")
93 flags.StringVarP(&options.params.BaseURL, "base-url", "b", "", "The base URL of your remote issue tracker")
94 flags.StringVarP(&options.params.Login, "login", "l", "", "The login on your remote issue tracker")
95 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\")")
96 flags.StringVar(&options.token, "token", "", "A raw authentication token for the remote issue tracker")
97 flags.BoolVar(&options.tokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token")
98 flags.StringVarP(&options.params.Owner, "owner", "o", "", "The owner of the remote repository")
99 flags.StringVarP(&options.params.Project, "project", "p", "", "The name of the remote repository")
100
101 return cmd
102}
103
104func runBridgeConfigure(env *Env, opts bridgeConfigureOptions) error {
105 var err error
106
107 if (opts.tokenStdin || opts.token != "" || opts.params.CredPrefix != "") &&
108 (opts.name == "" || opts.target == "") {
109 return fmt.Errorf("you must provide a bridge name and target to configure a bridge with a credential")
110 }
111
112 // early fail
113 if opts.params.CredPrefix != "" {
114 if _, err := auth.LoadWithPrefix(env.repo, opts.params.CredPrefix); err != nil {
115 return err
116 }
117 }
118
119 switch {
120 case opts.tokenStdin:
121 reader := bufio.NewReader(os.Stdin)
122 token, err := reader.ReadString('\n')
123 if err != nil {
124 return fmt.Errorf("reading from stdin: %v", err)
125 }
126 opts.params.TokenRaw = strings.TrimSpace(token)
127 case opts.token != "":
128 opts.params.TokenRaw = opts.token
129 }
130
131 if opts.target == "" {
132 opts.target, err = promptTarget()
133 if err != nil {
134 return err
135 }
136 }
137
138 if opts.name == "" {
139 opts.name, err = promptName(env.repo)
140 if err != nil {
141 return err
142 }
143 }
144
145 b, err := bridge.NewBridge(env.backend, opts.target, opts.name)
146 if err != nil {
147 return err
148 }
149
150 err = b.Configure(opts.params)
151 if err != nil {
152 return err
153 }
154
155 env.out.Printf("Successfully configured bridge: %s\n", opts.name)
156 return nil
157}
158
159func promptTarget() (string, error) {
160 // TODO: use the reusable prompt from the input package
161 targets := bridge.Targets()
162
163 for {
164 for i, target := range targets {
165 fmt.Printf("[%d]: %s\n", i+1, target)
166 }
167 fmt.Printf("target: ")
168
169 line, err := bufio.NewReader(os.Stdin).ReadString('\n')
170
171 if err != nil {
172 return "", err
173 }
174
175 line = strings.TrimSpace(line)
176
177 index, err := strconv.Atoi(line)
178 if err != nil || index <= 0 || index > len(targets) {
179 fmt.Println("invalid input")
180 continue
181 }
182
183 return targets[index-1], nil
184 }
185}
186
187func promptName(repo repository.RepoConfig) (string, error) {
188 // TODO: use the reusable prompt from the input package
189 const defaultName = "default"
190
191 defaultExist := core.BridgeExist(repo, defaultName)
192
193 for {
194 if defaultExist {
195 fmt.Printf("name: ")
196 } else {
197 fmt.Printf("name [%s]: ", defaultName)
198 }
199
200 line, err := bufio.NewReader(os.Stdin).ReadString('\n')
201 if err != nil {
202 return "", err
203 }
204
205 line = strings.TrimSpace(line)
206
207 name := line
208 if defaultExist && name == "" {
209 continue
210 }
211
212 if name == "" {
213 name = defaultName
214 }
215
216 if !core.BridgeExist(repo, name) {
217 return name, nil
218 }
219
220 fmt.Println("a bridge with the same name already exist")
221 }
222}