quidproquo-cli 0.1.6 → 0.1.8
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/docker/dev-server/Dockerfile +7 -9
- package/lib/commonjs/cli/commandMenu.d.ts +2 -0
- package/lib/commonjs/cli/commandMenu.js +81 -0
- package/lib/commonjs/cli/commandMenu.js.map +1 -0
- package/lib/commonjs/cli/commandRegistry.d.ts +10 -0
- package/lib/commonjs/cli/commandRegistry.js +131 -0
- package/lib/commonjs/cli/commandRegistry.js.map +1 -0
- package/lib/commonjs/cli/runCli.js +49 -49
- package/lib/commonjs/cli/runCli.js.map +1 -1
- package/lib/commonjs/commands/clearResources.d.ts +1 -0
- package/lib/commonjs/commands/clearResources.js +36 -0
- package/lib/commonjs/commands/clearResources.js.map +1 -0
- package/lib/commonjs/commands/go.js +3 -1
- package/lib/commonjs/commands/go.js.map +1 -1
- package/lib/commonjs/commands/goDev.js +23 -122
- package/lib/commonjs/commands/goDev.js.map +1 -1
- package/lib/commonjs/commands/goDevApi.d.ts +1 -0
- package/lib/commonjs/commands/goDevApi.js +110 -0
- package/lib/commonjs/commands/goDevApi.js.map +1 -0
- package/lib/commonjs/commands/goDevWeb.d.ts +4 -1
- package/lib/commonjs/commands/goDevWeb.js +4 -2
- package/lib/commonjs/commands/goDevWeb.js.map +1 -1
- package/lib/commonjs/commands/goDocker.js +3 -1
- package/lib/commonjs/commands/goDocker.js.map +1 -1
- package/lib/commonjs/commands/hooks.js +23 -5
- package/lib/commonjs/commands/hooks.js.map +1 -1
- package/lib/commonjs/lib/clearResourcesPlan.d.ts +15 -0
- package/lib/commonjs/lib/clearResourcesPlan.js +80 -0
- package/lib/commonjs/lib/clearResourcesPlan.js.map +1 -0
- package/lib/commonjs/lib/deployPrompts.d.ts +1 -0
- package/lib/commonjs/lib/deployPrompts.js +40 -2
- package/lib/commonjs/lib/deployPrompts.js.map +1 -1
- package/lib/commonjs/lib/devServerEntry.js +3 -0
- package/lib/commonjs/lib/devServerEntry.js.map +1 -1
- package/lib/commonjs/lib/hooks.d.ts +2 -1
- package/lib/commonjs/lib/hooks.js +100 -5
- package/lib/commonjs/lib/hooks.js.map +1 -1
- package/lib/commonjs/lib/killStaleListeners.d.ts +3 -0
- package/lib/commonjs/lib/killStaleListeners.js +170 -0
- package/lib/commonjs/lib/killStaleListeners.js.map +1 -0
- package/lib/commonjs/lib/prompts.d.ts +10 -0
- package/lib/commonjs/lib/prompts.js +15 -1
- package/lib/commonjs/lib/prompts.js.map +1 -1
- package/lib/commonjs/lib/qpqConfigs.d.ts +1 -0
- package/lib/commonjs/lib/qpqConfigs.js +9 -1
- package/lib/commonjs/lib/qpqConfigs.js.map +1 -1
- package/lib/commonjs/lib/resolveAppSelection.js +1 -1
- package/lib/commonjs/lib/resolveAppSelection.js.map +1 -1
- package/lib/commonjs/lib/runTasks.d.ts +5 -0
- package/lib/commonjs/lib/runTasks.js +82 -1
- package/lib/commonjs/lib/runTasks.js.map +1 -1
- package/lib/commonjs/lib/typeWatcher.d.ts +2 -0
- package/lib/commonjs/lib/typeWatcher.js +59 -0
- package/lib/commonjs/lib/typeWatcher.js.map +1 -0
- package/lib/commonjs/platforms/aws/cdkApp.js +5 -1
- package/lib/commonjs/platforms/aws/cdkApp.js.map +1 -1
- package/lib/commonjs/platforms/aws/clearResources.d.ts +2 -0
- package/lib/commonjs/platforms/aws/clearResources.js +167 -0
- package/lib/commonjs/platforms/aws/clearResources.js.map +1 -0
- package/lib/commonjs/platforms/aws/go.js +15 -1
- package/lib/commonjs/platforms/aws/go.js.map +1 -1
- package/lib/commonjs/platforms/aws/goDocker.js +76 -8
- package/lib/commonjs/platforms/aws/goDocker.js.map +1 -1
- package/lib/commonjs/platforms/aws/index.js +2 -0
- package/lib/commonjs/platforms/aws/index.js.map +1 -1
- package/lib/commonjs/platforms/docker/go.js +2 -2
- package/lib/commonjs/platforms/docker/go.js.map +1 -1
- package/lib/commonjs/platforms/types.d.ts +2 -0
- package/lib/commonjs/platforms/types.js.map +1 -1
- package/lib/esm/cli/commandMenu.d.ts +2 -0
- package/lib/esm/cli/commandMenu.js +67 -0
- package/lib/esm/cli/commandMenu.js.map +1 -0
- package/lib/esm/cli/commandRegistry.d.ts +10 -0
- package/lib/esm/cli/commandRegistry.js +115 -0
- package/lib/esm/cli/commandRegistry.js.map +1 -0
- package/lib/esm/cli/runCli.js +49 -49
- package/lib/esm/cli/runCli.js.map +1 -1
- package/lib/esm/commands/clearResources.d.ts +1 -0
- package/lib/esm/commands/clearResources.js +23 -0
- package/lib/esm/commands/clearResources.js.map +1 -0
- package/lib/esm/commands/go.js +3 -2
- package/lib/esm/commands/go.js.map +1 -1
- package/lib/esm/commands/goDev.js +24 -120
- package/lib/esm/commands/goDev.js.map +1 -1
- package/lib/esm/commands/goDevApi.d.ts +1 -0
- package/lib/esm/commands/goDevApi.js +94 -0
- package/lib/esm/commands/goDevApi.js.map +1 -0
- package/lib/esm/commands/goDevWeb.d.ts +4 -1
- package/lib/esm/commands/goDevWeb.js +4 -2
- package/lib/esm/commands/goDevWeb.js.map +1 -1
- package/lib/esm/commands/goDocker.js +3 -2
- package/lib/esm/commands/goDocker.js.map +1 -1
- package/lib/esm/commands/hooks.js +21 -6
- package/lib/esm/commands/hooks.js.map +1 -1
- package/lib/esm/lib/clearResourcesPlan.d.ts +15 -0
- package/lib/esm/lib/clearResourcesPlan.js +66 -0
- package/lib/esm/lib/clearResourcesPlan.js.map +1 -0
- package/lib/esm/lib/deployPrompts.d.ts +1 -0
- package/lib/esm/lib/deployPrompts.js +38 -1
- package/lib/esm/lib/deployPrompts.js.map +1 -1
- package/lib/esm/lib/devServerEntry.js +3 -0
- package/lib/esm/lib/devServerEntry.js.map +1 -1
- package/lib/esm/lib/hooks.d.ts +2 -1
- package/lib/esm/lib/hooks.js +97 -5
- package/lib/esm/lib/hooks.js.map +1 -1
- package/lib/esm/lib/killStaleListeners.d.ts +3 -0
- package/lib/esm/lib/killStaleListeners.js +161 -0
- package/lib/esm/lib/killStaleListeners.js.map +1 -0
- package/lib/esm/lib/prompts.d.ts +10 -0
- package/lib/esm/lib/prompts.js +12 -0
- package/lib/esm/lib/prompts.js.map +1 -1
- package/lib/esm/lib/qpqConfigs.d.ts +1 -0
- package/lib/esm/lib/qpqConfigs.js +7 -0
- package/lib/esm/lib/qpqConfigs.js.map +1 -1
- package/lib/esm/lib/resolveAppSelection.js +1 -1
- package/lib/esm/lib/resolveAppSelection.js.map +1 -1
- package/lib/esm/lib/runTasks.d.ts +5 -0
- package/lib/esm/lib/runTasks.js +80 -0
- package/lib/esm/lib/runTasks.js.map +1 -1
- package/lib/esm/lib/typeWatcher.d.ts +2 -0
- package/lib/esm/lib/typeWatcher.js +51 -0
- package/lib/esm/lib/typeWatcher.js.map +1 -0
- package/lib/esm/platforms/aws/cdkApp.js +5 -1
- package/lib/esm/platforms/aws/cdkApp.js.map +1 -1
- package/lib/esm/platforms/aws/clearResources.d.ts +2 -0
- package/lib/esm/platforms/aws/clearResources.js +151 -0
- package/lib/esm/platforms/aws/clearResources.js.map +1 -0
- package/lib/esm/platforms/aws/go.js +16 -2
- package/lib/esm/platforms/aws/go.js.map +1 -1
- package/lib/esm/platforms/aws/goDocker.js +77 -9
- package/lib/esm/platforms/aws/goDocker.js.map +1 -1
- package/lib/esm/platforms/aws/index.js +2 -0
- package/lib/esm/platforms/aws/index.js.map +1 -1
- package/lib/esm/platforms/docker/go.js +2 -2
- package/lib/esm/platforms/docker/go.js.map +1 -1
- package/lib/esm/platforms/types.d.ts +2 -0
- package/lib/esm/platforms/types.js.map +1 -1
- package/package.json +12 -10
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Keeps workspace packages' declaration output (dist/*.d.ts) fresh while
|
|
2
|
+
// `qpq go:dev` runs. The rspack dev builds bundle workspace source directly,
|
|
3
|
+
// so this watcher only feeds the editor and validate-ts, which resolve
|
|
4
|
+
// cross-package types through each package's built declarations.
|
|
5
|
+
//
|
|
6
|
+
// One `tsc -b --watch` over every workspace package that ships types; routine
|
|
7
|
+
// watch chatter is dropped so only real compile errors reach the shared
|
|
8
|
+
// terminal. Opt out with `--no-types`.
|
|
9
|
+
import { getWorkspacePackageDirs } from 'quidproquo-deploy-rspack';
|
|
10
|
+
import { spawn } from 'child_process';
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
// Routine tsc watch status lines; everything else is worth showing.
|
|
14
|
+
const routineTscPatterns = [/Starting compilation in watch mode/, /File change detected/, /Found 0 errors/];
|
|
15
|
+
const isRoutineTscLine = (line) => routineTscPatterns.some((pattern) => pattern.test(line));
|
|
16
|
+
// Forwards non-routine tsc output line by line under a [types] prefix.
|
|
17
|
+
const forwardTscOutput = (chunk) => {
|
|
18
|
+
for (const line of chunk.toString().split('\n')) {
|
|
19
|
+
if (line.trim().length === 0 || isRoutineTscLine(line)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
console.log(`[types] ${line}`);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// A workspace package wants declaration watching when it has a tsconfig and
|
|
26
|
+
// publishes types from its build output. Packages bundled by rspack (views,
|
|
27
|
+
// service handlers) don't ship types and are skipped.
|
|
28
|
+
const isTypedWorkspacePackage = (dir) => {
|
|
29
|
+
if (!fs.existsSync(path.join(dir, 'tsconfig.json'))) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
|
|
33
|
+
return !!(packageJson.types || packageJson.typings);
|
|
34
|
+
};
|
|
35
|
+
export const startTypeWatcher = (root) => {
|
|
36
|
+
const projectDirs = getWorkspacePackageDirs(root).filter(isTypedWorkspacePackage);
|
|
37
|
+
if (projectDirs.length === 0) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const relativeDirs = projectDirs.map((dir) => path.relative(root, dir));
|
|
41
|
+
const child = spawn('npx', ['tsc', '-b', ...relativeDirs, '--watch', '--preserveWatchOutput', '--pretty'], {
|
|
42
|
+
cwd: root,
|
|
43
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
44
|
+
shell: process.platform === 'win32',
|
|
45
|
+
});
|
|
46
|
+
child.stdout?.on('data', forwardTscOutput);
|
|
47
|
+
child.stderr?.on('data', forwardTscOutput);
|
|
48
|
+
console.log(`Type watcher: tsc -b --watch over ${relativeDirs.length} workspace packages (disable with --no-types).`);
|
|
49
|
+
return child;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=typeWatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeWatcher.js","sourceRoot":"","sources":["../../../src/lib/typeWatcher.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,6EAA6E;AAC7E,uEAAuE;AACvE,iEAAiE;AACjE,EAAE;AACF,8EAA8E;AAC9E,wEAAwE;AACxE,uCAAuC;AACvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAgB,KAAK,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,oEAAoE;AACpE,MAAM,kBAAkB,GAAG,CAAC,oCAAoC,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;AAE5G,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAE7G,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAQ,EAAE;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC;AAEF,4EAA4E;AAC5E,4EAA4E;AAC5E,sDAAsD;AACtD,MAAM,uBAAuB,GAAG,CAAC,GAAW,EAAW,EAAE;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;QACpD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACzF,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAuB,EAAE;IACpE,MAAM,WAAW,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;IAClF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,SAAS,EAAE,uBAAuB,EAAE,UAAU,CAAC,EAAE;QACzG,GAAG,EAAE,IAAI;QACT,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;KACpC,CAAC,CAAC;IAEH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,qCAAqC,YAAY,CAAC,MAAM,gDAAgD,CAAC,CAAC;IAEtH,OAAO,KAAK,CAAC;AACf,CAAC,CAAC","sourcesContent":["// Keeps workspace packages' declaration output (dist/*.d.ts) fresh while\n// `qpq go:dev` runs. The rspack dev builds bundle workspace source directly,\n// so this watcher only feeds the editor and validate-ts, which resolve\n// cross-package types through each package's built declarations.\n//\n// One `tsc -b --watch` over every workspace package that ships types; routine\n// watch chatter is dropped so only real compile errors reach the shared\n// terminal. Opt out with `--no-types`.\nimport { getWorkspacePackageDirs } from 'quidproquo-deploy-rspack';\n\nimport { ChildProcess, spawn } from 'child_process';\nimport fs from 'fs';\nimport path from 'path';\n\n// Routine tsc watch status lines; everything else is worth showing.\nconst routineTscPatterns = [/Starting compilation in watch mode/, /File change detected/, /Found 0 errors/];\n\nconst isRoutineTscLine = (line: string): boolean => routineTscPatterns.some((pattern) => pattern.test(line));\n\n// Forwards non-routine tsc output line by line under a [types] prefix.\nconst forwardTscOutput = (chunk: Buffer): void => {\n for (const line of chunk.toString().split('\\n')) {\n if (line.trim().length === 0 || isRoutineTscLine(line)) {\n continue;\n }\n console.log(`[types] ${line}`);\n }\n};\n\n// A workspace package wants declaration watching when it has a tsconfig and\n// publishes types from its build output. Packages bundled by rspack (views,\n// service handlers) don't ship types and are skipped.\nconst isTypedWorkspacePackage = (dir: string): boolean => {\n if (!fs.existsSync(path.join(dir, 'tsconfig.json'))) {\n return false;\n }\n\n const packageJson = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));\n return !!(packageJson.types || packageJson.typings);\n};\n\nexport const startTypeWatcher = (root: string): ChildProcess | null => {\n const projectDirs = getWorkspacePackageDirs(root).filter(isTypedWorkspacePackage);\n if (projectDirs.length === 0) {\n return null;\n }\n\n const relativeDirs = projectDirs.map((dir) => path.relative(root, dir));\n const child = spawn('npx', ['tsc', '-b', ...relativeDirs, '--watch', '--preserveWatchOutput', '--pretty'], {\n cwd: root,\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: process.platform === 'win32',\n });\n\n child.stdout?.on('data', forwardTscOutput);\n child.stderr?.on('data', forwardTscOutput);\n\n console.log(`Type watcher: tsc -b --watch over ${relativeDirs.length} workspace packages (disable with --no-types).`);\n\n return child;\n};\n"]}
|
|
@@ -3,8 +3,12 @@ import { getOwnPackageRoot } from '../../lib/packageRoot';
|
|
|
3
3
|
// The CDK app command (`cdk ... --app "<this>"`): quidproquo-deploy-awscdk's
|
|
4
4
|
// generic workspace app under ts-node hooks, with DEPLOY_* in the env.
|
|
5
5
|
export const getCdkAppCommand = () => {
|
|
6
|
+
// Resolve ts-node from this package, not the workspace cwd — under npm link
|
|
7
|
+
// the CLI's dependencies are never hoisted into the workspace's node_modules,
|
|
8
|
+
// so a bare `-r ts-node/...` preload fails there.
|
|
9
|
+
const tsNodeRegister = require.resolve('ts-node/register/transpile-only');
|
|
6
10
|
const cdkAppScript = path.join(getOwnPackageRoot(), 'lib', 'commonjs', 'bin', 'qpqCdkApp.js');
|
|
7
|
-
return `node -r
|
|
11
|
+
return `node -r "${tsNodeRegister}" "${cdkAppScript}"`;
|
|
8
12
|
};
|
|
9
13
|
// The docker build context for the go:docker deployer image.
|
|
10
14
|
export const getDockerDir = () => path.join(getOwnPackageRoot(), 'docker', 'cdk-deployer');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdkApp.js","sourceRoot":"","sources":["../../../../src/platforms/aws/cdkApp.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,6EAA6E;AAC7E,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAW,EAAE;IAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;IAC9F,OAAO,
|
|
1
|
+
{"version":3,"file":"cdkApp.js","sourceRoot":"","sources":["../../../../src/platforms/aws/cdkApp.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,6EAA6E;AAC7E,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAW,EAAE;IAC3C,4EAA4E;IAC5E,8EAA8E;IAC9E,kDAAkD;IAClD,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC1E,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;IAC9F,OAAO,YAAY,cAAc,MAAM,YAAY,GAAG,CAAC;AACzD,CAAC,CAAC;AAEF,6DAA6D;AAC7D,MAAM,CAAC,MAAM,YAAY,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC","sourcesContent":["import path from 'path';\n\nimport { getOwnPackageRoot } from '../../lib/packageRoot';\n\n// The CDK app command (`cdk ... --app \"<this>\"`): quidproquo-deploy-awscdk's\n// generic workspace app under ts-node hooks, with DEPLOY_* in the env.\nexport const getCdkAppCommand = (): string => {\n // Resolve ts-node from this package, not the workspace cwd — under npm link\n // the CLI's dependencies are never hoisted into the workspace's node_modules,\n // so a bare `-r ts-node/...` preload fails there.\n const tsNodeRegister = require.resolve('ts-node/register/transpile-only');\n const cdkAppScript = path.join(getOwnPackageRoot(), 'lib', 'commonjs', 'bin', 'qpqCdkApp.js');\n return `node -r \"${tsNodeRegister}\" \"${cdkAppScript}\"`;\n};\n\n// The docker build context for the go:docker deployer image.\nexport const getDockerDir = (): string => path.join(getOwnPackageRoot(), 'docker', 'cdk-deployer');\n"]}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// AWS implementation of `qpq clear-resources`: empty (never delete) the
|
|
2
|
+
// selected data resources. Storage drives are S3 buckets - emptied including
|
|
3
|
+
// every object version and delete marker, so versioned buckets come out clean
|
|
4
|
+
// too. Key value stores are DynamoDB tables - truncated by scanning keys only
|
|
5
|
+
// and batch-deleting. The stacks and the resources themselves are untouched,
|
|
6
|
+
// so this is the "reset my data" counterpart to `qpq teardown`.
|
|
7
|
+
//
|
|
8
|
+
// Runtime names are resolved from each service's live qpq config with the
|
|
9
|
+
// same naming utils the deployed runtime uses, so what gets emptied is
|
|
10
|
+
// exactly what the app reads and writes.
|
|
11
|
+
import { awsNamingUtils } from 'quidproquo-actionprocessor-awslambda';
|
|
12
|
+
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
13
|
+
import { qpqCoreUtils } from 'quidproquo-core';
|
|
14
|
+
import { BatchWriteItemCommand, DescribeTableCommand, DynamoDBClient, ResourceNotFoundException, ScanCommand, } from '@aws-sdk/client-dynamodb';
|
|
15
|
+
import { DeleteObjectsCommand, ListObjectVersionsCommand, NoSuchBucket, S3Client } from '@aws-sdk/client-s3';
|
|
16
|
+
import { loadServiceQpqConfig } from '../../lib/qpqConfigs';
|
|
17
|
+
import { isAwsCredentialsValid } from './awsCredentials';
|
|
18
|
+
// Same resolution as the runtime's resolveStorageDriveBucketName (not exported
|
|
19
|
+
// from the actionprocessor package root): honour the cross-module owner and
|
|
20
|
+
// its resourceNameOverride.
|
|
21
|
+
const resolveBucketName = (drive, qpqConfig) => {
|
|
22
|
+
const storageDriveConfig = qpqCoreUtils.getStorageDriveByName(drive, qpqConfig);
|
|
23
|
+
if (!storageDriveConfig) {
|
|
24
|
+
throw new Error(`Could not find storage drive config for [${drive}]`);
|
|
25
|
+
}
|
|
26
|
+
return awsNamingUtils.getConfigRuntimeResourceNameFromConfigWithServiceOverride(storageDriveConfig.owner?.resourceNameOverride || drive, qpqConfig, storageDriveConfig.owner?.module);
|
|
27
|
+
};
|
|
28
|
+
// Log a progress heartbeat roughly every this-many removals, so a large
|
|
29
|
+
// resource visibly ticks along instead of looking hung.
|
|
30
|
+
const PROGRESS_EVERY = 2000;
|
|
31
|
+
// Empty a bucket including all versions + delete markers. Returns the number
|
|
32
|
+
// of objects/versions removed.
|
|
33
|
+
const emptyBucket = async (bucketName, region) => {
|
|
34
|
+
const s3 = new S3Client({ region });
|
|
35
|
+
let removed = 0;
|
|
36
|
+
let lastReported = 0;
|
|
37
|
+
let keyMarker;
|
|
38
|
+
let versionIdMarker;
|
|
39
|
+
do {
|
|
40
|
+
const page = await s3.send(new ListObjectVersionsCommand({ Bucket: bucketName, KeyMarker: keyMarker, VersionIdMarker: versionIdMarker }));
|
|
41
|
+
const objects = [...(page.Versions ?? []), ...(page.DeleteMarkers ?? [])].map((v) => ({
|
|
42
|
+
Key: v.Key,
|
|
43
|
+
VersionId: v.VersionId,
|
|
44
|
+
}));
|
|
45
|
+
if (objects.length > 0) {
|
|
46
|
+
await s3.send(new DeleteObjectsCommand({ Bucket: bucketName, Delete: { Objects: objects, Quiet: true } }));
|
|
47
|
+
removed += objects.length;
|
|
48
|
+
}
|
|
49
|
+
if (removed - lastReported >= PROGRESS_EVERY) {
|
|
50
|
+
console.log(` ... ${removed} objects/versions so far`);
|
|
51
|
+
lastReported = removed;
|
|
52
|
+
}
|
|
53
|
+
keyMarker = page.IsTruncated ? page.NextKeyMarker : undefined;
|
|
54
|
+
versionIdMarker = page.IsTruncated ? page.NextVersionIdMarker : undefined;
|
|
55
|
+
} while (keyMarker || versionIdMarker);
|
|
56
|
+
return removed;
|
|
57
|
+
};
|
|
58
|
+
// Truncate a table: scan key attributes only, batch-delete 25 at a time,
|
|
59
|
+
// retrying unprocessed items. Returns the number of items removed.
|
|
60
|
+
const truncateTable = async (tableName, region) => {
|
|
61
|
+
const dynamo = new DynamoDBClient({ region });
|
|
62
|
+
const described = await dynamo.send(new DescribeTableCommand({ TableName: tableName }));
|
|
63
|
+
const keyAttributes = (described.Table?.KeySchema ?? []).map((k) => k.AttributeName);
|
|
64
|
+
// Alias every key attribute - a raw name could collide with a reserved word.
|
|
65
|
+
const projection = keyAttributes.map((_, i) => `#k${i}`).join(', ');
|
|
66
|
+
const attributeNames = Object.fromEntries(keyAttributes.map((attribute, i) => [`#k${i}`, attribute]));
|
|
67
|
+
let removed = 0;
|
|
68
|
+
let lastReported = 0;
|
|
69
|
+
let exclusiveStartKey;
|
|
70
|
+
do {
|
|
71
|
+
const page = await dynamo.send(new ScanCommand({
|
|
72
|
+
TableName: tableName,
|
|
73
|
+
ProjectionExpression: projection,
|
|
74
|
+
ExpressionAttributeNames: attributeNames,
|
|
75
|
+
ExclusiveStartKey: exclusiveStartKey,
|
|
76
|
+
}));
|
|
77
|
+
const items = page.Items ?? [];
|
|
78
|
+
for (let i = 0; i < items.length; i += 25) {
|
|
79
|
+
let requests = items.slice(i, i + 25).map((item) => ({ DeleteRequest: { Key: item } }));
|
|
80
|
+
while (requests.length > 0) {
|
|
81
|
+
const result = await dynamo.send(new BatchWriteItemCommand({ RequestItems: { [tableName]: requests } }));
|
|
82
|
+
removed += requests.length;
|
|
83
|
+
const unprocessed = result.UnprocessedItems?.[tableName] ?? [];
|
|
84
|
+
removed -= unprocessed.length;
|
|
85
|
+
requests = unprocessed;
|
|
86
|
+
if (requests.length > 0) {
|
|
87
|
+
// Throttled - back off briefly before retrying the leftovers.
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (removed - lastReported >= PROGRESS_EVERY) {
|
|
93
|
+
console.log(` ... ${removed} items so far`);
|
|
94
|
+
lastReported = removed;
|
|
95
|
+
}
|
|
96
|
+
exclusiveStartKey = page.LastEvaluatedKey;
|
|
97
|
+
} while (exclusiveStartKey);
|
|
98
|
+
return removed;
|
|
99
|
+
};
|
|
100
|
+
const clearResource = async (appName, resource) => {
|
|
101
|
+
const qpqConfig = loadServiceQpqConfig(appName, resource.service);
|
|
102
|
+
const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
|
|
103
|
+
if (resource.kind === 'storageDrive') {
|
|
104
|
+
const bucketName = resolveBucketName(resource.resourceName, qpqConfig);
|
|
105
|
+
console.log(` emptying s3://${bucketName} ...`);
|
|
106
|
+
try {
|
|
107
|
+
const removed = await emptyBucket(bucketName, region);
|
|
108
|
+
console.log(` emptied s3://${bucketName} (${removed} objects/versions)`);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error instanceof NoSuchBucket) {
|
|
112
|
+
console.log(` skipped s3://${bucketName} (bucket does not exist - not deployed?)`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// 'kvs' matches the resourceType every runtime KVS action processor passes,
|
|
120
|
+
// giving the deployed '-qpqkvs' table name suffix.
|
|
121
|
+
const tableName = awsNamingUtils.getKvsDynamoTableNameFromConfig(resource.resourceName, qpqConfig, 'kvs');
|
|
122
|
+
console.log(` truncating ${tableName} ...`);
|
|
123
|
+
try {
|
|
124
|
+
const removed = await truncateTable(tableName, region);
|
|
125
|
+
console.log(` truncated ${tableName} (${removed} items)`);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof ResourceNotFoundException) {
|
|
129
|
+
console.log(` skipped ${tableName} (table does not exist - not deployed?)`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
export const awsClearResources = async (appName, plan) => {
|
|
136
|
+
if (plan.kind === 'cancelled') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if ((await isAwsCredentialsValid()) === false) {
|
|
140
|
+
console.log('Credentials are expired or invalid.');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
console.log('\nEmptying resources:');
|
|
144
|
+
// Sequential on purpose: predictable output, and no burst of parallel scans
|
|
145
|
+
// against provisioned-throughput tables.
|
|
146
|
+
for (const resource of plan.resources) {
|
|
147
|
+
await clearResource(appName, resource);
|
|
148
|
+
}
|
|
149
|
+
console.log('Done.');
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=clearResources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clearResources.js","sourceRoot":"","sources":["../../../../src/platforms/aws/clearResources.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAC7E,gEAAgE;AAChE,EAAE;AACF,0EAA0E;AAC1E,uEAAuE;AACvE,yCAAyC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAa,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,yBAAyB,EACzB,WAAW,GAEZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,YAAY,EAAoB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG/H,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,+EAA+E;AAC/E,4EAA4E;AAC5E,4BAA4B;AAC5B,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAE,SAAoB,EAAU,EAAE;IACxE,MAAM,kBAAkB,GAAG,YAAY,CAAC,qBAAqB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAEhF,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4CAA4C,KAAK,GAAG,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,cAAc,CAAC,yDAAyD,CAC7E,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,IAAI,KAAK,EACvD,SAAS,EACT,kBAAkB,CAAC,KAAK,EAAE,MAAM,CACjC,CAAC;AACJ,CAAC,CAAC;AAEF,wEAAwE;AACxE,wDAAwD;AACxD,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,6EAA6E;AAC7E,+BAA+B;AAC/B,MAAM,WAAW,GAAG,KAAK,EAAE,UAAkB,EAAE,MAAc,EAAmB,EAAE;IAChF,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACpC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,SAA6B,CAAC;IAClC,IAAI,eAAmC,CAAC;IAExC,GAAG,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,yBAAyB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;QAE1I,MAAM,OAAO,GAAuB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxG,GAAG,EAAE,CAAC,CAAC,GAAI;YACX,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;QAEJ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YAC3G,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;QAC5B,CAAC;QAED,IAAI,OAAO,GAAG,YAAY,IAAI,cAAc,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,0BAA0B,CAAC,CAAC;YAC1D,YAAY,GAAG,OAAO,CAAC;QACzB,CAAC;QAED,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9D,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,CAAC,QAAQ,SAAS,IAAI,eAAe,EAAE;IAEvC,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,yEAAyE;AACzE,mEAAmE;AACnE,MAAM,aAAa,GAAG,KAAK,EAAE,SAAiB,EAAE,MAAc,EAAmB,EAAE;IACjF,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAE9C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACxF,MAAM,aAAa,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAc,CAAC,CAAC;IAEtF,6EAA6E;IAC7E,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAEtG,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,iBAAkD,CAAC;IAEvD,GAAG,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAC5B,IAAI,WAAW,CAAC;YACd,SAAS,EAAE,SAAS;YACpB,oBAAoB,EAAE,UAAU;YAChC,wBAAwB,EAAE,cAAc;YACxC,iBAAiB,EAAE,iBAAiB;SACrC,CAAC,CACH,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAE/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1C,IAAI,QAAQ,GAAmB,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YAExG,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;gBACzG,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC;gBAE3B,MAAM,WAAW,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC/D,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC;gBAC9B,QAAQ,GAAG,WAAW,CAAC;gBAEvB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACxB,8DAA8D;oBAC9D,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,OAAO,GAAG,YAAY,IAAI,cAAc,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,eAAe,CAAC,CAAC;YAC/C,YAAY,GAAG,OAAO,CAAC;QACzB,CAAC;QAED,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,CAAC;IAC5C,CAAC,QAAQ,iBAAiB,EAAE;IAE5B,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,KAAK,EAAE,OAAe,EAAE,QAA2B,EAAiB,EAAE;IAC1F,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,iBAAiB,CAAC,gCAAgC,CAAC,SAAS,CAAC,CAAC;IAE7E,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,mBAAmB,UAAU,MAAM,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,KAAK,OAAO,oBAAoB,CAAC,CAAC;QAC5E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,0CAA0C,CAAC,CAAC;gBACpF,OAAO;YACT,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QACD,OAAO;IACT,CAAC;IAED,4EAA4E;IAC5E,mDAAmD;IACnD,MAAM,SAAS,GAAG,cAAc,CAAC,+BAA+B,CAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC1G,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,KAAK,OAAO,SAAS,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,yBAAyB,EAAE,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,yCAAyC,CAAC,CAAC;YAC7E,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EAAE,OAAe,EAAE,IAAwB,EAAiB,EAAE;IAClG,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,IAAI,CAAC,MAAM,qBAAqB,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAErC,4EAA4E;IAC5E,yCAAyC;IACzC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC,CAAC","sourcesContent":["// AWS implementation of `qpq clear-resources`: empty (never delete) the\n// selected data resources. Storage drives are S3 buckets - emptied including\n// every object version and delete marker, so versioned buckets come out clean\n// too. Key value stores are DynamoDB tables - truncated by scanning keys only\n// and batch-deleting. The stacks and the resources themselves are untouched,\n// so this is the \"reset my data\" counterpart to `qpq teardown`.\n//\n// Runtime names are resolved from each service's live qpq config with the\n// same naming utils the deployed runtime uses, so what gets emptied is\n// exactly what the app reads and writes.\nimport { awsNamingUtils } from 'quidproquo-actionprocessor-awslambda';\nimport { qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport { QPQConfig, qpqCoreUtils } from 'quidproquo-core';\n\nimport {\n BatchWriteItemCommand,\n DescribeTableCommand,\n DynamoDBClient,\n ResourceNotFoundException,\n ScanCommand,\n WriteRequest,\n} from '@aws-sdk/client-dynamodb';\nimport { DeleteObjectsCommand, ListObjectVersionsCommand, NoSuchBucket, ObjectIdentifier, S3Client } from '@aws-sdk/client-s3';\n\nimport { ClearableResource, ClearResourcesPlan } from '../../lib/clearResourcesPlan';\nimport { loadServiceQpqConfig } from '../../lib/qpqConfigs';\nimport { isAwsCredentialsValid } from './awsCredentials';\n\n// Same resolution as the runtime's resolveStorageDriveBucketName (not exported\n// from the actionprocessor package root): honour the cross-module owner and\n// its resourceNameOverride.\nconst resolveBucketName = (drive: string, qpqConfig: QPQConfig): string => {\n const storageDriveConfig = qpqCoreUtils.getStorageDriveByName(drive, qpqConfig);\n\n if (!storageDriveConfig) {\n throw new Error(`Could not find storage drive config for [${drive}]`);\n }\n\n return awsNamingUtils.getConfigRuntimeResourceNameFromConfigWithServiceOverride(\n storageDriveConfig.owner?.resourceNameOverride || drive,\n qpqConfig,\n storageDriveConfig.owner?.module,\n );\n};\n\n// Log a progress heartbeat roughly every this-many removals, so a large\n// resource visibly ticks along instead of looking hung.\nconst PROGRESS_EVERY = 2000;\n\n// Empty a bucket including all versions + delete markers. Returns the number\n// of objects/versions removed.\nconst emptyBucket = async (bucketName: string, region: string): Promise<number> => {\n const s3 = new S3Client({ region });\n let removed = 0;\n let lastReported = 0;\n let keyMarker: string | undefined;\n let versionIdMarker: string | undefined;\n\n do {\n const page = await s3.send(new ListObjectVersionsCommand({ Bucket: bucketName, KeyMarker: keyMarker, VersionIdMarker: versionIdMarker }));\n\n const objects: ObjectIdentifier[] = [...(page.Versions ?? []), ...(page.DeleteMarkers ?? [])].map((v) => ({\n Key: v.Key!,\n VersionId: v.VersionId,\n }));\n\n if (objects.length > 0) {\n await s3.send(new DeleteObjectsCommand({ Bucket: bucketName, Delete: { Objects: objects, Quiet: true } }));\n removed += objects.length;\n }\n\n if (removed - lastReported >= PROGRESS_EVERY) {\n console.log(` ... ${removed} objects/versions so far`);\n lastReported = removed;\n }\n\n keyMarker = page.IsTruncated ? page.NextKeyMarker : undefined;\n versionIdMarker = page.IsTruncated ? page.NextVersionIdMarker : undefined;\n } while (keyMarker || versionIdMarker);\n\n return removed;\n};\n\n// Truncate a table: scan key attributes only, batch-delete 25 at a time,\n// retrying unprocessed items. Returns the number of items removed.\nconst truncateTable = async (tableName: string, region: string): Promise<number> => {\n const dynamo = new DynamoDBClient({ region });\n\n const described = await dynamo.send(new DescribeTableCommand({ TableName: tableName }));\n const keyAttributes = (described.Table?.KeySchema ?? []).map((k) => k.AttributeName!);\n\n // Alias every key attribute - a raw name could collide with a reserved word.\n const projection = keyAttributes.map((_, i) => `#k${i}`).join(', ');\n const attributeNames = Object.fromEntries(keyAttributes.map((attribute, i) => [`#k${i}`, attribute]));\n\n let removed = 0;\n let lastReported = 0;\n let exclusiveStartKey: Record<string, any> | undefined;\n\n do {\n const page = await dynamo.send(\n new ScanCommand({\n TableName: tableName,\n ProjectionExpression: projection,\n ExpressionAttributeNames: attributeNames,\n ExclusiveStartKey: exclusiveStartKey,\n }),\n );\n\n const items = page.Items ?? [];\n\n for (let i = 0; i < items.length; i += 25) {\n let requests: WriteRequest[] = items.slice(i, i + 25).map((item) => ({ DeleteRequest: { Key: item } }));\n\n while (requests.length > 0) {\n const result = await dynamo.send(new BatchWriteItemCommand({ RequestItems: { [tableName]: requests } }));\n removed += requests.length;\n\n const unprocessed = result.UnprocessedItems?.[tableName] ?? [];\n removed -= unprocessed.length;\n requests = unprocessed;\n\n if (requests.length > 0) {\n // Throttled - back off briefly before retrying the leftovers.\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n }\n }\n\n if (removed - lastReported >= PROGRESS_EVERY) {\n console.log(` ... ${removed} items so far`);\n lastReported = removed;\n }\n\n exclusiveStartKey = page.LastEvaluatedKey;\n } while (exclusiveStartKey);\n\n return removed;\n};\n\nconst clearResource = async (appName: string, resource: ClearableResource): Promise<void> => {\n const qpqConfig = loadServiceQpqConfig(appName, resource.service);\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n\n if (resource.kind === 'storageDrive') {\n const bucketName = resolveBucketName(resource.resourceName, qpqConfig);\n console.log(` emptying s3://${bucketName} ...`);\n try {\n const removed = await emptyBucket(bucketName, region);\n console.log(` emptied s3://${bucketName} (${removed} objects/versions)`);\n } catch (error) {\n if (error instanceof NoSuchBucket) {\n console.log(` skipped s3://${bucketName} (bucket does not exist - not deployed?)`);\n return;\n }\n throw error;\n }\n return;\n }\n\n // 'kvs' matches the resourceType every runtime KVS action processor passes,\n // giving the deployed '-qpqkvs' table name suffix.\n const tableName = awsNamingUtils.getKvsDynamoTableNameFromConfig(resource.resourceName, qpqConfig, 'kvs');\n console.log(` truncating ${tableName} ...`);\n try {\n const removed = await truncateTable(tableName, region);\n console.log(` truncated ${tableName} (${removed} items)`);\n } catch (error) {\n if (error instanceof ResourceNotFoundException) {\n console.log(` skipped ${tableName} (table does not exist - not deployed?)`);\n return;\n }\n throw error;\n }\n};\n\nexport const awsClearResources = async (appName: string, plan: ClearResourcesPlan): Promise<void> => {\n if (plan.kind === 'cancelled') {\n return;\n }\n\n if ((await isAwsCredentialsValid()) === false) {\n console.log('Credentials are expired or invalid.');\n return;\n }\n\n console.log('\\nEmptying resources:');\n\n // Sequential on purpose: predictable output, and no burst of parallel scans\n // against provisioned-throughput tables.\n for (const resource of plan.resources) {\n await clearResource(appName, resource);\n }\n\n console.log('Done.');\n};\n"]}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
// AWS sequential deploy (`qpq go`). Per service it: synths the QPQ config,
|
|
2
2
|
// rspack-bundles the backend (programmatic — no per-service rspack.config.ts),
|
|
3
3
|
// deploys the inf/api/web CDK stacks (cdk runs the generic workspace app via
|
|
4
|
-
// --app), bundles views (rspack, production) and syncs them to S3.
|
|
4
|
+
// --app), bundles views (rspack, production) and syncs them to S3. Finally, any
|
|
5
|
+
// service that opts into module federation has its federated remote published
|
|
6
|
+
// (built + uploaded + manifest flipped), so a deploy that ships new backend
|
|
7
|
+
// code also republishes the code the lambdas federate.
|
|
5
8
|
import { qpqDeployAwsCdkUtils } from 'quidproquo-deploy-awscdk';
|
|
6
9
|
import { getServiceRspackConfig } from 'quidproquo-deploy-rspack';
|
|
7
10
|
import path from 'path';
|
|
8
11
|
import { synthCommand } from '../../commands/synth';
|
|
9
12
|
import { getServiceDirectory, getServiceNamesWithViews } from '../../lib/discovery';
|
|
10
13
|
import { runAppHook } from '../../lib/hooks';
|
|
11
|
-
import { loadServiceQpqConfig } from '../../lib/qpqConfigs';
|
|
14
|
+
import { getServiceNamesWithFederation, loadServiceQpqConfig } from '../../lib/qpqConfigs';
|
|
12
15
|
import { runRspack } from '../../lib/rspackRun';
|
|
13
16
|
import { runCommand } from '../../lib/runCommand';
|
|
14
17
|
import { logTimeEnd, logTimeStart } from '../../lib/timing';
|
|
15
18
|
import { bundleViews } from '../../lib/views';
|
|
16
19
|
import { isAwsCredentialsValid } from './awsCredentials';
|
|
20
|
+
import { awsPublish } from './publish';
|
|
17
21
|
import { deployAccountStack, deployBootstrapStack, deployDomainStack, deployServiceStack } from './stacks';
|
|
18
22
|
import { syncViewsToS3 } from './viewsSync';
|
|
19
23
|
const buildAndDeploy = async (serviceName, needsSynth, needsBuildApi, needsInfStack, needsApiStack, needsWebStack, needsViewStack, appName) => {
|
|
@@ -95,6 +99,16 @@ export const awsGo = async (appName, plan) => {
|
|
|
95
99
|
return buildAndDeploy(service, phase === 0, phase === 1 && shouldBuildApi, phase === 2 && (allStacks || stacks === 'inf'), phase === 3 && (allStacks || stacks === 'api'), phase === 4 && (allStacks || stacks === 'web'), phase === 5 && (allStacks || stacks === 'views'), appName);
|
|
96
100
|
}, Promise.resolve());
|
|
97
101
|
}
|
|
102
|
+
// Publishing federated remotes only matters once the new backend code is
|
|
103
|
+
// live, so it's gated on the api stack having been (re)deployed. Services
|
|
104
|
+
// without defineFederatedModuleStore(...) are skipped.
|
|
105
|
+
if (allStacks || stacks === 'api') {
|
|
106
|
+
const federatedServices = getServiceNamesWithFederation(appName, services);
|
|
107
|
+
if (federatedServices.length > 0) {
|
|
108
|
+
console.log('\n\nPublishing federated backends\n');
|
|
109
|
+
await awsPublish(appName, federatedServices);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
98
112
|
logTimeEnd('totalTime');
|
|
99
113
|
};
|
|
100
114
|
//# sourceMappingURL=go.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"go.js","sourceRoot":"","sources":["../../../../src/platforms/aws/go.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,mEAAmE;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,cAAc,GAAG,KAAK,EAC1B,WAAmB,EACnB,UAAmB,EACnB,aAAsB,EACtB,aAAsB,EACtB,aAAsB,EACtB,aAAsB,EACtB,cAAuB,EACvB,OAAe,EACA,EAAE;IACjB,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjC,MAAM,YAAY,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAEvE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,SAAS,CAAC,sBAAsB,CAAC,mBAAmB,EAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAChI,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QACzC,MAAM,kBAAkB,CAAC,WAAW,EAAE,oBAAoB,CAAC,eAAe,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;IAC5G,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QACzC,MAAM,kBAAkB,CAAC,WAAW,EAAE,oBAAoB,CAAC,eAAe,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;IAC5G,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QACzC,MAAM,kBAAkB,CAAC,WAAW,EAAE,oBAAoB,CAAC,eAAe,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;IAC5G,CAAC;IAED,IAAI,cAAc,IAAI,wBAAwB,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9E,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;QAC7C,MAAM,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACxC,MAAM,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EAAE,OAAe,EAAE,IAAgB,EAAiB,EAAE;IAC9E,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,YAAY,CAAC,WAAW,CAAC,CAAC;IAE1B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvC,UAAU,CAAC,WAAW,CAAC,CAAC;QACxB,OAAO;IACT,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC9B,0EAA0E;QAC1E,0EAA0E;QAC1E,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,MAAM,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,UAAU,CAAC,WAAW,CAAC,CAAC;QACxB,OAAO;IACT,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,MAAM,cAAc,GAAG,MAAM,KAAK,OAAO,CAAC;IAE1C,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjC,IAAI,CAAC,MAAM,qBAAqB,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,KAAK,KAAK,CAAC;IAEnC,MAAM,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAEvC,4EAA4E;IAC5E,oEAAoE;IACpE,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC3C,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC;IAE1E,sEAAsE;IACtE,IAAI,SAAS,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACpC,MAAM,YAAY,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;YAC3C,MAAM,GAAG,CAAC;YACV,OAAO,cAAc,CACnB,OAAO,EACP,KAAK,KAAK,CAAC,EACX,KAAK,KAAK,CAAC,IAAI,cAAc,EAC7B,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,KAAK,KAAK,CAAC,EAC9C,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,KAAK,KAAK,CAAC,EAC9C,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,KAAK,KAAK,CAAC,EAC9C,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,KAAK,OAAO,CAAC,EAChD,OAAO,CACR,CAAC;QACJ,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACxB,CAAC;IAED,UAAU,CAAC,WAAW,CAAC,CAAC;AAC1B,CAAC,CAAC","sourcesContent":["// AWS sequential deploy (`qpq go`). Per service it: synths the QPQ config,\n// rspack-bundles the backend (programmatic — no per-service rspack.config.ts),\n// deploys the inf/api/web CDK stacks (cdk runs the generic workspace app via\n// --app), bundles views (rspack, production) and syncs them to S3.\nimport { qpqDeployAwsCdkUtils } from 'quidproquo-deploy-awscdk';\nimport { getServiceRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport path from 'path';\n\nimport { synthCommand } from '../../commands/synth';\nimport { DeployPlan } from '../../lib/deployPrompts';\nimport { getServiceDirectory, getServiceNamesWithViews } from '../../lib/discovery';\nimport { runAppHook } from '../../lib/hooks';\nimport { loadServiceQpqConfig } from '../../lib/qpqConfigs';\nimport { runRspack } from '../../lib/rspackRun';\nimport { runCommand } from '../../lib/runCommand';\nimport { logTimeEnd, logTimeStart } from '../../lib/timing';\nimport { bundleViews } from '../../lib/views';\nimport { isAwsCredentialsValid } from './awsCredentials';\nimport { deployAccountStack, deployBootstrapStack, deployDomainStack, deployServiceStack } from './stacks';\nimport { syncViewsToS3 } from './viewsSync';\n\nconst buildAndDeploy = async (\n serviceName: string,\n needsSynth: boolean,\n needsBuildApi: boolean,\n needsInfStack: boolean,\n needsApiStack: boolean,\n needsWebStack: boolean,\n needsViewStack: boolean,\n appName: string,\n): Promise<void> => {\n if (needsSynth) {\n console.log('Synth QPQ Configs');\n await synthCommand([serviceName, '--app', appName]);\n }\n\n const qpqConfigForService = loadServiceQpqConfig(appName, serviceName);\n\n if (needsBuildApi) {\n console.log('Building Api: ', serviceName);\n await runRspack(getServiceRspackConfig(qpqConfigForService, path.join(getServiceDirectory(appName, serviceName), 'service')));\n }\n\n if (needsInfStack) {\n console.log('Deploy Inf: ', serviceName);\n await deployServiceStack(serviceName, qpqDeployAwsCdkUtils.getInfStackName(qpqConfigForService), appName);\n }\n\n if (needsApiStack) {\n console.log('Deploy Api: ', serviceName);\n await deployServiceStack(serviceName, qpqDeployAwsCdkUtils.getApiStackName(qpqConfigForService), appName);\n }\n\n if (needsWebStack) {\n console.log('Deploy Web: ', serviceName);\n await deployServiceStack(serviceName, qpqDeployAwsCdkUtils.getWebStackName(qpqConfigForService), appName);\n }\n\n if (needsViewStack && getServiceNamesWithViews(appName).includes(serviceName)) {\n console.log('Building Views: ', serviceName);\n await bundleViews(appName, serviceName);\n await syncViewsToS3(appName, serviceName);\n }\n};\n\nexport const awsGo = async (appName: string, plan: DeployPlan): Promise<void> => {\n if (plan.kind === 'cancelled') {\n return;\n }\n\n logTimeStart('totalTime');\n\n if (plan.kind === 'account') {\n await deployAccountStack(plan.appName);\n logTimeEnd('totalTime');\n return;\n }\n\n if (plan.kind === 'bootstrap') {\n // Actor bootstraps deploy into a shared account — leave the account-level\n // guardrails stack alone (deploy it explicitly via the 'account' option).\n if (process.env.ACTOR_NAME) {\n console.log('Skipping account stack for actor deploy');\n } else {\n await deployAccountStack(plan.appName);\n }\n if (plan.includeDomain) {\n await deployDomainStack(plan.appName);\n }\n await deployBootstrapStack(plan.appName);\n logTimeEnd('totalTime');\n return;\n }\n\n const { services, stacks } = plan;\n const shouldBuildApi = stacks !== 'views';\n\n console.log('\\n\\nRunning automated deploy\\n\\n');\n console.log('Executing for services');\n console.log(services.join(', '));\n\n if ((await isAwsCredentialsValid()) === false) {\n console.log('Credentials are expired or invalid.');\n return;\n }\n\n const allStacks = stacks === 'all';\n\n await runAppHook(appName, 'predeploy');\n\n // Workspace packages resolve through their built dist/ (package.json main),\n // so bundles would ship stale code unless every lib is tsc'd first.\n console.log('Building workspace packages');\n await runCommand('npm', ['run', 'build', '--workspaces', '--if-present']);\n\n // Views uploads resolve bucket names from the shell service's config.\n if (allStacks || stacks === 'views') {\n await synthCommand(['shell', '--app', appName]);\n }\n\n for (let phase = 0; phase <= 5; phase += 1) {\n await services.reduce(async (cur, service) => {\n await cur;\n return buildAndDeploy(\n service,\n phase === 0,\n phase === 1 && shouldBuildApi,\n phase === 2 && (allStacks || stacks === 'inf'),\n phase === 3 && (allStacks || stacks === 'api'),\n phase === 4 && (allStacks || stacks === 'web'),\n phase === 5 && (allStacks || stacks === 'views'),\n appName,\n );\n }, Promise.resolve());\n }\n\n logTimeEnd('totalTime');\n};\n"]}
|
|
1
|
+
{"version":3,"file":"go.js","sourceRoot":"","sources":["../../../../src/platforms/aws/go.ts"],"names":[],"mappings":"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","sourcesContent":["// AWS sequential deploy (`qpq go`). Per service it: synths the QPQ config,\n// rspack-bundles the backend (programmatic — no per-service rspack.config.ts),\n// deploys the inf/api/web CDK stacks (cdk runs the generic workspace app via\n// --app), bundles views (rspack, production) and syncs them to S3. Finally, any\n// service that opts into module federation has its federated remote published\n// (built + uploaded + manifest flipped), so a deploy that ships new backend\n// code also republishes the code the lambdas federate.\nimport { qpqDeployAwsCdkUtils } from 'quidproquo-deploy-awscdk';\nimport { getServiceRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport path from 'path';\n\nimport { synthCommand } from '../../commands/synth';\nimport { DeployPlan } from '../../lib/deployPrompts';\nimport { getServiceDirectory, getServiceNamesWithViews } from '../../lib/discovery';\nimport { runAppHook } from '../../lib/hooks';\nimport { getServiceNamesWithFederation, loadServiceQpqConfig } from '../../lib/qpqConfigs';\nimport { runRspack } from '../../lib/rspackRun';\nimport { runCommand } from '../../lib/runCommand';\nimport { logTimeEnd, logTimeStart } from '../../lib/timing';\nimport { bundleViews } from '../../lib/views';\nimport { isAwsCredentialsValid } from './awsCredentials';\nimport { awsPublish } from './publish';\nimport { deployAccountStack, deployBootstrapStack, deployDomainStack, deployServiceStack } from './stacks';\nimport { syncViewsToS3 } from './viewsSync';\n\nconst buildAndDeploy = async (\n serviceName: string,\n needsSynth: boolean,\n needsBuildApi: boolean,\n needsInfStack: boolean,\n needsApiStack: boolean,\n needsWebStack: boolean,\n needsViewStack: boolean,\n appName: string,\n): Promise<void> => {\n if (needsSynth) {\n console.log('Synth QPQ Configs');\n await synthCommand([serviceName, '--app', appName]);\n }\n\n const qpqConfigForService = loadServiceQpqConfig(appName, serviceName);\n\n if (needsBuildApi) {\n console.log('Building Api: ', serviceName);\n await runRspack(getServiceRspackConfig(qpqConfigForService, path.join(getServiceDirectory(appName, serviceName), 'service')));\n }\n\n if (needsInfStack) {\n console.log('Deploy Inf: ', serviceName);\n await deployServiceStack(serviceName, qpqDeployAwsCdkUtils.getInfStackName(qpqConfigForService), appName);\n }\n\n if (needsApiStack) {\n console.log('Deploy Api: ', serviceName);\n await deployServiceStack(serviceName, qpqDeployAwsCdkUtils.getApiStackName(qpqConfigForService), appName);\n }\n\n if (needsWebStack) {\n console.log('Deploy Web: ', serviceName);\n await deployServiceStack(serviceName, qpqDeployAwsCdkUtils.getWebStackName(qpqConfigForService), appName);\n }\n\n if (needsViewStack && getServiceNamesWithViews(appName).includes(serviceName)) {\n console.log('Building Views: ', serviceName);\n await bundleViews(appName, serviceName);\n await syncViewsToS3(appName, serviceName);\n }\n};\n\nexport const awsGo = async (appName: string, plan: DeployPlan): Promise<void> => {\n if (plan.kind === 'cancelled') {\n return;\n }\n\n logTimeStart('totalTime');\n\n if (plan.kind === 'account') {\n await deployAccountStack(plan.appName);\n logTimeEnd('totalTime');\n return;\n }\n\n if (plan.kind === 'bootstrap') {\n // Actor bootstraps deploy into a shared account — leave the account-level\n // guardrails stack alone (deploy it explicitly via the 'account' option).\n if (process.env.ACTOR_NAME) {\n console.log('Skipping account stack for actor deploy');\n } else {\n await deployAccountStack(plan.appName);\n }\n if (plan.includeDomain) {\n await deployDomainStack(plan.appName);\n }\n await deployBootstrapStack(plan.appName);\n logTimeEnd('totalTime');\n return;\n }\n\n const { services, stacks } = plan;\n const shouldBuildApi = stacks !== 'views';\n\n console.log('\\n\\nRunning automated deploy\\n\\n');\n console.log('Executing for services');\n console.log(services.join(', '));\n\n if ((await isAwsCredentialsValid()) === false) {\n console.log('Credentials are expired or invalid.');\n return;\n }\n\n const allStacks = stacks === 'all';\n\n await runAppHook(appName, 'predeploy');\n\n // Workspace packages resolve through their built dist/ (package.json main),\n // so bundles would ship stale code unless every lib is tsc'd first.\n console.log('Building workspace packages');\n await runCommand('npm', ['run', 'build', '--workspaces', '--if-present']);\n\n // Views uploads resolve bucket names from the shell service's config.\n if (allStacks || stacks === 'views') {\n await synthCommand(['shell', '--app', appName]);\n }\n\n for (let phase = 0; phase <= 5; phase += 1) {\n await services.reduce(async (cur, service) => {\n await cur;\n return buildAndDeploy(\n service,\n phase === 0,\n phase === 1 && shouldBuildApi,\n phase === 2 && (allStacks || stacks === 'inf'),\n phase === 3 && (allStacks || stacks === 'api'),\n phase === 4 && (allStacks || stacks === 'web'),\n phase === 5 && (allStacks || stacks === 'views'),\n appName,\n );\n }, Promise.resolve());\n }\n\n // Publishing federated remotes only matters once the new backend code is\n // live, so it's gated on the api stack having been (re)deployed. Services\n // without defineFederatedModuleStore(...) are skipped.\n if (allStacks || stacks === 'api') {\n const federatedServices = getServiceNamesWithFederation(appName, services);\n if (federatedServices.length > 0) {\n console.log('\\n\\nPublishing federated backends\\n');\n await awsPublish(appName, federatedServices);\n }\n }\n\n logTimeEnd('totalTime');\n};\n"]}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// parallel via docker instead of sequentially:
|
|
3
3
|
//
|
|
4
4
|
// 1. Build everything locally first: QPQ config synth, rspack API bundles,
|
|
5
|
-
// rspack view bundles,
|
|
6
|
-
//
|
|
5
|
+
// rspack view bundles, rspack federated-remote bundles, and one
|
|
6
|
+
// self-contained CDK cloud assembly per service
|
|
7
|
+
// (`cdk synth --output dist/qpq/cdk-docker/<app>/<service>`).
|
|
7
8
|
// 2. Wave 1 (inf): one docker container per service deploys its inf stack,
|
|
8
9
|
// all concurrently. Containers run only the CDK CLI against the mounted
|
|
9
10
|
// pre-synthesized assembly — the workspace (and its npm-linked
|
|
@@ -13,6 +14,9 @@
|
|
|
13
14
|
// each other).
|
|
14
15
|
// 4. Views: the already-built bundles are synced to S3, one aws-cli
|
|
15
16
|
// container per service, all concurrently.
|
|
17
|
+
// 5. Publish: the already-built federated remotes (backends that opt in with
|
|
18
|
+
// defineFederatedModuleStore(...)) are synced to S3 and their manifests
|
|
19
|
+
// flipped, one aws-cli container per service, all concurrently.
|
|
16
20
|
//
|
|
17
21
|
// domain/bootstrap menu options deploy host-side exactly like `qpq go` —
|
|
18
22
|
// a single stack gains nothing from docker.
|
|
@@ -28,7 +32,7 @@ import path from 'path';
|
|
|
28
32
|
import { synthCommand } from '../../commands/synth';
|
|
29
33
|
import { getRoot, getServiceDirectory, getServiceNamesWithViews } from '../../lib/discovery';
|
|
30
34
|
import { runAppHook } from '../../lib/hooks';
|
|
31
|
-
import { loadServiceQpqConfig } from '../../lib/qpqConfigs';
|
|
35
|
+
import { getServiceNamesWithFederation, loadServiceQpqConfig } from '../../lib/qpqConfigs';
|
|
32
36
|
import { runRspack } from '../../lib/rspackRun';
|
|
33
37
|
import { nextPrefixColor, runCommand, runCommandPrefixed } from '../../lib/runCommand';
|
|
34
38
|
import { assertNoFailures, runTasks } from '../../lib/runTasks';
|
|
@@ -36,6 +40,7 @@ import { logTimeEnd, logTimeStart } from '../../lib/timing';
|
|
|
36
40
|
import { bundleViews, getViewsDistDir } from '../../lib/views';
|
|
37
41
|
import { isAwsCredentialsValid } from './awsCredentials';
|
|
38
42
|
import { getCdkAppCommand, getDockerDir } from './cdkApp';
|
|
43
|
+
import { buildRemote, resolvePublishTarget } from './remote';
|
|
39
44
|
import { deployAccountStack, deployBootstrapStack, deployDomainStack } from './stacks';
|
|
40
45
|
import { getViewsS3Destinations } from './viewsSync';
|
|
41
46
|
const IMAGE_TAG = 'qpq-cdk-deployer';
|
|
@@ -129,6 +134,42 @@ const dockerSyncViews = async (color, appName, serviceName) => {
|
|
|
129
134
|
], { color });
|
|
130
135
|
}
|
|
131
136
|
};
|
|
137
|
+
// Publish one service's already-built federated remote to S3 in a throwaway
|
|
138
|
+
// aws-cli container named <service>-publish. The version dir (everything the
|
|
139
|
+
// manifest references) syncs first, then the manifest.json is copied last —
|
|
140
|
+
// same ordering guarantee as the host-side publish, so a reading lambda never
|
|
141
|
+
// sees a manifest whose files aren't all present yet. resolvePublishTarget reads
|
|
142
|
+
// the manifest hash + resolved bucket/prefix from the buildRemote output.
|
|
143
|
+
const dockerPublishRemote = async (color, appName, serviceName) => {
|
|
144
|
+
const { publishPath, manifestHash, bucketName, servicePrefix } = resolvePublishTarget(appName, serviceName);
|
|
145
|
+
const containerName = `${serviceName}-publish`;
|
|
146
|
+
// /publish is the service-remote-published dir; the hashed version subdir and
|
|
147
|
+
// manifest.json both live directly under it.
|
|
148
|
+
const s3Operations = [
|
|
149
|
+
['s3', 'sync', `/publish/${manifestHash}`, `s3://${bucketName}/${servicePrefix}/${manifestHash}`],
|
|
150
|
+
['s3', 'cp', '/publish/manifest.json', `s3://${bucketName}/${servicePrefix}/manifest.json`],
|
|
151
|
+
];
|
|
152
|
+
for (const s3Operation of s3Operations) {
|
|
153
|
+
await runCommandPrefixed(`publish:${serviceName}`, 'docker', [
|
|
154
|
+
'rm',
|
|
155
|
+
'-f',
|
|
156
|
+
containerName,
|
|
157
|
+
'>/dev/null',
|
|
158
|
+
'2>&1',
|
|
159
|
+
';',
|
|
160
|
+
'docker',
|
|
161
|
+
'run',
|
|
162
|
+
'--rm',
|
|
163
|
+
'--name',
|
|
164
|
+
containerName,
|
|
165
|
+
'-v',
|
|
166
|
+
`"${publishPath}:/publish:ro"`,
|
|
167
|
+
...AWS_ENV_PASSTHROUGH.flatMap((name) => ['-e', name]),
|
|
168
|
+
AWS_CLI_IMAGE,
|
|
169
|
+
...s3Operation,
|
|
170
|
+
], { color });
|
|
171
|
+
}
|
|
172
|
+
};
|
|
132
173
|
export const awsGoDocker = async (appName, plan) => {
|
|
133
174
|
if (plan.kind === 'cancelled') {
|
|
134
175
|
return;
|
|
@@ -162,17 +203,14 @@ export const awsGoDocker = async (appName, plan) => {
|
|
|
162
203
|
console.log('\n\nRunning dockerized deploy\n\n');
|
|
163
204
|
console.log('Executing for services');
|
|
164
205
|
console.log(services.join(', '));
|
|
206
|
+
// Federated publish needs the api stack (needApi ⇒ needCdkDeploys), so any
|
|
207
|
+
// docker preflight it requires is already covered by these two checks.
|
|
165
208
|
if (needCdkDeploys || viewServices.length > 0) {
|
|
166
209
|
await assertDockerRunning();
|
|
167
210
|
}
|
|
168
211
|
if (needCdkDeploys) {
|
|
169
212
|
await buildDeployerImage();
|
|
170
213
|
}
|
|
171
|
-
if (viewServices.length > 0) {
|
|
172
|
-
// Pull up front so the views wave doesn't race N containers into pulling
|
|
173
|
-
// the same image.
|
|
174
|
-
await runCommand('docker', ['pull', AWS_CLI_IMAGE]);
|
|
175
|
-
}
|
|
176
214
|
// ---- Phase 1: build everything locally ----
|
|
177
215
|
console.log('\n=== Phase 1: local builds ===\n');
|
|
178
216
|
await runAppHook(appName, 'predeploy');
|
|
@@ -188,6 +226,16 @@ export const awsGoDocker = async (appName, plan) => {
|
|
|
188
226
|
for (const service of qpqSynthServices) {
|
|
189
227
|
await synthCommand([service, '--app', appName]);
|
|
190
228
|
}
|
|
229
|
+
// Federated remotes are the backend story code, so republish them whenever the
|
|
230
|
+
// api stack was (re)deployed. Skips services without defineFederatedModuleStore(...).
|
|
231
|
+
// Detected here (after the workspace build) because it loads each service's config.
|
|
232
|
+
const federatedServices = needApi ? getServiceNamesWithFederation(appName, services) : [];
|
|
233
|
+
// The aws-cli image backs both the views sync (phase 4) and the federated
|
|
234
|
+
// publish (phase 5) waves. Pull once now so those waves don't race N containers
|
|
235
|
+
// into pulling the same image. Docker is already confirmed running above.
|
|
236
|
+
if (viewServices.length > 0 || federatedServices.length > 0) {
|
|
237
|
+
await runCommand('docker', ['pull', AWS_CLI_IMAGE]);
|
|
238
|
+
}
|
|
191
239
|
if (stacks !== 'views') {
|
|
192
240
|
assertNoFailures('API bundling', await runTasks(services.map((service) => ({
|
|
193
241
|
label: `bundle-api:${service}`,
|
|
@@ -205,6 +253,16 @@ export const awsGoDocker = async (appName, plan) => {
|
|
|
205
253
|
await bundleViews(appName, service);
|
|
206
254
|
},
|
|
207
255
|
})), BUILD_CONCURRENCY));
|
|
256
|
+
// Build the federated remotes locally now (rspack needs the workspace), so the
|
|
257
|
+
// publish wave only has to sync the already-built output — mirroring how views
|
|
258
|
+
// are bundled here and synced later.
|
|
259
|
+
assertNoFailures('Remote bundling', await runTasks(federatedServices.map((service) => ({
|
|
260
|
+
label: `bundle-remote:${service}`,
|
|
261
|
+
run: async () => {
|
|
262
|
+
console.log(`Bundling federated remote: [${service}]`);
|
|
263
|
+
await buildRemote(appName, service);
|
|
264
|
+
},
|
|
265
|
+
})), BUILD_CONCURRENCY));
|
|
208
266
|
if (needCdkDeploys) {
|
|
209
267
|
// One self-contained cloud assembly per service (all its stacks + staged
|
|
210
268
|
// assets) — separate --output dirs so synths can run in parallel and
|
|
@@ -214,7 +272,7 @@ export const awsGoDocker = async (appName, plan) => {
|
|
|
214
272
|
run: async () => {
|
|
215
273
|
const outputDir = assemblyDir(appName, service);
|
|
216
274
|
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
217
|
-
await runCommandPrefixed(`cdk-synth:${service}`, 'npx', ['cdk', 'synth', '--
|
|
275
|
+
await runCommandPrefixed(`cdk-synth:${service}`, 'npx', ['cdk', 'synth', '--quiet', '--app', `'${getCdkAppCommand()}'`, '--output', `"${outputDir}"`], {
|
|
218
276
|
cwd: getRoot(),
|
|
219
277
|
env: { DEPLOY_SERVICE_NAME: service, DEPLOY_APP_NAME: appName },
|
|
220
278
|
color: nextPrefixColor(),
|
|
@@ -266,6 +324,16 @@ export const awsGoDocker = async (appName, plan) => {
|
|
|
266
324
|
})), DEPLOY_CONCURRENCY));
|
|
267
325
|
logTimeEnd('viewsWave');
|
|
268
326
|
}
|
|
327
|
+
// ---- Phase 5: publish the pre-built federated remotes to S3, all concurrent ----
|
|
328
|
+
if (federatedServices.length > 0) {
|
|
329
|
+
console.log('\n=== Phase 5: publish federated remotes to S3 (docker) ===\n');
|
|
330
|
+
logTimeStart('publishWave');
|
|
331
|
+
assertNoFailures('Federated publish', await runTasks(federatedServices.map((service) => ({
|
|
332
|
+
label: `publish:${service}`,
|
|
333
|
+
run: () => dockerPublishRemote(nextPrefixColor(), appName, service),
|
|
334
|
+
})), DEPLOY_CONCURRENCY));
|
|
335
|
+
logTimeEnd('publishWave');
|
|
336
|
+
}
|
|
269
337
|
logTimeEnd('totalTime');
|
|
270
338
|
};
|
|
271
339
|
//# sourceMappingURL=goDocker.js.map
|