feat!: add repos.getCloneCredentials - #12
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The shared toCloneUrl implementation and the exported RepoProvider interface change introduce correctness/backcompat risks that should be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a new repos.getCloneCredentials API to return clone credentials separately from a credential-free clone URL, and refactors providers so getCloneUrl is derived from these credentials via a shared helper. This fits the SDK’s normalized-provider design by offering a safer integration path for git credential helpers while keeping the existing clone-URL functionality.
Changes:
- Introduces
CloneCredentials/GetCloneCredentialsParamsand wiresrepos.getCloneCredentialsthrough the client + provider interface. - Refactors all providers to implement
getCloneCredentialsand derivesgetCloneUrlvia the new sharedtoCloneUrlhelper. - Adds provider/unit tests and updates docs/README/CLAUDE.md to document the new method and credential redaction exceptions.
File summaries
| File | Description |
|---|---|
| test/unit/client.test.ts | Updates the unit test fake RepoProvider to include getCloneCredentials. |
| test/providers/testing.test.ts | Adds in-memory provider coverage for client.repos.getCloneCredentials. |
| test/providers/gitlab.test.ts | Adds GitLab getCloneCredentials tests, including numeric project ID behavior. |
| test/providers/github.test.ts | Adds GitHub getCloneCredentials test. |
| test/providers/github-app.test.ts | Adds GitHub App installation token + expiresAt propagation test for clone credentials. |
| test/providers/gitea.test.ts | Adds Gitea token-in-username-slot clone-credentials test. |
| test/providers/git-http.test.ts | Adds anonymous + basic-auth clone-credentials tests for git-http. |
| test/providers/bitbucket.test.ts | Adds Bitbucket clone-credentials tests for API token vs access token auth. |
| test/providers/azure-devops.test.ts | Adds Azure DevOps clone-credentials tests for PAT vs OAuth. |
| src/types.ts | Adds CloneCredentials + GetCloneCredentialsParams; extends RepoProvider with getCloneCredentials. |
| src/providers/testing/index.ts | Implements getCloneCredentials for the in-memory provider; derives getCloneUrl via helper. |
| src/providers/shared.ts | Adds toCloneUrl helper to embed credentials into a credential-free URL. |
| src/providers/gitlab/index.ts | Implements getCloneCredentials; derives getCloneUrl via toCloneUrl; removes GitLab-specific injector. |
| src/providers/github/index.ts | Implements getCloneCredentials; derives getCloneUrl via toCloneUrl. |
| src/providers/gitea/index.ts | Implements getCloneCredentials; derives getCloneUrl via toCloneUrl. |
| src/providers/git-http/index.ts | Implements getCloneCredentials; derives getCloneUrl via toCloneUrl. |
| src/providers/bitbucket/index.ts | Implements getCloneCredentials; derives getCloneUrl via toCloneUrl. |
| src/providers/azure-devops/index.ts | Implements getCloneCredentials; derives getCloneUrl via toCloneUrl. |
| src/index.ts | Exports the new types (CloneCredentials, GetCloneCredentialsParams). |
| src/client.ts | Exposes repos.getCloneCredentials on the public client API. |
| README.md | Updates “API at a glance” and the credential-redaction exception note to include getCloneCredentials. |
| docs/guides/downloading-code.mdx | Documents repos.getCloneCredentials and warns to treat returned credentials as secrets. |
| docs/concepts/client-and-providers.mdx | Updates the repos namespace overview list to include getCloneCredentials. |
| CLAUDE.md | Updates the redaction exception note to include getCloneCredentials. |
Review details
- Files reviewed: 24/24 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| getCloneCredentials(params: GetCloneCredentialsParams): Promise<CloneCredentials>; | ||
| /** The same credentials embedded in the URL; derived from `getCloneCredentials`. */ | ||
| getCloneUrl(params: GetCloneUrlParams): Promise<CloneUrl>; |
There was a problem hiding this comment.
Agreed, this does break custom RepoProvider implementations. Rather than making the method optional with a URL-parsing fallback, which would reintroduce the parsing this PR removes, the PR is now marked as a breaking change (feat!: plus a BREAKING CHANGE: footer) so release-please bumps the minor version and records it in the changelog.
| export function toCloneUrl(credentials: CloneCredentials): CloneUrl { | ||
| const { expiresAt, password, url, username } = credentials; | ||
| if (username === null && password === null) return { url, expiresAt }; | ||
| const user = username === null ? '' : encodeURIComponent(username); | ||
| const userinfo = password === null ? user : `${user}:${encodeURIComponent(password)}`; | ||
| return { url: url.replace('://', `://${userinfo}@`), expiresAt }; | ||
| } |
There was a problem hiding this comment.
Skipping. toCloneUrl is internal (not exported from the package entry) and its only callers are the built-in providers, which construct the credential-free url themselves, so the invariant holds by construction. The URL API would not reject the username: null plus password shape either, and its setters use a different percent-encoding set and normalize the URL, which would change the existing getCloneUrl output this PR keeps unchanged.
|
Deploy preview available at: https://be1e845d.repo-sdk-page.pages.dev. Built with commit 186bee3. |
repos.getCloneCredentialsrepos.getCloneCredentials
Adds
repos.getCloneCredentials, the clone credential returned separately from the URL, for consumers that hand credentials to a git credential helper instead of embedding them in the remote URL. Today the only way to get one isrepos.getCloneUrl, which forces consumers to parse the userinfo back out of the URL and percent-decode it.CloneCredentialsis{ expiresAt?, password, url, username }:urlnever carries userinfo,usernameandpasswordare raw (never percent-encoded),passwordisnullwhere the provider puts the token in the username slot (Gitea) or access is anonymous (git-httpwithoutauth), andusernameisnullfor anonymous access. Provider usernames are unchanged (x-access-token,oauth2,pat,x-bitbucket-api-token-auth,x-token-auth), andexpiresAtis still set where it was before (GitHub App installation tokens).Also:
getCloneUrlkeeps its exact contract and is now derived: every provider builds credentials, and the newtoCloneUrlhelper insrc/providers/shared.tsembeds them as percent-encoded userinfo, one URL builder instead of six per-provider string templates. GitLab'sinjectCredentialsis gone.http_url_to_repothrough the API;git-httpstill normalizes and rejects credential-bearing remote URLs.getCloneUrlindocs/guides/downloading-code.mdx, plus the namespace overview, the README "API at a glance" table and the README/CLAUDE.md redaction-exception note.getCloneCredentialsblock per provider: password-slot token, Gitea's username-slot token, GitLab numeric id, Azurepatvsoauth2, Bitbucket's two usernames,git-httpanonymous,expiresAtpropagation, and thaturlcarries no userinfo.BREAKING CHANGE:
getCloneCredentialsis a new required method on theRepoProviderinterface. Custom provider implementations must add it; the built-in providers already do.getCloneUrlcan be derived from it withtoCloneUrlsemantics: embedusernameandpasswordas percent-encoded userinfo inurl.