Merge pull request #274 from MichaelMure/gitlab-bridge

bridge/gitlab: support self-hosted GitLab instance
This commit is contained in:
Michael Muré 2019-12-10 21:13:54 +01:00 committed by GitHub
commit e96d8e6771
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 84 additions and 15 deletions

View File

@ -35,6 +35,7 @@ type BridgeParams struct {
Owner string
Project string
URL string
BaseURL string
CredPrefix string
TokenRaw string
}

View File

@ -44,6 +44,10 @@ var (
)
func (g *Github) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) {
if params.BaseURL != "" {
fmt.Println("warning: --base-url is ineffective for a Github bridge")
}
conf := make(core.Configuration)
var err error

View File

@ -42,6 +42,10 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor
return nil, fmt.Errorf("you must provide a project URL to configure this bridge with a token")
}
if params.URL == "" {
params.URL = defaultBaseURL
}
var url string
// get project url
@ -56,6 +60,10 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor
}
}
if !strings.HasPrefix(url, params.BaseURL) {
return nil, fmt.Errorf("base URL (%s) doesn't match the project URL (%s)", params.BaseURL, url)
}
user, err := repo.GetUserIdentity()
if err != nil {
return nil, err
@ -87,13 +95,14 @@ func (g *Gitlab) Configure(repo *cache.RepoCache, params core.BridgeParams) (cor
}
// validate project url and get its ID
id, err := validateProjectURL(url, token)
id, err := validateProjectURL(params.BaseURL, url, token)
if err != nil {
return nil, errors.Wrap(err, "project validation")
}
conf[core.ConfigKeyTarget] = target
conf[keyProjectID] = strconv.Itoa(id)
conf[keyGitlabBaseUrl] = params.BaseURL
err = g.ValidateConfig(conf)
if err != nil {
@ -302,13 +311,16 @@ func getValidGitlabRemoteURLs(remotes map[string]string) []string {
return urls
}
func validateProjectURL(url string, token *auth.Token) (int, error) {
func validateProjectURL(baseURL, url string, token *auth.Token) (int, error) {
projectPath, err := getProjectPath(url)
if err != nil {
return 0, err
}
client := buildClient(token)
client, err := buildClient(baseURL, token)
if err != nil {
return 0, err
}
project, _, err := client.Projects.GetProject(projectPath, &gitlab.GetProjectOptions{})
if err != nil {

View File

@ -62,7 +62,11 @@ func (ge *gitlabExporter) cacheAllClient(repo repository.RepoConfig) error {
for _, cred := range creds {
if _, ok := ge.identityClient[cred.UserId()]; !ok {
client := buildClient(creds[0].(*auth.Token))
client, err := buildClient(ge.conf[keyGitlabBaseUrl], creds[0].(*auth.Token))
if err != nil {
return err
}
ge.identityClient[cred.UserId()] = client
}
}
@ -133,6 +137,7 @@ func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc
var err error
var bugGitlabID int
var bugGitlabIDString string
var GitlabBaseUrl string
var bugCreationId string
// Special case:
@ -153,6 +158,12 @@ func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc
// get gitlab bug ID
gitlabID, ok := snapshot.GetCreateMetadata(metaKeyGitlabId)
if ok {
gitlabBaseUrl, ok := snapshot.GetCreateMetadata(metaKeyGitlabBaseUrl)
if ok && gitlabBaseUrl != ge.conf[gitlabBaseUrl] {
out <- core.NewExportNothing(b.Id(), "skipping issue imported from another Gitlab instance")
return
}
projectID, ok := snapshot.GetCreateMetadata(metaKeyGitlabProject)
if !ok {
err := fmt.Errorf("expected to find gitlab project id")
@ -199,6 +210,7 @@ func (ge *gitlabExporter) exportBug(ctx context.Context, b *cache.BugCache, sinc
metaKeyGitlabId: idString,
metaKeyGitlabUrl: url,
metaKeyGitlabProject: ge.repositoryID,
metaKeyGitlabBaseUrl: GitlabBaseUrl,
},
)
if err != nil {

View File

@ -189,6 +189,7 @@ func TestPushPull(t *testing.T) {
exporter := &gitlabExporter{}
err = exporter.Init(backend, core.Configuration{
keyProjectID: strconv.Itoa(projectID),
keyGitlabBaseUrl: "https://gitlab.com/",
})
require.NoError(t, err)
@ -216,6 +217,7 @@ func TestPushPull(t *testing.T) {
importer := &gitlabImporter{}
err = importer.Init(backend, core.Configuration{
keyProjectID: strconv.Itoa(projectID),
keyGitlabBaseUrl: "https://gitlab.com/",
})
require.NoError(t, err)
@ -280,7 +282,11 @@ func generateRepoName() string {
// create repository need a token with scope 'repo'
func createRepository(ctx context.Context, name string, token *auth.Token) (int, error) {
client := buildClient(token)
client, err := buildClient("https://gitlab.com/", token)
if err != nil {
return 0, err
}
project, _, err := client.Projects.CreateProject(
&gitlab.CreateProjectOptions{
Name: gitlab.String(name),
@ -296,7 +302,11 @@ func createRepository(ctx context.Context, name string, token *auth.Token) (int,
// delete repository need a token with scope 'delete_repo'
func deleteRepository(ctx context.Context, project int, token *auth.Token) error {
client := buildClient(token)
_, err := client.Projects.DeleteProject(project, gitlab.WithContext(ctx))
client, err := buildClient("https://gitlab.com/", token)
if err != nil {
return err
}
_, err = client.Projects.DeleteProject(project, gitlab.WithContext(ctx))
return err
}

View File

@ -17,9 +17,12 @@ const (
metaKeyGitlabUrl = "gitlab-url"
metaKeyGitlabLogin = "gitlab-login"
metaKeyGitlabProject = "gitlab-project-id"
metaKeyGitlabBaseUrl = "gitlab-base-url"
keyProjectID = "project-id"
keyGitlabBaseUrl = "base-url"
defaultBaseURL = "https://gitlab.com/"
defaultTimeout = 60 * time.Second
)
@ -37,10 +40,16 @@ func (*Gitlab) NewExporter() core.Exporter {
return &gitlabExporter{}
}
func buildClient(token *auth.Token) *gitlab.Client {
client := &http.Client{
func buildClient(baseURL string, token *auth.Token) (*gitlab.Client, error) {
httpClient := &http.Client{
Timeout: defaultTimeout,
}
return gitlab.NewClient(client, token.Value)
gitlabClient := gitlab.NewClient(httpClient, token.Value)
err := gitlabClient.SetBaseURL(baseURL)
if err != nil {
return nil, err
}
return gitlabClient, nil
}

View File

@ -52,7 +52,10 @@ func (gi *gitlabImporter) Init(repo *cache.RepoCache, conf core.Configuration) e
return ErrMissingIdentityToken
}
gi.client = buildClient(creds[0].(*auth.Token))
gi.client, err = buildClient(conf[keyGitlabBaseUrl], creds[0].(*auth.Token))
if err != nil {
return err
}
return nil
}
@ -151,6 +154,7 @@ func (gi *gitlabImporter) ensureIssue(repo *cache.RepoCache, issue *gitlab.Issue
metaKeyGitlabId: parseID(issue.IID),
metaKeyGitlabUrl: issue.WebURL,
metaKeyGitlabProject: gi.conf[keyProjectID],
metaKeyGitlabBaseUrl: gi.conf[keyGitlabBaseUrl],
},
)

View File

@ -104,6 +104,7 @@ func TestImport(t *testing.T) {
importer := &gitlabImporter{}
err = importer.Init(backend, core.Configuration{
keyProjectID: projectID,
keyGitlabBaseUrl: "https://gitlab.com",
})
require.NoError(t, err)

View File

@ -29,6 +29,9 @@ func (l *Launchpad) Configure(repo *cache.RepoCache, params core.BridgeParams) (
if params.Owner != "" {
fmt.Println("warning: --owner is ineffective for a Launchpad bridge")
}
if params.BaseURL != "" {
fmt.Println("warning: --base-url is ineffective for a Launchpad bridge")
}
conf := make(core.Configuration)
var err error

View File

@ -216,6 +216,7 @@ func init() {
bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureTarget, "target", "t", "",
fmt.Sprintf("The target of the bridge. Valid values are [%s]", strings.Join(bridge.Targets(), ",")))
bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.URL, "url", "u", "", "The URL of the target repository")
bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.BaseURL, "base-url", "b", "", "The base URL of your issue tracker service")
bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.Owner, "owner", "o", "", "The owner of the target repository")
bridgeConfigureCmd.Flags().StringVarP(&bridgeConfigureParams.CredPrefix, "credential", "c", "", "The identifier or prefix of an already known credential for the API (see \"git-bug bridge auth\")")
bridgeConfigureCmd.Flags().StringVar(&bridgeConfigureToken, "token", "", "A raw authentication token for the API")

View File

@ -39,6 +39,10 @@ Token configuration can be directly passed with the \-\-token flag or in the ter
\fB\-u\fP, \fB\-\-url\fP=""
The URL of the target repository
.PP
\fB\-b\fP, \fB\-\-base\-url\fP=""
The base URL of your issue tracker service
.PP
\fB\-o\fP, \fB\-\-owner\fP=""
The owner of the target repository

View File

@ -73,6 +73,7 @@ git bug bridge configure \
-n, --name string A distinctive name to identify the bridge
-t, --target string The target of the bridge. Valid values are [github,gitlab,launchpad-preview]
-u, --url string The URL of the target repository
-b, --base-url string The base URL of your issue tracker service
-o, --owner string The owner of the target repository
-c, --credential string The identifier or prefix of an already known credential for the API (see "git-bug bridge auth")
--token string A raw authentication token for the API

View File

@ -400,6 +400,10 @@ _git-bug_bridge_configure()
two_word_flags+=("--url")
two_word_flags+=("-u")
local_nonpersistent_flags+=("--url=")
flags+=("--base-url=")
two_word_flags+=("--base-url")
two_word_flags+=("-b")
local_nonpersistent_flags+=("--base-url=")
flags+=("--owner=")
two_word_flags+=("--owner")
two_word_flags+=("-o")

View File

@ -79,6 +79,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
[CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'The target of the bridge. Valid values are [github,gitlab,launchpad-preview]')
[CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'The URL of the target repository')
[CompletionResult]::new('--url', 'url', [CompletionResultType]::ParameterName, 'The URL of the target repository')
[CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'The base URL of your issue tracker service')
[CompletionResult]::new('--base-url', 'base-url', [CompletionResultType]::ParameterName, 'The base URL of your issue tracker service')
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'The owner of the target repository')
[CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'The owner of the target repository')
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'The identifier or prefix of an already known credential for the API (see "git-bug bridge auth")')

View File

@ -193,6 +193,7 @@ function _git-bug_bridge_configure {
'(-n --name)'{-n,--name}'[A distinctive name to identify the bridge]:' \
'(-t --target)'{-t,--target}'[The target of the bridge. Valid values are [github,gitlab,launchpad-preview]]:' \
'(-u --url)'{-u,--url}'[The URL of the target repository]:' \
'(-b --base-url)'{-b,--base-url}'[The base URL of your issue tracker service]:' \
'(-o --owner)'{-o,--owner}'[The owner of the target repository]:' \
'(-c --credential)'{-c,--credential}'[The identifier or prefix of an already known credential for the API (see "git-bug bridge auth")]:' \
'--token[A raw authentication token for the API]:' \