@starci/hfs 4.2.17 → 4.4.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/CHANGELOG.md +28 -0
- package/README.md +7 -0
- package/db/migrate.mjs +361 -0
- package/db/post-cutover.mjs +136 -0
- package/emit/db-types.mjs +10 -1
- package/lint/run.mjs +1 -1
- package/package.json +4 -1
- package/release/gate.mjs +298 -0
- package/release/index.mjs +103 -0
- package/release/push.mjs +110 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
- package/runtime/knowledge/hfs/rules.yaml +3437 -160
- package/runtime/knowledge/hfs/slots.yaml +430 -92
- package/runtime/knowledge/patterns/be/api.yaml +92 -2
- package/runtime/knowledge/patterns/be/cli.yaml +37 -9
- package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
- package/runtime/knowledge/patterns/be/jobs.yaml +20 -2
- package/runtime/knowledge/patterns/be/projections.yaml +4 -2
- package/runtime/knowledge/patterns/be/queues.yaml +11 -3
- package/runtime/knowledge/patterns/be/reactors.yaml +20 -1
- package/runtime/knowledge/patterns/be/realtime.yaml +25 -3
- package/runtime/knowledge/patterns/be/saga.yaml +20 -3
- package/runtime/knowledge/patterns/be/webhooks.yaml +210 -36
- package/runtime/knowledge/patterns/fe/folder.yaml +4 -0
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/kernel/failure-codes.yaml +3128 -47
- package/runtime/modules/models/runtimes.yaml +1 -1
- package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
- package/runtime/packages/cli/src/catalog.generated.mjs +14104 -0
- package/runtime/packages/cli/src/validate-args.mjs +213 -0
- package/runtime/scripts/api/git/add.mjs +5 -0
- package/runtime/scripts/api/git/cat-file.mjs +5 -0
- package/runtime/scripts/api/git/init.mjs +7 -0
- package/runtime/scripts/api/git/lib.mjs +3 -0
- package/runtime/scripts/api/git/tag.mjs +5 -0
- package/runtime/scripts/api/node/lib.mjs +20 -0
- package/runtime/scripts/api/node/run-node.mjs +6 -0
- package/runtime/scripts/api/sops/lib.mjs +41 -0
- package/runtime/scripts/api/sops/resolve-sops.mjs +7 -0
- package/runtime/scripts/api/sops/seal.mjs +1 -1
- package/runtime/scripts/api/sops/selected-age-recipient.mjs +7 -0
- package/runtime/scripts/api/supabase/database-dump.mjs +5 -0
- package/runtime/scripts/api/supabase/database-query.mjs +5 -0
- package/runtime/scripts/api/supabase/lib.mjs +44 -0
- package/runtime/scripts/api/supabase/migration-repair.mjs +5 -0
- package/runtime/scripts/hfs/app-wiring.mjs +201 -0
- package/runtime/scripts/hfs/architecture/backend-app-files.mjs +103 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +22 -10
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +157 -41
- package/runtime/scripts/hfs/architecture/canon-clones.mjs +240 -0
- package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +143 -0
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
- package/runtime/scripts/hfs/architecture/clones.mjs +17 -8
- package/runtime/scripts/hfs/architecture/connection-map.mjs +14 -12
- package/runtime/scripts/hfs/architecture/context-map.mjs +25 -14
- package/runtime/scripts/hfs/architecture/context-owner.mjs +1 -2
- package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +23 -50
- package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
- package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +104 -0
- package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +30 -11
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
- package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +16 -3
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +17 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +49 -25
- package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
- package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
- package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
- package/runtime/scripts/hfs/architecture/register-once.mjs +15 -7
- package/runtime/scripts/hfs/architecture/required-files.mjs +2 -1
- package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
- package/runtime/scripts/hfs/architecture/staged-consumers.mjs +37 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +214 -17
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -6
- package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
- package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
- package/runtime/scripts/hfs/architecture/tiers.mjs +11 -4
- package/runtime/scripts/hfs/architecture/typescript.mjs +1 -8
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +9 -13
- package/runtime/scripts/hfs/check-results.mjs +28 -0
- package/runtime/scripts/hfs/check.mjs +63 -72
- package/runtime/scripts/hfs/coverage-from.mjs +70 -0
- package/runtime/scripts/hfs/db-baseline-verb.mjs +276 -0
- package/runtime/scripts/hfs/db-plan-verb.mjs +254 -0
- package/runtime/scripts/hfs/db-restamp-verb.mjs +60 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
- package/runtime/scripts/hfs/doc-commands.mjs +367 -0
- package/runtime/scripts/hfs/edition-slots.mjs +2 -2
- package/runtime/scripts/hfs/example-secrets.mjs +158 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +91 -6
- package/runtime/scripts/hfs/path-findings.mjs +32 -2
- package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
- package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
- package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
- package/runtime/scripts/hfs/rules/a15-findings.mjs +88 -0
- package/runtime/scripts/hfs/rules/app-env-required.mjs +20 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +2 -2
- package/runtime/scripts/hfs/rules/assets.mjs +79 -0
- package/runtime/scripts/hfs/rules/boot-proof.mjs +16 -0
- package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +59 -0
- package/runtime/scripts/hfs/rules/contract.mjs +66 -5
- package/runtime/scripts/hfs/rules/coverage-below.mjs +230 -0
- package/runtime/scripts/hfs/rules/database-access.mjs +343 -0
- package/runtime/scripts/hfs/rules/database-baseline.mjs +142 -0
- package/runtime/scripts/hfs/rules/database-config.mjs +250 -2
- package/runtime/scripts/hfs/rules/database-constants.mjs +9 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +317 -37
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +119 -9
- package/runtime/scripts/hfs/rules/database-queue-sources.mjs +176 -0
- package/runtime/scripts/hfs/rules/database-queues.mjs +361 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +91 -9
- package/runtime/scripts/hfs/rules/database.mjs +11 -2
- package/runtime/scripts/hfs/rules/deploy-scan.mjs +192 -0
- package/runtime/scripts/hfs/rules/deploy-workflow.mjs +219 -0
- package/runtime/scripts/hfs/rules/doc-fresh.mjs +286 -0
- package/runtime/scripts/hfs/rules/docker-health.mjs +249 -0
- package/runtime/scripts/hfs/rules/docker.mjs +24 -7
- package/runtime/scripts/hfs/rules/edition.mjs +11 -10
- package/runtime/scripts/hfs/rules/env-parity.mjs +373 -0
- package/runtime/scripts/hfs/rules/env-value-shape.mjs +113 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +3 -2
- package/runtime/scripts/hfs/rules/event-tables.mjs +100 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +115 -15
- package/runtime/scripts/hfs/rules/fe-stack.mjs +57 -0
- package/runtime/scripts/hfs/rules/feature-files.mjs +28 -0
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +54 -35
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -2
- package/runtime/scripts/hfs/rules/lexicon.mjs +62 -0
- package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +89 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +14 -1
- package/runtime/scripts/hfs/rules/pipeline.mjs +8 -1
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -0
- package/runtime/scripts/hfs/rules/release-provenance.mjs +211 -0
- package/runtime/scripts/hfs/rules/saga-delivery.mjs +66 -0
- package/runtime/scripts/hfs/rules/saga.mjs +8 -16
- package/runtime/scripts/hfs/rules/script-hygiene.mjs +151 -0
- package/runtime/scripts/hfs/rules/secret-custody-only.mjs +65 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +26 -0
- package/runtime/scripts/hfs/rules/services.mjs +4 -4
- package/runtime/scripts/hfs/rules/stack-tooling.mjs +93 -0
- package/runtime/scripts/hfs/rules/stacks-deploy.mjs +76 -0
- package/runtime/scripts/hfs/rules/stacks-ingress.mjs +87 -0
- package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -0
- package/runtime/scripts/hfs/rules/stacks-release.mjs +366 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +168 -0
- package/runtime/scripts/hfs/rules/stacks-swarm.mjs +417 -0
- package/runtime/scripts/hfs/rules/stacks.mjs +13 -8
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
- package/runtime/scripts/hfs/rules/supply-chain.mjs +167 -0
- package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +101 -0
- package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +77 -0
- package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +165 -0
- package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +103 -0
- package/runtime/scripts/hfs/rules/unused-symbol.mjs +320 -0
- package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +185 -0
- package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +19 -0
- package/runtime/scripts/hfs/sealed-members.mjs +101 -0
- package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +200 -0
- package/runtime/scripts/hfs/secret-document.mjs +97 -0
- package/runtime/scripts/hfs/secret-edit.mjs +170 -0
- package/runtime/scripts/hfs/secret-errors.mjs +20 -0
- package/runtime/scripts/hfs/secret-files.mjs +124 -0
- package/runtime/scripts/hfs/secret-init.mjs +300 -0
- package/runtime/scripts/hfs/secret-output.mjs +100 -0
- package/runtime/scripts/hfs/secret-sops.mjs +130 -0
- package/runtime/scripts/hfs/secret-store.mjs +163 -0
- package/runtime/scripts/hfs/secret.mjs +162 -193
- package/runtime/scripts/hfs/slot-imports.mjs +18 -3
- package/runtime/scripts/hfs/slot-required.mjs +13 -0
- package/runtime/scripts/hfs/slot-semantic-problems.mjs +2 -1
- package/runtime/scripts/hfs/slots.mjs +1 -0
- package/runtime/scripts/hfs/stack-config-kit.mjs +34 -0
- package/runtime/scripts/hfs/stack-deploy-config.mjs +313 -0
- package/runtime/scripts/hfs/stack-ingress-config.mjs +237 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +414 -0
- package/runtime/scripts/hfs/starcistacks-runtime.mjs +61 -0
- package/runtime/scripts/hfs/tree-findings.mjs +22 -0
- package/runtime/scripts/lib/command-tokens.mjs +126 -0
- package/runtime/scripts/lib/compose-values.mjs +11 -0
- package/runtime/scripts/lib/deployment-findings.mjs +7 -0
- package/runtime/scripts/lib/json-clone.mjs +61 -0
- package/runtime/scripts/lib/list.mjs +4 -0
- package/runtime/scripts/lib/roots.mjs +59 -0
- package/runtime/scripts/lib/runtime-example.mjs +13 -0
- package/runtime/scripts/lib/secret-patterns.mjs +60 -5
- package/runtime/scripts/lib/sql-ast.mjs +32 -0
- package/runtime/scripts/lib/stack-services.mjs +1 -1
- package/runtime/scripts/lib/swarm-stack.mjs +126 -0
- package/runtime/scripts/lib/trim-end.mjs +8 -0
- package/runtime/scripts/lib/ts-analysis.mjs +23 -0
- package/runtime/scripts/lib/ts-ast.mjs +27 -2
- package/runtime/scripts/lib/workflow-events.mjs +8 -0
- package/scaffold/add-app.mjs +77 -59
- package/scaffold/add-cli-lite.mjs +24 -17
- package/scaffold/add-compose.mjs +79 -0
- package/scaffold/add-database-lite.mjs +14 -0
- package/scaffold/add-fe-overlay.mjs +122 -0
- package/scaffold/add-fe-page.mjs +269 -0
- package/scaffold/add-inbox-lite.mjs +87 -124
- package/scaffold/add-plan.mjs +183 -0
- package/scaffold/add-table.mjs +132 -70
- package/scaffold/add.mjs +186 -204
- package/scaffold/app.mjs +128 -43
- package/scaffold/edition-gate.mjs +3 -3
- package/scaffold/image.mjs +2 -2
- package/scaffold/lite-exports.mjs +2 -1
- package/scaffold/service.mjs +2 -2
- package/src/main.mjs +238 -119
- package/stack/age-install.mjs +37 -0
- package/stack/backup.mjs +100 -0
- package/stack/build-args.mjs +42 -0
- package/stack/build.mjs +173 -0
- package/stack/cli.mjs +130 -0
- package/stack/config.mjs +57 -0
- package/stack/context.mjs +57 -0
- package/stack/converge.mjs +107 -0
- package/stack/deploy.mjs +301 -0
- package/stack/digest.mjs +16 -0
- package/stack/docker-transport.mjs +53 -0
- package/stack/edge-alias.mjs +118 -0
- package/stack/errors.mjs +21 -0
- package/stack/image-scan.mjs +152 -0
- package/stack/ingress-create.mjs +114 -0
- package/stack/ingress-publish.mjs +273 -0
- package/stack/ingress.mjs +706 -0
- package/stack/jobs.mjs +51 -0
- package/stack/live-restore.mjs +177 -0
- package/stack/prepare.mjs +266 -0
- package/stack/private-file.mjs +22 -0
- package/stack/provision.mjs +90 -0
- package/stack/prune.mjs +46 -0
- package/stack/release-store.mjs +87 -0
- package/stack/remote.mjs +205 -0
- package/stack/render.mjs +149 -0
- package/stack/retire.mjs +47 -0
- package/stack/sealed.mjs +149 -0
- package/stack/task.mjs +42 -0
- package/stack/verify.mjs +99 -0
- package/sync/ci-commands.mjs +13 -0
- package/sync/index.mjs +127 -64
- package/sync/stack-images.mjs +127 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +5 -1
- package/templates/app/ci-workflows/github/workflows/images.yml +15 -3
- package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +148 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +7 -3
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +38 -6
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +14 -2
- package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +148 -0
- package/templates/app/codegen-graphql.mjs.partial +13 -2
- package/templates/app/codegen-lite/scripts/codegen.mjs +6 -1
- package/templates/app/codegen-prelude.mjs.partial +8 -0
- package/templates/app/gitignore +4 -0
- package/templates/app/gitignore-lite +4 -0
- package/templates/app/hooks/husky/pre-commit +7 -12
- package/templates/app/package-scripts/package.json +1 -0
- package/templates/app/package-scripts/supabase.json +7 -0
- package/templates/app/package-scripts-lite/package.json +1 -7
- package/templates/app/skeleton/.editorconfig +1 -0
- package/templates/app/skeleton/.gitattributes +7 -1
- package/templates/app/skeleton/.github/dependabot.yml +17 -0
- package/templates/app/skeleton/.nvmrc +1 -1
- package/templates/app/skeleton/.sops.yaml +1 -1
- package/templates/app/skeleton/.starcistacks/application-stacks.yaml +134 -1
- package/templates/app/skeleton/.starcistacks/dev/README.md +16 -0
- package/templates/app/skeleton/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +71 -0
- package/templates/app/skeleton/.starcistacks/dev/runtime/env/KEYS.md +14 -0
- package/templates/app/skeleton/.starcistacks/production/README.md +37 -0
- package/templates/app/skeleton/.starcistacks/production/environment.json +5 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/Caddyfile +15 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +9 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +142 -0
- package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +14 -0
- package/templates/app/skeleton/README.md +6 -6
- package/templates/app/skeleton/scripts/codegen.mjs +1 -2
- package/templates/app/skeleton-lite/.editorconfig +16 -0
- package/templates/app/skeleton-lite/.gitattributes +8 -0
- package/templates/app/skeleton-lite/.github/dependabot.yml +17 -0
- package/templates/app/skeleton-lite/.nvmrc +1 -0
- package/templates/app/skeleton-lite/.sops.yaml +3 -2
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +193 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +20 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +2 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +16 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +8 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +120 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -0
- package/templates/app/skeleton-lite/.starcistacks/production/README.md +38 -0
- package/templates/app/skeleton-lite/.starcistacks/production/environment.json +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/Caddyfile +11 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/images.lock.yaml +8 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +121 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +21 -0
- package/templates/app/skeleton-lite/.starciwork/brand/index.yaml +12 -0
- package/templates/app/skeleton-lite/.starciwork/workspace.yaml +9 -0
- package/templates/app/skeleton-lite/README.md +8 -4
- package/templates/app/skeleton-lite/supabase/config.toml +8 -1
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +15 -6
- package/templates/app/starciwork.gitignore-lite +6 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +13 -7
- package/templates/be/image/worker/Dockerfile +1 -1
- package/templates/be/inbox/database-client.spec.ts.tpl +22 -28
- package/templates/be/inbox/database-client.ts.tpl +14 -28
- package/templates/be/inbox/delivery-recorded.event.ts.tpl +36 -0
- package/templates/be/inbox/event-index.ts.tpl +1 -0
- package/templates/be/inbox/events.json.tpl +10 -0
- package/templates/be/inbox/migration.sql.tpl +1 -0
- package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +42 -0
- package/templates/be/inbox/platform/event-bus/event-bus.decorators.ts.tpl +9 -0
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +6 -0
- package/templates/be/inbox/platform/event-bus/event-bus.module.ts.tpl +19 -0
- package/templates/be/inbox/platform/event-bus/event-bus.options.ts.tpl +2 -0
- package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +16 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +45 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +15 -0
- package/templates/be/inbox/platform/event-bus/index.ts.tpl +5 -0
- package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +5 -0
- package/templates/be/inbox/platform/inbox/inbox.decorators.ts.tpl +9 -0
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +6 -0
- package/templates/be/inbox/platform/inbox/inbox.module.ts.tpl +19 -0
- package/templates/be/inbox/platform/inbox/inbox.options.ts.tpl +2 -0
- package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +10 -0
- package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +41 -0
- package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +19 -0
- package/templates/be/inbox/platform/inbox/index.ts.tpl +3 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +7 -0
- package/templates/be/inbox/platform/migration.sql.tpl +48 -0
- package/templates/be/inbox/service.spec.ts.tpl +49 -20
- package/templates/be/inbox/service.ts.tpl +27 -16
- package/templates/be/inbox/sql.ts.tpl +2 -2
- package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -3
- package/templates/be/inbox/webhook-door.ts.tpl +4 -3
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +2 -1
- package/templates/be/patterns/api/feature.options.ts.tpl +2 -0
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +3 -2
- package/templates/be/patterns/api/graphql.options.ts.tpl +2 -0
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +2 -1
- package/templates/be/patterns/cli/group.options.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +0 -3
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +0 -2
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +2 -1
- package/templates/be/patterns/jobs/job.options.ts.tpl +2 -0
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +6 -3
- package/templates/be/patterns/jobs/job.queue.options.ts.tpl +2 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +3 -1
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +25 -9
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +2 -2
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +10 -0
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +2 -1
- package/templates/be/patterns/queues/queue.options.ts.tpl +2 -0
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +6 -3
- package/templates/be/patterns/reactors/message.options.ts.tpl +2 -0
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +2 -1
- package/templates/be/patterns/reactors/reactor.options.ts.tpl +2 -0
- package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +188 -0
- package/templates/be/patterns/realtime/channel.gateway.ts.tpl +22 -0
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +2 -1
- package/templates/be/patterns/realtime/channel.graphql.options.ts.tpl +2 -0
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +110 -0
- package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +6 -3
- package/templates/be/patterns/saga/saga.message.options.ts.tpl +2 -0
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +2 -1
- package/templates/be/patterns/saga/saga.options.ts.tpl +2 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +3 -3
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +3 -3
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +2 -1
- package/templates/be/patterns/webhooks/webhook.http.options.ts.tpl +2 -0
- package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +80 -26
- package/templates/be/platform/graphql/localize-errors.mapper.ts +2 -5
- package/templates/be/skeleton/apps/__app__/src/__app__.env.ts +21 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +1 -1
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -45
- package/templates/be/skeleton/apps/__app__/src/compose/api.ts +4 -0
- package/templates/be/skeleton/apps/__app__/src/compose/domain.ts +11 -0
- package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +44 -0
- package/templates/be/skeleton/apps/__app__/src/compose/system.ts +5 -0
- package/templates/be/skeleton/apps/__app__/src/compose/webhooks.ts +4 -0
- package/templates/be/skeleton/apps/__app__/src/main.ts +18 -10
- package/templates/be/skeleton/apps/cli/src/app.module.ts +3 -13
- package/templates/be/skeleton/apps/cli/src/cli.env.ts +13 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +7 -17
- package/templates/be/skeleton/apps/cli/src/compose/cli.ts +5 -0
- package/templates/be/skeleton/apps/cli/src/compose/platform.ts +24 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +3 -2
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.contracts.ts +2 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.handler.ts +22 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.query.ts +11 -0
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/api/system-health/system-health.module.ts +5 -3
- package/templates/be/skeleton/src/features/api/system-health/system-health.options.ts +2 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/ready.controller.ts +19 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module.ts +8 -3
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +3 -2
- package/templates/be/skeleton/src/features/cli/cli.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +2 -1
- package/templates/be/skeleton/src/features/cli/migrate/migrate.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +2 -1
- package/templates/be/skeleton/src/features/cli/seed/seed.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +5 -6
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +56 -4
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +36 -5
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +39 -8
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +14 -8
- package/templates/be/skeleton/src/modules/domain/identity/credential.contracts.ts +26 -0
- package/templates/be/skeleton/src/modules/domain/identity/credential.guard.spec.ts +119 -0
- package/templates/be/skeleton/src/modules/domain/identity/credential.guard.ts +43 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -1
- package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +22 -4
- package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +8 -2
- package/templates/be/skeleton/src/modules/domain/identity/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +6 -6
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +11 -4
- package/templates/be/skeleton/src/modules/platform/database/database-probe.service.spec.ts +32 -0
- package/templates/be/skeleton/src/modules/platform/database/database-probe.service.ts +14 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
- package/templates/be/skeleton/src/modules/platform/database/index.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +25 -6
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +16 -6
- package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.spec.ts +130 -0
- package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.ts +58 -0
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -4
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -10
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/http-security/index.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +12 -5
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +13 -8
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.contracts.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.decorators.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.spec.ts +86 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +139 -131
- package/templates/be/skeleton/src/modules/platform/i18n/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +91 -67
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +5 -4
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +0 -2
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +20 -6
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +16 -4
- package/templates/be/skeleton/src/modules/platform/retry/index.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.port.ts +11 -0
- package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.spec.ts +36 -0
- package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.ts +10 -0
- package/templates/be/skeleton/src/tests/contract/http-security/http-security.contract-spec.ts +54 -0
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +25 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +1 -1
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +18 -58
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +10 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +47 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/system.ts +5 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/webhooks.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +5 -13
- package/templates/be/skeleton-lite/apps/cli/src/cli.env.ts +17 -0
- package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +71 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +29 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +68 -15
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +14 -9
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +9 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +16 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +19 -4
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +5 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -3
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +17 -5
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +24 -13
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +5 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +5 -2
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +4 -1
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +7 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +29 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +16 -0
- package/templates/be/table/api/feature.module-definition.ts.tpl +6 -0
- package/templates/be/table/api/feature.module.ts.tpl +2 -1
- package/templates/be/table/api/feature.options.ts.tpl +2 -0
- package/templates/be/table/api/graphql.module-definition.ts.tpl +6 -0
- package/templates/be/table/api/graphql.module.ts.tpl +3 -2
- package/templates/be/table/api/graphql.options.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +1 -0
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +4 -6
- package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +7 -0
- package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +5 -0
- package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +9 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +3 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +3 -2
- package/templates/fe/auth/supabase-client.ts.tpl +34 -0
- package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignInForm/index.tsx +6 -1
- package/templates/fe/skeleton/apps/__app__/src/components/leaves/Icon/index.tsx +4 -0
- package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/component.tsx +31 -0
- package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/index.tsx +24 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +3 -4
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +15 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +18 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +7 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/en.json +57 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +6 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/en.json +31 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +1 -0
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +1 -34
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +7 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +57 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +30 -24
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +1 -1
- package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/locale.ts +2 -2
- package/upgrade/check.mjs +3 -1
- package/upgrade/file-state.mjs +68 -0
- package/upgrade/index.mjs +58 -94
- package/runtime/scripts/api/git/log.mjs +0 -5
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +0 -55
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// The Swarm deploy images an app declares, as the managed stack-deploy.yml workflow needs them.
|
|
2
|
+
//
|
|
3
|
+
// The workflow builds one image per `deploy.images` entry of .starcistacks/application-stacks.yaml and hands the pinned references to
|
|
4
|
+
// `starci app stack deploy`. Sync renders that list into the workflow, so it reads the declaration here: the union over the declared
|
|
5
|
+
// environments of `environments.<env>.deploy.images` (service -> { app, buildArgs }). Only names and the presence of build arguments are
|
|
6
|
+
// read; the real validation of the block is stack-deploy-config.mjs (`starci app check`, rule R294). Everything this module returns is
|
|
7
|
+
// pure text assembled from names that match the strict patterns below, so no free text reaches the rendered workflow.
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { parseYaml as bundledParseYaml } from '../runtime/engine/yaml.mjs';
|
|
11
|
+
import { DECLARATION } from './sonar-key.mjs';
|
|
12
|
+
import { byCodeUnit } from '../report/order.mjs';
|
|
13
|
+
|
|
14
|
+
const NAME = /^[a-z][a-z0-9-]*$/u;
|
|
15
|
+
const KEY = /^[A-Z][A-Z0-9_]*$/u;
|
|
16
|
+
const INDENT = ' '.repeat(6);
|
|
17
|
+
const CONTINUE = '\\';
|
|
18
|
+
const NO_IMAGES_MESSAGE = 'no image is declared: declare environments.<env>.deploy (with images) in .starcistacks/application-stacks.yaml before running this workflow';
|
|
19
|
+
|
|
20
|
+
const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
21
|
+
const hasBuildArgs = args => record(args) && (Object.keys(record(args.values) ? args.values : {}).length > 0 || record(args.sealed));
|
|
22
|
+
|
|
23
|
+
function environmentImages(environment) {
|
|
24
|
+
const images = environment?.deploy?.images;
|
|
25
|
+
if (!record(images)) return [];
|
|
26
|
+
return Object.entries(images)
|
|
27
|
+
.filter(([service, entry]) => NAME.test(service) && record(entry) && NAME.test(String(entry.app)))
|
|
28
|
+
.map(([service, entry]) => ({ service, app: entry.app, buildArgs: hasBuildArgs(entry.buildArgs) }));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The images of a parsed declaration, [{ service, app, buildArgs }] sorted by service: the union over its environments (the first
|
|
33
|
+
* environment that names a service decides its app; a later one only adds build arguments). A declaration with no deploy block has none.
|
|
34
|
+
*/
|
|
35
|
+
export function declaredStackImages(declaration) {
|
|
36
|
+
const byService = new Map();
|
|
37
|
+
for (const environment of Object.values(record(declaration?.environments) ? declaration.environments : {})) {
|
|
38
|
+
for (const image of environmentImages(environment)) {
|
|
39
|
+
const known = byService.get(image.service);
|
|
40
|
+
byService.set(image.service, known ? { ...known, buildArgs: known.buildArgs || image.buildArgs } : image);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return [...byService.values()].sort((a, b) => byCodeUnit(a.service, b.service));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The host variables (names only) the declared environments interpolate into `docker stack config`: the union of `deploy.passEnv`. */
|
|
47
|
+
export function declaredPassEnv(declaration) {
|
|
48
|
+
const names = Object.values(record(declaration?.environments) ? declaration.environments : {})
|
|
49
|
+
.flatMap(environment => (Array.isArray(environment?.deploy?.passEnv) ? environment.deploy.passEnv : []))
|
|
50
|
+
.filter(name => typeof name === 'string' && KEY.test(name));
|
|
51
|
+
return [...new Set(names)].sort(byCodeUnit);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* What the managed workflow renders from the declaration of the app at `root`: { images, passEnv } (both empty when the app has no
|
|
56
|
+
* declaration, no deploy block or a declaration that does not parse; `starci app check` judges those). `parseYaml(text)` is injected.
|
|
57
|
+
*/
|
|
58
|
+
export function readDeclaredStack(root, { parseYaml = bundledParseYaml } = {}) {
|
|
59
|
+
const file = path.join(root, DECLARATION);
|
|
60
|
+
if (!fs.existsSync(file)) return { images: [], passEnv: [] };
|
|
61
|
+
let declaration;
|
|
62
|
+
try {
|
|
63
|
+
declaration = parseYaml(fs.readFileSync(file, 'utf8'));
|
|
64
|
+
} catch {
|
|
65
|
+
return { images: [], passEnv: [] };
|
|
66
|
+
}
|
|
67
|
+
return { images: declaredStackImages(declaration), passEnv: declaredPassEnv(declaration) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const envName = service => `IMAGE_${service.toUpperCase().replaceAll('-', '_')}`;
|
|
71
|
+
/** Lines placed at the end of a template line: each one starts with a newline, so nothing declared leaves nothing, not even a blank line. */
|
|
72
|
+
const block = (items, indent = INDENT) => items.map(item => `\n${indent}${item}`).join('');
|
|
73
|
+
|
|
74
|
+
function buildArgsStep(image) {
|
|
75
|
+
return [
|
|
76
|
+
`- name: public build values of ${image.service}`,
|
|
77
|
+
` id: args-${image.service}`,
|
|
78
|
+
' env:',
|
|
79
|
+
' SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}',
|
|
80
|
+
` run: npx --no-install starci app stack build-args --env "$STACK_ENVIRONMENT" --app ${image.app}`,
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildPushStep(image, dockerfile) {
|
|
85
|
+
return [
|
|
86
|
+
`- name: build and push ${image.service}`,
|
|
87
|
+
` id: build-${image.service}`,
|
|
88
|
+
' uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2',
|
|
89
|
+
' with:',
|
|
90
|
+
' context: .',
|
|
91
|
+
` file: ${dockerfile}`,
|
|
92
|
+
' push: true',
|
|
93
|
+
' provenance: false',
|
|
94
|
+
` tags: \${{ steps.names.outputs.${image.service} }}:\${{ github.sha }}`,
|
|
95
|
+
...(image.buildArgs ? [` build-args: \${{ steps.args-${image.service}.outputs.args }}`] : []),
|
|
96
|
+
` cache-from: type=gha,scope=${image.service}`,
|
|
97
|
+
` cache-to: type=gha,scope=${image.service},mode=max`,
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The text blocks the stack-deploy.yml template places at the end of a line (each already indented for its position, every line led by
|
|
103
|
+
* a newline; empty when there is nothing to place):
|
|
104
|
+
* stackGuard the first step of the release job when the app declares no image (it fails with the reason)
|
|
105
|
+
* stackNames the `names` step lines assigning each image repository (ghcr.io/<lowercase owner>/<project>-<app>)
|
|
106
|
+
* stackImageSteps per image: the optional build-args step and the build-and-push step
|
|
107
|
+
* stackImageEnv the env entries of the deploy step: IMAGE_<SERVICE> = repository@digest
|
|
108
|
+
* stackImageArgs the `--image "<service>=$IMAGE_<SERVICE>" \` continuation lines of the deploy command
|
|
109
|
+
* stackPassEnv the env entries of the deploy step for the declared host variables (vars.<NAME>)
|
|
110
|
+
* `dockerfileOf(app)` names the Dockerfile of an app (the sync module knows the layout).
|
|
111
|
+
*/
|
|
112
|
+
export function stackWorkflowVariables({ images = [], passEnv = [] }, project, dockerfileOf) {
|
|
113
|
+
const guard = images.length ? '' : block([
|
|
114
|
+
'- name: require a declared image',
|
|
115
|
+
' shell: bash',
|
|
116
|
+
` run: 'echo "::error::${NO_IMAGES_MESSAGE}"; exit 1'`,
|
|
117
|
+
]);
|
|
118
|
+
const names = images.map(image => `echo "${image.service}=ghcr.io/\${owner}/${project}-${image.app}" >> "$GITHUB_OUTPUT"`);
|
|
119
|
+
return {
|
|
120
|
+
stackGuard: guard,
|
|
121
|
+
stackNames: block(names, ' '.repeat(10)),
|
|
122
|
+
stackImageSteps: images.map(image => block([...(image.buildArgs ? buildArgsStep(image) : []), ...buildPushStep(image, dockerfileOf(image.app))])).join(''),
|
|
123
|
+
stackImageEnv: block(images.map(image => `${envName(image.service)}: \${{ steps.names.outputs.${image.service} }}@\${{ steps.build-${image.service}.outputs.digest }}`), ' '.repeat(10)),
|
|
124
|
+
stackImageArgs: block(images.map(image => `--image "${image.service}=$${envName(image.service)}" ${CONTINUE}`), ' '.repeat(12)),
|
|
125
|
+
stackPassEnv: block(passEnv.map(name => `${name}: \${{ vars.${name} }}`), ' '.repeat(10)),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -30,14 +30,18 @@ jobs:
|
|
|
30
30
|
node-version: {{nodeMajor}}
|
|
31
31
|
cache: npm
|
|
32
32
|
- run: npm ci --ignore-scripts
|
|
33
|
+
- name: production dependency audit
|
|
34
|
+
run: npm audit --omit=dev --audit-level=high
|
|
33
35
|
- name: lint
|
|
34
|
-
run:
|
|
36
|
+
run: {{ciLintSonar}}
|
|
35
37
|
- name: format
|
|
36
38
|
run: npm run format:check
|
|
37
39
|
- name: typecheck
|
|
38
40
|
run: npm run typecheck
|
|
39
41
|
- name: unit
|
|
40
42
|
run: npm test -- --ci
|
|
43
|
+
- name: coverage proof
|
|
44
|
+
run: {{ciCoverageProof}}
|
|
41
45
|
- name: coverage upload
|
|
42
46
|
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5
|
|
43
47
|
if: ${{ !cancelled() && startsWith(github.ref, 'refs/tags/v') }}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{{header}}
|
|
2
|
-
# Builds the image of every app on
|
|
2
|
+
# Builds the image of every app on its release tag, so a Dockerfile that no longer builds fails that release.
|
|
3
3
|
# It never pushes: publishing an image is the product's decision, not this file's.
|
|
4
4
|
name: images
|
|
5
5
|
|
|
6
6
|
on:
|
|
7
7
|
push:
|
|
8
8
|
tags: ["v*"]
|
|
9
|
-
workflow_dispatch:
|
|
10
9
|
|
|
11
10
|
permissions:
|
|
12
11
|
contents: read
|
|
@@ -26,12 +25,25 @@ jobs:
|
|
|
26
25
|
{{imageMatrix}}
|
|
27
26
|
steps:
|
|
28
27
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
28
|
+
- name: release tag matches package version
|
|
29
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
29
30
|
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
|
30
31
|
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
|
31
32
|
with:
|
|
32
33
|
context: .
|
|
33
34
|
file: ${{ matrix.file }}
|
|
34
35
|
push: false
|
|
35
|
-
tags: {{imageProject}}/${{ matrix.name }}
|
|
36
|
+
tags: {{imageProject}}/${{ matrix.name }}:${{ github.ref_name }}
|
|
37
|
+
# OCI keeps the SBOM and provenance attestations without publishing to a registry.
|
|
38
|
+
outputs: type=oci,dest=${{ runner.temp }}/${{ matrix.name }}.oci.tar
|
|
39
|
+
sbom: true
|
|
40
|
+
provenance: mode=max
|
|
36
41
|
cache-from: type=gha,scope=${{ matrix.name }}
|
|
37
42
|
cache-to: type=gha,scope=${{ matrix.name }},mode=max
|
|
43
|
+
- name: retain image with SBOM and provenance
|
|
44
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
45
|
+
with:
|
|
46
|
+
name: image-${{ matrix.name }}-${{ github.sha }}-${{ github.run_attempt }}
|
|
47
|
+
path: ${{ runner.temp }}/${{ matrix.name }}.oci.tar
|
|
48
|
+
if-no-files-found: error
|
|
49
|
+
compression-level: 0
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{{header}}
|
|
2
|
+
# Deploys the app's Docker Swarm stack. This file only assembles arguments: the logic lives in the verbs connect, build-args, deploy,
|
|
3
|
+
# rollback and disconnect of "starci app stack". It runs by hand only (Actions > deploy stack > Run workflow) from a release tag v*
|
|
4
|
+
# whose version is the package.json version; it has no push or schedule trigger.
|
|
5
|
+
# operation deploy builds and pushes one image per environments.<env>.deploy.images entry of .starcistacks/application-stacks.yaml,
|
|
6
|
+
# then deploys their immutable digests (render, provision, converge, verify, accept; a failure redeploys the previous release)
|
|
7
|
+
# operation rollback redeploys a stored release; "release" is 16 lowercase hex characters, empty means the previous accepted release
|
|
8
|
+
# The environment input names a protected GitHub environment (required reviewers) that holds:
|
|
9
|
+
# vars VPS_HOST, VPS_USER (the swarm manager); a variable named in deploy.passEnv, when the stack interpolates one
|
|
10
|
+
# secrets SOPS_AGE_KEY (the age identity of .sops.yaml), VPS_SSH_KEY (CI-only deploy key), VPS_KNOWN_HOSTS (pinned host keys)
|
|
11
|
+
# Nothing else is a secret: images are pushed with the workflow token. Dispatch inputs reach scripts only through env.
|
|
12
|
+
name: deploy stack
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
inputs:
|
|
17
|
+
environment:
|
|
18
|
+
description: Protected environment holding the VPS variables and the sealed custody under .starcistacks
|
|
19
|
+
required: true
|
|
20
|
+
type: environment
|
|
21
|
+
operation:
|
|
22
|
+
description: Deploy a new release or roll back to a stored one
|
|
23
|
+
required: true
|
|
24
|
+
type: choice
|
|
25
|
+
default: deploy
|
|
26
|
+
options:
|
|
27
|
+
- deploy
|
|
28
|
+
- rollback
|
|
29
|
+
release:
|
|
30
|
+
description: "Rollback only: release id (16 lowercase hex) to deploy again; empty means the previous accepted release"
|
|
31
|
+
required: false
|
|
32
|
+
type: string
|
|
33
|
+
default: ""
|
|
34
|
+
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
|
|
38
|
+
concurrency:
|
|
39
|
+
group: stack-${{ inputs.environment }}
|
|
40
|
+
cancel-in-progress: false
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
release:
|
|
44
|
+
if: ${{ inputs.operation == 'deploy' }}
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment: ${{ inputs.environment }}
|
|
47
|
+
timeout-minutes: 60
|
|
48
|
+
permissions:
|
|
49
|
+
contents: read
|
|
50
|
+
packages: write
|
|
51
|
+
env:
|
|
52
|
+
STACK_ENVIRONMENT: ${{ inputs.environment }}
|
|
53
|
+
XDG_DATA_HOME: ${{ runner.temp }}/starci-data
|
|
54
|
+
steps:{{stackGuard}}
|
|
55
|
+
- name: require release tag
|
|
56
|
+
shell: bash
|
|
57
|
+
run: '[[ "$GITHUB_REF" == refs/tags/v* ]] || exit 1'
|
|
58
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
59
|
+
- name: release tag matches package version
|
|
60
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
61
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
62
|
+
with:
|
|
63
|
+
node-version: {{nodeMajor}}
|
|
64
|
+
cache: npm
|
|
65
|
+
- run: npm ci --ignore-scripts
|
|
66
|
+
- name: install SOPS
|
|
67
|
+
uses: mdgreenwald/mozilla-sops-action@bb2d96a13ef438ad122e5827a57ebd75d6ff118d # v2.1.0
|
|
68
|
+
with:
|
|
69
|
+
version: v3.13.2
|
|
70
|
+
- name: image repositories
|
|
71
|
+
id: names
|
|
72
|
+
shell: bash
|
|
73
|
+
run: |
|
|
74
|
+
owner="${GITHUB_REPOSITORY_OWNER,,}"{{stackNames}}
|
|
75
|
+
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
|
76
|
+
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
|
77
|
+
with:
|
|
78
|
+
registry: ghcr.io
|
|
79
|
+
username: ${{ github.actor }}
|
|
80
|
+
password: ${{ github.token }}{{stackImageSteps}}
|
|
81
|
+
- name: connect to the swarm manager
|
|
82
|
+
env:
|
|
83
|
+
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
84
|
+
VPS_KNOWN_HOSTS: ${{ secrets.VPS_KNOWN_HOSTS }}
|
|
85
|
+
VPS_HOST: ${{ vars.VPS_HOST }}
|
|
86
|
+
VPS_USER: ${{ vars.VPS_USER }}
|
|
87
|
+
run: npx --no-install starci app stack connect --env "$STACK_ENVIRONMENT" --execute
|
|
88
|
+
- name: deploy the release
|
|
89
|
+
env:
|
|
90
|
+
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}{{stackImageEnv}}{{stackPassEnv}}
|
|
91
|
+
run: |
|
|
92
|
+
npx --no-install starci app stack deploy --env "$STACK_ENVIRONMENT" \{{stackImageArgs}}
|
|
93
|
+
--source-sha "$GITHUB_SHA" --execute
|
|
94
|
+
- name: disconnect from the swarm manager
|
|
95
|
+
if: ${{ always() }}
|
|
96
|
+
run: npx --no-install starci app stack disconnect --env "$STACK_ENVIRONMENT" --execute
|
|
97
|
+
|
|
98
|
+
rollback:
|
|
99
|
+
if: ${{ inputs.operation == 'rollback' }}
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment: ${{ inputs.environment }}
|
|
102
|
+
timeout-minutes: 30
|
|
103
|
+
permissions:
|
|
104
|
+
contents: read
|
|
105
|
+
packages: read
|
|
106
|
+
env:
|
|
107
|
+
STACK_ENVIRONMENT: ${{ inputs.environment }}
|
|
108
|
+
XDG_DATA_HOME: ${{ runner.temp }}/starci-data
|
|
109
|
+
steps:
|
|
110
|
+
- name: require release tag
|
|
111
|
+
shell: bash
|
|
112
|
+
run: '[[ "$GITHUB_REF" == refs/tags/v* ]] || exit 1'
|
|
113
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
114
|
+
- name: release tag matches package version
|
|
115
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
116
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
117
|
+
with:
|
|
118
|
+
node-version: {{nodeMajor}}
|
|
119
|
+
cache: npm
|
|
120
|
+
- run: npm ci --ignore-scripts
|
|
121
|
+
- name: install SOPS
|
|
122
|
+
uses: mdgreenwald/mozilla-sops-action@bb2d96a13ef438ad122e5827a57ebd75d6ff118d # v2.1.0
|
|
123
|
+
with:
|
|
124
|
+
version: v3.13.2
|
|
125
|
+
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
|
126
|
+
with:
|
|
127
|
+
registry: ghcr.io
|
|
128
|
+
username: ${{ github.actor }}
|
|
129
|
+
password: ${{ github.token }}
|
|
130
|
+
- name: connect to the swarm manager
|
|
131
|
+
env:
|
|
132
|
+
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
133
|
+
VPS_KNOWN_HOSTS: ${{ secrets.VPS_KNOWN_HOSTS }}
|
|
134
|
+
VPS_HOST: ${{ vars.VPS_HOST }}
|
|
135
|
+
VPS_USER: ${{ vars.VPS_USER }}
|
|
136
|
+
run: npx --no-install starci app stack connect --env "$STACK_ENVIRONMENT" --execute
|
|
137
|
+
- name: redeploy the stored release
|
|
138
|
+
env:
|
|
139
|
+
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
|
|
140
|
+
RELEASE: ${{ inputs.release }}
|
|
141
|
+
shell: bash
|
|
142
|
+
run: |
|
|
143
|
+
release=()
|
|
144
|
+
[ -z "$RELEASE" ] || release=(--release "$RELEASE")
|
|
145
|
+
npx --no-install starci app stack rollback --env "$STACK_ENVIRONMENT" "${release[@]}" --execute
|
|
146
|
+
- name: disconnect from the swarm manager
|
|
147
|
+
if: ${{ always() }}
|
|
148
|
+
run: npx --no-install starci app stack disconnect --env "$STACK_ENVIRONMENT" --execute
|
|
@@ -30,18 +30,22 @@ jobs:
|
|
|
30
30
|
node-version: {{nodeMajor}}
|
|
31
31
|
cache: npm
|
|
32
32
|
- run: npm ci --ignore-scripts
|
|
33
|
+
- name: production dependency audit
|
|
34
|
+
run: npm audit --omit=dev --audit-level=high
|
|
33
35
|
- name: lint
|
|
34
36
|
if: env.SONAR_TOKEN == ''
|
|
35
|
-
run:
|
|
37
|
+
run: {{ciLint}}
|
|
36
38
|
- name: lint with Sonar import
|
|
37
39
|
if: env.SONAR_TOKEN != ''
|
|
38
|
-
run:
|
|
40
|
+
run: {{ciLintSonar}}
|
|
39
41
|
- name: format
|
|
40
42
|
run: npm run format:check
|
|
41
43
|
- name: typecheck
|
|
42
44
|
run: npm run typecheck
|
|
43
45
|
- name: unit
|
|
44
46
|
run: npm test -- --ci
|
|
47
|
+
- name: coverage proof
|
|
48
|
+
run: {{ciCoverageProof}}
|
|
45
49
|
- name: coverage upload
|
|
46
50
|
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5
|
|
47
51
|
if: ${{ !cancelled() && startsWith(github.ref, 'refs/tags/v') }}
|
|
@@ -55,7 +59,7 @@ jobs:
|
|
|
55
59
|
- name: build fe
|
|
56
60
|
run: npm run build:fe
|
|
57
61
|
- name: database migration checks
|
|
58
|
-
run:
|
|
62
|
+
run: {{ciDatabaseLint}}
|
|
59
63
|
{{> app/ci-workflows/sonar-steps.yml}}
|
|
60
64
|
|
|
61
65
|
db-types:
|
|
@@ -5,27 +5,59 @@ on:
|
|
|
5
5
|
workflow_dispatch:
|
|
6
6
|
inputs:
|
|
7
7
|
environment:
|
|
8
|
-
description:
|
|
8
|
+
description: Protected environment with sealed Supabase custody under .starcistacks
|
|
9
9
|
required: true
|
|
10
10
|
type: environment
|
|
11
|
+
backup_receipt:
|
|
12
|
+
description: Tracked backup receipt path verified by the deployment owner before migration
|
|
13
|
+
required: true
|
|
14
|
+
type: string
|
|
11
15
|
|
|
12
16
|
permissions:
|
|
13
17
|
contents: read
|
|
14
18
|
|
|
19
|
+
concurrency:
|
|
20
|
+
group: database-${{ inputs.environment }}
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
15
23
|
jobs:
|
|
16
24
|
deploy:
|
|
17
25
|
runs-on: ubuntu-latest
|
|
18
26
|
environment: ${{ inputs.environment }}
|
|
19
27
|
env:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
STACK_ENVIRONMENT: ${{ inputs.environment }}
|
|
29
|
+
BACKUP_RECEIPT: ${{ inputs.backup_receipt }}
|
|
30
|
+
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
|
|
31
|
+
XDG_DATA_HOME: ${{ runner.temp }}/starci-data
|
|
23
32
|
steps:
|
|
33
|
+
- name: require release tag
|
|
34
|
+
shell: bash
|
|
35
|
+
run: '[[ "$GITHUB_REF" == refs/tags/v* ]] || exit 1'
|
|
24
36
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
37
|
+
- name: release tag matches package version
|
|
38
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
25
39
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
26
40
|
with:
|
|
27
41
|
node-version: {{nodeMajor}}
|
|
28
42
|
cache: npm
|
|
29
43
|
- run: npm ci --ignore-scripts
|
|
30
|
-
- name:
|
|
31
|
-
|
|
44
|
+
- name: install SOPS
|
|
45
|
+
uses: mdgreenwald/mozilla-sops-action@bb2d96a13ef438ad122e5827a57ebd75d6ff118d # v2.1.0
|
|
46
|
+
with:
|
|
47
|
+
version: v3.13.2
|
|
48
|
+
- name: materialize sealed custody and push migrations
|
|
49
|
+
shell: bash
|
|
50
|
+
run: |
|
|
51
|
+
set -euo pipefail
|
|
52
|
+
[[ "$STACK_ENVIRONMENT" =~ ^[a-z][a-z0-9-]*$ ]]
|
|
53
|
+
git ls-files --error-unmatch -- "$BACKUP_RECEIPT" > /dev/null
|
|
54
|
+
test -s "$BACKUP_RECEIPT"
|
|
55
|
+
secret_dir="$XDG_DATA_HOME/StarCi/secrets/{{project}}/$STACK_ENVIRONMENT"
|
|
56
|
+
trap 'rm -rf -- "$secret_dir"' EXIT
|
|
57
|
+
npx --no-install starci app secret materialize "$STACK_ENVIRONMENT"
|
|
58
|
+
export SUPABASE_ACCESS_TOKEN="$(cat "$secret_dir/SUPABASE_ACCESS_TOKEN")"
|
|
59
|
+
export SUPABASE_DB_PASSWORD="$(cat "$secret_dir/SUPABASE_DB_PASSWORD")"
|
|
60
|
+
project_id="$(cat "$secret_dir/SUPABASE_PROJECT_ID")"
|
|
61
|
+
npx --no-install supabase link --project-ref "$project_id"
|
|
62
|
+
npm run db:push -- --dry-run
|
|
63
|
+
npm run db:push
|
|
@@ -6,7 +6,6 @@ name: images
|
|
|
6
6
|
on:
|
|
7
7
|
push:
|
|
8
8
|
tags: ["v*"]
|
|
9
|
-
workflow_dispatch:
|
|
10
9
|
|
|
11
10
|
permissions:
|
|
12
11
|
contents: read
|
|
@@ -26,12 +25,25 @@ jobs:
|
|
|
26
25
|
{{imageMatrix}}
|
|
27
26
|
steps:
|
|
28
27
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
28
|
+
- name: release tag matches package version
|
|
29
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
29
30
|
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
|
30
31
|
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
|
31
32
|
with:
|
|
32
33
|
context: .
|
|
33
34
|
file: ${{ matrix.file }}
|
|
34
35
|
push: false
|
|
35
|
-
tags: {{imageProject}}/${{ matrix.name }}
|
|
36
|
+
tags: {{imageProject}}/${{ matrix.name }}:${{ github.ref_name }}
|
|
37
|
+
# OCI keeps the SBOM and provenance attestations without publishing to a registry.
|
|
38
|
+
outputs: type=oci,dest=${{ runner.temp }}/${{ matrix.name }}.oci.tar
|
|
39
|
+
sbom: true
|
|
40
|
+
provenance: mode=max
|
|
36
41
|
cache-from: type=gha,scope=${{ matrix.name }}
|
|
37
42
|
cache-to: type=gha,scope=${{ matrix.name }},mode=max
|
|
43
|
+
- name: retain image with SBOM and provenance
|
|
44
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
45
|
+
with:
|
|
46
|
+
name: image-${{ matrix.name }}-${{ github.sha }}-${{ github.run_attempt }}
|
|
47
|
+
path: ${{ runner.temp }}/${{ matrix.name }}.oci.tar
|
|
48
|
+
if-no-files-found: error
|
|
49
|
+
compression-level: 0
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{{header}}
|
|
2
|
+
# Deploys the app's Docker Swarm stack. This file only assembles arguments: the logic lives in the verbs connect, build-args, deploy,
|
|
3
|
+
# rollback and disconnect of "starci app stack". It runs by hand only (Actions > deploy stack > Run workflow) from a release tag v*
|
|
4
|
+
# whose version is the package.json version; it has no push or schedule trigger.
|
|
5
|
+
# operation deploy builds and pushes one image per environments.<env>.deploy.images entry of .starcistacks/application-stacks.yaml,
|
|
6
|
+
# then deploys their immutable digests (render, provision, converge, verify, accept; a failure redeploys the previous release)
|
|
7
|
+
# operation rollback redeploys a stored release; "release" is 16 lowercase hex characters, empty means the previous accepted release
|
|
8
|
+
# The environment input names a protected GitHub environment (required reviewers) that holds:
|
|
9
|
+
# vars VPS_HOST, VPS_USER (the swarm manager); a variable named in deploy.passEnv, when the stack interpolates one
|
|
10
|
+
# secrets SOPS_AGE_KEY (the age identity of .sops.yaml), VPS_SSH_KEY (CI-only deploy key), VPS_KNOWN_HOSTS (pinned host keys)
|
|
11
|
+
# Nothing else is a secret: images are pushed with the workflow token. Dispatch inputs reach scripts only through env.
|
|
12
|
+
name: deploy stack
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
inputs:
|
|
17
|
+
environment:
|
|
18
|
+
description: Protected environment holding the VPS variables and the sealed custody under .starcistacks
|
|
19
|
+
required: true
|
|
20
|
+
type: environment
|
|
21
|
+
operation:
|
|
22
|
+
description: Deploy a new release or roll back to a stored one
|
|
23
|
+
required: true
|
|
24
|
+
type: choice
|
|
25
|
+
default: deploy
|
|
26
|
+
options:
|
|
27
|
+
- deploy
|
|
28
|
+
- rollback
|
|
29
|
+
release:
|
|
30
|
+
description: "Rollback only: release id (16 lowercase hex) to deploy again; empty means the previous accepted release"
|
|
31
|
+
required: false
|
|
32
|
+
type: string
|
|
33
|
+
default: ""
|
|
34
|
+
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
|
|
38
|
+
concurrency:
|
|
39
|
+
group: stack-${{ inputs.environment }}
|
|
40
|
+
cancel-in-progress: false
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
release:
|
|
44
|
+
if: ${{ inputs.operation == 'deploy' }}
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment: ${{ inputs.environment }}
|
|
47
|
+
timeout-minutes: 60
|
|
48
|
+
permissions:
|
|
49
|
+
contents: read
|
|
50
|
+
packages: write
|
|
51
|
+
env:
|
|
52
|
+
STACK_ENVIRONMENT: ${{ inputs.environment }}
|
|
53
|
+
XDG_DATA_HOME: ${{ runner.temp }}/starci-data
|
|
54
|
+
steps:{{stackGuard}}
|
|
55
|
+
- name: require release tag
|
|
56
|
+
shell: bash
|
|
57
|
+
run: '[[ "$GITHUB_REF" == refs/tags/v* ]] || exit 1'
|
|
58
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
59
|
+
- name: release tag matches package version
|
|
60
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
61
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
62
|
+
with:
|
|
63
|
+
node-version: {{nodeMajor}}
|
|
64
|
+
cache: npm
|
|
65
|
+
- run: npm ci --ignore-scripts
|
|
66
|
+
- name: install SOPS
|
|
67
|
+
uses: mdgreenwald/mozilla-sops-action@bb2d96a13ef438ad122e5827a57ebd75d6ff118d # v2.1.0
|
|
68
|
+
with:
|
|
69
|
+
version: v3.13.2
|
|
70
|
+
- name: image repositories
|
|
71
|
+
id: names
|
|
72
|
+
shell: bash
|
|
73
|
+
run: |
|
|
74
|
+
owner="${GITHUB_REPOSITORY_OWNER,,}"{{stackNames}}
|
|
75
|
+
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
|
76
|
+
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
|
77
|
+
with:
|
|
78
|
+
registry: ghcr.io
|
|
79
|
+
username: ${{ github.actor }}
|
|
80
|
+
password: ${{ github.token }}{{stackImageSteps}}
|
|
81
|
+
- name: connect to the swarm manager
|
|
82
|
+
env:
|
|
83
|
+
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
84
|
+
VPS_KNOWN_HOSTS: ${{ secrets.VPS_KNOWN_HOSTS }}
|
|
85
|
+
VPS_HOST: ${{ vars.VPS_HOST }}
|
|
86
|
+
VPS_USER: ${{ vars.VPS_USER }}
|
|
87
|
+
run: npx --no-install starci app stack connect --env "$STACK_ENVIRONMENT" --execute
|
|
88
|
+
- name: deploy the release
|
|
89
|
+
env:
|
|
90
|
+
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}{{stackImageEnv}}{{stackPassEnv}}
|
|
91
|
+
run: |
|
|
92
|
+
npx --no-install starci app stack deploy --env "$STACK_ENVIRONMENT" \{{stackImageArgs}}
|
|
93
|
+
--source-sha "$GITHUB_SHA" --execute
|
|
94
|
+
- name: disconnect from the swarm manager
|
|
95
|
+
if: ${{ always() }}
|
|
96
|
+
run: npx --no-install starci app stack disconnect --env "$STACK_ENVIRONMENT" --execute
|
|
97
|
+
|
|
98
|
+
rollback:
|
|
99
|
+
if: ${{ inputs.operation == 'rollback' }}
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment: ${{ inputs.environment }}
|
|
102
|
+
timeout-minutes: 30
|
|
103
|
+
permissions:
|
|
104
|
+
contents: read
|
|
105
|
+
packages: read
|
|
106
|
+
env:
|
|
107
|
+
STACK_ENVIRONMENT: ${{ inputs.environment }}
|
|
108
|
+
XDG_DATA_HOME: ${{ runner.temp }}/starci-data
|
|
109
|
+
steps:
|
|
110
|
+
- name: require release tag
|
|
111
|
+
shell: bash
|
|
112
|
+
run: '[[ "$GITHUB_REF" == refs/tags/v* ]] || exit 1'
|
|
113
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
114
|
+
- name: release tag matches package version
|
|
115
|
+
run: node -e "const version=require('./package.json').version;if(process.env.GITHUB_REF_NAME!=='v'+version)process.exit(1)"
|
|
116
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
117
|
+
with:
|
|
118
|
+
node-version: {{nodeMajor}}
|
|
119
|
+
cache: npm
|
|
120
|
+
- run: npm ci --ignore-scripts
|
|
121
|
+
- name: install SOPS
|
|
122
|
+
uses: mdgreenwald/mozilla-sops-action@bb2d96a13ef438ad122e5827a57ebd75d6ff118d # v2.1.0
|
|
123
|
+
with:
|
|
124
|
+
version: v3.13.2
|
|
125
|
+
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
|
126
|
+
with:
|
|
127
|
+
registry: ghcr.io
|
|
128
|
+
username: ${{ github.actor }}
|
|
129
|
+
password: ${{ github.token }}
|
|
130
|
+
- name: connect to the swarm manager
|
|
131
|
+
env:
|
|
132
|
+
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
133
|
+
VPS_KNOWN_HOSTS: ${{ secrets.VPS_KNOWN_HOSTS }}
|
|
134
|
+
VPS_HOST: ${{ vars.VPS_HOST }}
|
|
135
|
+
VPS_USER: ${{ vars.VPS_USER }}
|
|
136
|
+
run: npx --no-install starci app stack connect --env "$STACK_ENVIRONMENT" --execute
|
|
137
|
+
- name: redeploy the stored release
|
|
138
|
+
env:
|
|
139
|
+
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
|
|
140
|
+
RELEASE: ${{ inputs.release }}
|
|
141
|
+
shell: bash
|
|
142
|
+
run: |
|
|
143
|
+
release=()
|
|
144
|
+
[ -z "$RELEASE" ] || release=(--release "$RELEASE")
|
|
145
|
+
npx --no-install starci app stack rollback --env "$STACK_ENVIRONMENT" "${release[@]}" --execute
|
|
146
|
+
- name: disconnect from the swarm manager
|
|
147
|
+
if: ${{ always() }}
|
|
148
|
+
run: npx --no-install starci app stack disconnect --env "$STACK_ENVIRONMENT" --execute
|
|
@@ -8,12 +8,12 @@ const posix = (file) => file.split(path.sep).join("/")
|
|
|
8
8
|
const schemas = (fs.existsSync(CONTRACTS) ? fs.readdirSync(CONTRACTS) : [])
|
|
9
9
|
.map((app) => path.join(CONTRACTS, app, "schema.graphql"))
|
|
10
10
|
.filter((file) => fs.existsSync(file))
|
|
11
|
-
.sort()
|
|
11
|
+
.sort(byCodeUnit)
|
|
12
12
|
.map(posix)
|
|
13
13
|
const operationRoots = (fs.existsSync(FE_APPS) ? fs.readdirSync(FE_APPS) : [])
|
|
14
14
|
.map((app) => path.join(FE_APPS, app, "src", "modules", "api", "graphql"))
|
|
15
15
|
.filter((dir) => fs.existsSync(dir))
|
|
16
|
-
.sort()
|
|
16
|
+
.sort(byCodeUnit)
|
|
17
17
|
.map(posix)
|
|
18
18
|
if (schemas.length === 0 || operationRoots.length === 0) {
|
|
19
19
|
process.stdout.write("codegen: no GraphQL contract under be/contracts yet\n")
|
|
@@ -31,4 +31,15 @@ if (schemas.length === 0 || operationRoots.length === 0) {
|
|
|
31
31
|
]),
|
|
32
32
|
)
|
|
33
33
|
await generate({ schema: schemas, generates, ignoreNoDocuments: true, silent: true }, true)
|
|
34
|
+
// Stamp only a successful generation, using the same sorted paths and LF-normalized schemas as R52.
|
|
35
|
+
const hash = createHash("sha256")
|
|
36
|
+
for (const schema of schemas) {
|
|
37
|
+
const source = fs.readFileSync(schema, "utf8").replaceAll("\r\n", "\n")
|
|
38
|
+
hash.update(`${schema}\0${source}\0`)
|
|
39
|
+
}
|
|
40
|
+
const digest = hash.digest("hex")
|
|
41
|
+
for (const root of operationRoots) {
|
|
42
|
+
const generated = `${root}/__generated__`
|
|
43
|
+
if (fs.existsSync(generated)) fs.writeFileSync(`${generated}/.schema-sha256`, `${digest}\n`)
|
|
44
|
+
}
|
|
34
45
|
}
|