diff options
author | Michael Muré <batolettre@gmail.com> | 2020-02-10 01:29:51 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-10 01:29:51 +0100 |
commit | 9f3618d886d461ea806468a8c690a4a303d66d9b (patch) | |
tree | 9d941dd499a3bffbbba3295c58a6b77b5b6feeca | |
parent | 3caffeef4d2ed25d4eb5d4bfd262f4fc3b92561f (diff) | |
parent | bd7b50bc8638d3b6c776dd974de32a1ea385c835 (diff) | |
download | git-bug-9f3618d886d461ea806468a8c690a4a303d66d9b.tar.gz |
Merge pull request #317 from MichaelMure/bridges-improve
github/gitlab: many fixes and improvments at the config step
-rw-r--r-- | bridge/core/auth/credential.go | 3 | ||||
-rw-r--r-- | bridge/github/config.go | 110 | ||||
-rw-r--r-- | bridge/github/import_query.go | 14 | ||||
-rw-r--r-- | bridge/gitlab/config.go | 54 | ||||
-rw-r--r-- | bridge/gitlab/export.go | 10 | ||||
-rw-r--r-- | bridge/gitlab/export_test.go | 9 | ||||
-rw-r--r-- | bridge/gitlab/import.go | 6 | ||||
-rw-r--r-- | bridge/gitlab/import_test.go | 5 |
8 files changed, 140 insertions, 71 deletions
diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index c1255aa6..6dcac09f 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -18,7 +18,8 @@ const ( configKeyCreateTime = "createtime" configKeyPrefixMeta = "meta." - MetaKeyLogin = "login" + MetaKeyLogin = "login" + MetaKeyBaseURL = "base-url" ) type CredentialKind string diff --git a/bridge/github/config.go b/bridge/github/config.go index 9477801d..cc312230 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -3,6 +3,7 @@ package github import ( "bufio" "bytes" + "context" "encoding/json" "fmt" "io" @@ -70,25 +71,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, fmt.Errorf("invalid parameter owner: %v", owner) } - login := params.Login - if login == "" { - validator := func(name string, value string) (string, error) { - ok, err := validateUsername(value) - if err != nil { - return "", err - } - if !ok { - return "invalid login", nil - } - return "", nil - } - - login, err = input.Prompt("Github login", "login", input.Required, validator) - if err != nil { - return nil, err - } - } - + var login string var cred auth.Credential switch { @@ -97,10 +80,27 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if err != nil { return nil, err } + l, ok := cred.GetMetadata(auth.MetaKeyLogin) + if !ok { + return nil, fmt.Errorf("credential doesn't have a login") + } + login = l case params.TokenRaw != "": - cred = auth.NewToken(params.TokenRaw, target) - cred.SetMetadata(auth.MetaKeyLogin, login) + token := auth.NewToken(params.TokenRaw, target) + login, err = getLoginFromToken(token) + if err != nil { + return nil, err + } + token.SetMetadata(auth.MetaKeyLogin, login) + cred = token default: + login = params.Login + if login == "" { + login, err = input.Prompt("Github login", "login", input.Required, usernameValidator) + if err != nil { + return nil, err + } + } cred, err = promptTokenOptions(repo, login, owner, project) if err != nil { return nil, err @@ -159,6 +159,17 @@ func (*Github) ValidateConfig(conf core.Configuration) error { return nil } +func usernameValidator(name string, value string) (string, error) { + ok, err := validateUsername(value) + if err != nil { + return "", err + } + if !ok { + return "invalid login", nil + } + return "", nil +} + func requestToken(note, login, password string, scope string) (*http.Response, error) { return requestTokenWith2FA(note, login, password, "", scope) } @@ -231,7 +242,11 @@ func randomFingerprint() string { func promptTokenOptions(repo repository.RepoConfig, login, owner, project string) (auth.Credential, error) { for { - creds, err := auth.List(repo, auth.WithTarget(target), auth.WithMeta(auth.MetaKeyLogin, login)) + creds, err := auth.List(repo, + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + auth.WithMeta(auth.MetaKeyLogin, login), + ) if err != nil { return nil, err } @@ -275,13 +290,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string switch index { case 1: - value, err := promptToken() - if err != nil { - return nil, err - } - token := auth.NewToken(value, target) - token.SetMetadata(auth.MetaKeyLogin, login) - return token, nil + return promptToken() case 2: value, err := loginAndRequestToken(login, owner, project) if err != nil { @@ -296,7 +305,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string } } -func promptToken() (string, error) { +func promptToken() (*auth.Token, error) { fmt.Println("You can generate a new token by visiting https://github.com/settings/tokens.") fmt.Println("Choose 'Generate new token' and set the necessary access scope for your repository.") fmt.Println() @@ -312,14 +321,28 @@ func promptToken() (string, error) { panic("regexp compile:" + err.Error()) } + var login string + validator := func(name string, value string) (complaint string, err error) { - if re.MatchString(value) { - return "", nil + if !re.MatchString(value) { + return "token has incorrect format", nil + } + login, err = getLoginFromToken(auth.NewToken(value, target)) + if err != nil { + return fmt.Sprintf("token is invalid: %v", err), nil } - return "token has incorrect format", nil + return "", nil + } + + rawToken, err := input.Prompt("Enter token", "token", input.Required, validator) + if err != nil { + return nil, err } - return input.Prompt("Enter token", "token", input.Required, validator) + token := auth.NewToken(rawToken, target) + token.SetMetadata(auth.MetaKeyLogin, login) + + return token, nil } func loginAndRequestToken(login, owner, project string) (string, error) { @@ -543,3 +566,22 @@ func validateProject(owner, project string, token *auth.Token) (bool, error) { return resp.StatusCode == http.StatusOK, nil } + +func getLoginFromToken(token *auth.Token) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + client := buildClient(token) + + var q loginQuery + + err := client.Query(ctx, &q, nil) + if err != nil { + return "", err + } + if q.Viewer.Login == "" { + return "", fmt.Errorf("github say username is empty") + } + + return q.Viewer.Login, nil +} diff --git a/bridge/github/import_query.go b/bridge/github/import_query.go index f5cad299..58f6d95e 100644 --- a/bridge/github/import_query.go +++ b/bridge/github/import_query.go @@ -168,14 +168,6 @@ type ghostQuery struct { } `graphql:"user(login: $login)"` } -type labelQuery struct { - Repository struct { - Label struct { - ID string `graphql:"id"` - } `graphql:"label(name: $label)"` - } `graphql:"repository(owner: $owner, name: $name)"` -} - type labelsQuery struct { Repository struct { Labels struct { @@ -189,3 +181,9 @@ type labelsQuery struct { } `graphql:"labels(first: $first, after: $after)"` } `graphql:"repository(owner: $owner, name: $name)"` } + +type loginQuery struct { + Viewer struct { + Login string `graphql:"login"` + } `graphql:"viewer"` +} diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index fb593819..62d385dc 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -35,9 +35,6 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if params.Owner != "" { fmt.Println("warning: --owner is ineffective for a gitlab bridge") } - if params.Login != "" { - fmt.Println("warning: --login is ineffective for a gitlab bridge") - } conf := make(core.Configuration) var err error @@ -53,24 +50,25 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } } - var url string + var projectURL string // get project url switch { case params.URL != "": - url = params.URL + projectURL = params.URL default: // terminal prompt - url, err = promptURL(repo, baseUrl) + projectURL, err = promptProjectURL(repo, baseUrl) if err != nil { return nil, errors.Wrap(err, "url prompt") } } - if !strings.HasPrefix(url, params.BaseURL) { - return nil, fmt.Errorf("base URL (%s) doesn't match the project URL (%s)", params.BaseURL, url) + if !strings.HasPrefix(projectURL, params.BaseURL) { + return nil, fmt.Errorf("base URL (%s) doesn't match the project URL (%s)", params.BaseURL, projectURL) } + var login string var cred auth.Credential switch { @@ -79,16 +77,30 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if err != nil { return nil, err } + l, ok := cred.GetMetadata(auth.MetaKeyLogin) + if !ok { + return nil, fmt.Errorf("credential doesn't have a login") + } + login = l case params.TokenRaw != "": token := auth.NewToken(params.TokenRaw, target) - login, err := getLoginFromToken(baseUrl, token) + login, err = getLoginFromToken(baseUrl, token) if err != nil { return nil, err } token.SetMetadata(auth.MetaKeyLogin, login) + token.SetMetadata(auth.MetaKeyBaseURL, baseUrl) cred = token default: - cred, err = promptTokenOptions(repo, baseUrl) + login := params.Login + if login == "" { + // TODO: validate username + login, err = input.Prompt("Gitlab login", "login", input.Required) + if err != nil { + return nil, err + } + } + cred, err = promptTokenOptions(repo, login, baseUrl) if err != nil { return nil, err } @@ -100,7 +112,7 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } // validate project url and get its ID - id, err := validateProjectURL(baseUrl, url, token) + id, err := validateProjectURL(baseUrl, projectURL, token) if err != nil { return nil, errors.Wrap(err, "project validation") } @@ -122,7 +134,7 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } } - return conf, nil + return conf, core.FinishConfig(repo, metaKeyGitlabLogin, login) } func (g *Gitlab) ValidateConfig(conf core.Configuration) error { @@ -176,9 +188,14 @@ func promptBaseUrl() (string, error) { return input.Prompt("Base url", "url", input.Required, validator) } -func promptTokenOptions(repo repository.RepoConfig, baseUrl string) (auth.Credential, error) { +func promptTokenOptions(repo repository.RepoConfig, login, baseUrl string) (auth.Credential, error) { for { - creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) + 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 } @@ -262,11 +279,12 @@ func promptToken(baseUrl string) (*auth.Token, error) { token := auth.NewToken(rawToken, target) token.SetMetadata(auth.MetaKeyLogin, login) + token.SetMetadata(auth.MetaKeyBaseURL, baseUrl) return token, nil } -func promptURL(repo repository.RepoCommon, baseUrl string) (string, error) { +func promptProjectURL(repo repository.RepoCommon, baseUrl string) (string, error) { // remote suggestions remotes, err := repo.GetRemotes() if err != nil { @@ -317,13 +335,13 @@ func promptURL(repo repository.RepoCommon, baseUrl string) (string, error) { return "", err } - url := strings.TrimSpace(line) - if url == "" { + projectURL := strings.TrimSpace(line) + if projectURL == "" { fmt.Println("URL is empty") continue } - return url, nil + return projectURL, nil } } diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go index c5323da4..d747c6ac 100644 --- a/bridge/gitlab/export.go +++ b/bridge/gitlab/export.go @@ -47,7 +47,7 @@ func (ge *gitlabExporter) Init(repo *cache.RepoCache, conf core.Configuration) e ge.repositoryID = ge.conf[keyProjectID] // preload all clients - err := ge.cacheAllClient(repo) + err := ge.cacheAllClient(repo, ge.conf[keyGitlabBaseUrl]) if err != nil { return err } @@ -55,8 +55,12 @@ func (ge *gitlabExporter) Init(repo *cache.RepoCache, conf core.Configuration) e return nil } -func (ge *gitlabExporter) cacheAllClient(repo *cache.RepoCache) error { - creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) +func (ge *gitlabExporter) cacheAllClient(repo *cache.RepoCache, baseURL string) error { + creds, err := auth.List(repo, + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + auth.WithMeta(auth.MetaKeyBaseURL, baseURL), + ) if err != nil { return err } diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index 1d387655..c97416d8 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -164,6 +164,7 @@ func TestPushPull(t *testing.T) { token := auth.NewToken(envToken, target) token.SetMetadata(auth.MetaKeyLogin, login) + token.SetMetadata(auth.MetaKeyBaseURL, defaultBaseURL) err = auth.Store(repo, token) require.NoError(t, err) @@ -194,7 +195,7 @@ func TestPushPull(t *testing.T) { exporter := &gitlabExporter{} err = exporter.Init(backend, core.Configuration{ keyProjectID: strconv.Itoa(projectID), - keyGitlabBaseUrl: "https://gitlab.com/", + keyGitlabBaseUrl: defaultBaseURL, }) require.NoError(t, err) @@ -222,7 +223,7 @@ func TestPushPull(t *testing.T) { importer := &gitlabImporter{} err = importer.Init(backend, core.Configuration{ keyProjectID: strconv.Itoa(projectID), - keyGitlabBaseUrl: "https://gitlab.com/", + keyGitlabBaseUrl: defaultBaseURL, }) require.NoError(t, err) @@ -287,7 +288,7 @@ func generateRepoName() string { // create repository need a token with scope 'repo' func createRepository(ctx context.Context, name string, token *auth.Token) (int, error) { - client, err := buildClient("https://gitlab.com/", token) + client, err := buildClient(defaultBaseURL, token) if err != nil { return 0, err } @@ -307,7 +308,7 @@ func createRepository(ctx context.Context, name string, token *auth.Token) (int, // delete repository need a token with scope 'delete_repo' func deleteRepository(ctx context.Context, project int, token *auth.Token) error { - client, err := buildClient("https://gitlab.com/", token) + client, err := buildClient(defaultBaseURL, token) if err != nil { return err } diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index d699554b..4fccb47e 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -33,7 +33,11 @@ type gitlabImporter struct { func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) error { gi.conf = conf - creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) + creds, err := auth.List(repo, + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + auth.WithMeta(auth.MetaKeyBaseURL, conf[keyGitlabBaseUrl]), + ) if err != nil { return err } diff --git a/bridge/gitlab/import_test.go b/bridge/gitlab/import_test.go index 3c0caa55..a300acf1 100644 --- a/bridge/gitlab/import_test.go +++ b/bridge/gitlab/import_test.go @@ -99,14 +99,15 @@ func TestImport(t *testing.T) { author.SetMetadata(metaKeyGitlabLogin, login) token := auth.NewToken(envToken, target) - token.SetMetadata(metaKeyGitlabLogin, login) + token.SetMetadata(auth.MetaKeyLogin, login) + token.SetMetadata(auth.MetaKeyBaseURL, defaultBaseURL) err = auth.Store(repo, token) require.NoError(t, err) importer := &gitlabImporter{} err = importer.Init(backend, core.Configuration{ keyProjectID: projectID, - keyGitlabBaseUrl: "https://gitlab.com", + keyGitlabBaseUrl: defaultBaseURL, }) require.NoError(t, err) |