Repository navigation
Let projects use GitHub App installations; report a missing Members permission - #648
Merged
Merged
Conversation
A dashboard that runs only the GitHub App could link an installation but not create a project: the projects API only looked at the OAuth connection. The owner's GitHub sources are now its usable installations, in the order linked, then the OAuth connection, which is the order a sandbox checks a repository out in. The New Project checklist counts any of them as connected, GET /api/projects/repositories lists their repositories for the picker, and preflight, secret discovery and creation reach the repository through the first source that selected it, else by name through each source the caller may manage. Through an installation the files are read with a token minted for that one repository's contents. A removed or suspended installation answers installation_unavailable. Linking an organization's installation now reports members_permission when GitHub refuses the membership check with 403 (the App lacks Members: read), instead of not_admin. The dashboard guide documents the permission, that the dashboard callback must be the App's first callback URL, and how to link an installation that is already installed. Fixes #645 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCEJHsJpjXpDa9Kuq9i4Yg
The dashboard bundle is rebuilt from the merged sources. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCEJHsJpjXpDa9Kuq9i4Yg
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCEJHsJpjXpDa9Kuq9i4Yg
Merged
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.
Fixes #645
Behavior change
Projects accept a linked GitHub App installation (section 1).
Api::ProjectsControllernow resolves repositories through the owner's GitHub sources: usableGithubInstallations (only while an App is configured), in the order they were linked, then the OAuthGithubConnection. That is the same orderSandboxSession#repository_availablechecks a repository out in, so a project is read through the source its sandbox will clone from.github_connectionchecklist item holds when any source exists. With the App configured, its fix text now says "Install the GitHub App in Settings → Integrations".GET /api/projects/repositoriesfor the New Project picker. It lists the union of the installations' and the connection's repositories, each withselectedandsource(app/oauth), selections first and installations ahead of the connection. An installation lists repositories outside its selection only to a caller with:manage_github; anyone else sees its selection. The connection keeps today's listing.NewProject.jsxcalls this endpoint now instead of/api/github_connection/repositories.default_refchange go to the first source that selected the repository. Failing that, the repository is looked up by name through each source the caller may manage. The403is still decided before GitHub is asked. Through an installation the files are read with a token minted forcontents: readon that one repository id, used for the request only and never stored. Creating a project from an unselected repository selects it on the source it was found through.422 installation_unavailablewithreinstall_required: true, and the installation is marked the way a checkout marks it.GithubConnectionandGithubInstallationnow sharereachable_repositories,find_repository,contents_clientandcheckout_source, so the controller treats them the same way.Missing Members permission (section 2).
GithubClient#organization_membershipstill returns nil on 404. On 403 it now raisesGithubClient::MembershipForbidden, and the installation callback reports?github_app=members_permissioninstead ofnot_admin. Settings shows a message explaining how to add and accept Members (read), with a note about SAML SSO. The manifest flow already requestedmembers: read; the docs now explain why it is needed.Callback order (section 3, docs).
docs/framework/dashboard.mdnow says the dashboard callback must be the App's first callback URL, and documents thecallback?installation_id=<id>&setup_action=updatelink for an App that is already installed.Deferred
GET /user/installationspicker from section 3). It needs a new user-authorization flow that has no installation id, plus a way to choose among several installations without keeping the user token, so I left it for a follow-up. The documented callback link covers the gap for now.Tests
actionagent/test/projects_github_app_test.rb(11 tests) covers:contents: read,repository_ids: [id]):manage_githubgate, with no mint when it is deniedinstallation_unavailablegithub_app_client_test.rb: a 403 raisesMembershipForbidden.github_installations_test.rb: a 403 redirects withmembers_permission, notnot_admin.1845 runs, 13824 assertions, 0 failures, 0 errors, 7 skips.npm testpasses 711/711, andnpm run buildregeneratedapp/assets/builds/action_agent.js.🤖 Generated with Claude Code
https://claude.ai/code/session_01PCEJHsJpjXpDa9Kuq9i4Yg