diff --git a/packages/dataqueue/package.json b/packages/dataqueue/package.json index 1b888db..f576d8b 100644 --- a/packages/dataqueue/package.json +++ b/packages/dataqueue/package.json @@ -49,6 +49,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", "croner": "^10.0.1", + "dotenv": "^16.4.5", "pg": "^8.0.0", "pg-connection-string": "^2.9.1", "zod": "^3.25.67" diff --git a/packages/dataqueue/src/cli.test.ts b/packages/dataqueue/src/cli.test.ts index 3ec52f1..34694dc 100644 --- a/packages/dataqueue/src/cli.test.ts +++ b/packages/dataqueue/src/cli.test.ts @@ -21,7 +21,9 @@ function makeDeps() { error: vi.fn(), exit: vi.fn(), spawnSyncImpl: vi.fn(() => makeSpawnSyncReturns(0)), + loadEnvFromPath: vi.fn(), migrationsDir: '/migrations', + cwd: '/project', runInitImpl: vi.fn(), runInstallSkillsImpl: vi.fn(), runInstallRulesImpl: vi.fn(async () => {}), @@ -112,6 +114,11 @@ describe('runCli', () => { expect(deps.exit).toHaveBeenCalledWith(0); }); + it('calls loadEnvFromPath with env path when --envPath is given', () => { + runCli(['node', 'cli.js', 'migrate', '--envPath', '.env.local'], deps); + expect(deps.loadEnvFromPath).toHaveBeenCalledWith('.env.local'); + }); + it('passes extra args to spawnSyncImpl', () => { runCli(['node', 'cli.js', 'migrate', '--foo', 'bar'], deps); expect(deps.spawnSyncImpl).toHaveBeenCalledWith( diff --git a/packages/dataqueue/src/cli.ts b/packages/dataqueue/src/cli.ts index 4fff542..8e0dbab 100644 --- a/packages/dataqueue/src/cli.ts +++ b/packages/dataqueue/src/cli.ts @@ -2,6 +2,7 @@ import { spawnSync, SpawnSyncReturns } from 'child_process'; import path from 'path'; import { fileURLToPath } from 'url'; +import { config as loadDotenv } from 'dotenv'; import { InitDeps, runInit } from './init-command.js'; import { runInstallSkills, @@ -19,7 +20,10 @@ export interface CliDeps { error?: (...args: any[]) => void; exit?: (code: number) => void; spawnSyncImpl?: (...args: any[]) => SpawnSyncReturns; + /** Load env vars from a file path (default: dotenv). Path is resolved from cwd. */ + loadEnvFromPath?: (filePath: string) => void; migrationsDir?: string; + cwd?: string; initDeps?: InitDeps; runInitImpl?: (deps?: InitDeps) => void; installSkillsDeps?: InstallSkillsDeps; @@ -38,6 +42,10 @@ export function runCli( error = console.error, exit = (code: number) => process.exit(code), spawnSyncImpl = spawnSync, + cwd = process.cwd(), + loadEnvFromPath = (filePath: string) => { + loadDotenv({ path: path.resolve(cwd, filePath) }); + }, migrationsDir = path.join(__dirname, '../migrations'), initDeps, runInitImpl = runInit, @@ -94,11 +102,15 @@ export function runCli( restArgs.splice(schemaIndex, 2); } - // Support for --envPath argument + // Support for --envPath argument: load env file so PG_DATAQUEUE_DATABASE is set + // before spawning node-pg-migrate (node-pg-migrate only loads .env if dotenv is + // installed in its context, so we preload here for reliable behavior). let envPathArg: string[] = []; const envPathIndex = restArgs.indexOf('--envPath'); if (envPathIndex !== -1 && restArgs[envPathIndex + 1]) { - envPathArg = ['--envPath', restArgs[envPathIndex + 1]]; + const envPath = restArgs[envPathIndex + 1]; + loadEnvFromPath(envPath); + envPathArg = ['--envPath', envPath]; } const result: SpawnSyncReturns = spawnSyncImpl( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8bde37..a0cd311 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,7 +128,7 @@ importers: version: 2.0.1 fumadocs-mdx: specifier: 11.6.9 - version: 11.6.9(acorn@8.15.0)(fumadocs-core@15.5.5(@types/react@19.1.8)(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.0.0(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1)) + version: 11.6.9(acorn@8.15.0)(fumadocs-core@15.5.5(@types/react@19.1.8)(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) fumadocs-ui: specifier: 15.5.5 version: 15.5.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11) @@ -423,6 +423,9 @@ importers: croner: specifier: ^10.0.1 version: 10.0.1 + dotenv: + specifier: ^16.4.5 + version: 16.6.1 pg: specifier: ^8.0.0 version: 8.16.3 @@ -9825,7 +9828,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -9836,7 +9839,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -9847,7 +9850,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.30.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.30.0(jiti@2.4.2)))(eslint@9.30.0(jiti@2.4.2)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.30.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -9869,7 +9872,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9898,7 +9901,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.29.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.4.2)))(eslint@9.29.0(jiti@2.4.2)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9927,7 +9930,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.30.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.30.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.30.0(jiti@2.4.2)))(eslint@9.30.0(jiti@2.4.2)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.30.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -10473,7 +10476,7 @@ snapshots: unist-util-visit: 5.0.0 zod: 3.25.67 - fumadocs-mdx@11.6.9(acorn@8.15.0)(fumadocs-core@15.5.5(@types/react@19.1.8)(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@7.0.0(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1)): + fumadocs-mdx@11.6.9(acorn@8.15.0)(fumadocs-core@15.5.5(@types/react@19.1.8)(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.3.8(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): dependencies: '@mdx-js/mdx': 3.1.0(acorn@8.15.0) '@standard-schema/spec': 1.0.0 @@ -10490,7 +10493,6 @@ snapshots: zod: 3.25.67 optionalDependencies: next: 15.3.8(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - vite: 7.0.0(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1) transitivePeerDependencies: - acorn - supports-color @@ -13230,21 +13232,6 @@ snapshots: jiti: 2.4.2 lightningcss: 1.30.1 - vite@7.0.0(@types/node@24.0.3)(jiti@2.4.2)(lightningcss@1.30.1): - dependencies: - esbuild: 0.25.5 - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 - postcss: 8.5.6 - rollup: 4.44.1 - tinyglobby: 0.2.14 - optionalDependencies: - '@types/node': 24.0.3 - fsevents: 2.3.3 - jiti: 2.4.2 - lightningcss: 1.30.1 - optional: true - vite@7.0.0(@types/node@24.0.4)(jiti@2.4.2)(lightningcss@1.30.1): dependencies: esbuild: 0.25.5