Claude/convoy readme installer#38
Merged
Merged
Conversation
…VOY_HOME Closes #27. The convoy shell helper cd's into CONVOY_HOME before invoking npm, so `convoy onboard` run from a target repo resolved '.' to Convoy's own checkout and wrote .convoy/preferences.json there. The subsequent plan on the actual target found no preferences — the operator's declared platform mandate was silently ignored and the pipeline re-asked the question onboard exists to answer. Both commands now default to targetInvocationDir(), which reads CONVOY_INVOCATION_CWD (set by the shell helper) before falling back to process.cwd(). Explicit path arguments are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, not a phantom CLI flag The onboard mandate was folded into platformOverride at both CLI call sites, so even when preferences were honored the plan said "(override) respecting explicit --platform=vps override" — a flag the operator never passed. They had no way to see their onboarding answer was actually consumed, which reads as "Convoy ignored my preferences". The mandate now travels through BuildPlanOptions.platformMandate into the picker as a first-class source: override > mandate > existing-config > scored. Plans render "(mandate) honoring the team platform mandate declared at onboard", lanes inherit it, and the higher-score advisory names onboard as the thing to update rather than suggesting a re-run flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ate Cloud Run The rehearsal target descriptor and rollback strategy were if-else ternary chains with a Cloud Run final-else, so any platform without an explicit branch — vps in the field — inherited Cloud Run narrative and rollback text. Converted both to exhaustive switch statements over the Platform union with a never-typed exhaustiveness guard, so the next platform we add fails typecheck instead of silently narrating the wrong cloud. VPS now gets its own rehearsal descriptor (local compose twin) and rollback (previous image tag re-rolled via compose over SSH, ~15s). defaultShipNarrative already consumes platform.chosen from the plan's final decision; verified nothing in the narrative path re-derives a lane score. Closes #29. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…of re-scoring per node
Field bug: a repo with root compose + Caddyfile scored vps at the plan
level, but the backend lane re-scored fly and the frontend lane vercel —
'ship to vps' headline plus a bogus fly-auth preflight blocker.
Lanes now inherit root.chosen when the root decision is a repo-wide
statement (mandate / override / existing-config) or when root scored vps
off root-level compose/Caddy topology that binds the services into one
deploy unit. Per-lane re-scoring remains only for generically scored
roots with no binding topology.
When a lane's own top score disagrees with the inherited platform by
>=10, the lane decision carries an advisory ('frontend would score
vercel 75 standalone; keeping vps because the repo co-deploys via
compose') rendered in the CLI plan output and as a dim line on the web
lane card. Preflight was already lane-platform-driven, so the phantom
fly blocker disappears with the inheritance fix.
Adds a fixture test (tsx + node:test) building a plan from a temp dir
with root compose + Caddyfile + python backend/ + vite frontend/ and
asserting every lane lands on vps with the advisory present.
Closes #28.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pprove link The plan URL used to appear only in the --save footer and the run URL only inside the banner block — operators watching a long render scroll by had to hunt for where to follow along. Now: - convoy plan prints 'Plan <id8> — follow along: <url>' as the first output line once the plan object exists, before the render. - apply/ship/resume print 'Run <id8> — watch live: <url>' as the first line when the run row is created, ahead of the banner and any stage output. - every approval pause prints the gate name plus the exact approve URL: '⏸ awaiting <kind> — approve at <url>'. All URLs route through webUrl(), so CONVOY_WEB_URL keeps being honored. Closes #30. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…egistrar The --real-vps*, --real-vps-ghcr*, --real-railway, and --real-cloudrun* flag families were registered on apply (and partially on resume) only, so 'convoy ship --real-vps ...' was rejected even though ship's internal apply phase understood every one of those options. Extracted registerDeployFlags(cmd) — one definition of the shared deploy option set, registered identically on ship, apply, and resume, so the three can never drift again. Ship already threads ShipOpts (which extends ApplyOpts) straight into runApply, so the values flow with no further plumbing. Also: when team preferences carry deployment.vpsHost, 'ship --platform=vps' now defaults --vps-host from it instead of failing preflight on a host the operator already declared at onboard. Closes #32. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…thon signals, compose
Real rehearsal hard-blocked any repo whose start command didn't live in
package.json scripts — Python repos, Go binaries, anything containerized
hit 'real-rehearsal.no-start-command' even when the Dockerfile spelled
the command out. The scanner now derives startCommand through a fixed
precedence chain:
1. package.json scripts.start / scripts.serve (unchanged)
2. Dockerfile CMD/ENTRYPOINT — exec form (JSON array joined) and shell
form, ENTRYPOINT+CMD composed per Docker semantics, last wins
3. Procfile web: line
4. Python: uvicorn/gunicorn/fastapi in requirements.txt/pyproject.toml
plus main.py/app.py at the service root synthesizes
'uvicorn <module>:app --host 0.0.0.0 --port <detected or 8000>'
5. docker-compose service command: — directory-name match, else the
lone command-bearing service; ambiguity yields nothing
Every non-package.json source records its own scan-evidence line so the
plan shows where the command came from. The no-start-command blocker now
lists exactly what was checked, in order, and stops suggesting a
package.json start script for non-node ecosystems.
Closes #33.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every .env.example key used to render as a 'Required secret', and every
lane inherited the repo root's example file — both lanes listed
identical keys with identical red treatment.
- New src/core/env-classify.ts (mirrored in web/lib/env-classify.ts):
classifyEnvKey(key) → 'secret' | 'config'. Secret when the name
matches SECRET/TOKEN/PASSWORD/CREDENTIAL/PRIVATE/API_KEY/DSN/_KEY or
is a credentialed connection-string key (DATABASE_URL, REDIS_URL,
MONGODB_URI, ...); config otherwise.
- Per-lane attribution in the scanner: docker-compose per-service
environment:/env_file: entries belong to the matching lane (build
context or service-name match); <servicePath>/.env.example keys belong
to that lane; root example keys are repo-shared and attach ONCE to the
primary lane instead of duplicating into all lanes. Non-empty example
values are carried as candidate defaults for config keys.
- Lane secrets sections gain a classified keys[] view; renderPlan gets a
'Secrets & config' block with separate Secrets (n) / Config (m)
counts; preflight env staging splits missing keys by kind and treats
config keys with example defaults as prefilled ('from example —
confirm for production') instead of missing.
- Web plan page groups lane keys secret-first with config dimmer; the
config panel sorts secrets first, dims config rows, and badges
prefilled config defaults. Staging behavior is unchanged for both.
Closes #34.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
No description provided.