Skip to content

feat(cli): scaffold Dockerfile and .dockerignore in wheels deploy init#2686

Merged
bpamiri merged 4 commits into
developfrom
claude/resolve-issue-2673-ckb6X
May 15, 2026
Merged

feat(cli): scaffold Dockerfile and .dockerignore in wheels deploy init#2686
bpamiri merged 4 commits into
developfrom
claude/resolve-issue-2673-ckb6X

Conversation

@bpamiri
Copy link
Copy Markdown
Collaborator

@bpamiri bpamiri commented May 15, 2026

Summary

Closes #2673.

wheels deploy init now emits a starter Dockerfile and .dockerignore alongside config/deploy.yml and .kamal/secrets, so the init → setup walkthrough no longer dead-ends at "no Dockerfile to build from."

What changed

  • New templates under cli/lucli/templates/deploy/init/:
    • Dockerfile.mustache — multi-stage build (Node builder + lucee/lucee:7-tomcat10-jre21 runtime), matching the canonical pattern documented at deployment/docker-deployment.mdx. EXPOSE 8080 lines up with the default proxy.app_port in deploy.yml. Includes a /up HEALTHCHECK aligned with the generated deploy.yml's proxy.healthcheck.path, and production env defaults (WHEELS_ENV, WHEELS_DATASOURCE, CATALINA_OPTS). The builder stage works for any Wheels app — projects without an npm pipeline pass through unchanged; projects with a package.json install + build automatically. Secrets (reload password, DB password, registry password) are injected at deploy time via .kamal/secrets, not baked into the image.
    • dockerignore.mustache — keeps .kamal/secrets, .env*, vendor/wheels/tests, sourcemaps, and IDE noise out of the build context. (Filename keeps the leading dot off so the template isn't hidden from tooling that filters dotfiles.)
  • DeployMainCli.init_stub() renders both files. --force now also gates an existing Dockerfile (same DeployMainCli.InitAlreadyExists abort as deploy.yml). An existing .dockerignore is preserved unless --force is passed — users often have their own. The success message reports .dockerignore as either "created" or "preserved existing" accordingly.
  • Updated success message + next-steps output to list the Dockerfile review step.
  • Tests:
    • Existing init_stub happy-path spec now asserts Dockerfile and .dockerignore are emitted.
    • New spec verifies the rendered Dockerfile uses lucee/lucee:7-tomcat10-jre21, EXPOSE 8080, has a HEALTHCHECK, and contains the service name.
    • New spec guards --force on an existing Dockerfile (aborts and leaves the file untouched).
    • Existing force=true spec extended to pre-create .dockerignore with sentinel content and verify both Dockerfile and .dockerignore are overwritten (exercising the force || branch).
    • New spec verifies the silent-preservation path: an existing .dockerignore survives a no-force init, and the success message reflects "preserved" rather than "created".
    • $cliInstallDir() regression spec now also checks the two new templates exist on disk.
  • Docs:
    • command-line-tools/commands/deploy/init.mdx — flag table and "What it creates" section list the new files.
    • deployment/first-deploy.mdx — "Before you start" precondition rewritten (no longer says you must write a Dockerfile yourself); Step 1 enumerates the four files now emitted.
    • CHANGELOG.md[Unreleased] / Added entry under the canonical Keep-a-Changelog ordering.
    • v4-0-0/ docs untouched — that's the released-stable line and reflects the older behavior.

Test plan

  • bash tools/test-cli-local.sh — runs the deploy CLI specs that cover init_stub (cannot run from this sandbox; CI will exercise).
  • CI matrix: Lucee 6/7, Adobe 2023/2025, BoxLang. Only CFML touched is DeployMainCli.cfc + the spec; no engine-specific surface.

Generated by Claude Code

Closes #2673.

wheels deploy init now emits a starter Dockerfile and .dockerignore
alongside config/deploy.yml and .kamal/secrets, so the init -> setup
walkthrough no longer dead-ends at "no Dockerfile to build from".

The Dockerfile mirrors the canonical pattern from the deployment
docs: multi-stage (Node builder + lucee/lucee:7-tomcat10-jre21
runtime), EXPOSE 8080 to match the default proxy.app_port, /health
HEALTHCHECK, and production env-var defaults. The .dockerignore
keeps secrets (.kamal/secrets, .env*), test fixtures, and IDE noise
out of the build context.

--force now also gates an existing Dockerfile, with the same
DeployMainCli.InitAlreadyExists abort as deploy.yml. An existing
.dockerignore is preserved unless --force is passed.
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

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

REVIEW_BODY_UPDATE

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 15, 2026

Wheels Bot -- Reviewer B (round 1)

Reviewer A's submission is a broken non-review: the body is the literal string REVIEW_BODY_UPDATE and there are no line comments. There is nothing to align with, so this round focuses on what A should have found in the diff.

Sycophancy: None detected -- but only because the review has no content at all.

False positives: None detected -- no claims were made.

Missed issues

A filed a COMMENTED review with body REVIEW_BODY_UPDATE and zero line comments. The entire diff was left unexamined. Issues A should have flagged:

  1. ENV RELOAD_PASSWORD=change-me baked into the Dockerfile template (Dockerfile.mustache, line 46). Any image built without explicitly overriding this ENV will have a well-known default reload password in production. The template should either omit the variable entirely (forcing the user to supply it at docker run time via .kamal/secrets) or add a prominent warning comment. Worth at least a comment-level note even if not a blocker.

  2. .dockerignore abort asymmetry is untested. Dockerfile throws DeployMainCli.InitAlreadyExists on conflict without --force; .dockerignore silently skips. The PR description calls this intentional, but there is no spec that verifies the skip-without-throw path for .dockerignore. The existing force spec (DeployMainCliSpec.cfc) also never asserts that .dockerignore is overwritten when force=true.

  3. Sequential abort check ordering (DeployMainCli.cfc, lines 331-339). If config/deploy.yml and Dockerfile both exist without --force, the user sees only the deploy.yml error on the first run. After fixing that (or adding --force), they discover the Dockerfile conflict separately. A combined pre-flight check would be better UX. Not a correctness bug, but A should have noted it.

  4. dockerignore.mustache contains no Mustache variables -- it is a static file shipped through the Mustache renderer for no benefit. A should have noticed the inconsistency with Dockerfile.mustache (which does use a service name variable).

Verdict alignment: A's state is COMMENTED with an empty body. The verdict is neither approve nor request-changes, which is inconsistent with a diff that has real concerns worth surfacing. A left the PR in an ambiguous state.

Convergence: Not aligned. A's review provided no substantive content and cannot be the basis for a joint recommendation. A needs to re-review the diff and address the missed issues above -- specifically the RELOAD_PASSWORD default and the .dockerignore test gaps -- before convergence is possible.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: This PR closes a real gap — wheels deploy init used to dead-end users who hadn't written a Dockerfile. The overall shape is solid: templates are idiomatic, the force-flag guard is correctly placed before any writes, and the new specs cover the main paths. Four issues are worth addressing before merge, ranging from a misleading success message to a test coverage gap and a subtle Docker trap.


Correctness

1. Success message claims .dockerignore was created even when it was preserved

cli/lucli/services/deploy/cli/DeployMainCli.cfc, line 369:

return "Created config/deploy.yml, .kamal/secrets, Dockerfile, and .dockerignore." & chr(10)

The return message is unconditional, but the .dockerignore write at line 365 is gated:

if (force || !fileExists(dockerignorePath)) {
    fileWrite(dockerignorePath, ...);
}

When an existing .dockerignore is preserved (force=false, file present), the output still says "Created ... .dockerignore" — inaccurate. Track the write in a local boolean and emit "preserved" vs "created" accordingly, or simply omit .dockerignore from the message when it wasn't written.

2. Builder stage COPY package.json fails for apps without npm

cli/lucli/templates/deploy/init/Dockerfile.mustache, line 23:

COPY package.json package-lock.json* ./

package.json is a literal filename — Docker COPY fails with "no such file or directory" if it is absent from the build context, even in BuildKit. The majority of Wheels apps have no npm setup; a first-time user running docker build . immediately after wheels deploy init would get an opaque build error before reading the "Drop this stage if..." comment at line 16.

Suggested fix: restructure the builder-stage COPY to be resilient, e.g. copy the full context first and then conditionally install:

COPY . .
RUN if [ -f package.json ]; then npm ci --no-audit --no-fund; fi
RUN if [ -f package.json ] && node -e "..."; then npm run build; fi

Or, keep the current two-step pattern but note more prominently in the generated output that the builder stage must be removed for non-npm projects.


Security

3. RELOAD_PASSWORD=change-me baked into image ENV; name does not match the deploy.yml secret

cli/lucli/templates/deploy/init/Dockerfile.mustache, line 42:

ENV WHEELS_ENV=production \
    WHEELS_DATASOURCE=app \
    RELOAD_PASSWORD=change-me \
    ...

Two concerns:

  • Image layer history: RELOAD_PASSWORD=change-me persists in every layer of images built from this scaffold, visible in docker inspect and container registry metadata, even if overridden at runtime.
  • Name mismatch: the pre-existing deploy.yml.mustache (line 29) injects the secret as WHEELS_RELOAD_PASSWORD; the Dockerfile sets RELOAD_PASSWORD. When deployed via wheels deploy, Kamal puts WHEELS_RELOAD_PASSWORD=<value> in the container environment, but the Dockerfile ENV is RELOAD_PASSWORD=change-me — these are different names, so the Kamal secret does not override the baked-in default. Additionally, the default generated app (cli/lucli/templates/app/config/settings.cfm, line 25) uses a static literal for reloadPassword, not getenv(), so neither env var name is read at all without user modification.

Recommended: remove RELOAD_PASSWORD from the Dockerfile ENV block entirely. Add a comment explaining that the reload password must be injected at runtime (e.g. via WHEELS_RELOAD_PASSWORD in .kamal/secrets after updating config/settings.cfm to read the env var), and align the env var name with the secrets template.


Tests

4. No spec for the .dockerignore silent-preservation path

cli/lucli/tests/specs/deploy/cli/DeployMainCliSpec.cfc — there is no test that verifies: when a .dockerignore already exists and force=false, it is preserved (not overwritten) and the call still succeeds. The implementation at DeployMainCli.cfc:365 has this behavior but it is untested. Mirror the pattern of the Dockerfile guard spec at line 220 but assert the opposite: .dockerignore survives and init_stub returns without throwing.

5. force=true spec doesn't verify .dockerignore is also overwritten

cli/lucli/tests/specs/deploy/cli/DeployMainCliSpec.cfc, line 237–253: the spec pre-creates config/deploy.yml and Dockerfile but not .dockerignore. Because the .dockerignore isn't pre-created, the if (force || !fileExists(...)) branch always takes the !fileExists path — the force || ... part is never exercised. Pre-create .dockerignore with sentinel content, then assert it was overwritten.


Docs

The init.mdx and first-deploy.mdx updates are accurate and clear. The asymmetric behavior (.dockerignore silently preserved vs Dockerfile hard-aborts) is correctly documented in "What it creates."

One small omission: the plugins/ directory is not in the runtime COPY block (Dockerfile.mustache, lines 54–57). It's deprecated but still supported and users may have it. Worth a one-liner comment noting it.


Commits

Single commit feat(cli): scaffold Dockerfile and .dockerignore in wheels deploy init — valid type, in-scope, subject is 54 chars and sentence-case. Clean.

- Tolerate Wheels apps without an npm pipeline by copying the whole build
  context once and gating npm install/build on package.json existence. The
  prior `COPY package.json ...` line failed builds for apps lacking one.
- Remove the baked-in RELOAD_PASSWORD=change-me from the Dockerfile ENV.
  It was both a layer-history leak and named differently from the secret
  shipped in deploy.yml/.kamal/secrets (WHEELS_RELOAD_PASSWORD), so it
  never overrode anything anyway. Replaced with a comment pointing to
  the secrets-injection path.
- Report ".dockerignore" honestly in the init summary — say "preserved"
  when the existing file was kept, "created" only when written.
- Cover the silent-preserve path and force-overwrite path for
  .dockerignore in DeployMainCliSpec.
- Note the deprecated plugins/ COPY in the Dockerfile comment for users
  still on the legacy directory.
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: All five issues raised in the previous round have been cleanly addressed — the RELOAD_PASSWORD env leak is gone, the npm/no-npm Dockerfile now works for any Wheels app, the summary message is accurate, and the .dockerignore preservation paths are fully tested. What remains are two minor nits: a healthcheck path mismatch between the two generated templates, and a missing CHANGELOG entry for a user-visible new feature.


Correctness

1. Healthcheck path mismatch between Dockerfile and deploy.yml (minor, worth a comment)

cli/lucli/templates/deploy/init/Dockerfile.mustache, line 72:

CMD curl -fsS http://127.0.0.1:8080/health || exit 1

cli/lucli/templates/deploy/init/deploy.yml.mustache, line 16:

healthcheck:
  path: /up

These are technically independent probes — the Docker HEALTHCHECK fires from inside the container, while proxy.healthcheck.path tells kamal-proxy which HTTP path to poll before swapping traffic. But a first-time user who takes both files as-generated will need to add two distinct routes (/health and /up) — or change one of the paths. The Dockerfile comment on line 10 says "add [a /health route] to keep liveness probes off your slow paths," but the deploy.yml already names /up. Aligning both to the same path (or documenting the split explicitly) would reduce friction. Not a bug, but worth a one-sentence note in the Dockerfile comment.


Docs

2. No CHANGELOG entry for the new scaffold behaviour

CHANGELOG.md under [Unreleased] has no mention of this PR. wheels deploy init now emits two additional files and the --force guard now also protects the Dockerfile — these are user-visible behavioural changes. A brief entry under ### Added (or ### Changed for the guard) would follow the pattern of other CLI additions in the same file. Example:

- `wheels deploy init` now scaffolds a starter `Dockerfile` (Lucee 7 multi-stage, `/health` HEALTHCHECK) and `.dockerignore` alongside `config/deploy.yml` and `.kamal/secrets`. `--force` also gates an existing `Dockerfile`. (#2673)

Commits

Two commits on this PR:

  • feat(cli): scaffold Dockerfile and .dockerignore in wheels deploy init — valid
  • fix(cli): address Reviewer A feedback on deploy init scaffolding — valid

Both conform to commitlint: correct type, in-scope, subject ≤ 100 chars, not ALL-CAPS.


Everything from the previous review round is resolved. The two findings above are nits — merge can proceed once the CHANGELOG entry is added. The healthcheck-path note is advisory; it doesn't block users but is a DX improvement worth a quick comment in the generated Dockerfile.

- Add an Unreleased "Added" entry for `wheels deploy init` scaffolding the
  Dockerfile + .dockerignore.
- Switch the generated Dockerfile HEALTHCHECK from /health to /up so it
  matches the kamal-proxy healthcheck path already named in the bundled
  config/deploy.yml. A first-time user no longer has to add two distinct
  liveness routes for the same app.
@github-actions github-actions Bot added the docs label May 15, 2026
@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 15, 2026

Wheels Bot — Reviewer B (round 1)

Reviewer A's review was submitted against commit 3dc5cc344 (the second of three commits on this PR). A third commit (ca17811f9) was pushed after A's review and addressed both of A's nits — the CHANGELOG entry and the healthcheck path alignment. This critique therefore covers: (a) the quality of A's review at the time it was submitted, and (b) the state of the PR at the current head.


Sycophancy

A's TL;DR opens with "All five issues raised in the previous round have been cleanly addressed." Round B-1 did not raise five discrete issues — it raised a structural complaint (A's original review body was the literal placeholder REVIEW_BODY_UPDATE) and, within that, four substantive findings. Calling them "five issues from the previous round" is slightly inflated framing. Minor, but worth noting.

The overall tone is positive and proportionate. The two findings A did surface (healthcheck path mismatch, missing CHANGELOG) are legitimate, so the near-approval stance is not empty praise. No sycophancy beyond the inflated issue-count summary.


False positives

None. A's two findings are accurate against the diff A reviewed:

  • The HEALTHCHECK path in Dockerfile.mustache was /health at commit 3dc5cc344 while deploy.yml.mustache named /up. Verified in the templates.
  • The [Unreleased] section of CHANGELOG.md had no ### Added entry at that commit. Verified.

A correctly characterised both as nits rather than blockers.


Missed issues

1. A third commit (ca17811f9) was pushed after A's review — A did not re-review it

The third commit (docs(cli): changelog + align Dockerfile healthcheck path with deploy.yml) addresses both of A's nits. A's review verdict remains COMMENTED against commit 3dc5cc344; the PR has since advanced to ca17811f9. A should acknowledge this in a follow-up or re-review so the PR doesn't sit open waiting for action that has already been taken.

2. init.mdx still references /health in the Dockerfile description (stale after commit 3)

The third commit aligned Dockerfile.mustache from /health to /up and updated the CHANGELOG accordingly, but the description text in init.mdx was not updated:

- `Dockerfile` — multi-stage build targeting Lucee 7 on Java 21 with a `/health` HEALTHCHECK and `EXPOSE 8080`

The actual generated file now probes /up. This is the same healthcheck-path inconsistency A flagged, now shifted from template-vs-deploy.yml to doc-vs-template. A missed this because commit 3 hadn't landed yet, but it remains an open issue on the current head.

3. init_stub refuses to overwrite Dockerfile spec does not clean up on the throw path

In DeployMainCliSpec.cfc (the new "refuses to overwrite an existing Dockerfile without force" spec), the directoryDelete(tmpCwd, true) cleanup runs only in the happy-path it block after the expect(...).toThrow() assertion. If toThrow() itself throws unexpectedly (e.g. the exception type is wrong), the temp directory is left on disk. The existing deploy.yml refusal spec (lines 206-218) has the same pattern but was already present before this PR — the new spec copied the same omission. Low severity, but A should have noted it.

4. Sequential abort-check ordering is unchanged (from round B-1)

A did not address the UX concern raised in round B-1: if config/deploy.yml and Dockerfile both already exist, the user discovers the Dockerfile conflict only after resolving the deploy.yml conflict (or adding --force). A's review made no mention of this finding. It remains advisory; the code is correct. A silence here is acceptable given A rated it non-blocking, but an explicit acknowledgement would have been cleaner.


Commits

The third commit type is docs(cli) — valid type, subject within 100 chars, not ALL-CAPS. No commitlint violations across all three commits.


Convergence

The substantive concerns are narrow:

  • A's two nits were resolved in commit 3 — the PR is effectively in better shape than A's review describes.
  • One new issue opened by commit 3: the /health vs /up mismatch in init.mdx.
  • The temp-directory cleanup omission in the new spec is low-severity.

A's overall assessment (merge-ready after nits) was correct at the SHA A reviewed, and the nits have since been addressed. The one residual actionable item is the init.mdx description update. That is a documentation error, not a correctness or security issue.

Convergence: aligned — request changes on the init.mdx documentation inconsistency before merge. The rest of the PR is clean.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: The two findings from the previous round (missing CHANGELOG entry, healthcheck path mismatch) have been addressed in ca17811f9. The CHANGELOG entry is accurate, the Dockerfile template now probes /up throughout (CMD and the header comment), and the generated template aligns with config/deploy.yml's proxy.healthcheck.path. One minor doc inconsistency remains in init.mdx.


Docs

One stale /health reference in init.mdx

web/sites/guides/src/content/docs/v4-0-1-snapshot/command-line-tools/commands/deploy/init.mdx, line 32:

- `Dockerfile` — multi-stage build targeting Lucee 7 on Java 21 with a `/health` HEALTHCHECK and `EXPOSE 8080` ...

The commit updated the Dockerfile template (Dockerfile.mustache:73) and its header comment (Dockerfile.mustache:10-11) to use /up, but this prose description in init.mdx was not updated and still says /health. A user reading the docs after init will see the wrong path.

Suggested fix — change /health to /up in that bullet:

- `Dockerfile` — multi-stage build targeting Lucee 7 on Java 21 with a `/up` HEALTHCHECK and `EXPOSE 8080` (matches the default `proxy.app_port` and `proxy.healthcheck.path` in `deploy.yml`). Adjust the `COPY` lines and base image to match your app.

Commits

Three commits, all conforming to commitlint:

  • feat(cli): scaffold Dockerfile and .dockerignore in wheels deploy init — valid
  • fix(cli): address Reviewer A feedback on deploy init scaffolding — valid
  • docs(cli): changelog + align Dockerfile healthcheck path with deploy.yml — valid

All subjects <= 100 chars, sentence-case, correct types.


Everything else from prior rounds is resolved. The single finding above is a one-word doc fix. Merge can proceed once /health -> /up is corrected in init.mdx.

Commit ca17811 switched the generated Dockerfile HEALTHCHECK from /health
to /up to match the kamal-proxy healthcheck path in the bundled deploy.yml,
but the init.mdx description still named /health. Update the bullet so
the docs match the templates a freshly initialised app actually emits.
@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 15, 2026

Wheels Bot — Address Review (round 1)

The convergence verdict in Reviewer B's round-1 comment named one residual actionable finding:

The one residual actionable item is the init.mdx description update. That is a documentation error, not a correctness or security issue.

That finding was addressed by commit 0f5971870docs(cli): align init.mdx healthcheck description with /up template path — which landed 41 seconds after B's converge comment. The relevant line in web/sites/guides/src/content/docs/v4-0-1-snapshot/command-line-tools/commands/deploy/init.mdx:32 now reads:

Dockerfile — multi-stage build targeting Lucee 7 on Java 21 with a /up HEALTHCHECK (aligned with the proxy.healthcheck.path in the generated deploy.yml) …

No new commit is being made by this round — the consensus change is already in the PR at the current head.

Skipped (not in the consensus action list per B's convergence):

  • Reviewer A re-acknowledging commit 3 — process observation, not an actionable code change.
  • Temp-directory cleanup omission in the new refuses to overwrite Dockerfile spec — B explicitly classified as low severity and left out of the "request changes" trigger.
  • Sequential abort-check ordering UX — B classified as advisory ("remains advisory; the code is correct").

The next Reviewer A run on this SHA should align with approve, assuming it agrees the convergence finding is satisfied.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: All issues from the previous three rounds have been resolved. The latest commit (0f5971870) is a clean one-line doc fix: the /health path in init.mdx's Dockerfile bullet has been updated to /up, matching the template and deploy.yml. One informational note about stale PR description prose -- no blocking issues remain.


Docs

Stale PR description text (informational, not blocking)

The original PR body under "What changed" still reads:

Includes a /health HEALTHCHECK and production env defaults (WHEELS_ENV, WHEELS_DATASOURCE, RELOAD_PASSWORD, CATALINA_OPTS).

Both /health and RELOAD_PASSWORD were superseded by the fix commit (3dc5cc344). The actual templates (Dockerfile.mustache lines 10-11 and 72-73) correctly use /up, and RELOAD_PASSWORD was removed from the ENV block in the same fix commit. A maintainer merging this PR will not be misled by the commits themselves, but the description text does not reflect the current state of the PR.

Optional cleanup before merge:

Includes a `/up` HEALTHCHECK aligned with the generated deploy.yml, and production env defaults
(WHEELS_ENV, WHEELS_DATASOURCE, CATALINA_OPTS). Secrets (reload password, DB password, registry
password) are injected at deploy time via .kamal/secrets -- not baked into the image.

Summary of all rounds

Round Finding Status
1 Success message claimed .dockerignore created when preserved Fixed in 3dc5cc344
1 Builder stage COPY package.json failed for no-npm apps Fixed in 3dc5cc344
1 RELOAD_PASSWORD=change-me baked into image ENV Fixed in 3dc5cc344
1 No spec for .dockerignore silent-preservation path Fixed in 3dc5cc344
1 force=true spec did not exercise the force branch for .dockerignore Fixed in 3dc5cc344
2 Healthcheck path mismatch between Dockerfile (/health) and deploy.yml (/up) Fixed in ca17811f9
2 No CHANGELOG entry Fixed in ca17811f9
3 Stale /health reference in init.mdx Fixed in 0f5971870

All code, test, and doc issues are clean. The implementation is correct, the CFML changes are engine-agnostic (only fileWrite, fileExists, directoryCreate calls), the templates emit no baked-in secrets, and the BDD specs cover the happy path, abort-without-force, force-overwrite, and silent-preserve paths.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented May 15, 2026

Wheels Bot — Reviewer B (round 1)

A's review is substantive and the round-history table is a useful audit trail. One false positive mars an otherwise clean read. The PR is ready to merge.

Sycophancy

None detected. A's positive assessment is backed by a concrete commit-by-commit summary table and is consistent with the actual diff I re-read.

False positives

A's stale PR description finding is not supported by the current PR body.

A quotes the description as:

Includes a /health HEALTHCHECK and production env defaults (WHEELS_ENV, WHEELS_DATASOURCE, RELOAD_PASSWORD, CATALINA_OPTS).

The actual PR body (as returned by gh pr view 2686) reads:

Includes a /up HEALTHCHECK aligned with the generated deploy.yml's proxy.healthcheck.path, and production env defaults (WHEELS_ENV, WHEELS_DATASOURCE, CATALINA_OPTS).

The description already uses /up and omits RELOAD_PASSWORD. A's quoted text does not appear anywhere in the current PR description. Whether this was corrected before A reviewed or A mis-recalled the text, the finding has no basis in the PR's current state.

Missed issues

A submitted COMMENTED rather than APPROVED. A's assessment is unambiguously clean — no blocking issues, no request-changes, only an informational note that turns out to be a false positive. Leaving the PR in COMMENTED state rather than APPROVED is inconsistent with the finding and leaves the PR without a bot approval signal.

Verdict alignment

A's verdict (COMMENTED with no actionable findings) is inconsistent with A's own conclusions. Given a clean diff and a false-positive-only finding, APPROVED would have been the correct state. This does not change the substance of A's review, but it is a form error.

Convergence

After reading the diff at the current head (0f5971870): the CHANGELOG entry is correct, init.mdx uses /up, Dockerfile.mustache uses /up, no secrets are baked into the image ENV block, specs cover the happy path / abort / force-overwrite / silent-preserve paths, and the CFML is cross-engine safe (only standard fileWrite/fileExists/directoryCreate/fileRead calls). A's only finding is a false positive. We are aligned: the PR is clean.

@bpamiri bpamiri merged commit 8aa92f6 into develop May 15, 2026
14 checks passed
@bpamiri bpamiri deleted the claude/resolve-issue-2673-ckb6X branch May 15, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wheels deploy init should generate a default Dockerfile

2 participants