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
2 changes: 2 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ For existing LaunchQL projects:
- Command-line compatibility layer
- Unit test suite
- Test project setup
- Transaction support with --tx/--no-tx flags
- Cross-project dependency support (project:change format)

**IN PROGRESS:**
- Integration testing
Expand Down
24 changes: 14 additions & 10 deletions packages/cli/src/commands/revert.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CLIOptions, Inquirerer, Question } from 'inquirerer';
import { listModules, revert } from '@launchql/core';
import { LaunchQLProject, revert } from '@launchql/core';
import { errors, getEnvOptions, LaunchQLOptions } from '@launchql/types';
import { getPgEnvOptions, getSpawnEnvWithPg } from 'pg-env';
import { Logger } from '@launchql/logger';
Expand Down Expand Up @@ -43,34 +43,38 @@ export default async (
return;
}

log.debug(`Using current directory: ${cwd}`);

const project = new LaunchQLProject(cwd);

if (recursive) {
const modules = await listModules(cwd);
const mods = Object.keys(modules);
const modules = await project.getModules();
const moduleNames = modules.map(mod => mod.getModuleName());

if (!mods.length) {
log.error('No modules found to revert.');
if (!moduleNames.length) {
log.error('No modules found in the specified directory.');
prompter.close();
throw errors.NOT_FOUND({}, 'No modules found to revert.');
throw errors.NOT_FOUND({}, 'No modules found in the specified directory.');
}

const { project } = await prompter.prompt(argv, [
const { project: selectedProject } = await prompter.prompt(argv, [
{
type: 'autocomplete',
name: 'project',
message: 'Choose a project to revert',
options: mods,
options: moduleNames,
required: true
}
]);

log.success(`Reverting project ${project} on database ${database}...`);
log.success(`Reverting project ${selectedProject} on database ${database}...`);
const options: LaunchQLOptions = getEnvOptions({
pg: {
database
}
});

await revert(options, project, database, cwd, { useSqitch, useTransaction: tx });
await revert(options, selectedProject, database, cwd, { useSqitch, useTransaction: tx });
log.success('Revert complete.');
} else {
const pgEnv = getPgEnvOptions();
Expand Down
43 changes: 20 additions & 23 deletions packages/cli/src/commands/verify.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { CLIOptions, Inquirerer, Question } from 'inquirerer';
import { listModules, verify } from '@launchql/core';
import { LaunchQLProject, verify } from '@launchql/core';
import { errors, getEnvOptions, LaunchQLOptions } from '@launchql/types';
import { getPgEnvOptions, getSpawnEnvWithPg } from 'pg-env';
import { Logger } from '@launchql/logger';
import { verifyCommand } from '@launchql/migrate';
import { execSync } from 'child_process';
import { getTargetDatabase } from '../utils';

const log = new Logger('verify');

Expand All @@ -13,38 +14,34 @@ export default async (
prompter: Inquirerer,
_options: CLIOptions
) => {
const questions: Question[] = [
{
name: 'database',
message: 'Database name',
type: 'text',
required: true
}
];
const database = await getTargetDatabase(argv, prompter, {
message: 'Select database'
});

let { database, recursive, cwd, 'use-sqitch': useSqitch } = await prompter.prompt(argv, questions);
const questions: Question[] = [];

if (!cwd) {
cwd = process.cwd();
log.debug(`Using current directory: ${cwd}`);
}
let { recursive, cwd, 'use-sqitch': useSqitch } = await prompter.prompt(argv, questions);

log.debug(`Using current directory: ${cwd}`);

const project = new LaunchQLProject(cwd);

if (recursive) {
const modules = await listModules(cwd);
const mods = Object.keys(modules);
const modules = await project.getModules();
const moduleNames = modules.map(mod => mod.getModuleName());

if (!mods.length) {
log.error('No modules found to verify.');
if (!moduleNames.length) {
log.error('No modules found in the specified directory.');
prompter.close();
throw errors.NOT_FOUND({}, 'No modules found to verify.');
throw errors.NOT_FOUND({}, 'No modules found in the specified directory.');
}

const { project } = await prompter.prompt(argv, [
const { project: selectedProject } = await prompter.prompt(argv, [
{
type: 'autocomplete',
name: 'project',
message: 'Choose a project to verify',
options: mods,
options: moduleNames,
required: true
}
]);
Expand All @@ -55,8 +52,8 @@ export default async (
}
});

log.info(`Verifying project ${project} on database ${database}...`);
await verify(options, project, database, cwd, { useSqitch });
log.info(`Verifying project ${selectedProject} on database ${database}...`);
await verify(options, selectedProject, database, cwd, { useSqitch });
log.success('Verify complete.');
} else {
const pgEnv = getPgEnvOptions();
Expand Down
2 changes: 1 addition & 1 deletion packages/migrate/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class LaunchQLMigrate {
} else {
log.success('Migration schema found and ready');
}

this.initialized = true;
} catch (error) {
log.error('Failed to initialize migration schema:', error);
Expand Down
29 changes: 24 additions & 5 deletions packages/migrate/src/sql/procedures.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,37 @@ BEGIN
END;
$$;

-- Check if a change is deployed
-- Check if a change is deployed (handles both local and cross-project dependencies)
CREATE FUNCTION launchql_migrate.is_deployed(
p_project TEXT,
p_change_name TEXT
)
RETURNS BOOLEAN
LANGUAGE sql STABLE AS $$
SELECT EXISTS (
LANGUAGE plpgsql STABLE AS $$
DECLARE
v_actual_project TEXT;
v_actual_change TEXT;
v_colon_pos INT;
BEGIN
-- Check if change_name contains a project prefix (cross-project dependency)
v_colon_pos := position(':' in p_change_name);

IF v_colon_pos > 0 THEN
-- Split into project and change name
v_actual_project := substring(p_change_name from 1 for v_colon_pos - 1);
v_actual_change := substring(p_change_name from v_colon_pos + 1);
ELSE
-- Use provided project as default
v_actual_project := p_project;
v_actual_change := p_change_name;
END IF;

RETURN EXISTS (
SELECT 1 FROM launchql_migrate.changes
WHERE project = p_project
AND change_name = p_change_name
WHERE project = v_actual_project
AND change_name = v_actual_change
);
END;
$$;

-- Deploy a change
Expand Down