feat(source-control): package-manager-aware gh upgrade command#3809
feat(source-control): package-manager-aware gh upgrade command#3809Noisemaker111 wants to merge 4 commits into
gh upgrade command#3809Conversation
…enticated" When `gh` is installed but predates `gh auth status --json` (added in gh v2.81.0, cli/cli#11544), the server's auth probe can't be parsed, so the Source Control settings row shows a misleading "Not authenticated" — even though the user is signed in. Running `gh auth login` never clears it. Add an "outdated" source-control auth status: - contracts: new `outdated` literal on SourceControlProviderAuthStatus - server: thread the CLI `--version` output and host platform into parseAuth; the GitHub provider classifies the unknown-`--json`-flag / sub-2.81.0 case as outdated and puts an OS-appropriate upgrade command in `detail` - web: red version number + "Outdated CLI" badge + a copyable upgrade command Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ommand
- UpgradeCommandLine: `navigator.clipboard` is undefined in non-secure contexts
and some embedded webviews, so the copy handler threw. Guard it and fall back
to a hidden-textarea `execCommand("copy")`, only showing "Copied" on success.
- gitHubCliUpgradeCommand: return null for platforms without a known package
manager (freebsd, openbsd, sunos, aix) instead of an `apt` command that can't
run there; the row then shows the generic "update it" guidance with no
command line. Adds a regression test for the freebsd case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dd14b8a to
ac14be9
Compare
| // When the CLI is outdated, let the provider probe how it was | ||
| // installed and refine the upgrade command. Failures fall back to | ||
| // the platform default already carried on `auth.detail`. | ||
| if (auth.status === "outdated" && spec.resolveUpgradeCommand !== undefined) { |
There was a problem hiding this comment.
🟡 Medium sourceControl/SourceControlProviderDiscovery.ts:282
When auth.status === "outdated" and spec.resolveUpgradeCommand is defined, the code always runs the resolver and, if it returns a non-empty string, overwrites the auth detail — including for unsupported platforms like freebsd. parseAuth intentionally returns outdated with generic guidance (no upgrade command) on such platforms, but resolveUpgradeCommand treats every non-darwin/non-win32 host as Linux, so it can return a bogus dpkg/rpm/pacman command whenever one of those tools happens to be installed. Users on unsupported Unix hosts would be told to run the wrong upgrade command instead of the previous generic guidance. Consider only invoking resolveUpgradeCommand on platforms it actually supports, or having the resolver return null for unsupported platforms.
Also found in 1 other location(s)
apps/server/src/sourceControl/GitHubSourceControlProvider.ts:156
resolveGitHubUpgradeCommandtreats any successfulrpm -q ghprobe as proof thatdnfis the correct package manager and returnssudo dnf upgrade gh. On RPM-based systems that do not usednf(for example openSUSE/SLES, which usezypperbut still answerrpm -q gh), the discovery flow will show a non-working upgrade command. That breaks this PR's package-manager-aware guidance for those hosts instead of falling back to a usable generic hint.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/sourceControl/SourceControlProviderDiscovery.ts around line 282:
When `auth.status === "outdated"` and `spec.resolveUpgradeCommand` is defined, the code always runs the resolver and, if it returns a non-empty string, overwrites the `auth` detail — including for unsupported platforms like `freebsd`. `parseAuth` intentionally returns `outdated` with generic guidance (no upgrade command) on such platforms, but `resolveUpgradeCommand` treats every non-`darwin`/non-`win32` host as Linux, so it can return a bogus `dpkg`/`rpm`/`pacman` command whenever one of those tools happens to be installed. Users on unsupported Unix hosts would be told to run the wrong upgrade command instead of the previous generic guidance. Consider only invoking `resolveUpgradeCommand` on platforms it actually supports, or having the resolver return `null` for unsupported platforms.
Also found in 1 other location(s):
- apps/server/src/sourceControl/GitHubSourceControlProvider.ts:156 -- `resolveGitHubUpgradeCommand` treats any successful `rpm -q gh` probe as proof that `dnf` is the correct package manager and returns `sudo dnf upgrade gh`. On RPM-based systems that do not use `dnf` (for example openSUSE/SLES, which use `zypper` but still answer `rpm -q gh`), the discovery flow will show a non-working upgrade command. That breaks this PR's package-manager-aware guidance for those hosts instead of falling back to a usable generic hint.
…ated An `outdated` CLI can't confirm sign-in, but the readiness checks only gated on `unauthenticated`, so GitHub was marked usable and pull-request / publish / add-project actions were enabled and then failed at runtime. Treat `outdated` as not-ready (with an "update it" hint) in all three readiness paths: - CommandPalette add-project readiness - GitActionsControl publish readiness - client-runtime add-project remote readiness Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ommand When GitHub is flagged as an outdated CLI, probe the host for how `gh` was actually installed and surface the exact upgrade command instead of a single per-OS default: - winget / scoop / choco on Windows - brew / macports on macOS - apt (dpkg) / dnf (rpm) / pacman on Linux Adds an optional `resolveUpgradeCommand` hook to the CLI discovery spec, run from `probeSourceControlProvider` only when `parseAuth` reports `outdated`. Any probe failure falls back to the platform default from the base change, so this can never fail discovery. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ac14be9 to
ce83182
Compare
| readiness[source] = { ready: false, hint: provider.installHint }; | ||
| continue; | ||
| } | ||
| if (provider.auth.status === "outdated") { |
There was a problem hiding this comment.
🟡 Medium operations/projects.ts:154
buildAddProjectRemoteSourceReadiness now marks providers with provider.auth.status === "outdated" as not ready, but outdated means the CLI may still be authenticated — it just can't report auth status. Providers that are actually usable will be incorrectly disabled for cloning.
Also found in 2 other location(s)
apps/web/src/components/CommandPalette.tsx:315
buildAddProjectRemoteSourceReadinessnow marksprovider.auth.status === "outdated"as not ready, which disables the remote-add flow for that provider even though the contract explicitly says anoutdatedCLI user 'may well be signed in — we just can't read it'. Before this change those providers fell through to{ ready: true }, so a user with an older but still authenticated CLI can no longer add a project from that provider.
apps/web/src/components/GitActionsControl.tsx:228
getPublishProviderReadinessnow returnsready: falsefordiscovered.auth.status === "outdated", which blocks the publish dialog from selecting or submitting that provider. TheSourceControlProviderAuthStatuscontract saysoutdatedonly means the CLI is too old to report auth status and the user may still be signed in, so this change introduces a regression where publishing is disabled for users whose old CLI would still work.
🤖 Copy this AI Prompt to have your agent fix this:
In file @packages/client-runtime/src/operations/projects.ts around line 154:
`buildAddProjectRemoteSourceReadiness` now marks providers with `provider.auth.status === "outdated"` as not ready, but `outdated` means the CLI may still be authenticated — it just can't report auth status. Providers that are actually usable will be incorrectly disabled for cloning.
Also found in 2 other location(s):
- apps/web/src/components/CommandPalette.tsx:315 -- `buildAddProjectRemoteSourceReadiness` now marks `provider.auth.status === "outdated"` as not ready, which disables the remote-add flow for that provider even though the contract explicitly says an `outdated` CLI user 'may well be signed in — we just can't read it'. Before this change those providers fell through to `{ ready: true }`, so a user with an older but still authenticated CLI can no longer add a project from that provider.
- apps/web/src/components/GitActionsControl.tsx:228 -- `getPublishProviderReadiness` now returns `ready: false` for `discovered.auth.status === "outdated"`, which blocks the publish dialog from selecting or submitting that provider. The `SourceControlProviderAuthStatus` contract says `outdated` only means the CLI is too old to report auth status and the user may still be signed in, so this change introduces a regression where publishing is disabled for users whose old CLI would still work.
| * `navigator.clipboard` is undefined. Falls back to a hidden-textarea + | ||
| * `execCommand("copy")`. Returns whether the copy succeeded. | ||
| */ | ||
| async function copyTextToClipboard(text: string): Promise<boolean> { |
There was a problem hiding this comment.
🟡 Medium settings/SourceControlSettings.tsx:261
The execCommand("copy") fallback in copyTextToClipboard fails in webviews where the async Clipboard API exists but rejects. After await navigator.clipboard.writeText() throws, the fallback runs in a promise continuation rather than the original click handler. document.execCommand("copy") only works inside a user-gesture call stack, so the fallback silently fails to copy in exactly the environments it was added to support. Consider trying execCommand synchronously first, or restructuring so the fallback executes before the microtask boundary.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/settings/SourceControlSettings.tsx around line 261:
The `execCommand("copy")` fallback in `copyTextToClipboard` fails in webviews where the async Clipboard API exists but rejects. After `await navigator.clipboard.writeText()` throws, the fallback runs in a promise continuation rather than the original click handler. `document.execCommand("copy")` only works inside a user-gesture call stack, so the fallback silently fails to copy in exactly the environments it was added to support. Consider trying `execCommand` synchronously first, or restructuring so the fallback executes before the microtask boundary.
feat(source-control): package-manager-aware
ghupgrade commandWhat this ships
Follow-up to #3808. When GitHub is flagged as an Outdated CLI, instead of a single per-OS default command, the server probes the host for how
ghwas actually installed and surfaces the exact upgrade command:winget upgrade --id GitHub.cli -e· scoop →scoop update gh· choco →choco upgrade ghbrew upgrade gh· macports →sudo port upgrade ghsudo apt … gh· rpm/dnf →sudo dnf upgrade gh· pacman →sudo pacman -S github-cliImplementation:
resolveUpgradeCommandhook onSourceControlCliDiscoverySpec, run fromprobeSourceControlProvideronly whenparseAuthreportsoutdated(so there's zero cost on the healthy path).allowNonZeroExitpresence-probes per platform; any failure falls back to the platform default from feat(source-control): detect outdated GitHub CLI instead of "Not authenticated" #3808, so it can never fail discovery.Run-in-terminal — design proposal (NOT implemented here)
The original idea was a one-click "Run in terminal" button on this row (dashed in the mockup). Investigating, that turns out to need an architecture decision I don't want to make unilaterally:
ThreadTerminalDrawer) is mounted only insideChatViewand is keyed by athreadId(ScopedThreadRef). The Settings route unmountsChatView, so there is no terminal to write to from Settings.threadId— the only way to obtain one is the fullthread.createorchestration command (requiresmodelSelection/runtimeMode, and creates a real, visible thread).Two viable paths, both bigger than a button:
environmentProjects.environmentProjectRefsAtom(→workspaceRoot), reuse or create a thread, route into that chat view, then replicate theopenTerminal+writeTerminalsequence (asrunProjectScriptdoes inChatView.tsx). Downside: yanks the user out of Settings and may create a stray thread.ScopedThreadRef-scoped terminal drawer inside the Settings screen. Cleaner UX, but a self-contained new surface.Happy to implement whichever you prefer in a follow-up — flagging it here rather than guessing. The Copy button (from #3808) already covers the immediate need.
Testing / caveats
outdated-detection tests from feat(source-control): detect outdated GitHub CLI instead of "Not authenticated" #3808 still apply. The manager-probe resolver is best-effort and gated behind the outdated path; I couldn't run the repo toolchain locally, so please let CI runpnpm typecheck+ tests.Note
Add package-manager-aware
ghupgrade command to source control CLI discoveryghCLI versions by checking forunknown flag: --jsonin output or by comparing against a minimum version (2.81.0) using semver comparison.outdatedauth status fromparseGitHubAuthwith a platform-specific default upgrade command (brew,winget, orapt).ghwas actually installed.SourceControlProviderAuthStatuscontract schema with the newoutdatedliteral value.📊 Macroscope summarized dd14b8a. 4 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.