Skip to content

fix(bundles): don't refresh installed components on a repeated bundle init - #4963

Open
kartsan03 wants to merge 3 commits into
github:mainfrom
kartsan03:fix/bundle-init-no-implicit-refresh
Open

kartsan03 wants to merge 3 commits into
github:mainfrom
kartsan03:fix/bundle-init-no-implicit-refresh

Conversation

@kartsan03

Copy link
Copy Markdown
Contributor

Description

Running specify bundle init <bundle> a second time refreshes the bundle's components that are already installed, even though bundle init has no --refresh option. A local edit to an installed workflow is silently replaced, and the command exits 0.

bundle_init calls the Typer-decorated bundle_install() directly (src/specify_cli/bundles/command_init.py:51 on main) without passing refresh. So refresh keeps its parameter default, typer.Option(False, ...), which is an OptionInfo object and truthy. install_bundle then takes the explicit-refresh path (installer.py:119, :175). It's the same option-default pitfall #4284 fixed for the workflow_add call. The same truthy value also lets bundle init accept a changed bundle version that a plain bundle install refuses without --refresh.

This PR passes refresh=False explicitly from bundle_init. Direct bundle install and bundle install --refresh are unchanged. docs/reference/bundles.md gets one paragraph: repeating bundle init keeps installed components as they are, and bundle update / bundle install --refresh are the ways to refresh them.

Repro with the first-party bugfix bundle, offline: init, append a line to .specify/workflows/bugfix/workflow.yml, init again.

After the second specify bundle init bundles/bugfix --offline main (3238fa9) This PR
Install summary 0 added, 0 already present, 2 refreshed, 0 removed 0 added, 2 already present
Local edit to workflow.yml gone kept

Testing

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

New tests in tests/specify_cli/bundles/test_command_init.py:

  • bundle_init delegates to bundle_install with refresh=False (and doesn't call it without a bundle).
  • In process with CliRunner and the real bugfix bundle: a repeated bundle init keeps a local edit to the installed workflow (bytes compared, CRLF included) and leaves the registry alone; a plain bundle install does the same, and bundle install --refresh still restores the bundled file.
  • bundle init with a changed bundle version exits 1 asking for --refresh, and leaves the bundle records and the edited workflow untouched.

With only src/ reverted to main, 3 of the 7 tests in the file fail (the delegation check, the repeated-init case and the changed-version case). With the fix, all 54 tests in test_command_init.py, test_command_install.py and test_commands.py pass.

Full suite on Linux, Python 3.13: 12093 passed, 374 skipped (uv sync --extra test, then pytest in that venv, 20m).

Ruff 0.15.0 (ruff check src tests) passes.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (fill in the disclosure below)

AI disclosure: Two agents, both autonomous, working back and forth. Codex CLI (model: gpt-6-astra, reasoning effort max, autonomous, sandboxed and offline) found this while scouting the bundle commands, wrote the reproduction, and generated the fix, tests and docs paragraph. Claude Code (model: claude-opus-5-5, autonomous) reviewed the change, reran the tests against main and the fix, reran the repro with the real specify CLI, ran the full suite and lint, and drafted this description. Commits carry Assisted-by: trailers.

… init

bundle_init called the Typer-decorated bundle_install() without refresh, so
refresh kept its typer.Option(False) default, a truthy OptionInfo. A second
`specify bundle init <bundle>` then took the explicit-refresh path: it
replaced local edits to installed workflows and accepted a changed bundle
version that a plain `bundle install` refuses without --refresh. Pass
refresh=False explicitly, as github#4284 did for the workflow_add call.

Assisted-by: Codex CLI (model: gpt-6-astra, autonomous)
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The focused fix has targeted regression coverage, with only a non-blocking documentation clarification remaining.

1 open finding
What changed in this PR

Prevents repeated Specify CLI bundle initialization from unintentionally refreshing installed components and overwriting local edits.

Changes:

  • Passes refresh=False explicitly during bundle initialization.
  • Adds regression coverage for preserved edits, changed-version rejection, and explicit refresh.
  • Documents repeat-init behavior and refresh alternatives.

Tests were not rerun; regression results were assessed from the supplied evidence.

File Description
tests/​specify_cli/​bundles/​test_command_init.py Tests delegation, preservation, refresh, and version rejection.
src/​specify_cli/​bundles/​command_init.py Explicitly disables implicit refresh.
docs/​reference/​bundles.md Explains preservation and refresh options.

🧠 Review effort: Max


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/reference/bundles.md Outdated
@mnriem

mnriem commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

With refresh no longer implied, a repeated `bundle init` goes through the
same record check as a plain `bundle install`: a changed bundle version, or
a changed or removed owned component, fails until `bundle update` or
`bundle install --refresh` applies it.

Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
@kartsan03

Copy link
Copy Markdown
Contributor Author

Addressed Copilot's docs finding in 335d29f. The bundle init section now says a repeated init doesn't apply manifest changes: as with a plain bundle install, it fails without changing the installed bundle when the manifest changes the recorded bundle version, changes an owned component's version, source, preset priority or strategy, or removes an owned component. It points to bundle update and bundle install --refresh for those. This is the record check in installer.py that bundle init now goes through. Docs only; markdownlint passes on the file.

Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous), which also made the docs change.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The focused fix has targeted regression coverage, with only a nonblocking documentation correction remaining.

1 open finding
1 resolved since last review

🧠 Review effort: Max

Comment thread docs/reference/bundles.md Outdated

Ensures the current directory is a Spec Kit project (initializing it idempotently if needed), then optionally installs the given bundle. Useful as an explicit one-step bootstrap for a new checkout.

Repeating `bundle init` skips components already installed by the bundle and preserves their local edits. It does not apply manifest changes: as with a normal [`bundle install`](#install-a-bundle), it fails without changing the installed bundle if the manifest changes the recorded bundle version, changes an owned component's version, source, preset priority, or strategy, or removes an owned component. To refresh components or apply such changes, use `bundle update <id>` for a catalog bundle or `bundle install <path> --refresh` for a local source.
@mnriem

mnriem commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback. If not applicable, please explain why

…ents

Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)

This branch has not been deployed

No deployments
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