Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ describe('Forked Deployment with deployModules - my-third', () => {
expect(tables.rows).toHaveLength(1);
expect(tables.rows.map((r: any) => r.table_name)).toEqual(['customers']);

// await fixture.revertModule('my-third', db.name, ['sqitch', 'simple-w-tags'], 'my-first:@v1.0.0');
await fixture.revertModule('my-third', db.name, ['sqitch', 'simple-w-tags'], 'my-first:@v1.0.0');

// expect(await db.exists('schema', 'metaschema')).toBe(false);
// expect(await db.exists('table', 'metaschema.customers')).toBe(false);
expect(await db.exists('schema', 'metaschema')).toBe(false);
expect(await db.exists('table', 'metaschema.customers')).toBe(false);

});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports[`sqitch package dependencies with resolved tags [simple-w-tags/1st] 1`]
"table_users",
"table_products",
],
"resolvedTags": {},
}
`;

Expand Down Expand Up @@ -49,6 +50,7 @@ exports[`sqitch package dependencies with resolved tags [simple-w-tags/2nd] 1`]
"create_table",
"create_another_table",
],
"resolvedTags": {},
}
`;

Expand Down Expand Up @@ -76,5 +78,6 @@ exports[`sqitch package dependencies with resolved tags [simple-w-tags/3rd] 1`]
"create_schema",
"create_table",
],
"resolvedTags": {},
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports[`sqitch package dependencies [simple-w-tags/1st] 1`] = `
"table_users",
"table_products",
],
"resolvedTags": {},
}
`;

Expand Down Expand Up @@ -49,5 +50,6 @@ exports[`sqitch package dependencies [simple-w-tags/2nd] 1`] = `
"create_table",
"create_another_table",
],
"resolvedTags": {},
}
`;
128 changes: 125 additions & 3 deletions packages/core/src/migrate/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export class LaunchQLMigrate {

try {
// Call the deploy stored procedure
console.log('DEBUG: About to deploy change:', change.name, 'in project:', project || plan.project);
await executeQuery(
context,
'CALL launchql_migrate.deploy($1, $2, $3, $4, $5)',
Expand All @@ -184,6 +185,13 @@ export class LaunchQLMigrate {
]
);

const verifyResult = await executeQuery(
context,
'SELECT * FROM launchql_migrate.changes WHERE project = $1 AND change_name = $2',
[project || plan.project, change.name]
);
console.log('DEBUG: Change record after deploy:', JSON.stringify(verifyResult.rows, null, 2));

deployed.push(change.name);
log.success(`Successfully deployed: ${change.name}`);
} catch (error) {
Expand All @@ -210,7 +218,57 @@ export class LaunchQLMigrate {

const { project, targetDatabase, planPath, toChange, useTransaction = true } = options;
const plan = parsePlanFile(planPath);
const resolvedToChange = toChange && toChange.includes('@') ? resolveTagToChangeName(planPath, toChange, project || plan.project) : toChange;

const fullPlanResult = parsePlanFileFull(planPath);
const packageDir = dirname(planPath);

// Check if we have cross-module tag dependencies or cross-module toChange
const hasTagDependencies = fullPlanResult.data?.changes.some((change: any) =>
change.dependencies.some((dep: string) => dep.includes('@'))
) || (toChange && toChange.includes(':@'));

let resolvedDeps: any = null;
if (hasTagDependencies) {
const parentDir = dirname(dirname(packageDir));
console.log('DEBUG: Using parent directory for resolution:', parentDir);
resolvedDeps = resolveDependencies(parentDir, fullPlanResult.data?.project || plan.project, {
tagResolution: 'internal',
loadPlanFiles: true
});
console.log('DEBUG: resolvedDeps:', JSON.stringify(resolvedDeps, null, 2));
}

let resolvedToChange = toChange;
let targetProject = project || plan.project;
let targetChangeName = toChange;

if (toChange && toChange.includes('@')) {
console.log('DEBUG: Processing toChange:', toChange);
if (toChange.includes(':@')) {
const [crossProject, tag] = toChange.split(':@');
targetProject = crossProject;
console.log('DEBUG: Cross-module case - targetProject:', targetProject, 'tag:', tag);
const parentDir = dirname(dirname(packageDir));
const targetPlanPath = join(parentDir, 'packages', crossProject, 'launchql.plan');
console.log('DEBUG: Looking for target plan at:', targetPlanPath);

try {
const resolvedChange = resolveTagToChangeName(targetPlanPath, `@${tag}`, crossProject);
targetChangeName = resolvedChange;
resolvedToChange = resolvedChange;
console.log('DEBUG: Resolved cross-module tag to:', resolvedChange);
} catch (error) {
console.log('DEBUG: Failed to resolve cross-module tag, using original:', toChange);
resolvedToChange = toChange;
targetChangeName = toChange;
}
} else {
resolvedToChange = resolveTagToChangeName(planPath, toChange, project || plan.project);
targetChangeName = resolvedToChange;
console.log('DEBUG: Local tag resolved to:', resolvedToChange);
}
}

const changes = getChangesInOrder(planPath, true); // Reverse order for revert

const reverted: string[] = [];
Expand All @@ -227,8 +285,72 @@ export class LaunchQLMigrate {
await withTransaction(targetPool, { useTransaction }, async (context) => {
for (const change of changes) {
// Stop if we've reached the target change
if (resolvedToChange && change.name === resolvedToChange) {
break;
if (resolvedToChange && targetProject && targetChangeName) {
if (toChange && toChange.includes(':@')) {
let actualTargetChangeName = targetChangeName;
let actualTargetProject = targetProject;

if (resolvedDeps && resolvedDeps.resolvedTags && resolvedDeps.resolvedTags[toChange]) {
const resolvedTag = resolvedDeps.resolvedTags[toChange];
console.log('DEBUG: Using resolved tag:', resolvedTag);

if (resolvedTag.includes(':')) {
const [resolvedProject, resolvedChange] = resolvedTag.split(':', 2);
actualTargetProject = resolvedProject;
actualTargetChangeName = resolvedChange;
} else {
actualTargetChangeName = resolvedTag;
}
}

console.log('DEBUG: Checking deployment for project:', actualTargetProject, 'change:', actualTargetChangeName);

const allChangesResult = await executeQuery(
context,
'SELECT project, change_name, deployed_at FROM launchql_migrate.changes ORDER BY deployed_at',
[]
);
console.log('DEBUG: All changes in DB (including NULL deployed_at):', JSON.stringify(allChangesResult.rows, null, 2));

const targetDeployedResult = await executeQuery(
context,
'SELECT launchql_migrate.is_deployed($1, $2) as is_deployed',
[actualTargetProject, actualTargetChangeName]
);
console.log('DEBUG: is_deployed result:', targetDeployedResult.rows[0]);

if (!targetDeployedResult.rows[0]?.is_deployed) {
log.warn(`Target change ${targetProject}:${actualTargetChangeName} is not deployed, stopping revert`);
break;
}

// Get deployment time of target change
const targetTimeResult = await executeQuery(
context,
'SELECT deployed_at FROM launchql_migrate.changes WHERE project = $1 AND change_name = $2',
[actualTargetProject, actualTargetChangeName]
);

const currentTimeResult = await executeQuery(
context,
'SELECT deployed_at FROM launchql_migrate.changes WHERE project = $1 AND change_name = $2',
[project || plan.project, change.name]
);

if (targetTimeResult.rows[0] && currentTimeResult.rows[0]) {
const targetTime = new Date(targetTimeResult.rows[0].deployed_at);
const currentTime = new Date(currentTimeResult.rows[0].deployed_at);

if (currentTime <= targetTime) {
log.info(`Stopping revert at ${change.name} (deployed at ${currentTime}) as it was deployed before/at target ${actualTargetProject}:${actualTargetChangeName} (deployed at ${targetTime})`);
break;
}
}
} else {
if (change.name === resolvedToChange) {
break;
}
}
}

// Check if deployed
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/resolution/deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ interface DependencyResult {
resolved: string[];
/** The complete dependency graph mapping modules to their dependencies */
deps: DependencyGraph;
/** Mapping of resolved tags to their target changes (only for resolveDependencies) */
resolvedTags?: Record<string, string>;
}

/**
Expand Down Expand Up @@ -529,5 +531,5 @@ export const resolveDependencies = (
const normalSql = resolved.filter((module) => !module.startsWith('extensions/'));
resolved = [...extensions, ...normalSql];

return { external, resolved, deps };
return { external, resolved, deps, resolvedTags: tagMappings };
};
8 changes: 5 additions & 3 deletions packages/core/test-utils/CoreDeployTestFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ export class CoreDeployTestFixture extends TestFixture {
cwd: basePath,
recursive: true,
projectName,
fast: true,
usePlan: true
fast: false,
usePlan: true,
useSqitch: false
};

await deployModules(options);
Expand All @@ -58,7 +59,8 @@ export class CoreDeployTestFixture extends TestFixture {
cwd: projectPath,
recursive: true,
projectName,
toChange
toChange,
useSqitch: false
};

await revertModules(options);
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2864,7 +2864,7 @@
"@types/node" "*"
"@types/pg" "*"

"@types/pg@*", "@types/pg@>=6 <9", "@types/pg@^8.10.9", "@types/pg@^8.15.2":
"@types/pg@*", "@types/pg@>=6 <9", "@types/pg@^8.15.2":
version "8.15.4"
resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.15.4.tgz#419f791c6fac8e0bed66dd8f514b60f8ba8db46d"
integrity sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==
Expand Down Expand Up @@ -8333,7 +8333,7 @@ [email protected], pg-types@^2.2.0:
postgres-date "~1.0.4"
postgres-interval "^1.1.0"

"pg@>=6.1.0 <9", pg@^8.11.3, pg@^8.16.0:
"pg@>=6.1.0 <9", pg@^8.16.0:
version "8.16.3"
resolved "https://registry.yarnpkg.com/pg/-/pg-8.16.3.tgz#160741d0b44fdf64680e45374b06d632e86c99fd"
integrity sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==
Expand Down
Loading