2019-07-09 23:56:38 +03:00
|
|
|
package gitlab
|
|
|
|
|
|
|
|
import (
|
2019-08-13 20:51:14 +03:00
|
|
|
"context"
|
2019-07-17 01:06:42 +03:00
|
|
|
"fmt"
|
2019-07-17 01:09:02 +03:00
|
|
|
"strconv"
|
2019-07-09 23:56:38 +03:00
|
|
|
"time"
|
|
|
|
|
2019-07-17 01:06:42 +03:00
|
|
|
"github.com/xanzy/go-gitlab"
|
|
|
|
|
2019-07-09 23:56:38 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge/core"
|
2019-12-08 23:15:06 +03:00
|
|
|
"github.com/MichaelMure/git-bug/bridge/core/auth"
|
2019-07-09 23:56:38 +03:00
|
|
|
"github.com/MichaelMure/git-bug/cache"
|
2022-08-19 00:34:05 +03:00
|
|
|
"github.com/MichaelMure/git-bug/entities/bug"
|
2019-08-12 17:12:14 +03:00
|
|
|
"github.com/MichaelMure/git-bug/entity"
|
2019-07-17 01:06:42 +03:00
|
|
|
"github.com/MichaelMure/git-bug/util/text"
|
2019-07-09 23:56:38 +03:00
|
|
|
)
|
|
|
|
|
2019-07-22 01:13:47 +03:00
|
|
|
// gitlabImporter implement the Importer interface
|
2019-07-09 23:56:38 +03:00
|
|
|
type gitlabImporter struct {
|
|
|
|
conf core.Configuration
|
|
|
|
|
2020-02-23 16:05:03 +03:00
|
|
|
// default client
|
2019-12-08 23:15:06 +03:00
|
|
|
client *gitlab.Client
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
// send only channel
|
|
|
|
out chan<- core.ImportResult
|
2019-07-09 23:56:38 +03:00
|
|
|
}
|
|
|
|
|
2020-02-15 17:39:49 +03:00
|
|
|
func (gi *gitlabImporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error {
|
2019-07-17 01:06:42 +03:00
|
|
|
gi.conf = conf
|
2019-12-08 23:15:06 +03:00
|
|
|
|
2020-02-10 00:17:10 +03:00
|
|
|
creds, err := auth.List(repo,
|
|
|
|
auth.WithTarget(target),
|
|
|
|
auth.WithKind(auth.KindToken),
|
2020-02-15 04:55:19 +03:00
|
|
|
auth.WithMeta(auth.MetaKeyBaseURL, conf[confKeyGitlabBaseUrl]),
|
2020-02-23 16:05:03 +03:00
|
|
|
auth.WithMeta(auth.MetaKeyLogin, conf[confKeyDefaultLogin]),
|
2020-02-10 00:17:10 +03:00
|
|
|
)
|
2019-12-08 23:15:06 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(creds) == 0 {
|
|
|
|
return ErrMissingIdentityToken
|
|
|
|
}
|
|
|
|
|
2020-02-15 04:55:19 +03:00
|
|
|
gi.client, err = buildClient(conf[confKeyGitlabBaseUrl], creds[0].(*auth.Token))
|
2019-12-10 22:30:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-08 23:15:06 +03:00
|
|
|
|
2019-07-17 01:06:42 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-07-22 01:13:47 +03:00
|
|
|
// ImportAll iterate over all the configured repository issues (notes) and ensure the creation
|
|
|
|
// of the missing issues / comments / label events / title changes ...
|
2019-08-13 20:51:14 +03:00
|
|
|
func (gi *gitlabImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
|
|
|
|
out := make(chan core.ImportResult)
|
|
|
|
gi.out = out
|
|
|
|
|
|
|
|
go func() {
|
2021-02-14 22:35:03 +03:00
|
|
|
defer close(out)
|
2019-08-13 20:51:14 +03:00
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
for issue := range Issues(ctx, gi.client, gi.conf[confKeyProjectID], since) {
|
2019-08-13 20:51:14 +03:00
|
|
|
|
|
|
|
b, err := gi.ensureIssue(repo, issue)
|
|
|
|
if err != nil {
|
|
|
|
err := fmt.Errorf("issue creation: %v", err)
|
|
|
|
out <- core.NewImportError(err, "")
|
|
|
|
return
|
|
|
|
}
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-12-05 13:05:38 +03:00
|
|
|
issueEvents := SortedEvents(
|
|
|
|
Notes(ctx, gi.client, issue),
|
|
|
|
LabelEvents(ctx, gi.client, issue),
|
|
|
|
StateEvents(ctx, gi.client, issue),
|
|
|
|
)
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-12-05 13:05:38 +03:00
|
|
|
for e := range issueEvents {
|
2021-02-14 22:35:03 +03:00
|
|
|
if e, ok := e.(ErrorEvent); ok {
|
|
|
|
out <- core.NewImportError(e.Err, "")
|
|
|
|
continue
|
2019-08-13 20:51:14 +03:00
|
|
|
}
|
2021-02-14 22:35:03 +03:00
|
|
|
if err := gi.ensureIssueEvent(repo, b, issue, e); err != nil {
|
|
|
|
err := fmt.Errorf("issue event creation: %v", err)
|
|
|
|
out <- core.NewImportError(err, entity.Id(e.ID()))
|
2019-08-13 20:51:14 +03:00
|
|
|
}
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
2019-11-19 22:09:29 +03:00
|
|
|
if !b.NeedCommit() {
|
|
|
|
out <- core.NewImportNothing(b.Id(), "no imported operation")
|
|
|
|
} else if err := b.Commit(); err != nil {
|
|
|
|
// commit bug state
|
2019-08-13 20:51:14 +03:00
|
|
|
err := fmt.Errorf("bug commit: %v", err)
|
|
|
|
out <- core.NewImportError(err, "")
|
|
|
|
return
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-08-13 20:51:14 +03:00
|
|
|
}()
|
2019-07-23 19:40:10 +03:00
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
return out, nil
|
2019-07-09 23:56:38 +03:00
|
|
|
}
|
|
|
|
|
2019-07-17 01:06:42 +03:00
|
|
|
func (gi *gitlabImporter) ensureIssue(repo *cache.RepoCache, issue *gitlab.Issue) (*cache.BugCache, error) {
|
|
|
|
// ensure issue author
|
|
|
|
author, err := gi.ensurePerson(repo, issue.Author.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// resolve bug
|
2020-03-28 19:06:33 +03:00
|
|
|
b, err := repo.ResolveBugMatcher(func(excerpt *cache.BugExcerpt) bool {
|
|
|
|
return excerpt.CreateMetadata[core.MetaKeyOrigin] == target &&
|
2021-02-14 22:35:03 +03:00
|
|
|
excerpt.CreateMetadata[metaKeyGitlabId] == fmt.Sprintf("%d", issue.IID) &&
|
2021-02-07 19:01:59 +03:00
|
|
|
excerpt.CreateMetadata[metaKeyGitlabBaseUrl] == gi.conf[confKeyGitlabBaseUrl] &&
|
|
|
|
excerpt.CreateMetadata[metaKeyGitlabProject] == gi.conf[confKeyProjectID]
|
2020-03-28 19:06:33 +03:00
|
|
|
})
|
2019-07-23 19:40:10 +03:00
|
|
|
if err == nil {
|
|
|
|
return b, nil
|
|
|
|
}
|
2022-11-19 13:33:12 +03:00
|
|
|
if !entity.IsErrNotFound(err) {
|
2019-08-13 20:51:14 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-04-17 18:40:11 +03:00
|
|
|
// if bug was never imported, create bug
|
2019-07-23 19:40:10 +03:00
|
|
|
b, _, err = repo.NewBugRaw(
|
|
|
|
author,
|
|
|
|
issue.CreatedAt.Unix(),
|
2021-04-17 18:40:11 +03:00
|
|
|
text.CleanupOneLine(issue.Title),
|
|
|
|
text.Cleanup(issue.Description),
|
2019-07-23 19:40:10 +03:00
|
|
|
nil,
|
|
|
|
map[string]string{
|
2019-11-10 19:48:13 +03:00
|
|
|
core.MetaKeyOrigin: target,
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: fmt.Sprintf("%d", issue.IID),
|
2019-11-10 19:48:13 +03:00
|
|
|
metaKeyGitlabUrl: issue.WebURL,
|
2020-02-15 04:55:19 +03:00
|
|
|
metaKeyGitlabProject: gi.conf[confKeyProjectID],
|
|
|
|
metaKeyGitlabBaseUrl: gi.conf[confKeyGitlabBaseUrl],
|
2019-07-23 19:40:10 +03:00
|
|
|
},
|
|
|
|
)
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2019-07-23 19:40:10 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
2019-07-23 19:40:10 +03:00
|
|
|
// importing a new bug
|
2019-08-13 20:51:14 +03:00
|
|
|
gi.out <- core.NewImportBug(b.Id())
|
2019-07-23 19:40:10 +03:00
|
|
|
|
2019-07-19 20:39:15 +03:00
|
|
|
return b, nil
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
func (gi *gitlabImporter) ensureIssueEvent(repo *cache.RepoCache, b *cache.BugCache, issue *gitlab.Issue, event Event) error {
|
|
|
|
id, errResolve := b.ResolveOperationWithMetadata(metaKeyGitlabId, event.ID())
|
2019-07-31 00:51:37 +03:00
|
|
|
if errResolve != nil && errResolve != cache.ErrNoMatchingOp {
|
|
|
|
return errResolve
|
|
|
|
}
|
|
|
|
|
2019-07-17 01:06:42 +03:00
|
|
|
// ensure issue author
|
2021-02-14 22:35:03 +03:00
|
|
|
author, err := gi.ensurePerson(repo, event.UserID())
|
2019-07-17 01:06:42 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
switch event.Kind() {
|
|
|
|
case EventClosed:
|
2019-07-31 00:51:37 +03:00
|
|
|
if errResolve == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
op, err := b.CloseRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.CreatedAt().Unix(),
|
2019-07-17 01:06:42 +03:00
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2021-02-14 22:35:03 +03:00
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-11-13 14:31:38 +03:00
|
|
|
gi.out <- core.NewImportStatusChange(b.Id(), op.Id())
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
case EventReopened:
|
2019-07-31 00:51:37 +03:00
|
|
|
if errResolve == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
op, err := b.OpenRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.CreatedAt().Unix(),
|
2019-07-17 01:06:42 +03:00
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-11-13 14:31:38 +03:00
|
|
|
gi.out <- core.NewImportStatusChange(b.Id(), op.Id())
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
case EventDescriptionChanged:
|
2019-07-23 19:40:10 +03:00
|
|
|
firstComment := b.Snapshot().Comments[0]
|
2019-07-17 01:06:42 +03:00
|
|
|
// since gitlab doesn't provide the issue history
|
|
|
|
// we should check for "changed the description" notes and compare issue texts
|
2019-07-19 19:56:58 +03:00
|
|
|
// TODO: Check only one time and ignore next 'description change' within one issue
|
2021-05-03 12:45:15 +03:00
|
|
|
cleanedDesc := text.Cleanup(issue.Description)
|
|
|
|
if errResolve == cache.ErrNoMatchingOp && cleanedDesc != firstComment.Message {
|
2019-07-17 01:06:42 +03:00
|
|
|
// comment edition
|
2019-08-13 20:51:14 +03:00
|
|
|
op, err := b.EditCommentRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.(NoteEvent).UpdatedAt.Unix(),
|
2021-05-03 12:45:15 +03:00
|
|
|
firstComment.CombinedId(),
|
|
|
|
cleanedDesc,
|
2019-07-17 01:06:42 +03:00
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2022-11-13 14:31:38 +03:00
|
|
|
gi.out <- core.NewImportTitleEdition(b.Id(), op.Id())
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
case EventComment:
|
|
|
|
cleanText := text.Cleanup(event.(NoteEvent).Body)
|
2019-07-17 01:06:42 +03:00
|
|
|
|
|
|
|
// if we didn't import the comment
|
2019-07-17 19:54:19 +03:00
|
|
|
if errResolve == cache.ErrNoMatchingOp {
|
2019-07-17 01:06:42 +03:00
|
|
|
|
|
|
|
// add comment operation
|
2022-11-13 14:31:38 +03:00
|
|
|
commentId, _, err := b.AddCommentRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.CreatedAt().Unix(),
|
2019-07-17 01:06:42 +03:00
|
|
|
cleanText,
|
|
|
|
nil,
|
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-11-13 14:31:38 +03:00
|
|
|
gi.out <- core.NewImportComment(b.Id(), commentId)
|
2019-08-13 20:51:14 +03:00
|
|
|
return nil
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// if comment was already exported
|
|
|
|
|
|
|
|
// search for last comment update
|
2021-05-03 12:45:15 +03:00
|
|
|
comment, err := b.Snapshot().SearchCommentByOpId(id)
|
2019-07-17 01:06:42 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
// compare local bug comment with the new event body
|
2019-07-17 19:54:19 +03:00
|
|
|
if comment.Message != cleanText {
|
2019-07-17 01:06:42 +03:00
|
|
|
// comment edition
|
2022-11-13 14:31:38 +03:00
|
|
|
_, err := b.EditCommentRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.(NoteEvent).UpdatedAt.Unix(),
|
2021-05-03 12:45:15 +03:00
|
|
|
comment.CombinedId(),
|
2019-07-17 01:06:42 +03:00
|
|
|
cleanText,
|
2019-07-23 19:40:10 +03:00
|
|
|
nil,
|
2019-07-17 01:06:42 +03:00
|
|
|
)
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-11-13 14:31:38 +03:00
|
|
|
gi.out <- core.NewImportCommentEdition(b.Id(), comment.CombinedId())
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
case EventTitleChanged:
|
2019-07-17 19:54:19 +03:00
|
|
|
// title change events are given new notes
|
2019-07-31 00:51:37 +03:00
|
|
|
if errResolve == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
op, err := b.SetTitleRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.CreatedAt().Unix(),
|
|
|
|
event.(NoteEvent).Title(),
|
2019-07-17 01:06:42 +03:00
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2022-11-13 14:31:38 +03:00
|
|
|
gi.out <- core.NewImportTitleEdition(b.Id(), op.Id())
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
case EventAddLabel:
|
2019-07-17 01:06:42 +03:00
|
|
|
_, err = b.ForceChangeLabelsRaw(
|
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.CreatedAt().Unix(),
|
|
|
|
[]string{event.(LabelEvent).Label.Name},
|
2019-07-17 01:06:42 +03:00
|
|
|
nil,
|
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2021-02-14 22:35:03 +03:00
|
|
|
return err
|
2019-07-17 01:06:42 +03:00
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
case EventRemoveLabel:
|
2019-07-17 01:06:42 +03:00
|
|
|
_, err = b.ForceChangeLabelsRaw(
|
|
|
|
author,
|
2021-02-14 22:35:03 +03:00
|
|
|
event.CreatedAt().Unix(),
|
2019-07-17 01:06:42 +03:00
|
|
|
nil,
|
2021-02-14 22:35:03 +03:00
|
|
|
[]string{event.(LabelEvent).Label.Name},
|
2019-07-17 01:06:42 +03:00
|
|
|
map[string]string{
|
2021-02-14 22:35:03 +03:00
|
|
|
metaKeyGitlabId: event.ID(),
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2021-02-14 22:35:03 +03:00
|
|
|
return err
|
|
|
|
|
|
|
|
case EventAssigned,
|
|
|
|
EventUnassigned,
|
|
|
|
EventChangedMilestone,
|
|
|
|
EventRemovedMilestone,
|
|
|
|
EventChangedDuedate,
|
|
|
|
EventRemovedDuedate,
|
|
|
|
EventLocked,
|
|
|
|
EventUnlocked,
|
|
|
|
EventMentionedInIssue,
|
|
|
|
EventMentionedInMergeRequest:
|
|
|
|
|
|
|
|
return nil
|
2019-07-17 01:06:42 +03:00
|
|
|
|
|
|
|
default:
|
2021-02-14 22:35:03 +03:00
|
|
|
return fmt.Errorf("unexpected event")
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
2021-02-14 22:35:03 +03:00
|
|
|
return nil
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (gi *gitlabImporter) ensurePerson(repo *cache.RepoCache, id int) (*cache.IdentityCache, error) {
|
2019-07-17 01:09:02 +03:00
|
|
|
// Look first in the cache
|
2019-11-10 19:48:13 +03:00
|
|
|
i, err := repo.ResolveIdentityImmutableMetadata(metaKeyGitlabId, strconv.Itoa(id))
|
2019-07-17 01:09:02 +03:00
|
|
|
if err == nil {
|
|
|
|
return i, nil
|
|
|
|
}
|
2019-12-08 23:15:06 +03:00
|
|
|
if entity.IsErrMultipleMatch(err) {
|
2019-07-17 01:09:02 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-05-14 07:12:14 +03:00
|
|
|
user, _, err := gi.client.Users.GetUser(id, gitlab.GetUsersOptions{})
|
2019-07-17 01:06:42 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-13 20:51:14 +03:00
|
|
|
i, err = repo.NewIdentityRaw(
|
2019-07-17 01:06:42 +03:00
|
|
|
user.Name,
|
|
|
|
user.PublicEmail,
|
2020-02-25 23:35:57 +03:00
|
|
|
user.Username,
|
2019-07-17 01:06:42 +03:00
|
|
|
user.AvatarURL,
|
2020-11-08 21:18:44 +03:00
|
|
|
nil,
|
2019-07-17 01:06:42 +03:00
|
|
|
map[string]string{
|
2019-07-23 20:16:52 +03:00
|
|
|
// because Gitlab
|
2019-11-10 19:48:13 +03:00
|
|
|
metaKeyGitlabId: strconv.Itoa(id),
|
|
|
|
metaKeyGitlabLogin: user.Username,
|
2019-07-17 01:06:42 +03:00
|
|
|
},
|
|
|
|
)
|
2019-08-13 20:51:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
gi.out <- core.NewImportIdentity(i.Id())
|
|
|
|
return i, nil
|
2019-07-17 01:06:42 +03:00
|
|
|
}
|