Skip to content

Commit b1a0cd5

Browse files
refactor: move CLI business logic to LaunchQLPackage core methods
- Add validateModule(), syncModule(), and versionWorkspace() methods to LaunchQLPackage - Refactor CLI commands to be lightweight wrappers calling core methods - Add getWorkspaceProject() method to TestFixture for workspace-level testing - Add comprehensive tests in packages/core using TestFixture patterns - CLI commands now delegate all business logic to LaunchQLPackage methods Co-Authored-By: Dan Lynch <pyramation@gmail.com>
1 parent ec745d6 commit b1a0cd5

File tree

8 files changed

+842
-347
lines changed

8 files changed

+842
-347
lines changed

packages/cli/src/commands/sync.ts

Lines changed: 10 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { LaunchQLPackage, generateControlFileContent } from '@launchql/core';
1+
import { LaunchQLPackage } from '@launchql/core';
22
import { Logger } from '@launchql/logger';
33
import { CLIOptions, Inquirerer } from 'inquirerer';
44
import { ParsedArgs } from 'minimist';
5-
import fs from 'fs';
6-
import path from 'path';
75

86
const syncUsageText = `
97
LaunchQL Sync Command:
@@ -52,64 +50,20 @@ export default async (
5250
log.debug(`Using directory: ${cwd}`);
5351

5452
const project = new LaunchQLPackage(cwd);
53+
const result = project.syncModule();
5554

56-
if (!project.isInModule()) {
57-
throw new Error('This command must be run inside a LaunchQL module.');
55+
if (!result.success) {
56+
log.error(`Sync failed: ${result.message}`);
57+
throw new Error(result.message);
5858
}
5959

60-
try {
61-
const modPath = project.getModulePath()!;
62-
const info = project.getModuleInfo();
63-
64-
const pkgJsonPath = path.join(modPath, 'package.json');
65-
if (!fs.existsSync(pkgJsonPath)) {
66-
throw new Error('package.json not found');
67-
}
68-
69-
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
70-
const version = pkg.version;
71-
72-
if (!version) {
73-
throw new Error('No version found in package.json');
74-
}
75-
76-
log.info(`Syncing artifacts for version ${version}`);
77-
78-
const controlPath = info.controlFile;
79-
const requires = project.getRequiredModules();
80-
81-
const controlContent = generateControlFileContent({
82-
name: info.extname,
83-
version,
84-
requires
85-
});
86-
87-
fs.writeFileSync(controlPath, controlContent);
88-
log.success(`Updated control file: ${path.relative(modPath, controlPath)}`);
89-
90-
const sqlDir = path.join(modPath, 'sql');
91-
if (!fs.existsSync(sqlDir)) {
92-
fs.mkdirSync(sqlDir, { recursive: true });
93-
}
94-
95-
const sqlFile = path.join(sqlDir, `${info.extname}--${version}.sql`);
96-
if (!fs.existsSync(sqlFile)) {
97-
const sqlContent = `-- ${info.extname} extension version ${version}
98-
-- This file contains the SQL commands to create the extension
99-
100-
-- Add your SQL commands here
101-
`;
102-
fs.writeFileSync(sqlFile, sqlContent);
103-
log.success(`Created SQL migration file: sql/${info.extname}--${version}.sql`);
60+
log.success(result.message);
61+
for (const file of result.files) {
62+
if (file.includes('--')) {
63+
log.success(`Created SQL migration file: ${file}`);
10464
} else {
105-
log.info(`SQL migration file already exists: sql/${info.extname}--${version}.sql`);
65+
log.success(`Updated control file: ${file}`);
10666
}
107-
108-
log.success('Sync completed successfully');
109-
110-
} catch (error) {
111-
log.error(`Sync failed: ${error}`);
112-
throw error;
11367
}
11468

11569
return argv;

packages/cli/src/commands/validate.ts

Lines changed: 4 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import { LaunchQLPackage } from '@launchql/core';
22
import { Logger } from '@launchql/logger';
33
import { CLIOptions, Inquirerer } from 'inquirerer';
44
import { ParsedArgs } from 'minimist';
5-
import fs from 'fs';
6-
import path from 'path';
75

86
const validateUsageText = `
97
LaunchQL Validate Command:
@@ -57,77 +55,16 @@ export default async (
5755
log.debug(`Using directory: ${cwd}`);
5856

5957
const project = new LaunchQLPackage(cwd);
58+
const result = project.validateModule();
6059

61-
if (!project.isInModule()) {
62-
throw new Error('This command must be run inside a LaunchQL module.');
63-
}
64-
65-
const analysisResult = project.analyzeModule();
66-
let hasErrors = false;
67-
68-
if (!analysisResult.ok) {
69-
log.error(`Module analysis failed for ${analysisResult.name}:`);
70-
for (const issue of analysisResult.issues) {
60+
if (!result.ok) {
61+
log.error('Package validation failed:');
62+
for (const issue of result.issues) {
7163
log.error(` ${issue.code}: ${issue.message}`);
7264
if (issue.file) {
7365
log.error(` File: ${issue.file}`);
7466
}
7567
}
76-
hasErrors = true;
77-
}
78-
79-
const modPath = project.getModulePath()!;
80-
const info = project.getModuleInfo();
81-
82-
try {
83-
const pkgJsonPath = path.join(modPath, 'package.json');
84-
if (!fs.existsSync(pkgJsonPath)) {
85-
log.error('package.json not found');
86-
hasErrors = true;
87-
} else {
88-
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
89-
const pkgVersion = pkg.version;
90-
91-
const controlContent = project.getModuleControlFile();
92-
const defaultVersionMatch = controlContent.match(/^default_version\s*=\s*'([^']+)'/m);
93-
94-
if (!defaultVersionMatch) {
95-
log.error('Control file missing default_version');
96-
hasErrors = true;
97-
} else {
98-
const controlVersion = defaultVersionMatch[1];
99-
if (controlVersion !== pkgVersion) {
100-
log.error(`Version mismatch: control file default_version '${controlVersion}' !== package.json version '${pkgVersion}'`);
101-
hasErrors = true;
102-
}
103-
}
104-
105-
const sqlFile = path.join(modPath, 'sql', `${info.extname}--${pkgVersion}.sql`);
106-
if (!fs.existsSync(sqlFile)) {
107-
log.error(`SQL migration file missing: sql/${info.extname}--${pkgVersion}.sql`);
108-
hasErrors = true;
109-
}
110-
111-
const planPath = path.join(modPath, 'launchql.plan');
112-
if (fs.existsSync(planPath)) {
113-
try {
114-
const planContent = fs.readFileSync(planPath, 'utf8');
115-
const hasVersionTag = planContent.includes(`@v${pkgVersion}`) || planContent.includes(`@${pkgVersion}`);
116-
if (!hasVersionTag) {
117-
log.warn(`launchql.plan missing tag for version ${pkgVersion}`);
118-
}
119-
} catch (e) {
120-
log.error(`Failed to read launchql.plan: ${e}`);
121-
hasErrors = true;
122-
}
123-
}
124-
}
125-
} catch (error) {
126-
log.error(`Validation failed: ${error}`);
127-
hasErrors = true;
128-
}
129-
130-
if (hasErrors) {
13168
throw new Error('Package validation failed');
13269
} else {
13370
log.success('Package validation passed');

0 commit comments

Comments
 (0)