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