Same-org GitHub submodule repository access (scoped tokens)#1253
Merged
simple-agent-manager[bot] merged 7 commits intoJun 9, 2026
Merged
Conversation
a082ea6 to
d68bc0c
Compare
8f1d52e to
0dfaba7
Compare
Add project-scoped "Repository Access" so workspace GitHub tokens can be minted with explicit repository_ids covering the primary repo plus selected same-installation/same-org repositories (Codespaces-style). Primary repo is always included implicitly. - New project_github_repositories table (migration 0065, additive) storing verified additional repos with captured github_repo_id/node_id. - Repository access CRUD + .gitmodules discovery routes; user∩app access is verified before storing and re-checked at token-mint boundaries. - Workspace /git-token minting always sets repository_ids = [primary, ...active additional], never omitting it, with active profile GitHub/platform policy. - RepositoryAccessSettings UI in Project Settings shows per-repo access status (active, access-revoked, app-not-installed, unsupported-url) and submodule suggestions; profile platform-policy copy clarifies the repo *set* scope. - VM-agent best-effort same-org submodule init using the multi-repo token via inline insteadOf rewrite (never persisted, token redacted in logs). - Tests: multi-repo mint scoping, .gitmodules parser, access-assertion helpers, and RepositoryAccessSettings behavioral UI tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…section Replace free-text-only owner/repo entry in project Repository Access with a searchable combobox populated from the live user∩app GitHub installation intersection (GET /:id/repository-access/available), while preserving manual free-text entry as a fallback and keeping the .gitmodules discovery section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9ac268e to
fc634f4
Compare
- Rewrite repository-access-settings tests to the combobox interaction model (Enter commits manual owner/repo entry; lazy-loaded intersection) - Mock listProjectRepositories/discoverSubmoduleRepos/listAvailableRepositories in project.test.tsx so RepositoryAccessSettings (now in the settings tab) does not fire unmocked async calls during the runtime-env test - Correct git_credential.go doc comment to reflect multi-repo scoped tokens Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ories The delete cascade now removes project_github_repositories rows, bumping each batch/delete count by one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract resolveGitHubProjectContext helper to de-duplicate the project/installation resolution shared by the repository-access available + discover handlers. - Convert single-entry parseGitmodules tests to a table-driven it.each to remove structural duplication. - Suppress intentional thenable mock (drizzle-compatible) and the controlled-PATH git exec in initSubmodules with NOSONAR. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SonarCloud Automatic Analysis honors sonar.exclusions but ignores sonar.cpd.exclusions, so the new repository-access visual-audit spec's self-contained scaffolding counted as new-code duplication (5.3% > 3% gate). Visual-audit specs carry no behavioral logic and rule 17 mandates they be self-contained per file, so fully excluding them from analysis is correct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Import shared scaffolding (makeMockUser, screenshot, assertNoOverflow, setupAuditRoutes) from audit-helpers and restructure the route dispatch to a data-driven form, removing the duplicated lines that tripped the SonarCloud new-code duplication gate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Implements SAM idea
01KTHVE80Q4BVJYRG7D23A6WPY— Codespaces-style "additional Repository Access": let a workspace pull same-org GitHub submodules under a single, tightly-scoped installation access token.Rebased onto
main(the GitHub-token hardening branch, PR #1252, is now merged).Summary
repository_ids: [primaryRepoId, ...additionalRepoIds]and narrowed permissions — no broad org-wide token.insteadOfto inject the token at clone time without persisting credentials to.git/config..gitmodulesdiscovery surfaces candidate submodule repos and flags which are accessible through the installation.Implementation
API
apps/api/src/db/migrations/0065_project_github_repositories.sql+schema.ts: newproject_github_repositoriestable (project/user FKs CASCADE,github_repo_id, unique(project_id, repository); status is runtime-derived, not stored).apps/api/src/routes/projects/repository-access.ts(mounted inprojects/index.ts):GET /:id/repository-access— list (primary + additional)POST /:id/repository-access— add (access-gated against the installation)DELETE /:id/repository-access/:repoRowId— removeGET /:id/repository-access/discover— discover.gitmodulessuggestionsapps/api/src/services/github-app.ts: multi-repo scoped token minting +assertRepositoryAccesspreflight.apps/api/src/routes/workspaces/runtime.ts: inject the scoped multi-repo token into workspace boot.apps/api/src/services/limits.ts,schemas/projects.ts: limits + validation.Web
apps/web/src/components/RepositoryAccessSettings.tsx+RepositoryAccessCombobox.tsx— settings UI (primary "always included" badge, searchable combobox sourced from the user∩app installation intersection with manual owner/repo fallback, Discover/Re-scan.gitmodules, remove). Rendered only for GitHub-backed projects, wired intoProjectSettings.tsx.apps/web/src/lib/api/projects.ts— list/add/remove/discover/available client fns.VM agent
packages/vm-agent/internal/bootstrap/bootstrap.go—initSubmodulesinitializes submodules under the injected scoped token at boot.Shared
packages/shared/src/types/project.ts—ProjectRepository,ProjectRepositoryStatus,SubmoduleSuggestion,AvailableRepository; defaults/constants.Tests
apps/api/tests/unit/routes/project-repository-access.test.ts—normalizeRepository/isValidRepositoryFormat/assertRepositoryAccess.apps/api/tests/unit/routes/workspace-git-token.test.ts— multi-repo scoped token minting.apps/api/tests/unit/services/github-app.test.ts— installation access gating.apps/web/tests/unit/components/repository-access-settings.test.tsx— RTL behavioral tests aligned to the combobox interaction model (render, load primary, add/remove lifecycle, discover, access-status badges, error toasts, empty-state).apps/web/tests/unit/pages/project.test.tsx— mocks the repository-access API fns so the settings-tab integration does not fire unmocked async calls.typecheckandlintclean (0 errors).Review Follow-ups (this branch)
After rebase, a skeptical review found and fixed two issues that the original PR body misrepresented:
project.test.tsxregressed becauseRepositoryAccessSettingswas added to the settings tab without mocking its API calls; the missing mocks were added.git_credential.goto reflect multi-repo scoped tokens.Residual Risks / Caveats
packages/vm-agent/internal/bootstrap/bootstrap.go(initSubmodules) could not be compiled or unit-tested locally; it relies on the CI Go jobs and the staging Deploy build.🤖 Generated with Claude Code
Agent Preflight (Required)
Classification
External References
GitHub REST API — "Create an installation access token for an app" supports scoping a token to specific repositories via
repository_idsand narrowingpermissions, which is the mechanism used for multi-repo workspace tokens. Verified against GitHub's official documentation: https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app and https://docs.github.com/en/rest/git/submodulesCodebase Impact Analysis
apps/api/src/routes/projects/repository-access.ts,apps/api/src/routes/projects/crud.ts(delete cascade),apps/api/src/services/github-app.ts,apps/api/src/routes/workspaces/runtime.ts,apps/api/src/db/migrations/0065_project_github_repositories.sql,apps/api/src/db/schema.ts,apps/api/src/services/limits.ts.apps/web/src/components/RepositoryAccessSettings.tsx,apps/web/src/components/RepositoryAccessCombobox.tsx,apps/web/src/lib/api/projects.ts,apps/web/src/pages/ProjectSettings.tsx.packages/vm-agent/internal/bootstrap/bootstrap.go,packages/vm-agent/internal/server/git_credential.go.packages/shared/src/types/project.ts.Documentation & Specs
N/A: no public www docs or specs reference repository-access behavior yet; the work is tracked in
tasks/active/2026-06-08-same-org-submodule-repo-access.md.Constitution & Risk Check
Principle II (per-user encrypted credentials) and Principle XI (no hardcoded values — limits via
getRuntimeLimits(), sentinel-free counts) checked. Key risk: workspace tokens must remain tightly scoped to[primaryRepoId, ...additionalRepoIds]and never widen to org-wide access;assertRepositoryAccessgates every add against the installation. Multi-tenant: each add is access-gated and rows are project/user-scoped with CASCADE cleanup on project delete.