@ankhorage/infra 7.1.0 → 7.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/cli/runInfraCliAsync.d.ts.map +1 -1
- package/dist/cli/runInfraCliAsync.js +8 -6
- package/dist/cli/runInfraCliAsync.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/project/index.d.ts +1 -0
- package/dist/project/index.d.ts.map +1 -1
- package/dist/project/index.js +1 -0
- package/dist/project/index.js.map +1 -1
- package/package.json +4 -4
- package/src/cli/cli.test.ts +21 -5
- package/src/cli/runInfraCliAsync.ts +11 -7
- package/src/commands.test.ts +8 -2
- package/src/constants.ts +1 -0
- package/src/hetznerK3sProduction.test.ts +29 -3
- package/src/hetznerK3sSupabaseR2.test.ts +33 -4
- package/src/localDockerComposeProviders.integration.test.ts +90 -51
- package/src/localK3sSupabase.test.ts +85 -27
- package/src/localMinikubeSupabase.integration.test.ts +175 -122
- package/src/localMinikubeSupabaseRecovery.integration.test.ts +37 -10
- package/src/project/index.test.ts +3 -1
- package/src/project/index.ts +1 -0
- package/dist/cli/renderInfraRootHelp.d.ts +0 -3
- package/dist/cli/renderInfraRootHelp.d.ts.map +0 -1
- package/dist/cli/renderInfraRootHelp.js +0 -21
- package/dist/cli/renderInfraRootHelp.js.map +0 -1
- package/src/cli/renderInfraRootHelp.ts +0 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ankhorage/infra
|
|
2
2
|
|
|
3
|
+
## 7.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e707284: Render standalone Infra help from the canonical provider manifest through the shared Ankh CLI help runtime.
|
|
8
|
+
|
|
9
|
+
## 7.1.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 7440682: Expose the project-scoped credential port constructor from `@ankhorage/infra/project` for downstream host composition without exposing credential storage details.
|
|
14
|
+
|
|
3
15
|
## 7.1.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runInfraCliAsync.d.ts","sourceRoot":"","sources":["../../src/cli/runInfraCliAsync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runInfraCliAsync.d.ts","sourceRoot":"","sources":["../../src/cli/runInfraCliAsync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAO9B,iGAAiG;AACjG,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAwBhC;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,mBAAmB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACnD"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { isHelpToken, renderProviderHelp } from '@ankhorage/ankh';
|
|
2
|
+
import { INFRA_PACKAGE_DESCRIPTION } from '../constants.js';
|
|
1
3
|
import { createDefaultInfraCommandContext } from './createDefaultInfraCommandContext.js';
|
|
4
|
+
import { createInfraRuntimeProvider } from './createInfraRuntimeProvider.js';
|
|
2
5
|
import { findInfraCommand } from './findInfraCommand.js';
|
|
3
|
-
import { renderInfraRootHelp } from './renderInfraRootHelp.js';
|
|
4
6
|
import { renderUnknownInfraCommand } from './renderUnknownInfraCommand.js';
|
|
5
7
|
import { runInfraCommandAsync } from './runInfraCommandAsync.js';
|
|
6
8
|
/*** Dispatch the standalone executable arguments through the canonical Infra command surface. */
|
|
@@ -9,7 +11,11 @@ export async function runInfraCliAsync(argv, options = {}) {
|
|
|
9
11
|
const runCommand = options.runCommandImpl ?? runInfraCommandAsync;
|
|
10
12
|
const [firstToken, ...restTokens] = argv;
|
|
11
13
|
if (firstToken === undefined || isHelpToken(firstToken)) {
|
|
12
|
-
context.writeStdout(
|
|
14
|
+
context.writeStdout(renderProviderHelp({
|
|
15
|
+
commandPrefix: ['ankhorage-infra'],
|
|
16
|
+
description: INFRA_PACKAGE_DESCRIPTION,
|
|
17
|
+
manifest: createInfraRuntimeProvider(),
|
|
18
|
+
}));
|
|
13
19
|
return { exitCode: 0 };
|
|
14
20
|
}
|
|
15
21
|
if (isVersionToken(firstToken)) {
|
|
@@ -23,10 +29,6 @@ export async function runInfraCliAsync(argv, options = {}) {
|
|
|
23
29
|
}
|
|
24
30
|
return runCommand({ argv: restTokens, command, context }, { services: options.services });
|
|
25
31
|
}
|
|
26
|
-
/*** Recognize standalone help tokens. */
|
|
27
|
-
function isHelpToken(value) {
|
|
28
|
-
return value === '--help' || value === '-h' || value === 'help';
|
|
29
|
-
}
|
|
30
32
|
/*** Recognize standalone version tokens. */
|
|
31
33
|
function isVersionToken(value) {
|
|
32
34
|
return value === '--version' || value === '-v' || value === 'version';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runInfraCliAsync.js","sourceRoot":"","sources":["../../src/cli/runInfraCliAsync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runInfraCliAsync.js","sourceRoot":"","sources":["../../src/cli/runInfraCliAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAO5D,OAAO,EAAE,gCAAgC,EAAE,MAAM,uCAAuC,CAAC;AACzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAuB,EACvB,UAA2B,EAAE;IAE7B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gCAAgC,EAAE,CAAC;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,cAAc,IAAI,oBAAoB,CAAC;IAClE,MAAM,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IACzC,IAAI,UAAU,KAAK,SAAS,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,WAAW,CACjB,kBAAkB,CAAC;YACjB,aAAa,EAAE,CAAC,iBAAiB,CAAC;YAClC,WAAW,EAAE,yBAAyB;YACtC,QAAQ,EAAE,0BAA0B,EAAE;SACvC,CAAC,CACH,CAAC;QACF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IACD,IAAI,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;QAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IACD,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,WAAW,CAAC,yBAAyB,CAAC,UAAU,CAAC,CAAC,CAAC;QAC3D,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,UAAU,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC5F,CAAC;AAQD,4CAA4C;AAC5C,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AACxE,CAAC","sourcesContent":["import { isHelpToken, renderProviderHelp } from '@ankhorage/ankh';\n\nimport { INFRA_PACKAGE_DESCRIPTION } from '../constants.js';\nimport type {\n InfraCommandContext,\n InfraCommandRunResult,\n InfraCommandServices,\n RunInfraCommandImpl,\n} from '../types/infraCli.js';\nimport { createDefaultInfraCommandContext } from './createDefaultInfraCommandContext.js';\nimport { createInfraRuntimeProvider } from './createInfraRuntimeProvider.js';\nimport { findInfraCommand } from './findInfraCommand.js';\nimport { renderUnknownInfraCommand } from './renderUnknownInfraCommand.js';\nimport { runInfraCommandAsync } from './runInfraCommandAsync.js';\n\n/*** Dispatch the standalone executable arguments through the canonical Infra command surface. */\nexport async function runInfraCliAsync(\n argv: readonly string[],\n options: InfraCliOptions = {},\n): Promise<InfraCommandRunResult> {\n const context = options.context ?? createDefaultInfraCommandContext();\n const runCommand = options.runCommandImpl ?? runInfraCommandAsync;\n const [firstToken, ...restTokens] = argv;\n if (firstToken === undefined || isHelpToken(firstToken)) {\n context.writeStdout(\n renderProviderHelp({\n commandPrefix: ['ankhorage-infra'],\n description: INFRA_PACKAGE_DESCRIPTION,\n manifest: createInfraRuntimeProvider(),\n }),\n );\n return { exitCode: 0 };\n }\n if (isVersionToken(firstToken)) {\n context.writeStdout(`${context.version}\\n`);\n return { exitCode: 0 };\n }\n const command = findInfraCommand(firstToken);\n if (command === null) {\n context.writeStderr(renderUnknownInfraCommand(firstToken));\n return { exitCode: 1 };\n }\n return runCommand({ argv: restTokens, command, context }, { services: options.services });\n}\n\ninterface InfraCliOptions {\n readonly context?: InfraCommandContext;\n readonly runCommandImpl?: RunInfraCommandImpl;\n readonly services?: Partial<InfraCommandServices>;\n}\n\n/*** Recognize standalone version tokens. */\nfunction isVersionToken(value: string): boolean {\n return value === '--version' || value === '-v' || value === 'version';\n}\n"]}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const INFRA_PACKAGE_NAME: string;
|
|
2
2
|
export declare const INFRA_PACKAGE_VERSION: string;
|
|
3
|
+
export declare const INFRA_PACKAGE_DESCRIPTION: string;
|
|
3
4
|
export declare const INFRA_COMMAND_CATEGORY: "infra";
|
|
4
5
|
export declare const INFRA_CAPABILITIES: readonly ["infra.validate", "infra.plan", "infra.generate", "infra.up", "infra.status", "infra.outputs", "infra.down", "infra.destroy"];
|
|
5
6
|
export declare const INFRA_PACKAGE_METADATA: {
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AACnD,eAAO,MAAM,qBAAqB,QAAsB,CAAC;AACzD,eAAO,MAAM,sBAAsB,SAAiD,CAAC;AACrF,eAAO,MAAM,kBAAkB,yIASiB,CAAC;AAEjD,eAAO,MAAM,sBAAsB;;;;CAIK,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AACnD,eAAO,MAAM,qBAAqB,QAAsB,CAAC;AACzD,eAAO,MAAM,yBAAyB,QAA0B,CAAC;AACjE,eAAO,MAAM,sBAAsB,SAAiD,CAAC;AACrF,eAAO,MAAM,kBAAkB,yIASiB,CAAC;AAEjD,eAAO,MAAM,sBAAsB;;;;CAIK,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import packageJson from '../package.json';
|
|
2
2
|
export const INFRA_PACKAGE_NAME = packageJson.name;
|
|
3
3
|
export const INFRA_PACKAGE_VERSION = packageJson.version;
|
|
4
|
+
export const INFRA_PACKAGE_DESCRIPTION = packageJson.description;
|
|
4
5
|
export const INFRA_COMMAND_CATEGORY = 'infra';
|
|
5
6
|
export const INFRA_CAPABILITIES = [
|
|
6
7
|
'infra.validate',
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAMA,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC,OAAO,CAAC;AACzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAA8C,CAAC;AACrF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gBAAgB;IAChB,YAAY;IACZ,gBAAgB;IAChB,UAAU;IACV,cAAc;IACd,eAAe;IACf,YAAY;IACZ,eAAe;CAC+B,CAAC;AAEjD,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,QAAQ,EAAE,sBAAsB;IAChC,QAAQ,EAAE,qBAAqB;IAC/B,YAAY,EAAE,kBAAkB;CACM,CAAC","sourcesContent":["import type {\n AnkhCapabilityId,\n AnkhCommandCategory,\n AnkhPackageMetadata,\n} from '@ankhorage/contracts/cli';\n\nimport packageJson from '../package.json';\n\nexport const INFRA_PACKAGE_NAME = packageJson.name;\nexport const INFRA_PACKAGE_VERSION = packageJson.version;\nexport const INFRA_COMMAND_CATEGORY = 'infra' as const satisfies AnkhCommandCategory;\nexport const INFRA_CAPABILITIES = [\n 'infra.validate',\n 'infra.plan',\n 'infra.generate',\n 'infra.up',\n 'infra.status',\n 'infra.outputs',\n 'infra.down',\n 'infra.destroy',\n] as const satisfies readonly AnkhCapabilityId[];\n\nexport const INFRA_PACKAGE_METADATA = {\n category: INFRA_COMMAND_CATEGORY,\n provider: './dist/cli/index.js',\n capabilities: INFRA_CAPABILITIES,\n} as const satisfies AnkhPackageMetadata;\n"]}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAMA,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAE1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC,OAAO,CAAC;AACzD,MAAM,CAAC,MAAM,yBAAyB,GAAG,WAAW,CAAC,WAAW,CAAC;AACjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAA8C,CAAC;AACrF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gBAAgB;IAChB,YAAY;IACZ,gBAAgB;IAChB,UAAU;IACV,cAAc;IACd,eAAe;IACf,YAAY;IACZ,eAAe;CAC+B,CAAC;AAEjD,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,QAAQ,EAAE,sBAAsB;IAChC,QAAQ,EAAE,qBAAqB;IAC/B,YAAY,EAAE,kBAAkB;CACM,CAAC","sourcesContent":["import type {\n AnkhCapabilityId,\n AnkhCommandCategory,\n AnkhPackageMetadata,\n} from '@ankhorage/contracts/cli';\n\nimport packageJson from '../package.json';\n\nexport const INFRA_PACKAGE_NAME = packageJson.name;\nexport const INFRA_PACKAGE_VERSION = packageJson.version;\nexport const INFRA_PACKAGE_DESCRIPTION = packageJson.description;\nexport const INFRA_COMMAND_CATEGORY = 'infra' as const satisfies AnkhCommandCategory;\nexport const INFRA_CAPABILITIES = [\n 'infra.validate',\n 'infra.plan',\n 'infra.generate',\n 'infra.up',\n 'infra.status',\n 'infra.outputs',\n 'infra.down',\n 'infra.destroy',\n] as const satisfies readonly AnkhCapabilityId[];\n\nexport const INFRA_PACKAGE_METADATA = {\n category: INFRA_COMMAND_CATEGORY,\n provider: './dist/cli/index.js',\n capabilities: INFRA_CAPABILITIES,\n} as const satisfies AnkhPackageMetadata;\n"]}
|
package/dist/project/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { createProjectInfraCredentialPort } from '../features/environment-lifecycle/adapters/outbound/createProjectInfraCredentialPort.js';
|
|
1
2
|
export { readStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/readStoredInfraStateAsync.js';
|
|
2
3
|
export { removeStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/removeStoredInfraStateAsync.js';
|
|
3
4
|
export { resolveInfraProjectAsync } from '../features/environment-lifecycle/adapters/outbound/resolveInfraProjectAsync.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,kFAAkF,CAAC;AAC7H,OAAO,EAAE,2BAA2B,EAAE,MAAM,oFAAoF,CAAC;AACjI,OAAO,EAAE,wBAAwB,EAAE,MAAM,iFAAiF,CAAC;AAC3H,OAAO,EAAE,iCAAiC,EAAE,MAAM,0FAA0F,CAAC;AAC7I,OAAO,EAAE,0BAA0B,EAAE,MAAM,mFAAmF,CAAC;AAC/H,OAAO,EAAE,2BAA2B,EAAE,MAAM,8EAA8E,CAAC;AAC3H,OAAO,EAAE,kBAAkB,EAAE,MAAM,+DAA+D,CAAC;AACnG,YAAY,EACV,kCAAkC,EAClC,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,yFAAyF,CAAC;AAC3I,OAAO,EAAE,yBAAyB,EAAE,MAAM,kFAAkF,CAAC;AAC7H,OAAO,EAAE,2BAA2B,EAAE,MAAM,oFAAoF,CAAC;AACjI,OAAO,EAAE,wBAAwB,EAAE,MAAM,iFAAiF,CAAC;AAC3H,OAAO,EAAE,iCAAiC,EAAE,MAAM,0FAA0F,CAAC;AAC7I,OAAO,EAAE,0BAA0B,EAAE,MAAM,mFAAmF,CAAC;AAC/H,OAAO,EAAE,2BAA2B,EAAE,MAAM,8EAA8E,CAAC;AAC3H,OAAO,EAAE,kBAAkB,EAAE,MAAM,+DAA+D,CAAC;AACnG,YAAY,EACV,kCAAkC,EAClC,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,0BAA0B,CAAC"}
|
package/dist/project/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { createProjectInfraCredentialPort } from '../features/environment-lifecycle/adapters/outbound/createProjectInfraCredentialPort.js';
|
|
1
2
|
export { readStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/readStoredInfraStateAsync.js';
|
|
2
3
|
export { removeStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/removeStoredInfraStateAsync.js';
|
|
3
4
|
export { resolveInfraProjectAsync } from '../features/environment-lifecycle/adapters/outbound/resolveInfraProjectAsync.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,kFAAkF,CAAC;AAC7H,OAAO,EAAE,2BAA2B,EAAE,MAAM,oFAAoF,CAAC;AACjI,OAAO,EAAE,wBAAwB,EAAE,MAAM,iFAAiF,CAAC;AAC3H,OAAO,EAAE,iCAAiC,EAAE,MAAM,0FAA0F,CAAC;AAC7I,OAAO,EAAE,0BAA0B,EAAE,MAAM,mFAAmF,CAAC;AAC/H,OAAO,EAAE,2BAA2B,EAAE,MAAM,8EAA8E,CAAC;AAC3H,OAAO,EAAE,kBAAkB,EAAE,MAAM,+DAA+D,CAAC","sourcesContent":["export { readStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/readStoredInfraStateAsync.js';\nexport { removeStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/removeStoredInfraStateAsync.js';\nexport { resolveInfraProjectAsync } from '../features/environment-lifecycle/adapters/outbound/resolveInfraProjectAsync.js';\nexport { writeInfraGeneratedArtifactsAsync } from '../features/environment-lifecycle/adapters/outbound/writeInfraGeneratedArtifactsAsync.js';\nexport { writeStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/writeStoredInfraStateAsync.js';\nexport { createProjectInfraLifecycle } from '../features/environment-lifecycle/composition/createProjectInfraLifecycle.js';\nexport { resolveProjectFile } from '../features/environment-lifecycle/utils/resolveProjectFile.js';\nexport type {\n CreateProjectInfraLifecycleOptions,\n InfraArtifactWriteResult,\n ProjectInfraDestroyRequest,\n ProjectInfraLifecycle,\n ProjectInfraOperationRequest,\n ResolvedInfraProject,\n} from '../types/infraProject.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,yFAAyF,CAAC;AAC3I,OAAO,EAAE,yBAAyB,EAAE,MAAM,kFAAkF,CAAC;AAC7H,OAAO,EAAE,2BAA2B,EAAE,MAAM,oFAAoF,CAAC;AACjI,OAAO,EAAE,wBAAwB,EAAE,MAAM,iFAAiF,CAAC;AAC3H,OAAO,EAAE,iCAAiC,EAAE,MAAM,0FAA0F,CAAC;AAC7I,OAAO,EAAE,0BAA0B,EAAE,MAAM,mFAAmF,CAAC;AAC/H,OAAO,EAAE,2BAA2B,EAAE,MAAM,8EAA8E,CAAC;AAC3H,OAAO,EAAE,kBAAkB,EAAE,MAAM,+DAA+D,CAAC","sourcesContent":["export { createProjectInfraCredentialPort } from '../features/environment-lifecycle/adapters/outbound/createProjectInfraCredentialPort.js';\nexport { readStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/readStoredInfraStateAsync.js';\nexport { removeStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/removeStoredInfraStateAsync.js';\nexport { resolveInfraProjectAsync } from '../features/environment-lifecycle/adapters/outbound/resolveInfraProjectAsync.js';\nexport { writeInfraGeneratedArtifactsAsync } from '../features/environment-lifecycle/adapters/outbound/writeInfraGeneratedArtifactsAsync.js';\nexport { writeStoredInfraStateAsync } from '../features/environment-lifecycle/adapters/outbound/writeStoredInfraStateAsync.js';\nexport { createProjectInfraLifecycle } from '../features/environment-lifecycle/composition/createProjectInfraLifecycle.js';\nexport { resolveProjectFile } from '../features/environment-lifecycle/utils/resolveProjectFile.js';\nexport type {\n CreateProjectInfraLifecycleOptions,\n InfraArtifactWriteResult,\n ProjectInfraDestroyRequest,\n ProjectInfraLifecycle,\n ProjectInfraOperationRequest,\n ResolvedInfraProject,\n} from '../types/infraProject.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/infra",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.1.
|
|
4
|
+
"version": "7.1.2",
|
|
5
5
|
"description": "Project-aware infrastructure generation library and executable Ankh provider for Ankhorage.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/infra#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"openapi"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"@ankhorage/ankh": "^0.9.1",
|
|
38
39
|
"@ankhorage/contracts": "^21.0.0",
|
|
39
40
|
"@ankhorage/utility": "^1.2.0"
|
|
40
41
|
},
|
|
@@ -111,9 +112,8 @@
|
|
|
111
112
|
"knip:test": "bun run knip:check"
|
|
112
113
|
},
|
|
113
114
|
"devDependencies": {
|
|
114
|
-
"@ankhorage/ankh": "^0.8.11",
|
|
115
115
|
"@ankhorage/cerbos": "^0.2.0",
|
|
116
|
-
"@ankhorage/devtools": "^1.
|
|
116
|
+
"@ankhorage/devtools": "^1.15.3",
|
|
117
117
|
"@ankhorage/docker-compose": "^0.5.3",
|
|
118
118
|
"@ankhorage/hetzner": "^0.3.0",
|
|
119
119
|
"@ankhorage/k3s": "^0.4.1",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"@ankhorage/minikube": "^0.4.4",
|
|
122
122
|
"@ankhorage/paradox": "^0.1.24",
|
|
123
123
|
"@ankhorage/r2": "^0.2.0",
|
|
124
|
-
"@ankhorage/supabase": "^0.5.
|
|
124
|
+
"@ankhorage/supabase": "^0.5.10",
|
|
125
125
|
"@ankhorage/supabase-vault": "^0.3.1",
|
|
126
126
|
"@types/bun": "^1.4.1",
|
|
127
127
|
"@types/node": "^25.6.0",
|
package/src/cli/cli.test.ts
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
+
import { renderProviderHelp } from '@ankhorage/ankh';
|
|
1
2
|
import { describe, expect, test } from 'bun:test';
|
|
2
3
|
|
|
4
|
+
import { INFRA_PACKAGE_DESCRIPTION } from '../constants.js';
|
|
3
5
|
import { createCapturedCommandContext } from '../testSupport.js';
|
|
6
|
+
import { createInfraRuntimeProvider } from './createInfraRuntimeProvider.js';
|
|
4
7
|
import { runInfraCliAsync } from './runInfraCliAsync.js';
|
|
5
8
|
|
|
6
9
|
describe('standalone Infra CLI', () => {
|
|
7
|
-
test('
|
|
8
|
-
const
|
|
10
|
+
test('renders every help alias from the canonical provider manifest', async () => {
|
|
11
|
+
const expectedHelp = renderProviderHelp({
|
|
12
|
+
commandPrefix: ['ankhorage-infra'],
|
|
13
|
+
description: INFRA_PACKAGE_DESCRIPTION,
|
|
14
|
+
manifest: createInfraRuntimeProvider(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
for (const argv of [[], ['-h'], ['--help'], ['help']] as const) {
|
|
18
|
+
const captured = createCapturedCommandContext('/workspace', '9.9.9');
|
|
19
|
+
const result = await runInfraCliAsync(argv, { context: captured.context });
|
|
20
|
+
|
|
21
|
+
expect(result).toEqual({ exitCode: 0 });
|
|
22
|
+
expect(captured.stdout.value).toBe(expectedHelp);
|
|
23
|
+
expect(captured.stderr.value).toBe('');
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('prints version and dispatches all commands through the shared runner', async () => {
|
|
9
28
|
const version = createCapturedCommandContext('/workspace', '9.9.9');
|
|
10
|
-
expect((await runInfraCliAsync([], { context: help.context })).exitCode).toBe(0);
|
|
11
|
-
expect(help.stdout.value).toContain('ankhorage-infra <command>');
|
|
12
|
-
expect(help.stdout.value).toContain('destroy');
|
|
13
29
|
expect((await runInfraCliAsync(['--version'], { context: version.context })).exitCode).toBe(0);
|
|
14
30
|
expect(version.stdout.value).toBe('9.9.9\n');
|
|
15
31
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { isHelpToken, renderProviderHelp } from '@ankhorage/ankh';
|
|
2
|
+
|
|
3
|
+
import { INFRA_PACKAGE_DESCRIPTION } from '../constants.js';
|
|
1
4
|
import type {
|
|
2
5
|
InfraCommandContext,
|
|
3
6
|
InfraCommandRunResult,
|
|
@@ -5,8 +8,8 @@ import type {
|
|
|
5
8
|
RunInfraCommandImpl,
|
|
6
9
|
} from '../types/infraCli.js';
|
|
7
10
|
import { createDefaultInfraCommandContext } from './createDefaultInfraCommandContext.js';
|
|
11
|
+
import { createInfraRuntimeProvider } from './createInfraRuntimeProvider.js';
|
|
8
12
|
import { findInfraCommand } from './findInfraCommand.js';
|
|
9
|
-
import { renderInfraRootHelp } from './renderInfraRootHelp.js';
|
|
10
13
|
import { renderUnknownInfraCommand } from './renderUnknownInfraCommand.js';
|
|
11
14
|
import { runInfraCommandAsync } from './runInfraCommandAsync.js';
|
|
12
15
|
|
|
@@ -19,7 +22,13 @@ export async function runInfraCliAsync(
|
|
|
19
22
|
const runCommand = options.runCommandImpl ?? runInfraCommandAsync;
|
|
20
23
|
const [firstToken, ...restTokens] = argv;
|
|
21
24
|
if (firstToken === undefined || isHelpToken(firstToken)) {
|
|
22
|
-
context.writeStdout(
|
|
25
|
+
context.writeStdout(
|
|
26
|
+
renderProviderHelp({
|
|
27
|
+
commandPrefix: ['ankhorage-infra'],
|
|
28
|
+
description: INFRA_PACKAGE_DESCRIPTION,
|
|
29
|
+
manifest: createInfraRuntimeProvider(),
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
23
32
|
return { exitCode: 0 };
|
|
24
33
|
}
|
|
25
34
|
if (isVersionToken(firstToken)) {
|
|
@@ -40,11 +49,6 @@ interface InfraCliOptions {
|
|
|
40
49
|
readonly services?: Partial<InfraCommandServices>;
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
/*** Recognize standalone help tokens. */
|
|
44
|
-
function isHelpToken(value: string): boolean {
|
|
45
|
-
return value === '--help' || value === '-h' || value === 'help';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
52
|
/*** Recognize standalone version tokens. */
|
|
49
53
|
function isVersionToken(value: string): boolean {
|
|
50
54
|
return value === '--version' || value === '-v' || value === 'version';
|
package/src/commands.test.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { renderProviderHelp } from '@ankhorage/ankh';
|
|
1
2
|
import type { AppManifest } from '@ankhorage/contracts';
|
|
2
3
|
import type { InfraEnvironmentSpec, InfraLedger, InfraResult } from '@ankhorage/contracts/infra';
|
|
3
4
|
import { describe, expect, test } from 'bun:test';
|
|
4
5
|
|
|
5
6
|
import { INFRA_COMMANDS } from './cli/constants.js';
|
|
7
|
+
import { createInfraRuntimeProvider } from './cli/createInfraRuntimeProvider.js';
|
|
6
8
|
import { createProviderCommandDescriptors } from './cli/createProviderCommandDescriptors.js';
|
|
7
9
|
import { findInfraCommand } from './cli/findInfraCommand.js';
|
|
8
|
-
import { renderInfraRootHelp } from './cli/renderInfraRootHelp.js';
|
|
9
10
|
import { runInfraCommandAsync } from './cli/runInfraCommandAsync.js';
|
|
11
|
+
import { INFRA_PACKAGE_DESCRIPTION } from './constants.js';
|
|
10
12
|
import { createAppManifest, createCapturedCommandContext } from './testSupport.js';
|
|
11
13
|
import type { InfraCommandServices, InfraLifecycleOperations } from './types/infraCli.js';
|
|
12
14
|
import type { InfraDestroyOperationRequest } from './types/infraOrchestration.js';
|
|
@@ -16,7 +18,11 @@ describe('canonical Infra commands', () => {
|
|
|
16
18
|
const names = ['validate', 'plan', 'generate', 'up', 'status', 'outputs', 'down', 'destroy'];
|
|
17
19
|
expect(INFRA_COMMANDS.map(({ standaloneName }) => standaloneName)).toEqual(names);
|
|
18
20
|
expect(createProviderCommandDescriptors().map(({ path }) => path.join(' '))).toEqual(names);
|
|
19
|
-
const help =
|
|
21
|
+
const help = renderProviderHelp({
|
|
22
|
+
commandPrefix: ['ankhorage-infra'],
|
|
23
|
+
description: INFRA_PACKAGE_DESCRIPTION,
|
|
24
|
+
manifest: createInfraRuntimeProvider(),
|
|
25
|
+
});
|
|
20
26
|
for (const name of names) expect(help).toContain(name);
|
|
21
27
|
expect(help).not.toContain('port-forward');
|
|
22
28
|
expect(help).not.toContain('reset');
|
package/src/constants.ts
CHANGED
|
@@ -8,6 +8,7 @@ import packageJson from '../package.json';
|
|
|
8
8
|
|
|
9
9
|
export const INFRA_PACKAGE_NAME = packageJson.name;
|
|
10
10
|
export const INFRA_PACKAGE_VERSION = packageJson.version;
|
|
11
|
+
export const INFRA_PACKAGE_DESCRIPTION = packageJson.description;
|
|
11
12
|
export const INFRA_COMMAND_CATEGORY = 'infra' as const satisfies AnkhCommandCategory;
|
|
12
13
|
export const INFRA_CAPABILITIES = [
|
|
13
14
|
'infra.validate',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
InfraControlPlaneCredentialRef,
|
|
3
|
+
InfraCredentialPort,
|
|
2
4
|
InfraLedger,
|
|
3
5
|
InfraManifest,
|
|
4
6
|
InfraResourceIdentity,
|
|
@@ -230,9 +232,7 @@ function createDependencies(fixture: ProductionFixture): InfraOrchestrationDepen
|
|
|
230
232
|
}
|
|
231
233
|
},
|
|
232
234
|
},
|
|
233
|
-
credentials:
|
|
234
|
-
resolveAsync: ({ name }) => Promise.resolve(resolveCredential(name)),
|
|
235
|
-
},
|
|
235
|
+
credentials: createCredentialPort(),
|
|
236
236
|
secrets: {
|
|
237
237
|
resolveAsync: () =>
|
|
238
238
|
Promise.resolve({
|
|
@@ -245,6 +245,32 @@ function createDependencies(fixture: ProductionFixture): InfraOrchestrationDepen
|
|
|
245
245
|
};
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
function createCredentialPort(): InfraCredentialPort {
|
|
249
|
+
return {
|
|
250
|
+
findAsync: (reference) => Promise.resolve(findCredential(reference)),
|
|
251
|
+
resolveAsync: ({ name }) => Promise.resolve(resolveCredential(name)),
|
|
252
|
+
persistAsync: (reference) =>
|
|
253
|
+
Promise.resolve({
|
|
254
|
+
ok: false,
|
|
255
|
+
diagnostics: [
|
|
256
|
+
{
|
|
257
|
+
severity: 'error',
|
|
258
|
+
code: 'unexpected-credential-persist',
|
|
259
|
+
message: `Production fixture must not persist credential ${reference.name}.`,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
}),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function findCredential(
|
|
267
|
+
reference: InfraControlPlaneCredentialRef,
|
|
268
|
+
): InfraResult<Readonly<Record<string, string>> | null> {
|
|
269
|
+
return reference.name === 'SUPABASE_BOOTSTRAP'
|
|
270
|
+
? resolveCredential(reference.name)
|
|
271
|
+
: success(null);
|
|
272
|
+
}
|
|
273
|
+
|
|
248
274
|
function resolveCredential(name: string): InfraResult<Readonly<Record<string, string>>> {
|
|
249
275
|
if (name === 'HCLOUD_TOKEN') return success({ token: 'phase10-hcloud-token' });
|
|
250
276
|
if (name === 'HETZNER_SSH') {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
InfraControlPlaneCredentialRef,
|
|
3
|
+
InfraCredentialPort,
|
|
4
|
+
InfraManifest,
|
|
5
|
+
InfraResult,
|
|
6
|
+
} from '@ankhorage/contracts/infra';
|
|
2
7
|
import {
|
|
3
8
|
createInfraAdapter as createHetznerInfraAdapter,
|
|
4
9
|
type HetznerCloudApi,
|
|
@@ -189,9 +194,7 @@ function createDependencies(fixture: ProductionR2Fixture): InfraOrchestrationDep
|
|
|
189
194
|
}
|
|
190
195
|
},
|
|
191
196
|
},
|
|
192
|
-
credentials:
|
|
193
|
-
resolveAsync: ({ name }) => Promise.resolve(resolveCredential(name)),
|
|
194
|
-
},
|
|
197
|
+
credentials: createCredentialPort(),
|
|
195
198
|
secrets: {
|
|
196
199
|
resolveAsync: () =>
|
|
197
200
|
Promise.resolve({
|
|
@@ -204,6 +207,32 @@ function createDependencies(fixture: ProductionR2Fixture): InfraOrchestrationDep
|
|
|
204
207
|
};
|
|
205
208
|
}
|
|
206
209
|
|
|
210
|
+
function createCredentialPort(): InfraCredentialPort {
|
|
211
|
+
return {
|
|
212
|
+
findAsync: (reference) => Promise.resolve(findCredential(reference)),
|
|
213
|
+
resolveAsync: ({ name }) => Promise.resolve(resolveCredential(name)),
|
|
214
|
+
persistAsync: (reference) =>
|
|
215
|
+
Promise.resolve({
|
|
216
|
+
ok: false,
|
|
217
|
+
diagnostics: [
|
|
218
|
+
{
|
|
219
|
+
severity: 'error',
|
|
220
|
+
code: 'unexpected-credential-persist',
|
|
221
|
+
message: `Production fixture must not persist credential ${reference.name}.`,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
}),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function findCredential(
|
|
229
|
+
reference: InfraControlPlaneCredentialRef,
|
|
230
|
+
): InfraResult<Readonly<Record<string, string>> | null> {
|
|
231
|
+
return reference.name === 'SUPABASE_BOOTSTRAP'
|
|
232
|
+
? resolveCredential(reference.name)
|
|
233
|
+
: success(null);
|
|
234
|
+
}
|
|
235
|
+
|
|
207
236
|
function resolveCredential(name: string): InfraResult<Readonly<Record<string, string>>> {
|
|
208
237
|
if (name === 'HCLOUD_TOKEN') return success({ token: 'phase10-hcloud-token' });
|
|
209
238
|
if (name === 'HETZNER_SSH') {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
+
InfraControlPlaneCredentialRef,
|
|
5
|
+
InfraCredentialPort,
|
|
4
6
|
InfraLedger,
|
|
5
7
|
InfraManifest,
|
|
6
8
|
InfraResourceIdentity,
|
|
@@ -19,16 +21,7 @@ import type { InfraOrchestrationDependencies } from './types/infraOrchestration.
|
|
|
19
21
|
const projectId = 'infra145-compose-providers';
|
|
20
22
|
const baseUrl = 'http://127.0.0.1:54322';
|
|
21
23
|
const bucket = 'phase9-acceptance';
|
|
22
|
-
const
|
|
23
|
-
const credentials = {
|
|
24
|
-
postgresPassword: 'phase9-postgres-password',
|
|
25
|
-
jwtSecret,
|
|
26
|
-
anonKey: createJwt('anon'),
|
|
27
|
-
serviceRoleKey: createJwt('service_role'),
|
|
28
|
-
realtimeSecretKeyBase: 'r'.repeat(64),
|
|
29
|
-
realtimeDatabaseEncryptionKey: '0123456789abcdef',
|
|
30
|
-
pgMetaCryptoKey: 'phase9-meta-crypto-key-that-is-at-least-32-characters',
|
|
31
|
-
} as const;
|
|
24
|
+
const bootstrapCredential = { source: 'control-plane', name: 'SUPABASE_BOOTSTRAP' } as const;
|
|
32
25
|
const manifest = {
|
|
33
26
|
environments: {
|
|
34
27
|
local: {
|
|
@@ -59,7 +52,8 @@ const manifest = {
|
|
|
59
52
|
test.skipIf(process.env.ANKH_INFRA_DOCKER_COMPOSE_PROVIDERS_E2E !== '1')(
|
|
60
53
|
'runs Supabase and Cerbos through the provider-neutral Docker Compose lifecycle',
|
|
61
54
|
async () => {
|
|
62
|
-
const
|
|
55
|
+
const credentialPort = new MemoryCredentialPort();
|
|
56
|
+
const dependencies = createDependencies(credentialPort);
|
|
63
57
|
let ledger: InfraLedger | undefined;
|
|
64
58
|
|
|
65
59
|
try {
|
|
@@ -72,7 +66,10 @@ test.skipIf(process.env.ANKH_INFRA_DOCKER_COMPOSE_PROVIDERS_E2E !== '1')(
|
|
|
72
66
|
await upInfraEnvironmentAsync({ projectId, manifest }, dependencies),
|
|
73
67
|
);
|
|
74
68
|
ledger = firstLedger;
|
|
75
|
-
|
|
69
|
+
expect(credentialPort.persistCount).toBe(1);
|
|
70
|
+
const firstCredentials = credentialPort.readRequired();
|
|
71
|
+
const firstCredentialFingerprint = credentialFingerprint(firstCredentials);
|
|
72
|
+
assertProviderNeutralOutputs(firstOutputs, firstCredentials);
|
|
76
73
|
|
|
77
74
|
const status = requireSuccess(
|
|
78
75
|
await statusInfraEnvironmentAsync({ projectId, manifest, previous: ledger }, dependencies),
|
|
@@ -98,7 +95,9 @@ test.skipIf(process.env.ANKH_INFRA_DOCKER_COMPOSE_PROVIDERS_E2E !== '1')(
|
|
|
98
95
|
await upInfraEnvironmentAsync({ projectId, manifest, previous: ledger }, dependencies),
|
|
99
96
|
);
|
|
100
97
|
ledger = resumedLedger;
|
|
101
|
-
|
|
98
|
+
expect(credentialPort.persistCount).toBe(1);
|
|
99
|
+
expect(credentialFingerprint(credentialPort.readRequired())).toBe(firstCredentialFingerprint);
|
|
100
|
+
assertProviderNeutralOutputs(resumedOutputs, firstCredentials);
|
|
102
101
|
|
|
103
102
|
const health = await fetch(`${baseUrl}/auth/v1/health`);
|
|
104
103
|
expect(health.ok).toBe(true);
|
|
@@ -123,20 +122,10 @@ test.skipIf(process.env.ANKH_INFRA_DOCKER_COMPOSE_PROVIDERS_E2E !== '1')(
|
|
|
123
122
|
900_000,
|
|
124
123
|
);
|
|
125
124
|
|
|
126
|
-
function createDependencies(): InfraOrchestrationDependencies {
|
|
125
|
+
function createDependencies(credentials: InfraCredentialPort): InfraOrchestrationDependencies {
|
|
127
126
|
return {
|
|
128
127
|
adapterResolver: createNodeInfraAdapterPackageResolver(),
|
|
129
|
-
credentials
|
|
130
|
-
resolveAsync: (reference) =>
|
|
131
|
-
Promise.resolve(
|
|
132
|
-
reference.name === 'SUPABASE_BOOTSTRAP'
|
|
133
|
-
? success(credentials)
|
|
134
|
-
: failure(
|
|
135
|
-
'unexpected-control-plane-credential',
|
|
136
|
-
`Unexpected control-plane credential ${reference.name}.`,
|
|
137
|
-
),
|
|
138
|
-
),
|
|
139
|
-
},
|
|
128
|
+
credentials,
|
|
140
129
|
secrets: {
|
|
141
130
|
resolveAsync: (reference) =>
|
|
142
131
|
Promise.resolve(
|
|
@@ -149,6 +138,73 @@ function createDependencies(): InfraOrchestrationDependencies {
|
|
|
149
138
|
};
|
|
150
139
|
}
|
|
151
140
|
|
|
141
|
+
class MemoryCredentialPort implements InfraCredentialPort {
|
|
142
|
+
private values: Readonly<Record<string, string>> | null = null;
|
|
143
|
+
persistCount = 0;
|
|
144
|
+
|
|
145
|
+
findAsync(
|
|
146
|
+
reference: InfraControlPlaneCredentialRef,
|
|
147
|
+
): Promise<InfraResult<Readonly<Record<string, string>> | null>> {
|
|
148
|
+
if (reference.name !== bootstrapCredential.name) return Promise.resolve(success(null));
|
|
149
|
+
return Promise.resolve(success(this.values === null ? null : { ...this.values }));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
resolveAsync(
|
|
153
|
+
reference: InfraControlPlaneCredentialRef,
|
|
154
|
+
): Promise<InfraResult<Readonly<Record<string, string>>>> {
|
|
155
|
+
if (reference.name === bootstrapCredential.name && this.values !== null) {
|
|
156
|
+
return Promise.resolve(success({ ...this.values }));
|
|
157
|
+
}
|
|
158
|
+
return Promise.resolve(
|
|
159
|
+
failure(
|
|
160
|
+
'unexpected-control-plane-credential',
|
|
161
|
+
`Unexpected or missing control-plane credential ${reference.name}.`,
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
persistAsync(
|
|
167
|
+
reference: InfraControlPlaneCredentialRef,
|
|
168
|
+
values: Readonly<Record<string, string>>,
|
|
169
|
+
): Promise<InfraResult<null>> {
|
|
170
|
+
if (reference.name !== bootstrapCredential.name) {
|
|
171
|
+
return Promise.resolve(
|
|
172
|
+
failure(
|
|
173
|
+
'unexpected-control-plane-credential',
|
|
174
|
+
`Unexpected control-plane credential ${reference.name}.`,
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
this.values = { ...values };
|
|
179
|
+
this.persistCount += 1;
|
|
180
|
+
return Promise.resolve(success(null));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
readRequired(): Readonly<Record<string, string>> {
|
|
184
|
+
if (this.values === null) throw new Error('Expected generated Supabase bootstrap credentials.');
|
|
185
|
+
return { ...this.values };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function credentialFingerprint(values: Readonly<Record<string, string>>): string {
|
|
190
|
+
const canonical = Object.entries(values)
|
|
191
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
192
|
+
.map(([key, value]) => `${key}\u0000${value}`)
|
|
193
|
+
.join('\u0001');
|
|
194
|
+
return createHash('sha256').update(canonical).digest('hex');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function privilegedCredentialValues(values: Readonly<Record<string, string>>): readonly string[] {
|
|
198
|
+
const { anonKey: _anonKey, ...privileged } = values;
|
|
199
|
+
return Object.values(privileged);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function readAnonKey(values: Readonly<Record<string, string>>): string {
|
|
203
|
+
const { anonKey } = values;
|
|
204
|
+
if (anonKey === undefined) throw new Error('Expected generated Supabase anonKey.');
|
|
205
|
+
return anonKey;
|
|
206
|
+
}
|
|
207
|
+
|
|
152
208
|
function createDestroyRequest(
|
|
153
209
|
previous: InfraLedger,
|
|
154
210
|
confirmedResources: readonly InfraResourceIdentity[],
|
|
@@ -167,14 +223,14 @@ function persistentIdentities(ledger: InfraLedger): readonly InfraResourceIdenti
|
|
|
167
223
|
return ledger.resources.filter(({ persistent }) => persistent).map(({ identity }) => identity);
|
|
168
224
|
}
|
|
169
225
|
|
|
170
|
-
function assertProviderNeutralOutputs(
|
|
226
|
+
function assertProviderNeutralOutputs(
|
|
227
|
+
outputs: InfraLedger['outputs'],
|
|
228
|
+
credentials: Readonly<Record<string, string>>,
|
|
229
|
+
): void {
|
|
171
230
|
const serialized = JSON.stringify(outputs);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
expect(serialized).not.toContain(credentials.realtimeSecretKeyBase);
|
|
176
|
-
expect(serialized).not.toContain(credentials.realtimeDatabaseEncryptionKey);
|
|
177
|
-
expect(serialized).not.toContain(credentials.pgMetaCryptoKey);
|
|
231
|
+
for (const secret of privilegedCredentialValues(credentials)) {
|
|
232
|
+
expect(serialized).not.toContain(secret);
|
|
233
|
+
}
|
|
178
234
|
expect(serialized.toLowerCase()).not.toContain('kubernetes');
|
|
179
235
|
expect(serialized.toLowerCase()).not.toContain('minikube');
|
|
180
236
|
expect(
|
|
@@ -190,7 +246,7 @@ function assertProviderNeutralOutputs(outputs: InfraLedger['outputs']): void {
|
|
|
190
246
|
({ owner, environmentVariable, value }) =>
|
|
191
247
|
owner.adapter === 'supabase' &&
|
|
192
248
|
environmentVariable === 'EXPO_PUBLIC_SUPABASE_ANON_KEY' &&
|
|
193
|
-
value === credentials
|
|
249
|
+
value === readAnonKey(credentials),
|
|
194
250
|
),
|
|
195
251
|
).toBe(true);
|
|
196
252
|
expect(
|
|
@@ -209,23 +265,6 @@ function assertProviderNeutralOutputs(outputs: InfraLedger['outputs']): void {
|
|
|
209
265
|
).toBe(true);
|
|
210
266
|
}
|
|
211
267
|
|
|
212
|
-
function createJwt(role: 'anon' | 'service_role'): string {
|
|
213
|
-
const encodedHeader = encodeJwtPart({ alg: 'HS256', typ: 'JWT' });
|
|
214
|
-
const encodedPayload = encodeJwtPart({
|
|
215
|
-
role,
|
|
216
|
-
iss: 'supabase',
|
|
217
|
-
iat: 1_700_000_000,
|
|
218
|
-
exp: 4_102_444_800,
|
|
219
|
-
});
|
|
220
|
-
const unsigned = `${encodedHeader}.${encodedPayload}`;
|
|
221
|
-
const signature = createHmac('sha256', jwtSecret).update(unsigned).digest('base64url');
|
|
222
|
-
return `${unsigned}.${signature}`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function encodeJwtPart(value: Readonly<Record<string, string | number>>): string {
|
|
226
|
-
return Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
268
|
function requireSuccess<T>(result: InfraResult<T>): T {
|
|
230
269
|
if (!result.ok) throw new Error(result.diagnostics.map(({ message }) => message).join(' '));
|
|
231
270
|
return result.value;
|