bridge_configure.go

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