Skip to content

fix(install): guard link-mode conflicts#449

Merged
devinoldenburg merged 2 commits into
devinoldenburg:mainfrom
fengjikui:codex/guard-link-install-conflicts
Jul 8, 2026
Merged

fix(install): guard link-mode conflicts#449
devinoldenburg merged 2 commits into
devinoldenburg:mainfrom
fengjikui:codex/guard-link-install-conflicts

Conversation

@fengjikui

Copy link
Copy Markdown
Contributor

Summary

  • Refuse to replace existing non-symlink paths during --link installs unless --force is provided.
  • Apply the same conflict check to --dry-run so destructive link-mode conflicts are visible before install.
  • Add regression coverage for preserving a user-owned plugins/goal-mode/ directory and for the explicit --force replacement path.

Why

opencode-goal-mode-install --link removed existing destinations before creating symlinks. If a user already had a real plugins/goal-mode/ directory under their OpenCode config, the installer could delete those files without requiring --force. Copy installs already treat conflicts conservatively, so link installs should do the same.

Validation

  • Reproduced the original behavior with a temp config containing plugins/goal-mode/keep.txt: install exited 0 and removed the file.
  • Verified the fix: --link exits 1 and preserves keep.txt without --force.
  • Verified --link --force replaces the directory with a symlink.
  • npm test (26 pass)
  • npm run lint (passes; existing warnings only in unrelated files)
  • git diff --check

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@devinoldenburg devinoldenburg left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nice fix, this is definitely the right direction. One small thing before merging: prepareLinkDestination() uses existsSync(dest), which misses dangling symlinks because it follows the link. With a broken existing symlink, --link --force now skips cleanup and then fails with EEXIST when symlinkSync() runs. Could you switch that check to lstatSync() in a try/catch and treat only ENOENT as missing? A small regression test for a dangling symlink would be great too.

@fengjikui

fengjikui commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the dangling-symlink case and refreshed the branch on current main.

Current head: a32d1e28ecfe669d9f880370decd5baddbda461a

Changes:

  • prepareLinkDestination() now uses lstatSync() in a try/catch, treating only ENOENT as missing so dangling symlinks are still detected and removable.
  • Added a regression test covering --link --force replacing an existing dangling plugins/goal-mode symlink before calling symlinkSync().

Validation:

  • node --test tests/install.test.mjs (4 tests passed)
  • git diff --check

I also checked the full suite after rebasing. npm test currently fails only in tests/transcript.test.mjs at formatTranscript > applies truncation when transcript is very large; the same targeted test fails on upstream main head ad68debdf6dd20773b48e42b5a4ad80772ea49c3, so I left that unrelated transcript issue out of this installer PR.

@fengjikui fengjikui force-pushed the codex/guard-link-install-conflicts branch from 1909f6d to a32d1e2 Compare July 8, 2026 15:56
@devinoldenburg devinoldenburg merged commit 0b81080 into devinoldenburg:main Jul 8, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants