test(cli): pin deploy init scaffold env.secret round-trip (#3158)#3204
Conversation
#3008 added a $rejectEnvSecrets guard that hard-failed any non-empty env.secret with Wheels.Deploy.EnvSecretUnsupported, which made the env.secret block scaffolded by `wheels deploy init` (WHEELS_RELOAD_PASSWORD) un-deployable without manual editing. #3167 then retired that guard and implemented env.secret delivery via a remote --env-file (600 perms, SFTP), so the scaffolded block is now correct and deploys end-to-end. This pins that contract with two DeployMainCliSpec regression tests: - the init scaffold round-trips through config() and deploy --dry-run with no EnvSecretUnsupported/EnvSecretMissing, and the dry-run routes the secret through the --env-file path; - a deploy of the scaffold delivers WHEELS_RELOAD_PASSWORD to the role env file over SFTP (FakeSshPool uploadString), never in argv. Both tests fail if the scaffold drops its env.secret block (verified by temporarily removing it), so they guard the scaffold and the deploy engine against drifting apart again. No template or engine change is needed: the scaffold is correct as shipped now that env.secret is a delivered feature. CLI suite (lucee7 docker harness): 1071 pass / 0 fail / 2 tolerated docker-env artifacts (SshClientSpec/SshPoolSpec require docker-in-docker). Refs #3008, #3167 Fixes #3158 Signed-off-by: Peter Amiri <petera@pai.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer
TL;DR — This PR pins the wheels deploy init scaffold to the deploy-engine env.secret contract (#3158) with two DeployMainCliSpec regression tests plus a fixed changelog fragment. No production code changes: the underlying fix landed in #3167 (remote --env-file delivery), so this is purely a guard against future drift. I re-verified every API the new tests touch against the actual deploy engine and everything lines up. Verdict: approve.
Tests
The two new specs are well-targeted and accurately model the real engine:
DeployMainCliSpec.cfc:357— round-trip test. Constructornew DeployMainCli(fake, {projectRoot: tmpCwd})matchesDeployMainCli.cfc:17init(any sshPool = "", struct opts = {});init_stub({cwd, service, image})matchesDeployMainCli.cfc:469;deploy({..., dryRun: true})is honored (DeployMainCli.cfc:73var dryRun = arguments.opts.dryRun ?: false). Theexpect(arrayLen(fake2.calls())).toBe(0)assertion correctly encodes "dry-run issues no SSH calls."DeployMainCliSpec.cfc:391— delivery test. The asserted env-file path.kamal/apps/demo/env/roles/web.envmatches the engine layout (AppCommands.cfc:158.kamal/apps/<service[-destination]>/env/roles/<role>.env, default roleweb, no destination). TheuploadStringcall-record fields the test reads (.remote,.content) and thec.cmdscan matchFakeSshPool.cfc:179-181. The "value never in argv" loop is a genuine secret-leak guard.
Red-proof is documented in the PR body (both fail if the scaffold drops its env.secret block), so they exercise the contract rather than passing vacuously.
Conventions
(##3158)in theit()descriptions correctly escapes to a literal#— matches existing prior art in the same file ((##3085)atDeployMainCliSpec.cfc:33). The#chr(10)#interpolation in the secrets-file write is a proper expression, not the comment/#-escape hazard.- These are CLI-runtime specs (
cli/lucli/tests), outside the vendor cross-engine matrix, so the Lucee/Adobe/BoxLang invariants do not apply here.
Docs
- Minor, non-blocking:
changelog.d/3158-deploy-init-envsecret-roundtrip.fixed.mdis afixedfragment, but the actual user-facing fix shipped in #3167 — this PR only adds the regression tests. The wording reads slightly as if this PR performed the fix. It is defensible (it documents real now-pinned behavior and the slug/type follow the convention), so not a change request — just flagging in case you would prefer it phrased as "regression coverage for the #3167 fix."
Commits
test(cli): pin deploy init scaffold env.secret round-trip (#3158) — valid type, scope, subject length, casing. Good.
Clean test-only change with verified API usage and red-proofed coverage. Approving.
What
#3158 reported that
wheels deploy initscaffolds anenv.secret: [WHEELS_RELOAD_PASSWORD]block (cli/lucli/templates/deploy/init/deploy.yml.mustache) that, since the #3008$rejectEnvSecretsguard, hard-failed any non-emptyenv.secretwithWheels.Deploy.EnvSecretUnsupported— so the freshly-scaffolded config couldn't be deployed without manual editing.Verify-first finding
The premise is already resolved on develop by #3167 (
df62f4683), which:Base.$rejectEnvSecrets(grep finds zerorejectEnvSecrets/EnvSecretUnsupportedreferences anywhere incli/), andenv.secretdelivery via a remote--env-file(created at 600 perms, content over SFTP only, referenced bydocker run --env-file).So the scaffolded
env.secretblock is now correct — the issue's original "remove the block" direction would now break a shipped feature. The fix reduces to pinning the round-trip so the scaffold and the deploy engine can't drift apart again. No template or engine change is needed.Reproduced empirically on the lucee7 docker harness:
.kamal/secretsdeclaresWHEELS_RELOAD_PASSWORD=(empty); the SecretResolver still exports it (structKeyExiststrue), soenv_file_content()does not raiseEnvSecretMissing.init→config()→deploy --dry-runsucceeds and routes the secret through--env-file .kamal/apps/<service>/env/roles/<role>.env.Change
Two
DeployMainCliSpecregression tests:config()anddeploy --dry-runwith noEnvSecretUnsupported/EnvSecretMissing, and the dry-run references the--env-filepath;WHEELS_RELOAD_PASSWORDto the role env file over SFTP (FakeSshPooluploadString), never in argv.Red-proof: both tests fail if the scaffold drops its
env.secretblock (verified by temporarily removing it from the template), so they genuinely guard the scaffold→delivery contract.Plus a
changelog.dfixedfragment.Tests
CLI suite (lucee7 docker harness): 1071 pass / 0 fail / 2 tolerated docker-env artifacts (
SshClientSpec/SshPoolSpecrequire docker-in-docker, pre-existing/environmental).DeployMainCliSpec66 → 68 pass.Fixes #3158
🤖 Generated with Claude Code