Instruction file imported from DanielCarmel/courtyard (
.github/instructions/git-providers.instructions.md). Copyright stays with the author.
Git Provider Implementation
Interface Contract
Every provider must implement GitProvider exactly. All methods:
- Accept
ctx context.Contextas first param - Accept
token string— the end-user's OAuth access token - Never cache or store tokens beyond the request scope
- Never use app-level or admin tokens
Client Construction
- Create a new API client per request — never reuse or cache clients
- For GitHub:
github.NewClient(github.WithHTTPClient(oauth2HttpClient)) - For Bitbucket: create a new
*http.Clientwith Bearer token per request
Pagination
- Always handle paginated API responses — never assume single page
- GitHub: check
resp.NextPage != 0, loop until exhausted - Bitbucket: follow the
nextURL field in response JSON until absent - Set reasonable page size (100 per page)
Error Handling
- Wrap API errors with provider + method context:
fmt.Errorf("github.GetRepositories: %w", err) - Translate HTTP status codes to meaningful errors:
- 401 → user token expired/invalid
- 403 → insufficient permissions
- 404 → resource not found (repo, file, branch)
- 422 → validation error (branch already exists, etc.)
- Return structured error types where the handler needs to distinguish cases
GitHub-Specific
- Use Git Trees API (
recursive: true) forGetTemplateFiles— one call for the full tree - Use
client.Git.GetBlob()for individual file content after getting tree - Reject blobs > 1MB
- For
CreateBranchAndPullRequest:GetRef→ get base branch SHA- Handle
branchMode: check if branch exists, reuse or error accordingly CreateTree→ all output files in one treeCreateCommit→ single commit with all changesUpdateReforCreateRef→ point branch at new commitPullRequests.Create→ open PR (skip if reuse mode and PR already open)
Bitbucket-Specific
- Base URL:
https://api.bitbucket.org/2.0 - Workspace aggregation: call
GET /workspaces?role=memberfirst, then aggregate repos across all workspaces - File commits: use
POST /repositories/{workspace}/{repo}/srcwithmultipart/form-data - No tree API — commit files directly in one request
- Tokens expire in 1 hour — refresh transparently using refresh_token
Testing
- Mock with
httptest.NewServer— return canned JSON responses - Test pagination (multi-page responses)
- Test error cases (401, 404, network errors)
- Test the full
CreateBranchAndPullRequestflow with mock responses for each step