bridge_configure.go

  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}