Skip to content

fix: ensure installed files are owner-writable regardless of source permissions#2884

Open
rayhem wants to merge 1 commit into
github:mainfrom
rayhem:main
Open

fix: ensure installed files are owner-writable regardless of source permissions#2884
rayhem wants to merge 1 commit into
github:mainfrom
rayhem:main

Conversation

@rayhem
Copy link
Copy Markdown

@rayhem rayhem commented Jun 6, 2026

Description

shutil.copy2 and shutil.copytree (which uses copy2 by default) propagate permission bits from source to destination. When the source lives on a read-only filesystem — the Nix store, any read-only mount, or a permission-restricted directory — installed files land at 0o444 and directories at 0o555. Any subsequent write to .specify/ (re-running specify init, upgrading, editing installed config) then fails with PermissionError.

An install operation should produce owner-writable destinations. The installed file's mtime should also reflect when it was installed, not when the bundled asset was built — so the copy2copyfile change is correct on both counts.

I encountered this problem attempting to package spec-kit on NixOS. The repository gets added read-only to the Nix store. specify assumes it can write to the files it creates, but the copy utilities preserve the read-only permissions resulting in an error.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

Fix devised and generated by Claude Opus 4.6

…ermissions

shutil.copy2 and copytree propagate permission bits from the source,
leaving destination files at 0o444 and dirs at 0o555 when copying from
any read-only source (Nix store, read-only mounts, etc.). Subsequent
writes to .specify/ then fail with PermissionError.

Replace copy2 with copyfile (content-only, no permission bits) at all
four install-path call sites. Add ensure_writable_tree() to fix directory
permissions after copytree calls — copytree always stamps dest dirs via
copystat() regardless of copy_function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rayhem rayhem requested a review from mnriem as a code owner June 6, 2026 16:44
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.

1 participant