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