Skip to content

Commit 921d37a

Browse files
jycouetmanuel3108
andauthored
fix(drizzle): compose file & --cwd (#738)
* Docker Compose file is now stored in `compose.yaml` * first step, improve tests (even on non docker env) * fix CI * for CI ? * fixing tests * fix --cwd in add for drizzle + cancel option * if docker is here, let's use it * only for MUST_HAVE_DOCKER * Update .changeset/great-mammals-buy.md Co-authored-by: Manuel <[email protected]> --------- Co-authored-by: Manuel <[email protected]>
1 parent c8d0225 commit 921d37a

File tree

7 files changed

+125
-48
lines changed

7 files changed

+125
-48
lines changed

.changeset/bright-flies-argue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sv': patch
3+
---
4+
5+
fix(drizzle): `--cwd` option in `add` command is now taken into account

.changeset/great-mammals-buy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sv': patch
3+
---
4+
5+
feat(drizzle): Docker Compose file is now stored in `compose.yaml` instead of `docker-compose.yml`

packages/addons/_tests/drizzle/test.ts

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import drizzle from '../../drizzle/index.ts';
1010
import { pageServer, pageComp } from './fixtures.ts';
1111

1212
// only linux is supported for running docker containers in github runners
13-
const noDocker = process.env.CI && process.platform !== 'linux';
13+
const MUST_HAVE_DOCKER = process.env.CI && process.platform === 'linux';
14+
let dockerInstalled = false;
1415

1516
const { test, testCases, prepareServer } = setupTest(
1617
{ drizzle },
@@ -38,44 +39,67 @@ const { test, testCases, prepareServer } = setupTest(
3839
);
3940

4041
beforeAll(() => {
41-
if (noDocker) return;
42+
if (!MUST_HAVE_DOCKER) return;
4243
const cwd = path.dirname(fileURLToPath(import.meta.url));
43-
execSync('docker compose up --detach', { cwd, stdio: 'pipe' });
44+
45+
try {
46+
execSync('docker --version', { cwd, stdio: 'pipe' });
47+
dockerInstalled = true;
48+
} catch {
49+
dockerInstalled = false;
50+
}
51+
52+
if (dockerInstalled) execSync('docker compose up --detach', { cwd, stdio: 'pipe' });
4453

4554
// cleans up the containers on interrupts (ctrl+c)
4655
process.addListener('SIGINT', () => {
47-
execSync('docker compose down --volumes', { cwd, stdio: 'pipe' });
56+
if (dockerInstalled) execSync('docker compose down --volumes', { cwd, stdio: 'pipe' });
4857
});
4958

5059
return () => {
51-
execSync('docker compose down --volumes', { cwd, stdio: 'pipe' });
60+
if (dockerInstalled) execSync('docker compose down --volumes', { cwd, stdio: 'pipe' });
5261
};
5362
});
5463

5564
test.concurrent.for(testCases)(
5665
'drizzle $kind.type $variant',
5766
async (testCase, { page, ...ctx }) => {
58-
if (testCase.kind.options.drizzle.docker && noDocker) ctx.skip();
5967
const cwd = ctx.cwd(testCase);
6068

61-
const ts = testCase.variant === 'kit-ts';
69+
const ts = testCase.variant.endsWith('ts');
6270
const drizzleConfig = path.resolve(cwd, `drizzle.config.${ts ? 'ts' : 'js'}`);
63-
const content = fs.readFileSync(drizzleConfig, 'utf8').replace(/strict: true[,\s]/, '');
64-
fs.writeFileSync(drizzleConfig, content, 'utf8');
71+
const content = fs.readFileSync(drizzleConfig, 'utf8');
72+
73+
expect(content.length, 'drizzle config should have content').toBeGreaterThan(0);
74+
75+
if (MUST_HAVE_DOCKER) expect(dockerInstalled, 'docker must be installed').toBe(true);
76+
77+
if (testCase.kind.options.drizzle.docker) {
78+
const dockerCompose = path.resolve(cwd, 'compose.yaml');
79+
expect(fs.existsSync(dockerCompose), 'file should exist').toBe(true);
80+
}
81+
82+
const db_can_be_tested =
83+
!testCase.kind.options.drizzle.docker ||
84+
(testCase.kind.options.drizzle.docker && dockerInstalled);
85+
86+
if (db_can_be_tested) {
87+
fs.writeFileSync(drizzleConfig, content.replace(/strict: true[,\s]/, ''), 'utf8');
6588

66-
const routes = path.resolve(cwd, 'src', 'routes');
67-
const pagePath = path.resolve(routes, '+page.svelte');
68-
fs.writeFileSync(pagePath, pageComp, 'utf8');
89+
const routes = path.resolve(cwd, 'src', 'routes');
90+
const pagePath = path.resolve(routes, '+page.svelte');
91+
fs.writeFileSync(pagePath, pageComp, 'utf8');
6992

70-
const pageServerPath = path.resolve(routes, `+page.server.${ts ? 'ts' : 'js'}`);
71-
fs.writeFileSync(pageServerPath, pageServer, 'utf8');
93+
const pageServerPath = path.resolve(routes, `+page.server.${ts ? 'ts' : 'js'}`);
94+
fs.writeFileSync(pageServerPath, pageServer, 'utf8');
7295

73-
execSync('npm run db:push', { cwd, stdio: 'pipe' });
96+
execSync('npm run db:push', { cwd, stdio: 'pipe' });
7497

75-
const { close } = await prepareServer({ cwd, page });
76-
// kill server process when we're done
77-
ctx.onTestFinished(async () => await close());
98+
const { close } = await prepareServer({ cwd, page });
99+
// kill server process when we're done
100+
ctx.onTestFinished(async () => await close());
78101

79-
expect(page.locator('[data-testid]')).toBeTruthy();
102+
expect(page.locator('[data-testid]')).toBeTruthy();
103+
}
80104
}
81105
);

packages/addons/drizzle/index.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,30 +73,28 @@ export default defineAddon({
7373
shortDescription: 'database orm',
7474
homepage: 'https://orm.drizzle.team',
7575
options,
76-
setup: ({ kit, unsupported, runsAfter, cwd, typescript }) => {
76+
setup: ({ kit, unsupported, runsAfter }) => {
7777
runsAfter('prettier');
7878

79-
const ext = typescript ? 'ts' : 'js';
80-
if (!kit) {
81-
return unsupported('Requires SvelteKit');
82-
}
79+
if (!kit) return unsupported('Requires SvelteKit');
80+
},
81+
run: ({ sv, typescript, options, kit, dependencyVersion, cwd, cancel }) => {
82+
if (!kit) throw new Error('SvelteKit is required');
8383

84-
const baseDBPath = path.resolve(kit.libDirectory, 'server', 'db');
84+
const ext = typescript ? 'ts' : 'js';
85+
const baseDBPath = path.resolve(cwd, kit.libDirectory, 'server', 'db');
8586
const paths = {
86-
'drizzle config': path.relative(cwd, path.resolve(cwd, `drizzle.config.${ext}`)),
87-
'database schema': path.relative(cwd, path.resolve(baseDBPath, `schema.${ext}`)),
88-
database: path.relative(cwd, path.resolve(baseDBPath, `index.${ext}`))
87+
'drizzle config': path.resolve(cwd, `drizzle.config.${ext}`),
88+
'database schema': path.resolve(baseDBPath, `schema.${ext}`),
89+
database: path.resolve(baseDBPath, `index.${ext}`)
8990
};
9091

9192
for (const [fileType, filePath] of Object.entries(paths)) {
9293
if (fs.existsSync(filePath)) {
93-
unsupported(`Preexisting ${fileType} file at '${filePath}'`);
94+
return cancel(`Preexisting ${fileType} file at '${filePath}'`);
9495
}
9596
}
96-
},
97-
run: ({ sv, typescript, options, kit, dependencyVersion }) => {
98-
const ext = typescript ? 'ts' : 'js';
99-
97+
console.log(`no preexisting files`);
10098
sv.devDependency('drizzle-orm', '^0.44.5');
10199
sv.devDependency('drizzle-kit', '^0.31.4');
102100
sv.devDependency('@types/node', getNodeTypesVersion());
@@ -123,7 +121,15 @@ export default defineAddon({
123121
sv.file('.env.example', (content) => generateEnvFileContent(content, options));
124122

125123
if (options.docker && (options.mysql === 'mysql2' || options.postgresql === 'postgres.js')) {
126-
sv.file('docker-compose.yml', (content) => {
124+
const composeFileOptions = ['docker-compose.yml', 'docker-compose.yaml', 'compose.yaml'];
125+
let composeFile = '';
126+
for (const option of composeFileOptions) {
127+
composeFile = option;
128+
if (fs.existsSync(path.resolve(cwd, option))) break;
129+
}
130+
if (composeFile === '') throw new Error('unreachable state...');
131+
132+
sv.file(composeFile, (content) => {
127133
// if the file already exists, don't modify it
128134
// (in the future, we could add some tooling for modifying yaml)
129135
if (content.length > 0) return content;
@@ -206,7 +212,7 @@ export default defineAddon({
206212
});
207213
}
208214

209-
sv.file(`drizzle.config.${ext}`, (content) => {
215+
sv.file(paths['drizzle config'], (content) => {
210216
const { ast, generateCode } = parseScript(content);
211217

212218
imports.addNamed(ast, { from: 'drizzle-kit', imports: { defineConfig: 'defineConfig' } });
@@ -234,7 +240,7 @@ export default defineAddon({
234240
return generateCode();
235241
});
236242

237-
sv.file(`${kit?.libDirectory}/server/db/schema.${ext}`, (content) => {
243+
sv.file(paths['database schema'], (content) => {
238244
const { ast, generateCode } = parseScript(content);
239245

240246
let userSchemaExpression;
@@ -286,7 +292,7 @@ export default defineAddon({
286292
return generateCode();
287293
});
288294

289-
sv.file(`${kit?.libDirectory}/server/db/index.${ext}`, (content) => {
295+
sv.file(paths['database'], (content) => {
290296
const { ast, generateCode } = parseScript(content);
291297

292298
imports.addNamed(ast, {

packages/cli/commands/add/index.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ export async function runAddCommand(
205205
options: Options,
206206
selectedAddonIds: string[]
207207
): Promise<{ nextSteps: string[]; packageManager?: AgentName | null }> {
208-
const selectedAddons: SelectedAddon[] = selectedAddonIds.map((id) => ({
208+
let selectedAddons: SelectedAddon[] = selectedAddonIds.map((id) => ({
209209
type: 'official',
210210
addon: getAddonDetails(id)
211211
}));
@@ -542,14 +542,30 @@ export async function runAddCommand(
542542
const details = officialDetails.concat(commDetails);
543543

544544
const addonMap: AddonMap = Object.assign({}, ...details.map((a) => ({ [a.id]: a })));
545-
const { filesToFormat, pnpmBuildDependencies: addonPnpmBuildDependencies } = await applyAddons({
545+
const { filesToFormat, pnpmBuildDependencies, status } = await applyAddons({
546546
workspace,
547547
addonSetupResults,
548548
addons: addonMap,
549549
options: official
550550
});
551551

552-
p.log.success('Successfully setup add-ons');
552+
const addonSuccess: string[] = [];
553+
for (const [addonId, info] of Object.entries(status)) {
554+
if (info === 'success') addonSuccess.push(addonId);
555+
else {
556+
p.log.warn(`Canceled ${addonId}: ${info.join(', ')}`);
557+
selectedAddons = selectedAddons.filter((a) => a.addon.id !== addonId);
558+
}
559+
}
560+
561+
if (addonSuccess.length === 0) {
562+
p.cancel('All selected add-ons were canceled.');
563+
process.exit(1);
564+
} else if (addonSuccess.length === Object.entries(status).length) {
565+
p.log.success('Successfully setup add-ons');
566+
} else {
567+
p.log.success(`Successfully setup: ${addonSuccess.join(', ')}`);
568+
}
553569

554570
// prompt for package manager and install dependencies
555571
let packageManager: PackageManager | undefined;
@@ -562,7 +578,7 @@ export async function runAddCommand(
562578

563579
await addPnpmBuildDependencies(workspace.cwd, packageManager, [
564580
'esbuild',
565-
...addonPnpmBuildDependencies
581+
...pnpmBuildDependencies
566582
]);
567583

568584
await installDependencies(packageManager, options.cwd);

packages/cli/lib/install.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,37 @@ export async function applyAddons({
5454
}: ApplyAddonOptions): Promise<{
5555
filesToFormat: string[];
5656
pnpmBuildDependencies: string[];
57+
status: Record<string, string[] | 'success'>;
5758
}> {
5859
const filesToFormat = new Set<string>();
5960
const allPnpmBuildDependencies: string[] = [];
61+
const status: Record<string, string[] | 'success'> = {};
6062

6163
const mapped = Object.entries(addons).map(([, addon]) => addon);
6264
const ordered = orderAddons(mapped, addonSetupResults);
6365

6466
for (const addon of ordered) {
6567
workspace = await createWorkspace({ ...workspace, options: options[addon.id] });
6668

67-
const { files, pnpmBuildDependencies } = await runAddon({
69+
const { files, pnpmBuildDependencies, cancels } = await runAddon({
6870
workspace,
6971
addon,
7072
multiple: ordered.length > 1
7173
});
7274

7375
files.forEach((f) => filesToFormat.add(f));
7476
pnpmBuildDependencies.forEach((s) => allPnpmBuildDependencies.push(s));
77+
if (cancels.length === 0) {
78+
status[addon.id] = 'success';
79+
} else {
80+
status[addon.id] = cancels;
81+
}
7582
}
7683

7784
return {
7885
filesToFormat: Array.from(filesToFormat),
79-
pnpmBuildDependencies: allPnpmBuildDependencies
86+
pnpmBuildDependencies: allPnpmBuildDependencies,
87+
status
8088
};
8189
}
8290

@@ -177,14 +185,25 @@ async function runAddon({ addon, multiple, workspace }: RunAddon) {
177185
pnpmBuildDependencies.push(pkg);
178186
}
179187
};
180-
await addon.run({ ...workspace, sv });
181188

182-
const pkgPath = installPackages(dependencies, workspace);
183-
files.add(pkgPath);
189+
const cancels: string[] = [];
190+
await addon.run({
191+
cancel: (reason) => {
192+
cancels.push(reason);
193+
},
194+
...workspace,
195+
sv
196+
});
197+
198+
if (cancels.length === 0) {
199+
const pkgPath = installPackages(dependencies, workspace);
200+
files.add(pkgPath);
201+
}
184202

185203
return {
186204
files: Array.from(files),
187-
pnpmBuildDependencies
205+
pnpmBuildDependencies,
206+
cancels
188207
};
189208
}
190209

packages/core/addon/config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ export type Addon<Args extends OptionDefinition> = {
4040
runsAfter: (addonName: string) => void;
4141
}
4242
) => MaybePromise<void>;
43-
run: (workspace: Workspace<Args> & { sv: SvApi }) => MaybePromise<void>;
43+
run: (
44+
workspace: Workspace<Args> & { sv: SvApi; cancel: (reason: string) => void }
45+
) => MaybePromise<void>;
4446
nextSteps?: (
4547
data: {
4648
highlighter: Highlighter;

0 commit comments

Comments
 (0)