Skip to content

Commit 653415c

Browse files
Merge pull request #256 from contentstack/CL-7170
feat(deployments): add the deployments resource (list, get, create, rollback, cancel)
2 parents 80187de + 5c0147f commit 653415c

77 files changed

Lines changed: 7169 additions & 278 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.talismanrc‎

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ fileignoreconfig:
22
- filename: src/core/failure.test.ts
33
checksum: 242d43885d1e3ef46edf1e347899a90549ebf95ec6827426746852f7a6dfc418
44
- filename: test/integration/retired-commands.test.ts
5-
checksum: f43448a6d3855c3823d65dd66150bbd26410ae0be8a921476ca8a948d65caa16
5+
checksum: 4f1a3d7b6b6684986f720a44dcd4d4c014a64dd15c41d41d0266afb0ebe828c0
66
- filename: test/integration/manage-base-path.test.ts
77
checksum: 79eb9ad420ea47d5bee8045d812c45b010bbeeafcaf5f85d11653140b0cd0db1
88
- filename: src/transport/rest-client.ts
@@ -87,7 +87,7 @@ fileignoreconfig:
8787
- "'SECRET=hunter2'"
8888
- "formFieldKey"
8989
- filename: test/integration/projects-create.test.ts
90-
checksum: 0161c3bba176134df0bc1a3a29cda4e1c0b143afbee202c110cdfed19b08d8e0
90+
checksum: e37d9a0fdce75f942836f621a2e281cb9b448cd583673ebcacb075de3faaf187
9191
- filename: test/fixtures/README.md
9292
checksum: d801c84c8b7d7f38f967573e9a3a70da4405b7cbcbf10d2a6e5be3d65fb3daa1
9393
allowed_patterns:
@@ -167,11 +167,11 @@ fileignoreconfig:
167167
ignore_detectors:
168168
- filename
169169
- filename: src/environments/environment.inputs.test.ts
170-
checksum: 499a4601152dded32b766abd8e43aa2ddd88e4e62fc5389b00a268d730178565
170+
checksum: 3590697ca0f0f0c0c7b6e64eddfecbdef8838ff260315c70178786490664682c
171171
ignore_detectors:
172172
- filename
173173
- filename: src/core/inputs.ts
174-
checksum: 638c1093562cc19038395b498202d06aef53b8d2537ee80b715611f19f83c78b
174+
checksum: a4094f68a2fb6b012cfc259f686e362d5a00b34dce85f9d46f1ead270263f15e
175175
allowed_patterns:
176176
- "FlagKey"
177177
- "InputKeys"
@@ -224,11 +224,12 @@ fileignoreconfig:
224224
allowed_patterns:
225225
- "FlagKey"
226226
- filename: src/resources.test.ts
227+
checksum: 742d9faaa855390c853963388bb2479355fe69f0ee05b5a8965de091621d711d
227228
allowed_patterns:
228229
- "a project name passed on argv to the project resolver"
229230
- "expect\\(Object.keys\\(DEPENDENCIES\\)\\).toEqual\\(\\['project', 'env'\\]\\)"
230231
- filename: AGENTS.md
231-
checksum: 562fc91581867ca4682ce2181379aeedd6c50ff666fa706c558eabfb688e1a46
232+
checksum: 89b7e72236e7218326eb3c9c3176c5ab36d58e79a9bf845f422fe28d98c144a1
232233
allowed_patterns:
233234
- "Do not add an exemption to a guard to make a new file pass"
234235
- "passes its own error wording to `client.request\\(req, MESSAGES\\)`"
@@ -246,25 +247,25 @@ fileignoreconfig:
246247
- filename: src/core/project-config.test.ts
247248
checksum: 65c0a613ec77d4dc6727224d4d097de4a02b300339286db692dbaea9379565ae
248249
- filename: src/core/resolve.ts
249-
checksum: 5d150db11cb1c976911ecba61336118ddad051942da95f809695c0cf0346de42
250+
checksum: a5608d22286efdd4915639faff6e05a588e60e205c74e0398e389822eb8c7292
250251
- filename: src/core/resolve.test.ts
251-
checksum: b62b53aedd32ab01212508d79bf4c306dfbd9b0c9788e5fc449886eb770e0377
252+
checksum: 8740a27f759a106e0ddcf38874f83394d2226dd8a908d9499898d774fea3a7a0
252253
- filename: src/git/local-repository.ts
253254
checksum: b9091ed135dfebdf590a5412bbfba2b2f96d4a177cf944a20909b93405268e9f
254255
- filename: test/guards/layering.test.ts
255-
checksum: 2f1b3934e685c2366e45c75f379d63c0ca7bee11108df1e0010ead13159c6b9b
256+
checksum: fea55eeb036ea4acb1cc4d177ae9cbfc1a06bbc5a1ea65b2f5c82717cd0251a9
256257
- filename: src/commands/launch/projects/create.test.ts
257258
checksum: 9c40b4b6ed4f7d678ecb921a71f5af7284cd685c1337f47144456ae43a38b1ad
258259
- filename: src/commands/launch/projects/delete.test.ts
259260
checksum: d67d8a804d14f9b809153d10b0cf9d5e52b70f9e0b65678ebb1663a7d2da22c1
260261
- filename: test/integration/projects-delete.test.ts
261262
checksum: bb59cbe1f4f7b3ac6aa7944067c1ab1214a7c5d20229e9d6577cdaabec56622e
262263
- filename: src/core/launch-command.ts
263-
checksum: b14abd1d4cee3e93c4458ab3567b1f252564bda7e12773cfa1e0d678967664a4
264+
checksum: 01904855aca54cbbcb846069cee5a0732da1db082110e7e454a5c1c532ab02fb
264265
- filename: src/core/launch-command.test.ts
265266
checksum: c4d3785f25377db560bb9daa5d0d080a0131056e6c31e3b45cb4f1f2347305ed
266267
- filename: src/projects/project.inputs.test.ts
267-
checksum: eaecaba9eddec128186871550fc5802166fa56a2e89741d72ac955fcfe2f952f
268+
checksum: 8242f4d40388f1a7f1d08c11d2571cb45a9294069a8248a5272bc5606807bb60
268269
- filename: test/integration/projects-command-flows.test.ts
269270
checksum: 30858bb3dff8a22406efa4ac82d882d1501797cdda24e09555aecc0bcd8dca0b
270271
- filename: src/core/redact.test.ts
@@ -292,7 +293,7 @@ fileignoreconfig:
292293
- filename: src/functions/function.errors.test.ts
293294
checksum: ac0a55e5bee974fefa9fb95541adba63e9a2c2c6e6c47b87c8ca1022542e75f0
294295
- filename: src/transport/errors.test.ts
295-
checksum: b7e28a9062e29a87c8c8b882c6543d50e90e12509c5629ee362ffcac430d01f1
296+
checksum: f38547e2a4639ae150b53f955963ce00c1922a4223c89e489ba5b7ca75a691d0
296297
- filename: src/transport/auth-strategy.ts
297298
checksum: 6fe87def1b508cb67203d4445d9bd889570e512ed4194dfc4fdd3182bc8b87b2
298299
- filename: src/functions/function.logger.test.ts
@@ -304,7 +305,7 @@ fileignoreconfig:
304305
- filename: test/credential-guard.setup.ts
305306
checksum: 519ccbeb0552fdb1d5f27308c56477c0258ee6cc0adec8f618e8e5bc047cbf6d
306307
- filename: src/environments/types.ts
307-
checksum: 8f57348764a69aeb7c2351faf3a5c80fda483a1a1f009e126ff71a9679d92457
308+
checksum: 6c054295075e51e9ce0f4514ebdc86a48bb74db9932a2baaa9f9158e9c14c6e8
308309
- filename: src/commands/launch/env/get.test.ts
309310
checksum: 6e739ef12bc0d887e652e23ee54d9fac30dd6a3505cf464cdaefc0e696cf3e51
310311
- filename: test/integration/environments-delete.test.ts
@@ -324,13 +325,13 @@ fileignoreconfig:
324325
- filename: src/commands/launch/env/update.test.ts
325326
checksum: 2da219594e7bdd589adc0eff9ff035252a75aaa51dfa20514b18fd3266d7b752
326327
- filename: README.md
327-
checksum: 6d13077c028465448536779a02a635c55069d5aeaeea6f6e4882226da8b9f75e
328+
checksum: 9c87e9e46dbc9606b4946bb6d84ce36352d8f6d733280f5f48e0a65bd6cf6ded
328329
allowed_patterns:
329330
- "password-protection"
330331
- "-p, --password"
331332
- "-u, --username"
332333
- filename: test/integration/environments-create.test.ts
333-
checksum: 1b87f454b1534bf0b73ebb52cdaf9af231beb57d5746e521a4504edad819c89a
334+
checksum: 0c64a4408d3c150a33c442403688d1e1d058f415cfdba704e372455927daa7e4
334335
- filename: src/commands/launch/variables/list.test.ts
335336
checksum: 87ccb762f4931326fc50bca1c01aeb4a16d552e4dfa141d00370fdfafa4363be
336337
- filename: src/commands/launch/variables/set.test.ts
@@ -411,8 +412,52 @@ fileignoreconfig:
411412
checksum: 5ea6a0fcd331496ca19773e90dab81e09eb5290b8f22a1880bffc7916f3778b2
412413
- filename: test/integration/variables-set-unset.test.ts
413414
checksum: 7af9dde4711849d0908d0de54347055a7c2a8d8920f9fcc0b24382b02bceaed9
415+
- filename: test/fixtures/deployments-list.json
416+
checksum: 2f10f24dc0ceb8e85f5201b7c1530c0c540c044ef50b732cc29e07289112316e
417+
- filename: test/integration/deployments-get.test.ts
418+
checksum: beb431ee58fbb3cf72da369514bbeede2c7358f95960979157362053f5b89789
419+
- filename: test/integration/deployments-list.test.ts
420+
checksum: c661f0bd3682ea5b0bfeed782a7c5d03ed75bd86aa1c283bb4b2bda1325bab00
421+
- filename: src/deployments/deployment.inputs.test.ts
422+
checksum: 4c6244f99f62f9731ee925526d17f3ee611d3562cc51cace09ad740ba20c4c84
423+
- filename: test/fixtures/deployment-get.json
424+
checksum: acae8ee5ba96a2aafcf43e0b35b3bd22df78bb9ddbe3bad5e7f86c537316721a
425+
- filename: src/commands/launch/deployments/get.test.ts
426+
checksum: ae3346f213f9ba5a0a32ef2f2f88146e705838f1eca035de659fc4f8d9c0223b
427+
- filename: src/commands/launch/deployments/list.test.ts
428+
checksum: e493cd51e7c65bbf5b4f71704478b7a669bca4342efc865a1363bc508fe6f9f0
429+
- filename: src/deployments/deployment.presenter.test.ts
430+
checksum: e393d6bb6ce3fd92920f5003603b161186085ea8939fae704e6f71bd9e1778bc
431+
- filename: test/integration/deployments-create.test.ts
432+
checksum: fd367019e10b47121898440e4b3a54fb2041d6302bee2dea28a97ad58eb88ee9
433+
- filename: src/deployments/deployment.watcher.test.ts
434+
checksum: c2cdab908cba7d4e162d06f59a471f918c959eb9c220ebc3666f0c6299f141b2
435+
- filename: src/deployments/deployment.create.ts
436+
checksum: ca64f3b29a68c4a2755a88a141e2cd9dbd6fa851a2776876d78c40f3168492e3
437+
- filename: src/commands/launch/deployments/create.test.ts
438+
checksum: 50c3f35c7246b49f6ebf5e48a9a9646ccbcb563c0c0e042fd38917e2f98cbba3
439+
- filename: src/deployments/deployment.create.test.ts
440+
checksum: 6545afa21a23b7e5de18ad8ff417f88a184577e7042713cfda90758ccbcd9869
441+
- filename: test/integration/deployments-rollback.test.ts
442+
checksum: 08400c5435c84cd162fefc585f6c32cddfcaa8909d2369341748b421e7f12073
443+
- filename: test/integration/deployments-cancel.test.ts
444+
checksum: 02c012da974f1f9b8b851d7e00878fef50c1ba7e32b6acf72a309582c66aa5c2
445+
- filename: src/commands/launch/deployments/cancel.test.ts
446+
checksum: da8d09de7f03cfe67df75ebc1636b36feb6688cf85984c357fd5304852d126e5
447+
- filename: src/commands/launch/deployments/rollback.test.ts
448+
checksum: 5331e774d07387b1691c47168afa8d454bdee60d26ac9c6ed3f4640fc85ff8b8
449+
- filename: src/deployments/deployments.api.test.ts
450+
checksum: b27d91317c17f1754d5853c68f4968e63dc2b3579eab26b4c772cf3cd3efbdd0
451+
- filename: src/deployments/deployment.prompt.test.ts
452+
checksum: 3367e53ce2f0284c14524da893e7f82ef613fcdac1e9689ac408709e7f7c153d
453+
- filename: test/integration/deployments-request-bodies.test.ts
454+
checksum: 37d518b1d7671dc1bb9edd147f381c63ddb22e8a413dd43c1c280fb583e83ad2
414455
- filename: src/cache/cache.inputs.test.ts
415456
checksum: 4c602f53a296caec6bab752c905a8abb3da2462ed83b0a5337e8838e87cd9dbd
416457
- filename: src/commands/launch/cache/purge.test.ts
417458
checksum: f1a319cc3af775f28919e56fa17bd7b7e6b89cb4541db506ca8e5fac5edb6026
459+
- filename: src/deployments/commit.prompt.test.ts
460+
checksum: d811a8b35d640708a9d0e7b03d006fcae4718192adee94c47f625ce7662ea715
461+
- filename: src/deployments/commit.prompt.ts
462+
checksum: 6991eb373ee016ce44fc4f47044d559cfeefa1c411526d6fd88d77c568379041
418463
version: "1.0"

‎AGENTS.md‎

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ file and line that broke its rule, and each was proven red by reintroducing its
5858
| `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 |
5959
| `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` |
6060
| `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` |
6162
| `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 |
6263
| `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 |
6364

@@ -151,8 +152,9 @@ src/
151152
each name the other in the layering allow-list
152153
deployments/ the deployment repository, the status classification, the
153154
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
156158
uploads/ zipping a folder, the signed upload URL, and the transfer behind every
157159
FileUpload source (FolderUploader). The UploadScope picks the endpoint:
158160
{ org } for a new project, { org, project } for a new environment,
@@ -640,6 +642,23 @@ there is **no server-side maximum at all**.
640642
each repository builds on `scanPages`, because the page size is that repository's decision and not a
641643
CLI-wide one; the scan-limit error each raises carries its own wording.
642644

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+
643662
**The deployment wait/stream loop.** `src/deployments/deployment.watcher.ts` polls a deployment to a
644663
terminal state and is the single place any command waits on one. `POST /projects` creates the
645664
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`,
686705
redirected output stays free of escape codes). Once `POST /projects` succeeds, before the config file is
687706
written or anything is watched, create prints V1's green `info: New project created successfully`
688707
(`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,
690709
waits `SITE_OPEN_DELAY_MS` (six seconds, V1's wait, because a site opened the moment it reports
691710
live can still answer "site not reachable") and opens that url in the browser, terminal or not, as V1
692711
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
696715
ascending order, and without one it returns the newest page in **descending** order, so never let the
697716
cursor drop the query. The poll that sees the terminal status still reads the logs once more before
698717
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.
702720

703721
**Writing a test for it: the fake clock must advance.** `now()` and `sleep()` are injected and both
704722
are required, not optional with defaults. A fake `now: () => 0` with a `sleep` that does nothing
@@ -812,12 +830,11 @@ project and `--config`.
812830
**Deployment failure is a partial success, and the wording says so.** The project and environment are
813831
real and are **not** rolled back. Everything after `POST /projects` - finding the first environment,
814832
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.
821838

822839
**The FileUpload archive.** `src/uploads/upload.archive.ts` owns the exclusion list
823840
(`node_modules`, `.git`, `.env`, `.env.local`, `.next`, `logs`, `.vscode`, `.cs-launch.json`) and

0 commit comments

Comments
 (0)