@geekmidas/cli 1.10.28 → 1.10.29
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 +9 -0
- package/dist/{fullstack-secrets-CsxU84eN.cjs → fullstack-secrets-Bchl2MDd.cjs} +2 -1
- package/dist/{fullstack-secrets-CsxU84eN.cjs.map → fullstack-secrets-Bchl2MDd.cjs.map} +1 -1
- package/dist/{fullstack-secrets-BDE7Sk93.mjs → fullstack-secrets-P84v8zWn.mjs} +2 -1
- package/dist/{fullstack-secrets-BDE7Sk93.mjs.map → fullstack-secrets-P84v8zWn.mjs.map} +1 -1
- package/dist/index.cjs +64 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +64 -18
- package/dist/index.mjs.map +1 -1
- package/dist/{reconcile-DF8t4HC7.cjs → reconcile-CZtzdrnc.cjs} +2 -2
- package/dist/{reconcile-DF8t4HC7.cjs.map → reconcile-CZtzdrnc.cjs.map} +1 -1
- package/dist/{reconcile-BjWhjfSp.mjs → reconcile-DAAKa0YR.mjs} +2 -2
- package/dist/{reconcile-BjWhjfSp.mjs.map → reconcile-DAAKa0YR.mjs.map} +1 -1
- package/package.json +5 -5
- package/src/init/__tests__/generators.spec.ts +5 -3
- package/src/init/generators/package.ts +1 -0
- package/src/init/generators/test.ts +61 -17
- package/src/setup/fullstack-secrets.ts +11 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_fullstack_secrets = require('./fullstack-secrets-
|
|
1
|
+
const require_fullstack_secrets = require('./fullstack-secrets-Bchl2MDd.cjs');
|
|
2
2
|
|
|
3
3
|
//#region src/secrets/reconcile.ts
|
|
4
4
|
/**
|
|
@@ -33,4 +33,4 @@ function reconcileMissingSecrets(secrets, workspace) {
|
|
|
33
33
|
|
|
34
34
|
//#endregion
|
|
35
35
|
exports.reconcileMissingSecrets = reconcileMissingSecrets;
|
|
36
|
-
//# sourceMappingURL=reconcile-
|
|
36
|
+
//# sourceMappingURL=reconcile-CZtzdrnc.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-
|
|
1
|
+
{"version":3,"file":"reconcile-CZtzdrnc.cjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,yDAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateFullstackCustomSecrets } from "./fullstack-secrets-
|
|
1
|
+
import { generateFullstackCustomSecrets } from "./fullstack-secrets-P84v8zWn.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/secrets/reconcile.ts
|
|
4
4
|
/**
|
|
@@ -33,4 +33,4 @@ function reconcileMissingSecrets(secrets, workspace) {
|
|
|
33
33
|
|
|
34
34
|
//#endregion
|
|
35
35
|
export { reconcileMissingSecrets };
|
|
36
|
-
//# sourceMappingURL=reconcile-
|
|
36
|
+
//# sourceMappingURL=reconcile-DAAKa0YR.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-
|
|
1
|
+
{"version":3,"file":"reconcile-DAAKa0YR.mjs","names":["secrets: StageSecrets","workspace: NormalizedWorkspace","addedKeys: string[]"],"sources":["../src/secrets/reconcile.ts"],"sourcesContent":["import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,wBACfA,SACAC,WACyB;CACzB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,MAAK,WACJ,QAAO;CAGR,MAAM,iBAAiB,+BAA+B,UAAU;CAChE,MAAMC,YAAsB,CAAE;CAC9B,MAAM,eAAe,EAAE,GAAG,QAAQ,OAAQ;AAE1C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,eAAe,CACxD,OAAM,OAAO,eAAe;AAC3B,eAAa,OAAO;AACpB,YAAU,KAAK,IAAI;CACnB;AAGF,KAAI,UAAU,WAAW,EACxB,QAAO;AAGR,QAAO;EACN,SAAS;GACR,GAAG;GACH,WAAW,qBAAI,QAAO,aAAa;GACnC,QAAQ;EACR;EACD;CACA;AACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.29",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"prompts": "~2.4.2",
|
|
57
57
|
"tsx": "~4.20.3",
|
|
58
58
|
"yaml": "~2.8.2",
|
|
59
|
-
"@geekmidas/constructs": "~3.0.
|
|
60
|
-
"@geekmidas/errors": "~1.0.0",
|
|
59
|
+
"@geekmidas/constructs": "~3.0.6",
|
|
61
60
|
"@geekmidas/envkit": "~1.0.4",
|
|
62
|
-
"@geekmidas/
|
|
63
|
-
"@geekmidas/logger": "~1.0.0"
|
|
61
|
+
"@geekmidas/errors": "~1.0.0",
|
|
62
|
+
"@geekmidas/logger": "~1.0.0",
|
|
63
|
+
"@geekmidas/schema": "~1.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/lodash.kebabcase": "^4.1.9",
|
|
@@ -814,13 +814,15 @@ describe('generateTestFiles', () => {
|
|
|
814
814
|
expect(configFile!.content).toContain('~/services/database.ts');
|
|
815
815
|
});
|
|
816
816
|
|
|
817
|
-
it('should use PostgresKyselyMigrator in globalSetup', () => {
|
|
817
|
+
it('should use PostgresKyselyMigrator with runInitScript in globalSetup', () => {
|
|
818
818
|
const files = generateTestFiles(baseOptions, minimalTemplate);
|
|
819
819
|
const setupFile = files.find((f) => f.path === 'test/globalSetup.ts');
|
|
820
820
|
expect(setupFile).toBeDefined();
|
|
821
821
|
expect(setupFile!.content).toContain('PostgresKyselyMigrator');
|
|
822
|
-
expect(setupFile!.content).toContain('
|
|
823
|
-
expect(setupFile!.content).toContain('
|
|
822
|
+
expect(setupFile!.content).toContain('runInitScript');
|
|
823
|
+
expect(setupFile!.content).toContain('afterCreate');
|
|
824
|
+
expect(setupFile!.content).toContain('Credentials');
|
|
825
|
+
expect(setupFile!.content).toContain('PGBOSS_DB_PASSWORD');
|
|
824
826
|
});
|
|
825
827
|
|
|
826
828
|
it('should use KyselyFactory in factory files', () => {
|
|
@@ -38,6 +38,7 @@ export function generatePackageJson(
|
|
|
38
38
|
devDependencies['@geekmidas/testkit'] =
|
|
39
39
|
GEEKMIDAS_VERSIONS['@geekmidas/testkit'];
|
|
40
40
|
devDependencies['@faker-js/faker'] = '~9.8.0';
|
|
41
|
+
devDependencies['kysely-ctl'] = '~0.10.0';
|
|
41
42
|
devDependencies['vite-tsconfig-paths'] = '~5.1.0';
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -18,6 +18,31 @@ export function generateTestFiles(
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
return [
|
|
21
|
+
// kysely.config.ts - Kysely CLI configuration for migrations
|
|
22
|
+
{
|
|
23
|
+
path: 'kysely.config.ts',
|
|
24
|
+
content: `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
25
|
+
import { PostgresDialect } from 'kysely';
|
|
26
|
+
import { defineConfig } from 'kysely-ctl';
|
|
27
|
+
import pg from 'pg';
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
dialect: new PostgresDialect({
|
|
31
|
+
pool: new pg.Pool({
|
|
32
|
+
password: Credentials.POSTGRES_PASSWORD,
|
|
33
|
+
user: Credentials.POSTGRES_USER,
|
|
34
|
+
database: Credentials.POSTGRES_DB,
|
|
35
|
+
port: Number(Credentials.POSTGRES_PORT),
|
|
36
|
+
host: Credentials.POSTGRES_HOST,
|
|
37
|
+
}),
|
|
38
|
+
}),
|
|
39
|
+
migrations: {
|
|
40
|
+
migrationFolder: './src/db/migrations',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
`,
|
|
44
|
+
},
|
|
45
|
+
|
|
21
46
|
// test/config.ts - Wraps vitest `it` with transaction auto-rollback
|
|
22
47
|
{
|
|
23
48
|
path: 'test/config.ts',
|
|
@@ -39,36 +64,55 @@ export const it = wrapVitestKyselyTransaction<Database>(itVitest, {
|
|
|
39
64
|
`,
|
|
40
65
|
},
|
|
41
66
|
|
|
42
|
-
// test/globalSetup.ts -
|
|
43
|
-
// Note: gkm test automatically rewrites DATABASE_URL to use a _test
|
|
44
|
-
// suffixed database and creates it if needed. This setup only runs
|
|
45
|
-
// migrations.
|
|
67
|
+
// test/globalSetup.ts - Creates test database, provisions users, runs migrations
|
|
46
68
|
{
|
|
47
69
|
path: 'test/globalSetup.ts',
|
|
48
|
-
content: `import
|
|
49
|
-
import
|
|
70
|
+
content: `import fs from 'node:fs/promises';
|
|
71
|
+
import path from 'node:path';
|
|
72
|
+
import { Credentials } from '@geekmidas/envkit/credentials';
|
|
50
73
|
import { PostgresKyselyMigrator } from '@geekmidas/testkit/kysely';
|
|
51
|
-
import
|
|
74
|
+
import { runInitScript } from '@geekmidas/testkit/postgres';
|
|
75
|
+
import { FileMigrationProvider, Kysely, PostgresDialect } from 'kysely';
|
|
76
|
+
import pg from 'pg';
|
|
52
77
|
|
|
53
|
-
export async function
|
|
54
|
-
const
|
|
55
|
-
|
|
78
|
+
export default async function globalSetup() {
|
|
79
|
+
const databaseUrl = Credentials.DATABASE_URL!;
|
|
80
|
+
const migrationFolder = path.resolve(import.meta.dirname, '../db/migrations');
|
|
56
81
|
|
|
57
|
-
|
|
58
|
-
// (gkm test already rewrites DATABASE_URL to point to the _test database)
|
|
59
|
-
const db = new Kysely<Database>({
|
|
82
|
+
const db = new Kysely({
|
|
60
83
|
dialect: new PostgresDialect({
|
|
61
|
-
pool: new pg.Pool({ connectionString:
|
|
84
|
+
pool: new pg.Pool({ connectionString: databaseUrl }),
|
|
62
85
|
}),
|
|
63
86
|
});
|
|
64
87
|
|
|
65
88
|
const migrator = new PostgresKyselyMigrator({
|
|
89
|
+
uri: databaseUrl,
|
|
66
90
|
db,
|
|
67
|
-
|
|
91
|
+
afterCreate: async (uri) => {
|
|
92
|
+
const initScriptPath = path.resolve(process.cwd(), 'docker/postgres/init.sh');
|
|
93
|
+
const dbUrl = new URL(Credentials.DATABASE_URL!);
|
|
94
|
+
const apiUrl = new URL(Credentials.API_DATABASE_URL!);
|
|
95
|
+
const authUrl = new URL(Credentials.AUTH_DATABASE_URL!);
|
|
96
|
+
|
|
97
|
+
const env = {
|
|
98
|
+
POSTGRES_USER: dbUrl.username,
|
|
99
|
+
POSTGRES_DB: dbUrl.pathname.slice(1),
|
|
100
|
+
API_DB_PASSWORD: decodeURIComponent(apiUrl.password),
|
|
101
|
+
AUTH_DB_PASSWORD: decodeURIComponent(authUrl.password),
|
|
102
|
+
PGBOSS_DB_PASSWORD: Credentials.PGBOSS_DB_PASSWORD ?? 'pgboss-dev-password',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
await runInitScript(initScriptPath, uri, env);
|
|
106
|
+
},
|
|
107
|
+
provider: new FileMigrationProvider({
|
|
108
|
+
fs,
|
|
109
|
+
path,
|
|
110
|
+
migrationFolder,
|
|
111
|
+
}),
|
|
68
112
|
});
|
|
69
113
|
|
|
70
|
-
await migrator.
|
|
71
|
-
|
|
114
|
+
const teardown = await migrator.start();
|
|
115
|
+
return teardown;
|
|
72
116
|
}
|
|
73
117
|
`,
|
|
74
118
|
},
|
|
@@ -107,6 +107,17 @@ export async function writeDockerEnvFromSecrets(
|
|
|
107
107
|
key.endsWith('_DB_PASSWORD'),
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
+
// Always include pgboss password when pgboss credentials exist
|
|
111
|
+
if (
|
|
112
|
+
secrets.services.pgboss &&
|
|
113
|
+
!dbPasswordEntries.some(([key]) => key === 'PGBOSS_DB_PASSWORD')
|
|
114
|
+
) {
|
|
115
|
+
dbPasswordEntries.push([
|
|
116
|
+
'PGBOSS_DB_PASSWORD',
|
|
117
|
+
secrets.services.pgboss.password,
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
if (dbPasswordEntries.length === 0) {
|
|
111
122
|
return;
|
|
112
123
|
}
|