@cosmicdrift/kumiko-dev-server 0.368.0 → 0.370.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.
Files changed (43) hide show
  1. package/dist/build.d.ts +1 -1
  2. package/dist/cli.d.ts +3 -3
  3. package/dist/compose-stacks.d.ts +57 -3
  4. package/dist/compose-stacks.js +88 -9
  5. package/dist/host-dispatch-adapter.d.ts +7 -0
  6. package/dist/host-dispatch-adapter.js +31 -0
  7. package/dist/index.d.ts +21 -20
  8. package/dist/index.js +2 -1
  9. package/dist/init-deploy-cli.js +6 -0
  10. package/dist/managed-files/app-scripts.d.ts +13 -0
  11. package/dist/managed-files/app-scripts.js +40 -0
  12. package/dist/managed-files/check.d.ts +34 -0
  13. package/dist/managed-files/check.js +109 -0
  14. package/dist/managed-files/index.d.ts +8 -0
  15. package/dist/managed-files/index.js +8 -0
  16. package/dist/managed-files/jsonc.d.ts +1 -0
  17. package/dist/managed-files/jsonc.js +12 -0
  18. package/dist/managed-files/line-diff.d.ts +2 -0
  19. package/dist/managed-files/line-diff.js +60 -0
  20. package/dist/managed-files/marker.d.ts +17 -0
  21. package/dist/managed-files/marker.js +49 -0
  22. package/dist/managed-files/port-registry.d.ts +17 -0
  23. package/dist/managed-files/port-registry.js +31 -0
  24. package/dist/managed-files/registry.d.ts +51 -0
  25. package/dist/managed-files/registry.js +115 -0
  26. package/dist/managed-files/static-app-config.d.ts +16 -0
  27. package/dist/managed-files/static-app-config.js +99 -0
  28. package/dist/managed-files/sync.d.ts +19 -0
  29. package/dist/managed-files/sync.js +101 -0
  30. package/dist/run-dev-app.d.ts +6 -1
  31. package/dist/run-dev-app.js +21 -3
  32. package/dist/scaffold-app.d.ts +3 -3
  33. package/dist/scaffold-app.js +125 -199
  34. package/dist/scaffold-deploy.d.ts +9 -0
  35. package/dist/scaffold-deploy.js +34 -6
  36. package/dist/scaffold-feature.js +1 -0
  37. package/package.json +18 -9
  38. package/src/changes.json +19 -0
  39. package/templates/managed/cache-cleanup.yml.template +30 -0
  40. package/templates/managed/ci.yml.template +65 -0
  41. package/templates/managed/husky-pre-push.template +36 -0
  42. package/templates/managed/kumiko-schema.ts.template +12 -0
  43. package/templates/managed/release.yml.template +55 -0
@@ -13,6 +13,7 @@
13
13
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
14
  import { dirname, join } from "node:path";
15
15
  import { fileURLToPath } from "node:url";
16
+ import { parseJsonOrThrow } from "@cosmicdrift/kumiko-framework/utils";
16
17
  import * as z from "zod";
17
18
  import { isKebabSegment } from "./kebab.js";
18
19
  const REGISTRY_CONFIG_FILES = ["bunfig.toml", ".npmrc"];
@@ -46,7 +47,7 @@ export function renderDeployFiles(options) {
46
47
  // a `seeds/` directory (e.g. studio) crash in Docker-build with
47
48
  // `failed to compute cache key: "/app/seeds": not found`.
48
49
  const sourceDir = options.sourceDir ?? destinationRoot;
49
- const detected = detectOptionalSurfaces(sourceDir, options.appName);
50
+ const detected = detectOptionalSurfaces(sourceDir, options.appName, options.ensuredRegistryConfigFiles ?? []);
50
51
  if (detected.hasPrivateGhPackages && detected.registryConfigFiles.length === 0) {
51
52
  // The Dockerfile only forwards the auth build-arg; without a registry
52
53
  // config scope entry bun resolves @cosmicdriftgamestudio/* against npmjs
@@ -143,10 +144,17 @@ const LOCAL_DEP_SPEC_RE = /^(file:|workspace:|link:)/;
143
144
  // full Postgres identifier grammar (no quoting support) since they are
144
145
  // interpolated into a shell string, not passed through a driver's escaping.
145
146
  const DB_IDENTIFIER_RE = /^[a-zA-Z0-9_][a-zA-Z0-9_-]{0,62}$/;
147
+ // Kubernetes object names (RFC 1123 label); they are interpolated into the
148
+ // generated release workflow, so nothing outside this charset may pass.
149
+ const K8S_NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
146
150
  const kumikoDeployConfigSchema = z
147
151
  .object({
148
152
  dbUser: z.string().regex(DB_IDENTIFIER_RE).optional(),
149
153
  dbName: z.string().regex(DB_IDENTIFIER_RE).optional(),
154
+ /** Namespace of the prod deployment; default = appName. */
155
+ namespace: z.string().regex(K8S_NAME_RE).optional(),
156
+ /** Name of the prod Deployment; default = appName. */
157
+ deployment: z.string().regex(K8S_NAME_RE).optional(),
150
158
  // "directory" is the only behaviour left; the key stays valid so existing
151
159
  // package.json files keep scaffolding.
152
160
  stackNetwork: z
@@ -176,6 +184,25 @@ function parseDeployConfig(raw) {
176
184
  : ["kumiko", "deploy", ...issue.path.map(String)].join(".");
177
185
  throw new Error(`scaffoldDeploy: invalid package.json#${location} — ${issue?.message ?? "validation failed"}`);
178
186
  }
187
+ /** Release target from `package.json#kumiko.deploy`; throws on an invalid deploy config. */
188
+ export function readReleaseTarget(sourceDir, appName) {
189
+ const pkgJsonPath = join(sourceDir, "package.json");
190
+ const raw = existsSync(pkgJsonPath)
191
+ ? parseJsonOrThrow(readFileSync(pkgJsonPath, "utf-8"), pkgJsonPath)
192
+ : undefined;
193
+ const config = parseDeployConfig(extractDeployConfigRaw(raw));
194
+ const namespace = config.namespace ?? appName;
195
+ const deployment = config.deployment ?? appName;
196
+ for (const [field, value] of [
197
+ ["namespace", namespace],
198
+ ["deployment", deployment],
199
+ ]) {
200
+ if (!K8S_NAME_RE.test(value)) {
201
+ throw new Error(`scaffoldDeploy: "${value}" cannot serve as the release ${field} (must match ${K8S_NAME_RE}); set package.json#kumiko.deploy.${field}`);
202
+ }
203
+ }
204
+ return { namespace, deployment };
205
+ }
179
206
  function resolveDbIdentifier(configured, appName, field, role) {
180
207
  const effective = configured ?? appName;
181
208
  // The default (appName) already passed isKebabSegment's charset check but
@@ -197,7 +224,7 @@ function resolveDeployConfig(deployConfigRaw, appName) {
197
224
  dbName: resolveDbIdentifier(config.dbName, appName, "dbName", "DB name"),
198
225
  };
199
226
  }
200
- function detectOptionalSurfaces(sourceDir, appName) {
227
+ function detectOptionalSurfaces(sourceDir, appName, ensuredRegistryConfigFiles) {
201
228
  const hasSeeds = existsSync(join(sourceDir, "seeds"));
202
229
  let hasPrivateGhPackages = false;
203
230
  let installFromFullTree = false;
@@ -237,7 +264,7 @@ function detectOptionalSurfaces(sourceDir, appName) {
237
264
  console.warn(message);
238
265
  }
239
266
  }
240
- const registryConfigFiles = REGISTRY_CONFIG_FILES.filter((f) => existsSync(join(sourceDir, f)));
267
+ const registryConfigFiles = REGISTRY_CONFIG_FILES.filter((f) => ensuredRegistryConfigFiles.includes(f) || existsSync(join(sourceDir, f)));
241
268
  return {
242
269
  hasSeeds,
243
270
  hasPrivateGhPackages,
@@ -247,10 +274,11 @@ function detectOptionalSurfaces(sourceDir, appName) {
247
274
  };
248
275
  }
249
276
  // Unconsumed-mustache guard. After step 1+2 the only legitimate `{{`
250
- // survivors are Docker/Go template tags (`{{.Name}}`, leading `.`). An
277
+ // survivors are Docker/Go template tags (`{{.Name}}`, leading `.`) and GitHub
278
+ // Actions expressions (`${{ github.sha }}`, leading `$`). An
251
279
  // orphan close-tag (`{{/foo}}` without an opener) matches neither step and
252
280
  // would otherwise leak into the rendered file as a silent failure.
253
- const ORPHAN_MUSTACHE_RE = /\{\{(?!\.)[^}]*\}\}/;
281
+ const ORPHAN_MUSTACHE_RE = /(?<!\$)\{\{(?!\.)[^}]*\}\}/;
254
282
  export function render(source, subs, flags) {
255
283
  // Step 1: handle mustache-style block conditionals `{{#flag}}...{{/flag}}`
256
284
  // (multiline-aware via [\s\S]). When flag is truthy → keep inner content;
@@ -267,7 +295,7 @@ export function render(source, subs, flags) {
267
295
  // narrow: lowercase-leading identifier followed by alphanumerics. That
268
296
  // excludes Docker/Go template syntax like `{{.Name}}` (leading `.`)
269
297
  // which appears verbatim in the migrate-step.sh shell snippet.
270
- result = result.replace(/\{\{([a-z][a-zA-Z0-9]*)\}\}/g, (full, key) => {
298
+ result = result.replace(/(?<!\$)\{\{([a-z][a-zA-Z0-9]*)\}\}/g, (full, key) => {
271
299
  const value = subs[key];
272
300
  if (value === undefined) {
273
301
  throw new Error(`scaffoldDeploy.render: unknown placeholder "${full}"`);
@@ -127,6 +127,7 @@ function renderTsconfig() {
127
127
  target: "ESNext",
128
128
  module: "ESNext",
129
129
  moduleResolution: "bundler",
130
+ allowImportingTsExtensions: true,
130
131
  esModuleInterop: true,
131
132
  skipLibCheck: true,
132
133
  lib: ["ESNext"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-dev-server",
3
- "version": "0.368.0",
3
+ "version": "0.370.0",
4
4
  "description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding. Its compose-stacks/env-schema subpaths are consumed at prod boot too — see @cosmicdrift/kumiko-server-runtime for the prod runner itself.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -18,6 +18,7 @@
18
18
  "runtime": "dev",
19
19
  "bunOnlyExports": [
20
20
  ".",
21
+ "./managed-files",
21
22
  "./setup-test-stack-from-features"
22
23
  ]
23
24
  },
@@ -38,6 +39,10 @@
38
39
  "types": "./dist/env-schema.d.ts",
39
40
  "default": "./dist/env-schema.js"
40
41
  },
42
+ "./managed-files": {
43
+ "types": "./dist/managed-files/index.d.ts",
44
+ "default": "./dist/managed-files/index.js"
45
+ },
41
46
  "./setup-test-stack-from-features": {
42
47
  "types": "./dist/setup-test-stack-from-features.d.ts",
43
48
  "default": "./dist/setup-test-stack-from-features.js"
@@ -67,14 +72,14 @@
67
72
  "kumiko-upgrade": "./bin/kumiko-upgrade.ts"
68
73
  },
69
74
  "dependencies": {
70
- "@cosmicdrift/kumiko-bundled-features": "0.368.0",
71
- "@cosmicdrift/kumiko-framework": "0.368.0",
72
- "@cosmicdrift/kumiko-headless": "0.368.0",
73
- "@cosmicdrift/kumiko-renderer-mail-html": "0.368.0",
74
- "@cosmicdrift/kumiko-renderer-web": "0.368.0",
75
- "@cosmicdrift/kumiko-server-runtime": "0.368.0",
76
- "@cosmicdrift/kumiko-testing": "0.368.0",
77
- "@cosmicdrift/kumiko-types": "0.368.0",
75
+ "@cosmicdrift/kumiko-bundled-features": "0.370.0",
76
+ "@cosmicdrift/kumiko-framework": "0.370.0",
77
+ "@cosmicdrift/kumiko-headless": "0.370.0",
78
+ "@cosmicdrift/kumiko-renderer-mail-html": "0.370.0",
79
+ "@cosmicdrift/kumiko-renderer-web": "0.370.0",
80
+ "@cosmicdrift/kumiko-server-runtime": "0.370.0",
81
+ "@cosmicdrift/kumiko-testing": "0.370.0",
82
+ "@cosmicdrift/kumiko-types": "0.370.0",
78
83
  "bun-types": "^1.3.13",
79
84
  "ts-morph": "^28.0.0",
80
85
  "zod": "^4.4.3"
@@ -99,6 +104,10 @@
99
104
  "types": "./dist/env-schema.d.ts",
100
105
  "default": "./dist/env-schema.js"
101
106
  },
107
+ "./managed-files": {
108
+ "types": "./dist/managed-files/index.d.ts",
109
+ "default": "./dist/managed-files/index.js"
110
+ },
102
111
  "./setup-test-stack-from-features": {
103
112
  "types": "./dist/setup-test-stack-from-features.d.ts",
104
113
  "default": "./dist/setup-test-stack-from-features.js"
package/src/changes.json CHANGED
@@ -1,4 +1,23 @@
1
1
  [
2
+ {
3
+ "version": "0.370.0",
4
+ "type": "improvement",
5
+ "title": "runDevApp binds the MFA token secrets",
6
+ "detail": "When auth-mfa is mounted and JWT_SECRET is set, runDevApp binds the MFA token secrets the same way runProdApp does."
7
+ },
8
+ {
9
+ "version": "0.370.0",
10
+ "type": "improvement",
11
+ "title": "composeAuthStack, composeSaasStack and composeSecurityStack",
12
+ "detail": "composeAuthStack({ sessions?, mfa?, accountSecurity? }) mounts auth-foundation and sessions, with mfa also auth-mfa and account-security; the password login still comes from the auth option or composeFeatures. composeSaasStack({ billing: \"stripe\" | \"mollie\", tiers, caps?, waitlist?, billingFoundation? }) mounts billing-foundation and tier-engine, cap-counter plus cap-overview with caps, waitlist with waitlist. With \"stripe\" the runProdApp billing option mounts subscription-stripe and the tier-sync webhook route; with \"mollie\" the stack mounts subscription-mollie from the mollie options and the app adds the webhook route. composeSecurityStack({ includeSessions? }) mounts sessions, crypto-shredding, rate-limiting and audit. composeGdprStack({ selfService: { userDataRights? } }) adds sessions, user-data-rights and user-profile in the order dsgvoSelfServiceFeatures used. billing-foundation requires tenant-lifecycle and compliance-profiles, which composeGdprStack({ tenantLifecycle: true }) mounts. user-profile in selfService requires user-data-rights-defaults and auth-email-password; composeUserDataRightsStack mounts the defaults, and the login comes from the auth option. Recipe: samples/recipes/compose-stacks."
13
+ },
14
+ {
15
+ "version": "0.370.0",
16
+ "type": "improvement",
17
+ "title": "Managed files: one template source for the scaffold, kumiko check managed and kumiko sync-managed",
18
+ "detail": "deploy/Dockerfile, deploy/Dockerfile.dockerignore, deploy/migrate-step.sh, .husky/pre-push, .github/workflows/{ci,release,cache-cleanup}.yml, the bunfigs and kumiko/schema.ts carry a kumiko-managed marker on line 1 (line 2 after a shebang or # syntax= line). kumiko new app writes them, plus kumiko.config.ts, bin/server.ts (was bin/dev.ts), bin/env-schema.ts, e2e/_helpers and src/__tests__/_setup.ts; bunfig.ci.toml is gone. kumiko/schema.ts is generated from kumiko.config.ts. kumiko-init-deploy prints a deprecation notice and goes away in the next release. Release targets come from package.json#kumiko.deploy.namespace and .deployment (default: the app name).",
19
+ "migration": "Existing apps: add kumiko.config.ts with defineAppConfig, then run kumiko sync-managed (dry run), kumiko sync-managed --write --adopt for files that are identical or should follow the template, and mark deliberate deviations with \"# kumiko-managed: local <reason>\" on line 2. Until kumiko.config.ts calls defineAppConfig, kumiko check only warns about drift and missing scripts."
20
+ },
2
21
  {
3
22
  "version": "0.368.0",
4
23
  "type": "breaking",
@@ -0,0 +1,30 @@
1
+ name: cache-cleanup
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 3 * * 1"
6
+ workflow_dispatch: {}
7
+
8
+ jobs:
9
+ cleanup:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ actions: write
13
+ steps:
14
+ - name: Delete caches unused for 3+ days
15
+ # Actions cache is branch-scoped: every PR branch writes its own copy
16
+ # even with an identical key, and GitHub only auto-evicts on the
17
+ # per-repo 10GB cap or 7-day-unused rule. This keeps repos well under
18
+ # that cap instead of relying on eviction churn.
19
+ env:
20
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21
+ REPO: ${{ github.repository }}
22
+ run: |
23
+ cutoff=$(date -u -d '3 days ago' +%s)
24
+ gh api --paginate "repos/$REPO/actions/caches?per_page=100" --jq '.actions_caches[] | "\(.id) \(.last_accessed_at)"' |
25
+ while read -r id ts; do
26
+ age=$(date -u -d "$ts" +%s)
27
+ if [ "$age" -lt "$cutoff" ]; then
28
+ gh api -X DELETE "repos/$REPO/actions/caches/$id" && echo "deleted $id"
29
+ fi
30
+ done
@@ -0,0 +1,65 @@
1
+ # CI for {{appName}}: a thin orchestrator over the reusable workflows in
2
+ # cosmicdriftgamestudio/infra. Jobs only this app needs go into an unmanaged
3
+ # workflow next to this one (e.g. ci-extra.yml).
4
+
5
+ name: CI
6
+
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ pull_request:
11
+ branches: [main]
12
+
13
+ concurrency:
14
+ group: ci-${{ github.ref }}
15
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
16
+
17
+ env:
18
+ HUSKY: "0"
19
+
20
+ jobs:
21
+ test:
22
+ uses: cosmicdriftgamestudio/infra/.github/workflows/_app-test.yml@main
23
+ with:
24
+ ui-guards: {{uiGuards}}
25
+ # The boot gate in kumiko-schema validate demands a provider for file
26
+ # fields; CI only checks that one is configured.
27
+ file-storage-provider: "inmemory"
28
+ secrets: inherit
29
+
30
+ integration:
31
+ uses: cosmicdriftgamestudio/infra/.github/workflows/_app-integration.yml@main
32
+ {{#minio}}
33
+ with:
34
+ minio: true
35
+ {{/minio}}
36
+ secrets: inherit
37
+
38
+ # merge_group included: as a required check in a merge queue, e2e would
39
+ # otherwise never report and block the merge.
40
+ e2e:
41
+ if: contains(fromJSON('["pull_request","merge_group"]'), github.event_name)
42
+ uses: cosmicdriftgamestudio/infra/.github/workflows/_app-e2e.yml@main
43
+ {{#e2eInputs}}
44
+ with:
45
+ {{/e2eInputs}}
46
+ {{#meilisearch}}
47
+ meilisearch: true
48
+ {{/meilisearch}}
49
+ {{#minio}}
50
+ minio: true
51
+ {{/minio}}
52
+ secrets: inherit
53
+
54
+ # Build on main only; the rollout runs through the manual release workflow.
55
+ build:
56
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
57
+ needs: [test, integration]
58
+ permissions:
59
+ contents: read
60
+ packages: write
61
+ uses: cosmicdriftgamestudio/infra/.github/workflows/_docker-build-push.yml@main
62
+ with:
63
+ image-name: {{appName}}
64
+ dockerfile: deploy/Dockerfile
65
+ secrets: inherit
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env sh
2
+ # PRE_PUSH_SKIP is checked here too so the bypass works even without node_modules.
3
+ if [ "${PRE_PUSH_SKIP:-0}" = "1" ]; then
4
+ echo "[pre-push] PRE_PUSH_SKIP=1 — hook skipped"
5
+ exit 0
6
+ fi
7
+
8
+ REPO_ROOT="$(git rev-parse --show-toplevel)"
9
+ if [ -z "$REPO_ROOT" ]; then
10
+ echo "[pre-push] FATAL: git rev-parse --show-toplevel failed — refusing push" >&2
11
+ exit 1
12
+ fi
13
+
14
+ # Walk up to / so a worktree without its own node_modules resolves via the
15
+ # parent install; `[ -x ]` also skips a broken (uninstalled) symlink.
16
+ BIN=""
17
+ DIR="$REPO_ROOT"
18
+ while true; do
19
+ CANDIDATE="$DIR/node_modules/.bin/kumiko-pre-push"
20
+ if [ -x "$CANDIDATE" ]; then
21
+ BIN="$CANDIDATE"
22
+ break
23
+ fi
24
+ if [ "$DIR" = "/" ]; then
25
+ break
26
+ fi
27
+ DIR="$(dirname "$DIR")"
28
+ done
29
+
30
+ if [ -z "$BIN" ]; then
31
+ echo "[pre-push] kumiko-pre-push not found in any node_modules/.bin above $REPO_ROOT." >&2
32
+ echo "Run \`bun install\`, or set PRE_PUSH_SKIP=1 to bypass this hook." >&2
33
+ exit 1
34
+ fi
35
+
36
+ cd "$REPO_ROOT" && exec "$BIN" "$@"
@@ -0,0 +1,12 @@
1
+ // Input of the schema tooling (kumiko-schema generate|apply|validate and the
2
+ // boot step of kumiko check), derived from kumiko.config.ts. Edit the feature
3
+ // lists there.
4
+
5
+ import { allAppFeatures } from "@cosmicdrift/kumiko-framework/config";
6
+ import { collectTableMetas, type EntityTableMeta } from "@cosmicdrift/kumiko-framework/db";
7
+ import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
8
+ import appConfig from "../kumiko.config";
9
+
10
+ export const FEATURES: readonly FeatureDefinition[] = allAppFeatures(appConfig);
11
+
12
+ export const ENTITY_METAS: readonly EntityTableMeta[] = collectTableMetas(FEATURES);
@@ -0,0 +1,55 @@
1
+ name: release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ sha:
7
+ description: "Full 40-char main SHA; image sha-<7> must exist"
8
+ required: true
9
+ type: string
10
+
11
+ concurrency:
12
+ group: release-{{appName}}
13
+ cancel-in-progress: false
14
+
15
+ permissions:
16
+ contents: read
17
+ actions: read
18
+
19
+ jobs:
20
+ verify:
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 5
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ with:
26
+ fetch-depth: 0
27
+ - name: Verify SHA
28
+ env:
29
+ SHA: ${{ inputs.sha }}
30
+ GH_TOKEN: ${{ github.token }}
31
+ run: |
32
+ set -euo pipefail
33
+ if ! [[ "$SHA" =~ ^[0-9a-f]{40}$ ]]; then
34
+ echo "::error::sha must be a full 40-char lowercase hex SHA."
35
+ exit 1
36
+ fi
37
+ if ! git merge-base --is-ancestor "$SHA" origin/main; then
38
+ echo "::error::$SHA is not an ancestor of origin/main."
39
+ exit 1
40
+ fi
41
+ count=$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs?head_sha=$SHA&event=push&status=success" --jq '.total_count')
42
+ if [ "${count:-0}" -le 0 ]; then
43
+ echo "::error::No successful CI push run found for $SHA."
44
+ exit 1
45
+ fi
46
+
47
+ deploy:
48
+ needs: verify
49
+ uses: cosmicdriftgamestudio/infra/.github/workflows/_app-deploy.yml@main
50
+ with:
51
+ namespace: {{namespace}}
52
+ deployment: {{deployment}}
53
+ image-name: {{appName}}
54
+ git_sha: ${{ inputs.sha }}
55
+ secrets: inherit