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