feat(extensions): scaffold config templates on extension add/enable (import of github/spec-kit#2000) - #30
Open
one-kash wants to merge 2 commits into
Open
feat(extensions): scaffold config templates on extension add/enable (import of github/spec-kit#2000)#30one-kash wants to merge 2 commits into
one-kash wants to merge 2 commits into
Conversation
Deploy an extension's provides.config templates into .specify/ when the extension is added or enabled. Existing files are never overwritten, so user customizations are preserved. Addresses the review on github#2000: - ExtensionManifest.config returns [] unless provides.config is a list of dicts, so a malformed manifest cannot crash callers. - scaffold_config returns a consistent (deployed, skipped_existing, failed) tuple on every path, including a missing manifest. - Template paths must resolve inside the extension dir and targets inside .specify/; symlinks and non-regular files are rejected. - Callers distinguish "already exists (preserved)" from "not scaffolded", and extension_enable no longer crashes on a corrupt manifest. - Tests cover traversal, absolute paths, symlinks, directory templates, malformed provides.config, and the missing-manifest tuple shape. Ported onto the extensions package introduced by github#3014: the manager and manifest changes land in extensions/__init__.py and the CLI wiring in extensions/_commands.py.
Addresses @Copilot's review. Config now lands in .specify/extensions/<id>/ rather than the .specify/ root. ConfigManager._get_project_config() reads .specify/extensions/<id>/<id>-config.yml, and the bundled scripts and READMEs use the same path, so a scaffolded git-config.yml was being written somewhere the git extension never looks. Containment is checked component by component before .specify is used as the root. Resolving it first and trusting the result let a symlinked component point outside the project, after which every target satisfied relative_to and copy2 wrote externally. This matches the project safe-write path in shared_infra. mkdir moved inside the OSError handler. A nested target like foo/config.yml raised out of scaffolding when its parent could not be created, and on extension add that happened after the extension was already installed. The 'Configuration may be required' warning is now conditional. It ran unconditionally after the scaffolding block, so it contradicted the success output directly above it and fired for extensions with no provides.config at all. Tests cover the corrected location, a symlinked config root, and an uncreatable nested target.
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.
Imported from upstream PR github#2000
Original author: @mvanhorn
Original head branch:
osc/fix-extension-scaffolding-lifecycleDescription
Rework of github#1929 per @mnriem's feedback. Instead of a standalone
specify extension initcommand, config scaffolding now runs automatically as part of the extension lifecycle:extension add: after installing an extension, config templates fromprovides.configare deployed to.specify/extension enable: when re-enabling a disabled extension, missing config templates are deployedExisting user-customized config files are never overwritten. Extensions without a
provides.configsection are unaffected.Changes:
ExtensionManifest.configproperty readsprovides.configfrom the manifestExtensionManager.scaffold_config()copies config templates to the project, skipping existing filesextension_addreplaces the "Configuration may be required" warning with automatic deploymentextension_enabledeploys config on re-enableVideo Demo
The demo shows: config template exists in extension dir, no project config yet,
scaffold_configdeploys it, re-running preserves existing config (idempotent).Testing
uv run specify --helpuv sync --extra test && uv run python -m pytest- all 890 tests passAI Disclosure
This contribution was developed with AI assistance (Claude Code + Codex CLI).
Closes github#1929