@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
package/src/index.test.ts
CHANGED
|
@@ -45,7 +45,7 @@ describe('generateInfrastructure', () => {
|
|
|
45
45
|
plugins: [],
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
const result = generateInfrastructure(manifest);
|
|
48
|
+
const result = generateInfrastructure(manifest, { appManifest: createAppManifest('alpha') });
|
|
49
49
|
const paths = result.files.map((f) => f.path);
|
|
50
50
|
|
|
51
51
|
expect(result.meta.target).toBe('minikube');
|
|
@@ -147,8 +147,8 @@ describe('generateInfrastructure', () => {
|
|
|
147
147
|
expect(envExample?.content).toContain('APP_SOURCE_DIR=');
|
|
148
148
|
expect(envExample?.content).toContain('APP_WEB_EXPORT_DIR=.ankh/web-export');
|
|
149
149
|
expect(envExample?.content).toContain('SUPABASE_PROJECT_DIR=');
|
|
150
|
-
expect(envExample?.content).toContain('SUPABASE_LOCAL_PORT_BASE=
|
|
151
|
-
expect(envExample?.content).toContain('SUPABASE_LOCAL_ANALYTICS_PORT=
|
|
150
|
+
expect(envExample?.content).toContain('SUPABASE_LOCAL_PORT_BASE=55540');
|
|
151
|
+
expect(envExample?.content).toContain('SUPABASE_LOCAL_ANALYTICS_PORT=55545');
|
|
152
152
|
expect(envExample?.content).toContain('APP_IMAGE=ankh/alpha-local:dev');
|
|
153
153
|
expect(envExample?.content).toContain('APP_IMAGE_SYNC_STRATEGY=docker-load');
|
|
154
154
|
expect(envExample?.content).toContain('AUTH_RUNTIME_MODE=local');
|
|
@@ -168,7 +168,8 @@ describe('generateInfrastructure', () => {
|
|
|
168
168
|
|
|
169
169
|
const minikubeReadme = result.files.find((f) => f.path === 'infra/minikube/README.md');
|
|
170
170
|
expect(minikubeReadme?.content).toContain('SUPABASE_LOCAL_ANALYTICS_PORT');
|
|
171
|
-
expect(minikubeReadme?.content).toContain('
|
|
171
|
+
expect(minikubeReadme?.content).toContain('55545');
|
|
172
|
+
expect(minikubeReadme?.content).toContain('Supabase local project identity: `alpha`');
|
|
172
173
|
expect(minikubeReadme?.content).toContain('$APP_SOURCE_DIR/.env.local');
|
|
173
174
|
expect(minikubeReadme?.content).toContain('apps/card/.env.local');
|
|
174
175
|
|
|
@@ -333,7 +334,10 @@ describe('generateInfrastructure', () => {
|
|
|
333
334
|
plugins: [],
|
|
334
335
|
};
|
|
335
336
|
|
|
336
|
-
const result = generateInfrastructure(manifest, {
|
|
337
|
+
const result = generateInfrastructure(manifest, {
|
|
338
|
+
namespaceHint: 'shop',
|
|
339
|
+
appManifest: createAppManifest('shop'),
|
|
340
|
+
});
|
|
337
341
|
|
|
338
342
|
expect(result.warnings).toContain(
|
|
339
343
|
'Storage buckets are configured but not created automatically yet. Ensure buckets exist in Supabase Storage: assets,uploads.',
|
|
@@ -380,7 +384,10 @@ describe('generateInfrastructure', () => {
|
|
|
380
384
|
plugins: [],
|
|
381
385
|
};
|
|
382
386
|
|
|
383
|
-
const result = generateInfrastructure(manifest, {
|
|
387
|
+
const result = generateInfrastructure(manifest, {
|
|
388
|
+
namespaceHint: 'shop',
|
|
389
|
+
appManifest: createAppManifest('shop'),
|
|
390
|
+
});
|
|
384
391
|
|
|
385
392
|
expect(result.files.map((f) => f.path)).toContain(
|
|
386
393
|
'infra/minikube/k8s/storage/supabase/app-runtime-storage.env.configmap.yaml',
|
|
@@ -425,7 +432,10 @@ describe('generateInfrastructure', () => {
|
|
|
425
432
|
plugins: [],
|
|
426
433
|
};
|
|
427
434
|
|
|
428
|
-
const result = generateInfrastructure(manifest, {
|
|
435
|
+
const result = generateInfrastructure(manifest, {
|
|
436
|
+
namespaceHint: 'shop',
|
|
437
|
+
appManifest: createAppManifest('shop'),
|
|
438
|
+
});
|
|
429
439
|
expect(result.warnings).toContain(
|
|
430
440
|
'Storage buckets are empty after normalization; no storage artifacts generated.',
|
|
431
441
|
);
|
|
@@ -451,7 +461,10 @@ describe('generateInfrastructure', () => {
|
|
|
451
461
|
plugins: [],
|
|
452
462
|
};
|
|
453
463
|
|
|
454
|
-
const result = generateInfrastructure(manifest, {
|
|
464
|
+
const result = generateInfrastructure(manifest, {
|
|
465
|
+
namespaceHint: 'shop',
|
|
466
|
+
appManifest: createAppManifest('shop'),
|
|
467
|
+
});
|
|
455
468
|
expect(result.warnings).toContain(
|
|
456
469
|
'Storage buckets are empty after normalization; no storage artifacts generated.',
|
|
457
470
|
);
|
|
@@ -473,7 +486,10 @@ describe('generateInfrastructure', () => {
|
|
|
473
486
|
plugins: [],
|
|
474
487
|
};
|
|
475
488
|
|
|
476
|
-
const result = generateInfrastructure(manifest, {
|
|
489
|
+
const result = generateInfrastructure(manifest, {
|
|
490
|
+
namespaceHint: 'shop',
|
|
491
|
+
appManifest: createAppManifest('shop'),
|
|
492
|
+
});
|
|
477
493
|
expect(result.warnings).toContain(
|
|
478
494
|
'Storage provider "auto" could not be resolved for minikube; no storage artifacts generated.',
|
|
479
495
|
);
|
|
@@ -645,9 +661,98 @@ describe('generateInfrastructure', () => {
|
|
|
645
661
|
plugins: [],
|
|
646
662
|
};
|
|
647
663
|
|
|
648
|
-
expect(() =>
|
|
649
|
-
|
|
664
|
+
expect(() =>
|
|
665
|
+
generateInfrastructure(manifest, { appManifest: createAppManifest('shop') }),
|
|
666
|
+
).toThrow('Unsupported authorization engine for minikube adapter: opa');
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
test('requires canonical app slug for local Supabase infrastructure', () => {
|
|
670
|
+
const manifest: InfraManifestInput = {
|
|
671
|
+
deployment: {
|
|
672
|
+
target: 'minikube',
|
|
673
|
+
monitoring: false,
|
|
674
|
+
},
|
|
675
|
+
database: {
|
|
676
|
+
provider: 'supabase',
|
|
677
|
+
tier: 'dev',
|
|
678
|
+
},
|
|
679
|
+
plugins: [],
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
expect(() => generateInfrastructure(manifest, { namespaceHint: 'shop' })).toThrow(
|
|
683
|
+
'Cannot generate local Supabase infrastructure: appManifest.metadata.slug is required.',
|
|
684
|
+
);
|
|
685
|
+
expect(() =>
|
|
686
|
+
generateInfrastructure(manifest, {
|
|
687
|
+
namespaceHint: 'shop',
|
|
688
|
+
appManifest: createAppManifest('Scanner App'),
|
|
689
|
+
}),
|
|
690
|
+
).toThrow('appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters');
|
|
691
|
+
|
|
692
|
+
const fortyCharacterSlug = 'a'.repeat(40);
|
|
693
|
+
const fortyCharacterResult = generateInfrastructure(manifest, {
|
|
694
|
+
namespaceHint: 'shop',
|
|
695
|
+
appManifest: createAppManifest(fortyCharacterSlug),
|
|
696
|
+
});
|
|
697
|
+
const fortyCharacterScript = fortyCharacterResult.files.find(
|
|
698
|
+
(f) => f.path === 'infra/minikube/scripts/supabase-local-env.sh',
|
|
650
699
|
);
|
|
700
|
+
expect(fortyCharacterScript?.content).toContain(
|
|
701
|
+
`EXPECTED_SUPABASE_PROJECT_ID="${fortyCharacterSlug}"`,
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
expect(() =>
|
|
705
|
+
generateInfrastructure(manifest, {
|
|
706
|
+
namespaceHint: 'shop',
|
|
707
|
+
appManifest: createAppManifest('a'.repeat(41)),
|
|
708
|
+
}),
|
|
709
|
+
).toThrow('appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters');
|
|
710
|
+
|
|
711
|
+
expect(() =>
|
|
712
|
+
generateInfrastructure(manifest, {
|
|
713
|
+
namespaceHint: 'shop',
|
|
714
|
+
appManifest: createAppManifest('very-long-project-name-with-identical-prefix-alpha'),
|
|
715
|
+
}),
|
|
716
|
+
).toThrow('appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters');
|
|
717
|
+
expect(() =>
|
|
718
|
+
generateInfrastructure(manifest, {
|
|
719
|
+
namespaceHint: 'shop',
|
|
720
|
+
appManifest: createAppManifest('very-long-project-name-with-identical-prefix-beta'),
|
|
721
|
+
}),
|
|
722
|
+
).toThrow('appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters');
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
test('keeps Supabase project identity independent from domain-derived namespace', () => {
|
|
726
|
+
const manifest: InfraManifestInput = {
|
|
727
|
+
deployment: {
|
|
728
|
+
target: 'minikube',
|
|
729
|
+
monitoring: false,
|
|
730
|
+
},
|
|
731
|
+
database: {
|
|
732
|
+
provider: 'supabase',
|
|
733
|
+
tier: 'dev',
|
|
734
|
+
},
|
|
735
|
+
networking: {
|
|
736
|
+
domain: 'local.example.test',
|
|
737
|
+
cdn: false,
|
|
738
|
+
},
|
|
739
|
+
plugins: [],
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
const result = generateInfrastructure(manifest, {
|
|
743
|
+
namespaceHint: 'ignored',
|
|
744
|
+
appManifest: createAppManifest('scanner'),
|
|
745
|
+
});
|
|
746
|
+
const namespaceFile = result.files.find((f) => f.path === 'infra/minikube/k8s/namespace.yaml');
|
|
747
|
+
const envExample = result.files.find((f) => f.path === 'infra/minikube/.env.example');
|
|
748
|
+
const script = result.files.find(
|
|
749
|
+
(f) => f.path === 'infra/minikube/scripts/supabase-local-env.sh',
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
expect(namespaceFile?.content).toContain('name: local-example-test');
|
|
753
|
+
expect(envExample?.content).toContain('SUPABASE_LOCAL_PORT_BASE=64020');
|
|
754
|
+
expect(envExample?.content).not.toContain('SUPABASE_LOCAL_PORT_BASE=61550');
|
|
755
|
+
expect(script?.content).toContain('EXPECTED_SUPABASE_PROJECT_ID="scanner"');
|
|
651
756
|
});
|
|
652
757
|
|
|
653
758
|
test('uses namespace hint when domain is not provided', () => {
|
|
@@ -671,7 +776,10 @@ describe('generateInfrastructure', () => {
|
|
|
671
776
|
plugins: [],
|
|
672
777
|
};
|
|
673
778
|
|
|
674
|
-
const result = generateInfrastructure(manifest, {
|
|
779
|
+
const result = generateInfrastructure(manifest, {
|
|
780
|
+
namespaceHint: 'shop',
|
|
781
|
+
appManifest: createAppManifest('shop'),
|
|
782
|
+
});
|
|
675
783
|
const namespaceFile = result.files.find((f) => f.path === 'infra/minikube/k8s/namespace.yaml');
|
|
676
784
|
const upScript = result.files.find((f) => f.path === 'infra/minikube/scripts/up.sh');
|
|
677
785
|
const statusScript = result.files.find((f) => f.path === 'infra/minikube/scripts/status.sh');
|
|
@@ -688,3 +796,34 @@ describe('generateInfrastructure', () => {
|
|
|
688
796
|
expect(statusScript?.content).toContain('- secret/supabase-auth-secrets: present');
|
|
689
797
|
});
|
|
690
798
|
});
|
|
799
|
+
|
|
800
|
+
function createAppManifest(
|
|
801
|
+
slug: string,
|
|
802
|
+
): Pick<AppManifest, 'metadata' | 'navigator' | 'screens' | 'settings'> {
|
|
803
|
+
return {
|
|
804
|
+
metadata: {
|
|
805
|
+
name: slug,
|
|
806
|
+
slug,
|
|
807
|
+
version: '1.0.0',
|
|
808
|
+
themeId: 'default',
|
|
809
|
+
},
|
|
810
|
+
navigator: {
|
|
811
|
+
type: 'stack',
|
|
812
|
+
routes: [],
|
|
813
|
+
},
|
|
814
|
+
screens: {},
|
|
815
|
+
settings: {
|
|
816
|
+
localization: {
|
|
817
|
+
defaultLocale: 'en',
|
|
818
|
+
locales: ['en'],
|
|
819
|
+
},
|
|
820
|
+
authFlow: {
|
|
821
|
+
signInRoute: '/sign-in',
|
|
822
|
+
signUpRoute: '/sign-up',
|
|
823
|
+
signOutRoute: '/sign-out',
|
|
824
|
+
unauthorizedRoute: '/sign-in',
|
|
825
|
+
postSignInRoute: '/',
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
}
|