Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/eql-migration-drizzle-kit-spawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'stash': patch
---

Fix `stash eql migration --drizzle`, which aborted for every project with a `drizzle.config.ts` (#924).

- **Stop passing `--out` to `drizzle-kit generate`.** drizzle-kit reads its config file *or* its command-line options, never both: any of `--schema`/`--out`/`--dialect` switches it into CLI mode, where it then aborts demanding the two we cannot supply (`Please provide required params: [x] schema [x] dialect`). Verified against drizzle-kit 0.28.5, 0.30.6 and 0.31.4 — this was never version-specific. Your `drizzle.config.ts` now decides the output directory and stash follows the path drizzle-kit reports, warning when it differs from a `--out` you passed. `--out` remains the fallback directory to search.
- **Pass the resolved `DATABASE_URL` into the drizzle-kit child process.** A `drizzle.config.ts` that reads `process.env.DATABASE_URL` (and often throws when it is missing) previously saw nothing, because the project's usual `dotenv -e .env.local -- drizzle-kit …` wrapper never runs when stash invokes drizzle-kit directly. stash already loads `.env`/`.env.local` at startup; it now also threads down a URL only the CLI can find, such as a running local Supabase.
- **Report the actual failure.** drizzle-kit writes its errors to stdout, not stderr, so the abort printed nothing but "Make sure drizzle-kit is installed and configured" — the one thing that was never wrong. Both streams are now surfaced, and a config that could not read `DATABASE_URL` gets a follow-up naming that instead.
67 changes: 64 additions & 3 deletions packages/cli/src/__tests__/database-url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ vi.mock('@clack/prompts', () => ({
note: clack.note,
}))

const { resolveDatabaseUrl, withResolverContext } = await import(
'../config/database-url.js'
)
const { resolveDatabaseUrl, tryResolveDatabaseUrl, withResolverContext } =
await import('../config/database-url.js')

const VALID_URL = 'postgresql://postgres:postgres@127.0.0.1:54322/postgres'

Expand Down Expand Up @@ -317,3 +316,65 @@ describe('withResolverContext — concurrent isolation', () => {
expect(b).toBe(URL_B)
})
})

/**
* The non-blocking variant, used to decorate a spawned `drizzle-kit`'s
* environment (#924). Same sources as {@link resolveDatabaseUrl} minus the
* prompt and the hard exit — a missing URL is an ordinary answer here, because
* the child may not need one.
*/
describe('tryResolveDatabaseUrl', () => {
it('prefers the flag from the resolver context', async () => {
process.env.DATABASE_URL = 'postgresql://env@localhost:5432/env'
const result = await withResolverContext(
{ databaseUrlFlag: VALID_URL },
async () => tryResolveDatabaseUrl(),
)
expect(result).toBe(VALID_URL)
})

it('falls back to process.env — the dotenv files bin/main.ts loaded', () => {
process.env.DATABASE_URL = VALID_URL
expect(tryResolveDatabaseUrl()).toBe(VALID_URL)
})

it('reaches supabase status when the project has a config.toml', () => {
detect.detectSupabaseProject.mockReturnValue({
hasMigrationsDir: true,
hasConfigToml: true,
migrationsDir: path.join(tmpDir, 'supabase/migrations'),
})
supabase.execSync.mockReturnValue(`DB_URL="${VALID_URL}"\n`)
expect(tryResolveDatabaseUrl({ cwd: tmpDir })).toBe(VALID_URL)
})

it('returns undefined instead of prompting or exiting', () => {
Object.defineProperty(process.stdin, 'isTTY', {
value: true,
configurable: true,
})
const exit = vi.spyOn(process, 'exit').mockImplementation((() => {
throw new Error('process.exit called')
}) as never)

expect(tryResolveDatabaseUrl({ cwd: tmpDir })).toBeUndefined()
expect(clack.text).not.toHaveBeenCalled()
expect(exit).not.toHaveBeenCalled()
})

it('ignores a malformed flag rather than exiting on it', async () => {
const exit = vi.spyOn(process, 'exit').mockImplementation((() => {
throw new Error('process.exit called')
}) as never)
process.env.DATABASE_URL = VALID_URL

const result = await withResolverContext(
{ databaseUrlFlag: 'not a url' },
async () => tryResolveDatabaseUrl(),
)
// resolveDatabaseUrl exits here; this one is advisory, so it moves on to
// the next source and the caller's own resolution reports the bad flag.
expect(result).toBe(VALID_URL)
expect(exit).not.toHaveBeenCalled()
})
})
2 changes: 1 addition & 1 deletion packages/cli/src/cli/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ export const registry: CommandGroup[] = [
name: '--out',
value: '<path>',
description:
'Where the migration is written. Drizzle: passed to `drizzle-kit generate --out`, defaults to `drizzle` — set it to match your drizzle.config.ts. Supabase: leave it alone. The Supabase CLI replays `supabase/migrations` and has no setting to move it, so pointing elsewhere means `supabase db reset` / `db push` never apply the install; the command warns when you do.',
'Where the migration is written. Drizzle: your drizzle.config.ts `out` decides that, and stash follows the path drizzle-kit reports — this is only the fallback directory to look in (defaults to `drizzle`) if it reports none. Supabase: leave it alone. The Supabase CLI replays `supabase/migrations` and has no setting to move it, so pointing elsewhere means `supabase db reset` / `db push` never apply the install; the command warns when you do.',
},
{
name: '--force',
Expand Down
Loading
Loading