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.BaseURL, "base-url", "b", "", "The base URL of your issue tracker service")
220	bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.Owner, "owner", "o", "", "The owner of the target repository")
221	bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.CredPrefix, "credential", "c", "", "The identifier or prefix of an already known credential for the API (see \"git-bug bridge auth\")")
222	bridgeConfigureCmd.Flags().StringVar(&bridgeConfigureToken, "token", "", "A raw authentication token for the API")
223	bridgeConfigureCmd.Flags().BoolVar(&bridgeConfigureTokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token")
224	bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.Project, "project", "p", "", "The name of the target repository")
225	bridgeConfigureCmd.Flags().SortFlags = false
226}