diff options
author | Michael Muré <batolettre@gmail.com> | 2019-07-23 19:50:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-23 19:50:58 +0200 |
commit | 9ecbcb1cf6348b95b31ccef3f9722be078dbe223 (patch) | |
tree | d855b993905051d5ff5dbc3e30460bc09fa2e2c4 /bridge/gitlab/config.go | |
parent | ca00c9c6b84f0b1333e40666ab979d0d8fdc4036 (diff) | |
parent | 29fdd37ce69b48aa9fc3c1b829ff67818041068f (diff) | |
download | git-bug-9ecbcb1cf6348b95b31ccef3f9722be078dbe223.tar.gz |
Merge pull request #179 from MichaelMure/gitlab-support
Add gitlab bridge configuration and importer
Diffstat (limited to 'bridge/gitlab/config.go')
-rw-r--r-- | bridge/gitlab/config.go | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go new file mode 100644 index 00000000..15172871 --- /dev/null +++ b/bridge/gitlab/config.go @@ -0,0 +1,218 @@ +package gitlab + +import ( + "bufio" + "fmt" + "net/url" + "os" + "regexp" + "strconv" + "strings" + + "github.com/pkg/errors" + "github.com/xanzy/go-gitlab" + + "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/repository" +) + +var ( + ErrBadProjectURL = errors.New("bad project url") +) + +func (*Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) { + if params.Project != "" { + fmt.Println("warning: --project is ineffective for a gitlab bridge") + } + if params.Owner != "" { + fmt.Println("warning: --owner is ineffective for a gitlab bridge") + } + + conf := make(core.Configuration) + var err error + var url string + var token string + + // get project url + if params.URL != "" { + url = params.URL + + } else { + // remote suggestions + remotes, err := repo.GetRemotes() + if err != nil { + return nil, errors.Wrap(err, "getting remotes") + } + + // terminal prompt + url, err = promptURL(remotes) + if err != nil { + return nil, errors.Wrap(err, "url prompt") + } + } + + // get user token + if params.Token != "" { + token = params.Token + } else { + token, err = promptToken() + if err != nil { + return nil, errors.Wrap(err, "token prompt") + } + } + + // validate project url and get its ID + id, err := validateProjectURL(url, token) + if err != nil { + return nil, errors.Wrap(err, "project validation") + } + + conf[keyProjectID] = strconv.Itoa(id) + conf[keyToken] = token + conf[core.KeyTarget] = target + + return conf, nil +} + +func (*Gitlab) ValidateConfig(conf core.Configuration) error { + if v, ok := conf[core.KeyTarget]; !ok { + return fmt.Errorf("missing %s key", core.KeyTarget) + } else if v != target { + return fmt.Errorf("unexpected target name: %v", v) + } + + if _, ok := conf[keyToken]; !ok { + return fmt.Errorf("missing %s key", keyToken) + } + + if _, ok := conf[keyProjectID]; !ok { + return fmt.Errorf("missing %s key", keyProjectID) + } + + return nil +} + +func promptToken() (string, error) { + fmt.Println("You can generate a new token by visiting https://gitlab.com/profile/personal_access_tokens.") + fmt.Println("Choose 'Create personal access token' and set the necessary access scope for your repository.") + fmt.Println() + fmt.Println("'api' access scope: to be able to make api calls") + fmt.Println() + + re, err := regexp.Compile(`^[a-zA-Z0-9\-]{20}`) + if err != nil { + panic("regexp compile:" + err.Error()) + } + + for { + fmt.Print("Enter token: ") + + line, err := bufio.NewReader(os.Stdin).ReadString('\n') + if err != nil { + return "", err + } + + token := strings.TrimRight(line, "\n") + if re.MatchString(token) { + return token, nil + } + + fmt.Println("token format is invalid") + } +} + +func promptURL(remotes map[string]string) (string, error) { + validRemotes := getValidGitlabRemoteURLs(remotes) + if len(validRemotes) > 0 { + for { + fmt.Println("\nDetected projects:") + + // print valid remote gitlab urls + for i, remote := range validRemotes { + fmt.Printf("[%d]: %v\n", i+1, remote) + } + + fmt.Printf("\n[0]: Another project\n\n") + fmt.Printf("Select option: ") + + line, err := bufio.NewReader(os.Stdin).ReadString('\n') + if err != nil { + return "", err + } + + line = strings.TrimRight(line, "\n") + + index, err := strconv.Atoi(line) + if err != nil || index < 0 || index > len(validRemotes) { + fmt.Println("invalid input") + continue + } + + // if user want to enter another project url break this loop + if index == 0 { + break + } + + return validRemotes[index-1], nil + } + } + + // manually enter gitlab url + for { + fmt.Print("Gitlab project URL: ") + + line, err := bufio.NewReader(os.Stdin).ReadString('\n') + if err != nil { + return "", err + } + + url := strings.TrimRight(line, "\n") + if line == "" { + fmt.Println("URL is empty") + continue + } + + return url, nil + } +} + +func getProjectPath(projectUrl string) (string, error) { + cleanUrl := strings.TrimSuffix(projectUrl, ".git") + cleanUrl = strings.Replace(cleanUrl, "git@", "https://", 1) + objectUrl, err := url.Parse(cleanUrl) + if err != nil { + return "", ErrBadProjectURL + } + + return objectUrl.Path[1:], nil +} + +func getValidGitlabRemoteURLs(remotes map[string]string) []string { + urls := make([]string, 0, len(remotes)) + for _, u := range remotes { + path, err := getProjectPath(u) + if err != nil { + continue + } + + urls = append(urls, fmt.Sprintf("%s%s", "gitlab.com", path)) + } + + return urls +} + +func validateProjectURL(url, token string) (int, error) { + projectPath, err := getProjectPath(url) + if err != nil { + return 0, err + } + + client := buildClient(token) + + project, _, err := client.Projects.GetProject(projectPath, &gitlab.GetProjectOptions{}) + if err != nil { + return 0, err + } + + return project.ID, nil +} |