@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 +12 -0
- package/dist/adapters/minikube/base/index.d.ts +1 -0
- package/dist/adapters/minikube/base/index.d.ts.map +1 -1
- package/dist/adapters/minikube/base/index.js +166 -84
- package/dist/adapters/minikube/base/index.js.map +1 -1
- package/dist/adapters/minikube/index.d.ts.map +1 -1
- package/dist/adapters/minikube/index.js +3 -0
- package/dist/adapters/minikube/index.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/minikube/README.md +18 -8
- package/src/adapters/minikube/base/index.test.ts +143 -6
- package/src/adapters/minikube/base/index.ts +176 -82
- package/src/adapters/minikube/base/portForwardRuntime.test.ts +355 -19
- package/src/adapters/minikube/base/upgradeReconciliation.test.ts +135 -0
- package/src/adapters/minikube/index.ts +4 -0
|
@@ -60,6 +60,7 @@ const PORT_REFERENCE_SLUG = 'my-app';
|
|
|
60
60
|
export function generateMinikubeBaseArtifacts(args: {
|
|
61
61
|
manifest: InfraManifestInput;
|
|
62
62
|
appSlug: string;
|
|
63
|
+
appRuntimeEnabled: boolean;
|
|
63
64
|
extraResources: string[];
|
|
64
65
|
providerNamespaces: string[];
|
|
65
66
|
providerLifecycle: MinikubeProviderLifecycle[];
|
|
@@ -68,6 +69,7 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
68
69
|
const {
|
|
69
70
|
manifest,
|
|
70
71
|
appSlug,
|
|
72
|
+
appRuntimeEnabled,
|
|
71
73
|
extraResources,
|
|
72
74
|
providerNamespaces,
|
|
73
75
|
providerLifecycle,
|
|
@@ -109,6 +111,7 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
109
111
|
path: `${root}/README.md`,
|
|
110
112
|
content: getReadmeMarkdown({
|
|
111
113
|
appSlug,
|
|
114
|
+
appRuntimeEnabled,
|
|
112
115
|
defaultAppImage,
|
|
113
116
|
monitoringEnabled,
|
|
114
117
|
authProvider,
|
|
@@ -150,32 +153,37 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
150
153
|
path: `${k8sRoot}/namespaces/${namespace}.yaml`,
|
|
151
154
|
content: getNamespaceManifest(namespace),
|
|
152
155
|
})),
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
156
|
+
...(appRuntimeEnabled
|
|
157
|
+
? [
|
|
158
|
+
{
|
|
159
|
+
path: `${k8sRoot}/app.configmap.yaml`,
|
|
160
|
+
content: getAppConfigMap({
|
|
161
|
+
appSlug,
|
|
162
|
+
authScope,
|
|
163
|
+
authProvider,
|
|
164
|
+
databaseProvider,
|
|
165
|
+
secretStoreProvider,
|
|
166
|
+
storageMetadata,
|
|
167
|
+
monitoringEnabled,
|
|
168
|
+
domain,
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
path: `${k8sRoot}/app/deployment.yaml`,
|
|
173
|
+
content: getAppDeploymentManifest(defaultAppImage),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
path: `${k8sRoot}/app/service.yaml`,
|
|
177
|
+
content: getAppServiceManifest(),
|
|
178
|
+
},
|
|
179
|
+
]
|
|
180
|
+
: []),
|
|
174
181
|
...supabaseResources,
|
|
175
182
|
{
|
|
176
183
|
path: `${k8sRoot}/kustomization.yaml`,
|
|
177
184
|
content: getKustomizationManifest({
|
|
178
185
|
namespaceResources,
|
|
186
|
+
appRuntimeEnabled,
|
|
179
187
|
supabaseEnabled: supabaseKubernetesEnabled,
|
|
180
188
|
extraResources,
|
|
181
189
|
}),
|
|
@@ -184,6 +192,7 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
184
192
|
path: `${scriptsRoot}/up.sh`,
|
|
185
193
|
content: getUpScript({
|
|
186
194
|
appSlug,
|
|
195
|
+
appRuntimeEnabled,
|
|
187
196
|
defaultAppImage,
|
|
188
197
|
supabaseKubernetesEnabled,
|
|
189
198
|
profileModel,
|
|
@@ -203,6 +212,7 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
203
212
|
path: `${scriptsRoot}/port-forward.sh`,
|
|
204
213
|
content: getPortForwardScript({
|
|
205
214
|
appSlug,
|
|
215
|
+
appRuntimeEnabled,
|
|
206
216
|
supabaseKubernetesEnabled,
|
|
207
217
|
supabaseHostPorts,
|
|
208
218
|
}),
|
|
@@ -231,6 +241,7 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
231
241
|
path: `${scriptsRoot}/status.sh`,
|
|
232
242
|
content: getStatusScript({
|
|
233
243
|
appSlug,
|
|
244
|
+
appRuntimeEnabled,
|
|
234
245
|
supabaseKubernetesEnabled,
|
|
235
246
|
profileModel,
|
|
236
247
|
supabaseHostPorts,
|
|
@@ -248,6 +259,7 @@ export function generateMinikubeBaseArtifacts(args: {
|
|
|
248
259
|
|
|
249
260
|
function getReadmeMarkdown(args: {
|
|
250
261
|
appSlug: string;
|
|
262
|
+
appRuntimeEnabled: boolean;
|
|
251
263
|
defaultAppImage: string;
|
|
252
264
|
monitoringEnabled: boolean;
|
|
253
265
|
authProvider: string;
|
|
@@ -262,6 +274,7 @@ function getReadmeMarkdown(args: {
|
|
|
262
274
|
}): string {
|
|
263
275
|
const {
|
|
264
276
|
appSlug,
|
|
277
|
+
appRuntimeEnabled,
|
|
265
278
|
defaultAppImage,
|
|
266
279
|
monitoringEnabled,
|
|
267
280
|
authProvider,
|
|
@@ -278,9 +291,7 @@ function getReadmeMarkdown(args: {
|
|
|
278
291
|
'namespaces/app.yaml',
|
|
279
292
|
...(supabaseKubernetesEnabled ? ['namespaces/supabase.yaml'] : []),
|
|
280
293
|
...providerNamespaces.map((namespace) => `namespaces/${namespace}.yaml`),
|
|
281
|
-
'app.configmap.yaml',
|
|
282
|
-
'app/deployment.yaml',
|
|
283
|
-
'app/service.yaml',
|
|
294
|
+
...(appRuntimeEnabled ? ['app.configmap.yaml', 'app/deployment.yaml', 'app/service.yaml'] : []),
|
|
284
295
|
...(supabaseKubernetesEnabled
|
|
285
296
|
? [
|
|
286
297
|
'supabase/bootstrap.sql',
|
|
@@ -320,7 +331,7 @@ function getReadmeMarkdown(args: {
|
|
|
320
331
|
: '';
|
|
321
332
|
const providerLifecycleLines =
|
|
322
333
|
providerLifecycle.length > 0
|
|
323
|
-
?
|
|
334
|
+
? `## Provider Lifecycle Contributions\n\n${providerLifecycle
|
|
324
335
|
.map((provider) => {
|
|
325
336
|
const readiness =
|
|
326
337
|
provider.readinessChecks.length > 0
|
|
@@ -336,13 +347,21 @@ function getReadmeMarkdown(args: {
|
|
|
336
347
|
: 'none';
|
|
337
348
|
return `- \`${provider.id}\`: namespace \`${provider.namespace}\`; readiness ${readiness}; endpoints ${endpoints}`;
|
|
338
349
|
})
|
|
339
|
-
.join('\n')}
|
|
350
|
+
.join('\n')}`
|
|
340
351
|
: '';
|
|
341
352
|
const oauthDescription =
|
|
342
353
|
oauthProviders.length > 0
|
|
343
|
-
? `\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
|
|
354
|
+
? `\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.`
|
|
344
355
|
: '';
|
|
345
356
|
|
|
357
|
+
const runtimeForwardDescription = appRuntimeEnabled
|
|
358
|
+
? supabaseKubernetesEnabled
|
|
359
|
+
? '`app` and `supabase-gateway`'
|
|
360
|
+
: '`app`'
|
|
361
|
+
: supabaseKubernetesEnabled
|
|
362
|
+
? '`supabase-gateway`'
|
|
363
|
+
: 'no host endpoints';
|
|
364
|
+
|
|
346
365
|
return `# Minikube Infra
|
|
347
366
|
|
|
348
367
|
This directory is generated from \`ankh.config.json\` (infra manifest).
|
|
@@ -350,7 +369,7 @@ This directory is generated from \`ankh.config.json\` (infra manifest).
|
|
|
350
369
|
## Ownership Model
|
|
351
370
|
|
|
352
371
|
- App slug / Minikube profile: \`${appSlug}\`
|
|
353
|
-
- App runtime
|
|
372
|
+
- App runtime: \`${appRuntimeEnabled ? `deployment/app-runtime in namespace ${APP_NAMESPACE}` : 'disabled (no enabled Web deploy target)'}\`
|
|
354
373
|
- Supabase namespace: \`${supabaseKubernetesEnabled ? SUPABASE_NAMESPACE : 'unused'}\`
|
|
355
374
|
${providerNamespaceLine}- Default app image: \`${defaultAppImage}\`
|
|
356
375
|
|
|
@@ -380,16 +399,14 @@ host-level Supabase Compose runtime.
|
|
|
380
399
|
|
|
381
400
|
## Lifecycle Semantics
|
|
382
401
|
|
|
383
|
-
- \`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.
|
|
402
|
+
- \`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' : ''}.
|
|
384
403
|
- \`down.sh\`: stops slug-owned port-forwards, then stops \`minikube -p ${appSlug}\`. Persistent data remains in the profile.
|
|
385
404
|
- \`reset.sh\`: requires \`ANKH_RESET_CONFIRM=${appSlug}\`; ${resetDescription}. It does not delete the Minikube profile.
|
|
386
405
|
- \`destroy.sh\`: deletes only \`minikube -p ${appSlug}\`.
|
|
387
406
|
|
|
388
407
|
### Port-forward groups
|
|
389
408
|
|
|
390
|
-
- \`./scripts/port-forward.sh start runtime\` idempotently ensures ${
|
|
391
|
-
supabaseKubernetesEnabled ? '`app` and `supabase-gateway`' : '`app` only'
|
|
392
|
-
}, the host endpoints required by the running application. The same group supports \`stop\` and \`status\`.
|
|
409
|
+
- \`./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\`.
|
|
393
410
|
- \`runtime\` excludes \`studio\` and \`db-migration\`, which are operational/bootstrap endpoints.
|
|
394
411
|
- \`all\` retains every generated forward, including operational/bootstrap endpoints.
|
|
395
412
|
- Concrete targets remain available for individual lifecycle control.
|
|
@@ -428,8 +445,7 @@ Pinned images:
|
|
|
428
445
|
|
|
429
446
|
## Host URLs
|
|
430
447
|
|
|
431
|
-
|
|
432
|
-
- Supabase gateway: \`http://127.0.0.1:${supabaseHostPorts.gateway}\`
|
|
448
|
+
${appRuntimeEnabled ? `- App: \`http://127.0.0.1:${supabaseHostPorts.app}\`\n` : ''}- Supabase gateway: \`http://127.0.0.1:${supabaseHostPorts.gateway}\`
|
|
433
449
|
- Studio: \`http://127.0.0.1:${supabaseHostPorts.studio}\`
|
|
434
450
|
- DB migration endpoint: \`127.0.0.1:${supabaseHostPorts.db}\`
|
|
435
451
|
|
|
@@ -965,6 +981,7 @@ function normalizeBuckets(rawBuckets: readonly string[]): string[] {
|
|
|
965
981
|
|
|
966
982
|
function getKustomizationManifest(args: {
|
|
967
983
|
namespaceResources: string[];
|
|
984
|
+
appRuntimeEnabled: boolean;
|
|
968
985
|
supabaseEnabled: boolean;
|
|
969
986
|
extraResources: string[];
|
|
970
987
|
}): string {
|
|
@@ -985,9 +1002,9 @@ function getKustomizationManifest(args: {
|
|
|
985
1002
|
: [];
|
|
986
1003
|
const resources = [
|
|
987
1004
|
...args.namespaceResources,
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1005
|
+
...(args.appRuntimeEnabled
|
|
1006
|
+
? ['app.configmap.yaml', 'app/deployment.yaml', 'app/service.yaml']
|
|
1007
|
+
: []),
|
|
991
1008
|
...supabaseResources,
|
|
992
1009
|
...args.extraResources,
|
|
993
1010
|
];
|
|
@@ -1818,7 +1835,7 @@ spec:
|
|
|
1818
1835
|
}
|
|
1819
1836
|
|
|
1820
1837
|
function getSupabaseGatewayTemplate(): string {
|
|
1821
|
-
return `_format_version:
|
|
1838
|
+
return `_format_version: "2.1"
|
|
1822
1839
|
_transform: true
|
|
1823
1840
|
|
|
1824
1841
|
consumers:
|
|
@@ -1840,8 +1857,8 @@ acls:
|
|
|
1840
1857
|
|
|
1841
1858
|
basicauth_credentials:
|
|
1842
1859
|
- consumer: DASHBOARD
|
|
1843
|
-
username:
|
|
1844
|
-
password:
|
|
1860
|
+
username: "$DASHBOARD_USERNAME"
|
|
1861
|
+
password: "$DASHBOARD_PASSWORD"
|
|
1845
1862
|
|
|
1846
1863
|
services:
|
|
1847
1864
|
- name: auth-v1-open
|
|
@@ -2261,6 +2278,7 @@ spec:
|
|
|
2261
2278
|
|
|
2262
2279
|
function getUpScript(args: {
|
|
2263
2280
|
appSlug: string;
|
|
2281
|
+
appRuntimeEnabled: boolean;
|
|
2264
2282
|
defaultAppImage: string;
|
|
2265
2283
|
supabaseKubernetesEnabled: boolean;
|
|
2266
2284
|
profileModel: ResolvedProfileModel;
|
|
@@ -2271,6 +2289,7 @@ function getUpScript(args: {
|
|
|
2271
2289
|
}): string {
|
|
2272
2290
|
const {
|
|
2273
2291
|
appSlug,
|
|
2292
|
+
appRuntimeEnabled,
|
|
2274
2293
|
defaultAppImage,
|
|
2275
2294
|
supabaseKubernetesEnabled,
|
|
2276
2295
|
profileModel,
|
|
@@ -2290,6 +2309,14 @@ function getUpScript(args: {
|
|
|
2290
2309
|
secretStoreProvider,
|
|
2291
2310
|
});
|
|
2292
2311
|
const supabaseMigrationCommand = getSupabaseMigrationCommandScript();
|
|
2312
|
+
const disabledAppRuntimeReconciliation = appRuntimeEnabled
|
|
2313
|
+
? ''
|
|
2314
|
+
: `reconcile_disabled_app_runtime() {
|
|
2315
|
+
kubectl --context "\${PROFILE}" -n "\${APP_NAMESPACE}" delete deployment/app-runtime service/app-runtime configmap/app-infra-config --ignore-not-found
|
|
2316
|
+
"\${PORT_FORWARD_SCRIPT}" stop obsolete
|
|
2317
|
+
}
|
|
2318
|
+
`;
|
|
2319
|
+
const reconcileDisabledAppRuntime = appRuntimeEnabled ? '' : 'reconcile_disabled_app_runtime';
|
|
2293
2320
|
|
|
2294
2321
|
return `#!/usr/bin/env bash
|
|
2295
2322
|
set -Eeuo pipefail
|
|
@@ -2305,6 +2332,7 @@ DRIVER="\${MINIKUBE_DRIVER:-}"
|
|
|
2305
2332
|
APP_NAMESPACE="${APP_NAMESPACE}"
|
|
2306
2333
|
SUPABASE_NAMESPACE="${SUPABASE_NAMESPACE}"
|
|
2307
2334
|
SUPABASE_RUNTIME_ENABLED="${supabaseKubernetesEnabled ? 'true' : 'false'}"
|
|
2335
|
+
APP_RUNTIME_ENABLED="${appRuntimeEnabled ? 'true' : 'false'}"
|
|
2308
2336
|
APP_BUILD_ENABLED="\${APP_BUILD_ENABLED:-}"
|
|
2309
2337
|
APP_SOURCE_DIR="\${APP_SOURCE_DIR:-}"
|
|
2310
2338
|
APP_WEB_EXPORT_DIR="\${APP_WEB_EXPORT_DIR:-}"
|
|
@@ -2735,6 +2763,7 @@ apply_namespace_manifests() {
|
|
|
2735
2763
|
done
|
|
2736
2764
|
}
|
|
2737
2765
|
|
|
2766
|
+
${disabledAppRuntimeReconciliation}
|
|
2738
2767
|
${providerLifecycleStageScripts}
|
|
2739
2768
|
|
|
2740
2769
|
load_env_file_preserving_process_env
|
|
@@ -2808,7 +2837,7 @@ else
|
|
|
2808
2837
|
kubectl --context "\${PROFILE}" apply -k "\${K8S_DIR}"
|
|
2809
2838
|
fi
|
|
2810
2839
|
|
|
2811
|
-
if [[ "\${APP_BUILD_ENABLED}" == "true" ]]; then
|
|
2840
|
+
if [[ "\${APP_RUNTIME_ENABLED}" == "true" && "\${APP_BUILD_ENABLED}" == "true" ]]; then
|
|
2812
2841
|
if [[ ! -x "\${BUILD_SCRIPT}" ]]; then
|
|
2813
2842
|
echo "Missing build helper script: \${BUILD_SCRIPT}"
|
|
2814
2843
|
exit 1
|
|
@@ -2823,6 +2852,7 @@ else
|
|
|
2823
2852
|
EXPORT_DIR="\${APP_SOURCE_DIR}/\${APP_WEB_EXPORT_DIR}"
|
|
2824
2853
|
fi
|
|
2825
2854
|
|
|
2855
|
+
if [[ "\${APP_RUNTIME_ENABLED}" == "true" ]]; then
|
|
2826
2856
|
case "\${APP_IMAGE_SYNC_STRATEGY}" in
|
|
2827
2857
|
minikube-build)
|
|
2828
2858
|
if [[ ! -f "\${EXPORT_DIR}/index.html" ]]; then
|
|
@@ -2844,15 +2874,18 @@ case "\${APP_IMAGE_SYNC_STRATEGY}" in
|
|
|
2844
2874
|
exit 1
|
|
2845
2875
|
;;
|
|
2846
2876
|
esac
|
|
2877
|
+
fi
|
|
2847
2878
|
|
|
2848
2879
|
if [[ "\${SUPABASE_RUNTIME_ENABLED}" == "true" ]]; then
|
|
2849
2880
|
kubectl --context "\${PROFILE}" apply -k "\${K8S_DIR}"
|
|
2850
2881
|
fi
|
|
2851
2882
|
|
|
2883
|
+
${reconcileDisabledAppRuntime}
|
|
2852
2884
|
wait_for_provider_readiness
|
|
2853
2885
|
run_provider_migrations
|
|
2854
2886
|
run_provider_reconciliation
|
|
2855
2887
|
|
|
2888
|
+
if [[ "\${APP_RUNTIME_ENABLED}" == "true" ]]; then
|
|
2856
2889
|
if [[ -n "\${APP_IMAGE_PULL_SECRET_NAME}" ]]; then
|
|
2857
2890
|
if [[ -z "\${APP_IMAGE_PULL_SECRET_USERNAME}" || -z "\${APP_IMAGE_PULL_SECRET_PASSWORD}" ]]; then
|
|
2858
2891
|
echo "APP_IMAGE_PULL_SECRET_NAME is set, but username/password are missing."
|
|
@@ -2884,6 +2917,7 @@ fi
|
|
|
2884
2917
|
|
|
2885
2918
|
kubectl --context "\${PROFILE}" -n "\${APP_NAMESPACE}" rollout status deployment/app-runtime --timeout=180s >/dev/null
|
|
2886
2919
|
"\${PORT_FORWARD_SCRIPT}" start app >/dev/null
|
|
2920
|
+
fi
|
|
2887
2921
|
|
|
2888
2922
|
trap - ERR
|
|
2889
2923
|
echo "Minikube infrastructure for '\${PROFILE}' is running."
|
|
@@ -2997,15 +3031,44 @@ CMD ["nginx", "-g", "daemon off;"]
|
|
|
2997
3031
|
|
|
2998
3032
|
function getPortForwardScript(args: {
|
|
2999
3033
|
appSlug: string;
|
|
3034
|
+
appRuntimeEnabled: boolean;
|
|
3000
3035
|
supabaseKubernetesEnabled: boolean;
|
|
3001
3036
|
supabaseHostPorts: SupabaseHostPorts;
|
|
3002
3037
|
}): string {
|
|
3003
|
-
const { appSlug, supabaseKubernetesEnabled, supabaseHostPorts } = args;
|
|
3004
|
-
const runtimeForwardNames = [
|
|
3038
|
+
const { appSlug, appRuntimeEnabled, supabaseKubernetesEnabled, supabaseHostPorts } = args;
|
|
3039
|
+
const runtimeForwardNames = [
|
|
3040
|
+
...(appRuntimeEnabled ? ['app'] : []),
|
|
3041
|
+
...(supabaseKubernetesEnabled ? ['supabase-gateway'] : []),
|
|
3042
|
+
];
|
|
3005
3043
|
const allForwardNames = [
|
|
3006
3044
|
...runtimeForwardNames,
|
|
3007
3045
|
...(supabaseKubernetesEnabled ? ['studio', 'db-migration'] : []),
|
|
3008
3046
|
];
|
|
3047
|
+
const managedForwardNames = ['app', 'supabase-gateway', 'studio', 'db-migration'];
|
|
3048
|
+
const obsoleteForwardNames = managedForwardNames.filter(
|
|
3049
|
+
(name) => !allForwardNames.includes(name),
|
|
3050
|
+
);
|
|
3051
|
+
const appTargetCase = ` app)
|
|
3052
|
+
echo "${APP_NAMESPACE} service/app-runtime \${APP_PORT_FORWARD_LOCAL_PORT} \${APP_PORT_FORWARD_REMOTE_PORT}"
|
|
3053
|
+
;;`;
|
|
3054
|
+
const supabaseTargetCases = ` supabase-gateway)
|
|
3055
|
+
echo "${SUPABASE_NAMESPACE} service/gateway \${SUPABASE_GATEWAY_FORWARD_LOCAL_PORT} \${SUPABASE_GATEWAY_FORWARD_REMOTE_PORT}"
|
|
3056
|
+
;;
|
|
3057
|
+
studio)
|
|
3058
|
+
echo "${SUPABASE_NAMESPACE} service/studio \${SUPABASE_STUDIO_FORWARD_LOCAL_PORT} \${SUPABASE_STUDIO_FORWARD_REMOTE_PORT}"
|
|
3059
|
+
;;
|
|
3060
|
+
db-migration)
|
|
3061
|
+
echo "${SUPABASE_NAMESPACE} service/postgres \${SUPABASE_DB_FORWARD_LOCAL_PORT} \${SUPABASE_DB_FORWARD_REMOTE_PORT}"
|
|
3062
|
+
;;`;
|
|
3063
|
+
const concreteForwardNames = [
|
|
3064
|
+
...(appRuntimeEnabled ? ['app'] : []),
|
|
3065
|
+
...(supabaseKubernetesEnabled ? ['supabase-gateway', 'studio', 'db-migration'] : []),
|
|
3066
|
+
];
|
|
3067
|
+
const forwardUsage = [...concreteForwardNames, 'runtime', 'all'].join('|');
|
|
3068
|
+
const forwardNamesDescription =
|
|
3069
|
+
concreteForwardNames.length > 0
|
|
3070
|
+
? `${concreteForwardNames.join(', ')}, runtime, or all`
|
|
3071
|
+
: 'runtime or all';
|
|
3009
3072
|
|
|
3010
3073
|
return `#!/usr/bin/env bash
|
|
3011
3074
|
set -euo pipefail
|
|
@@ -3016,6 +3079,7 @@ STATE_DIR="\${ROOT_DIR}/.state/forwards"
|
|
|
3016
3079
|
APP_SLUG="${appSlug}"
|
|
3017
3080
|
PROFILE="\${ANKH_APP_SLUG:-${appSlug}}"
|
|
3018
3081
|
SUPABASE_RUNTIME_ENABLED="${supabaseKubernetesEnabled ? 'true' : 'false'}"
|
|
3082
|
+
APP_RUNTIME_ENABLED="${appRuntimeEnabled ? 'true' : 'false'}"
|
|
3019
3083
|
APP_PORT_FORWARD_LOCAL_PORT="\${APP_PORT_FORWARD_LOCAL_PORT:-${supabaseHostPorts.app}}"
|
|
3020
3084
|
APP_PORT_FORWARD_REMOTE_PORT="\${APP_PORT_FORWARD_REMOTE_PORT:-80}"
|
|
3021
3085
|
SUPABASE_GATEWAY_FORWARD_LOCAL_PORT="\${SUPABASE_GATEWAY_FORWARD_LOCAL_PORT:-${supabaseHostPorts.gateway}}"
|
|
@@ -3026,6 +3090,8 @@ SUPABASE_DB_FORWARD_LOCAL_PORT="\${SUPABASE_DB_FORWARD_LOCAL_PORT:-${supabaseHos
|
|
|
3026
3090
|
SUPABASE_DB_FORWARD_REMOTE_PORT="\${SUPABASE_DB_FORWARD_REMOTE_PORT:-5432}"
|
|
3027
3091
|
RUNTIME_FORWARD_NAMES=(${runtimeForwardNames.join(' ')})
|
|
3028
3092
|
ALL_FORWARD_NAMES=(${allForwardNames.join(' ')})
|
|
3093
|
+
MANAGED_FORWARD_NAMES=(${managedForwardNames.join(' ')})
|
|
3094
|
+
OBSOLETE_FORWARD_NAMES=(${obsoleteForwardNames.join(' ')})
|
|
3029
3095
|
|
|
3030
3096
|
if [[ -f "\${ROOT_DIR}/.env" ]]; then
|
|
3031
3097
|
set -a
|
|
@@ -3034,6 +3100,7 @@ if [[ -f "\${ROOT_DIR}/.env" ]]; then
|
|
|
3034
3100
|
set +a
|
|
3035
3101
|
fi
|
|
3036
3102
|
SUPABASE_RUNTIME_ENABLED="${supabaseKubernetesEnabled ? 'true' : 'false'}"
|
|
3103
|
+
APP_RUNTIME_ENABLED="${appRuntimeEnabled ? 'true' : 'false'}"
|
|
3037
3104
|
|
|
3038
3105
|
if [[ "\${PROFILE}" != "\${APP_SLUG}" ]]; then
|
|
3039
3106
|
echo "ANKH_APP_SLUG must remain the generated canonical slug '\${APP_SLUG}' for this infra directory."
|
|
@@ -3053,20 +3120,10 @@ pid_file_for() {
|
|
|
3053
3120
|
|
|
3054
3121
|
target_for() {
|
|
3055
3122
|
case "\${1}" in
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
;;
|
|
3059
|
-
supabase-gateway)
|
|
3060
|
-
echo "${SUPABASE_NAMESPACE} service/gateway \${SUPABASE_GATEWAY_FORWARD_LOCAL_PORT} \${SUPABASE_GATEWAY_FORWARD_REMOTE_PORT}"
|
|
3061
|
-
;;
|
|
3062
|
-
studio)
|
|
3063
|
-
echo "${SUPABASE_NAMESPACE} service/studio \${SUPABASE_STUDIO_FORWARD_LOCAL_PORT} \${SUPABASE_STUDIO_FORWARD_REMOTE_PORT}"
|
|
3064
|
-
;;
|
|
3065
|
-
db-migration)
|
|
3066
|
-
echo "${SUPABASE_NAMESPACE} service/postgres \${SUPABASE_DB_FORWARD_LOCAL_PORT} \${SUPABASE_DB_FORWARD_REMOTE_PORT}"
|
|
3067
|
-
;;
|
|
3123
|
+
${appTargetCase}
|
|
3124
|
+
${supabaseTargetCases}
|
|
3068
3125
|
*)
|
|
3069
|
-
echo "Unknown forward '\${1}'. Use
|
|
3126
|
+
echo "Unknown forward '\${1}'. Use ${forwardNamesDescription}." >&2
|
|
3070
3127
|
return 1
|
|
3071
3128
|
;;
|
|
3072
3129
|
esac
|
|
@@ -3202,6 +3259,11 @@ start_forward() {
|
|
|
3202
3259
|
pid_file="$(pid_file_for "\${name}")"
|
|
3203
3260
|
read -r namespace resource local_port remote_port <<<"$(target_for "\${name}")"
|
|
3204
3261
|
|
|
3262
|
+
if [[ "\${name}" == "app" && "\${APP_RUNTIME_ENABLED}" != "true" ]]; then
|
|
3263
|
+
echo "\${name}: skipped (Web app runtime disabled)"
|
|
3264
|
+
return 0
|
|
3265
|
+
fi
|
|
3266
|
+
|
|
3205
3267
|
if [[ -f "\${pid_file}" ]]; then
|
|
3206
3268
|
local existing_pid
|
|
3207
3269
|
existing_pid="$(cat "\${pid_file}")"
|
|
@@ -3231,28 +3293,52 @@ start_forward() {
|
|
|
3231
3293
|
return 1
|
|
3232
3294
|
fi
|
|
3233
3295
|
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3296
|
+
for start_attempt in {1..5}; do
|
|
3297
|
+
nohup kubectl --context "\${PROFILE}" -n "\${namespace}" port-forward "\${resource}" "\${local_port}:\${remote_port}" >"\${STATE_DIR}/\${PROFILE}-\${name}.log" 2>&1 &
|
|
3298
|
+
local pid="$!"
|
|
3299
|
+
echo "\${pid}" > "\${pid_file}"
|
|
3237
3300
|
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3301
|
+
for readiness_attempt in {1..20}; do
|
|
3302
|
+
if ! is_pid_running "\${pid}"; then
|
|
3303
|
+
rm -f "\${pid_file}"
|
|
3304
|
+
if [[ "\${start_attempt}" -lt 5 ]]; then
|
|
3305
|
+
echo "\${name}: port-forward attempt \${start_attempt} exited before readiness; retrying"
|
|
3306
|
+
sleep 2
|
|
3307
|
+
break
|
|
3308
|
+
fi
|
|
3309
|
+
|
|
3310
|
+
echo "\${name}: failed to start local port \${local_port} for \${namespace}/\${resource}:\${remote_port} after \${start_attempt} attempts"
|
|
3311
|
+
print_forward_log_tail "\${name}"
|
|
3312
|
+
return 1
|
|
3313
|
+
fi
|
|
3314
|
+
if is_port_accepting "\${local_port}"; then
|
|
3315
|
+
sleep 1
|
|
3316
|
+
if is_pid_running "\${pid}" && is_port_accepting "\${local_port}"; then
|
|
3317
|
+
sleep 0.5
|
|
3318
|
+
if is_pid_running "\${pid}"; then
|
|
3319
|
+
echo "\${name}: started (pid \${pid}, local port \${local_port})"
|
|
3320
|
+
return 0
|
|
3321
|
+
fi
|
|
3322
|
+
fi
|
|
3323
|
+
fi
|
|
3324
|
+
sleep 0.5
|
|
3325
|
+
done
|
|
3326
|
+
|
|
3327
|
+
if [[ -f "\${pid_file}" ]]; then
|
|
3328
|
+
terminate_forward_process "\${name}" "\${pid}" "\${namespace}" "\${resource}" "\${local_port}" "\${remote_port}" || return 1
|
|
3242
3329
|
rm -f "\${pid_file}"
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3330
|
+
|
|
3331
|
+
if [[ "\${start_attempt}" -lt 5 ]]; then
|
|
3332
|
+
echo "\${name}: port-forward attempt \${start_attempt} did not become ready; retrying"
|
|
3333
|
+
sleep 2
|
|
3334
|
+
else
|
|
3335
|
+
echo "\${name}: did not become ready on local port \${local_port} for \${namespace}/\${resource}:\${remote_port} after \${start_attempt} attempts"
|
|
3336
|
+
print_forward_log_tail "\${name}"
|
|
3337
|
+
return 1
|
|
3338
|
+
fi
|
|
3248
3339
|
fi
|
|
3249
|
-
sleep 0.5
|
|
3250
3340
|
done
|
|
3251
3341
|
|
|
3252
|
-
echo "\${name}: did not become ready on local port \${local_port} for \${namespace}/\${resource}:\${remote_port}"
|
|
3253
|
-
print_forward_log_tail "\${name}"
|
|
3254
|
-
terminate_forward_process "\${name}" "\${pid}" "\${namespace}" "\${resource}" "\${local_port}" "\${remote_port}" || return 1
|
|
3255
|
-
rm -f "\${pid_file}"
|
|
3256
3342
|
return 1
|
|
3257
3343
|
}
|
|
3258
3344
|
|
|
@@ -3313,7 +3399,9 @@ for_each_forward() {
|
|
|
3313
3399
|
local action="\${1}"
|
|
3314
3400
|
shift
|
|
3315
3401
|
local name
|
|
3316
|
-
|
|
3402
|
+
while [[ "\${#}" -gt 0 ]]; do
|
|
3403
|
+
name="\${1}"
|
|
3404
|
+
shift
|
|
3317
3405
|
"\${action}_forward" "\${name}"
|
|
3318
3406
|
done
|
|
3319
3407
|
}
|
|
@@ -3322,17 +3410,21 @@ ACTION="\${1:-start}"
|
|
|
3322
3410
|
NAME="\${2:-all}"
|
|
3323
3411
|
|
|
3324
3412
|
case "\${ACTION}:\${NAME}" in
|
|
3325
|
-
start:runtime) for_each_forward start
|
|
3326
|
-
stop:runtime)
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
status:
|
|
3413
|
+
start:runtime) for_each_forward start ${runtimeForwardNames.join(' ')} ;;
|
|
3414
|
+
stop:runtime)
|
|
3415
|
+
for_each_forward stop ${runtimeForwardNames.join(' ')}
|
|
3416
|
+
for_each_forward stop ${obsoleteForwardNames.join(' ')}
|
|
3417
|
+
;;
|
|
3418
|
+
status:runtime) for_each_forward status ${runtimeForwardNames.join(' ')} ;;
|
|
3419
|
+
start:all) for_each_forward start ${allForwardNames.join(' ')} ;;
|
|
3420
|
+
stop:all) for_each_forward stop ${managedForwardNames.join(' ')} ;;
|
|
3421
|
+
status:all) for_each_forward status ${allForwardNames.join(' ')} ;;
|
|
3422
|
+
stop:obsolete) for_each_forward stop ${obsoleteForwardNames.join(' ')} ;;
|
|
3331
3423
|
start:*) start_forward "\${NAME}" ;;
|
|
3332
3424
|
stop:*) stop_forward "\${NAME}" ;;
|
|
3333
3425
|
status:*) status_forward "\${NAME}" ;;
|
|
3334
3426
|
*)
|
|
3335
|
-
echo "Usage: ./scripts/port-forward.sh {start|stop|status} {
|
|
3427
|
+
echo "Usage: ./scripts/port-forward.sh {start|stop|status} {${forwardUsage}}"
|
|
3336
3428
|
exit 1
|
|
3337
3429
|
;;
|
|
3338
3430
|
esac
|
|
@@ -3466,6 +3558,7 @@ echo "Deleted Minikube profile '\${PROFILE}'."
|
|
|
3466
3558
|
|
|
3467
3559
|
function getStatusScript(args: {
|
|
3468
3560
|
appSlug: string;
|
|
3561
|
+
appRuntimeEnabled: boolean;
|
|
3469
3562
|
supabaseKubernetesEnabled: boolean;
|
|
3470
3563
|
profileModel: ResolvedProfileModel;
|
|
3471
3564
|
supabaseHostPorts: SupabaseHostPorts;
|
|
@@ -3474,6 +3567,7 @@ function getStatusScript(args: {
|
|
|
3474
3567
|
}): string {
|
|
3475
3568
|
const {
|
|
3476
3569
|
appSlug,
|
|
3570
|
+
appRuntimeEnabled,
|
|
3477
3571
|
supabaseKubernetesEnabled,
|
|
3478
3572
|
profileModel,
|
|
3479
3573
|
supabaseHostPorts,
|
|
@@ -3558,7 +3652,7 @@ for namespace in ${namespaces}; do
|
|
|
3558
3652
|
fi
|
|
3559
3653
|
done
|
|
3560
3654
|
|
|
3561
|
-
if kubectl --context "\${PROFILE}" -n ${APP_NAMESPACE} get deployment app-runtime >/dev/null 2>&1; then
|
|
3655
|
+
if [[ "${appRuntimeEnabled ? 'true' : 'false'}" == "true" ]] && kubectl --context "\${PROFILE}" -n ${APP_NAMESPACE} get deployment app-runtime >/dev/null 2>&1; then
|
|
3562
3656
|
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"
|
|
3563
3657
|
fi
|
|
3564
3658
|
|