From db893494bb1492a3d9e32787a5ada1dd8f639ef3 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 7 Jan 2020 22:06:42 +0100 Subject: input: better reusable prompt functions --- bridge/github/config.go | 139 +++++------------------------------------------- 1 file changed, 12 insertions(+), 127 deletions(-) (limited to 'bridge') diff --git a/bridge/github/config.go b/bridge/github/config.go index ea4b622f..8c4bf7c5 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -14,21 +14,19 @@ import ( "sort" "strconv" "strings" - "syscall" "time" text "github.com/MichaelMure/go-term-text" "github.com/pkg/errors" - "golang.org/x/crypto/ssh/terminal" "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" - "github.com/MichaelMure/git-bug/util/interrupt" ) const ( @@ -320,21 +318,14 @@ func promptToken() (string, error) { panic("regexp compile:" + err.Error()) } - for { - fmt.Print("Enter token: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err + validator := func(name string, value string) (complaint string, err error) { + if re.MatchString(value) { + return "", nil } - - token := strings.TrimSpace(line) - if re.MatchString(token) { - return token, nil - } - - fmt.Println("token has incorrect format") + return "token has incorrect format", nil } + + return input.Prompt("Enter token", "token", "", input.Required, validator) } func loginAndRequestToken(owner, project string) (string, error) { @@ -348,17 +339,18 @@ func loginAndRequestToken(owner, project string) (string, error) { fmt.Println() // prompt project visibility to know the token scope needed for the repository - isPublic, err := promptProjectVisibility() + i, err := input.PromptChoice("repository visibility", []string{"public", "private"}) if err != nil { return "", err } + isPublic := i == 0 username, err := promptUsername() if err != nil { return "", err } - password, err := promptPassword() + password, err := input.PromptPassword("Password", "password", input.Required) if err != nil { return "", err } @@ -387,12 +379,12 @@ func loginAndRequestToken(owner, project string) (string, error) { // Handle 2FA is needed OTPHeader := resp.Header.Get("X-GitHub-OTP") if resp.StatusCode == http.StatusUnauthorized && OTPHeader != "" { - otpCode, err := prompt2FA() + otpCode, err := input.PromptPassword("Two-factor authentication code", "code", input.Required) if err != nil { return "", err } - resp, err = requestTokenWith2FA(note, username, password, otpCode, scope) + resp, err = requestTokenWith2FA(note, login, password, otpCode, scope) if err != nil { return "", err } @@ -408,29 +400,6 @@ func loginAndRequestToken(owner, project string) (string, error) { return "", fmt.Errorf("error creating token %v: %v", resp.StatusCode, string(b)) } -func promptUsername() (string, error) { - for { - fmt.Print("username: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err - } - - line = strings.TrimSpace(line) - - ok, err := validateUsername(line) - if err != nil { - return "", err - } - if ok { - return line, nil - } - - fmt.Println("invalid username") - } -} - func promptURL(repo repository.RepoCommon) (string, string, error) { // remote suggestions remotes, err := repo.GetRemotes() @@ -585,87 +554,3 @@ func validateProject(owner, project string, token *auth.Token) (bool, error) { return resp.StatusCode == http.StatusOK, nil } - -func promptPassword() (string, error) { - termState, err := terminal.GetState(int(syscall.Stdin)) - if err != nil { - return "", err - } - - cancel := interrupt.RegisterCleaner(func() error { - return terminal.Restore(int(syscall.Stdin), termState) - }) - defer cancel() - - for { - fmt.Print("password: ") - - bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) - // new line for coherent formatting, ReadPassword clip the normal new line - // entered by the user - fmt.Println() - - if err != nil { - return "", err - } - - if len(bytePassword) > 0 { - return string(bytePassword), nil - } - - fmt.Println("password is empty") - } -} - -func prompt2FA() (string, error) { - termState, err := terminal.GetState(int(syscall.Stdin)) - if err != nil { - return "", err - } - - cancel := interrupt.RegisterCleaner(func() error { - return terminal.Restore(int(syscall.Stdin), termState) - }) - defer cancel() - - for { - fmt.Print("two-factor authentication code: ") - - byte2fa, err := terminal.ReadPassword(int(syscall.Stdin)) - fmt.Println() - if err != nil { - return "", err - } - - if len(byte2fa) > 0 { - return string(byte2fa), nil - } - - fmt.Println("code is empty") - } -} - -func promptProjectVisibility() (bool, error) { - for { - fmt.Println("[1]: public") - fmt.Println("[2]: private") - fmt.Print("repository visibility: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - fmt.Println() - if err != nil { - return false, err - } - - line = strings.TrimSpace(line) - - index, err := strconv.Atoi(line) - if err != nil || (index != 1 && index != 2) { - fmt.Println("invalid input") - continue - } - - // return true for public repositories, false for private - return index == 1, nil - } -} -- cgit From 26f0152384f77d2bfd16c6762f5618bc966809a6 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 7 Jan 2020 22:07:25 +0100 Subject: WIP --- bridge/core/auth/credential.go | 56 +++++++++++++++--------------------- bridge/core/auth/options.go | 32 +++++++++------------ bridge/core/auth/token.go | 20 ++++++------- bridge/core/bridge.go | 13 +++++---- bridge/github/config.go | 65 +++++++++++++++++------------------------- bridge/gitlab/config.go | 2 +- 6 files changed, 78 insertions(+), 110 deletions(-) (limited to 'bridge') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index fd026c5d..228eb006 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -14,9 +14,11 @@ import ( const ( configKeyPrefix = "git-bug.auth" configKeyKind = "kind" - configKeyUserId = "userid" configKeyTarget = "target" configKeyCreateTime = "createtime" + configKeyPrefixMeta = "meta." + + MetaKeyLogin = "login" ) type CredentialKind string @@ -32,19 +34,13 @@ func NewErrMultipleMatchCredential(matching []entity.Id) *entity.ErrMultipleMatc return entity.NewErrMultipleMatch("credential", matching) } -// Special Id to mark a credential as being associated to the default user, whoever it might be. -// The intended use is for the bridge configuration, to be able to create and store a credential -// with no identities created yet, and then select one with `git-bug user adopt` -const DefaultUserId = entity.Id("default-user") - type Credential interface { ID() entity.Id - UserId() entity.Id - updateUserId(id entity.Id) Target() string Kind() CredentialKind CreateTime() time.Time Validate() error + Metadata() map[string]string // Return all the specific properties of the credential that need to be saved into the configuration. // This does not include Target, User, Kind and CreateTime. @@ -120,6 +116,17 @@ func loadFromConfig(rawConfigs map[string]string, id entity.Id) (Credential, err return cred, nil } +func metaFromConfig(configs map[string]string) map[string]string { + result := make(map[string]string) + for key, val := range configs { + if strings.HasPrefix(key, configKeyPrefixMeta) { + key = strings.TrimPrefix(key, configKeyPrefixMeta) + result[key] = val + } + } + return result +} + // List load all existing credentials func List(repo repository.RepoConfig, opts ...Option) ([]Credential, error) { rawConfigs, err := repo.GlobalConfig().ReadAll(configKeyPrefix + ".") @@ -185,12 +192,6 @@ func Store(repo repository.RepoConfig, cred Credential) error { return err } - // UserId - err = repo.GlobalConfig().StoreString(prefix+configKeyUserId, cred.UserId().String()) - if err != nil { - return err - } - // Target err = repo.GlobalConfig().StoreString(prefix+configKeyTarget, cred.Target()) if err != nil { @@ -203,6 +204,14 @@ func Store(repo repository.RepoConfig, cred Credential) error { return err } + // Metadata + for key, val := range cred.Metadata() { + err := repo.GlobalConfig().StoreString(prefix+configKeyPrefixMeta+key, val) + if err != nil { + return err + } + } + // Custom for key, val := range confs { err := repo.GlobalConfig().StoreString(prefix+key, val) @@ -220,25 +229,6 @@ func Remove(repo repository.RepoConfig, id entity.Id) error { return repo.GlobalConfig().RemoveAll(keyPrefix) } -// ReplaceDefaultUser update all the credential attributed to the temporary "default user" -// with a real user Id -func ReplaceDefaultUser(repo repository.RepoConfig, id entity.Id) error { - list, err := List(repo, WithUserId(DefaultUserId)) - if err != nil { - return err - } - - for _, cred := range list { - cred.updateUserId(id) - err = Store(repo, cred) - if err != nil { - return err - } - } - - return nil -} - /* * Sorting */ diff --git a/bridge/core/auth/options.go b/bridge/core/auth/options.go index 7bcda68e..0c780dc1 100644 --- a/bridge/core/auth/options.go +++ b/bridge/core/auth/options.go @@ -1,14 +1,9 @@ package auth -import ( - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" -) - type options struct { target string - userId entity.Id kind CredentialKind + meta map[string]string } type Option func(opts *options) @@ -26,12 +21,14 @@ func (opts *options) Match(cred Credential) bool { return false } - if opts.userId != "" && cred.UserId() != opts.userId { + if opts.kind != "" && cred.Kind() != opts.kind { return false } - if opts.kind != "" && cred.Kind() != opts.kind { - return false + for key, val := range opts.meta { + if v, ok := cred.Metadata()[key]; !ok || v != val { + return false + } } return true @@ -43,20 +40,17 @@ func WithTarget(target string) Option { } } -func WithUser(user identity.Interface) Option { - return func(opts *options) { - opts.userId = user.Id() - } -} - -func WithUserId(userId entity.Id) Option { +func WithKind(kind CredentialKind) Option { return func(opts *options) { - opts.userId = userId + opts.kind = kind } } -func WithKind(kind CredentialKind) Option { +func WithMeta(key string, val string) Option { return func(opts *options) { - opts.kind = kind + if opts.meta == nil { + opts.meta = make(map[string]string) + } + opts.meta[key] = val } } diff --git a/bridge/core/auth/token.go b/bridge/core/auth/token.go index 8333ef12..60137cd9 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -18,26 +18,25 @@ var _ Credential = &Token{} // Token holds an API access token data type Token struct { - userId entity.Id target string createTime time.Time Value string + meta map[string]string } // NewToken instantiate a new token -func NewToken(userId entity.Id, value, target string) *Token { +func NewToken(value, target string) *Token { return &Token{ - userId: userId, target: target, createTime: time.Now(), Value: value, + meta: make(map[string]string), } } func NewTokenFromConfig(conf map[string]string) *Token { token := &Token{} - token.userId = entity.Id(conf[configKeyUserId]) token.target = conf[configKeyTarget] if createTime, ok := conf[configKeyCreateTime]; ok { if t, err := repository.ParseTimestamp(createTime); err == nil { @@ -46,6 +45,7 @@ func NewTokenFromConfig(conf map[string]string) *Token { } token.Value = conf[tokenValueKey] + token.meta = metaFromConfig(conf) return token } @@ -55,14 +55,6 @@ func (t *Token) ID() entity.Id { return entity.Id(fmt.Sprintf("%x", sum)) } -func (t *Token) UserId() entity.Id { - return t.userId -} - -func (t *Token) updateUserId(id entity.Id) { - t.userId = id -} - func (t *Token) Target() string { return t.target } @@ -92,6 +84,10 @@ func (t *Token) Validate() error { return nil } +func (t *Token) Metadata() map[string]string { + return t.meta +} + func (t *Token) toConfig() map[string]string { return map[string]string{ tokenValueKey: t.Value, diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go index f606d2da..7891763f 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -32,12 +32,13 @@ var bridgeImpl map[string]reflect.Type // BridgeParams holds parameters to simplify the bridge configuration without // having to make terminal prompts. type BridgeParams struct { - Owner string - Project string - URL string - BaseURL string - CredPrefix string - TokenRaw string + Owner string // owner of the repo (Github) + Project string // name of the repo (Github, Launchpad) + URL string // complete URL of a repo (Github, Gitlab, Launchpad) + BaseURL string // base URL for self-hosted instance ( Gitlab) + CredPrefix string // ID prefix of the credential to use (Github, Gitlab) + TokenRaw string // pre-existing token to use (Github, Gitlab) + Login string // username for the passed credential (Github, Gitlab) } // Bridge is a wrapper around a BridgeImpl that will bind low-level diff --git a/bridge/github/config.go b/bridge/github/config.go index 8c4bf7c5..e51f244b 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -22,8 +22,6 @@ import ( "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" @@ -49,12 +47,6 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor conf := make(core.Configuration) var err error - - if (params.CredPrefix != "" || params.TokenRaw != "") && - (params.URL == "" && (params.Project == "" || params.Owner == "")) { - return nil, fmt.Errorf("you must provide a project URL or Owner/Name to configure this bridge with a token") - } - var owner string var project string @@ -87,15 +79,12 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, fmt.Errorf("invalid parameter owner: %v", owner) } - user, err := repo.GetUserIdentity() - if err != nil && err != identity.ErrNoIdentitySet { - return nil, err - } - - // default to a "to be filled" user Id if we don't have a valid one yet - userId := auth.DefaultUserId - if user != nil { - userId = user.Id() + login := params.Login + if login == "" { + login, err = input.Prompt("Github login", "", true, validateUsername) + if err != nil { + return nil, err + } } var cred auth.Credential @@ -106,13 +95,11 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if err != nil { return nil, err } - if user != nil && cred.UserId() != user.Id() { - return nil, fmt.Errorf("selected credential don't match the user") - } case params.TokenRaw != "": - cred = auth.NewToken(userId, params.TokenRaw, target) + cred = auth.NewToken(params.TokenRaw, target) + cred.Metadata()[auth.MetaKeyLogin] = login default: - cred, err = promptTokenOptions(repo, userId, owner, project) + cred, err = promptTokenOptions(repo, login, owner, project) if err != nil { return nil, err } @@ -170,11 +157,11 @@ func (*Github) ValidateConfig(conf core.Configuration) error { return nil } -func requestToken(note, username, password string, scope string) (*http.Response, error) { - return requestTokenWith2FA(note, username, password, "", scope) +func requestToken(note, login, password string, scope string) (*http.Response, error) { + return requestTokenWith2FA(note, login, password, "", scope) } -func requestTokenWith2FA(note, username, password, otpCode string, scope string) (*http.Response, error) { +func requestTokenWith2FA(note, login, password, otpCode string, scope string) (*http.Response, error) { url := fmt.Sprintf("%s/authorizations", githubV3Url) params := struct { Scopes []string `json:"scopes"` @@ -196,7 +183,7 @@ func requestTokenWith2FA(note, username, password, otpCode string, scope string) return nil, err } - req.SetBasicAuth(username, password) + req.SetBasicAuth(login, password) req.Header.Set("Content-Type", "application/json") if otpCode != "" { @@ -240,9 +227,9 @@ func randomFingerprint() string { return string(b) } -func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, owner, project string) (auth.Credential, error) { +func promptTokenOptions(repo repository.RepoConfig, login, owner, project string) (auth.Credential, error) { for { - creds, err := auth.List(repo, auth.WithUserId(userId), auth.WithTarget(target)) + creds, err := auth.List(repo, auth.WithTarget(target), auth.WithMeta(auth.MetaKeyLogin, login)) if err != nil { return nil, err } @@ -258,10 +245,11 @@ func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, owner, pro fmt.Println("Existing tokens for Github:") for i, cred := range creds { token := cred.(*auth.Token) - fmt.Printf("[%d]: %s => %s (%s)\n", + fmt.Printf("[%d]: %s => %s (login: %s, %s)\n", i+3, colors.Cyan(token.ID().Human()), colors.Red(text.TruncateMax(token.Value, 10)), + token.Metadata()[auth.MetaKeyLogin], token.CreateTime().Format(time.RFC822), ) } @@ -289,13 +277,17 @@ func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, owner, pro if err != nil { return nil, err } - return auth.NewToken(userId, value, target), nil + token := auth.NewToken(value, target) + token.Metadata()[auth.MetaKeyLogin] = login + return token, nil case 2: - value, err := loginAndRequestToken(owner, project) + value, err := loginAndRequestToken(login, owner, project) if err != nil { return nil, err } - return auth.NewToken(userId, value, target), nil + token := auth.NewToken(value, target) + token.Metadata()[auth.MetaKeyLogin] = login + return token, nil default: return creds[index-3], nil } @@ -328,7 +320,7 @@ func promptToken() (string, error) { return input.Prompt("Enter token", "token", "", input.Required, validator) } -func loginAndRequestToken(owner, project string) (string, error) { +func loginAndRequestToken(login, owner, project string) (string, error) { fmt.Println("git-bug will now generate an access token in your Github profile. Your credential are not stored and are only used to generate the token. The token is stored in the global git config.") fmt.Println() fmt.Println("The access scope depend on the type of repository.") @@ -345,11 +337,6 @@ func loginAndRequestToken(owner, project string) (string, error) { } isPublic := i == 0 - username, err := promptUsername() - if err != nil { - return "", err - } - password, err := input.PromptPassword("Password", "password", input.Required) if err != nil { return "", err @@ -369,7 +356,7 @@ func loginAndRequestToken(owner, project string) (string, error) { note := fmt.Sprintf("git-bug - %s/%s", owner, project) - resp, err := requestToken(note, username, password, scope) + resp, err := requestToken(note, login, password, scope) if err != nil { return "", err } diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 5e345b31..0758074c 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -212,7 +212,7 @@ func validateBaseUrl(baseUrl string) (bool, error) { func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, baseUrl string) (auth.Credential, error) { for { - creds, err := auth.List(repo, auth.WithUserId(userId), auth.WithTarget(target), auth.WithKind(auth.KindToken)) + creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { return nil, err } -- cgit From ae2f942ef907161af0aba5f3511db72cf9801dca Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sun, 12 Jan 2020 16:38:16 +0100 Subject: more wip --- bridge/core/auth/credential.go | 4 ++-- bridge/core/auth/credential_test.go | 41 ++++++++++++++------------------- bridge/core/config.go | 1 + bridge/github/config.go | 8 +++++-- bridge/github/config_test.go | 5 ++--- bridge/github/export.go | 45 +++++++++++++------------------------ bridge/github/import.go | 16 +------------ bridge/github/import_test.go | 8 +------ 8 files changed, 45 insertions(+), 83 deletions(-) create mode 100644 bridge/core/config.go (limited to 'bridge') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index 228eb006..e843ede7 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -43,7 +43,7 @@ type Credential interface { Metadata() map[string]string // Return all the specific properties of the credential that need to be saved into the configuration. - // This does not include Target, User, Kind and CreateTime. + // This does not include Target, Kind, CreateTime and Metadata. toConfig() map[string]string } @@ -134,7 +134,7 @@ func List(repo repository.RepoConfig, opts ...Option) ([]Credential, error) { return nil, err } - re, err := regexp.Compile(configKeyPrefix + `.([^.]+).([^.]+)`) + re, err := regexp.Compile(`^` + configKeyPrefix + `\.([^.]+)\.([^.]+(?:\.[^.]+)*)$`) if err != nil { panic(err) } diff --git a/bridge/core/auth/credential_test.go b/bridge/core/auth/credential_test.go index f91d273d..49c138cf 100644 --- a/bridge/core/auth/credential_test.go +++ b/bridge/core/auth/credential_test.go @@ -7,32 +7,23 @@ import ( "github.com/stretchr/testify/require" "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/repository" ) func TestCredential(t *testing.T) { repo := repository.NewMockRepoForTest() - user1 := identity.NewIdentity("user1", "email") - err := user1.Commit(repo) - assert.NoError(t, err) - - user2 := identity.NewIdentity("user2", "email") - err = user2.Commit(repo) - assert.NoError(t, err) - - storeToken := func(user identity.Interface, val string, target string) *Token { - token := NewToken(user.Id(), val, target) - err = Store(repo, token) + storeToken := func(val string, target string) *Token { + token := NewToken(val, target) + err := Store(repo, token) require.NoError(t, err) return token } - token := storeToken(user1, "foobar", "github") + token := storeToken("foobar", "github") // Store + Load - err = Store(repo, token) + err := Store(repo, token) assert.NoError(t, err) token2, err := LoadWithId(repo, token.ID()) @@ -50,8 +41,8 @@ func TestCredential(t *testing.T) { token.createTime = token3.CreateTime() assert.Equal(t, token, token3) - token4 := storeToken(user1, "foo", "gitlab") - token5 := storeToken(user2, "bar", "github") + token4 := storeToken("foo", "gitlab") + token5 := storeToken("bar", "github") // List + options creds, err := List(repo, WithTarget("github")) @@ -62,14 +53,6 @@ func TestCredential(t *testing.T) { assert.NoError(t, err) sameIds(t, creds, []Credential{token4}) - creds, err = List(repo, WithUser(user1)) - assert.NoError(t, err) - sameIds(t, creds, []Credential{token, token4}) - - creds, err = List(repo, WithUserId(user1.Id())) - assert.NoError(t, err) - sameIds(t, creds, []Credential{token, token4}) - creds, err = List(repo, WithKind(KindToken)) assert.NoError(t, err) sameIds(t, creds, []Credential{token, token4, token5}) @@ -78,6 +61,16 @@ func TestCredential(t *testing.T) { assert.NoError(t, err) sameIds(t, creds, []Credential{}) + // Metadata + + token4.Metadata()["key"] = "value" + err = Store(repo, token4) + assert.NoError(t, err) + + creds, err = List(repo, WithMeta("key", "value")) + assert.NoError(t, err) + sameIds(t, creds, []Credential{token4}) + // Exist exist := IdExist(repo, token.ID()) assert.True(t, exist) diff --git a/bridge/core/config.go b/bridge/core/config.go new file mode 100644 index 00000000..9a8bc959 --- /dev/null +++ b/bridge/core/config.go @@ -0,0 +1 @@ +package core diff --git a/bridge/github/config.go b/bridge/github/config.go index e51f244b..fcb94079 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -81,7 +81,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor login := params.Login if login == "" { - login, err = input.Prompt("Github login", "", true, validateUsername) + login, err = input.Prompt("Github login", "login", input.Required, validateUsername) if err != nil { return nil, err } @@ -128,6 +128,10 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, err } + // Todo: if no user exist with the given login + // - tag the default user with the github login + // - add a command to manually tag a user ? + // don't forget to store the now known valid token if !auth.IdExist(repo, cred.ID()) { err = auth.Store(repo, cred) @@ -317,7 +321,7 @@ func promptToken() (string, error) { return "token has incorrect format", nil } - return input.Prompt("Enter token", "token", "", input.Required, validator) + return input.Prompt("Enter token", "token", input.Required, validator) } func loginAndRequestToken(login, owner, project string) (string, error) { diff --git a/bridge/github/config_test.go b/bridge/github/config_test.go index 9798d26b..d7b1b38d 100644 --- a/bridge/github/config_test.go +++ b/bridge/github/config_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/MichaelMure/git-bug/bridge/core/auth" - "github.com/MichaelMure/git-bug/entity" ) func TestSplitURL(t *testing.T) { @@ -155,8 +154,8 @@ func TestValidateProject(t *testing.T) { t.Skip("Env var GITHUB_TOKEN_PUBLIC missing") } - tokenPrivate := auth.NewToken(entity.UnsetId, envPrivate, target) - tokenPublic := auth.NewToken(entity.UnsetId, envPublic, target) + tokenPrivate := auth.NewToken(envPrivate, target) + tokenPublic := auth.NewToken(envPublic, target) type args struct { owner string diff --git a/bridge/github/export.go b/bridge/github/export.go index 6c089a47..1cc66dee 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "strings" "time" @@ -19,6 +20,7 @@ import ( "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/repository" ) @@ -33,13 +35,6 @@ type githubExporter struct { // cache identities clients identityClient map[entity.Id]*githubv4.Client - // the client to use for non user-specific queries - // should be the client of the default user - defaultClient *githubv4.Client - - // the token of the default user - defaultToken *auth.Token - // github repository ID repositoryID string @@ -58,43 +53,33 @@ func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) e ge.cachedOperationIDs = make(map[entity.Id]string) ge.cachedLabels = make(map[string]string) - user, err := repo.GetUserIdentity() - if err != nil { - return err - } - // preload all clients - err = ge.cacheAllClient(repo) - if err != nil { - return err - } - - ge.defaultClient, err = ge.getClientForIdentity(user.Id()) - if err != nil { - return err - } - - creds, err := auth.List(repo, auth.WithUserId(user.Id()), auth.WithTarget(target), auth.WithKind(auth.KindToken)) + err := ge.cacheAllClient(repo) if err != nil { return err } - if len(creds) == 0 { - return ErrMissingIdentityToken - } - - ge.defaultToken = creds[0].(*auth.Token) - return nil } -func (ge *githubExporter) cacheAllClient(repo repository.RepoConfig) error { +func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { return err } for _, cred := range creds { + login, ok := cred.Metadata()[auth.MetaKeyLogin] + if !ok { + _, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with Github login\n", cred.ID().Human()) + continue + } + + user, err := repo.ResolveIdentityImmutableMetadata(metaKeyGithubLogin, login) + if err == identity.ErrIdentityNotExist { + continue + } + if _, ok := ge.identityClient[cred.UserId()]; !ok { client := buildClient(creds[0].(*auth.Token)) ge.identityClient[cred.UserId()] = client diff --git a/bridge/github/import.go b/bridge/github/import.go index 39aebccb..aac4f119 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -12,7 +12,6 @@ import ( "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/util/text" ) @@ -39,20 +38,7 @@ type githubImporter struct { func (gi *githubImporter) Init(repo *cache.RepoCache, conf core.Configuration) error { gi.conf = conf - opts := []auth.Option{ - auth.WithTarget(target), - auth.WithKind(auth.KindToken), - } - - user, err := repo.GetUserIdentity() - if err == nil { - opts = append(opts, auth.WithUserId(user.Id())) - } - if err == identity.ErrNoIdentitySet { - opts = append(opts, auth.WithUserId(auth.DefaultUserId)) - } - - creds, err := auth.List(repo, opts...) + creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { return err } diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 57bab61e..75310ab3 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -140,13 +140,7 @@ func Test_Importer(t *testing.T) { t.Skip("Env var GITHUB_TOKEN_PRIVATE missing") } - err = author.Commit(repo) - require.NoError(t, err) - - err = identity.SetUserIdentity(repo, author) - require.NoError(t, err) - - token := auth.NewToken(author.Id(), envToken, target) + token := auth.NewToken(envToken, target) err = auth.Store(repo, token) require.NoError(t, err) -- cgit From b950c2580dfbf7672ee9e5e1e3f5b45c8cbc2788 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 14 Jan 2020 22:02:35 +0100 Subject: more wip --- bridge/github/config.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'bridge') diff --git a/bridge/github/config.go b/bridge/github/config.go index fcb94079..30e28d0d 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -22,6 +22,7 @@ import ( "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" @@ -81,7 +82,18 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor login := params.Login if login == "" { - login, err = input.Prompt("Github login", "login", input.Required, validateUsername) + 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 } @@ -128,6 +140,28 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, err } + // TODO + func(login string) error { + // if no user exist with the given login + _, err := repo.ResolveIdentityLogin(login) + if err != nil && err != identity.ErrIdentityNotExist { + return err + } + + // tag the default user with the github login, if any + // if not, + user, err := repo.GetUserIdentity() + if err == identity.ErrNoIdentitySet { + return nil + } + if err != nil { + return err + } + + repo.GetUserIdentity() + + }(login) + // Todo: if no user exist with the given login // - tag the default user with the github login // - add a command to manually tag a user ? -- cgit From 8da522d97af3dcaca8a8424e3541705c69779d6f Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 21 Jan 2020 18:49:33 +0100 Subject: wip --- bridge/github/config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'bridge') diff --git a/bridge/github/config.go b/bridge/github/config.go index 30e28d0d..40653afa 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -149,7 +149,6 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } // tag the default user with the github login, if any - // if not, user, err := repo.GetUserIdentity() if err == identity.ErrNoIdentitySet { return nil @@ -158,7 +157,10 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return err } - repo.GetUserIdentity() + userLogin, ok := user.ImmutableMetadata()[metaKeyGithubLogin] + if !ok { + user.SetMetadata() + } }(login) -- cgit From 74e91144105790cc997c1d79a7f638e1e3a1f3f8 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Fri, 24 Jan 2020 00:30:13 +0100 Subject: more more wip --- bridge/core/auth/credential.go | 6 ++++++ bridge/core/auth/credential_test.go | 2 +- bridge/core/auth/options.go | 2 +- bridge/core/auth/token.go | 13 +++++++++++- bridge/core/config.go | 42 +++++++++++++++++++++++++++++++++++++ bridge/github/config.go | 37 ++++---------------------------- bridge/github/export.go | 41 ++++++++++++++++++++++++++++++------ bridge/github/export_test.go | 2 +- bridge/github/import.go | 2 -- bridge/launchpad/import.go | 1 - 10 files changed, 102 insertions(+), 46 deletions(-) (limited to 'bridge') diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index e843ede7..c1255aa6 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -40,7 +40,10 @@ type Credential interface { Kind() CredentialKind CreateTime() time.Time Validate() error + Metadata() map[string]string + GetMetadata(key string) (string, bool) + SetMetadata(key string, value string) // Return all the specific properties of the credential that need to be saved into the configuration. // This does not include Target, Kind, CreateTime and Metadata. @@ -124,6 +127,9 @@ func metaFromConfig(configs map[string]string) map[string]string { result[key] = val } } + if len(result) == 0 { + return nil + } return result } diff --git a/bridge/core/auth/credential_test.go b/bridge/core/auth/credential_test.go index 49c138cf..2f8806c9 100644 --- a/bridge/core/auth/credential_test.go +++ b/bridge/core/auth/credential_test.go @@ -63,7 +63,7 @@ func TestCredential(t *testing.T) { // Metadata - token4.Metadata()["key"] = "value" + token4.SetMetadata("key", "value") err = Store(repo, token4) assert.NoError(t, err) diff --git a/bridge/core/auth/options.go b/bridge/core/auth/options.go index 0c780dc1..74189874 100644 --- a/bridge/core/auth/options.go +++ b/bridge/core/auth/options.go @@ -26,7 +26,7 @@ func (opts *options) Match(cred Credential) bool { } for key, val := range opts.meta { - if v, ok := cred.Metadata()[key]; !ok || v != val { + if v, ok := cred.GetMetadata(key); !ok || v != val { return false } } diff --git a/bridge/core/auth/token.go b/bridge/core/auth/token.go index 60137cd9..42f960bf 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -30,7 +30,6 @@ func NewToken(value, target string) *Token { target: target, createTime: time.Now(), Value: value, - meta: make(map[string]string), } } @@ -88,6 +87,18 @@ func (t *Token) Metadata() map[string]string { return t.meta } +func (t *Token) GetMetadata(key string) (string, bool) { + val, ok := t.meta[key] + return val, ok +} + +func (t *Token) SetMetadata(key string, value string) { + if t.meta == nil { + t.meta = make(map[string]string) + } + t.meta[key] = value +} + func (t *Token) toConfig() map[string]string { return map[string]string{ tokenValueKey: t.Value, diff --git a/bridge/core/config.go b/bridge/core/config.go index 9a8bc959..adee5f08 100644 --- a/bridge/core/config.go +++ b/bridge/core/config.go @@ -1 +1,43 @@ package core + +import ( + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/identity" +) + +func FinishConfig(repo *cache.RepoCache, metaKey string, login string) error { + // if no user exist with the given login metadata + _, err := repo.ResolveIdentityImmutableMetadata(metaKey, login) + if err != nil && err != identity.ErrIdentityNotExist { + // real error + return err + } + if err == nil { + // found an already valid user, all good + return nil + } + + // if a default user exist, tag it with the login + user, err := repo.GetUserIdentity() + if err != nil && err != identity.ErrIdentityNotExist { + // real error + return err + } + if err == nil { + // found one + user.SetMetadata(metaKey, login) + return user.CommitAsNeeded() + } + + // otherwise create a user with that metadata + i, err := repo.NewIdentityFromGitUserRaw(map[string]string{ + metaKey: login, + }) + + err = repo.SetUserIdentity(i) + if err != nil { + return err + } + + return nil +} diff --git a/bridge/github/config.go b/bridge/github/config.go index 40653afa..9ede72d4 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -22,7 +22,6 @@ import ( "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" @@ -109,7 +108,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } case params.TokenRaw != "": cred = auth.NewToken(params.TokenRaw, target) - cred.Metadata()[auth.MetaKeyLogin] = login + cred.SetMetadata(auth.MetaKeyLogin, login) default: cred, err = promptTokenOptions(repo, login, owner, project) if err != nil { @@ -140,34 +139,6 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, err } - // TODO - func(login string) error { - // if no user exist with the given login - _, err := repo.ResolveIdentityLogin(login) - if err != nil && err != identity.ErrIdentityNotExist { - return err - } - - // tag the default user with the github login, if any - user, err := repo.GetUserIdentity() - if err == identity.ErrNoIdentitySet { - return nil - } - if err != nil { - return err - } - - userLogin, ok := user.ImmutableMetadata()[metaKeyGithubLogin] - if !ok { - user.SetMetadata() - } - - }(login) - - // Todo: if no user exist with the given login - // - tag the default user with the github login - // - add a command to manually tag a user ? - // don't forget to store the now known valid token if !auth.IdExist(repo, cred.ID()) { err = auth.Store(repo, cred) @@ -176,7 +147,7 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } } - return conf, nil + return conf, core.FinishConfig(repo, metaKeyGithubLogin, login) } func (*Github) ValidateConfig(conf core.Configuration) error { @@ -318,7 +289,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string return nil, err } token := auth.NewToken(value, target) - token.Metadata()[auth.MetaKeyLogin] = login + token.SetMetadata(auth.MetaKeyLogin, login) return token, nil case 2: value, err := loginAndRequestToken(login, owner, project) @@ -326,7 +297,7 @@ func promptTokenOptions(repo repository.RepoConfig, login, owner, project string return nil, err } token := auth.NewToken(value, target) - token.Metadata()[auth.MetaKeyLogin] = login + token.SetMetadata(auth.MetaKeyLogin, login) return token, nil default: return creds[index-3], nil diff --git a/bridge/github/export.go b/bridge/github/export.go index 1cc66dee..663361f5 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -21,7 +21,6 @@ import ( "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/identity" - "github.com/MichaelMure/git-bug/repository" ) var ( @@ -35,6 +34,13 @@ type githubExporter struct { // cache identities clients identityClient map[entity.Id]*githubv4.Client + // the client to use for non user-specific queries + // should be the client of the default user + defaultClient *githubv4.Client + + // the token of the default user + defaultToken *auth.Token + // github repository ID repositoryID string @@ -53,12 +59,34 @@ func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) e ge.cachedOperationIDs = make(map[entity.Id]string) ge.cachedLabels = make(map[string]string) + user, err := repo.GetUserIdentity() + if err != nil { + return err + } + // preload all clients - err := ge.cacheAllClient(repo) + err = ge.cacheAllClient(repo) if err != nil { return err } + ge.defaultClient, err = ge.getClientForIdentity(user.Id()) + if err != nil { + return err + } + + login := user.ImmutableMetadata()[metaKeyGithubLogin] + creds, err := auth.List(repo, auth.WithMeta(metaKeyGithubLogin, login), auth.WithTarget(target), auth.WithKind(auth.KindToken)) + if err != nil { + return err + } + + if len(creds) == 0 { + return ErrMissingIdentityToken + } + + ge.defaultToken = creds[0].(*auth.Token) + return nil } @@ -69,7 +97,7 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { } for _, cred := range creds { - login, ok := cred.Metadata()[auth.MetaKeyLogin] + login, ok := cred.GetMetadata(auth.MetaKeyLogin) if !ok { _, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with Github login\n", cred.ID().Human()) continue @@ -80,9 +108,9 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { continue } - if _, ok := ge.identityClient[cred.UserId()]; !ok { + if _, ok := ge.identityClient[user.Id()]; !ok { client := buildClient(creds[0].(*auth.Token)) - ge.identityClient[cred.UserId()] = client + ge.identityClient[user.Id()] = client } } @@ -462,11 +490,12 @@ func (ge *githubExporter) cacheGithubLabels(ctx context.Context, gc *githubv4.Cl for hasNextPage { // create a new timeout context at each iteration ctx, cancel := context.WithTimeout(ctx, defaultTimeout) - defer cancel() if err := gc.Query(ctx, &q, variables); err != nil { + cancel() return err } + cancel() for _, label := range q.Repository.Labels.Nodes { ge.cachedLabels[label.Name] = label.ID diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index 5a0bc653..d2cfb1f9 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -176,7 +176,7 @@ func TestPushPull(t *testing.T) { return deleteRepository(projectName, envUser, envToken) }) - token := auth.NewToken(author.Id(), envToken, target) + token := auth.NewToken(envToken, target) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/github/import.go b/bridge/github/import.go index aac4f119..f2c9a53d 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -543,7 +543,6 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca i, err = repo.NewIdentityRaw( name, email, - string(actor.Login), string(actor.AvatarUrl), map[string]string{ metaKeyGithubLogin: string(actor.Login), @@ -590,7 +589,6 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache, return repo.NewIdentityRaw( name, "", - string(q.User.Login), string(q.User.AvatarUrl), map[string]string{ metaKeyGithubLogin: string(q.User.Login), diff --git a/bridge/launchpad/import.go b/bridge/launchpad/import.go index 619631b3..ecbf74f8 100644 --- a/bridge/launchpad/import.go +++ b/bridge/launchpad/import.go @@ -38,7 +38,6 @@ func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson) return repo.NewIdentityRaw( owner.Name, "", - owner.Login, "", map[string]string{ metaKeyLaunchpadLogin: owner.Login, -- cgit From f515b9a1291ddd3e4fe1b43bf5891ab19569e33f Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 4 Feb 2020 00:25:27 +0100 Subject: gitlab also compile --- bridge/github/config.go | 2 +- bridge/github/export.go | 5 +- bridge/github/export_test.go | 3 + bridge/github/import_test.go | 5 ++ bridge/gitlab/config.go | 159 +++++++++++++++++++------------------------ bridge/gitlab/export.go | 24 +++++-- bridge/gitlab/export_test.go | 5 +- bridge/gitlab/import.go | 17 +---- bridge/gitlab/import_test.go | 11 ++- bridge/launchpad/config.go | 26 +------ 10 files changed, 113 insertions(+), 144 deletions(-) (limited to 'bridge') diff --git a/bridge/github/config.go b/bridge/github/config.go index 9ede72d4..ed32f398 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -316,7 +316,7 @@ func promptToken() (string, error) { fmt.Println(" - 'repo' : to be able to read private repositories") fmt.Println() - re, err := regexp.Compile(`^[a-zA-Z0-9]{40}`) + re, err := regexp.Compile(`^[a-zA-Z0-9]{40}$`) if err != nil { panic("regexp compile:" + err.Error()) } diff --git a/bridge/github/export.go b/bridge/github/export.go index 663361f5..1e27be4a 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -99,7 +99,7 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { for _, cred := range creds { login, ok := cred.GetMetadata(auth.MetaKeyLogin) if !ok { - _, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with Github login\n", cred.ID().Human()) + _, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with a Github login\n", cred.ID().Human()) continue } @@ -107,6 +107,9 @@ func (ge *githubExporter) cacheAllClient(repo *cache.RepoCache) error { if err == identity.ErrIdentityNotExist { continue } + if err != nil { + return nil + } if _, ok := ge.identityClient[user.Id()]; !ok { client := buildClient(creds[0].(*auth.Token)) diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index d2cfb1f9..cb6bacc1 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -144,8 +144,10 @@ func TestPushPull(t *testing.T) { require.NoError(t, err) // set author identity + login := "identity-test" author, err := backend.NewIdentity("test identity", "test@test.org") require.NoError(t, err) + author.SetMetadata(metaKeyGithubLogin, login) err = backend.SetUserIdentity(author) require.NoError(t, err) @@ -177,6 +179,7 @@ func TestPushPull(t *testing.T) { }) token := auth.NewToken(envToken, target) + token.SetMetadata(metaKeyGithubLogin, login) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 75310ab3..73eaa096 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -21,6 +21,7 @@ import ( func Test_Importer(t *testing.T) { author := identity.NewIdentity("Michael Muré", "batolettre@gmail.com") + tests := []struct { name string url string @@ -140,7 +141,11 @@ func Test_Importer(t *testing.T) { t.Skip("Env var GITHUB_TOKEN_PRIVATE missing") } + login := "test-identity" + author.SetMetadata(metaKeyGithubLogin, login) + token := auth.NewToken(envToken, target) + token.SetMetadata(metaKeyGithubLogin, login) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 0758074c..36daaa68 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -19,8 +19,7 @@ import ( "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" - "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/colors" ) @@ -36,14 +35,12 @@ 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 - - if (params.CredPrefix != "" || params.TokenRaw != "") && params.URL == "" { - return nil, fmt.Errorf("you must provide a project URL to configure this bridge with a token") - } - var baseUrl string switch { @@ -74,17 +71,6 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor return nil, fmt.Errorf("base URL (%s) doesn't match the project URL (%s)", params.BaseURL, url) } - user, err := repo.GetUserIdentity() - if err != nil && err != identity.ErrNoIdentitySet { - return nil, err - } - - // default to a "to be filled" user Id if we don't have a valid one yet - userId := auth.DefaultUserId - if user != nil { - userId = user.Id() - } - var cred auth.Credential switch { @@ -93,13 +79,16 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if err != nil { return nil, err } - if user != nil && cred.UserId() != user.Id() { - return nil, fmt.Errorf("selected credential don't match the user") - } case params.TokenRaw != "": - cred = auth.NewToken(userId, params.TokenRaw, target) + token := auth.NewToken(params.TokenRaw, target) + login, err := getLoginFromToken(baseUrl, token) + if err != nil { + return nil, err + } + token.SetMetadata(auth.MetaKeyLogin, login) + cred = token default: - cred, err = promptTokenOptions(repo, userId, baseUrl) + cred, err = promptTokenOptions(repo, baseUrl) if err != nil { return nil, err } @@ -153,64 +142,41 @@ func (g *Gitlab) ValidateConfig(conf core.Configuration) error { } 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 := input.PromptChoice("Gitlab base url", []string{ + "https://gitlab.com", + "enter your own base url", + }) - index, err := strconv.Atoi(line) - if err != nil || index < 0 || index > 1 { - fmt.Println("invalid input") - continue - } + if err != nil { + return "", err + } - switch index { - case 0: - return defaultBaseURL, nil - case 1: - return promptBaseUrl() - } + if index == 0 { + return defaultBaseURL, nil + } else { + return promptBaseUrl() } } func promptBaseUrl() (string, error) { - for { - fmt.Print("Base url: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') + validator := func(name string, value string) (string, error) { + u, err := url.Parse(value) if err != nil { - return "", err + return err.Error(), nil } - - line = strings.TrimSpace(line) - - ok, err := validateBaseUrl(line) - if err != nil { - return "", err + if u.Scheme == "" { + return "missing scheme", nil } - if ok { - return line, nil + if u.Host == "" { + return "missing host", nil } + return "", nil } -} -func validateBaseUrl(baseUrl string) (bool, error) { - u, err := url.Parse(baseUrl) - if err != nil { - return false, err - } - return u.Scheme != "" && u.Host != "", nil + return input.Prompt("Base url", "url", input.Required, validator) } -func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, baseUrl string) (auth.Credential, error) { +func promptTokenOptions(repo repository.RepoConfig, baseUrl string) (auth.Credential, error) { for { creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { @@ -219,11 +185,7 @@ func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, baseUrl st // if we don't have existing token, fast-track to the token prompt if len(creds) == 0 { - value, err := promptToken(baseUrl) - if err != nil { - return nil, err - } - return auth.NewToken(userId, value, target), nil + return promptToken(baseUrl) } fmt.Println() @@ -261,44 +223,44 @@ func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, baseUrl st switch index { case 1: - value, err := promptToken(baseUrl) - if err != nil { - return nil, err - } - return auth.NewToken(userId, value, target), nil + return promptToken(baseUrl) default: return creds[index-2], nil } } } -func promptToken(baseUrl string) (string, error) { +func promptToken(baseUrl string) (*auth.Token, error) { fmt.Printf("You can generate a new token by visiting %s.\n", path.Join(baseUrl, "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}`) + re, err := regexp.Compile(`^[a-zA-Z0-9\-\_]{20}$`) if err != nil { panic("regexp compile:" + err.Error()) } - for { - fmt.Print("Enter token: ") + var login string - line, err := bufio.NewReader(os.Stdin).ReadString('\n') + validator := func(name string, value string) (complaint string, err error) { + if !re.MatchString(value) { + return "token has incorrect format", nil + } + login, err = getLoginFromToken(baseUrl, auth.NewToken(value, target)) if err != nil { - return "", err + return fmt.Sprintf("token is invalid: %v", err), nil } + return "", nil + } - token := strings.TrimSpace(line) - if re.MatchString(token) { - return token, nil - } + rawToken, err := input.Prompt("Enter token", "token", input.Required, validator) - fmt.Println("token has incorrect format") - } + token := auth.NewToken(rawToken, target) + token.SetMetadata(auth.MetaKeyLogin, login) + + return token, nil } func promptURL(repo repository.RepoCommon, baseUrl string) (string, error) { @@ -408,8 +370,25 @@ func validateProjectURL(baseUrl, url string, token *auth.Token) (int, error) { project, _, err := client.Projects.GetProject(projectPath, &gitlab.GetProjectOptions{}) if err != nil { - return 0, errors.Wrap(err, "wrong token scope ou inexistent project") + return 0, errors.Wrap(err, "wrong token scope ou non-existent project") } return project.ID, nil } + +func getLoginFromToken(baseUrl string, token *auth.Token) (string, error) { + client, err := buildClient(baseUrl, token) + if err != nil { + return "", err + } + + user, _, err := client.Users.CurrentUser() + if err != nil { + return "", err + } + if user.Username == "" { + return "", fmt.Errorf("gitlab say username is empty") + } + + return user.Username, nil +} diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go index 2ba149a2..c5323da4 100644 --- a/bridge/gitlab/export.go +++ b/bridge/gitlab/export.go @@ -3,6 +3,7 @@ package gitlab import ( "context" "fmt" + "os" "strconv" "time" @@ -14,7 +15,7 @@ import ( "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/repository" + "github.com/MichaelMure/git-bug/identity" ) var ( @@ -54,20 +55,33 @@ func (ge *gitlabExporter) Init(repo *cache.RepoCache, conf core.Configuration) e return nil } -func (ge *gitlabExporter) cacheAllClient(repo repository.RepoConfig) error { +func (ge *gitlabExporter) cacheAllClient(repo *cache.RepoCache) error { creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { return err } for _, cred := range creds { - if _, ok := ge.identityClient[cred.UserId()]; !ok { + login, ok := cred.GetMetadata(auth.MetaKeyLogin) + if !ok { + _, _ = fmt.Fprintf(os.Stderr, "credential %s is not tagged with a Gitlab login\n", cred.ID().Human()) + continue + } + + user, err := repo.ResolveIdentityImmutableMetadata(metaKeyGitlabLogin, login) + if err == identity.ErrIdentityNotExist { + continue + } + if err != nil { + return nil + } + + if _, ok := ge.identityClient[user.Id()]; !ok { client, err := buildClient(ge.conf[keyGitlabBaseUrl], creds[0].(*auth.Token)) if err != nil { return err } - - ge.identityClient[cred.UserId()] = client + ge.identityClient[user.Id()] = client } } diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index d16defd0..50dbd04a 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -149,8 +149,10 @@ func TestPushPull(t *testing.T) { require.NoError(t, err) // set author identity + login := "test-identity" author, err := backend.NewIdentity("test identity", "test@test.org") require.NoError(t, err) + author.SetMetadata(metaKeyGitlabLogin, login) err = backend.SetUserIdentity(author) require.NoError(t, err) @@ -160,7 +162,8 @@ func TestPushPull(t *testing.T) { tests := testCases(t, backend) - token := auth.NewToken(author.Id(), envToken, target) + token := auth.NewToken(envToken, target) + token.SetMetadata(metaKeyGitlabLogin, login) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index fa6bbfb6..d699554b 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -13,7 +13,6 @@ import ( "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/entity" - "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/util/text" ) @@ -34,20 +33,7 @@ type gitlabImporter struct { func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) error { gi.conf = conf - opts := []auth.Option{ - auth.WithTarget(target), - auth.WithKind(auth.KindToken), - } - - user, err := repo.GetUserIdentity() - if err == nil { - opts = append(opts, auth.WithUserId(user.Id())) - } - if err == identity.ErrNoIdentitySet { - opts = append(opts, auth.WithUserId(auth.DefaultUserId)) - } - - creds, err := auth.List(repo, opts...) + creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { return err } @@ -403,7 +389,6 @@ func (gi *gitlabImporter) ensurePerson(repo *cache.RepoCache, id int) (*cache.Id i, err = repo.NewIdentityRaw( user.Name, user.PublicEmail, - user.Username, user.AvatarURL, map[string]string{ // because Gitlab diff --git a/bridge/gitlab/import_test.go b/bridge/gitlab/import_test.go index 1e2f5d50..3c0caa55 100644 --- a/bridge/gitlab/import_test.go +++ b/bridge/gitlab/import_test.go @@ -21,6 +21,7 @@ import ( func TestImport(t *testing.T) { author := identity.NewIdentity("Amine Hilaly", "hilalyamine@gmail.com") + tests := []struct { name string url string @@ -94,13 +95,11 @@ func TestImport(t *testing.T) { t.Skip("Env var GITLAB_PROJECT_ID missing") } - err = author.Commit(repo) - require.NoError(t, err) - - err = identity.SetUserIdentity(repo, author) - require.NoError(t, err) + login := "test-identity" + author.SetMetadata(metaKeyGitlabLogin, login) - token := auth.NewToken(author.Id(), envToken, target) + token := auth.NewToken(envToken, target) + token.SetMetadata(metaKeyGitlabLogin, login) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/launchpad/config.go b/bridge/launchpad/config.go index edbd941d..674aff00 100644 --- a/bridge/launchpad/config.go +++ b/bridge/launchpad/config.go @@ -1,17 +1,15 @@ package launchpad import ( - "bufio" "errors" "fmt" "net/http" - "os" "regexp" - "strings" "time" "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/input" ) var ErrBadProjectURL = errors.New("bad Launchpad project URL") @@ -45,7 +43,7 @@ func (l *Launchpad) Configure(repo *cache.RepoCache, params core.BridgeParams) ( project, err = splitURL(params.URL) default: // get project name from terminal prompt - project, err = promptProjectName() + project, err = input.Prompt("Launchpad project name", "project name", input.Required) } if err != nil { @@ -86,26 +84,6 @@ func (*Launchpad) ValidateConfig(conf core.Configuration) error { return nil } -func promptProjectName() (string, error) { - for { - fmt.Print("Launchpad project name: ") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err - } - - line = strings.TrimRight(line, "\n") - - if line == "" { - fmt.Println("Project name is empty") - continue - } - - return line, nil - } -} - func validateProject(project string) (bool, error) { url := fmt.Sprintf("%s/%s", apiRoot, project) -- cgit From 646fd681ffba59d4a74ec3d99558d2ed70b0106b Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 4 Feb 2020 22:05:34 +0100 Subject: it compiles \o/ --- bridge/bridges.go | 7 +++++++ bridge/core/bridge.go | 17 +++++++++++++++++ bridge/core/interfaces.go | 5 +++++ bridge/github/config.go | 9 --------- bridge/github/export.go | 2 +- bridge/github/export_test.go | 2 +- bridge/github/github.go | 21 +++++++++++++++++++++ bridge/github/import.go | 6 ------ bridge/github/import_test.go | 2 +- bridge/gitlab/gitlab.go | 6 ++++++ bridge/launchpad/config.go | 7 ------- bridge/launchpad/import.go | 5 ----- bridge/launchpad/launchpad.go | 19 +++++++++++++++++++ 13 files changed, 78 insertions(+), 30 deletions(-) (limited to 'bridge') diff --git a/bridge/bridges.go b/bridge/bridges.go index a306fe5d..5d3066f9 100644 --- a/bridge/bridges.go +++ b/bridge/bridges.go @@ -21,6 +21,13 @@ func Targets() []string { return core.Targets() } +// LoginMetaKey return the metadata key used to store the remote bug-tracker login +// on the user identity. The corresponding value is used to match identities and +// credentials. +func LoginMetaKey(target string) (string, error) { + return core.LoginMetaKey(target) +} + // Instantiate a new Bridge for a repo, from the given target and name func NewBridge(repo *cache.RepoCache, target string, name string) (*core.Bridge, error) { return core.NewBridge(repo, target, name) diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go index 7891763f..ac0d47d7 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -28,6 +28,7 @@ const ( ) var bridgeImpl map[string]reflect.Type +var bridgeLoginMetaKey map[string]string // BridgeParams holds parameters to simplify the bridge configuration without // having to make terminal prompts. @@ -59,7 +60,11 @@ func Register(impl BridgeImpl) { if bridgeImpl == nil { bridgeImpl = make(map[string]reflect.Type) } + if bridgeLoginMetaKey == nil { + bridgeLoginMetaKey = make(map[string]string) + } bridgeImpl[impl.Target()] = reflect.TypeOf(impl) + bridgeLoginMetaKey[impl.Target()] = impl.LoginMetaKey() } // Targets return all known bridge implementation target @@ -81,6 +86,18 @@ func TargetExist(target string) bool { return ok } +// LoginMetaKey return the metadata key used to store the remote bug-tracker login +// on the user identity. The corresponding value is used to match identities and +// credentials. +func LoginMetaKey(target string) (string, error) { + metaKey, ok := bridgeLoginMetaKey[target] + if !ok { + return "", fmt.Errorf("unknown bridge target %v", target) + } + + return metaKey, nil +} + // Instantiate a new Bridge for a repo, from the given target and name func NewBridge(repo *cache.RepoCache, target string, name string) (*Bridge, error) { implType, ok := bridgeImpl[target] diff --git a/bridge/core/interfaces.go b/bridge/core/interfaces.go index 77e0a7b9..ab2f3977 100644 --- a/bridge/core/interfaces.go +++ b/bridge/core/interfaces.go @@ -13,6 +13,11 @@ type BridgeImpl interface { // Target return the target of the bridge (e.g.: "github") Target() string + // LoginMetaKey return the metadata key used to store the remote bug-tracker login + // on the user identity. The corresponding value is used to match identities and + // credentials. + LoginMetaKey() string + // Configure handle the user interaction and return a key/value configuration // for future use Configure(repo *cache.RepoCache, params BridgeParams) (Configuration, error) diff --git a/bridge/github/config.go b/bridge/github/config.go index ed32f398..9477801d 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -27,15 +27,6 @@ import ( "github.com/MichaelMure/git-bug/util/colors" ) -const ( - target = "github" - githubV3Url = "https://api.github.com" - keyOwner = "owner" - keyProject = "project" - - defaultTimeout = 60 * time.Second -) - var ( ErrBadProjectURL = errors.New("bad project url") ) diff --git a/bridge/github/export.go b/bridge/github/export.go index 1e27be4a..c363e188 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -76,7 +76,7 @@ func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) e } login := user.ImmutableMetadata()[metaKeyGithubLogin] - creds, err := auth.List(repo, auth.WithMeta(metaKeyGithubLogin, login), auth.WithTarget(target), auth.WithKind(auth.KindToken)) + creds, err := auth.List(repo, auth.WithMeta(auth.MetaKeyLogin, login), auth.WithTarget(target), auth.WithKind(auth.KindToken)) if err != nil { return err } diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index cb6bacc1..89040d8f 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -179,7 +179,7 @@ func TestPushPull(t *testing.T) { }) token := auth.NewToken(envToken, target) - token.SetMetadata(metaKeyGithubLogin, login) + token.SetMetadata(auth.MetaKeyLogin, login) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/github/github.go b/bridge/github/github.go index 874c2d11..19dc8a08 100644 --- a/bridge/github/github.go +++ b/bridge/github/github.go @@ -3,6 +3,7 @@ package github import ( "context" + "time" "github.com/shurcooL/githubv4" "golang.org/x/oauth2" @@ -11,12 +12,32 @@ import ( "github.com/MichaelMure/git-bug/bridge/core/auth" ) +const ( + target = "github" + + metaKeyGithubId = "github-id" + metaKeyGithubUrl = "github-url" + metaKeyGithubLogin = "github-login" + + keyOwner = "owner" + keyProject = "project" + + githubV3Url = "https://api.github.com" + defaultTimeout = 60 * time.Second +) + +var _ core.BridgeImpl = &Github{} + type Github struct{} func (*Github) Target() string { return target } +func (g *Github) LoginMetaKey() string { + return metaKeyGithubLogin +} + func (*Github) NewImporter() core.Importer { return &githubImporter{} } diff --git a/bridge/github/import.go b/bridge/github/import.go index f2c9a53d..6a4c0110 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -15,12 +15,6 @@ import ( "github.com/MichaelMure/git-bug/util/text" ) -const ( - metaKeyGithubId = "github-id" - metaKeyGithubUrl = "github-url" - metaKeyGithubLogin = "github-login" -) - // githubImporter implement the Importer interface type githubImporter struct { conf core.Configuration diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 73eaa096..a8f8e346 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -145,7 +145,7 @@ func Test_Importer(t *testing.T) { author.SetMetadata(metaKeyGithubLogin, login) token := auth.NewToken(envToken, target) - token.SetMetadata(metaKeyGithubLogin, login) + token.SetMetadata(auth.MetaKeyLogin, login) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/gitlab/gitlab.go b/bridge/gitlab/gitlab.go index 9298dc8e..8512379c 100644 --- a/bridge/gitlab/gitlab.go +++ b/bridge/gitlab/gitlab.go @@ -26,12 +26,18 @@ const ( defaultTimeout = 60 * time.Second ) +var _ core.BridgeImpl = &Gitlab{} + type Gitlab struct{} func (*Gitlab) Target() string { return target } +func (g *Gitlab) LoginMetaKey() string { + return metaKeyGitlabLogin +} + func (*Gitlab) NewImporter() core.Importer { return &gitlabImporter{} } diff --git a/bridge/launchpad/config.go b/bridge/launchpad/config.go index 674aff00..e029fad3 100644 --- a/bridge/launchpad/config.go +++ b/bridge/launchpad/config.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" "regexp" - "time" "github.com/MichaelMure/git-bug/bridge/core" "github.com/MichaelMure/git-bug/cache" @@ -14,12 +13,6 @@ import ( var ErrBadProjectURL = errors.New("bad Launchpad project URL") -const ( - target = "launchpad-preview" - keyProject = "project" - defaultTimeout = 60 * time.Second -) - func (l *Launchpad) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) { if params.TokenRaw != "" { fmt.Println("warning: token params are ineffective for a Launchpad bridge") diff --git a/bridge/launchpad/import.go b/bridge/launchpad/import.go index ecbf74f8..5bca8e63 100644 --- a/bridge/launchpad/import.go +++ b/bridge/launchpad/import.go @@ -20,11 +20,6 @@ func (li *launchpadImporter) Init(repo *cache.RepoCache, conf core.Configuration return nil } -const ( - metaKeyLaunchpadID = "launchpad-id" - metaKeyLaunchpadLogin = "launchpad-login" -) - func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson) (*cache.IdentityCache, error) { // Look first in the cache i, err := repo.ResolveIdentityImmutableMetadata(metaKeyLaunchpadLogin, owner.Login) diff --git a/bridge/launchpad/launchpad.go b/bridge/launchpad/launchpad.go index 030d9169..b4fcdd00 100644 --- a/bridge/launchpad/launchpad.go +++ b/bridge/launchpad/launchpad.go @@ -2,15 +2,34 @@ package launchpad import ( + "time" + "github.com/MichaelMure/git-bug/bridge/core" ) +const ( + target = "launchpad-preview" + + metaKeyLaunchpadID = "launchpad-id" + metaKeyLaunchpadLogin = "launchpad-login" + + keyProject = "project" + + defaultTimeout = 60 * time.Second +) + +var _ core.BridgeImpl = &Launchpad{} + type Launchpad struct{} func (*Launchpad) Target() string { return "launchpad-preview" } +func (l *Launchpad) LoginMetaKey() string { + return metaKeyLaunchpadLogin +} + func (*Launchpad) NewImporter() core.Importer { return &launchpadImporter{} } -- cgit From 2e7ac569ad3146e5fa22907d8a4e63d2a524533b Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 8 Feb 2020 17:35:35 +0100 Subject: fix tests ? --- bridge/github/export_test.go | 12 ++++++------ bridge/gitlab/export_test.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'bridge') diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index 89040d8f..36d43173 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -155,6 +155,11 @@ func TestPushPull(t *testing.T) { defer backend.Close() interrupt.RegisterCleaner(backend.Close) + token := auth.NewToken(envToken, target) + token.SetMetadata(auth.MetaKeyLogin, login) + err = auth.Store(repo, token) + require.NoError(t, err) + tests := testCases(t, backend) // generate project name @@ -178,11 +183,6 @@ func TestPushPull(t *testing.T) { return deleteRepository(projectName, envUser, envToken) }) - token := auth.NewToken(envToken, target) - token.SetMetadata(auth.MetaKeyLogin, login) - err = auth.Store(repo, token) - require.NoError(t, err) - // initialize exporter exporter := &githubExporter{} err = exporter.Init(backend, core.Configuration{ @@ -258,7 +258,7 @@ func TestPushPull(t *testing.T) { // verify bug have same number of original operations require.Len(t, importedBug.Snapshot().Operations, tt.numOrOp) - // verify bugs are taged with origin=github + // verify bugs are tagged with origin=github issueOrigin, ok := importedBug.Snapshot().GetCreateMetadata(core.MetaKeyOrigin) require.True(t, ok) require.Equal(t, issueOrigin, target) diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index 50dbd04a..8410ebba 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -160,13 +160,13 @@ func TestPushPull(t *testing.T) { defer backend.Close() interrupt.RegisterCleaner(backend.Close) - tests := testCases(t, backend) - token := auth.NewToken(envToken, target) - token.SetMetadata(metaKeyGitlabLogin, login) + token.SetMetadata(auth.MetaKeyLogin, login) err = auth.Store(repo, token) require.NoError(t, err) + tests := testCases(t, backend) + // generate project name projectName := generateRepoName() @@ -263,7 +263,7 @@ func TestPushPull(t *testing.T) { // verify bug have same number of original operations require.Len(t, importedBug.Snapshot().Operations, tt.numOpImp) - // verify bugs are taged with origin=gitlab + // verify bugs are tagged with origin=gitlab issueOrigin, ok := importedBug.Snapshot().GetCreateMetadata(core.MetaKeyOrigin) require.True(t, ok) require.Equal(t, issueOrigin, target) -- cgit From 8773929f963b847841fac0d3c42d6278de69b79c Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 8 Feb 2020 22:04:25 +0100 Subject: github: make sure to have a name --- bridge/github/import.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'bridge') diff --git a/bridge/github/import.go b/bridge/github/import.go index 6a4c0110..ea0ccba3 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -534,6 +534,11 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca case "Bot": } + // Name is not necessarily set, fallback to login as a name is required in the identity + if name == "" { + name = string(actor.Login) + } + i, err = repo.NewIdentityRaw( name, email, -- cgit From a335725cc5f712f3f3089154afa96881284d4853 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 8 Feb 2020 22:04:51 +0100 Subject: bridge: fix wrong error used --- bridge/core/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bridge') diff --git a/bridge/core/config.go b/bridge/core/config.go index adee5f08..8d306ef6 100644 --- a/bridge/core/config.go +++ b/bridge/core/config.go @@ -19,7 +19,7 @@ func FinishConfig(repo *cache.RepoCache, metaKey string, login string) error { // if a default user exist, tag it with the login user, err := repo.GetUserIdentity() - if err != nil && err != identity.ErrIdentityNotExist { + if err != nil && err != identity.ErrNoIdentitySet { // real error return err } -- cgit From bef35d4c679de81a214c99a30916fa7ebfc3f6a8 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 8 Feb 2020 22:05:13 +0100 Subject: bridge: hopefully fix tests --- bridge/github/export_test.go | 2 ++ bridge/gitlab/export_test.go | 2 ++ 2 files changed, 4 insertions(+) (limited to 'bridge') diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go index 36d43173..7d6e6fb1 100644 --- a/bridge/github/export_test.go +++ b/bridge/github/export_test.go @@ -148,6 +148,8 @@ func TestPushPull(t *testing.T) { author, err := backend.NewIdentity("test identity", "test@test.org") require.NoError(t, err) author.SetMetadata(metaKeyGithubLogin, login) + err = author.Commit() + require.NoError(t, err) err = backend.SetUserIdentity(author) require.NoError(t, err) diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index 8410ebba..1d387655 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -153,6 +153,8 @@ func TestPushPull(t *testing.T) { author, err := backend.NewIdentity("test identity", "test@test.org") require.NoError(t, err) author.SetMetadata(metaKeyGitlabLogin, login) + err = author.Commit() + require.NoError(t, err) err = backend.SetUserIdentity(author) require.NoError(t, err) -- cgit From 9b1aaa032d36e1ac05504916e359f767d1622d9d Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 8 Feb 2020 22:08:35 +0100 Subject: bridge: fix 2 uncatched errors --- bridge/core/config.go | 3 +++ bridge/gitlab/config.go | 3 +++ 2 files changed, 6 insertions(+) (limited to 'bridge') diff --git a/bridge/core/config.go b/bridge/core/config.go index 8d306ef6..afcda560 100644 --- a/bridge/core/config.go +++ b/bridge/core/config.go @@ -33,6 +33,9 @@ func FinishConfig(repo *cache.RepoCache, metaKey string, login string) error { i, err := repo.NewIdentityFromGitUserRaw(map[string]string{ metaKey: login, }) + if err != nil { + return err + } err = repo.SetUserIdentity(i) if err != nil { diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 36daaa68..fb593819 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -256,6 +256,9 @@ func promptToken(baseUrl string) (*auth.Token, error) { } rawToken, err := input.Prompt("Enter token", "token", input.Required, validator) + if err != nil { + return nil, err + } token := auth.NewToken(rawToken, target) token.SetMetadata(auth.MetaKeyLogin, login) -- cgit