Skip to content

Show the release command in the deploy and rollback plans (#120) - #136

Merged
shipmill-romamo[bot] merged 1 commit into
mainfrom
feat/120-release-command-plan
Oct 27, 2026
Merged

shipmill-romamo[bot] merged 1 commit into
mainfrom
feat/120-release-command-plan

Conversation

@shipmill-romamo

Copy link
Copy Markdown
Contributor

Summary

Shows S-005's release command in the plans (docs/specs/005-release-command.md), criteria S-005-8 and S-005-9. It was the spec's last build issue, so the spec is now status: built, with its Verification filled in.

  • deploy without --yes adds releaseCommand to its plan (release_command on the wire, like every snake_cased data key): command, workingDirectory resolved to the absolute path on the server (<installRoot>/releases/<release>, or the declared workingDirectory under it, the path release_command.yml passes to --working-directory), timeoutSeconds (300 when not declared) and server (the release server, deployment.servers[0]). For a component without release it is null. wouldRun adds that the command runs once on that server before any server switches
  • rollback without --yes, for a component that declares release, adds releaseCommandSkipped: true and its wouldRun says the release command is not run. For a component without one the key is null, since treaty writes every schema key on every answer
  • Confirmed on main that rollback runs no release command: rollback.yml includes the role's tasks/rollback.yml, which includes only health_gate.yml. release_command.yml is included from tasks/main.yml alone. No fix was needed
  • Output contract: commands.py declares releaseCommand (deploy plan) and releaseCommandSkipped? (rollback plan). releaseCommand gets named properties through a new KEY_PROPERTIES table, so treaty treats it as a closed object and writes a missing one as null instead of the {} that none_as_empty writes for an open object. The --schema output and the MCP tools that come from these commands pick it up from the same place. The change only adds keys
  • SDK: new lifecycle.ReleaseCommandPlan, plus two defaulted fields on LifecyclePreview (release_command, release_command_skipped)

Fixes #120
Part of #88

Test plan

  • sdk/tests/test_release_command_plan.py:
    • the CLI deploy plan shows release_command with the resolved working directory, the default and the declared timeout, and server h2 for deployment.servers: [h2, h1]; it shows null for a component without release
    • "no ansible-playbook in plan mode": the CLI points at a stand-in engine whose deploy.yml/rollback.yml only write a marker file on the controller. After the plan runs there is no marker and no rendered inventory. A control run with --yes on the same engine does leave the marker
    • the CLI rollback plan shows release_command_skipped: true, or null without release. A check of the tasks finds no release command task in rollback.yml or anything it includes
    • the deploy/rollback --schema output schemas declare the new keys (release_command as a nullable closed object)
    • the typed SDK preview (preview_deploy/preview_rollback)
  • Ran locally: uv run ruff check ., uv run mypy, uv run cloudfall config validate --project config/examples, specs.py check, and specs.py coverage --spec 005 (every S-005 criterion lists a test), plus tests of the touched files and their neighbours: test_release_command_plan.py, test_lifecycle.py, test_app.py, test_commands.py, test_agent_server.py, test_operator.py, test_domains_converge.py, test_inventory.py and engine/tests/test_release_command.py, all passing. No Ansible files changed, so ansible-lint was not run. The full suite is left to CI

Plan

  • Serves: quality bar
  • Files: sdk/src/cloudfall/lifecycle.py, sdk/src/cloudfall/commands.py, sdk/src/cloudfall/app.py, sdk/tests/test_release_command_plan.py, engine/README.md, docs/specs/005-release-command.md, changelog.d/120-release-command-plan.md
  • Order: confirm rollback runs no release command on main, resolve the release command into the deploy preview and the skip flag into the rollback preview, declare both keys in the output contract (a closed object, so null stays null), tests per criterion, then the spec's status and Verification (see the spec's Plan and rollback)
  • Proof: S-005-8, S-005-9 (specs.py coverage --spec 005 now lists a test for S-005-1 to S-005-11)

🤖 Generated with Claude Code

deploy without --yes now shows the component's release command as
releaseCommand: its command, the working directory resolved under the new
release directory, the timeout (300 by default) and the release server, or
null when the component declares none, and its wouldRun says the command
runs once on that server before any switch. A rollback never runs the
command; its plan says so and adds releaseCommandSkipped: true.

releaseCommand is declared in the output contract with its named
properties, so treaty writes a missing one as null rather than as {}. This
is S-005's last build issue, so the spec is now built, with its
Verification filled in.

Fixes #120

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shipmill-romamo

Copy link
Copy Markdown
Contributor Author

@romamo Decided without waiting: the deploy plan prints release_command.working_directory as the absolute path on the server (<installRoot>/releases/<release> plus any declared workingDirectory), and the new keys come out snake_case (release_command, release_command_skipped) like every other plan key.

Why: S-005-8 asks the plan to show where the command runs, and the absolute path is the one the engine uses. Snake_case is how every existing output key is written (wouldRun in the specs is would_run on the wire).
Not taken: the declared relative value as the receipt stores it (the plan would match the receipt but not say where the command runs), and camelCase keys (out of step with every other key).

Reply with a comment starting with veto before 2026-10-28 09:52 UTC to reverse it, adding what you want instead if you like. A later veto still counts and gets a follow-up.

@shipmill-romamo

Copy link
Copy Markdown
Contributor Author

@romamo Decided without waiting: deploy and rollback plans gain release_command (an object, or null for a component without release) and release_command_skipped, and the deploy output schema and its MCP tool describe them; cloudfall.lifecycle gains ReleaseCommandPlan and two defaulted LifecyclePreview fields.

Why: S-005-8 and S-005-9 ask for exactly these keys, and every existing key and caller behaves as before.
Not taken: holding the plan keys back, which would leave S-005 unbuilt.

Reply with a comment starting with veto before 2026-10-28 09:52 UTC to reverse it, adding what you want instead if you like. A later veto still counts and gets a follow-up.

@shipmill-romamo shipmill-romamo Bot added the decided An agent decided at notify; the maintainer may veto label Oct 27, 2026
@shipmill-romamo
shipmill-romamo Bot merged commit 8c2bad0 into main Oct 27, 2026
1 check passed
@shipmill-romamo
shipmill-romamo Bot deleted the feat/120-release-command-plan branch October 27, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

decided An agent decided at notify; the maintainer may veto

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show the release command in the deploy and rollback plans (S-005-8, S-005-9)

0 participants