mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add GitLab support to coderd/externalauth/gitprovider
Fixes CODAGT-146 Add GitLab support to the gitprovider package for gitsync/chatd PR diff flows. This is a squashed stack of 3 PRs: #25651 - refactor(coderd/externalauth): prepare gitprovider for multi-provider support - Change gitprovider.New to return (Provider, error) - Extract shared helpers (parseRetryAfter, checkRateLimitError, countDiffLines, escapePathPreserveSlashes) from github.go - Update all callers (db2sdk, exp_chats, gitsync) for new signature - Add error logging for provider construction failures - Thread context through provider resolution #25652 - feat(coderd/externalauth/gitprovider): add GitLab provider - Implement full Provider interface: FetchPullRequestStatus, FetchPullRequestDiff, FetchBranchDiff, ResolveBranchPullRequest - Handle nested groups, forks, and self-hosted instances - Rate limit detection on both library and raw HTTP paths - URL parsing/building with NormalizePullRequestURL support - Unit tests covering error paths, URL parsing, state mapping - Document GitLab configuration and known limitations #25653 - test(coderd/externalauth/gitprovider): add GitLab VCR integration tests - FetchPullRequestStatus: 4 fixtures (open, conflicts, merged, closed) - FetchPullRequestDiff: 4 fixtures - FetchBranchDiff: 3 fixtures (open, deleted, fork) - ResolveBranchPullRequest: 3 fixtures - go-vcr cassettes with sanitized GitLab API responses
This commit is contained in:
@@ -30,7 +30,7 @@ const (
|
||||
|
||||
// ProviderResolver maps a git remote origin to the gitprovider
|
||||
// that handles it. Returns nil if no provider matches.
|
||||
type ProviderResolver func(origin string) gitprovider.Provider
|
||||
type ProviderResolver func(ctx context.Context, origin string) gitprovider.Provider
|
||||
|
||||
var ErrNoTokenAvailable error = errors.New("no token available")
|
||||
|
||||
@@ -159,7 +159,7 @@ func (r *Refresher) Refresh(
|
||||
// duplicate resolution for rows in the same group.
|
||||
var resolved []resolvedGroup
|
||||
for key, indices := range groups {
|
||||
provider := r.providers(key.origin)
|
||||
provider := r.providers(ctx, key.origin)
|
||||
if provider == nil {
|
||||
err := xerrors.Errorf("no provider for origin %q", key.origin)
|
||||
for _, i := range indices {
|
||||
|
||||
@@ -128,7 +128,7 @@ func TestRefresher_WithPRURL(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func TestRefresher_BranchResolvesToPR(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -226,7 +226,7 @@ func TestRefresher_BranchNoPRYet(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -255,7 +255,7 @@ func TestRefresher_BranchNoPRYet(t *testing.T) {
|
||||
func TestRefresher_NoProviderForOrigin(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return nil }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return nil }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func TestRefresher_TokenResolutionFails(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return nil, errors.New("token lookup failed")
|
||||
}
|
||||
@@ -328,7 +328,7 @@ func TestRefresher_EmptyToken(t *testing.T) {
|
||||
|
||||
mp := &mockProvider{}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref(""), nil
|
||||
}
|
||||
@@ -366,7 +366,7 @@ func TestRefresher_ProviderFetchFails(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -402,7 +402,7 @@ func TestRefresher_PRURLParseFailure(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -440,7 +440,7 @@ func TestRefresher_BatchGroupsByOwnerAndOrigin(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
|
||||
var tokenCalls atomic.Int32
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
@@ -522,7 +522,7 @@ func TestRefresher_UsesInjectedClock(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -574,7 +574,7 @@ func TestRefresher_RateLimitSkipsRemainingInGroup(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
@@ -695,7 +695,7 @@ func TestRefresher_CorrectTokenPerOrigin(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
|
||||
r := gitsync.NewRefresher(providers, tokens, slogtest.Make(t, nil), quartz.NewReal())
|
||||
|
||||
@@ -780,7 +780,7 @@ func TestRefresher_ConcurrentProcessing(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(_ string) gitprovider.Provider { return mp }
|
||||
providers := func(_ context.Context, _ string) gitprovider.Provider { return mp }
|
||||
tokens := func(_ context.Context, _ uuid.UUID, _ string) (*string, error) {
|
||||
return ptr.Ref("test-token"), nil
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func newTestRefresher(t *testing.T, clk quartz.Clock, opts ...testRefresherOpt)
|
||||
},
|
||||
}
|
||||
|
||||
providers := func(string) gitprovider.Provider { return prov }
|
||||
providers := func(context.Context, string) gitprovider.Provider { return prov }
|
||||
tokens := func(context.Context, uuid.UUID, string) (*string, error) {
|
||||
return ptr.Ref("tok"), nil
|
||||
}
|
||||
@@ -1120,7 +1120,7 @@ func TestRefreshChat_RefreshError(t *testing.T) {
|
||||
// UpsertChatDiffStatus should NOT be called.
|
||||
|
||||
// Provider resolver returns nil → "no provider" error.
|
||||
providers := func(string) gitprovider.Provider { return nil }
|
||||
providers := func(context.Context, string) gitprovider.Provider { return nil }
|
||||
tokens := func(context.Context, uuid.UUID, string) (*string, error) {
|
||||
return ptr.Ref("tok"), nil
|
||||
}
|
||||
@@ -1205,7 +1205,7 @@ func TestWorker_NoTokenBackoff(t *testing.T) {
|
||||
// Token resolver returns empty token → ErrNoTokenAvailable.
|
||||
// Provider methods should never be called.
|
||||
prov := &mockProvider{}
|
||||
providers := func(string) gitprovider.Provider { return prov }
|
||||
providers := func(context.Context, string) gitprovider.Provider { return prov }
|
||||
tokens := func(context.Context, uuid.UUID, string) (*string, error) {
|
||||
return ptr.Ref(""), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user