@ankhorage/infra 0.4.0 → 0.4.1
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 +6 -0
- package/README.md +1 -1
- 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 +231 -20
- 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 +26 -0
- package/dist/adapters/minikube/index.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/minikube/base/index.test.ts +456 -0
- package/src/adapters/minikube/base/index.ts +235 -19
- package/src/adapters/minikube/index.ts +37 -0
- package/src/adapters/minikube/secrets/index.test.ts +37 -1
- package/src/index.test.ts +151 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { generateApiInfrastructureArtifacts } from '../../apiArtifacts';\nimport type {\n GeneratedPackageDependency,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from '../../types';\nimport { generateAuthProviderArtifacts } from './auth';\nimport { generateAuthorizationArtifacts } from './authz';\nimport { generateMinikubeBaseArtifacts } from './base';\nimport { generateSecretStoreArtifacts } from './secrets';\nimport { generateStorageArtifacts } from './storage';\n\nconst DEFAULT_NAMESPACE = 'ankh-app';\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 namespace = resolveNamespace(manifest, options);\n\n const authArtifacts = generateAuthProviderArtifacts({ manifest, namespace });\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 const apiArtifacts = generateApiInfrastructureArtifacts({\n data: options.appManifest?.data,\n databaseProvider: manifest.database?.provider,\n });\n\n const extraResources = unique([\n ...authArtifacts.resources,\n ...authzArtifacts.resources,\n ...storageArtifacts.resources,\n ...secretStoreArtifacts.resources,\n ]);\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 namespace,\n extraResources,\n extraEnvEntries,\n });\n\n const warnings = unique([\n ...collectWarnings(manifest),\n ...authArtifacts.warnings,\n ...authzArtifacts.warnings,\n ...storageArtifacts.warnings,\n ...secretStoreArtifacts.warnings,\n ...apiArtifacts.warnings,\n ]);\n\n return {\n files: [\n ...baseFiles,\n ...authArtifacts.files,\n ...authzArtifacts.files,\n ...storageArtifacts.files,\n ...secretStoreArtifacts.files,\n ...apiArtifacts.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: string[] = [];\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 resolveNamespace(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions,\n): string {\n const source =\n manifest.networking?.domain?.trim() ?? options.namespaceHint?.trim() ?? DEFAULT_NAMESPACE;\n\n const normalized = source\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n\n const safe = normalized.slice(0, 63).replace(/-+$/g, '');\n return safe || DEFAULT_NAMESPACE;\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 { generateApiInfrastructureArtifacts } from '../../apiArtifacts';\nimport type {\n GeneratedPackageDependency,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from '../../types';\nimport { generateAuthProviderArtifacts } from './auth';\nimport { generateAuthorizationArtifacts } from './authz';\nimport { generateMinikubeBaseArtifacts } from './base';\nimport { generateSecretStoreArtifacts } from './secrets';\nimport { generateStorageArtifacts } from './storage';\n\nconst DEFAULT_NAMESPACE = 'ankh-app';\nconst CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;\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 namespace = resolveNamespace(manifest, options);\n const supabaseProjectId = isSupabaseLocalEnabled(manifest)\n ? validateCanonicalProjectSlug(options.appManifest?.metadata.slug)\n : null;\n\n const authArtifacts = generateAuthProviderArtifacts({ manifest, namespace });\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 const apiArtifacts = generateApiInfrastructureArtifacts({\n data: options.appManifest?.data,\n databaseProvider: manifest.database?.provider,\n });\n\n const extraResources = unique([\n ...authArtifacts.resources,\n ...authzArtifacts.resources,\n ...storageArtifacts.resources,\n ...secretStoreArtifacts.resources,\n ]);\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 namespace,\n supabaseProjectId,\n extraResources,\n extraEnvEntries,\n });\n\n const warnings = unique([\n ...collectWarnings(manifest),\n ...authArtifacts.warnings,\n ...authzArtifacts.warnings,\n ...storageArtifacts.warnings,\n ...secretStoreArtifacts.warnings,\n ...apiArtifacts.warnings,\n ]);\n\n return {\n files: [\n ...baseFiles,\n ...authArtifacts.files,\n ...authzArtifacts.files,\n ...storageArtifacts.files,\n ...secretStoreArtifacts.files,\n ...apiArtifacts.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: string[] = [];\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 isSupabaseLocalEnabled(manifest: InfraManifestInput): boolean {\n const authProvider = manifest.auth?.provider;\n const databaseProvider = manifest.database?.provider;\n const storageProvider = manifest.storage?.provider;\n const secretStoreProvider = manifest.secretStore?.provider;\n\n return (\n authProvider === 'supabase' ||\n databaseProvider === 'supabase' ||\n storageProvider === 'supabase' ||\n (storageProvider === 'auto' &&\n (authProvider === 'supabase' || databaseProvider === 'supabase')) ||\n secretStoreProvider === 'supabase-vault'\n );\n}\n\nfunction validateCanonicalProjectSlug(slug: string | undefined): string {\n if (!slug) {\n throw new Error(\n 'Cannot generate local Supabase 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 local Supabase 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 resolveNamespace(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions,\n): string {\n const source =\n manifest.networking?.domain?.trim() ?? options.namespaceHint?.trim() ?? DEFAULT_NAMESPACE;\n\n const normalized = source\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n\n const safe = normalized.slice(0, 63).replace(/-+$/g, '');\n return safe || DEFAULT_NAMESPACE;\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": "0.4.
|
|
4
|
+
"version": "0.4.1",
|
|
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": {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
1
6
|
import { describe, expect, test } from 'bun:test';
|
|
2
7
|
|
|
3
8
|
import type { InfraManifestInput } from '../../../types';
|
|
@@ -43,6 +48,159 @@ describe('generateMinikubeBaseArtifacts Supabase local ports', () => {
|
|
|
43
48
|
);
|
|
44
49
|
});
|
|
45
50
|
|
|
51
|
+
test('uses app project identity for Supabase ports independently from namespace', () => {
|
|
52
|
+
const scannerEnv = getGeneratedFileContent(
|
|
53
|
+
'local-example-test',
|
|
54
|
+
'infra/minikube/.env.example',
|
|
55
|
+
{},
|
|
56
|
+
'scanner',
|
|
57
|
+
);
|
|
58
|
+
const scannerNamespace = getGeneratedFileContent(
|
|
59
|
+
'local-example-test',
|
|
60
|
+
'infra/minikube/k8s/namespace.yaml',
|
|
61
|
+
{},
|
|
62
|
+
'scanner',
|
|
63
|
+
);
|
|
64
|
+
const chessEnv = getGeneratedFileContent(
|
|
65
|
+
'local-example-test',
|
|
66
|
+
'infra/minikube/.env.example',
|
|
67
|
+
{},
|
|
68
|
+
'chess',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(scannerNamespace).toContain('name: local-example-test');
|
|
72
|
+
expect(scannerEnv).toContain('SUPABASE_LOCAL_PORT_BASE=64020');
|
|
73
|
+
expect(chessEnv).toContain('SUPABASE_LOCAL_PORT_BASE=62200');
|
|
74
|
+
expect(scannerEnv).not.toContain('SUPABASE_LOCAL_PORT_BASE=62200');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('generates canonical Supabase project identity checks', () => {
|
|
78
|
+
const script = getGeneratedFileContent(
|
|
79
|
+
'scanner',
|
|
80
|
+
'infra/minikube/scripts/supabase-local-env.sh',
|
|
81
|
+
);
|
|
82
|
+
const status = getGeneratedFileContent('scanner', 'infra/minikube/scripts/status.sh');
|
|
83
|
+
|
|
84
|
+
expect(script).toContain('EXPECTED_SUPABASE_PROJECT_ID="scanner"');
|
|
85
|
+
expect(script).toContain('write_supabase_project_identity_for_new_config');
|
|
86
|
+
expect(script).toContain('validate_supabase_project_identity');
|
|
87
|
+
expect(script).toContain('project_id = "{expected}"');
|
|
88
|
+
expect(script).toContain(
|
|
89
|
+
'The existing local Supabase project belongs to a different identity.',
|
|
90
|
+
);
|
|
91
|
+
expect(script).toContain(
|
|
92
|
+
'Destroy the invalid local stack and its project-owned resources, then run Infra Up again.',
|
|
93
|
+
);
|
|
94
|
+
expect(script).toContain(
|
|
95
|
+
'supabase --workdir "${SUPABASE_PROJECT_DIR}" init --yes >/dev/null\n write_supabase_project_identity_for_new_config',
|
|
96
|
+
);
|
|
97
|
+
expect(script).toContain('validate_supabase_project_identity\nconfigure_supabase_local_ports');
|
|
98
|
+
expect(script).toContain(
|
|
99
|
+
'validate_supabase_project_identity\n assert_supabase_local_ports_available',
|
|
100
|
+
);
|
|
101
|
+
expect(script).toContain('validate_supabase_project_identity\nrun_checked_command');
|
|
102
|
+
expect(script).not.toContain('basename "${APP_SOURCE_DIR}"');
|
|
103
|
+
expect(script).not.toContain('_minikube');
|
|
104
|
+
|
|
105
|
+
expect(status).toContain('EXPECTED_SUPABASE_PROJECT_ID="scanner"');
|
|
106
|
+
expect(status).toContain('reject_supabase_project_id_override');
|
|
107
|
+
expect(status).toContain('validate_supabase_project_identity');
|
|
108
|
+
expect(status).toContain('Supabase project identity mismatch.');
|
|
109
|
+
expect(status).toContain('Destroy the invalid local stack and run Infra Up again.');
|
|
110
|
+
expect(status).not.toContain('write_supabase_project_identity_for_new_config');
|
|
111
|
+
expect(status).not.toContain('configure_supabase_local_ports');
|
|
112
|
+
expect(status).not.toContain('_minikube');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('patches only a newly initialized Supabase config to the canonical project identity', () => {
|
|
116
|
+
const run = runGeneratedSupabaseScript({
|
|
117
|
+
projectId: 'scanner',
|
|
118
|
+
existingConfig: null,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(run.status).toBe(0);
|
|
122
|
+
expect(readFileSync(run.configPath, 'utf-8')).toContain('project_id = "scanner"');
|
|
123
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).toContain(
|
|
124
|
+
'--workdir ' + run.root + ' init --yes',
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('rejects an existing Supabase config with a different project identity before lifecycle work', () => {
|
|
129
|
+
const run = runGeneratedSupabaseScript({
|
|
130
|
+
projectId: 'scanner',
|
|
131
|
+
existingConfig: 'project_id = "minikube"\n[api]\nport = 54321\n',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(run.status).not.toBe(0);
|
|
135
|
+
expect(run.combinedOutput).toContain('Supabase project identity mismatch.');
|
|
136
|
+
expect(run.combinedOutput).toContain('Expected "scanner", found "minikube".');
|
|
137
|
+
expect(run.combinedOutput).toContain(
|
|
138
|
+
'Destroy the invalid local stack and its project-owned resources, then run Infra Up again.',
|
|
139
|
+
);
|
|
140
|
+
expect(readFileSync(run.configPath, 'utf-8')).toContain('project_id = "minikube"');
|
|
141
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('--workdir');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('rejects an existing Supabase config with missing top-level project identity', () => {
|
|
145
|
+
const run = runGeneratedSupabaseScript({
|
|
146
|
+
projectId: 'scanner',
|
|
147
|
+
existingConfig: '[api]\nport = 54321\n',
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(run.status).not.toBe(0);
|
|
151
|
+
expect(run.combinedOutput).toContain('Expected "scanner", found missing.');
|
|
152
|
+
expect(readFileSync(run.configPath, 'utf-8')).not.toContain('project_id = "scanner"');
|
|
153
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('--workdir');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('rejects ambient SUPABASE_PROJECT_ID before Supabase CLI operations', () => {
|
|
157
|
+
const run = runGeneratedSupabaseScript({
|
|
158
|
+
projectId: 'scanner',
|
|
159
|
+
existingConfig: 'project_id = "scanner"\n[api]\nport = 54321\n',
|
|
160
|
+
env: {
|
|
161
|
+
SUPABASE_PROJECT_ID: 'minikube',
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
expect(run.status).not.toBe(0);
|
|
166
|
+
expect(run.combinedOutput).toContain(
|
|
167
|
+
'SUPABASE_PROJECT_ID must not be set for generated local Infra scripts.',
|
|
168
|
+
);
|
|
169
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('--workdir');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('rejects empty Supabase project identity before Supabase CLI operations', () => {
|
|
173
|
+
const run = runGeneratedSupabaseScript({
|
|
174
|
+
projectId: 'plain',
|
|
175
|
+
supabaseProjectId: null,
|
|
176
|
+
existingConfig: null,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(run.status).not.toBe(0);
|
|
180
|
+
expect(run.combinedOutput).toContain(
|
|
181
|
+
'Cannot run local Supabase infrastructure: expected Supabase project identity is empty.',
|
|
182
|
+
);
|
|
183
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('--workdir');
|
|
184
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('init --yes');
|
|
185
|
+
expect(() => readFileSync(run.configPath, 'utf-8')).toThrow();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('requires python before Supabase init mutates the workdir', () => {
|
|
189
|
+
const run = runGeneratedSupabaseScript({
|
|
190
|
+
projectId: 'scanner',
|
|
191
|
+
existingConfig: null,
|
|
192
|
+
withoutPython: true,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(run.status).not.toBe(0);
|
|
196
|
+
expect(run.combinedOutput).toContain(
|
|
197
|
+
'python3 is required to reconcile supabase/config.toml before local Supabase startup.',
|
|
198
|
+
);
|
|
199
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('--workdir');
|
|
200
|
+
expect(readFileSync(run.supabaseLogPath, 'utf-8')).not.toContain('init --yes');
|
|
201
|
+
expect(() => readFileSync(run.configPath, 'utf-8')).toThrow();
|
|
202
|
+
});
|
|
203
|
+
|
|
46
204
|
test('generates bootstrap logic that retries unhealthy local Supabase startup once', () => {
|
|
47
205
|
const script = getGeneratedFileContent('chess', 'infra/minikube/scripts/supabase-local-env.sh');
|
|
48
206
|
|
|
@@ -67,9 +225,16 @@ describe('generateMinikubeBaseArtifacts Supabase local ports', () => {
|
|
|
67
225
|
const script = getGeneratedFileContent('chess', 'infra/minikube/scripts/supabase-local-env.sh');
|
|
68
226
|
|
|
69
227
|
expect(script).toContain('SUPABASE_PROJECT_DIR="${SUPABASE_PROJECT_DIR:-${ROOT_DIR}}"');
|
|
228
|
+
expect(script).toContain('require_expected_supabase_project_identity');
|
|
229
|
+
expect(script).toContain(
|
|
230
|
+
'require_expected_supabase_project_identity\nreject_supabase_project_id_override\nrequire_supabase_cli_capabilities',
|
|
231
|
+
);
|
|
70
232
|
expect(script).toContain('supabase CLI >= 2.106.0 is required');
|
|
71
233
|
expect(script).toContain('supabase CLI does not support required global --workdir flag');
|
|
72
234
|
expect(script).toContain('supabase CLI does not support required migration up --local command');
|
|
235
|
+
expect(script).toContain(
|
|
236
|
+
'python3 is required to reconcile supabase/config.toml before local Supabase startup.',
|
|
237
|
+
);
|
|
73
238
|
expect(script).toContain('psql is required but not installed');
|
|
74
239
|
expect(script).toContain('supabase --workdir "${SUPABASE_PROJECT_DIR}" init --yes');
|
|
75
240
|
expect(script).toContain('supabase --workdir "${SUPABASE_PROJECT_DIR}" migration up --local');
|
|
@@ -136,16 +301,62 @@ describe('generateMinikubeBaseArtifacts Supabase local ports', () => {
|
|
|
136
301
|
);
|
|
137
302
|
expect(status).toContain('generated trigger function execute privilege must be revoked');
|
|
138
303
|
});
|
|
304
|
+
|
|
305
|
+
test('does not reject SUPABASE_PROJECT_ID for non-Supabase status checks', () => {
|
|
306
|
+
const run = runGeneratedStatusScript({
|
|
307
|
+
manifest: {
|
|
308
|
+
deployment: {
|
|
309
|
+
target: 'minikube',
|
|
310
|
+
monitoring: false,
|
|
311
|
+
},
|
|
312
|
+
plugins: [],
|
|
313
|
+
},
|
|
314
|
+
supabaseProjectId: null,
|
|
315
|
+
env: {
|
|
316
|
+
SUPABASE_PROJECT_ID: 'ambient-project',
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
expect(run.status).toBe(0);
|
|
321
|
+
expect(run.combinedOutput).not.toContain(
|
|
322
|
+
'SUPABASE_PROJECT_ID must not be set for generated local Infra scripts.',
|
|
323
|
+
);
|
|
324
|
+
expect(run.kubectlLog).toContain('get all -n plain');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test('rejects empty Supabase project identity before local Supabase status checks', () => {
|
|
328
|
+
const run = runGeneratedStatusScript({
|
|
329
|
+
manifest: {
|
|
330
|
+
deployment: {
|
|
331
|
+
target: 'minikube',
|
|
332
|
+
monitoring: false,
|
|
333
|
+
},
|
|
334
|
+
plugins: [],
|
|
335
|
+
},
|
|
336
|
+
supabaseProjectId: null,
|
|
337
|
+
env: {
|
|
338
|
+
SUPABASE_LOCAL_ENABLED: 'true',
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(run.status).not.toBe(0);
|
|
343
|
+
expect(run.combinedOutput).toContain(
|
|
344
|
+
'Cannot run local Supabase infrastructure: expected Supabase project identity is empty.',
|
|
345
|
+
);
|
|
346
|
+
expect(run.supabaseLog).toBe('');
|
|
347
|
+
});
|
|
139
348
|
});
|
|
140
349
|
|
|
141
350
|
function getGeneratedFileContent(
|
|
142
351
|
namespace: string,
|
|
143
352
|
filePath: string,
|
|
144
353
|
overrides: Partial<InfraManifestInput> = {},
|
|
354
|
+
supabaseProjectId = namespace,
|
|
145
355
|
): string {
|
|
146
356
|
const artifact = generateMinikubeBaseArtifacts({
|
|
147
357
|
manifest: createInfraManifest(overrides),
|
|
148
358
|
namespace,
|
|
359
|
+
supabaseProjectId,
|
|
149
360
|
extraResources: [],
|
|
150
361
|
extraEnvEntries: [],
|
|
151
362
|
}).find((file) => file.path === filePath);
|
|
@@ -195,3 +406,248 @@ function createInfraManifest(overrides: Partial<InfraManifestInput> = {}): Infra
|
|
|
195
406
|
},
|
|
196
407
|
};
|
|
197
408
|
}
|
|
409
|
+
|
|
410
|
+
function runGeneratedSupabaseScript(args: {
|
|
411
|
+
projectId: string;
|
|
412
|
+
supabaseProjectId?: string | null;
|
|
413
|
+
existingConfig: string | null;
|
|
414
|
+
env?: Record<string, string>;
|
|
415
|
+
withoutPython?: boolean;
|
|
416
|
+
}): {
|
|
417
|
+
status: number | null;
|
|
418
|
+
combinedOutput: string;
|
|
419
|
+
root: string;
|
|
420
|
+
configPath: string;
|
|
421
|
+
supabaseLogPath: string;
|
|
422
|
+
} {
|
|
423
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'infra-supabase-identity-'));
|
|
424
|
+
const root = join(tempRoot, 'infra', 'minikube');
|
|
425
|
+
const scriptsDir = join(root, 'scripts');
|
|
426
|
+
const supabaseDir = join(root, 'supabase');
|
|
427
|
+
const binDir = join(tempRoot, 'bin');
|
|
428
|
+
const appSourceDir = join(tempRoot, 'app');
|
|
429
|
+
const scriptPath = join(scriptsDir, 'supabase-local-env.sh');
|
|
430
|
+
const configPath = join(supabaseDir, 'config.toml');
|
|
431
|
+
const supabaseLogPath = join(tempRoot, 'supabase.log');
|
|
432
|
+
|
|
433
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
434
|
+
mkdirSync(binDir, { recursive: true });
|
|
435
|
+
mkdirSync(appSourceDir, { recursive: true });
|
|
436
|
+
writeFileSync(join(root, '.env.example'), '');
|
|
437
|
+
writeFileSync(supabaseLogPath, '');
|
|
438
|
+
|
|
439
|
+
if (args.existingConfig !== null) {
|
|
440
|
+
mkdirSync(supabaseDir, { recursive: true });
|
|
441
|
+
writeFileSync(configPath, args.existingConfig);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const script = getGeneratedFileContent(
|
|
445
|
+
args.projectId,
|
|
446
|
+
'infra/minikube/scripts/supabase-local-env.sh',
|
|
447
|
+
{},
|
|
448
|
+
args.supabaseProjectId === undefined ? args.projectId : args.supabaseProjectId,
|
|
449
|
+
);
|
|
450
|
+
writeFileSync(scriptPath, script);
|
|
451
|
+
chmodSync(scriptPath, 0o755);
|
|
452
|
+
|
|
453
|
+
const supabaseStubPath = join(binDir, 'supabase');
|
|
454
|
+
writeFileSync(
|
|
455
|
+
supabaseStubPath,
|
|
456
|
+
`#!/bin/bash
|
|
457
|
+
set -euo pipefail
|
|
458
|
+
printf '%s\\n' "$*" >> "\${SUPABASE_STUB_LOG}"
|
|
459
|
+
|
|
460
|
+
if [[ "\${1:-}" == "--version" ]]; then
|
|
461
|
+
echo "supabase 2.106.0"
|
|
462
|
+
exit 0
|
|
463
|
+
fi
|
|
464
|
+
|
|
465
|
+
if [[ "\${1:-}" == "status" && "\${2:-}" == "--help" ]]; then
|
|
466
|
+
echo "--workdir"
|
|
467
|
+
exit 0
|
|
468
|
+
fi
|
|
469
|
+
|
|
470
|
+
if [[ "\${1:-}" == "migration" && "\${2:-}" == "up" && "\${3:-}" == "--help" ]]; then
|
|
471
|
+
echo "--local"
|
|
472
|
+
exit 0
|
|
473
|
+
fi
|
|
474
|
+
|
|
475
|
+
if [[ "\${1:-}" == "--workdir" && "\${3:-}" == "init" && "\${4:-}" == "--yes" ]]; then
|
|
476
|
+
mkdir -p "\${2}/supabase"
|
|
477
|
+
printf '%s\\n' 'project_id = "minikube"' '[api]' 'port = 54321' > "\${2}/supabase/config.toml"
|
|
478
|
+
exit 0
|
|
479
|
+
fi
|
|
480
|
+
|
|
481
|
+
if [[ "\${1:-}" == "--workdir" && "\${3:-}" == "status" && "\${4:-}" == "-o" && "\${5:-}" == "env" ]]; then
|
|
482
|
+
cat <<'ENV'
|
|
483
|
+
DB_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
|
484
|
+
API_URL=http://127.0.0.1:54321
|
|
485
|
+
ANON_KEY=anon
|
|
486
|
+
SERVICE_ROLE_KEY=service
|
|
487
|
+
JWT_SECRET=secret
|
|
488
|
+
ENV
|
|
489
|
+
exit 0
|
|
490
|
+
fi
|
|
491
|
+
|
|
492
|
+
if [[ "\${1:-}" == "--workdir" && "\${3:-}" == "status" ]]; then
|
|
493
|
+
exit 0
|
|
494
|
+
fi
|
|
495
|
+
|
|
496
|
+
if [[ "\${1:-}" == "--workdir" && "\${3:-}" == "migration" && "\${4:-}" == "up" && "\${5:-}" == "--local" ]]; then
|
|
497
|
+
exit 0
|
|
498
|
+
fi
|
|
499
|
+
|
|
500
|
+
echo "unexpected supabase call: $*" >&2
|
|
501
|
+
exit 1
|
|
502
|
+
`,
|
|
503
|
+
);
|
|
504
|
+
chmodSync(supabaseStubPath, 0o755);
|
|
505
|
+
|
|
506
|
+
const psqlStubPath = join(binDir, 'psql');
|
|
507
|
+
writeFileSync(
|
|
508
|
+
psqlStubPath,
|
|
509
|
+
`#!/bin/bash
|
|
510
|
+
exit 0
|
|
511
|
+
`,
|
|
512
|
+
);
|
|
513
|
+
chmodSync(psqlStubPath, 0o755);
|
|
514
|
+
|
|
515
|
+
const result = spawnSync('/bin/bash', [scriptPath], {
|
|
516
|
+
cwd: tempRoot,
|
|
517
|
+
env: {
|
|
518
|
+
...(args.withoutPython ? {} : process.env),
|
|
519
|
+
PATH: args.withoutPython ? binDir : `${binDir}:${process.env.PATH ?? ''}`,
|
|
520
|
+
APP_SOURCE_DIR: appSourceDir,
|
|
521
|
+
SUPABASE_STUB_LOG: supabaseLogPath,
|
|
522
|
+
...args.env,
|
|
523
|
+
},
|
|
524
|
+
encoding: 'utf-8',
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
status: result.status,
|
|
529
|
+
combinedOutput: `${result.stdout}${result.stderr}`,
|
|
530
|
+
root,
|
|
531
|
+
configPath,
|
|
532
|
+
supabaseLogPath,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function runGeneratedStatusScript(args: {
|
|
537
|
+
manifest: InfraManifestInput;
|
|
538
|
+
supabaseProjectId: string | null;
|
|
539
|
+
env?: Record<string, string>;
|
|
540
|
+
}): {
|
|
541
|
+
status: number | null;
|
|
542
|
+
combinedOutput: string;
|
|
543
|
+
kubectlLog: string;
|
|
544
|
+
supabaseLog: string;
|
|
545
|
+
} {
|
|
546
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'infra-status-'));
|
|
547
|
+
const root = join(tempRoot, 'infra', 'minikube');
|
|
548
|
+
const scriptsDir = join(root, 'scripts');
|
|
549
|
+
const binDir = join(tempRoot, 'bin');
|
|
550
|
+
const scriptPath = join(scriptsDir, 'status.sh');
|
|
551
|
+
const kubectlLogPath = join(tempRoot, 'kubectl.log');
|
|
552
|
+
const supabaseLogPath = join(tempRoot, 'supabase.log');
|
|
553
|
+
|
|
554
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
555
|
+
mkdirSync(binDir, { recursive: true });
|
|
556
|
+
writeFileSync(join(root, '.env.example'), '');
|
|
557
|
+
writeFileSync(kubectlLogPath, '');
|
|
558
|
+
writeFileSync(supabaseLogPath, '');
|
|
559
|
+
|
|
560
|
+
const artifact = generateMinikubeBaseArtifacts({
|
|
561
|
+
manifest: args.manifest,
|
|
562
|
+
namespace: 'plain',
|
|
563
|
+
supabaseProjectId: args.supabaseProjectId,
|
|
564
|
+
extraResources: [],
|
|
565
|
+
extraEnvEntries: [],
|
|
566
|
+
}).find((file) => file.path === 'infra/minikube/scripts/status.sh');
|
|
567
|
+
|
|
568
|
+
if (!artifact) {
|
|
569
|
+
throw new Error('Expected generated status.sh artifact');
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
writeFileSync(scriptPath, artifact.content);
|
|
573
|
+
chmodSync(scriptPath, 0o755);
|
|
574
|
+
|
|
575
|
+
const kubectlStubPath = join(binDir, 'kubectl');
|
|
576
|
+
writeFileSync(
|
|
577
|
+
kubectlStubPath,
|
|
578
|
+
`#!/bin/bash
|
|
579
|
+
set -euo pipefail
|
|
580
|
+
printf '%s\\n' "$*" >> "\${KUBECTL_STUB_LOG}"
|
|
581
|
+
|
|
582
|
+
if [[ "\${1:-}" == "config" && "\${2:-}" == "current-context" ]]; then
|
|
583
|
+
echo "minikube"
|
|
584
|
+
exit 0
|
|
585
|
+
fi
|
|
586
|
+
|
|
587
|
+
if [[ "\${1:-}" == "cluster-info" ]]; then
|
|
588
|
+
echo "Kubernetes control plane is running"
|
|
589
|
+
exit 0
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
if [[ "\${1:-}" == "get" && "\${2:-}" == "namespace" ]]; then
|
|
593
|
+
exit 0
|
|
594
|
+
fi
|
|
595
|
+
|
|
596
|
+
if [[ "\${1:-}" == "get" && "\${2:-}" == "all" ]]; then
|
|
597
|
+
echo "No resources found"
|
|
598
|
+
exit 0
|
|
599
|
+
fi
|
|
600
|
+
|
|
601
|
+
echo "unexpected kubectl call: $*" >&2
|
|
602
|
+
exit 1
|
|
603
|
+
`,
|
|
604
|
+
);
|
|
605
|
+
chmodSync(kubectlStubPath, 0o755);
|
|
606
|
+
|
|
607
|
+
const supabaseStubPath = join(binDir, 'supabase');
|
|
608
|
+
writeFileSync(
|
|
609
|
+
supabaseStubPath,
|
|
610
|
+
`#!/bin/bash
|
|
611
|
+
set -euo pipefail
|
|
612
|
+
printf '%s\\n' "$*" >> "\${SUPABASE_STUB_LOG}"
|
|
613
|
+
|
|
614
|
+
if [[ "\${1:-}" == "--version" ]]; then
|
|
615
|
+
echo "supabase 2.106.0"
|
|
616
|
+
exit 0
|
|
617
|
+
fi
|
|
618
|
+
|
|
619
|
+
if [[ "\${1:-}" == "status" && "\${2:-}" == "--help" ]]; then
|
|
620
|
+
echo "--workdir"
|
|
621
|
+
exit 0
|
|
622
|
+
fi
|
|
623
|
+
|
|
624
|
+
if [[ "\${1:-}" == "migration" && "\${2:-}" == "up" && "\${3:-}" == "--help" ]]; then
|
|
625
|
+
echo "--local"
|
|
626
|
+
exit 0
|
|
627
|
+
fi
|
|
628
|
+
|
|
629
|
+
echo "unexpected supabase call: $*" >&2
|
|
630
|
+
exit 1
|
|
631
|
+
`,
|
|
632
|
+
);
|
|
633
|
+
chmodSync(supabaseStubPath, 0o755);
|
|
634
|
+
|
|
635
|
+
const result = spawnSync('/bin/bash', [scriptPath], {
|
|
636
|
+
cwd: tempRoot,
|
|
637
|
+
env: {
|
|
638
|
+
...process.env,
|
|
639
|
+
PATH: `${binDir}:${process.env.PATH ?? ''}`,
|
|
640
|
+
KUBECTL_STUB_LOG: kubectlLogPath,
|
|
641
|
+
SUPABASE_STUB_LOG: supabaseLogPath,
|
|
642
|
+
...args.env,
|
|
643
|
+
},
|
|
644
|
+
encoding: 'utf-8',
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
return {
|
|
648
|
+
status: result.status,
|
|
649
|
+
combinedOutput: `${result.stdout}${result.stderr}`,
|
|
650
|
+
kubectlLog: readFileSync(kubectlLogPath, 'utf-8'),
|
|
651
|
+
supabaseLog: readFileSync(supabaseLogPath, 'utf-8'),
|
|
652
|
+
};
|
|
653
|
+
}
|