Skip to content

v0.3.3 — 2026-05-09

PATCH bump fixing a Windows-only --force upgrade crash that surfaced as soon as users started upgrading from baselines that had cloned godot-docs into .claude/skills/godot-api/doc_source/.

  • PermissionError [WinError 5] on --force upgrade. publish.py cleared .claude/skills/ via plain shutil.rmtree, which on Windows refuses to unlink read-only files. Git writes pack-*.idx files as r--r--r--, and any prior version that cloned godot-docs left those files behind. All three shutil.rmtree call sites now go through a new rmtree_force() helper that clears the read-only bit in the rmtree onerror/onexc hook and retries.
  • The crash had a worse downstream symptom than the error suggested. main() aborted before .godotmaker/version was stamped, so any caller re-invoking publish.py on installed_version != target_version would re-trigger the same crash on every retry — looking like an "infinite republish" symptom from the caller's side rather than a one-off filesystem error.

No migration script is required — this is a runtime fix to publish.py itself; nothing about target-project layout or content changes.

Changed

  • tools/publish.py — added rmtree_force() helper and _rmtree_handle_readonly() onerror/onexc hook (signature compatible with Python 3.10–3.11 onerror and 3.12+ onexc). Replaced all three shutil.rmtree(...) call sites: copy_tree(), the MAJOR --force cleanup loop, and the elif args.force skills cleanup branch.
  • tests/tools/test_publish.py — added TestPublishMainForceRmtree driving real publish.main(--force) against a target seeded with .godotmaker/version=0.3.1 and a read-only pack-*.idx under .claude/skills/godot-api/doc_source/.git/objects/pack/. Mutation-verified: reverting the rmtree fix makes the test fail with the real PermissionError [WinError 5]. This branch had zero integration coverage before — tests/tools/test_publish.py was unit-only and the only main()-driving test (tests/tools/test_migrate.py::TestPublishMainMigrationRouting) seeded an empty target so the elif args.force and skills_target.exists() branch was never exercised. Also added TestRmtreeForce unit tests, with cross-platform notes in both classes' docstrings (the read-only failure is Windows-only by nature; on Linux/macOS the tests verify the helper doesn't break the normal-tree path).

Fixed

  • Windows users upgrading from any prior version that cloned godot-docs no longer hit PermissionError [WinError 5] when running publish.py --force. The crash previously aborted the entire publish before .godotmaker/version was stamped, leaving downstream version-comparing callers stuck in a re-trigger loop.