Skip to content

Disable keyring in installer canary - #571

Merged
jeremy merged 1 commit into
mainfrom
fix/installer-canary-keyring
Jul 27, 2026
Merged

Disable keyring in installer canary#571
jeremy merged 1 commit into
mainfrom
fix/installer-canary-keyring

Conversation

@jeremy

@jeremy jeremy commented Jul 27, 2026

Copy link
Copy Markdown
Member

The canary added in #569 hung on its first two macOS runs (push-triggered and dispatched) for the full 10-minute timeout-minutes, right after "Skipping setup wizard". The cancelled job's orphan processes were basecamp and security: the post-install skill step reaches for the OS keyring, and on a headless macOS runner the security tool blocks forever on the locked keychain. The Linux leg passed on both runs.

Set BASECAMP_NO_KEYRING=1 at the workflow level — the same escape hatch test.yml uses. The variable reaches the installer's child basecamp processes through the step environment.

Verification: will dispatch the canary after merge and confirm both legs pass.

Refs #569


Summary by cubic

Disable keyring usage in the installer canary workflow to stop macOS runs from hanging on headless keychain prompts. Sets BASECAMP_NO_KEYRING=1 at the workflow level so child basecamp processes skip the security keychain call; Linux runs are unchanged.

Written for commit 8d8b5ad. Summary will update on new commits.

Review in cubic

The canary's first macOS run hung for its full 10-minute timeout after
"Skipping setup wizard": the post-install skill step reaches for the OS
keyring, and on a headless runner the `security` tool blocks forever on
the locked keychain (the cancelled job's orphan processes were
`basecamp` and `security`). Set BASECAMP_NO_KEYRING=1 workflow-wide,
the same escape hatch the Test workflow uses.

Refs #569
Copilot AI review requested due to automatic review settings July 27, 2026 19:17
@github-actions

Copy link
Copy Markdown

Sensitive Change Detection (shadow mode)

This PR modifies control-plane files:

  • .github/workflows/installer-smoke.yml

Shadow mode — this check is informational only. When activated, changes to these paths will require approval from a maintainer.

@github-actions github-actions Bot added the ci CI/CD workflows label Jul 27, 2026
@github-actions github-actions Bot added the bug Something isn't working label Jul 27, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the installer smoke canary workflow to disable keyring usage so macOS runs don’t hang on headless keychain prompts during the post-install skill step.

Changes:

  • Sets BASECAMP_NO_KEYRING=1 at the workflow level in installer-smoke.yml so it applies to all jobs/steps (including child basecamp processes).
  • Documents the rationale inline, matching the existing escape-hatch usage in test.yml.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

Re-trigger cubic

@jeremy
jeremy merged commit f707dae into main Jul 27, 2026
27 checks passed
@jeremy
jeremy deleted the fix/installer-canary-keyring branch July 27, 2026 19:22
jeremy added a commit that referenced this pull request Jul 27, 2026
Every CLI command runs root PersistentPreRunE -> appctx.NewApp ->
auth.NewManager -> auth.NewStore, and credstore.NewStore eagerly probes
OS keyring availability with a write. On macOS that probe shells out to
an uncancellable `security` child; a locked keychain with no TTY or GUI
blocks it forever — on *every* command, including credential-free ones
like `setup agents` and `skill install`. That is how the installer hung
headless (#568, canary discovery #569/#571 -> #574 drill lineage).

Go fix (reaches users at the next release): Store now records the
fallback dir at construction and builds the credstore lazily behind
sync.Once on first credential operation. Credential-free commands never
touch the keyring; credential-touching commands probe on first use
exactly as before, with the documented BASECAMP_NO_KEYRING=1 escape
hatch. No timeout was added deliberately: an abandoned goroutine cannot
kill the hung `security` child, so a timeout would hide the hang while
keeping the leak. Bounded fallback needs cancellation support upstream
in credstore/go-keyring first.

Installer belt (deploys on merge, covers released binaries <= v0.7.2
which still probe eagerly): post_install_setup in install.sh prefixes
each best-effort child with per-command BASECAMP_NO_KEYRING=1;
Invoke-PostInstallSetup in install.ps1 sets it for the duration of setup
and restores the caller's value on exit. These children never touch
credentials — the var only skips the old binaries' startup probe.

Tests: keyring_test.go pins constructor laziness through an injectable
credstore seam. installer.bats gains belt tests that explicitly unset
the suite-wide BASECAMP_NO_KEYRING and assert both directions (real
calls carry it, the sh capability probe does not), plus a pwsh test that
extracts Invoke-PostInstallSetup from install.ps1's AST (no test hooks
in the production script), runs it against a logging stub, and asserts
set + restore; it fails closed when pwsh is missing in CI.

The canary's workflow-level BASECAMP_NO_KEYRING is removed with no
step-scoped replacement: the macOS leg now genuinely exercises the
deployed belt (a missing belt = hung security child = timeout = notify),
and the direct `basecamp version` asserts skip PersistentPreRunE
entirely.

Refs #558 #568 #569
jeremy added a commit that referenced this pull request Jul 27, 2026
* Defer keyring probe to first credential use; belt the installers

Every CLI command runs root PersistentPreRunE -> appctx.NewApp ->
auth.NewManager -> auth.NewStore, and credstore.NewStore eagerly probes
OS keyring availability with a write. On macOS that probe shells out to
an uncancellable `security` child; a locked keychain with no TTY or GUI
blocks it forever — on *every* command, including credential-free ones
like `setup agents` and `skill install`. That is how the installer hung
headless (#568, canary discovery #569/#571 -> #574 drill lineage).

Go fix (reaches users at the next release): Store now records the
fallback dir at construction and builds the credstore lazily behind
sync.Once on first credential operation. Credential-free commands never
touch the keyring; credential-touching commands probe on first use
exactly as before, with the documented BASECAMP_NO_KEYRING=1 escape
hatch. No timeout was added deliberately: an abandoned goroutine cannot
kill the hung `security` child, so a timeout would hide the hang while
keeping the leak. Bounded fallback needs cancellation support upstream
in credstore/go-keyring first.

Installer belt (deploys on merge, covers released binaries <= v0.7.2
which still probe eagerly): post_install_setup in install.sh prefixes
each best-effort child with per-command BASECAMP_NO_KEYRING=1;
Invoke-PostInstallSetup in install.ps1 sets it for the duration of setup
and restores the caller's value on exit. These children never touch
credentials — the var only skips the old binaries' startup probe.

Tests: keyring_test.go pins constructor laziness through an injectable
credstore seam. installer.bats gains belt tests that explicitly unset
the suite-wide BASECAMP_NO_KEYRING and assert both directions (real
calls carry it, the sh capability probe does not), plus a pwsh test that
extracts Invoke-PostInstallSetup from install.ps1's AST (no test hooks
in the production script), runs it against a logging stub, and asserts
set + restore; it fails closed when pwsh is missing in CI.

The canary's workflow-level BASECAMP_NO_KEYRING is removed with no
step-scoped replacement: the macOS leg now genuinely exercises the
deployed belt (a missing belt = hung security child = timeout = notify),
and the direct `basecamp version` asserts skip PersistentPreRunE
entirely.

Refs #558 #568 #569

* test(installer): pin restore of a caller-set BASECAMP_NO_KEYRING

The pwsh belt test proved restore-to-unset only; a regression that
overwrites an existing caller value would have passed. Second driver
pass sets the var to 0 before Invoke-PostInstallSetup and asserts it
survives. Review feedback on #578.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ci CI/CD workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants