@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ankhorage/infra
2
2
 
3
+ ## 4.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - b25415f: Emit Minikube README and Kong templates in their canonical generated format.
8
+
9
+ ## 4.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 591cecb: Derive Minikube app runtime resources and canonical forward groups from enabled app deployment targets, reconcile obsolete Web runtime resources and owned forwards during native-only upgrades, and retry transient post-restart forwarding failures so native apps can recover provider endpoints without a nonexistent app Service.
14
+
3
15
  ## 4.1.0
4
16
 
5
17
  ### Minor Changes
@@ -5,6 +5,7 @@ export declare const APP_NAMESPACE = "app";
5
5
  export declare function generateMinikubeBaseArtifacts(args: {
6
6
  manifest: InfraManifestInput;
7
7
  appSlug: string;
8
+ appRuntimeEnabled: boolean;
8
9
  extraResources: string[];
9
10
  providerNamespaces: string[];
10
11
  providerLifecycle: MinikubeProviderLifecycle[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/minikube/base/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAG9D,eAAO,MAAM,aAAa,QAAQ,CAAC;AAoDnC,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,EAAE,yBAAyB,EAAE,CAAC;IAC/C,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,GAAG,2BAA2B,EAAE,CAoLhC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/minikube/base/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAG9D,eAAO,MAAM,aAAa,QAAQ,CAAC;AAoDnC,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,EAAE,yBAAyB,EAAE,CAAC;IAC/C,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,GAAG,2BAA2B,EAAE,CA8LhC"}
@@ -18,7 +18,7 @@ const PORT_BUCKET_SIZE = 20;
18
18
  const PORT_BUCKET_COUNT = 1000;
19
19
  const PORT_REFERENCE_SLUG = 'my-app';
20
20
  export function generateMinikubeBaseArtifacts(args) {
21
- const { manifest, appSlug, extraResources, providerNamespaces, providerLifecycle, extraEnvEntries, } = args;
21
+ const { manifest, appSlug, appRuntimeEnabled, extraResources, providerNamespaces, providerLifecycle, extraEnvEntries, } = args;
22
22
  const root = 'infra/minikube';
23
23
  const k8sRoot = `${root}/k8s`;
24
24
  const scriptsRoot = `${root}/scripts`;
@@ -50,6 +50,7 @@ export function generateMinikubeBaseArtifacts(args) {
50
50
  path: `${root}/README.md`,
51
51
  content: getReadmeMarkdown({
52
52
  appSlug,
53
+ appRuntimeEnabled,
53
54
  defaultAppImage,
54
55
  monitoringEnabled,
55
56
  authProvider,
@@ -91,32 +92,37 @@ export function generateMinikubeBaseArtifacts(args) {
91
92
  path: `${k8sRoot}/namespaces/${namespace}.yaml`,
92
93
  content: getNamespaceManifest(namespace),
93
94
  })),
94
- {
95
- path: `${k8sRoot}/app.configmap.yaml`,
96
- content: getAppConfigMap({
97
- appSlug,
98
- authScope,
99
- authProvider,
100
- databaseProvider,
101
- secretStoreProvider,
102
- storageMetadata,
103
- monitoringEnabled,
104
- domain,
105
- }),
106
- },
107
- {
108
- path: `${k8sRoot}/app/deployment.yaml`,
109
- content: getAppDeploymentManifest(defaultAppImage),
110
- },
111
- {
112
- path: `${k8sRoot}/app/service.yaml`,
113
- content: getAppServiceManifest(),
114
- },
95
+ ...(appRuntimeEnabled
96
+ ? [
97
+ {
98
+ path: `${k8sRoot}/app.configmap.yaml`,
99
+ content: getAppConfigMap({
100
+ appSlug,
101
+ authScope,
102
+ authProvider,
103
+ databaseProvider,
104
+ secretStoreProvider,
105
+ storageMetadata,
106
+ monitoringEnabled,
107
+ domain,
108
+ }),
109
+ },
110
+ {
111
+ path: `${k8sRoot}/app/deployment.yaml`,
112
+ content: getAppDeploymentManifest(defaultAppImage),
113
+ },
114
+ {
115
+ path: `${k8sRoot}/app/service.yaml`,
116
+ content: getAppServiceManifest(),
117
+ },
118
+ ]
119
+ : []),
115
120
  ...supabaseResources,
116
121
  {
117
122
  path: `${k8sRoot}/kustomization.yaml`,
118
123
  content: getKustomizationManifest({
119
124
  namespaceResources,
125
+ appRuntimeEnabled,
120
126
  supabaseEnabled: supabaseKubernetesEnabled,
121
127
  extraResources,
122
128
  }),
@@ -125,6 +131,7 @@ export function generateMinikubeBaseArtifacts(args) {
125
131
  path: `${scriptsRoot}/up.sh`,
126
132
  content: getUpScript({
127
133
  appSlug,
134
+ appRuntimeEnabled,
128
135
  defaultAppImage,
129
136
  supabaseKubernetesEnabled,
130
137
  profileModel,
@@ -144,6 +151,7 @@ export function generateMinikubeBaseArtifacts(args) {
144
151
  path: `${scriptsRoot}/port-forward.sh`,
145
152
  content: getPortForwardScript({
146
153
  appSlug,
154
+ appRuntimeEnabled,
147
155
  supabaseKubernetesEnabled,
148
156
  supabaseHostPorts,
149
157
  }),
@@ -172,6 +180,7 @@ export function generateMinikubeBaseArtifacts(args) {
172
180
  path: `${scriptsRoot}/status.sh`,
173
181
  content: getStatusScript({
174
182
  appSlug,
183
+ appRuntimeEnabled,
175
184
  supabaseKubernetesEnabled,
176
185
  profileModel,
177
186
  supabaseHostPorts,
@@ -187,14 +196,12 @@ export function generateMinikubeBaseArtifacts(args) {
187
196
  ];
188
197
  }
189
198
  function getReadmeMarkdown(args) {
190
- const { appSlug, defaultAppImage, monitoringEnabled, authProvider, authzEngine, extraResources, supabaseHostPorts, supabaseKubernetesEnabled, profileModel, oauthProviders, providerNamespaces, providerLifecycle, } = args;
199
+ const { appSlug, appRuntimeEnabled, defaultAppImage, monitoringEnabled, authProvider, authzEngine, extraResources, supabaseHostPorts, supabaseKubernetesEnabled, profileModel, oauthProviders, providerNamespaces, providerLifecycle, } = args;
191
200
  const resourceLines = [
192
201
  'namespaces/app.yaml',
193
202
  ...(supabaseKubernetesEnabled ? ['namespaces/supabase.yaml'] : []),
194
203
  ...providerNamespaces.map((namespace) => `namespaces/${namespace}.yaml`),
195
- 'app.configmap.yaml',
196
- 'app/deployment.yaml',
197
- 'app/service.yaml',
204
+ ...(appRuntimeEnabled ? ['app.configmap.yaml', 'app/deployment.yaml', 'app/service.yaml'] : []),
198
205
  ...(supabaseKubernetesEnabled
199
206
  ? [
200
207
  'supabase/bootstrap.sql',
@@ -229,7 +236,7 @@ function getReadmeMarkdown(args) {
229
236
  ? `- Provider namespaces: \`${providerNamespaces.join('`, `')}\`\n`
230
237
  : '';
231
238
  const providerLifecycleLines = providerLifecycle.length > 0
232
- ? `\n## Provider Lifecycle Contributions\n\n${providerLifecycle
239
+ ? `## Provider Lifecycle Contributions\n\n${providerLifecycle
233
240
  .map((provider) => {
234
241
  const readiness = provider.readinessChecks.length > 0
235
242
  ? provider.readinessChecks
@@ -243,11 +250,18 @@ function getReadmeMarkdown(args) {
243
250
  : 'none';
244
251
  return `- \`${provider.id}\`: namespace \`${provider.namespace}\`; readiness ${readiness}; endpoints ${endpoints}`;
245
252
  })
246
- .join('\n')}\n`
253
+ .join('\n')}`
247
254
  : '';
248
255
  const oauthDescription = oauthProviders.length > 0
249
- ? `\nConfigured OAuth providers are wired into GoTrue through generated \`GOTRUE_EXTERNAL_*\` runtime keys. The keys are named in \`.env.example\`; values must come from trusted secret resolution for the configured \`credentialsRef\` entries.\n`
256
+ ? `\nConfigured OAuth providers are wired into GoTrue through generated \`GOTRUE_EXTERNAL_*\` runtime keys. The keys are named in \`.env.example\`; values must come from trusted secret resolution for the configured \`credentialsRef\` entries.`
250
257
  : '';
258
+ const runtimeForwardDescription = appRuntimeEnabled
259
+ ? supabaseKubernetesEnabled
260
+ ? '`app` and `supabase-gateway`'
261
+ : '`app`'
262
+ : supabaseKubernetesEnabled
263
+ ? '`supabase-gateway`'
264
+ : 'no host endpoints';
251
265
  return `# Minikube Infra
252
266
 
253
267
  This directory is generated from \`ankh.config.json\` (infra manifest).
@@ -255,7 +269,7 @@ This directory is generated from \`ankh.config.json\` (infra manifest).
255
269
  ## Ownership Model
256
270
 
257
271
  - App slug / Minikube profile: \`${appSlug}\`
258
- - App runtime namespace: \`${APP_NAMESPACE}\`
272
+ - App runtime: \`${appRuntimeEnabled ? `deployment/app-runtime in namespace ${APP_NAMESPACE}` : 'disabled (no enabled Web deploy target)'}\`
259
273
  - Supabase namespace: \`${supabaseKubernetesEnabled ? SUPABASE_NAMESPACE : 'unused'}\`
260
274
  ${providerNamespaceLine}- Default app image: \`${defaultAppImage}\`
261
275
 
@@ -285,14 +299,14 @@ host-level Supabase Compose runtime.
285
299
 
286
300
  ## Lifecycle Semantics
287
301
 
288
- - \`up.sh\`: starts \`minikube -p ${appSlug}\`, deploys provider namespaces, runs migrations with CLI telemetry disabled for that command, starts slug-owned port-forwards, and deploys the app runtime.
302
+ - \`up.sh\`: starts \`minikube -p ${appSlug}\`, deploys provider namespaces, runs migrations with CLI telemetry disabled for that command, starts slug-owned port-forwards${appRuntimeEnabled ? ', and deploys the Web app runtime' : ''}.
289
303
  - \`down.sh\`: stops slug-owned port-forwards, then stops \`minikube -p ${appSlug}\`. Persistent data remains in the profile.
290
304
  - \`reset.sh\`: requires \`ANKH_RESET_CONFIRM=${appSlug}\`; ${resetDescription}. It does not delete the Minikube profile.
291
305
  - \`destroy.sh\`: deletes only \`minikube -p ${appSlug}\`.
292
306
 
293
307
  ### Port-forward groups
294
308
 
295
- - \`./scripts/port-forward.sh start runtime\` idempotently ensures ${supabaseKubernetesEnabled ? '`app` and `supabase-gateway`' : '`app` only'}, the host endpoints required by the running application. The same group supports \`stop\` and \`status\`.
309
+ - \`./scripts/port-forward.sh start runtime\` idempotently ensures ${runtimeForwardDescription}, the generated host endpoints required by the running application. The same group supports \`stop\` and \`status\`.
296
310
  - \`runtime\` excludes \`studio\` and \`db-migration\`, which are operational/bootstrap endpoints.
297
311
  - \`all\` retains every generated forward, including operational/bootstrap endpoints.
298
312
  - Concrete targets remain available for individual lifecycle control.
@@ -331,8 +345,7 @@ Pinned images:
331
345
 
332
346
  ## Host URLs
333
347
 
334
- - App: \`http://127.0.0.1:${supabaseHostPorts.app}\`
335
- - Supabase gateway: \`http://127.0.0.1:${supabaseHostPorts.gateway}\`
348
+ ${appRuntimeEnabled ? `- App: \`http://127.0.0.1:${supabaseHostPorts.app}\`\n` : ''}- Supabase gateway: \`http://127.0.0.1:${supabaseHostPorts.gateway}\`
336
349
  - Studio: \`http://127.0.0.1:${supabaseHostPorts.studio}\`
337
350
  - DB migration endpoint: \`127.0.0.1:${supabaseHostPorts.db}\`
338
351
 
@@ -748,9 +761,9 @@ function getKustomizationManifest(args) {
748
761
  : [];
749
762
  const resources = [
750
763
  ...args.namespaceResources,
751
- 'app.configmap.yaml',
752
- 'app/deployment.yaml',
753
- 'app/service.yaml',
764
+ ...(args.appRuntimeEnabled
765
+ ? ['app.configmap.yaml', 'app/deployment.yaml', 'app/service.yaml']
766
+ : []),
754
767
  ...supabaseResources,
755
768
  ...args.extraResources,
756
769
  ];
@@ -1566,7 +1579,7 @@ spec:
1566
1579
  `;
1567
1580
  }
1568
1581
  function getSupabaseGatewayTemplate() {
1569
- return `_format_version: '2.1'
1582
+ return `_format_version: "2.1"
1570
1583
  _transform: true
1571
1584
 
1572
1585
  consumers:
@@ -1588,8 +1601,8 @@ acls:
1588
1601
 
1589
1602
  basicauth_credentials:
1590
1603
  - consumer: DASHBOARD
1591
- username: '$DASHBOARD_USERNAME'
1592
- password: '$DASHBOARD_PASSWORD'
1604
+ username: "$DASHBOARD_USERNAME"
1605
+ password: "$DASHBOARD_PASSWORD"
1593
1606
 
1594
1607
  services:
1595
1608
  - name: auth-v1-open
@@ -2004,7 +2017,7 @@ spec:
2004
2017
  `;
2005
2018
  }
2006
2019
  function getUpScript(args) {
2007
- const { appSlug, defaultAppImage, supabaseKubernetesEnabled, profileModel, secretStoreProvider, supabaseHostPorts, oauthProviders, providerLifecycle, } = args;
2020
+ const { appSlug, appRuntimeEnabled, defaultAppImage, supabaseKubernetesEnabled, profileModel, secretStoreProvider, supabaseHostPorts, oauthProviders, providerLifecycle, } = args;
2008
2021
  const oauthEnvDefaults = getOAuthScriptEnvDefaults(oauthProviders);
2009
2022
  const oauthRuntimeSecretLines = getOAuthRuntimeSecretLines(oauthProviders);
2010
2023
  const oauthValidation = getOAuthRuntimeValidationScript(oauthProviders);
@@ -2016,6 +2029,14 @@ function getUpScript(args) {
2016
2029
  secretStoreProvider,
2017
2030
  });
2018
2031
  const supabaseMigrationCommand = getSupabaseMigrationCommandScript();
2032
+ const disabledAppRuntimeReconciliation = appRuntimeEnabled
2033
+ ? ''
2034
+ : `reconcile_disabled_app_runtime() {
2035
+ kubectl --context "\${PROFILE}" -n "\${APP_NAMESPACE}" delete deployment/app-runtime service/app-runtime configmap/app-infra-config --ignore-not-found
2036
+ "\${PORT_FORWARD_SCRIPT}" stop obsolete
2037
+ }
2038
+ `;
2039
+ const reconcileDisabledAppRuntime = appRuntimeEnabled ? '' : 'reconcile_disabled_app_runtime';
2019
2040
  return `#!/usr/bin/env bash
2020
2041
  set -Eeuo pipefail
2021
2042
 
@@ -2030,6 +2051,7 @@ DRIVER="\${MINIKUBE_DRIVER:-}"
2030
2051
  APP_NAMESPACE="${APP_NAMESPACE}"
2031
2052
  SUPABASE_NAMESPACE="${SUPABASE_NAMESPACE}"
2032
2053
  SUPABASE_RUNTIME_ENABLED="${supabaseKubernetesEnabled ? 'true' : 'false'}"
2054
+ APP_RUNTIME_ENABLED="${appRuntimeEnabled ? 'true' : 'false'}"
2033
2055
  APP_BUILD_ENABLED="\${APP_BUILD_ENABLED:-}"
2034
2056
  APP_SOURCE_DIR="\${APP_SOURCE_DIR:-}"
2035
2057
  APP_WEB_EXPORT_DIR="\${APP_WEB_EXPORT_DIR:-}"
@@ -2460,6 +2482,7 @@ apply_namespace_manifests() {
2460
2482
  done
2461
2483
  }
2462
2484
 
2485
+ ${disabledAppRuntimeReconciliation}
2463
2486
  ${providerLifecycleStageScripts}
2464
2487
 
2465
2488
  load_env_file_preserving_process_env
@@ -2533,7 +2556,7 @@ else
2533
2556
  kubectl --context "\${PROFILE}" apply -k "\${K8S_DIR}"
2534
2557
  fi
2535
2558
 
2536
- if [[ "\${APP_BUILD_ENABLED}" == "true" ]]; then
2559
+ if [[ "\${APP_RUNTIME_ENABLED}" == "true" && "\${APP_BUILD_ENABLED}" == "true" ]]; then
2537
2560
  if [[ ! -x "\${BUILD_SCRIPT}" ]]; then
2538
2561
  echo "Missing build helper script: \${BUILD_SCRIPT}"
2539
2562
  exit 1
@@ -2548,6 +2571,7 @@ else
2548
2571
  EXPORT_DIR="\${APP_SOURCE_DIR}/\${APP_WEB_EXPORT_DIR}"
2549
2572
  fi
2550
2573
 
2574
+ if [[ "\${APP_RUNTIME_ENABLED}" == "true" ]]; then
2551
2575
  case "\${APP_IMAGE_SYNC_STRATEGY}" in
2552
2576
  minikube-build)
2553
2577
  if [[ ! -f "\${EXPORT_DIR}/index.html" ]]; then
@@ -2569,15 +2593,18 @@ case "\${APP_IMAGE_SYNC_STRATEGY}" in
2569
2593
  exit 1
2570
2594
  ;;
2571
2595
  esac
2596
+ fi
2572
2597
 
2573
2598
  if [[ "\${SUPABASE_RUNTIME_ENABLED}" == "true" ]]; then
2574
2599
  kubectl --context "\${PROFILE}" apply -k "\${K8S_DIR}"
2575
2600
  fi
2576
2601
 
2602
+ ${reconcileDisabledAppRuntime}
2577
2603
  wait_for_provider_readiness
2578
2604
  run_provider_migrations
2579
2605
  run_provider_reconciliation
2580
2606
 
2607
+ if [[ "\${APP_RUNTIME_ENABLED}" == "true" ]]; then
2581
2608
  if [[ -n "\${APP_IMAGE_PULL_SECRET_NAME}" ]]; then
2582
2609
  if [[ -z "\${APP_IMAGE_PULL_SECRET_USERNAME}" || -z "\${APP_IMAGE_PULL_SECRET_PASSWORD}" ]]; then
2583
2610
  echo "APP_IMAGE_PULL_SECRET_NAME is set, but username/password are missing."
@@ -2609,6 +2636,7 @@ fi
2609
2636
 
2610
2637
  kubectl --context "\${PROFILE}" -n "\${APP_NAMESPACE}" rollout status deployment/app-runtime --timeout=180s >/dev/null
2611
2638
  "\${PORT_FORWARD_SCRIPT}" start app >/dev/null
2639
+ fi
2612
2640
 
2613
2641
  trap - ERR
2614
2642
  echo "Minikube infrastructure for '\${PROFILE}' is running."
@@ -2717,12 +2745,37 @@ CMD ["nginx", "-g", "daemon off;"]
2717
2745
  `;
2718
2746
  }
2719
2747
  function getPortForwardScript(args) {
2720
- const { appSlug, supabaseKubernetesEnabled, supabaseHostPorts } = args;
2721
- const runtimeForwardNames = ['app', ...(supabaseKubernetesEnabled ? ['supabase-gateway'] : [])];
2748
+ const { appSlug, appRuntimeEnabled, supabaseKubernetesEnabled, supabaseHostPorts } = args;
2749
+ const runtimeForwardNames = [
2750
+ ...(appRuntimeEnabled ? ['app'] : []),
2751
+ ...(supabaseKubernetesEnabled ? ['supabase-gateway'] : []),
2752
+ ];
2722
2753
  const allForwardNames = [
2723
2754
  ...runtimeForwardNames,
2724
2755
  ...(supabaseKubernetesEnabled ? ['studio', 'db-migration'] : []),
2725
2756
  ];
2757
+ const managedForwardNames = ['app', 'supabase-gateway', 'studio', 'db-migration'];
2758
+ const obsoleteForwardNames = managedForwardNames.filter((name) => !allForwardNames.includes(name));
2759
+ const appTargetCase = ` app)
2760
+ echo "${APP_NAMESPACE} service/app-runtime \${APP_PORT_FORWARD_LOCAL_PORT} \${APP_PORT_FORWARD_REMOTE_PORT}"
2761
+ ;;`;
2762
+ const supabaseTargetCases = ` supabase-gateway)
2763
+ echo "${SUPABASE_NAMESPACE} service/gateway \${SUPABASE_GATEWAY_FORWARD_LOCAL_PORT} \${SUPABASE_GATEWAY_FORWARD_REMOTE_PORT}"
2764
+ ;;
2765
+ studio)
2766
+ echo "${SUPABASE_NAMESPACE} service/studio \${SUPABASE_STUDIO_FORWARD_LOCAL_PORT} \${SUPABASE_STUDIO_FORWARD_REMOTE_PORT}"
2767
+ ;;
2768
+ db-migration)
2769
+ echo "${SUPABASE_NAMESPACE} service/postgres \${SUPABASE_DB_FORWARD_LOCAL_PORT} \${SUPABASE_DB_FORWARD_REMOTE_PORT}"
2770
+ ;;`;
2771
+ const concreteForwardNames = [
2772
+ ...(appRuntimeEnabled ? ['app'] : []),
2773
+ ...(supabaseKubernetesEnabled ? ['supabase-gateway', 'studio', 'db-migration'] : []),
2774
+ ];
2775
+ const forwardUsage = [...concreteForwardNames, 'runtime', 'all'].join('|');
2776
+ const forwardNamesDescription = concreteForwardNames.length > 0
2777
+ ? `${concreteForwardNames.join(', ')}, runtime, or all`
2778
+ : 'runtime or all';
2726
2779
  return `#!/usr/bin/env bash
2727
2780
  set -euo pipefail
2728
2781
 
@@ -2732,6 +2785,7 @@ STATE_DIR="\${ROOT_DIR}/.state/forwards"
2732
2785
  APP_SLUG="${appSlug}"
2733
2786
  PROFILE="\${ANKH_APP_SLUG:-${appSlug}}"
2734
2787
  SUPABASE_RUNTIME_ENABLED="${supabaseKubernetesEnabled ? 'true' : 'false'}"
2788
+ APP_RUNTIME_ENABLED="${appRuntimeEnabled ? 'true' : 'false'}"
2735
2789
  APP_PORT_FORWARD_LOCAL_PORT="\${APP_PORT_FORWARD_LOCAL_PORT:-${supabaseHostPorts.app}}"
2736
2790
  APP_PORT_FORWARD_REMOTE_PORT="\${APP_PORT_FORWARD_REMOTE_PORT:-80}"
2737
2791
  SUPABASE_GATEWAY_FORWARD_LOCAL_PORT="\${SUPABASE_GATEWAY_FORWARD_LOCAL_PORT:-${supabaseHostPorts.gateway}}"
@@ -2742,6 +2796,8 @@ SUPABASE_DB_FORWARD_LOCAL_PORT="\${SUPABASE_DB_FORWARD_LOCAL_PORT:-${supabaseHos
2742
2796
  SUPABASE_DB_FORWARD_REMOTE_PORT="\${SUPABASE_DB_FORWARD_REMOTE_PORT:-5432}"
2743
2797
  RUNTIME_FORWARD_NAMES=(${runtimeForwardNames.join(' ')})
2744
2798
  ALL_FORWARD_NAMES=(${allForwardNames.join(' ')})
2799
+ MANAGED_FORWARD_NAMES=(${managedForwardNames.join(' ')})
2800
+ OBSOLETE_FORWARD_NAMES=(${obsoleteForwardNames.join(' ')})
2745
2801
 
2746
2802
  if [[ -f "\${ROOT_DIR}/.env" ]]; then
2747
2803
  set -a
@@ -2750,6 +2806,7 @@ if [[ -f "\${ROOT_DIR}/.env" ]]; then
2750
2806
  set +a
2751
2807
  fi
2752
2808
  SUPABASE_RUNTIME_ENABLED="${supabaseKubernetesEnabled ? 'true' : 'false'}"
2809
+ APP_RUNTIME_ENABLED="${appRuntimeEnabled ? 'true' : 'false'}"
2753
2810
 
2754
2811
  if [[ "\${PROFILE}" != "\${APP_SLUG}" ]]; then
2755
2812
  echo "ANKH_APP_SLUG must remain the generated canonical slug '\${APP_SLUG}' for this infra directory."
@@ -2769,20 +2826,10 @@ pid_file_for() {
2769
2826
 
2770
2827
  target_for() {
2771
2828
  case "\${1}" in
2772
- app)
2773
- echo "${APP_NAMESPACE} service/app-runtime \${APP_PORT_FORWARD_LOCAL_PORT} \${APP_PORT_FORWARD_REMOTE_PORT}"
2774
- ;;
2775
- supabase-gateway)
2776
- echo "${SUPABASE_NAMESPACE} service/gateway \${SUPABASE_GATEWAY_FORWARD_LOCAL_PORT} \${SUPABASE_GATEWAY_FORWARD_REMOTE_PORT}"
2777
- ;;
2778
- studio)
2779
- echo "${SUPABASE_NAMESPACE} service/studio \${SUPABASE_STUDIO_FORWARD_LOCAL_PORT} \${SUPABASE_STUDIO_FORWARD_REMOTE_PORT}"
2780
- ;;
2781
- db-migration)
2782
- echo "${SUPABASE_NAMESPACE} service/postgres \${SUPABASE_DB_FORWARD_LOCAL_PORT} \${SUPABASE_DB_FORWARD_REMOTE_PORT}"
2783
- ;;
2829
+ ${appTargetCase}
2830
+ ${supabaseTargetCases}
2784
2831
  *)
2785
- echo "Unknown forward '\${1}'. Use app, supabase-gateway, studio, db-migration, or all." >&2
2832
+ echo "Unknown forward '\${1}'. Use ${forwardNamesDescription}." >&2
2786
2833
  return 1
2787
2834
  ;;
2788
2835
  esac
@@ -2918,6 +2965,11 @@ start_forward() {
2918
2965
  pid_file="$(pid_file_for "\${name}")"
2919
2966
  read -r namespace resource local_port remote_port <<<"$(target_for "\${name}")"
2920
2967
 
2968
+ if [[ "\${name}" == "app" && "\${APP_RUNTIME_ENABLED}" != "true" ]]; then
2969
+ echo "\${name}: skipped (Web app runtime disabled)"
2970
+ return 0
2971
+ fi
2972
+
2921
2973
  if [[ -f "\${pid_file}" ]]; then
2922
2974
  local existing_pid
2923
2975
  existing_pid="$(cat "\${pid_file}")"
@@ -2947,28 +2999,52 @@ start_forward() {
2947
2999
  return 1
2948
3000
  fi
2949
3001
 
2950
- nohup kubectl --context "\${PROFILE}" -n "\${namespace}" port-forward "\${resource}" "\${local_port}:\${remote_port}" >"\${STATE_DIR}/\${PROFILE}-\${name}.log" 2>&1 &
2951
- local pid="$!"
2952
- echo "\${pid}" > "\${pid_file}"
3002
+ for start_attempt in {1..5}; do
3003
+ nohup kubectl --context "\${PROFILE}" -n "\${namespace}" port-forward "\${resource}" "\${local_port}:\${remote_port}" >"\${STATE_DIR}/\${PROFILE}-\${name}.log" 2>&1 &
3004
+ local pid="$!"
3005
+ echo "\${pid}" > "\${pid_file}"
2953
3006
 
2954
- for attempt in {1..40}; do
2955
- if ! is_pid_running "\${pid}"; then
2956
- echo "\${name}: failed to start local port \${local_port} for \${namespace}/\${resource}:\${remote_port}"
2957
- print_forward_log_tail "\${name}"
3007
+ for readiness_attempt in {1..20}; do
3008
+ if ! is_pid_running "\${pid}"; then
3009
+ rm -f "\${pid_file}"
3010
+ if [[ "\${start_attempt}" -lt 5 ]]; then
3011
+ echo "\${name}: port-forward attempt \${start_attempt} exited before readiness; retrying"
3012
+ sleep 2
3013
+ break
3014
+ fi
3015
+
3016
+ echo "\${name}: failed to start local port \${local_port} for \${namespace}/\${resource}:\${remote_port} after \${start_attempt} attempts"
3017
+ print_forward_log_tail "\${name}"
3018
+ return 1
3019
+ fi
3020
+ if is_port_accepting "\${local_port}"; then
3021
+ sleep 1
3022
+ if is_pid_running "\${pid}" && is_port_accepting "\${local_port}"; then
3023
+ sleep 0.5
3024
+ if is_pid_running "\${pid}"; then
3025
+ echo "\${name}: started (pid \${pid}, local port \${local_port})"
3026
+ return 0
3027
+ fi
3028
+ fi
3029
+ fi
3030
+ sleep 0.5
3031
+ done
3032
+
3033
+ if [[ -f "\${pid_file}" ]]; then
3034
+ terminate_forward_process "\${name}" "\${pid}" "\${namespace}" "\${resource}" "\${local_port}" "\${remote_port}" || return 1
2958
3035
  rm -f "\${pid_file}"
2959
- return 1
2960
- fi
2961
- if is_port_accepting "\${local_port}"; then
2962
- echo "\${name}: started (pid \${pid}, local port \${local_port})"
2963
- return 0
3036
+
3037
+ if [[ "\${start_attempt}" -lt 5 ]]; then
3038
+ echo "\${name}: port-forward attempt \${start_attempt} did not become ready; retrying"
3039
+ sleep 2
3040
+ else
3041
+ echo "\${name}: did not become ready on local port \${local_port} for \${namespace}/\${resource}:\${remote_port} after \${start_attempt} attempts"
3042
+ print_forward_log_tail "\${name}"
3043
+ return 1
3044
+ fi
2964
3045
  fi
2965
- sleep 0.5
2966
3046
  done
2967
3047
 
2968
- echo "\${name}: did not become ready on local port \${local_port} for \${namespace}/\${resource}:\${remote_port}"
2969
- print_forward_log_tail "\${name}"
2970
- terminate_forward_process "\${name}" "\${pid}" "\${namespace}" "\${resource}" "\${local_port}" "\${remote_port}" || return 1
2971
- rm -f "\${pid_file}"
2972
3048
  return 1
2973
3049
  }
2974
3050
 
@@ -3029,7 +3105,9 @@ for_each_forward() {
3029
3105
  local action="\${1}"
3030
3106
  shift
3031
3107
  local name
3032
- for name in "\${@}"; do
3108
+ while [[ "\${#}" -gt 0 ]]; do
3109
+ name="\${1}"
3110
+ shift
3033
3111
  "\${action}_forward" "\${name}"
3034
3112
  done
3035
3113
  }
@@ -3038,17 +3116,21 @@ ACTION="\${1:-start}"
3038
3116
  NAME="\${2:-all}"
3039
3117
 
3040
3118
  case "\${ACTION}:\${NAME}" in
3041
- start:runtime) for_each_forward start "\${RUNTIME_FORWARD_NAMES[@]}" ;;
3042
- stop:runtime) for_each_forward stop "\${RUNTIME_FORWARD_NAMES[@]}" ;;
3043
- status:runtime) for_each_forward status "\${RUNTIME_FORWARD_NAMES[@]}" ;;
3044
- start:all) for_each_forward start "\${ALL_FORWARD_NAMES[@]}" ;;
3045
- stop:all) for_each_forward stop "\${ALL_FORWARD_NAMES[@]}" ;;
3046
- status:all) for_each_forward status "\${ALL_FORWARD_NAMES[@]}" ;;
3119
+ start:runtime) for_each_forward start ${runtimeForwardNames.join(' ')} ;;
3120
+ stop:runtime)
3121
+ for_each_forward stop ${runtimeForwardNames.join(' ')}
3122
+ for_each_forward stop ${obsoleteForwardNames.join(' ')}
3123
+ ;;
3124
+ status:runtime) for_each_forward status ${runtimeForwardNames.join(' ')} ;;
3125
+ start:all) for_each_forward start ${allForwardNames.join(' ')} ;;
3126
+ stop:all) for_each_forward stop ${managedForwardNames.join(' ')} ;;
3127
+ status:all) for_each_forward status ${allForwardNames.join(' ')} ;;
3128
+ stop:obsolete) for_each_forward stop ${obsoleteForwardNames.join(' ')} ;;
3047
3129
  start:*) start_forward "\${NAME}" ;;
3048
3130
  stop:*) stop_forward "\${NAME}" ;;
3049
3131
  status:*) status_forward "\${NAME}" ;;
3050
3132
  *)
3051
- echo "Usage: ./scripts/port-forward.sh {start|stop|status} {app|supabase-gateway|studio|db-migration|runtime|all}"
3133
+ echo "Usage: ./scripts/port-forward.sh {start|stop|status} {${forwardUsage}}"
3052
3134
  exit 1
3053
3135
  ;;
3054
3136
  esac
@@ -3166,7 +3248,7 @@ echo "Deleted Minikube profile '\${PROFILE}'."
3166
3248
  `;
3167
3249
  }
3168
3250
  function getStatusScript(args) {
3169
- const { appSlug, supabaseKubernetesEnabled, profileModel, supabaseHostPorts, providerNamespaces, providerLifecycle, } = args;
3251
+ const { appSlug, appRuntimeEnabled, supabaseKubernetesEnabled, profileModel, supabaseHostPorts, providerNamespaces, providerLifecycle, } = args;
3170
3252
  const namespaces = [
3171
3253
  APP_NAMESPACE,
3172
3254
  ...(supabaseKubernetesEnabled ? [SUPABASE_NAMESPACE] : []),
@@ -3244,7 +3326,7 @@ for namespace in ${namespaces}; do
3244
3326
  fi
3245
3327
  done
3246
3328
 
3247
- if kubectl --context "\${PROFILE}" -n ${APP_NAMESPACE} get deployment app-runtime >/dev/null 2>&1; then
3329
+ if [[ "${appRuntimeEnabled ? 'true' : 'false'}" == "true" ]] && kubectl --context "\${PROFILE}" -n ${APP_NAMESPACE} get deployment app-runtime >/dev/null 2>&1; then
3248
3330
  kubectl --context "\${PROFILE}" -n ${APP_NAMESPACE} rollout status deployment/app-runtime --timeout=5s >/dev/null 2>&1 && echo "- app-runtime: ready" || echo "- app-runtime: not ready"
3249
3331
  fi
3250
3332