From 5cffb5d11435257ebf5213c9a1710afb04978e9e Mon Sep 17 00:00:00 2001 From: amine Date: Thu, 26 Dec 2019 20:16:18 +0100 Subject: bridge/gitlab: add missing baseUrl prompt and options bridge/gitlab: fix api calls with self hosted Gitlab instances --- bridge/gitlab/config.go | 110 ++++++++++++++++++++++++++++++++++++------- bridge/gitlab/config_test.go | 2 +- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 99c27836..c92515b5 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -42,8 +42,16 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, fmt.Errorf("you must provide a project URL to configure this bridge with a token") } - if params.URL == "" { - params.URL = defaultBaseURL + var baseUrl string + + switch { + case params.BaseURL != "": + baseUrl = params.BaseURL + default: + baseUrl, err = promptBaseUrlOptions() + if err != nil { + return nil, errors.Wrap(err, "base url prompt") + } } var url string @@ -54,7 +62,7 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor url = params.URL default: // terminal prompt - url, err = promptURL(repo) + url, err = promptURL(repo, baseUrl) if err != nil { return nil, errors.Wrap(err, "url prompt") } @@ -95,14 +103,14 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } // validate project url and get its ID - id, err := validateProjectURL(params.BaseURL, url, token) + id, err := validateProjectURL(baseUrl, url, token) if err != nil { return nil, errors.Wrap(err, "project validation") } conf[core.ConfigKeyTarget] = target conf[keyProjectID] = strconv.Itoa(id) - conf[keyGitlabBaseUrl] = params.BaseURL + conf[keyGitlabBaseUrl] = baseUrl err = g.ValidateConfig(conf) if err != nil { @@ -126,7 +134,9 @@ func (g *Gitlab) ValidateConfig(conf core.Configuration) error { } else if v != target { return fmt.Errorf("unexpected target name: %v", v) } - + if _, ok := conf[keyGitlabBaseUrl]; !ok { + return fmt.Errorf("missing %s key", keyGitlabBaseUrl) + } if _, ok := conf[keyProjectID]; !ok { return fmt.Errorf("missing %s key", keyProjectID) } @@ -134,6 +144,64 @@ func (g *Gitlab) ValidateConfig(conf core.Configuration) error { return nil } +func promptBaseUrlOptions() (string, error) { + for { + fmt.Printf("Gitlab base url:\n") + fmt.Printf("[0]: https://gitlab.com\n") + fmt.Printf("[1]: enter your own base url\n") + fmt.Printf("Select option: ") + + line, err := bufio.NewReader(os.Stdin).ReadString('\n') + if err != nil { + return "", err + } + + line = strings.TrimSpace(line) + + index, err := strconv.Atoi(line) + if err != nil || index < 0 || index > 1 { + fmt.Println("invalid input") + continue + } + + switch index { + case 0: + return defaultBaseURL, nil + case 1: + return promptBaseUrl() + } + } +} + +func promptBaseUrl() (string, error) { + for { + fmt.Print("base url: ") + + line, err := bufio.NewReader(os.Stdin).ReadString('\n') + if err != nil { + return "", err + } + + line = strings.TrimSpace(line) + + ok, err := validateBaseUrl(line) + if err != nil { + return "", err + } + if ok { + return line, nil + } + } +} + +func validateBaseUrl(baseUrl string) (bool, error) { + u, err := url.Parse(baseUrl) + if err != nil { + return false, err + } + return u.Scheme != "" && u.Host != "", nil +} + func promptTokenOptions(repo repository.RepoConfig, userId entity.Id) (auth.Credential, error) { for { creds, err := auth.List(repo, auth.WithUserId(userId), auth.WithTarget(target), auth.WithKind(auth.KindToken)) @@ -203,7 +271,7 @@ func promptToken() (string, error) { fmt.Println("'api' access scope: to be able to make api calls") fmt.Println() - re, err := regexp.Compile(`^[a-zA-Z0-9\-]{20}`) + re, err := regexp.Compile(`^[a-zA-Z0-9\-\_]{20}`) if err != nil { panic("regexp compile:" + err.Error()) } @@ -225,14 +293,14 @@ func promptToken() (string, error) { } } -func promptURL(repo repository.RepoCommon) (string, error) { +func promptURL(repo repository.RepoCommon, baseUrl string) (string, error) { // remote suggestions remotes, err := repo.GetRemotes() if err != nil { return "", errors.Wrap(err, "getting remotes") } - validRemotes := getValidGitlabRemoteURLs(remotes) + validRemotes := getValidGitlabRemoteURLs(baseUrl, remotes) if len(validRemotes) > 0 { for { fmt.Println("\nDetected projects:") @@ -286,7 +354,7 @@ func promptURL(repo repository.RepoCommon) (string, error) { } } -func getProjectPath(projectUrl string) (string, error) { +func getProjectPath(baseUrl, projectUrl string) (string, error) { cleanUrl := strings.TrimSuffix(projectUrl, ".git") cleanUrl = strings.Replace(cleanUrl, "git@", "https://", 1) objectUrl, err := url.Parse(cleanUrl) @@ -294,37 +362,45 @@ func getProjectPath(projectUrl string) (string, error) { return "", ErrBadProjectURL } + objectBaseUrl, err := url.Parse(baseUrl) + if err != nil { + return "", ErrBadProjectURL + } + + if objectUrl.Hostname() != objectBaseUrl.Hostname() { + return "", fmt.Errorf("base url and project url hostnames doesn't match") + } return objectUrl.Path[1:], nil } -func getValidGitlabRemoteURLs(remotes map[string]string) []string { +func getValidGitlabRemoteURLs(baseUrl string, remotes map[string]string) []string { urls := make([]string, 0, len(remotes)) for _, u := range remotes { - path, err := getProjectPath(u) + path, err := getProjectPath(baseUrl, u) if err != nil { continue } - urls = append(urls, fmt.Sprintf("%s%s", "gitlab.com", path)) + urls = append(urls, fmt.Sprintf("%s/%s", baseUrl, path)) } return urls } -func validateProjectURL(baseURL, url string, token *auth.Token) (int, error) { - projectPath, err := getProjectPath(url) +func validateProjectURL(baseUrl, url string, token *auth.Token) (int, error) { + projectPath, err := getProjectPath(baseUrl, url) if err != nil { return 0, err } - client, err := buildClient(baseURL, token) + client, err := buildClient(baseUrl, token) if err != nil { return 0, err } project, _, err := client.Projects.GetProject(projectPath, &gitlab.GetProjectOptions{}) if err != nil { - return 0, err + return 0, errors.Wrap(err, "wrong token scope ou inexistent project") } return project.ID, nil diff --git a/bridge/gitlab/config_test.go b/bridge/gitlab/config_test.go index 87469796..43ed649a 100644 --- a/bridge/gitlab/config_test.go +++ b/bridge/gitlab/config_test.go @@ -82,7 +82,7 @@ func TestProjectPath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - path, err := getProjectPath(tt.args.url) + path, err := getProjectPath(defaultBaseURL, tt.args.url) assert.Equal(t, tt.want.path, path) assert.Equal(t, tt.want.err, err) }) -- cgit