diff --git a/bridge/core/auth/credential.go b/bridge/core/auth/credential.go index a462a1163c945c9416f86a9bbd7c60524e2ca429..fd026c5d967354620b80f95b76a172899c038442 100644 --- a/bridge/core/auth/credential.go +++ b/bridge/core/auth/credential.go @@ -32,9 +32,15 @@ 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 @@ -42,7 +48,7 @@ type Credential interface { // 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. - ToConfig() map[string]string + toConfig() map[string]string } // Load loads a credential from the repo config @@ -90,6 +96,7 @@ func LoadWithPrefix(repo repository.RepoConfig, prefix string) (Credential, erro return matching[0], nil } +// loadFromConfig is a helper to construct a Credential from the set of git configs func loadFromConfig(rawConfigs map[string]string, id entity.Id) (Credential, error) { keyPrefix := fmt.Sprintf("%s.%s.", configKeyPrefix, id) @@ -168,7 +175,7 @@ func PrefixExist(repo repository.RepoConfig, prefix string) bool { // Store stores a credential in the global git config func Store(repo repository.RepoConfig, cred Credential) error { - confs := cred.ToConfig() + confs := cred.toConfig() prefix := fmt.Sprintf("%s.%s.", configKeyPrefix, cred.ID()) @@ -213,6 +220,25 @@ 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/token.go b/bridge/core/auth/token.go index 12a3bfc06bfb0cd4f5305caa1afd01482983426b..8333ef129035494557e681dc075f4c7539b7cfe0 100644 --- a/bridge/core/auth/token.go +++ b/bridge/core/auth/token.go @@ -59,6 +59,10 @@ 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 } @@ -88,7 +92,7 @@ func (t *Token) Validate() error { return nil } -func (t *Token) ToConfig() map[string]string { +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 c6731ff9a06ceeea306a1950bc62ebb517d3d3b6..1d0a468153277593bcef6a9a8178f2e418187ea3 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -43,13 +43,14 @@ type BridgeParams struct { // Bridge is a wrapper around a BridgeImpl that will bind low-level // implementation with utility code to provide high-level functions. type Bridge struct { - Name string - repo *cache.RepoCache - impl BridgeImpl - importer Importer - exporter Exporter - conf Configuration - initDone bool + Name string + repo *cache.RepoCache + impl BridgeImpl + importer Importer + exporter Exporter + conf Configuration + initImportDone bool + initExportDone bool } // Register will register a new BridgeImpl @@ -273,8 +274,25 @@ func (b *Bridge) getExporter() Exporter { return b.exporter } -func (b *Bridge) ensureInit() error { - if b.initDone { +func (b *Bridge) ensureImportInit() error { + if b.initImportDone { + return nil + } + + importer := b.getImporter() + if importer != nil { + err := importer.Init(b.repo, b.conf) + if err != nil { + return err + } + } + + b.initImportDone = true + return nil +} + +func (b *Bridge) ensureExportInit() error { + if b.initExportDone { return nil } @@ -294,8 +312,7 @@ func (b *Bridge) ensureInit() error { } } - b.initDone = true - + b.initExportDone = true return nil } @@ -313,7 +330,7 @@ func (b *Bridge) ImportAllSince(ctx context.Context, since time.Time) (<-chan Im return nil, err } - err = b.ensureInit() + err = b.ensureImportInit() if err != nil { return nil, err } @@ -367,7 +384,7 @@ func (b *Bridge) ExportAll(ctx context.Context, since time.Time) (<-chan ExportR return nil, err } - err = b.ensureInit() + err = b.ensureExportInit() if err != nil { return nil, err } diff --git a/bridge/github/config.go b/bridge/github/config.go index bc26a2fc740c6e01be8899405f25cbf263b3bd11..ea4b622f5f29f46631f82b2135195c3ff3d65189 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -25,6 +25,7 @@ import ( "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/repository" "github.com/MichaelMure/git-bug/util/colors" "github.com/MichaelMure/git-bug/util/interrupt" @@ -89,10 +90,16 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } user, err := repo.GetUserIdentity() - if err != nil { + 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 { @@ -101,13 +108,13 @@ func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if err != nil { return nil, err } - if cred.UserId() != user.Id() { + if user != nil && cred.UserId() != user.Id() { return nil, fmt.Errorf("selected credential don't match the user") } case params.TokenRaw != "": - cred = auth.NewToken(user.Id(), params.TokenRaw, target) + cred = auth.NewToken(userId, params.TokenRaw, target) default: - cred, err = promptTokenOptions(repo, user.Id(), owner, project) + cred, err = promptTokenOptions(repo, userId, owner, project) if err != nil { return nil, err } @@ -326,7 +333,7 @@ func promptToken() (string, error) { return token, nil } - fmt.Println("token is invalid") + fmt.Println("token has incorrect format") } } diff --git a/bridge/github/import.go b/bridge/github/import.go index 092e3e71fe88f4be2393dee46554e78eea7b1459..dfc851fded10d571eb38a9432ed058a63471d98a 100644 --- a/bridge/github/import.go +++ b/bridge/github/import.go @@ -12,6 +12,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/util/text" ) @@ -47,6 +48,9 @@ func (gi *githubImporter) Init(repo *cache.RepoCache, conf core.Configuration) e 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...) if err != nil { diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go index 304229a0e4544e88ff871f51c74d1bb3a8ea367b..57bab61ecb18ece6a14424b672b0f69d8e62cfaf 100644 --- a/bridge/github/import_test.go +++ b/bridge/github/import_test.go @@ -143,6 +143,9 @@ func Test_Importer(t *testing.T) { err = author.Commit(repo) require.NoError(t, err) + err = identity.SetUserIdentity(repo, author) + require.NoError(t, err) + token := auth.NewToken(author.Id(), envToken, target) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 99c27836dd87dedbbb020f6ae9db35f9df1be24c..3a48ec4ea22679cd8ff2ca874aba9e2012cca3ee 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -19,6 +19,7 @@ import ( "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/repository" "github.com/MichaelMure/git-bug/util/colors" ) @@ -65,10 +66,16 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor } user, err := repo.GetUserIdentity() - if err != nil { + 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 { @@ -77,13 +84,13 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor if err != nil { return nil, err } - if cred.UserId() != user.Id() { + if user != nil && cred.UserId() != user.Id() { return nil, fmt.Errorf("selected credential don't match the user") } case params.TokenRaw != "": - cred = auth.NewToken(user.Id(), params.TokenRaw, target) + cred = auth.NewToken(userId, params.TokenRaw, target) default: - cred, err = promptTokenOptions(repo, user.Id()) + cred, err = promptTokenOptions(repo, userId) if err != nil { return nil, err } @@ -221,7 +228,7 @@ func promptToken() (string, error) { return token, nil } - fmt.Println("token format is invalid") + fmt.Println("token has incorrect format") } } diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index 4fa375055d8e7aa9df95d60d91bf677f8c264f85..fa6bbfb66aa662a54bd11247388b1aa47f4e173d 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -13,6 +13,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/util/text" ) @@ -42,6 +43,9 @@ func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) e 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...) if err != nil { diff --git a/bridge/gitlab/import_test.go b/bridge/gitlab/import_test.go index 6e378b075432da5a0c7d4fdf22e3ba4bdc705562..1e2f5d502eb6fc52aefaa5d58ff77f974e347865 100644 --- a/bridge/gitlab/import_test.go +++ b/bridge/gitlab/import_test.go @@ -97,6 +97,9 @@ func TestImport(t *testing.T) { err = author.Commit(repo) require.NoError(t, err) + err = identity.SetUserIdentity(repo, author) + require.NoError(t, err) + token := auth.NewToken(author.Id(), envToken, target) err = auth.Store(repo, token) require.NoError(t, err) diff --git a/commands/bridge_auth.go b/commands/bridge_auth.go index 4e8b50c417b95bcff783828509cd2554d32d14ae..bfbab33cb362953f9dda793b567ee6765d03730d 100644 --- a/commands/bridge_auth.go +++ b/commands/bridge_auth.go @@ -37,14 +37,21 @@ func runBridgeAuth(cmd *cobra.Command, args []string) error { value = cred.Value } - user, err := backend.ResolveIdentity(cred.UserId()) - if err != nil { - return err - } - userFmt := user.DisplayName() + var userFmt string + + switch cred.UserId() { + case auth.DefaultUserId: + userFmt = colors.Red("default user") + default: + user, err := backend.ResolveIdentity(cred.UserId()) + if err != nil { + return err + } + userFmt = user.DisplayName() - if cred.UserId() == defaultUser.Id() { - userFmt = colors.Red(userFmt) + if cred.UserId() == defaultUser.Id() { + userFmt = colors.Red(userFmt) + } } fmt.Printf("%s %s %s %s %s\n", diff --git a/commands/bridge_auth_show.go b/commands/bridge_auth_show.go index 5352957dd9cf175b99969be0f7dd00be30d9f221..02c568064f7b3cfc5b768dbb16b594893183fccf 100644 --- a/commands/bridge_auth_show.go +++ b/commands/bridge_auth_show.go @@ -7,17 +7,46 @@ import ( "github.com/spf13/cobra" "github.com/MichaelMure/git-bug/bridge/core/auth" + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/util/colors" + "github.com/MichaelMure/git-bug/util/interrupt" ) func runBridgeAuthShow(cmd *cobra.Command, args []string) error { + backend, err := cache.NewRepoCache(repo) + if err != nil { + return err + } + defer backend.Close() + interrupt.RegisterCleaner(backend.Close) + cred, err := auth.LoadWithPrefix(repo, args[0]) if err != nil { return err } + var userFmt string + + switch cred.UserId() { + case auth.DefaultUserId: + userFmt = colors.Red("default user") + default: + user, err := backend.ResolveIdentity(cred.UserId()) + if err != nil { + return err + } + userFmt = user.DisplayName() + + defaultUser, _ := backend.GetUserIdentity() + if cred.UserId() == defaultUser.Id() { + userFmt = colors.Red(userFmt) + } + } + fmt.Printf("Id: %s\n", cred.ID()) fmt.Printf("Target: %s\n", cred.Target()) fmt.Printf("Kind: %s\n", cred.Kind()) + fmt.Printf("User: %s\n", userFmt) fmt.Printf("Creation: %s\n", cred.CreateTime().Format(time.RFC822)) switch cred := cred.(type) { diff --git a/commands/bridge_configure.go b/commands/bridge_configure.go index 26bd7bc22c6de74527ac94e56324826f7204264e..0e29d06a469a7dc74f6877653a8fbf80c1d25473 100644 --- a/commands/bridge_configure.go +++ b/commands/bridge_configure.go @@ -206,7 +206,7 @@ git bug bridge configure \ --target=github \ --url=https://github.com/michaelmure/git-bug \ --token=$(TOKEN)`, - PreRunE: loadRepoEnsureUser, + PreRunE: loadRepo, RunE: runBridgeConfigure, } diff --git a/commands/bridge_pull.go b/commands/bridge_pull.go index 692ec5e953d8904a1a6e661391f25e7756529ec2..2dd3d93e716a587b16dba34de5de7a8102ddc5b6 100644 --- a/commands/bridge_pull.go +++ b/commands/bridge_pull.go @@ -138,7 +138,7 @@ func parseSince(since string) (time.Time, error) { var bridgePullCmd = &cobra.Command{ Use: "pull []", Short: "Pull updates.", - PreRunE: loadRepoEnsureUser, + PreRunE: loadRepo, RunE: runBridgePull, Args: cobra.MaximumNArgs(1), } diff --git a/commands/user_adopt.go b/commands/user_adopt.go index d84d404823aae3473d90e97d72150109474eac95..a7de54d9bfd31b39caf7b1c31f0c1fb80a614f3d 100644 --- a/commands/user_adopt.go +++ b/commands/user_adopt.go @@ -4,7 +4,9 @@ import ( "fmt" "os" + "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/util/interrupt" "github.com/spf13/cobra" ) @@ -24,6 +26,16 @@ func runUserAdopt(cmd *cobra.Command, args []string) error { return err } + _, err = backend.GetUserIdentity() + if err == identity.ErrNoIdentitySet { + err = auth.ReplaceDefaultUser(repo, i.Id()) + if err != nil { + return err + } + } else if err != nil { + return err + } + err = backend.SetUserIdentity(i) if err != nil { return err