aboutsummaryrefslogtreecommitdiffstats
path: root/bridge/github
diff options
context:
space:
mode:
Diffstat (limited to 'bridge/github')
-rw-r--r--bridge/github/config.go135
-rw-r--r--bridge/github/config_test.go24
-rw-r--r--bridge/github/export.go108
-rw-r--r--bridge/github/export_test.go43
-rw-r--r--bridge/github/github.go5
-rw-r--r--bridge/github/import.go38
-rw-r--r--bridge/github/import_test.go15
-rw-r--r--bridge/github/iterator.go4
8 files changed, 214 insertions, 158 deletions
diff --git a/bridge/github/config.go b/bridge/github/config.go
index 9d059b00..a74e7f6b 100644
--- a/bridge/github/config.go
+++ b/bridge/github/config.go
@@ -22,6 +22,8 @@ import (
"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/repository"
"github.com/MichaelMure/git-bug/util/colors"
@@ -33,7 +35,6 @@ const (
githubV3Url = "https://api.github.com"
keyOwner = "owner"
keyProject = "project"
- keyToken = "token"
defaultTimeout = 60 * time.Second
)
@@ -42,40 +43,33 @@ var (
ErrBadProjectURL = errors.New("bad project url")
)
-func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams) (core.Configuration, error) {
+func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) {
conf := make(core.Configuration)
var err error
- if (params.Token != "" || params.TokenId != "" || params.TokenStdin) &&
+ 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
+
// getting owner and project name
switch {
case params.Owner != "" && params.Project != "":
// first try to use params if both or project and owner are provided
owner = params.Owner
project = params.Project
-
case params.URL != "":
// try to parse params URL and extract owner and project
owner, project, err = splitURL(params.URL)
if err != nil {
return nil, err
}
-
default:
- // remote suggestions
- remotes, err := repo.GetRemotes()
- if err != nil {
- return nil, err
- }
-
// terminal prompt
- owner, project, err = promptURL(remotes)
+ owner, project, err = promptURL(repo)
if err != nil {
return nil, err
}
@@ -90,49 +84,38 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
return nil, fmt.Errorf("invalid parameter owner: %v", owner)
}
- var token string
- var tokenId entity.Id
- var tokenObj *core.Token
-
- // try to get token from params if provided, else use terminal prompt
- // to either enter a token or login and generate a new one, or choose
- // an existing token
- if params.Token != "" {
- token = params.Token
- } else if params.TokenStdin {
- reader := bufio.NewReader(os.Stdin)
- token, err = reader.ReadString('\n')
- if err != nil {
- return nil, fmt.Errorf("reading from stdin: %v", err)
- }
- token = strings.TrimSpace(token)
- } else if params.TokenId != "" {
- tokenId = entity.Id(params.TokenId)
- } else {
- tokenObj, err = promptTokenOptions(repo, owner, project)
- if err != nil {
- return nil, err
- }
+ user, err := repo.GetUserIdentity()
+ if err != nil {
+ return nil, err
}
- // at this point, we check if the token already exist or we create a new one
- if token != "" {
- tokenObj, err = core.LoadOrCreateToken(repo, target, token)
+ var cred auth.Credential
+
+ switch {
+ case params.CredPrefix != "":
+ cred, err = auth.LoadWithPrefix(repo, params.CredPrefix)
if err != nil {
return nil, err
}
- } else if tokenId != "" {
- tokenObj, err = core.LoadToken(repo, tokenId)
+ if 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)
+ default:
+ cred, err = promptTokenOptions(repo, user.Id(), owner, project)
if err != nil {
return nil, err
}
- if tokenObj.Target != target {
- return nil, fmt.Errorf("token target is incompatible %s", tokenObj.Target)
- }
+ }
+
+ token, ok := cred.(*auth.Token)
+ if !ok {
+ return nil, fmt.Errorf("the Github bridge only handle token credentials")
}
// verify access to the repository with token
- ok, err = validateProject(owner, project, tokenObj.Value)
+ ok, err = validateProject(owner, project, token)
if err != nil {
return nil, err
}
@@ -141,7 +124,6 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
}
conf[core.ConfigKeyTarget] = target
- conf[core.ConfigKeyTokenId] = tokenObj.ID().String()
conf[keyOwner] = owner
conf[keyProject] = project
@@ -150,6 +132,14 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
return nil, err
}
+ // don't forget to store the now known valid token
+ if !auth.IdExist(repo, cred.ID()) {
+ err = auth.Store(repo, cred)
+ if err != nil {
+ return nil, err
+ }
+ }
+
return conf, nil
}
@@ -160,10 +150,6 @@ func (*Github) ValidateConfig(conf core.Configuration) error {
return fmt.Errorf("unexpected target name: %v", v)
}
- if _, ok := conf[core.ConfigKeyTokenId]; !ok {
- return fmt.Errorf("missing %s key", core.ConfigKeyTokenId)
- }
-
if _, ok := conf[keyOwner]; !ok {
return fmt.Errorf("missing %s key", keyOwner)
}
@@ -245,9 +231,9 @@ func randomFingerprint() string {
return string(b)
}
-func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*core.Token, error) {
+func promptTokenOptions(repo repository.RepoConfig, userId entity.Id, owner, project string) (auth.Credential, error) {
for {
- tokens, err := core.LoadTokensWithTarget(repo, target)
+ creds, err := auth.List(repo, auth.WithUserId(userId), auth.WithTarget(target))
if err != nil {
return nil, err
}
@@ -256,18 +242,19 @@ func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*cor
fmt.Println("[1]: enter my token")
fmt.Println("[2]: interactive token creation")
- if len(tokens) > 0 {
+ if len(creds) > 0 {
+ sort.Sort(auth.ById(creds))
+
fmt.Println()
fmt.Println("Existing tokens for Github:")
- for i, token := range tokens {
- if token.Target == target {
- fmt.Printf("[%d]: %s => %s (%s)\n",
- i+3,
- colors.Cyan(token.ID().Human()),
- text.TruncateMax(token.Value, 10),
- token.CreateTime.Format(time.RFC822),
- )
- }
+ for i, cred := range creds {
+ token := cred.(*auth.Token)
+ fmt.Printf("[%d]: %s => %s (%s)\n",
+ i+3,
+ colors.Cyan(token.ID().Human()),
+ colors.Red(text.TruncateMax(token.Value, 10)),
+ token.CreateTime().Format(time.RFC822),
+ )
}
}
@@ -281,30 +268,28 @@ func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*cor
}
line = strings.TrimSpace(line)
-
index, err := strconv.Atoi(line)
- if err != nil || index < 1 || index > len(tokens)+2 {
+ if err != nil || index < 1 || index > len(creds)+2 {
fmt.Println("invalid input")
continue
}
- var token string
switch index {
case 1:
- token, err = promptToken()
+ value, err := promptToken()
if err != nil {
return nil, err
}
+ return auth.NewToken(userId, value, target), nil
case 2:
- token, err = loginAndRequestToken(owner, project)
+ value, err := loginAndRequestToken(owner, project)
if err != nil {
return nil, err
}
+ return auth.NewToken(userId, value, target), nil
default:
- return tokens[index-3], nil
+ return creds[index-3], nil
}
-
- return core.LoadOrCreateToken(repo, target, token)
}
}
@@ -435,7 +420,13 @@ func promptUsername() (string, error) {
}
}
-func promptURL(remotes map[string]string) (string, string, error) {
+func promptURL(repo repository.RepoCommon) (string, string, error) {
+ // remote suggestions
+ remotes, err := repo.GetRemotes()
+ if err != nil {
+ return "", "", err
+ }
+
validRemotes := getValidGithubRemoteURLs(remotes)
if len(validRemotes) > 0 {
for {
@@ -556,7 +547,7 @@ func validateUsername(username string) (bool, error) {
return resp.StatusCode == http.StatusOK, nil
}
-func validateProject(owner, project, token string) (bool, error) {
+func validateProject(owner, project string, token *auth.Token) (bool, error) {
url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project)
req, err := http.NewRequest("GET", url, nil)
@@ -565,7 +556,7 @@ func validateProject(owner, project, token string) (bool, error) {
}
// need the token for private repositories
- req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
+ req.Header.Set("Authorization", fmt.Sprintf("token %s", token.Value))
client := &http.Client{
Timeout: defaultTimeout,
diff --git a/bridge/github/config_test.go b/bridge/github/config_test.go
index 4feeaa74..9798d26b 100644
--- a/bridge/github/config_test.go
+++ b/bridge/github/config_test.go
@@ -5,6 +5,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+
+ "github.com/MichaelMure/git-bug/bridge/core/auth"
+ "github.com/MichaelMure/git-bug/entity"
)
func TestSplitURL(t *testing.T) {
@@ -142,20 +145,23 @@ func TestValidateUsername(t *testing.T) {
}
func TestValidateProject(t *testing.T) {
- tokenPrivateScope := os.Getenv("GITHUB_TOKEN_PRIVATE")
- if tokenPrivateScope == "" {
+ envPrivate := os.Getenv("GITHUB_TOKEN_PRIVATE")
+ if envPrivate == "" {
t.Skip("Env var GITHUB_TOKEN_PRIVATE missing")
}
- tokenPublicScope := os.Getenv("GITHUB_TOKEN_PUBLIC")
- if tokenPublicScope == "" {
+ envPublic := os.Getenv("GITHUB_TOKEN_PUBLIC")
+ if envPublic == "" {
t.Skip("Env var GITHUB_TOKEN_PUBLIC missing")
}
+ tokenPrivate := auth.NewToken(entity.UnsetId, envPrivate, target)
+ tokenPublic := auth.NewToken(entity.UnsetId, envPublic, target)
+
type args struct {
owner string
project string
- token string
+ token *auth.Token
}
tests := []struct {
name string
@@ -167,7 +173,7 @@ func TestValidateProject(t *testing.T) {
args: args{
project: "git-bug",
owner: "MichaelMure",
- token: tokenPublicScope,
+ token: tokenPublic,
},
want: true,
},
@@ -176,7 +182,7 @@ func TestValidateProject(t *testing.T) {
args: args{
project: "git-bug-test-github-bridge",
owner: "MichaelMure",
- token: tokenPrivateScope,
+ token: tokenPrivate,
},
want: true,
},
@@ -185,7 +191,7 @@ func TestValidateProject(t *testing.T) {
args: args{
project: "git-bug-test-github-bridge",
owner: "MichaelMure",
- token: tokenPublicScope,
+ token: tokenPublic,
},
want: false,
},
@@ -194,7 +200,7 @@ func TestValidateProject(t *testing.T) {
args: args{
project: "cant-find-this",
owner: "organisation-not-found",
- token: tokenPublicScope,
+ token: tokenPublic,
},
want: false,
},
diff --git a/bridge/github/export.go b/bridge/github/export.go
index 8d515802..6c089a47 100644
--- a/bridge/github/export.go
+++ b/bridge/github/export.go
@@ -15,9 +15,11 @@ import (
"golang.org/x/sync/errgroup"
"github.com/MichaelMure/git-bug/bridge/core"
+ "github.com/MichaelMure/git-bug/bridge/core/auth"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/entity"
+ "github.com/MichaelMure/git-bug/repository"
)
var (
@@ -31,8 +33,12 @@ type githubExporter struct {
// cache identities clients
identityClient map[entity.Id]*githubv4.Client
- // map identities with their tokens
- identityToken map[entity.Id]string
+ // 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
@@ -46,68 +52,86 @@ type githubExporter struct {
}
// Init .
-func (ge *githubExporter) Init(conf core.Configuration) error {
+func (ge *githubExporter) Init(repo *cache.RepoCache, conf core.Configuration) error {
ge.conf = conf
- //TODO: initialize with multiple tokens
- ge.identityToken = make(map[entity.Id]string)
ge.identityClient = make(map[entity.Id]*githubv4.Client)
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))
+ if err != nil {
+ return err
+ }
+
+ if len(creds) == 0 {
+ return ErrMissingIdentityToken
+ }
+
+ ge.defaultToken = creds[0].(*auth.Token)
+
return nil
}
-// getIdentityClient return a githubv4 API client configured with the access token of the given identity.
-// if no client were found it will initialize it from the known tokens map and cache it for next use
-func (ge *githubExporter) getIdentityClient(id entity.Id) (*githubv4.Client, error) {
- client, ok := ge.identityClient[id]
- if ok {
- return client, nil
+func (ge *githubExporter) cacheAllClient(repo repository.RepoConfig) error {
+ creds, err := auth.List(repo, auth.WithTarget(target), auth.WithKind(auth.KindToken))
+ if err != nil {
+ return err
}
- // get token
- token, ok := ge.identityToken[id]
- if !ok {
- return nil, ErrMissingIdentityToken
+ for _, cred := range creds {
+ if _, ok := ge.identityClient[cred.UserId()]; !ok {
+ client := buildClient(creds[0].(*auth.Token))
+ ge.identityClient[cred.UserId()] = client
+ }
}
- // create client
- client = buildClient(token)
- // cache client
- ge.identityClient[id] = client
+ return nil
+}
- return client, nil
+// getClientForIdentity return a githubv4 API client configured with the access token of the given identity.
+func (ge *githubExporter) getClientForIdentity(userId entity.Id) (*githubv4.Client, error) {
+ client, ok := ge.identityClient[userId]
+ if ok {
+ return client, nil
+ }
+
+ return nil, ErrMissingIdentityToken
}
// ExportAll export all event made by the current user to Github
func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ExportResult, error) {
out := make(chan core.ExportResult)
- user, err := repo.GetUserIdentity()
- if err != nil {
- return nil, err
- }
-
- ge.identityToken[user.Id()] = ge.conf[core.ConfigKeyToken]
-
+ var err error
// get repository node id
ge.repositoryID, err = getRepositoryNodeID(
ctx,
+ ge.defaultToken,
ge.conf[keyOwner],
ge.conf[keyProject],
- ge.conf[core.ConfigKeyToken],
)
-
- if err != nil {
- return nil, err
- }
-
- client, err := ge.getIdentityClient(user.Id())
if err != nil {
return nil, err
}
// query all labels
- err = ge.cacheGithubLabels(ctx, client)
+ err = ge.cacheGithubLabels(ctx, ge.defaultClient)
if err != nil {
return nil, err
}
@@ -115,8 +139,8 @@ func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache,
go func() {
defer close(out)
- var allIdentitiesIds []entity.Id
- for id := range ge.identityToken {
+ allIdentitiesIds := make([]entity.Id, 0, len(ge.identityClient))
+ for id := range ge.identityClient {
allIdentitiesIds = append(allIdentitiesIds, id)
}
@@ -209,7 +233,7 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc
} else {
// check that we have a token for operation author
- client, err := ge.getIdentityClient(author.Id())
+ client, err := ge.getClientForIdentity(author.Id())
if err != nil {
// if bug is still not exported and we do not have the author stop the execution
out <- core.NewExportNothing(b.Id(), fmt.Sprintf("missing author token"))
@@ -262,7 +286,7 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc
}
opAuthor := op.GetAuthor()
- client, err := ge.getIdentityClient(opAuthor.Id())
+ client, err := ge.getClientForIdentity(opAuthor.Id())
if err != nil {
continue
}
@@ -384,7 +408,7 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc
}
// getRepositoryNodeID request github api v3 to get repository node id
-func getRepositoryNodeID(ctx context.Context, owner, project, token string) (string, error) {
+func getRepositoryNodeID(ctx context.Context, token *auth.Token, owner, project string) (string, error) {
url := fmt.Sprintf("%s/repos/%s/%s", githubV3Url, owner, project)
client := &http.Client{}
@@ -394,7 +418,7 @@ func getRepositoryNodeID(ctx context.Context, owner, project, token string) (str
}
// need the token for private repositories
- req.Header.Set("Authorization", fmt.Sprintf("token %s", token))
+ req.Header.Set("Authorization", fmt.Sprintf("token %s", token.Value))
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
@@ -512,7 +536,7 @@ func (ge *githubExporter) createGithubLabel(ctx context.Context, label, color st
req = req.WithContext(ctx)
// need the token for private repositories
- req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.conf[core.ConfigKeyToken]))
+ req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.defaultToken.Value))
resp, err := client.Do(req)
if err != nil {
diff --git a/bridge/github/export_test.go b/bridge/github/export_test.go
index dba72f3f..5a0bc653 100644
--- a/bridge/github/export_test.go
+++ b/bridge/github/export_test.go
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/MichaelMure/git-bug/bridge/core"
+ "github.com/MichaelMure/git-bug/bridge/core/auth"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/repository"
@@ -30,7 +31,7 @@ type testCase struct {
numOrOp int // number of original operations
}
-func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCache) []*testCase {
+func testCases(t *testing.T, repo *cache.RepoCache) []*testCase {
// simple bug
simpleBug, _, err := repo.NewBug("simple bug", "new bug")
require.NoError(t, err)
@@ -92,32 +93,32 @@ func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCach
require.NoError(t, err)
return []*testCase{
- &testCase{
+ {
name: "simple bug",
bug: simpleBug,
numOrOp: 1,
},
- &testCase{
+ {
name: "bug with comments",
bug: bugWithComments,
numOrOp: 2,
},
- &testCase{
+ {
name: "bug label change",
bug: bugLabelChange,
numOrOp: 6,
},
- &testCase{
+ {
name: "bug with comment editions",
bug: bugWithCommentEditions,
numOrOp: 4,
},
- &testCase{
+ {
name: "bug changed status",
bug: bugStatusChanged,
numOrOp: 3,
},
- &testCase{
+ {
name: "bug title edited",
bug: bugTitleEdited,
numOrOp: 2,
@@ -127,11 +128,11 @@ func testCases(t *testing.T, repo *cache.RepoCache, identity *cache.IdentityCach
func TestPushPull(t *testing.T) {
// repo owner
- user := os.Getenv("GITHUB_TEST_USER")
+ envUser := os.Getenv("GITHUB_TEST_USER")
// token must have 'repo' and 'delete_repo' scopes
- token := os.Getenv("GITHUB_TOKEN_ADMIN")
- if token == "" {
+ envToken := os.Getenv("GITHUB_TOKEN_ADMIN")
+ if envToken == "" {
t.Skip("Env var GITHUB_TOKEN_ADMIN missing")
}
@@ -152,35 +153,38 @@ func TestPushPull(t *testing.T) {
defer backend.Close()
interrupt.RegisterCleaner(backend.Close)
- tests := testCases(t, backend, author)
+ tests := testCases(t, backend)
// generate project name
projectName := generateRepoName()
// create target Github repository
- err = createRepository(projectName, token)
+ err = createRepository(projectName, envToken)
require.NoError(t, err)
fmt.Println("created repository", projectName)
// Make sure to remove the Github repository when the test end
defer func(t *testing.T) {
- if err := deleteRepository(projectName, user, token); err != nil {
+ if err := deleteRepository(projectName, envUser, envToken); err != nil {
t.Fatal(err)
}
fmt.Println("deleted repository:", projectName)
}(t)
interrupt.RegisterCleaner(func() error {
- return deleteRepository(projectName, user, token)
+ return deleteRepository(projectName, envUser, envToken)
})
+ token := auth.NewToken(author.Id(), envToken, target)
+ err = auth.Store(repo, token)
+ require.NoError(t, err)
+
// initialize exporter
exporter := &githubExporter{}
- err = exporter.Init(core.Configuration{
- keyOwner: user,
+ err = exporter.Init(backend, core.Configuration{
+ keyOwner: envUser,
keyProject: projectName,
- keyToken: token,
})
require.NoError(t, err)
@@ -206,10 +210,9 @@ func TestPushPull(t *testing.T) {
require.NoError(t, err)
importer := &githubImporter{}
- err = importer.Init(core.Configuration{
- keyOwner: user,
+ err = importer.Init(backend, core.Configuration{
+ keyOwner: envUser,
keyProject: projectName,
- keyToken: token,
})
require.NoError(t, err)
diff --git a/bridge/github/github.go b/bridge/github/github.go
index e4fb03dd..874c2d11 100644
--- a/bridge/github/github.go
+++ b/bridge/github/github.go
@@ -8,6 +8,7 @@ import (
"golang.org/x/oauth2"
"github.com/MichaelMure/git-bug/bridge/core"
+ "github.com/MichaelMure/git-bug/bridge/core/auth"
)
type Github struct{}
@@ -24,9 +25,9 @@ func (*Github) NewExporter() core.Exporter {
return &githubExporter{}
}
-func buildClient(token string) *githubv4.Client {
+func buildClient(token *auth.Token) *githubv4.Client {
src := oauth2.StaticTokenSource(
- &oauth2.Token{AccessToken: token},
+ &oauth2.Token{AccessToken: token.Value},
)
httpClient := oauth2.NewClient(context.TODO(), src)
diff --git a/bridge/github/import.go b/bridge/github/import.go
index 67ab9351..092e3e71 100644
--- a/bridge/github/import.go
+++ b/bridge/github/import.go
@@ -8,6 +8,7 @@ import (
"github.com/shurcooL/githubv4"
"github.com/MichaelMure/git-bug/bridge/core"
+ "github.com/MichaelMure/git-bug/bridge/core/auth"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/entity"
@@ -24,6 +25,9 @@ const (
type githubImporter struct {
conf core.Configuration
+ // default user client
+ client *githubv4.Client
+
// iterator
iterator *iterator
@@ -31,15 +35,37 @@ type githubImporter struct {
out chan<- core.ImportResult
}
-func (gi *githubImporter) Init(conf core.Configuration) error {
+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()))
+ }
+
+ creds, err := auth.List(repo, opts...)
+ if err != nil {
+ return err
+ }
+
+ if len(creds) == 0 {
+ return ErrMissingIdentityToken
+ }
+
+ gi.client = buildClient(creds[0].(*auth.Token))
+
return nil
}
// ImportAll iterate over all the configured repository issues and ensure the creation of the
// missing issues / timeline items / edits / label events ...
func (gi *githubImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
- gi.iterator = NewIterator(ctx, 10, gi.conf[keyOwner], gi.conf[keyProject], gi.conf[core.ConfigKeyToken], since)
+ gi.iterator = NewIterator(ctx, gi.client, 10, gi.conf[keyOwner], gi.conf[keyProject], since)
out := make(chan core.ImportResult)
gi.out = out
@@ -494,7 +520,7 @@ func (gi *githubImporter) ensurePerson(repo *cache.RepoCache, actor *actor) (*ca
if err == nil {
return i, nil
}
- if _, ok := err.(entity.ErrMultipleMatch); ok {
+ if entity.IsErrMultipleMatch(err) {
return nil, err
}
@@ -543,7 +569,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache,
if err == nil {
return i, nil
}
- if _, ok := err.(entity.ErrMultipleMatch); ok {
+ if entity.IsErrMultipleMatch(err) {
return nil, err
}
@@ -553,12 +579,10 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache,
"login": githubv4.String("ghost"),
}
- gc := buildClient(gi.conf[core.ConfigKeyToken])
-
ctx, cancel := context.WithTimeout(gi.iterator.ctx, defaultTimeout)
defer cancel()
- err = gc.Query(ctx, &q, variables)
+ err = gi.client.Query(ctx, &q, variables)
if err != nil {
return nil, err
}
diff --git a/bridge/github/import_test.go b/bridge/github/import_test.go
index f1558831..304229a0 100644
--- a/bridge/github/import_test.go
+++ b/bridge/github/import_test.go
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/MichaelMure/git-bug/bridge/core"
+ "github.com/MichaelMure/git-bug/bridge/core/auth"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/identity"
@@ -134,16 +135,22 @@ func Test_Importer(t *testing.T) {
defer backend.Close()
interrupt.RegisterCleaner(backend.Close)
- token := os.Getenv("GITHUB_TOKEN_PRIVATE")
- if token == "" {
+ envToken := os.Getenv("GITHUB_TOKEN_PRIVATE")
+ if envToken == "" {
t.Skip("Env var GITHUB_TOKEN_PRIVATE missing")
}
+ err = author.Commit(repo)
+ require.NoError(t, err)
+
+ token := auth.NewToken(author.Id(), envToken, target)
+ err = auth.Store(repo, token)
+ require.NoError(t, err)
+
importer := &githubImporter{}
- err = importer.Init(core.Configuration{
+ err = importer.Init(backend, core.Configuration{
keyOwner: "MichaelMure",
keyProject: "git-bug-test-github-bridge",
- keyToken: token,
})
require.NoError(t, err)
diff --git a/bridge/github/iterator.go b/bridge/github/iterator.go
index d1d7900f..40b00292 100644
--- a/bridge/github/iterator.go
+++ b/bridge/github/iterator.go
@@ -63,9 +63,9 @@ type iterator struct {
}
// NewIterator create and initialize a new iterator
-func NewIterator(ctx context.Context, capacity int, owner, project, token string, since time.Time) *iterator {
+func NewIterator(ctx context.Context, client *githubv4.Client, capacity int, owner, project string, since time.Time) *iterator {
i := &iterator{
- gc: buildClient(token),
+ gc: client,
since: since,
capacity: capacity,
ctx: ctx,