diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go index 9a46e7b1d287540d1a565268778d87e841571c35..c6731ff9a06ceeea306a1950bc62ebb517d3d3b6 100644 --- a/bridge/core/bridge.go +++ b/bridge/core/bridge.go @@ -35,6 +35,7 @@ type BridgeParams struct { Owner string Project string URL string + BaseURL string CredPrefix string TokenRaw string } diff --git a/bridge/github/config.go b/bridge/github/config.go index a74e7f6b8fa5051b810c066d831ea94fa9fd84ba..bc26a2fc740c6e01be8899405f25cbf263b3bd11 100644 --- a/bridge/github/config.go +++ b/bridge/github/config.go @@ -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 diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go index 7bc2e577b0ac3fab53a4bb329de80c482e2f41b1..99c27836dd87dedbbb020f6ae9db35f9df1be24c 100644 --- a/bridge/gitlab/config.go +++ b/bridge/gitlab/config.go @@ -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 { diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go index 373cf637e18a5093e85d060d4e8d83f281798672..d42ef1cdf1b6311b5272fc6451c3bb0fd7d20883 100644 --- a/bridge/gitlab/export.go +++ b/bridge/gitlab/export.go @@ -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 { diff --git a/bridge/gitlab/export_test.go b/bridge/gitlab/export_test.go index 645e2d7692f84e7c94a95b34a1c561573cca5083..d16defd007468fb454d2d6ff3fbc75618fb6ea0c 100644 --- a/bridge/gitlab/export_test.go +++ b/bridge/gitlab/export_test.go @@ -188,7 +188,8 @@ func TestPushPull(t *testing.T) { // initialize exporter exporter := &gitlabExporter{} err = exporter.Init(backend, core.Configuration{ - keyProjectID: strconv.Itoa(projectID), + keyProjectID: strconv.Itoa(projectID), + keyGitlabBaseUrl: "https://gitlab.com/", }) require.NoError(t, err) @@ -215,7 +216,8 @@ func TestPushPull(t *testing.T) { importer := &gitlabImporter{} err = importer.Init(backend, core.Configuration{ - keyProjectID: strconv.Itoa(projectID), + 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 } diff --git a/bridge/gitlab/gitlab.go b/bridge/gitlab/gitlab.go index bcc50e4cce2b7389c949d5699dc5178f10e4d9c5..9298dc8edbb7dae90c1e8048c21fa29b20596f8e 100644 --- a/bridge/gitlab/gitlab.go +++ b/bridge/gitlab/gitlab.go @@ -17,9 +17,12 @@ const ( metaKeyGitlabUrl = "gitlab-url" metaKeyGitlabLogin = "gitlab-login" metaKeyGitlabProject = "gitlab-project-id" + metaKeyGitlabBaseUrl = "gitlab-base-url" - keyProjectID = "project-id" + 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 } diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go index 00dee25215f4b6cb82d9f00e06ba01fea0378430..4fa375055d8e7aa9df95d60d91bf677f8c264f85 100644 --- a/bridge/gitlab/import.go +++ b/bridge/gitlab/import.go @@ -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], }, ) diff --git a/bridge/gitlab/import_test.go b/bridge/gitlab/import_test.go index 1676bdf35bd62820ac96c493b2c6ebe4fa4583a7..6e378b075432da5a0c7d4fdf22e3ba4bdc705562 100644 --- a/bridge/gitlab/import_test.go +++ b/bridge/gitlab/import_test.go @@ -103,7 +103,8 @@ func TestImport(t *testing.T) { importer := &gitlabImporter{} err = importer.Init(backend, core.Configuration{ - keyProjectID: projectID, + keyProjectID: projectID, + keyGitlabBaseUrl: "https://gitlab.com", }) require.NoError(t, err) diff --git a/bridge/launchpad/config.go b/bridge/launchpad/config.go index 8db39100cf8bbaa752a426f0739a88d7543e4b6f..edbd941d9a44971bceb0ccf8c3d9ca55ab795445 100644 --- a/bridge/launchpad/config.go +++ b/bridge/launchpad/config.go @@ -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 diff --git a/commands/bridge_configure.go b/commands/bridge_configure.go index 00634b287dcf6674938768a18c1fbd0e564bed6c..26bd7bc22c6de74527ac94e56324826f7204264e 100644 --- a/commands/bridge_configure.go +++ b/commands/bridge_configure.go @@ -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") diff --git a/doc/man/git-bug-bridge-configure.1 b/doc/man/git-bug-bridge-configure.1 index 14b773a6335081fd776605ef38f3afba6d94bd92..d1dc9f7dcef3dc2d05e29b1b8c9ac15ab14512df 100644 --- a/doc/man/git-bug-bridge-configure.1 +++ b/doc/man/git-bug-bridge-configure.1 @@ -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 diff --git a/doc/md/git-bug_bridge_configure.md b/doc/md/git-bug_bridge_configure.md index 73121072e271816ced98076bbed782e4ff210872..c0f89cf3da89cad9546c44b610e9640e0fa02378 100644 --- a/doc/md/git-bug_bridge_configure.md +++ b/doc/md/git-bug_bridge_configure.md @@ -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 diff --git a/misc/bash_completion/git-bug b/misc/bash_completion/git-bug index 707369c5523c29a15791fcfe41e7581226aab594..557bbf2094454ca8049a47c98971480699716214 100644 --- a/misc/bash_completion/git-bug +++ b/misc/bash_completion/git-bug @@ -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") diff --git a/misc/powershell_completion/git-bug b/misc/powershell_completion/git-bug index b6086f276164fb511d703c2ef994e1bccb5f7fe0..d52113e4b75fffc4d91a9c9cf3a87a8b7e47d9a6 100644 --- a/misc/powershell_completion/git-bug +++ b/misc/powershell_completion/git-bug @@ -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")') diff --git a/misc/zsh_completion/git-bug b/misc/zsh_completion/git-bug index a0b4840ba1cd8a6570cd96e1850ae96cbe9a29a1..3b06a3963af63e72262828efddbe4a0f2303b8a4 100644 --- a/misc/zsh_completion/git-bug +++ b/misc/zsh_completion/git-bug @@ -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]:' \