diff options
Diffstat (limited to 'bridge/gitlab')
-rw-r--r-- | bridge/gitlab/config.go | 223 | ||||
-rw-r--r-- | bridge/gitlab/export.go | 16 | ||||
-rw-r--r-- | bridge/gitlab/export_test.go | 17 | ||||
-rw-r--r-- | bridge/gitlab/gitlab.go | 10 | ||||
-rw-r--r-- | bridge/gitlab/import.go | 12 | ||||
-rw-r--r-- | bridge/gitlab/import_test.go | 11 |
6 files changed, 85 insertions, 204 deletions
diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 62d385dc..d5dc418c 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -1,18 +1,13 @@ package gitlab import ( - "bufio" "fmt" "net/url" - "os" "path" "regexp" - "sort" "strconv" "strings" - "time" - text "github.com/MichaelMure/go-term-text" "github.com/pkg/errors" "github.com/xanzy/go-gitlab" @@ -21,22 +16,23 @@ import ( "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" - "github.com/MichaelMure/git-bug/util/colors" ) var ( ErrBadProjectURL = errors.New("bad project url") ) -func (g *Gitlab) Configure(repo *cache.RepoCache, 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") +func (g *Gitlab) ValidParams() map[string]interface{} { + return map[string]interface{}{ + "URL": nil, + "BaseURL": nil, + "Login": nil, + "CredPrefix": nil, + "TokenRaw": nil, } +} - conf := make(core.Configuration) +func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) { var err error var baseUrl string @@ -44,7 +40,7 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor case params.BaseURL != "": baseUrl = params.BaseURL default: - baseUrl, err = promptBaseUrlOptions() + baseUrl, err = input.PromptDefault("Gitlab server URL", "URL", defaultBaseURL, input.Required, input.IsURL) if err != nil { return nil, errors.Wrap(err, "base url prompt") } @@ -83,7 +79,7 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } login = l case params.TokenRaw != "": - token := auth.NewToken(params.TokenRaw, target) + token := auth.NewToken(target, params.TokenRaw) login, err = getLoginFromToken(baseUrl, token) if err != nil { return nil, err @@ -117,9 +113,10 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, errors.Wrap(err, "project validation") } + conf := make(core.Configuration) conf[core.ConfigKeyTarget] = target - conf[keyProjectID] = strconv.Itoa(id) - conf[keyGitlabBaseUrl] = baseUrl + conf[confKeyProjectID] = strconv.Itoa(id) + conf[confKeyGitlabBaseUrl] = baseUrl err = g.ValidateConfig(conf) if err != nil { @@ -143,107 +140,39 @@ 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[confKeyGitlabBaseUrl]; !ok { + return fmt.Errorf("missing %s key", confKeyGitlabBaseUrl) } - if _, ok := conf[keyProjectID]; !ok { - return fmt.Errorf("missing %s key", keyProjectID) + if _, ok := conf[confKeyProjectID]; !ok { + return fmt.Errorf("missing %s key", confKeyProjectID) } return nil } -func promptBaseUrlOptions() (string, error) { - index, err := input.PromptChoice("Gitlab base url", []string{ - "https://gitlab.com", - "enter your own base url", - }) - +func promptTokenOptions(repo repository.RepoConfig, login, baseUrl string) (auth.Credential, error) { + creds, err := auth.List(repo, + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + auth.WithMeta(auth.MetaKeyLogin, login), + auth.WithMeta(auth.MetaKeyBaseURL, baseUrl), + ) if err != nil { - return "", err - } - - if index == 0 { - return defaultBaseURL, nil - } else { - return promptBaseUrl() - } -} - -func promptBaseUrl() (string, error) { - validator := func(name string, value string) (string, error) { - u, err := url.Parse(value) - if err != nil { - return err.Error(), nil - } - if u.Scheme == "" { - return "missing scheme", nil - } - if u.Host == "" { - return "missing host", nil - } - return "", nil + return nil, err } - return input.Prompt("Base url", "url", input.Required, validator) -} - -func promptTokenOptions(repo repository.RepoConfig, login, baseUrl string) (auth.Credential, error) { - for { - creds, err := auth.List(repo, - auth.WithTarget(target), - auth.WithKind(auth.KindToken), - auth.WithMeta(auth.MetaKeyLogin, login), - auth.WithMeta(auth.MetaKeyBaseURL, baseUrl), - ) - if err != nil { - return nil, err - } - - // if we don't have existing token, fast-track to the token prompt - if len(creds) == 0 { - return promptToken(baseUrl) - } - - fmt.Println() - fmt.Println("[1]: enter my token") - - fmt.Println() - fmt.Println("Existing tokens for Gitlab:") - - sort.Sort(auth.ById(creds)) - for i, cred := range creds { - token := cred.(*auth.Token) - fmt.Printf("[%d]: %s => %s (%s)\n", - i+2, - colors.Cyan(token.ID().Human()), - colors.Red(text.TruncateMax(token.Value, 10)), - token.CreateTime().Format(time.RFC822), - ) - } - - fmt.Println() - fmt.Print("Select option: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - fmt.Println() - if err != nil { - return nil, err - } - - line = strings.TrimSpace(line) - index, err := strconv.Atoi(line) - if err != nil || index < 1 || index > len(creds)+1 { - fmt.Println("invalid input") - continue - } - - switch index { - case 1: - return promptToken(baseUrl) - default: - return creds[index-2], nil - } + cred, index, err := input.PromptCredential(target, "token", creds, []string{ + "enter my token", + }) + switch { + case err != nil: + return nil, err + case cred != nil: + return cred, nil + case index == 0: + return promptToken(baseUrl) + default: + panic("missed case") } } @@ -254,10 +183,7 @@ func promptToken(baseUrl string) (*auth.Token, error) { 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()) - } + re := regexp.MustCompile(`^[a-zA-Z0-9\-\_]{20}$`) var login string @@ -265,7 +191,7 @@ func promptToken(baseUrl string) (*auth.Token, error) { if !re.MatchString(value) { return "token has incorrect format", nil } - login, err = getLoginFromToken(baseUrl, auth.NewToken(value, target)) + login, err = getLoginFromToken(baseUrl, auth.NewToken(target, value)) if err != nil { return fmt.Sprintf("token is invalid: %v", err), nil } @@ -277,7 +203,7 @@ func promptToken(baseUrl string) (*auth.Token, error) { return nil, err } - token := auth.NewToken(rawToken, target) + token := auth.NewToken(target, rawToken) token.SetMetadata(auth.MetaKeyLogin, login) token.SetMetadata(auth.MetaKeyBaseURL, baseUrl) @@ -285,64 +211,12 @@ func promptToken(baseUrl string) (*auth.Token, error) { } func promptProjectURL(repo repository.RepoCommon, baseUrl string) (string, error) { - // remote suggestions - remotes, err := repo.GetRemotes() + validRemotes, err := getValidGitlabRemoteURLs(repo, baseUrl) if err != nil { - return "", errors.Wrap(err, "getting remotes") - } - - validRemotes := getValidGitlabRemoteURLs(baseUrl, 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.TrimSpace(line) - - 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 - } + return "", err } - // manually enter gitlab url - for { - fmt.Print("Gitlab project URL: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err - } - - projectURL := strings.TrimSpace(line) - if projectURL == "" { - fmt.Println("URL is empty") - continue - } - - return projectURL, nil - } + return input.PromptURLWithRemote("Gitlab project URL", "URL", validRemotes, input.Required) } func getProjectPath(baseUrl, projectUrl string) (string, error) { @@ -364,7 +238,12 @@ func getProjectPath(baseUrl, projectUrl string) (string, error) { return objectUrl.Path[1:], nil } -func getValidGitlabRemoteURLs(baseUrl string, remotes map[string]string) []string { +func getValidGitlabRemoteURLs(repo repository.RepoCommon, baseUrl string) ([]string, error) { + remotes, err := repo.GetRemotes() + if err != nil { + return nil, err + } + urls := make([]string, 0, len(remotes)) for _, u := range remotes { path, err := getProjectPath(baseUrl, u) @@ -375,7 +254,7 @@ func getValidGitlabRemoteURLs(baseUrl string, remotes map[string]string) []strin urls = append(urls, fmt.Sprintf("%s/%s", baseUrl, path)) } - return urls + return urls, nil } func validateProjectURL(baseUrl, url string, token *auth.Token) (int, error) { diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go index d747c6ac..918e6b5e 100644 --- a/bridge/gitlab/export.go +++ b/bridge/gitlab/export.go @@ -38,16 +38,16 @@ type gitlabExporter struct { } // Init . -func (ge *gitlabExporter) Init(repo *cache.RepoCache, conf core.Configuration) error { +func (ge *gitlabExporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error { ge.conf = conf ge.identityClient = make(map[entity.Id]*gitlab.Client) ge.cachedOperationIDs = make(map[string]string) // get repository node id - ge.repositoryID = ge.conf[keyProjectID] + ge.repositoryID = ge.conf[confKeyProjectID] // preload all clients - err := ge.cacheAllClient(repo, ge.conf[keyGitlabBaseUrl]) + err := ge.cacheAllClient(repo, ge.conf[confKeyGitlabBaseUrl]) if err != nil { return err } @@ -81,7 +81,7 @@ func (ge *gitlabExporter) cacheAllClient(repo *cache.RepoCache, baseURL string) } if _, ok := ge.identityClient[user.Id()]; !ok { - client, err := buildClient(ge.conf[keyGitlabBaseUrl], creds[0].(*auth.Token)) + client, err := buildClient(ge.conf[confKeyGitlabBaseUrl], creds[0].(*auth.Token)) if err != nil { return err } @@ -138,7 +138,7 @@ func (ge *gitlabExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, if snapshot.HasAnyActor(allIdentitiesIds...) { // try to export the bug and it associated events - ge.exportBug(ctx, b, since, out) + ge.exportBug(ctx, b, out) } } } @@ -148,7 +148,7 @@ func (ge *gitlabExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, } // exportBug publish bugs and related events -func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, since time.Time, out chan<- core.ExportResult) { +func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, out chan<- core.ExportResult) { snapshot := b.Snapshot() var bugUpdated bool @@ -177,7 +177,7 @@ func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc gitlabID, ok := snapshot.GetCreateMetadata(metaKeyGitlabId) if ok { gitlabBaseUrl, ok := snapshot.GetCreateMetadata(metaKeyGitlabBaseUrl) - if ok && gitlabBaseUrl != ge.conf[keyGitlabBaseUrl] { + if ok && gitlabBaseUrl != ge.conf[confKeyGitlabBaseUrl] { out <- core.NewExportNothing(b.Id(), "skipping issue imported from another Gitlab instance") return } @@ -189,7 +189,7 @@ func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc return } - if projectID != ge.conf[keyProjectID] { + if projectID != ge.conf[confKeyProjectID] { out <- core.NewExportNothing(b.Id(), "skipping issue imported from another repository") return } diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index c97416d8..d704ac3b 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -162,7 +162,7 @@ func TestPushPull(t *testing.T) { defer backend.Close() interrupt.RegisterCleaner(backend.Close) - token := auth.NewToken(envToken, target) + token := auth.NewToken(target, envToken) token.SetMetadata(auth.MetaKeyLogin, login) token.SetMetadata(auth.MetaKeyBaseURL, defaultBaseURL) err = auth.Store(repo, token) @@ -191,15 +191,16 @@ func TestPushPull(t *testing.T) { return deleteRepository(context.TODO(), projectID, token) }) + ctx := context.Background() + // initialize exporter exporter := &gitlabExporter{} - err = exporter.Init(backend, core.Configuration{ - keyProjectID: strconv.Itoa(projectID), - keyGitlabBaseUrl: defaultBaseURL, + err = exporter.Init(ctx, backend, core.Configuration{ + confKeyProjectID: strconv.Itoa(projectID), + confKeyGitlabBaseUrl: defaultBaseURL, }) require.NoError(t, err) - ctx := context.Background() start := time.Now() // export all bugs @@ -221,9 +222,9 @@ func TestPushPull(t *testing.T) { require.NoError(t, err) importer := &gitlabImporter{} - err = importer.Init(backend, core.Configuration{ - keyProjectID: strconv.Itoa(projectID), - keyGitlabBaseUrl: defaultBaseURL, + err = importer.Init(ctx, backend, core.Configuration{ + confKeyProjectID: strconv.Itoa(projectID), + confKeyGitlabBaseUrl: defaultBaseURL, }) require.NoError(t, err) diff --git a/bridge/gitlab/gitlab.go b/bridge/gitlab/gitlab.go index 8512379c..ec7b7e57 100644 --- a/bridge/gitlab/gitlab.go +++ b/bridge/gitlab/gitlab.go @@ -19,8 +19,8 @@ const ( metaKeyGitlabProject = "gitlab-project-id" metaKeyGitlabBaseUrl = "gitlab-base-url" - keyProjectID = "project-id" - keyGitlabBaseUrl = "base-url" + confKeyProjectID = "project-id" + confKeyGitlabBaseUrl = "base-url" defaultBaseURL = "https://gitlab.com/" defaultTimeout = 60 * time.Second @@ -30,7 +30,7 @@ var _ core.BridgeImpl = &Gitlab{} type Gitlab struct{} -func (*Gitlab) Target() string { +func (Gitlab) Target() string { return target } @@ -38,11 +38,11 @@ func (g *Gitlab) LoginMetaKey() string { return metaKeyGitlabLogin } -func (*Gitlab) NewImporter() core.Importer { +func (Gitlab) NewImporter() core.Importer { return &gitlabImporter{} } -func (*Gitlab) NewExporter() core.Exporter { +func (Gitlab) NewExporter() core.Exporter { return &gitlabExporter{} } diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index 4fccb47e..5faf5c48 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -30,13 +30,13 @@ type gitlabImporter struct { out chan<- core.ImportResult } -func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) error { +func (gi *gitlabImporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error { gi.conf = conf creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken), - auth.WithMeta(auth.MetaKeyBaseURL, conf[keyGitlabBaseUrl]), + auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyGitlabBaseUrl]), ) if err != nil { return err @@ -46,7 +46,7 @@ func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) e return ErrMissingIdentityToken } - gi.client, err = buildClient(conf[keyGitlabBaseUrl], creds[0].(*auth.Token)) + gi.client, err = buildClient(conf[confKeyGitlabBaseUrl], creds[0].(*auth.Token)) if err != nil { return err } @@ -57,7 +57,7 @@ func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) e // ImportAll iterate over all the configured repository issues (notes) and ensure the creation // of the missing issues / comments / label events / title changes ... func (gi *gitlabImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) { - gi.iterator = NewIterator(ctx, gi.client, 10, gi.conf[keyProjectID], since) + gi.iterator = NewIterator(ctx, gi.client, 10, gi.conf[confKeyProjectID], since) out := make(chan core.ImportResult) gi.out = out @@ -147,8 +147,8 @@ func (gi *gitlabImporter) ensureIssue(repo *cache.RepoCache, issue *gitlab.Issue core.MetaKeyOrigin: target, metaKeyGitlabId: parseID(issue.IID), metaKeyGitlabUrl: issue.WebURL, - metaKeyGitlabProject: gi.conf[keyProjectID], - metaKeyGitlabBaseUrl: gi.conf[keyGitlabBaseUrl], + metaKeyGitlabProject: gi.conf[confKeyProjectID], + metaKeyGitlabBaseUrl: gi.conf[confKeyGitlabBaseUrl], }, ) diff --git a/bridge/gitlab/import_test.go b/bridge/gitlab/import_test.go index a300acf1..ea7acc18 100644 --- a/bridge/gitlab/import_test.go +++ b/bridge/gitlab/import_test.go @@ -98,20 +98,21 @@ func TestImport(t *testing.T) { login := "test-identity" author.SetMetadata(metaKeyGitlabLogin, login) - token := auth.NewToken(envToken, target) + token := auth.NewToken(target, envToken) token.SetMetadata(auth.MetaKeyLogin, login) token.SetMetadata(auth.MetaKeyBaseURL, defaultBaseURL) err = auth.Store(repo, token) require.NoError(t, err) + ctx := context.Background() + importer := &gitlabImporter{} - err = importer.Init(backend, core.Configuration{ - keyProjectID: projectID, - keyGitlabBaseUrl: defaultBaseURL, + err = importer.Init(ctx, backend, core.Configuration{ + confKeyProjectID: projectID, + confKeyGitlabBaseUrl: defaultBaseURL, }) require.NoError(t, err) - ctx := context.Background() start := time.Now() events, err := importer.ImportAll(ctx, backend, time.Time{}) |