@ultimat3/cli 29.3.2 → 30.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +64 -40
- package/README.md +61 -11
- package/package.json +42 -28
- package/src/affected-tests.ts +97 -0
- package/src/affected-unowned.ts +43 -0
- package/src/affected.ts +25 -6
- package/src/app-boundaries.ts +29 -1
- package/src/app-evals.ts +12 -4
- package/src/app-load-unresolved.ts +62 -0
- package/src/app-load.ts +30 -37
- package/src/app-mcp.ts +14 -2
- package/src/app-module-graph.ts +232 -0
- package/src/app-reload-graph.ts +2 -2
- package/src/app-root.ts +37 -1
- package/src/bin.ts +1 -0
- package/src/boundary-findings.ts +6 -0
- package/src/budgets-freshness.ts +32 -0
- package/src/budgets.ts +20 -1
- package/src/build-capture-env.ts +9 -0
- package/src/cdp-shot-a11y.ts +2 -39
- package/src/ci-tested.ts +243 -0
- package/src/client-emit-customer-runtime.ts +139 -0
- package/src/client-emit-customer.ts +108 -0
- package/src/client-emit-drift.ts +72 -0
- package/src/client-emit-header.ts +67 -0
- package/src/client-emit-schemas.ts +175 -0
- package/src/client-emit-types.ts +121 -0
- package/src/client-emit-vendor.ts +146 -0
- package/src/client-errors-routes.ts +229 -0
- package/src/client-errors-script.ts +74 -0
- package/src/client-vitals-route.ts +197 -0
- package/src/client-vitals-script.ts +56 -0
- package/src/cmd-affected.ts +23 -0
- package/src/cmd-build-spec.ts +7 -1
- package/src/cmd-build.ts +35 -9
- package/src/cmd-ci-spec.ts +42 -6
- package/src/cmd-ci.ts +99 -2
- package/src/cmd-client-customer.ts +134 -0
- package/src/cmd-client-source.ts +81 -0
- package/src/cmd-client-spec.ts +48 -0
- package/src/cmd-client-write.ts +41 -0
- package/src/cmd-client.ts +111 -0
- package/src/cmd-db-recount.ts +120 -0
- package/src/cmd-db-spec.ts +36 -6
- package/src/cmd-db.ts +13 -3
- package/src/cmd-deploy-compose.ts +61 -3
- package/src/cmd-deploy-drain.ts +10 -2
- package/src/cmd-deploy-helm.ts +11 -0
- package/src/cmd-deploy-preflight.ts +118 -0
- package/src/cmd-deploy-spec.ts +58 -6
- package/src/cmd-deploy-verify.ts +380 -0
- package/src/cmd-deploy.ts +42 -9
- package/src/cmd-docs.ts +34 -4
- package/src/cmd-doctor.ts +66 -9
- package/src/cmd-drill-spec.ts +25 -0
- package/src/cmd-drill.ts +176 -0
- package/src/cmd-env-init.ts +102 -0
- package/src/cmd-env-spec.ts +10 -3
- package/src/cmd-env.ts +15 -10
- package/src/cmd-errors-recent.ts +60 -0
- package/src/cmd-errors-spec.ts +9 -1
- package/src/cmd-errors.ts +4 -0
- package/src/cmd-exercise-spec.ts +19 -0
- package/src/cmd-exercise.ts +129 -0
- package/src/cmd-generate-spec.ts +9 -1
- package/src/cmd-generate.ts +32 -27
- package/src/cmd-graph-load.ts +97 -0
- package/src/cmd-graph-spec.ts +16 -0
- package/src/cmd-graph.ts +73 -0
- package/src/cmd-i18n.ts +4 -1
- package/src/cmd-impact-spec.ts +13 -0
- package/src/cmd-impact.ts +64 -0
- package/src/cmd-jobs-resolve.ts +76 -0
- package/src/cmd-jobs-spec.ts +21 -2
- package/src/cmd-jobs.ts +10 -6
- package/src/cmd-mail-spec.ts +28 -0
- package/src/cmd-mail.ts +70 -0
- package/src/cmd-mcp-lock.ts +69 -0
- package/src/cmd-mcp-spec.ts +23 -4
- package/src/cmd-mcp.ts +17 -3
- package/src/cmd-new-spec.ts +8 -1
- package/src/cmd-new.ts +61 -9
- package/src/cmd-planned.ts +0 -6
- package/src/cmd-repro-spec.ts +31 -0
- package/src/cmd-repro.ts +174 -0
- package/src/cmd-stop-spec.ts +21 -0
- package/src/cmd-stop.ts +166 -0
- package/src/cmd-test-spec.ts +17 -4
- package/src/cmd-test.ts +70 -14
- package/src/cmd-upgrade-spec.ts +26 -0
- package/src/cmd-upgrade.ts +274 -0
- package/src/cmd-verify-spec.ts +3 -3
- package/src/db-backfill.ts +6 -0
- package/src/db-diagnostics.ts +106 -0
- package/src/db-generate.ts +28 -8
- package/src/db-reset-template.ts +1 -1
- package/src/db-rolling.ts +45 -0
- package/src/db-seed-generate.ts +188 -0
- package/src/deploy-verify-findings.ts +98 -0
- package/src/deploy-verify-flags.ts +109 -0
- package/src/deploy-verify-grade.ts +185 -0
- package/src/deploy-verify-queues.ts +53 -0
- package/src/dev-boot.ts +35 -15
- package/src/dev-child-watch.ts +16 -4
- package/src/dev-dashboard-guard.ts +8 -1
- package/src/dev-dashboard.ts +7 -0
- package/src/dev-lock-identity.ts +61 -0
- package/src/dev-lock-port-holder.ts +80 -0
- package/src/dev-lock-steal.ts +124 -0
- package/src/dev-lock.ts +92 -120
- package/src/dev-mail-templates.ts +51 -0
- package/src/dev-rebuild.ts +26 -2
- package/src/dev-route-table.ts +17 -0
- package/src/dev-supervisor.ts +5 -2
- package/src/dev-watch-tree.ts +11 -10
- package/src/dev-watch.ts +1 -1
- package/src/dispatch.ts +29 -6
- package/src/docs-section.ts +69 -0
- package/src/docs-sources.ts +100 -0
- package/src/doctor-auth.ts +11 -2
- package/src/doctor-chart-alerts.ts +55 -0
- package/src/doctor-pooler.ts +107 -0
- package/src/doctor-rolling.ts +35 -0
- package/src/doctor-row-security.ts +40 -0
- package/src/document-options.ts +13 -0
- package/src/drill-sync-lane.ts +25 -0
- package/src/drill-targets.ts +39 -0
- package/src/duplicate-packages.ts +1 -1
- package/src/env-production.ts +93 -0
- package/src/error-codes-shot.ts +59 -0
- package/src/error-codes-tooling.ts +29 -0
- package/src/error-codes.ts +100 -54
- package/src/error-contract.ts +2 -2
- package/src/errors.ts +22 -3
- package/src/exercise-database.ts +99 -0
- package/src/exercise-findings.ts +159 -0
- package/src/exercise-overrides.ts +42 -0
- package/src/exercise-registration.ts +58 -0
- package/src/exercise-static.ts +88 -0
- package/src/exercise-subject.ts +69 -0
- package/src/failure-capture-boot.ts +30 -0
- package/src/framework-env.ts +7 -1
- package/src/framework-schema.ts +72 -5
- package/src/generate-catalog-locales.ts +19 -0
- package/src/generate-chart-alerts.ts +156 -0
- package/src/generate-feature-required.ts +65 -0
- package/src/generate-feature.ts +83 -6
- package/src/generate-files.ts +24 -0
- package/src/generate-format-written.ts +32 -0
- package/src/generate-kinds.ts +6 -0
- package/src/generate-sign-in-config.ts +100 -0
- package/src/generate-sign-in-manifest.ts +28 -0
- package/src/generate-sign-in-mode.ts +101 -0
- package/src/generate-sign-in.ts +250 -0
- package/src/generate-write.ts +44 -7
- package/src/hand-rolled-socket.ts +72 -0
- package/src/i18n-app-key-shapes.ts +29 -0
- package/src/i18n-audit.ts +10 -3
- package/src/i18n-registration.ts +18 -3
- package/src/index.ts +2 -0
- package/src/island-bundle.ts +4 -3
- package/src/island-disposer.ts +116 -0
- package/src/island-realtime-state.ts +34 -6
- package/src/island-realtime.ts +6 -2
- package/src/island-routes.ts +3 -2
- package/src/isr-query-advice.ts +6 -11
- package/src/isr-query-guard.ts +4 -3
- package/src/job-budget.ts +95 -0
- package/src/jobs-json.ts +11 -0
- package/src/lint-findings.ts +104 -0
- package/src/local-cli.ts +34 -4
- package/src/loopback-host.ts +2 -2
- package/src/mail-preview-findings.ts +24 -0
- package/src/mcp-errors.ts +82 -2
- package/src/mcp-host.ts +11 -1
- package/src/mcp-lock-findings.ts +57 -0
- package/src/measure-paths.ts +12 -4
- package/src/measure-scope.ts +27 -13
- package/src/messages-client.ts +23 -0
- package/src/messages-db.ts +56 -0
- package/src/messages-gate.ts +107 -0
- package/src/messages-repro.ts +11 -0
- package/src/messages-shot.ts +51 -0
- package/src/messages-upgrade.ts +45 -0
- package/src/messages.ts +53 -178
- package/src/metrics-endpoint.ts +23 -5
- package/src/new-shapes-generate.ts +71 -0
- package/src/new-shapes.ts +63 -0
- package/src/openapi-compile-ir.ts +68 -0
- package/src/openapi-compile-schema.ts +284 -0
- package/src/openapi-compile.ts +273 -0
- package/src/openapi-refuse.ts +45 -0
- package/src/otlp-export.ts +2 -1
- package/src/output-findings.ts +123 -0
- package/src/output.ts +32 -8
- package/src/package-boundaries.ts +110 -0
- package/src/page-pins.ts +23 -0
- package/src/plain-routes.ts +13 -0
- package/src/prerender-entries.ts +59 -0
- package/src/prerender-heaviest.ts +27 -0
- package/src/prerender-preload.ts +6 -0
- package/src/prerender-report.ts +12 -0
- package/src/prerender-seo-reads.ts +38 -0
- package/src/prerender-served.ts +22 -7
- package/src/prerender-vitals.ts +42 -0
- package/src/prerender.ts +61 -32
- package/src/pwa-artifacts.ts +4 -3
- package/src/pwa-manifest-locales.ts +1 -1
- package/src/registry.ts +18 -0
- package/src/repro-bundle.ts +65 -0
- package/src/repro-errors.ts +47 -0
- package/src/repro-job.ts +72 -0
- package/src/repro-literal.ts +41 -0
- package/src/repro-request.ts +48 -0
- package/src/repro-write.ts +100 -0
- package/src/retain-sweep.ts +27 -0
- package/src/retained-assets-boot.ts +113 -0
- package/src/retained-assets-layout.ts +110 -0
- package/src/retained-assets-read.ts +142 -0
- package/src/retained-assets-sweep.ts +70 -0
- package/src/retained-assets.ts +171 -0
- package/src/revalidated-response.ts +8 -5
- package/src/role-replicator.ts +7 -1
- package/src/role-start-devices.ts +119 -0
- package/src/role-start-types.ts +6 -0
- package/src/role-start.ts +22 -5
- package/src/role-sync.ts +7 -1
- package/src/route-islands.ts +42 -0
- package/src/route-table.ts +2 -1
- package/src/row-security-boot.ts +58 -0
- package/src/row-security-wanted.ts +32 -0
- package/src/runtime-admin.ts +0 -1
- package/src/runtime-assets-site-cache.ts +55 -0
- package/src/runtime-assets-site.ts +136 -0
- package/src/runtime-assets.ts +99 -55
- package/src/runtime-bindings.ts +19 -3
- package/src/runtime-cache.ts +23 -14
- package/src/runtime-isr-outcome.ts +114 -60
- package/src/runtime-purge.ts +14 -2
- package/src/runtime-queue.ts +31 -4
- package/src/runtime-render-document.ts +204 -0
- package/src/runtime-render.ts +48 -198
- package/src/runtime-replica-position.ts +62 -0
- package/src/runtime-replica.ts +18 -3
- package/src/runtime-s3-path-style.ts +17 -0
- package/src/runtime-services.ts +19 -6
- package/src/runtime-statement-bound.ts +22 -0
- package/src/runtime-storage-upload.ts +9 -0
- package/src/runtime-storage.ts +42 -138
- package/src/schema-diff-views.ts +54 -0
- package/src/schema-diff.ts +21 -3
- package/src/schema-drift.ts +28 -9
- package/src/schema-dump-drift.ts +91 -7
- package/src/script-csp.ts +1 -1
- package/src/seo-cards.ts +26 -0
- package/src/seo-routes.ts +22 -8
- package/src/serve-boot.ts +54 -21
- package/src/serve-drain.ts +1 -0
- package/src/serve-env.ts +26 -8
- package/src/serve-retire.ts +12 -6
- package/src/serve-web.ts +42 -4
- package/src/serve.ts +9 -7
- package/src/site-asset-routes.ts +17 -27
- package/src/site-config.ts +4 -0
- package/src/site-llms-pages.ts +106 -0
- package/src/site-llms.ts +64 -0
- package/src/site-seo-write.ts +4 -2
- package/src/site-seo.ts +55 -6
- package/src/source-mtime.ts +50 -0
- package/src/static-capture-errors.ts +41 -0
- package/src/static-capture.ts +56 -0
- package/src/static-cards-serve.ts +39 -0
- package/src/static-cards.ts +93 -0
- package/src/static-document.ts +14 -13
- package/src/static-images.ts +115 -0
- package/src/stream-hole-scope.ts +55 -0
- package/src/style-bundle.ts +1 -1
- package/src/style-routes.ts +3 -2
- package/src/sw-precache-plan.ts +2 -2
- package/src/sw-routes.ts +2 -2
- package/src/templates/action-tests.ts +306 -0
- package/src/templates/action.ts +81 -269
- package/src/templates/backfill.ts +1 -1
- package/src/templates/entity.ts +21 -5
- package/src/templates/github/ci.yml.ts +8 -9
- package/src/templates/guard-bare-error.ts +2 -2
- package/src/templates/guard-error-declared.ts +263 -0
- package/src/templates/guard-native-confirm.ts +132 -0
- package/src/templates/guard-raw-fetch.ts +151 -0
- package/src/templates/guard-raw-interval.ts +121 -0
- package/src/templates/guard-repo-raw-sql.ts +71 -2
- package/src/templates/job.ts +1 -1
- package/src/templates/naming.ts +5 -0
- package/src/templates/pinned/docker-compose.yml +6 -0
- package/src/templates/pinned-images.ts +8 -0
- package/src/templates/policy.ts +85 -2
- package/src/templates/query.ts +70 -13
- package/src/templates/resource-create.ts +3 -1
- package/src/templates/resource-form-island.ts +15 -4
- package/src/templates/resource-page-db.ts +83 -0
- package/src/templates/resource-page.ts +161 -52
- package/src/templates/route.ts +4 -6
- package/src/templates/scaffold-app.ts +3 -0
- package/src/templates/scaffold-auth.ts +2 -1
- package/src/templates/scaffold-bin.ts +5 -52
- package/src/templates/scaffold-claude-agents.ts +19 -19
- package/src/templates/scaffold-claude-commands.ts +28 -31
- package/src/templates/scaffold-claude.ts +26 -23
- package/src/templates/scaffold-container-compose.ts +6 -1
- package/src/templates/scaffold-container.ts +24 -4
- package/src/templates/scaffold-dashboard-bare.ts +3 -3
- package/src/templates/scaffold-dashboard-example-tests.ts +49 -9
- package/src/templates/scaffold-dashboard-example-view.ts +156 -0
- package/src/templates/scaffold-dashboard-example.ts +43 -131
- package/src/templates/scaffold-dashboard-shared.ts +3 -7
- package/src/templates/scaffold-db-client.ts +20 -6
- package/src/templates/scaffold-db-package.ts +5 -14
- package/src/templates/scaffold-docs.ts +73 -57
- package/src/templates/scaffold-domain-package.ts +18 -14
- package/src/templates/scaffold-entries.ts +7 -2
- package/src/templates/scaffold-exercise.ts +37 -0
- package/src/templates/scaffold-guards.ts +13 -1
- package/src/templates/scaffold-helm-alerts.ts +234 -0
- package/src/templates/scaffold-helm-templates.ts +7 -5
- package/src/templates/scaffold-helm.ts +46 -12
- package/src/templates/scaffold-i18n.ts +6 -2
- package/src/templates/scaffold-mcp-endpoint.ts +182 -0
- package/src/templates/scaffold-mcp-json.ts +31 -0
- package/src/templates/scaffold-mcp-package.ts +59 -56
- package/src/templates/scaffold-repo.ts +7 -2
- package/src/templates/scaffold-shell.ts +45 -79
- package/src/templates/scaffold-test-services.ts +13 -5
- package/src/templates/shapes/api-keys.ts +170 -0
- package/src/templates/shapes/api.ts +101 -0
- package/src/templates/shapes/blog-site.ts +340 -0
- package/src/templates/shapes/blog-slice.ts +265 -0
- package/src/templates/shapes/blog.ts +114 -0
- package/src/templates/shapes/dashboard-detail.ts +242 -0
- package/src/templates/shapes/dashboard.ts +53 -0
- package/src/templates/shapes/index.ts +17 -0
- package/src/templates/shapes/overlay.ts +99 -0
- package/src/templates/shapes/shared-edits.ts +46 -0
- package/src/templates/sign-in-action-tests.ts +237 -0
- package/src/templates/sign-in-authenticator.ts +112 -0
- package/src/templates/sign-in-oidc.ts +189 -0
- package/src/templates/sign-in-pages.ts +148 -0
- package/src/templates/sign-in-password.ts +216 -0
- package/src/templates/sign-in-shared.ts +268 -0
- package/src/templates/slice-foundation.ts +70 -15
- package/src/test-batches.ts +1 -1
- package/src/test-database.ts +1 -1
- package/src/test-shards.ts +7 -1
- package/src/test-workers.ts +18 -12
- package/src/tool-gate-names.ts +34 -0
- package/src/upgrade-agents-link.ts +49 -0
- package/src/upgrade-bun-floor.ts +225 -0
- package/src/upgrade-gate.ts +228 -0
- package/src/upgrade-pins.ts +63 -0
- package/src/upgrade-plan.ts +214 -0
- package/src/upgrade-registry.ts +53 -0
- package/src/upgrade-report.ts +139 -0
- package/src/upgrade-root.ts +74 -0
- package/src/verify-budgets-build.ts +118 -0
- package/src/verify-checks.ts +67 -24
- package/src/verify-coverage-run.ts +1 -1
- package/src/verify-e2e-lane.ts +41 -0
- package/src/verify-exercise.ts +64 -0
- package/src/verify-floor.ts +41 -0
- package/src/verify-merge.ts +19 -4
- package/src/verify-role-load.ts +2 -0
- package/src/verify-run.ts +28 -8
- package/src/verify-shard.ts +12 -2
- package/src/verify-skip-ceiling.ts +87 -0
- package/src/verify-step.ts +5 -0
- package/src/verify-tests.ts +25 -5
- package/src/view-drift.ts +55 -0
- package/src/worker-bundle.ts +3 -2
- package/src/workspace-graph.ts +38 -19
- package/src/write-generated.ts +43 -0
- package/src/zone-boot.ts +20 -0
- package/wiki/Actions.md +342 -0
- package/wiki/Admin-Dashboard.md +432 -0
- package/wiki/Agents.md +958 -0
- package/wiki/Auth.md +195 -0
- package/wiki/Bare-VM.md +111 -0
- package/wiki/Batching-And-Preloading.md +136 -0
- package/wiki/Building-Your-Own-Base.md +203 -0
- package/wiki/CI-Parallel-Gate.md +220 -0
- package/wiki/CLI-Reference.md +1894 -0
- package/wiki/Caching-And-Invalidation.md +255 -0
- package/wiki/Client-Data.md +194 -0
- package/wiki/Client-Navigation.md +513 -0
- package/wiki/Compared.md +93 -0
- package/wiki/Configuration.md +868 -0
- package/wiki/Contributing.md +12 -0
- package/wiki/Deploy-Recipes.md +122 -0
- package/wiki/Deployment.md +508 -0
- package/wiki/Entities-And-Migrations.md +1116 -0
- package/wiki/FAQ.md +175 -0
- package/wiki/Feature-Flags.md +65 -0
- package/wiki/Getting-Started.md +221 -0
- package/wiki/Home.md +160 -0
- package/wiki/I18n.md +239 -0
- package/wiki/Installation.md +206 -0
- package/wiki/Interface-Rules.md +339 -0
- package/wiki/Jobs-And-Workflows.md +783 -0
- package/wiki/Known-Gaps.md +185 -0
- package/wiki/MCP-And-AI.md +753 -0
- package/wiki/Mail.md +138 -0
- package/wiki/Migrating-An-Existing-App.md +594 -0
- package/wiki/Migrating-From-Django.md +54 -0
- package/wiki/Migrating-From-Laravel.md +53 -0
- package/wiki/Migrating-From-Node-Drizzle.md +53 -0
- package/wiki/Migrating-From-Rails.md +53 -0
- package/wiki/Migrations-And-Backfills.md +552 -0
- package/wiki/Money.md +338 -0
- package/wiki/N-Plus-One-Detection.md +197 -0
- package/wiki/Notify.md +208 -0
- package/wiki/Observability.md +245 -0
- package/wiki/Outbound-HTTP.md +109 -0
- package/wiki/PWA-And-Offline.md +343 -0
- package/wiki/Policies-And-Authz.md +251 -0
- package/wiki/Project-Layout.md +150 -0
- package/wiki/Queries-And-Live-Queries.md +429 -0
- package/wiki/Realtime.md +734 -0
- package/wiki/Resource-Management.md +121 -0
- package/wiki/Routes-And-Render-Modes.md +521 -0
- package/wiki/SEO.md +288 -0
- package/wiki/Scheduled-Tasks.md +175 -0
- package/wiki/Scraping.md +493 -0
- package/wiki/Shapes.md +63 -0
- package/wiki/Stability.md +97 -0
- package/wiki/Static-Assets.md +166 -0
- package/wiki/Storage-And-Uploads.md +404 -0
- package/wiki/Testing.md +1313 -0
- package/wiki/The-Eight-Primitives.md +247 -0
- package/wiki/Theming.md +362 -0
- package/wiki/Timezones-And-Dates.md +194 -0
- package/wiki/Troubleshooting.md +163 -0
- package/wiki/Tutorial-01-First-App.md +289 -0
- package/wiki/Tutorial-02-First-Feature.md +339 -0
- package/wiki/Tutorial-03-Auth-And-Admin.md +379 -0
- package/wiki/Tutorial-04-Jobs-And-Realtime.md +263 -0
- package/wiki/Tutorial-05-Deploy-Free.md +231 -0
- package/wiki/Tutorial-06-Growing-Up.md +158 -0
- package/wiki/UI-Components.md +156 -0
- package/wiki/Upgrading.md +3667 -0
- package/src/stored-object-headers.ts +0 -43
package/src/cmd-db-spec.ts
CHANGED
|
@@ -12,13 +12,15 @@ export const DB_SUBCOMMANDS = [
|
|
|
12
12
|
'studio',
|
|
13
13
|
'branch',
|
|
14
14
|
'backfill',
|
|
15
|
+
'diagnostics',
|
|
16
|
+
'recount',
|
|
15
17
|
] as const;
|
|
16
18
|
|
|
17
19
|
export const dbSpec: CommandSpec = {
|
|
18
20
|
name: 'db',
|
|
19
|
-
summary: 'gen, migrate, reset, seed, studio, branch, backfill',
|
|
21
|
+
summary: 'gen, migrate, reset, seed, studio, branch, backfill, diagnostics, recount',
|
|
20
22
|
usage:
|
|
21
|
-
'x db gen "add publish_at" | migrate | reset | seed [<name>] [--tier reference|dev] [--dry-run] | studio | branch list | branch create <name> | branch delete <name> | backfill [<name>|--all] [--write] [--force] | backfill --pending | backfill --list [--name n] [--status s] [--limit n]',
|
|
23
|
+
'x db gen "add publish_at" | migrate | reset | seed [<name>] [--tier reference|dev] [--dry-run] | seed --generate [--rows n] [--seed n] [--dry-run] | studio | branch list | branch create <name> | branch delete <name> | backfill [<name>|--all] [--write] [--force] | backfill --pending | backfill --list [--name n] [--status s] [--limit n] | diagnostics [--limit n] | recount <entity>.<column> [--write]',
|
|
22
24
|
requiresApp: true,
|
|
23
25
|
subcommands: DB_SUBCOMMANDS,
|
|
24
26
|
// Declared from the constant `runBranchCommand` validates against, never a second literal: it
|
|
@@ -46,6 +48,26 @@ export const dbSpec: CommandSpec = {
|
|
|
46
48
|
summary: 'seed: report what each seed would write, and write nothing',
|
|
47
49
|
subcommands: ['seed'],
|
|
48
50
|
},
|
|
51
|
+
// Rows for every table off the entity declarations, with no seed written: the seed graph
|
|
52
|
+
// `x exercise` runs on. Fixture data, so `dev` only; production is refused whatever the tier.
|
|
53
|
+
{
|
|
54
|
+
name: 'generate',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
summary: 'seed: generated dev rows for every entity, parents first; never in production',
|
|
57
|
+
subcommands: ['seed'],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'rows',
|
|
61
|
+
type: 'string',
|
|
62
|
+
summary: 'seed: with --generate, rows per table (per tenant when tenant-scoped; default 3)',
|
|
63
|
+
subcommands: ['seed'],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'seed',
|
|
67
|
+
type: 'string',
|
|
68
|
+
summary: 'seed: with --generate, the integer the graph is drawn from — same seed, same rows',
|
|
69
|
+
subcommands: ['seed'],
|
|
70
|
+
},
|
|
49
71
|
{
|
|
50
72
|
name: 'list',
|
|
51
73
|
type: 'boolean',
|
|
@@ -67,8 +89,8 @@ export const dbSpec: CommandSpec = {
|
|
|
67
89
|
{
|
|
68
90
|
name: 'write',
|
|
69
91
|
type: 'boolean',
|
|
70
|
-
summary: 'backfill: enqueue the pass; dry run without it',
|
|
71
|
-
subcommands: ['backfill'],
|
|
92
|
+
summary: 'backfill, recount: enqueue the pass, or set drifted counters; dry run without it',
|
|
93
|
+
subcommands: ['backfill', 'recount'],
|
|
72
94
|
},
|
|
73
95
|
{
|
|
74
96
|
name: 'force',
|
|
@@ -85,8 +107,9 @@ export const dbSpec: CommandSpec = {
|
|
|
85
107
|
{
|
|
86
108
|
name: 'limit',
|
|
87
109
|
type: 'string',
|
|
88
|
-
summary:
|
|
89
|
-
|
|
110
|
+
summary:
|
|
111
|
+
'backfill, diagnostics: max ledger rows, or rows per diagnostics section (default 20)',
|
|
112
|
+
subcommands: ['backfill', 'diagnostics'],
|
|
90
113
|
},
|
|
91
114
|
// Declared because `X_MIGRATION_IRREVERSIBLE`'s own fix line names it. A `fix:` is copied
|
|
92
115
|
// and run verbatim, so a flag the parser refuses would make the error unfollowable.
|
|
@@ -95,5 +118,12 @@ export const dbSpec: CommandSpec = {
|
|
|
95
118
|
type: 'boolean',
|
|
96
119
|
summary: 'let x db gen emit a drop whose down cannot restore the rows',
|
|
97
120
|
},
|
|
121
|
+
// Declared because `X_MIGRATION_ROLLING_UNSAFE`'s own fix line names it.
|
|
122
|
+
{
|
|
123
|
+
name: 'rolling-unsafe',
|
|
124
|
+
type: 'boolean',
|
|
125
|
+
summary:
|
|
126
|
+
'let x db gen drop a column never retired, for an app that stops before it starts: one instance, or strategy: Recreate',
|
|
127
|
+
},
|
|
98
128
|
],
|
|
99
129
|
};
|
package/src/cmd-db.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// `x db gen|migrate|reset|seed|studio|branch|backfill` — everything that touches the
|
|
2
|
-
// subcommand per line and no fall-through: a word this file does not know is refused,
|
|
3
|
-
// re-read as an argument to the last branch. `branch`
|
|
1
|
+
// `x db gen|migrate|reset|seed|studio|branch|backfill|diagnostics|recount` — everything that touches the
|
|
2
|
+
// database. One subcommand per line and no fall-through: a word this file does not know is refused,
|
|
3
|
+
// never re-read as an argument to the last branch. `branch` is `cmd-db-branch.ts`, `diagnostics`
|
|
4
|
+
// is `db-diagnostics.ts`.
|
|
4
5
|
//
|
|
5
6
|
// Every subcommand here runs `@ultimat3/db`'s own engine, which is the engine `ROLE=migrate` runs
|
|
6
7
|
// (`serve.ts`): one `x_migrations` ledger, one checksum rule, one advisory lock, from a laptop to
|
|
@@ -17,9 +18,11 @@ import { postgresDriver } from '@ultimat3/entity';
|
|
|
17
18
|
import { requireAppRoot } from './app-root';
|
|
18
19
|
import { runBackfillCommand } from './cmd-db-backfill';
|
|
19
20
|
import { runBranchCommand } from './cmd-db-branch';
|
|
21
|
+
import { runRecountCommand } from './cmd-db-recount';
|
|
20
22
|
import { DB_SUBCOMMANDS, dbSpec } from './cmd-db-spec';
|
|
21
23
|
import { plannedSubcommand } from './cmd-planned';
|
|
22
24
|
import type { CliCommand, CommandContext } from './command';
|
|
25
|
+
import { runDiagnostics } from './db-diagnostics';
|
|
23
26
|
import { stepFinding } from './db-finding';
|
|
24
27
|
import { generateAppMigration, unrenderedJson, unrenderedLines } from './db-generate';
|
|
25
28
|
import { liveObjectDrift } from './db-object-drift';
|
|
@@ -40,6 +43,7 @@ import {
|
|
|
40
43
|
seedTotals,
|
|
41
44
|
selectSeeds,
|
|
42
45
|
} from './db-seed';
|
|
46
|
+
import { runGeneratedSeed } from './db-seed-generate';
|
|
43
47
|
import { DevAlreadyRunningError, liveDevLock } from './dev-lock';
|
|
44
48
|
import { MissingSubcommandError, UnknownCommandError } from './errors';
|
|
45
49
|
import { withJobDriver } from './jobs-driver';
|
|
@@ -71,6 +75,8 @@ export const dbCommand: CliCommand = {
|
|
|
71
75
|
if (sub === 'studio') throw plannedSubcommand('db', 'studio');
|
|
72
76
|
if (sub === 'backfill') return runBackfillCommand(ctx, root);
|
|
73
77
|
if (sub === 'branch') return runBranchCommand(ctx, root);
|
|
78
|
+
if (sub === 'diagnostics') return runDiagnostics(ctx, root);
|
|
79
|
+
if (sub === 'recount') return runRecountCommand(ctx, root);
|
|
74
80
|
|
|
75
81
|
// Never a fall-through. This used to end `return runBranch(ctx, root, argument ?? 'preview')`,
|
|
76
82
|
// so ANY subcommand the parser had not already refused was reinterpreted as a branch NAME and
|
|
@@ -102,6 +108,7 @@ async function runGen(ctx: CommandContext, root: string, name: string): Promise<
|
|
|
102
108
|
generated = await generateAppMigration(root, {
|
|
103
109
|
name,
|
|
104
110
|
allowDestructive: flagBool(ctx.args, 'allow-destructive'),
|
|
111
|
+
rollingUnsafe: flagBool(ctx.args, 'rolling-unsafe'),
|
|
105
112
|
});
|
|
106
113
|
} catch (error) {
|
|
107
114
|
return {
|
|
@@ -166,6 +173,8 @@ async function runGen(ctx: CommandContext, root: string, name: string): Promise<
|
|
|
166
173
|
data: {
|
|
167
174
|
outcome: generated.outcome,
|
|
168
175
|
migration: migration.id,
|
|
176
|
+
// The `-- transaction: false` step written beside it, when the diff needed one.
|
|
177
|
+
followUp: migration.followUp?.id ?? null,
|
|
169
178
|
name: migration.name,
|
|
170
179
|
files: [...generated.files],
|
|
171
180
|
schemaHash: generated.schemaHash ?? null,
|
|
@@ -295,6 +304,7 @@ async function runReset(ctx: CommandContext, root: string): Promise<CommandResul
|
|
|
295
304
|
* `handle.enqueue()` a seeded write triggers. A second boot path would be a second answer.
|
|
296
305
|
*/
|
|
297
306
|
async function runSeed(ctx: CommandContext, root: string): Promise<CommandResult> {
|
|
307
|
+
if (flagBool(ctx.args, 'generate')) return runGeneratedSeed(ctx, root);
|
|
298
308
|
const environment = resolveEnvironment({ env: ctx.env });
|
|
299
309
|
const requested = parseSeedTierFlag(
|
|
300
310
|
flagString(ctx.args, 'tier') ?? ctx.env['ULTIMATE_SEED_TIER'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// The compose half of `x deploy
|
|
2
|
-
// BEFORE the old one stops
|
|
3
|
-
// `
|
|
1
|
+
// The compose half of `x deploy`, read off the app's compose file: which serving roles can start
|
|
2
|
+
// their new container BEFORE the old one stops (the sequence is `cmd-deploy-start-first.ts`), and
|
|
3
|
+
// what the file demands of `.env.production` — read by the deploy's pre-flight and `x env init`.
|
|
4
4
|
// `docker compose up -d <role>` alone is stop-first — it recreates, so the role serves nothing
|
|
5
5
|
// between the old container's exit and the new one's healthcheck.
|
|
6
6
|
|
|
@@ -101,3 +101,61 @@ export async function readComposeFile(path: string): Promise<string | undefined>
|
|
|
101
101
|
const file = Bun.file(path);
|
|
102
102
|
return (await file.exists()) ? file.text() : undefined;
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The reference `x build --target docker` tags and `x deploy` deploys when neither is given one
|
|
107
|
+
* (`cmd-build.ts` spells the same literal). `x env init` writes it as `IMAGE`, so a compose command
|
|
108
|
+
* run by hand reaches the image the build made rather than the compose file's own default.
|
|
109
|
+
*/
|
|
110
|
+
export const DEFAULT_IMAGE = 'ultimate-app:dev';
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Every variable the file interpolates with `${VAR:?…}` or `${VAR?…}`, in first-use order — the
|
|
114
|
+
* ones compose refuses to parse without. Read off the text, not the parse: YAML keeps the
|
|
115
|
+
* interpolation inside a string, wherever in the document it sits. Comment lines are skipped.
|
|
116
|
+
*/
|
|
117
|
+
export function composeRequiredVars(text: string): readonly string[] {
|
|
118
|
+
// A comment line is never interpolated, and the scaffold's header spells `${VAR:?…}` in prose.
|
|
119
|
+
const live = text
|
|
120
|
+
.split('\n')
|
|
121
|
+
.filter((line) => !line.trimStart().startsWith('#'))
|
|
122
|
+
.join('\n');
|
|
123
|
+
const names = [...live.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*):?\?/g)].map((m) => m[1] ?? '');
|
|
124
|
+
return [...new Set(names)];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The `db` service's role and database, which a `DATABASE_URL` for this file must name. */
|
|
128
|
+
export interface ComposeDatabase {
|
|
129
|
+
readonly user: string;
|
|
130
|
+
readonly database: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** One `environment:` entry, from either of compose's two spellings (map or `KEY=value` list). */
|
|
134
|
+
function environmentValue(environment: unknown, key: string): string | undefined {
|
|
135
|
+
if (isRecord(environment)) {
|
|
136
|
+
const value = environment[key];
|
|
137
|
+
return typeof value === 'string' || typeof value === 'number' ? String(value) : undefined;
|
|
138
|
+
}
|
|
139
|
+
if (!Array.isArray(environment)) return undefined;
|
|
140
|
+
const entry = environment.find((e) => typeof e === 'string' && e.startsWith(`${key}=`));
|
|
141
|
+
return typeof entry === 'string' ? entry.slice(key.length + 1) : undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The `db` service's Postgres role and database: the image's defaults (`postgres`, and a database
|
|
146
|
+
* named after the role) unless `POSTGRES_USER` / `POSTGRES_DB` say otherwise. `undefined` when the
|
|
147
|
+
* file runs no `db` service, so the database is the operator's and no URL can be derived.
|
|
148
|
+
*/
|
|
149
|
+
export function composeDatabase(text: string | undefined): ComposeDatabase | undefined {
|
|
150
|
+
let parsed: unknown;
|
|
151
|
+
try {
|
|
152
|
+
parsed = text === undefined ? undefined : Bun.YAML.parse(text);
|
|
153
|
+
} catch {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const services = isRecord(parsed) ? parsed['services'] : undefined;
|
|
157
|
+
const db = isRecord(services) ? services['db'] : undefined;
|
|
158
|
+
if (!isRecord(db)) return undefined;
|
|
159
|
+
const user = environmentValue(db['environment'], 'POSTGRES_USER') ?? 'postgres';
|
|
160
|
+
return { user, database: environmentValue(db['environment'], 'POSTGRES_DB') ?? user };
|
|
161
|
+
}
|
package/src/cmd-deploy-drain.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// What a helm deploy tells the chart about the drain: `app.config.ts`'s `drain` section — the ONE
|
|
2
2
|
// drain budget since 25.0.0 deleted `http.drainTimeoutMs` — in the seconds `docker/helm` derives
|
|
3
3
|
// each role's terminationGracePeriodSeconds from. Passed on every upgrade, so a raised
|
|
4
|
-
// budget can never ship against a grace period sized for 25 s.
|
|
4
|
+
// budget can never ship against a grace period sized for 25 s, and nor can the retire beside it.
|
|
5
5
|
|
|
6
6
|
import type { DrainConfig } from '@ultimat3/core';
|
|
7
7
|
import { isUltimateError, READINESS_GRACE_DEFAULT_MS } from '@ultimat3/core';
|
|
8
|
+
import { loadAppConfig } from './app-config-load';
|
|
9
|
+
import { helmWorkerTargetOverrides } from './cmd-deploy-helm';
|
|
8
10
|
import { loadDrainConfig } from './serve-drain';
|
|
9
11
|
|
|
10
12
|
const seconds = (ms: number): number => Math.ceil(ms / 1000);
|
|
@@ -29,6 +31,10 @@ export function helmDrainOverrides(drain: Partial<DrainConfig> | undefined): rea
|
|
|
29
31
|
if (drain?.workerDeadlineMs !== undefined) {
|
|
30
32
|
out.push('--set', `drain.workerDeadlineSeconds=${seconds(drain.workerDeadlineMs)}`);
|
|
31
33
|
}
|
|
34
|
+
// Sent at 0 as well: `workerRetireMs: 0` must reach a chart whose default is on.
|
|
35
|
+
if (drain?.workerRetireMs !== undefined) {
|
|
36
|
+
out.push('--set', `roles.worker.retireSeconds=${seconds(drain.workerRetireMs)}`);
|
|
37
|
+
}
|
|
32
38
|
if (drain === undefined) return out;
|
|
33
39
|
const grace = Math.max(drain.readinessGraceMs ?? 0, READINESS_GRACE_DEFAULT_MS);
|
|
34
40
|
out.push('--set', `drain.readinessGraceSeconds=${seconds(grace)}`);
|
|
@@ -43,11 +49,13 @@ export function helmDrainOverrides(drain: Partial<DrainConfig> | undefined): rea
|
|
|
43
49
|
*/
|
|
44
50
|
export async function readHelmDrainOverrides(root: string): Promise<readonly string[]> {
|
|
45
51
|
let drain: Partial<DrainConfig> | undefined;
|
|
52
|
+
let maxInFlight: number | undefined;
|
|
46
53
|
try {
|
|
47
54
|
drain = await loadDrainConfig(root);
|
|
55
|
+
maxInFlight = (await loadAppConfig(root))?.jobs?.maxInFlight;
|
|
48
56
|
} catch (error) {
|
|
49
57
|
if (!isUltimateError(error) || error.code !== 'X_CONFIG_INVALID') throw error;
|
|
50
58
|
return [];
|
|
51
59
|
}
|
|
52
|
-
return helmDrainOverrides(drain);
|
|
60
|
+
return [...helmDrainOverrides(drain), ...helmWorkerTargetOverrides(maxInFlight)];
|
|
53
61
|
}
|
package/src/cmd-deploy-helm.ts
CHANGED
|
@@ -104,6 +104,17 @@ export function helmUpgradeArgs(
|
|
|
104
104
|
];
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* One worker pod's capacity is what an AverageValue target on `queue_demand` must be: the HPA asks
|
|
109
|
+
* for `ceil(demand / target)` pods, so a target above `jobs.maxInFlight` leaves demand no pod can
|
|
110
|
+
* hold. Undeclared, the chart's value stands: nothing here knows the size of a pod.
|
|
111
|
+
*/
|
|
112
|
+
export function helmWorkerTargetOverrides(maxInFlight: number | undefined): readonly string[] {
|
|
113
|
+
return maxInFlight === undefined
|
|
114
|
+
? []
|
|
115
|
+
: ['--set', `roles.worker.autoscaling.targetAverageValue=${maxInFlight}`];
|
|
116
|
+
}
|
|
117
|
+
|
|
107
118
|
/** What `--json` reports about the rollout helm waited on. */
|
|
108
119
|
export interface Rollout {
|
|
109
120
|
readonly release: string;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// What `x deploy --method compose` checks before its first step, and what it reads after a failed
|
|
2
|
+
// one. Before: `.env.production` exists and sets every `${VAR:?}` the compose file demands —
|
|
3
|
+
// without it each step died as `role "migrate" step exited 1`. After: the role's last log line.
|
|
4
|
+
|
|
5
|
+
// why: Bun ships no path-join primitive; the env file is app-root-relative.
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import type { EnvSchema } from '@ultimat3/core';
|
|
8
|
+
import { ERROR_DOCS_URL } from '@ultimat3/core';
|
|
9
|
+
import { composeRequiredVars } from './cmd-deploy-compose';
|
|
10
|
+
import { requiredAtBoot } from './env-production';
|
|
11
|
+
import type { ExecOptions, Runner } from './exec';
|
|
12
|
+
import type { Finding } from './output';
|
|
13
|
+
import { parseDotenv } from './root-env';
|
|
14
|
+
import { PROD_ENV_FILE } from './templates/scaffold-container-compose';
|
|
15
|
+
|
|
16
|
+
type EnvTable = Readonly<Record<string, string | undefined>>;
|
|
17
|
+
|
|
18
|
+
const missingFinding = (cause: string): Finding => ({
|
|
19
|
+
code: 'X_DEPLOY_ENV_MISSING',
|
|
20
|
+
cause,
|
|
21
|
+
fix: 'x env init --production --json',
|
|
22
|
+
docs: ERROR_DOCS_URL,
|
|
23
|
+
at: PROD_ENV_FILE,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The refusal, or `undefined` when every step can parse the compose file and every role can boot.
|
|
28
|
+
* A compose variable counts as set from the shell too — compose reads it there first; a key the
|
|
29
|
+
* app's `envSchema` requires reaches the containers through `env_file:` alone, so only the file
|
|
30
|
+
* counts for it. Empty is as absent as none: `${VAR:?}` and the boot's env check refuse both.
|
|
31
|
+
*/
|
|
32
|
+
export async function composeEnvPreflight(
|
|
33
|
+
root: string,
|
|
34
|
+
compose: string | undefined,
|
|
35
|
+
shell: EnvTable,
|
|
36
|
+
schema: EnvSchema = {},
|
|
37
|
+
): Promise<Finding | undefined> {
|
|
38
|
+
const file = Bun.file(join(root, PROD_ENV_FILE));
|
|
39
|
+
if (!(await file.exists())) {
|
|
40
|
+
return missingFinding(
|
|
41
|
+
`${PROD_ENV_FILE} does not exist, and every compose step reads it (--env-file and env_file:)`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const values = parseDotenv(await file.text());
|
|
45
|
+
const interpolated = (compose === undefined ? [] : composeRequiredVars(compose)).filter(
|
|
46
|
+
(key) => (shell[key] ?? values.get(key) ?? '') === '',
|
|
47
|
+
);
|
|
48
|
+
const declared = Object.entries(schema)
|
|
49
|
+
.filter(([key, decl]) => requiredAtBoot(decl) && (values.get(key) ?? '') === '')
|
|
50
|
+
.map(([key]) => key)
|
|
51
|
+
.filter((key) => !interpolated.includes(key));
|
|
52
|
+
const missing = [...interpolated, ...declared];
|
|
53
|
+
if (missing.length === 0) return undefined;
|
|
54
|
+
return {
|
|
55
|
+
...missingFinding(
|
|
56
|
+
`${PROD_ENV_FILE} leaves ${missing.join(', ')} unset or empty, and ${missing.length === 1 ? 'it is' : 'each is'} required: by docker/docker-compose.prod.yml (\${VAR:?}) or by app.config.ts's envSchema, which every role checks at boot`,
|
|
57
|
+
),
|
|
58
|
+
// The file exists, so the generator would refuse it: the edit is the fix.
|
|
59
|
+
fix: `NODE_ENV=production x env check --json # after setting ${missing.join(', ')} in ${PROD_ENV_FILE}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A rendered `UltimateError`: `X_CODE: title — cause`. A JSON log's `"code":"X_…"` is not one. */
|
|
64
|
+
const RENDERED_CODE = /\bX_[A-Z][A-Z0-9_]*: /;
|
|
65
|
+
|
|
66
|
+
/** Bun's trailer after an uncaught error, the last line of every crash and never its reason. */
|
|
67
|
+
const BUN_TRAILER = /^Bun v\d+\.\d+/;
|
|
68
|
+
|
|
69
|
+
/** The longest line a finding carries: the reason fits, a stack-sized blob does not. */
|
|
70
|
+
const LINE_CEILING = 500;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The line that says why a process failed: the last rendered `X_*` refusal, else the last
|
|
74
|
+
* non-empty line that is not Bun's version trailer. stderr first, where a refusal is written.
|
|
75
|
+
*/
|
|
76
|
+
export function lastLine(output: { readonly stdout: string; readonly stderr: string }): string {
|
|
77
|
+
for (const text of [output.stderr, output.stdout]) {
|
|
78
|
+
const lines = text
|
|
79
|
+
.split(/\r?\n/)
|
|
80
|
+
.map((entry) => entry.trim())
|
|
81
|
+
.filter((entry) => entry !== '');
|
|
82
|
+
const line =
|
|
83
|
+
lines.findLast((entry) => RENDERED_CODE.test(entry)) ??
|
|
84
|
+
lines.findLast((entry) => !BUN_TRAILER.test(entry));
|
|
85
|
+
if (line !== undefined) return line.slice(0, LINE_CEILING);
|
|
86
|
+
}
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A serving role's last log line, read after its step failed: a crash-looping container says why
|
|
92
|
+
* in its log, and `up -d` itself only says that it did not become healthy. Empty when compose
|
|
93
|
+
* cannot read the log either; the finding then carries the rerun command alone.
|
|
94
|
+
*/
|
|
95
|
+
export async function roleLastLogLine(input: {
|
|
96
|
+
readonly base: readonly string[];
|
|
97
|
+
readonly role: string;
|
|
98
|
+
readonly runner: Runner;
|
|
99
|
+
readonly options: ExecOptions;
|
|
100
|
+
}): Promise<string> {
|
|
101
|
+
const command = [
|
|
102
|
+
...input.base,
|
|
103
|
+
'logs',
|
|
104
|
+
'--no-color',
|
|
105
|
+
'--no-log-prefix',
|
|
106
|
+
'--tail',
|
|
107
|
+
'50',
|
|
108
|
+
input.role,
|
|
109
|
+
];
|
|
110
|
+
try {
|
|
111
|
+
const result = await input.runner(command, input.options);
|
|
112
|
+
return result.ok ? lastLine(result) : '';
|
|
113
|
+
} catch {
|
|
114
|
+
// The log is a courtesy on a failure already being reported; a runner that cannot spawn
|
|
115
|
+
// compose here must not replace that report with its own.
|
|
116
|
+
return '';
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/cmd-deploy-spec.ts
CHANGED
|
@@ -7,24 +7,76 @@ export const deploySpec: CommandSpec = {
|
|
|
7
7
|
name: 'deploy',
|
|
8
8
|
summary: 'run the container deploy plan: migrate first, then the serving roles',
|
|
9
9
|
usage:
|
|
10
|
-
'x deploy --image repo/app:tag [--method compose|helm] [--release name] [--namespace ns] [--timeout 15m] [--dry-run] [--json]',
|
|
10
|
+
'x deploy --image repo/app:tag [--method compose|helm] [--release name] [--namespace ns] [--timeout 15m] [--dry-run] [--json] | x deploy verify --url <origin> [--build <id>] [--window 60s] [--interval 5s] [--timeout 10m] [--idle-ok <queue,…>] [--json]',
|
|
11
11
|
requiresApp: true,
|
|
12
|
+
// `up` is the parser's name for the bare form, so `x deploy --image …` stays the deploy. The usage
|
|
13
|
+
// and every doc keep that one spelling.
|
|
14
|
+
subcommands: ['up', 'verify'],
|
|
15
|
+
defaultSubcommand: 'up',
|
|
12
16
|
flags: [
|
|
13
|
-
{ name: 'image', type: 'string', summary: 'image reference to deploy' },
|
|
14
|
-
{
|
|
15
|
-
|
|
17
|
+
{ name: 'image', type: 'string', summary: 'image reference to deploy', subcommands: ['up'] },
|
|
18
|
+
{
|
|
19
|
+
name: 'method',
|
|
20
|
+
type: 'string',
|
|
21
|
+
summary: 'compose | helm',
|
|
22
|
+
default: 'compose',
|
|
23
|
+
subcommands: ['up'],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'dry-run',
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
summary: 'print the plan, run nothing',
|
|
29
|
+
subcommands: ['up'],
|
|
30
|
+
},
|
|
16
31
|
{
|
|
17
32
|
name: 'release',
|
|
18
33
|
type: 'string',
|
|
19
34
|
summary: 'helm release name (default: app.config.ts name)',
|
|
35
|
+
subcommands: ['up'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'namespace',
|
|
39
|
+
type: 'string',
|
|
40
|
+
summary: 'helm namespace (default: the kube context)',
|
|
41
|
+
subcommands: ['up'],
|
|
20
42
|
},
|
|
21
|
-
{ name: 'namespace', type: 'string', summary: 'helm namespace (default: the kube context)' },
|
|
22
43
|
{
|
|
23
44
|
name: 'timeout',
|
|
24
45
|
type: 'string',
|
|
25
46
|
// No `default:` — the parser would then fill it on every compose deploy too, and the refusal
|
|
26
47
|
// of a helm-only flag there could not tell an operator's value from its own.
|
|
27
|
-
summary:
|
|
48
|
+
summary:
|
|
49
|
+
'how long helm waits for the migrate hook and the rollout (default 15m); verify: the whole wait (default 10m)',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'url',
|
|
53
|
+
type: 'string',
|
|
54
|
+
summary: 'the origin to sample: /readyz behind the load balancer',
|
|
55
|
+
subcommands: ['verify'],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'build',
|
|
59
|
+
type: 'string',
|
|
60
|
+
summary: "the build that must answer (default: x.manifest.json's buildId)",
|
|
61
|
+
subcommands: ['verify'],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'window',
|
|
65
|
+
type: 'string',
|
|
66
|
+
summary: 'how long consecutive good samples must span (default 60s); 0: answered once',
|
|
67
|
+
subcommands: ['verify'],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'interval',
|
|
71
|
+
type: 'string',
|
|
72
|
+
summary: 'the gap between samples (default 5s)',
|
|
73
|
+
subcommands: ['verify'],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'idle-ok',
|
|
77
|
+
type: 'string',
|
|
78
|
+
summary: 'queues a scale-to-zero pool may leave unserved while their depth is 0',
|
|
79
|
+
subcommands: ['verify'],
|
|
28
80
|
},
|
|
29
81
|
// `--critical` was here and is gone. It parsed, it was echoed into the plan JSON as
|
|
30
82
|
// `critical: <bool>`, and no file in `packages/` read that field — so the flag changed
|