@ankhorage/infra 4.1.0 → 4.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,kBAAkB,EAClB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AAUrB,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,+BAAoC,GAC5C,8BAA8B,CAmFhC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,kBAAkB,EAClB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AAUrB,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,+BAAoC,GAC5C,8BAA8B,CAuFhC"}
@@ -14,6 +14,8 @@ export function generateMinikubeInfra(manifest, options = {}) {
14
14
  throw new Error(`Unsupported database provider for minikube adapter: "${manifest.database.provider}". Only "supabase" is implemented.`);
15
15
  }
16
16
  const appSlug = validateCanonicalAppSlug(options.appManifest?.metadata.slug);
17
+ const appRuntimeEnabled = options.appManifest?.deploy === undefined ||
18
+ options.appManifest.deploy.targets.web?.enabled === true;
17
19
  const namespace = APP_NAMESPACE;
18
20
  const authArtifacts = generateAuthProviderArtifacts({
19
21
  appManifest: options.appManifest,
@@ -55,6 +57,7 @@ export function generateMinikubeInfra(manifest, options = {}) {
55
57
  providerNamespaces,
56
58
  providerLifecycle,
57
59
  extraEnvEntries,
60
+ appRuntimeEnabled,
58
61
  });
59
62
  const warnings = unique([
60
63
  ...collectWarnings(manifest),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { validateInfraApiSupport } from '../../infraValidation';\nimport type {\n GeneratedPackageDependency,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from '../../types';\nimport { generateAuthProviderArtifacts } from './auth';\nimport { generateAuthorizationArtifacts } from './authz';\nimport { APP_NAMESPACE, generateMinikubeBaseArtifacts } from './base';\nimport { generateSecretStoreArtifacts } from './secrets';\nimport { generateStorageArtifacts } from './storage';\n\nconst CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;\nconst BUILT_IN_NAMESPACES = new Set([APP_NAMESPACE, 'supabase']);\n\nexport function generateMinikubeInfra(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions = {},\n): InfrastructureGenerationResult {\n if (manifest.deployment?.target !== 'minikube') {\n throw new Error(\n `Minikube adapter only supports deployment target \"minikube\" (received \"${manifest.deployment?.target ?? 'undefined'}\").`,\n );\n }\n\n if (manifest.database?.provider && manifest.database.provider !== 'supabase') {\n throw new Error(\n `Unsupported database provider for minikube adapter: \"${manifest.database.provider}\". Only \"supabase\" is implemented.`,\n );\n }\n\n const appSlug = validateCanonicalAppSlug(options.appManifest?.metadata.slug);\n const namespace = APP_NAMESPACE;\n\n const authArtifacts = generateAuthProviderArtifacts({\n appManifest: options.appManifest,\n manifest,\n namespace,\n });\n const authzArtifacts = generateAuthorizationArtifacts({\n manifest,\n namespace,\n appManifest: options.appManifest,\n });\n const storageArtifacts = generateStorageArtifacts({ manifest, namespace });\n const secretStoreArtifacts = generateSecretStoreArtifacts({ manifest, namespace });\n\n const extraResources = unique([\n ...authArtifacts.resources,\n ...authzArtifacts.resources,\n ...storageArtifacts.resources,\n ...secretStoreArtifacts.resources,\n ]);\n const providerLifecycle = [\n ...authArtifacts.providerLifecycle,\n ...authzArtifacts.providerLifecycle,\n ...storageArtifacts.providerLifecycle,\n ...secretStoreArtifacts.providerLifecycle,\n ];\n const providerNamespaces = unique([\n ...providerLifecycle.map((contribution) => contribution.namespace),\n ]).filter((providerNamespace) => !BUILT_IN_NAMESPACES.has(providerNamespace));\n const extraEnvEntries = unique([\n ...authArtifacts.envEntries,\n ...authzArtifacts.envEntries,\n ...storageArtifacts.envEntries,\n ...secretStoreArtifacts.envEntries,\n ]);\n\n const baseFiles = generateMinikubeBaseArtifacts({\n manifest,\n appSlug,\n extraResources,\n providerNamespaces,\n providerLifecycle,\n extraEnvEntries,\n });\n\n const warnings = unique([\n ...collectWarnings(manifest),\n ...authArtifacts.warnings,\n ...authzArtifacts.warnings,\n ...storageArtifacts.warnings,\n ...secretStoreArtifacts.warnings,\n ]);\n\n return {\n files: [\n ...baseFiles,\n ...authArtifacts.files,\n ...authzArtifacts.files,\n ...storageArtifacts.files,\n ...secretStoreArtifacts.files,\n ],\n warnings,\n meta: {\n target: 'minikube',\n providers: collectProviders(manifest),\n },\n dependencies: collectDependencies(manifest),\n };\n}\n\nfunction collectProviders(manifest: InfraManifestInput): string[] {\n const providers = new Set<string>();\n\n if (manifest.auth?.provider) providers.add(manifest.auth.provider);\n if (manifest.auth?.authorization?.engine) {\n providers.add(manifest.auth.authorization.engine);\n }\n if (manifest.database?.provider) providers.add(manifest.database.provider);\n if (manifest.storage?.provider) providers.add(manifest.storage.provider);\n if (manifest.secretStore?.provider) providers.add(manifest.secretStore.provider);\n if (manifest.state?.provider) providers.add(manifest.state.provider);\n\n return [...providers].sort();\n}\n\nfunction collectDependencies(manifest: InfraManifestInput): readonly GeneratedPackageDependency[] {\n if (manifest.state?.provider !== 'legend') {\n return [];\n }\n\n return [\n {\n name: '@ankhorage/state-legend',\n version: '^0.1.0',\n reason: 'Selected by infra.state.provider=legend.',\n },\n ];\n}\n\nfunction collectWarnings(manifest: InfraManifestInput): string[] {\n const warnings = [...validateInfraApiSupport(manifest)];\n\n if (manifest.deployment?.monitoring) {\n warnings.push(\n 'Monitoring is enabled in manifest, but monitoring stack resources are not generated yet.',\n );\n }\n\n if (manifest.networking?.cdn) {\n warnings.push('CDN is enabled in manifest, but CDN resources are not generated for minikube.');\n }\n\n return warnings;\n}\n\nfunction validateCanonicalAppSlug(slug: string | undefined): string {\n if (!slug) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug is required.',\n );\n }\n\n if (slug.trim() !== slug || !CANONICAL_PROJECT_SLUG_RE.test(slug)) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters using only a-z, 0-9, and hyphens, without leading or trailing hyphens.',\n );\n }\n\n return slug;\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { validateInfraApiSupport } from '../../infraValidation';\nimport type {\n GeneratedPackageDependency,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from '../../types';\nimport { generateAuthProviderArtifacts } from './auth';\nimport { generateAuthorizationArtifacts } from './authz';\nimport { APP_NAMESPACE, generateMinikubeBaseArtifacts } from './base';\nimport { generateSecretStoreArtifacts } from './secrets';\nimport { generateStorageArtifacts } from './storage';\n\nconst CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;\nconst BUILT_IN_NAMESPACES = new Set([APP_NAMESPACE, 'supabase']);\n\nexport function generateMinikubeInfra(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions = {},\n): InfrastructureGenerationResult {\n if (manifest.deployment?.target !== 'minikube') {\n throw new Error(\n `Minikube adapter only supports deployment target \"minikube\" (received \"${manifest.deployment?.target ?? 'undefined'}\").`,\n );\n }\n\n if (manifest.database?.provider && manifest.database.provider !== 'supabase') {\n throw new Error(\n `Unsupported database provider for minikube adapter: \"${manifest.database.provider}\". Only \"supabase\" is implemented.`,\n );\n }\n\n const appSlug = validateCanonicalAppSlug(options.appManifest?.metadata.slug);\n const appRuntimeEnabled =\n options.appManifest?.deploy === undefined ||\n options.appManifest.deploy.targets.web?.enabled === true;\n const namespace = APP_NAMESPACE;\n\n const authArtifacts = generateAuthProviderArtifacts({\n appManifest: options.appManifest,\n manifest,\n namespace,\n });\n const authzArtifacts = generateAuthorizationArtifacts({\n manifest,\n namespace,\n appManifest: options.appManifest,\n });\n const storageArtifacts = generateStorageArtifacts({ manifest, namespace });\n const secretStoreArtifacts = generateSecretStoreArtifacts({ manifest, namespace });\n\n const extraResources = unique([\n ...authArtifacts.resources,\n ...authzArtifacts.resources,\n ...storageArtifacts.resources,\n ...secretStoreArtifacts.resources,\n ]);\n const providerLifecycle = [\n ...authArtifacts.providerLifecycle,\n ...authzArtifacts.providerLifecycle,\n ...storageArtifacts.providerLifecycle,\n ...secretStoreArtifacts.providerLifecycle,\n ];\n const providerNamespaces = unique([\n ...providerLifecycle.map((contribution) => contribution.namespace),\n ]).filter((providerNamespace) => !BUILT_IN_NAMESPACES.has(providerNamespace));\n const extraEnvEntries = unique([\n ...authArtifacts.envEntries,\n ...authzArtifacts.envEntries,\n ...storageArtifacts.envEntries,\n ...secretStoreArtifacts.envEntries,\n ]);\n\n const baseFiles = generateMinikubeBaseArtifacts({\n manifest,\n appSlug,\n extraResources,\n providerNamespaces,\n providerLifecycle,\n extraEnvEntries,\n appRuntimeEnabled,\n });\n\n const warnings = unique([\n ...collectWarnings(manifest),\n ...authArtifacts.warnings,\n ...authzArtifacts.warnings,\n ...storageArtifacts.warnings,\n ...secretStoreArtifacts.warnings,\n ]);\n\n return {\n files: [\n ...baseFiles,\n ...authArtifacts.files,\n ...authzArtifacts.files,\n ...storageArtifacts.files,\n ...secretStoreArtifacts.files,\n ],\n warnings,\n meta: {\n target: 'minikube',\n providers: collectProviders(manifest),\n },\n dependencies: collectDependencies(manifest),\n };\n}\n\nfunction collectProviders(manifest: InfraManifestInput): string[] {\n const providers = new Set<string>();\n\n if (manifest.auth?.provider) providers.add(manifest.auth.provider);\n if (manifest.auth?.authorization?.engine) {\n providers.add(manifest.auth.authorization.engine);\n }\n if (manifest.database?.provider) providers.add(manifest.database.provider);\n if (manifest.storage?.provider) providers.add(manifest.storage.provider);\n if (manifest.secretStore?.provider) providers.add(manifest.secretStore.provider);\n if (manifest.state?.provider) providers.add(manifest.state.provider);\n\n return [...providers].sort();\n}\n\nfunction collectDependencies(manifest: InfraManifestInput): readonly GeneratedPackageDependency[] {\n if (manifest.state?.provider !== 'legend') {\n return [];\n }\n\n return [\n {\n name: '@ankhorage/state-legend',\n version: '^0.1.0',\n reason: 'Selected by infra.state.provider=legend.',\n },\n ];\n}\n\nfunction collectWarnings(manifest: InfraManifestInput): string[] {\n const warnings = [...validateInfraApiSupport(manifest)];\n\n if (manifest.deployment?.monitoring) {\n warnings.push(\n 'Monitoring is enabled in manifest, but monitoring stack resources are not generated yet.',\n );\n }\n\n if (manifest.networking?.cdn) {\n warnings.push('CDN is enabled in manifest, but CDN resources are not generated for minikube.');\n }\n\n return warnings;\n}\n\nfunction validateCanonicalAppSlug(slug: string | undefined): string {\n if (!slug) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug is required.',\n );\n }\n\n if (slug.trim() !== slug || !CANONICAL_PROJECT_SLUG_RE.test(slug)) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters using only a-z, 0-9, and hyphens, without leading or trailing hyphens.',\n );\n }\n\n return slug;\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/infra",
3
3
  "type": "module",
4
- "version": "4.1.0",
4
+ "version": "4.1.2",
5
5
  "description": "Project-aware infrastructure generation library and executable Ankh provider for Ankhorage.",
6
6
  "homepage": "https://github.com/ankhorage/infra#readme",
7
7
  "bugs": {
@@ -25,21 +25,31 @@ when provided by caller.
25
25
 
26
26
  ## Runtime Scripts
27
27
 
28
- - `scripts/up.sh`: starts `minikube -p <slug>`, applies manifests, waits for Supabase, runs migrations with CLI telemetry disabled for that command, builds/loads the app image, and starts slug-owned port-forwards.
28
+ - `scripts/up.sh`: starts `minikube -p <slug>`, applies manifests, waits for Supabase, runs migrations with CLI telemetry disabled for that command, and starts slug-owned port-forwards. It builds and deploys the Kubernetes Web app runtime only when the canonical app manifest enables `deploy.targets.web` (legacy manifests without `deploy` retain the Web runtime).
29
29
  - `scripts/down.sh`: stops slug-owned port-forwards, then runs `minikube stop -p <slug>`. Persistent profile data remains.
30
30
  - `scripts/reset.sh`: requires `ANKH_RESET_CONFIRM=<slug>` and deletes/recreates namespaces `app` and `supabase`, including Supabase DB and Storage PVC data. It does not delete the Minikube profile.
31
31
  - `scripts/destroy.sh`: stops slug-owned port-forwards and runs `minikube delete -p <slug>`.
32
32
  - `scripts/status.sh`: reports profile, namespace, workload, and port-forward health.
33
- - `scripts/port-forward.sh`: owns named forwards for `app`, `supabase-gateway`, `studio`, and `db-migration`, plus provider-aware `runtime` and `all` groups.
33
+ - `scripts/port-forward.sh`: owns topology-derived named forwards plus provider-aware `runtime` and `all` groups.
34
34
  - `scripts/build-app-image.sh`: exports Expo web build from app source and builds the Docker image.
35
35
 
36
36
  The `runtime` group is the canonical repair lifecycle for host endpoints required by the
37
- running app. It always contains `app` and adds `supabase-gateway` when the generated
38
- Supabase Kubernetes runtime is enabled. It deliberately excludes `studio` and
39
- `db-migration`; `all` retains those operational/bootstrap forwards. Start, stop, or inspect
40
- the group with `scripts/port-forward.sh {start|stop|status} runtime`. Package consumers
41
- should call `ensureProjectInfrastructureRuntime()` from `@ankhorage/infra/project` instead
42
- of depending on concrete provider forward names.
37
+ running app. It contains `app` only when the generated topology includes the Kubernetes Web
38
+ app Service, and adds `supabase-gateway` when the generated Supabase Kubernetes runtime is
39
+ enabled. Native-only apps therefore restore provider endpoints without requiring a fictional
40
+ app Service. The group deliberately excludes `studio` and `db-migration`; `all` retains
41
+ those operational/bootstrap forwards. Start, stop, or inspect the group with
42
+ `scripts/port-forward.sh {start|stop|status} runtime`. Package consumers should call
43
+ `ensureProjectInfrastructureRuntime()` from `@ankhorage/infra/project` instead of depending
44
+ on concrete provider forward names. Forward startup retries bounded transient Kubernetes
45
+ pod-selection and readiness failures that can occur immediately after a profile restart.
46
+ Active runtime/all groups contain only targets in the generated topology. Cleanup retains
47
+ the exact identities of all Infra-managed forwards so a Web-to-native regeneration safely
48
+ stops an obsolete owned `app` forward without making it an active start target.
49
+
50
+ When the Web app runtime is disabled, `up.sh` also reconciles the former generated
51
+ `Deployment/app-runtime`, `Service/app-runtime`, and `ConfigMap/app-infra-config` with exact,
52
+ idempotent deletes. It does not prune unrelated resources or delete the `app` namespace.
43
53
 
44
54
  ## Supabase Runtime
45
55
 
@@ -169,6 +169,9 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
169
169
  expect(postgres).toContain('config_file=/etc/postgresql/postgresql.conf');
170
170
  expect(gatewayTemplate).toContain('keyauth_credentials');
171
171
  expect(gatewayTemplate).toContain('request-transformer');
172
+ expect(gatewayTemplate).toContain('_format_version: "2.1"');
173
+ expect(gatewayTemplate).toContain('username: "$DASHBOARD_USERNAME"');
174
+ expect(gatewayTemplate).toContain('password: "$DASHBOARD_PASSWORD"');
172
175
  expect(kongEntrypoint).toContain('LUA_AUTH_EXPR');
173
176
  expect(auth).toContain('name: GOTRUE_JWT_EXP');
174
177
  expect(auth).toContain('key: JWT_EXPIRY');
@@ -268,6 +271,7 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
268
271
  expect(kustomization).toContain('namespaces/cerbos.yaml');
269
272
  expect(readme).toContain('- Provider namespaces: `cerbos`');
270
273
  expect(readme).toContain('## Provider Lifecycle Contributions');
274
+ expect(readme).not.toContain('\n\n\n## Provider Lifecycle Contributions');
271
275
  expect(readme).toContain('`cerbos`: namespace `cerbos`');
272
276
  expect(readme).toContain('`deployment/cerbos` in namespace `cerbos`');
273
277
  expect(readme).toContain('`http` = `http://cerbos.cerbos.svc.cluster.local:3592`');
@@ -329,9 +333,11 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
329
333
  },
330
334
  );
331
335
  const envExample = getFile(result.files, 'infra/minikube/.env.example');
336
+ const readme = getFile(result.files, 'infra/minikube/README.md');
332
337
  const upScript = getFile(result.files, 'infra/minikube/scripts/up.sh');
333
338
 
334
339
  expect(envExample).toContain('# google: auth/oauth/google');
340
+ expect(readme).not.toContain('credentialsRef` entries.\n\n\nPinned images:');
335
341
  expect(envExample).toContain('GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=');
336
342
  expect(envExample).toContain('GOTRUE_EXTERNAL_GOOGLE_SECRET=');
337
343
  expect(upScript).toContain('GOTRUE_EXTERNAL_GOOGLE_ENABLED=true');
@@ -387,7 +393,9 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
387
393
  );
388
394
  expect(upScript).toContain('ensure_supabase_runtime_env\nsync_app_public_supabase_env');
389
395
  expect(upScript.lastIndexOf('sync_app_public_supabase_env')).toBeLessThan(
390
- upScript.indexOf('if [[ "${APP_BUILD_ENABLED}" == "true" ]]'),
396
+ upScript.indexOf(
397
+ 'if [[ "${APP_RUNTIME_ENABLED}" == "true" && "${APP_BUILD_ENABLED}" == "true" ]]',
398
+ ),
391
399
  );
392
400
  expect(upScript).not.toContain('SUPABASE_SERVICE_ROLE_KEY" "${SUPABASE_SERVICE_ROLE_KEY}"');
393
401
  expect(buildScript).toContain(
@@ -457,13 +465,11 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
457
465
  'ALL_FORWARD_NAMES=(app supabase-gateway studio db-migration)',
458
466
  );
459
467
  expect(portForwardScript).toContain(
460
- 'start:runtime) for_each_forward start "${RUNTIME_FORWARD_NAMES[@]}"',
461
- );
462
- expect(portForwardScript).toContain(
463
- 'stop:runtime) for_each_forward stop "${RUNTIME_FORWARD_NAMES[@]}"',
468
+ 'start:runtime) for_each_forward start app supabase-gateway',
464
469
  );
470
+ expect(portForwardScript).toContain('for_each_forward stop app supabase-gateway');
465
471
  expect(portForwardScript).toContain(
466
- 'status:runtime) for_each_forward status "${RUNTIME_FORWARD_NAMES[@]}"',
472
+ 'status:runtime) for_each_forward status app supabase-gateway',
467
473
  );
468
474
  expect(portForwardScript).toContain('${PROFILE}-${1}.pid');
469
475
  expect(portForwardScript).toContain('crashed stale_pid');
@@ -519,8 +525,139 @@ describe('generateMinikubeBaseArtifacts app-owned cluster model', () => {
519
525
 
520
526
  expect(portForwardScript).toContain('RUNTIME_FORWARD_NAMES=(app)');
521
527
  expect(portForwardScript).toContain('ALL_FORWARD_NAMES=(app)');
528
+ expect(portForwardScript).toContain(
529
+ 'OBSOLETE_FORWARD_NAMES=(supabase-gateway studio db-migration)',
530
+ );
522
531
  expect(portForwardScript).not.toContain('RUNTIME_FORWARD_NAMES=(app supabase-gateway)');
523
532
  });
533
+
534
+ test('reconciles a generated Web topology to the exact native-only desired topology', () => {
535
+ const baseManifest = createAppManifest('topology-upgrade');
536
+ const webResult = generateInfrastructure(createSupabaseManifest(), {
537
+ appManifest: {
538
+ ...baseManifest,
539
+ deploy: { targets: { web: { enabled: true } } },
540
+ },
541
+ });
542
+ const nativeResult = generateInfrastructure(createSupabaseManifest(), {
543
+ appManifest: {
544
+ ...baseManifest,
545
+ deploy: {
546
+ targets: {
547
+ android: { enabled: true, package: 'com.ankh.topologyupgrade' },
548
+ },
549
+ },
550
+ },
551
+ });
552
+ const webPaths = webResult.files.map((file) => file.path);
553
+ const nativePaths = nativeResult.files.map((file) => file.path);
554
+ const webAppConfig = getFile(webResult.files, 'infra/minikube/k8s/app.configmap.yaml');
555
+ const webUpScript = getFile(webResult.files, 'infra/minikube/scripts/up.sh');
556
+ const nativeUpScript = getFile(nativeResult.files, 'infra/minikube/scripts/up.sh');
557
+ const nativePortForwardScript = getFile(
558
+ nativeResult.files,
559
+ 'infra/minikube/scripts/port-forward.sh',
560
+ );
561
+ const exactDelete =
562
+ 'delete deployment/app-runtime service/app-runtime configmap/app-infra-config --ignore-not-found';
563
+
564
+ expect(webPaths).toContain('infra/minikube/k8s/app/deployment.yaml');
565
+ expect(webPaths).toContain('infra/minikube/k8s/app/service.yaml');
566
+ expect(webPaths).toContain('infra/minikube/k8s/app.configmap.yaml');
567
+ expect(webAppConfig).toContain('name: app-infra-config');
568
+ expect(getFile(webResult.files, 'infra/minikube/scripts/port-forward.sh')).toContain(
569
+ 'RUNTIME_FORWARD_NAMES=(app supabase-gateway)',
570
+ );
571
+
572
+ expect(nativePaths).not.toContain('infra/minikube/k8s/app/deployment.yaml');
573
+ expect(nativePaths).not.toContain('infra/minikube/k8s/app/service.yaml');
574
+ expect(nativePaths).not.toContain('infra/minikube/k8s/app.configmap.yaml');
575
+ expect(nativeUpScript).toContain(exactDelete);
576
+ expect(nativeUpScript).toContain('"${PORT_FORWARD_SCRIPT}" stop obsolete');
577
+ expect(nativeUpScript.indexOf(exactDelete)).toBeLessThan(
578
+ nativeUpScript.indexOf('"${PORT_FORWARD_SCRIPT}" stop obsolete'),
579
+ );
580
+ expect(nativePortForwardScript).toContain(
581
+ 'MANAGED_FORWARD_NAMES=(app supabase-gateway studio db-migration)',
582
+ );
583
+ expect(nativePortForwardScript).toContain('OBSOLETE_FORWARD_NAMES=(app)');
584
+ expect(webUpScript).not.toContain(exactDelete);
585
+ expect(webUpScript).not.toContain('stop obsolete');
586
+ });
587
+
588
+ test('derives native-only runtime topology without a fictional Kubernetes app service', () => {
589
+ const appManifest = {
590
+ ...createAppManifest('native-only'),
591
+ deploy: {
592
+ targets: {
593
+ android: { enabled: true, package: 'com.ankh.nativeonly' },
594
+ ios: { enabled: true, bundleIdentifier: 'com.ankh.nativeonly' },
595
+ },
596
+ },
597
+ };
598
+ const result = generateInfrastructure(createSupabaseManifest(), { appManifest });
599
+ const paths = result.files.map((file) => file.path);
600
+ const kustomization = getFile(result.files, 'infra/minikube/k8s/kustomization.yaml');
601
+ const portForwardScript = getFile(result.files, 'infra/minikube/scripts/port-forward.sh');
602
+ const upScript = getFile(result.files, 'infra/minikube/scripts/up.sh');
603
+ const readme = getFile(result.files, 'infra/minikube/README.md');
604
+
605
+ expect(paths).not.toContain('infra/minikube/k8s/app/deployment.yaml');
606
+ expect(paths).not.toContain('infra/minikube/k8s/app/service.yaml');
607
+ expect(paths).not.toContain('infra/minikube/k8s/app.configmap.yaml');
608
+ expect(kustomization).not.toContain('app/deployment.yaml');
609
+ expect(kustomization).not.toContain('app/service.yaml');
610
+ expect(portForwardScript).toContain('RUNTIME_FORWARD_NAMES=(supabase-gateway)');
611
+ expect(portForwardScript).toContain('ALL_FORWARD_NAMES=(supabase-gateway studio db-migration)');
612
+ expect(portForwardScript).toContain(
613
+ 'MANAGED_FORWARD_NAMES=(app supabase-gateway studio db-migration)',
614
+ );
615
+ expect(portForwardScript).toContain('OBSOLETE_FORWARD_NAMES=(app)');
616
+ expect(portForwardScript).toContain('${name}: skipped (Web app runtime disabled)');
617
+ expect(upScript).toContain('APP_RUNTIME_ENABLED="false"');
618
+ expect(upScript).toContain(
619
+ 'delete deployment/app-runtime service/app-runtime configmap/app-infra-config --ignore-not-found',
620
+ );
621
+ expect(readme).toContain('disabled (no enabled Web deploy target)');
622
+ expect(JSON.stringify(result.files)).not.toContain('adb');
623
+ expect(JSON.stringify(result.files)).not.toContain('10.0.2.2');
624
+ });
625
+
626
+ test('keeps legacy manifests without deploy targets on the Web app runtime topology', () => {
627
+ const result = generateInfrastructure(createSupabaseManifest(), {
628
+ appManifest: createAppManifest('legacy-web'),
629
+ });
630
+ const paths = result.files.map((file) => file.path);
631
+ const portForwardScript = getFile(result.files, 'infra/minikube/scripts/port-forward.sh');
632
+ const upScript = getFile(result.files, 'infra/minikube/scripts/up.sh');
633
+
634
+ expect(paths).toContain('infra/minikube/k8s/app/service.yaml');
635
+ expect(portForwardScript).toContain('RUNTIME_FORWARD_NAMES=(app supabase-gateway)');
636
+ expect(portForwardScript).toContain('OBSOLETE_FORWARD_NAMES=()');
637
+ expect(upScript).not.toContain('delete deployment/app-runtime');
638
+ });
639
+
640
+ test('generates an empty provider-agnostic runtime group when no host target exists', () => {
641
+ const appManifest = {
642
+ ...createAppManifest('native-no-provider'),
643
+ deploy: {
644
+ targets: {
645
+ android: { enabled: true, package: 'com.ankh.nativeproviderless' },
646
+ },
647
+ },
648
+ };
649
+ const result = generateInfrastructure(
650
+ { deployment: { target: 'minikube', monitoring: false }, modules: [] },
651
+ { appManifest },
652
+ );
653
+ const portForwardScript = getFile(result.files, 'infra/minikube/scripts/port-forward.sh');
654
+
655
+ expect(portForwardScript).toContain('RUNTIME_FORWARD_NAMES=()');
656
+ expect(portForwardScript).toContain('ALL_FORWARD_NAMES=()');
657
+ expect(portForwardScript).toContain(
658
+ 'OBSOLETE_FORWARD_NAMES=(app supabase-gateway studio db-migration)',
659
+ );
660
+ });
524
661
  });
525
662
 
526
663
  function createSupabaseManifest(): InfraManifestInput {