You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 653415c
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: AGENTS.md
+29-12Lines changed: 29 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,7 @@ file and line that broke its rule, and each was proven red by reintroducing its
58
58
|`test/guards/prompt-types.test.ts`| Every prompt call in `src` names a literal `type`, and every such type is registered with the real inquirer `cliux.inquire` uses once `LaunchCommand.init` has run. inquirer silently turns an unregistered type into a plain text box. | D1: `search-list` used, never registered |
59
59
|`test/integration/transport-content-type.test.ts`| For every method in `HTTP_METHODS`, a bodyless request carries no content type and a request with a body carries `application/json`, asserted on the wire. `withoutDefaultContentType` suppresses the utility client's default, so a bodyless POST, PUT or PATCH (for example `deployments:cancel`) no longer goes out as `application/x-www-form-urlencoded`. Only `utility-http-client.ts` builds an `HttpClient` and only `rest-client.ts` sets the JSON type. | D5: every DELETE sent `Content-Type: application/json`|
60
60
|`test/guards/tty-streams.test.ts`|`stdin.isTTY`, and the `isTTY` it becomes on the service context, are read only to decide whether the CLI may prompt (a read next to a prompt or a refusal to prompt) or on the plumbing lines that carry it there. Anything deciding what to draw reads `outputIsTTY` / `process.stdout.isTTY`. | D6: heartbeat drawn into a redirected file |
61
+
|`test/integration/deployments-request-bodies.test.ts`| A deployments mutation puts on the wire only the fields the management service DTO declares - `{commitHash?, uploadUid?}` for a create, `{deployment, reason?}` for a rollback - and never `environment`, which is the URL path. The service validates with `whitelist` and `forbidNonWhitelisted`, so any other key is a bare 400. | Live harness: `deployments:create` and `:rollback` sent `{environment, ...}` and failed with `Bad request`|
61
62
|`test/integration/retired-commands.test.ts` (named commands) | Every `launch:…` command named anywhere in `src` - a retirement message, a failure hint, an example - is either a registered command or on `PLANNED_COMMANDS` with the ticket that builds it, and a command drops off that list once it ships. | Review: retirement messages and hints pointed at commands the beta does not have |
62
63
|`test/guards/prompt-funnel.test.ts`| Every prompt goes through `LaunchCommand`'s `ux`, built once as `cancelOnInterrupt(cliux)`, so Ctrl-C at any prompt exits 3. The wrapper stays attached to `SIGINT` until the signal has reached every listener (it detaches in a microtask): inquirer's `cli-cursor` loads `signal-exit`, which re-raises `SIGINT` - exit 130 - when it finds itself the only listener, so a `once` listener made every list and checkbox prompt exit 130 (`src/core/interruptible-ux.test.ts` pins it). No source calls `cliux.inquire` / `prompt` / `confirm` under any name, hands `cliux` on as a value, or imports a prompt library; `search-list.ts` may register a type but never prompt. | D2: Ctrl-C exited 130 |
63
64
@@ -151,8 +152,9 @@ src/
151
152
each name the other in the layering allow-list
152
153
deployments/ the deployment repository, the status classification, the
153
154
wait/stream loop and LogTail that projects:create, deployments:create
154
-
and logs:get all drive, and the DeploymentFollower that watches one
155
-
deployment and announces its live URL
155
+
and logs:get all drive, the DeploymentFollower that watches one
156
+
deployment and announces its live URL, and the DeploymentCreator behind
157
+
deployments:create. It reaches uploads, for a FileUpload create
156
158
uploads/ zipping a folder, the signed upload URL, and the transfer behind every
157
159
FileUpload source (FolderUploader). The UploadScope picks the endpoint:
158
160
{ org } for a new project, { org, project } for a new environment,
@@ -640,6 +642,23 @@ there is **no server-side maximum at all**.
640
642
each repository builds on `scanPages`, because the page size is that repository's decision and not a
641
643
CLI-wide one; the scan-limit error each raises carries its own wording.
642
644
645
+
**The deployment commands (CL-7170).**`deployments:list`, `:get`, `:create`, `:rollback` and `:cancel`.
646
+
647
+
-`--deployment` is a 24-hex uid, validated while parsing (`FlagValueError`, exit 2, nothing sent; `LaunchCommand.init`
648
+
rethrows it as a plain `UsageError` so oclif's `Parsing --deployment` prefix never reaches the user), so `get`,
649
+
`rollback` and `cancel` share it. It depends on `--org`, `--project` and `--env`. `get` leaves it optional and
650
+
defaults to the latest deployment; `rollback` and `cancel` declare `picker` on the input (`all`, or `in-flight`
651
+
for `cancel`), which is the only case where the spec's `promptWhen` lets the picker run (`promptForDeployment`,
652
+
`src/deployments/deployment.prompt.ts`), so a terminal gets a list of the environment's newest deployments and
653
+
anything else gets the exit-2 missing-value error. A supplied flag never prompts.
654
+
- The service answers HTTP 200 `{ "deployment": null }` for a deployment that is not in the environment, so
655
+
`DeploymentsApi.get` words that as the same 404 `launch.DEPLOYMENT.NOT_FOUND` a missing one gets, and the commands
656
+
report the generic `No deployment with UID "<uid>" found in this environment.`
657
+
- Request bodies carry no `environment`; see the guard table. A validation rejection the service sends keyed by
658
+
field with only the generic `Bad request` is worded by `LaunchApiError` from the field and its code.
659
+
- Every table cell is stripped of control characters and line breaks by `renderTable`, and the presenter shows
660
+
only the first line of a commit message, truncated to 72 characters.
661
+
643
662
**The deployment wait/stream loop.**`src/deployments/deployment.watcher.ts` polls a deployment to a
644
663
terminal state and is the single place any command waits on one. `POST /projects` creates the
645
664
project, its first environment **and** its first deployment, so `projects:create` is its first
@@ -686,7 +705,7 @@ shade, and in green when stdout is a terminal (`styled` in `src/core/style.ts`,
686
705
redirected output stays free of escape codes). Once `POST /projects` succeeds, before the config file is
687
706
written or anything is watched, create prints V1's green `info: New project created successfully`
688
707
(`projectCreatedLine`); a failed create never prints it. On success create then prints V1's
689
-
`Deployment URL <url>` line - label bold, url blue on a terminal - before the project summary,
708
+
`Deployment URL: <url>` line - label bold, url blue on a terminal - before the project summary,
690
709
waits `SITE_OPEN_DELAY_MS` (six seconds, V1's wait, because a site opened the moment it reports
691
710
live can still answer "site not reachable") and opens that url in the browser, terminal or not, as V1
692
711
did. The wait goes through the injected `sleep`, so it is awaited and the browser opens before the
@@ -696,9 +715,8 @@ parseable timestamp printed: with a `timestamp` the service returns what came st
696
715
ascending order, and without one it returns the newest page in **descending** order, so never let the
697
716
cursor drop the query. The poll that sees the terminal status still reads the logs once more before
698
717
returning, so the last lines of a build are never lost. The logs are auxiliary: a
699
-
failed read prints one ` ! Could not read the deployment logs (...)` notice for the whole wait, is
700
-
retried from the same cursor on the next poll, and never changes the outcome - the status poll alone
701
-
decides that.
718
+
failed read prints nothing, is retried from the same cursor on the next poll, and never changes the
719
+
outcome - the status poll alone decides that.
702
720
703
721
**Writing a test for it: the fake clock must advance.**`now()` and `sleep()` are injected and both
704
722
are required, not optional with defaults. A fake `now: () => 0` with a `sleep` that does nothing
@@ -812,12 +830,11 @@ project and `--config`.
812
830
**Deployment failure is a partial success, and the wording says so.** The project and environment are
813
831
real and are **not** rolled back. Everything after `POST /projects` - finding the first environment,
814
832
finding its first deployment, and the wait itself - goes through the same wording when it fails, so a
815
-
502 from a lookup still tells the user what exists. `deploymentFailureMessage` names the status, the project and
816
-
environment that survived, and both follow-up commands with their scope already filled in -
817
-
`deployments:create` to retry and `logs:get` to inspect. It exits **1**, not 2: nothing about the
818
-
invocation was wrong. A wait that runs out is worded differently: the deployment may still finish, so
819
-
the message says not to start another one and names `deployments:get` and `logs:get` instead of
820
-
`deployments:create`.
833
+
502 from a lookup still tells the user what exists. `firstDeploymentFailureMessage` is the one builder:
834
+
it names the deployment and its status, says what was created (when something was) and ends with one
835
+
command - `logs:get` for a failed deployment, `deployments:get` for a wait that ran out or could not be
836
+
followed, `deployments:list` when no deployment was found - with its scope already filled in. It exits
837
+
**1**, not 2: nothing about the invocation was wrong.
821
838
822
839
**The FileUpload archive.**`src/uploads/upload.archive.ts` owns the exclusion list
823
840
(`node_modules`, `.git`, `.env`, `.env.local`, `.next`, `logs`, `.vscode`, `.cs-launch.json`) and
0 commit comments