Skip to content

fix(ci+installers): harden permission checks and guard completion/profile writes#1247

Merged
TabishB merged 3 commits into
mainfrom
codex/investigate-ci-failure-and-fix-root-cause
Jun 24, 2026
Merged

fix(ci+installers): harden permission checks and guard completion/profile writes#1247
TabishB merged 3 commits into
mainfrom
codex/investigate-ci-failure-and-fix-root-cause

Conversation

@TabishB

@TabishB TabishB commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

  • Tests that simulate permission-denied behavior were flaking when run as root because fs.access(..., W_OK) can report writable for root, causing permission-sensitive tests and lock-create logic to pass unexpectedly.
  • Completion installers and state-file locks mutate user files (.zshrc, PowerShell profile, completion scripts, lock files) without pre-validating writability, which can hide errors or produce confusing diagnostics in CI.
  • CI Node version 20 is ambiguous; pin to the package-engine floor 20.19.0 to avoid platform/engine differences when reproducing failures.

Description

  • Make write checks deterministic: add POSIX mode-bit validation plus fs.access via hasWritableModeAndAccess() and update FileSystemUtils.canWriteFile() to prefer mode-bit checks on POSIX, preserving Windows behavior (file: src/utils/file-system.ts).
  • Validate lock directory writability before creating locks in acquireFileLock() so permission errors produce the correct create-failed diagnostic (file: src/core/file-state.ts).
  • Guard completion install/uninstall and profile edits by checking FileSystemUtils.canWriteFile() before mutating files or unlinking, for Fish, PowerShell and Zsh installers (files: src/core/completions/installers/fish-installer.ts, src/core/completions/installers/powershell-installer.ts, src/core/completions/installers/zsh-installer.ts).
  • Make Zsh permission-error tests deterministic in CI by using local read-only temp fixtures instead of relying on /root paths (file: test/core/completions/installers/zsh-installer.test.ts).
  • Pin Node in CI workflow to 20.19.0 for all Node setup steps in .github/workflows/ci.yml to reduce CI variability.

Testing

  • Ran focused unit suites: corepack pnpm@9 exec vitest run test/core/completions/installers/bash-installer.test.ts test/core/completions/installers/zsh-installer.test.ts test/core/completions/installers/fish-installer.test.ts test/core/completions/installers/powershell-installer.test.ts test/core/file-state.test.ts and observed all focused tests pass (permission-related failures are fixed).
  • Built and type-checked: corepack pnpm@9 run build && corepack pnpm@9 exec tsc --noEmit && corepack pnpm@9 lint succeeded; lint reports one preexisting warning in src/core/references.ts.
  • Full suite: corepack pnpm@9 test was exercised during investigation; earlier full runs in this constrained environment still exhibited unrelated, long-running e2e/timeouts (environment/order-sensitive) but the root permission-related failures addressed here no longer repro in focused coverage.

Codex Task

Summary by CodeRabbit

  • Bug Fixes
    • Completion install/uninstall now performs upfront write-permission checks for target paths (and relevant parent directories), failing early with clearer “Path is not writable” errors.
    • File locking now verifies the lock directory is writable before creating it.
    • Writability detection is more consistent across platforms using unified permission logic for files and directories.
  • Tests
    • Updated and added permission-focused installer and filesystem tests for determinism, with Windows skips where needed.
  • Chores
    • Updated CI workflow Node.js version across multiple jobs.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 96ca6afc-b0ef-4e4e-83b5-8019392e7d60

📥 Commits

Reviewing files that changed from the base of the PR and between 632259d and 634195b.

📒 Files selected for processing (2)
  • test/core/completions/installers/bash-installer.test.ts
  • test/core/completions/installers/powershell-installer.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/core/completions/installers/powershell-installer.test.ts
  • test/core/completions/installers/bash-installer.test.ts

📝 Walkthrough

Walkthrough

Adds writability checks in filesystem helpers, completion installers, and file locking, updates related tests for permission-restricted paths, and pins GitHub Actions Node.js to 20.19.0.

Changes

Filesystem Writability Checks

Layer / File(s) Summary
FileSystemUtils permission helpers
src/utils/file-system.ts
Adds internal writability helpers, updates canWriteFile to use them for files and directories, and changes the ENOENT parent-directory fallback to use the same helper.
Installer and lock writability guards
src/core/completions/installers/bash-installer.ts, src/core/completions/installers/fish-installer.ts, src/core/completions/installers/powershell-installer.ts, src/core/completions/installers/zsh-installer.ts, src/core/file-state.ts
Adds preflight canWriteFile checks in completion installers and acquireFileLock.
Permission-based installer tests
test/core/completions/installers/*, test/utils/file-system.test.ts
Updates permission-focused tests to use chmod-restricted temp paths, Windows skips, and permission restoration helpers.

CI Node.js Version Pin

Layer / File(s) Summary
Node.js 20.19.0 pin in CI workflow
.github/workflows/ci.yml
Changes the Setup Node.js version in four jobs from 20 to 20.19.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • alfred-openspec

Poem

🐇 I hop where paths are writable,
And check before I write.
The lock stays safe, the scripts comply,
And CI pins things tight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: CI pinning plus hardened permission checks for installers and profile/completion writes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/investigate-ci-failure-and-fix-root-cause

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@TabishB TabishB marked this pull request as ready for review June 24, 2026 05:04

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/completions/installers/fish-installer.ts`:
- Around line 143-145: The uninstall check in fish-installer’s target removal
path is too strict because it requires the completion file itself to be writable
before unlinking; adjust the precondition in the targetPath/targetDir check so
only the parent directory’s writability is enforced, and keep the unlink path in
FishInstaller aligned with POSIX delete semantics by relying on targetDir rather
than FileSystemUtils.canWriteFile(targetPath).

In `@src/core/completions/installers/powershell-installer.ts`:
- Around line 415-417: The uninstall path in powershell-installer.ts is checking
both the target file and its parent directory for writability, but `uninstall()`
should only gate on the containing directory because `fs.unlink()` depends on
directory permissions, not the file mode. Update the `uninstall()` logic around
`FileSystemUtils.canWriteFile` to remove the target file check and rely on the
directory-derived path (`path.dirname(targetPath)`) while keeping the existing
error handling for unwritable locations.

In `@src/utils/file-system.ts`:
- Around line 20-25: The permission check in canWriteFile is too weak for
directories because W_OK alone can pass on a write-only directory even though
POSIX operations still fail without execute/search access. Update canWriteFile
to treat directories differently by also requiring execute/search permission
before returning true, while keeping the existing behavior for regular files,
and use the same helper path around fs.access and hasOwnerGroupOrOtherWriteBit
to keep the preflight guard accurate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a2757577-54fc-4603-8325-dff8e67e5e19

📥 Commits

Reviewing files that changed from the base of the PR and between a0decbe and 6455e9b.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • src/core/completions/installers/fish-installer.ts
  • src/core/completions/installers/powershell-installer.ts
  • src/core/completions/installers/zsh-installer.ts
  • src/core/file-state.ts
  • src/utils/file-system.ts
  • test/core/completions/installers/zsh-installer.test.ts

Comment thread src/core/completions/installers/fish-installer.ts Outdated
Comment thread src/core/completions/installers/powershell-installer.ts Outdated
Comment thread src/utils/file-system.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/core/completions/installers/bash-installer.test.ts`:
- Around line 167-168: The current assertion only checks for a generic install
failure message, so update this test to verify the permission-specific
diagnostic emitted by the new preflight path instead of just any “Failed to
install” text. Use the existing result?.message check in the bash installer test
to assert the exact permission-related wording returned by the installer, so
regressions to a generic write failure will be caught.

In `@test/core/completions/installers/powershell-installer.test.ts`:
- Line 262: The tests in powershell-installer.test.ts are mutating
process.env.OPENSPEC_NO_AUTO_CONFIG and only deleting it, which leaves global
state changed for later cases. Update the affected test(s) by saving the
original OPENSPEC_NO_AUTO_CONFIG value before mutation and restoring it in
cleanup, or by using a shared afterEach in the test suite so each case returns
the environment to its prior state. Use the test blocks that currently touch
process.env.OPENSPEC_NO_AUTO_CONFIG as the fix points.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f0cd3ea-709d-45ef-add5-8213a2cd82f9

📥 Commits

Reviewing files that changed from the base of the PR and between 6455e9b and 632259d.

📒 Files selected for processing (8)
  • src/core/completions/installers/bash-installer.ts
  • src/core/completions/installers/fish-installer.ts
  • src/core/completions/installers/powershell-installer.ts
  • src/utils/file-system.ts
  • test/core/completions/installers/bash-installer.test.ts
  • test/core/completions/installers/fish-installer.test.ts
  • test/core/completions/installers/powershell-installer.test.ts
  • test/utils/file-system.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/completions/installers/fish-installer.ts
  • src/utils/file-system.ts

Comment thread test/core/completions/installers/bash-installer.test.ts
Comment thread test/core/completions/installers/powershell-installer.test.ts Outdated

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the permission-guard changes and follow-up fixes. The implementation now checks parent-dir writability for uninstall paths, preserves PowerShell env state in tests, and covers the POSIX mode-bit/root-CI edge cases.\n\nVerified locally:\n- corepack pnpm@9 exec vitest run test/core/completions/installers/bash-installer.test.ts test/core/completions/installers/zsh-installer.test.ts test/core/completions/installers/fish-installer.test.ts test/core/completions/installers/powershell-installer.test.ts test/core/file-state.test.ts test/utils/file-system.test.ts → 198 passed\n- corepack pnpm@9 exec tsc --noEmit && corepack pnpm@9 lint → passed, with only the preexisting src/core/references.ts unused eslint-disable warning\n\nShip it.

@TabishB TabishB added this pull request to the merge queue Jun 24, 2026
Merged via the queue into main with commit 41ceebe Jun 24, 2026
12 checks passed
@TabishB TabishB deleted the codex/investigate-ci-failure-and-fix-root-cause branch June 24, 2026 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants