fix: prevent extension self-install from deleting source dir (#2990)#2991
Open
jinaparkdev wants to merge 1 commit into
Open
fix: prevent extension self-install from deleting source dir (#2990)#2991jinaparkdev wants to merge 1 commit into
jinaparkdev wants to merge 1 commit into
Conversation
…2990) `specify extension add <path> --dev --force` permanently deleted the extension directory without registering it when the source path resolved to the extension's own install location (`.specify/extensions/<id>`). With `--force`, `install_from_directory()` removed the existing installation (the source) and then `shutil.copytree()` tried to copy from the now-deleted directory, destroying it and crashing. Add a guard that fails fast with a clear ValidationError when the resolved source path equals the install destination, before any destructive operation runs. Includes a regression test asserting the directory and its contents survive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
specify extension add <path> --dev --forcepermanently deleted the extension directory without registering it when the source path resolved to the extension's own install location(
.specify/extensions/<id>).With
--force,install_from_directory()removed the existing installation (the source) and thenshutil.copytree()tried to copy from the now-deleted directory, destroying itand crashing.
Add a guard that fails fast with a clear ValidationError when the resolved source path equals the install destination, before any destructive operation runs. Includes a regression
test asserting the directory and its contents survive.
Description
When the source path passed to
extension add --dev --forceresolves to the extension's own install destination, the old flow removed the source and then copied from a directorythat no longer existed — destroying the extension with no registration. This PR adds an early
source == destinationcheck inExtensionManager.install_from_directory()(
src/specify_cli/extensions.py) that raises a clearValidationErrorbefore any removal/copy happens, so the directory is never touched on the error path.Testing
uv run specify --helpuv sync && uv run pytestManual end-to-end on macOS/zsh:
extension add .specify/extensions/<id> --dev --force(self-install) — with fixextension.yml+ commands left intactFileNotFoundError, directory destroyedextension add /other/path --dev --force(different location)Added regression test
test_install_from_install_dir_is_rejected_without_data_loss; full suitetests/test_extensions.py→ 283 passed.AI Disclosure
Developed with Claude Code (AI). The AI located the root cause, wrote the guard and the regression test. I reviewed every change, reproduced the original data-loss bug locally, and
verified the fix end-to-end myself before submitting.