0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(cli): add rollback-to db action (#4329)

* feat(cli): add rollback-to db action

add rollback-to db action

* chore: bump alteration timestamp

bump alteration timestamp

* chore: rename the alteration action name

rename the alteration action name
This commit is contained in:
simeng-li 2023-08-15 16:54:53 +08:00 committed by GitHub
parent 44d023ab2e
commit 6771f9ed6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 42 deletions

View file

@ -12,7 +12,11 @@ import {
import { consoleLog } from '../../../utils.js';
import type { AlterationFile } from './type.js';
import { getAlterationFiles, getTimestampFromFilename } from './utils.js';
import {
getAlterationFiles,
getTimestampFromFilename,
chooseRevertAlterationsByTimestamp,
} from './utils.js';
import { chooseAlterationsByVersion, chooseRevertAlterationsByVersion } from './version.js';
const importAlterationScript = async (filePath: string): Promise<AlterationScript> => {
@ -87,6 +91,21 @@ const deployAlteration = async (
consoleLog.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
};
const revertAlterations = async (alterations: AlterationFile[], pool: DatabasePool) => {
consoleLog.info(
`Found ${alterations.length} alteration${conditionalString(
alterations.length > 1 && 's'
)} to revert`
);
// The await inside the loop is intended, alterations should run in order
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
for (const alteration of alterations.slice().reverse()) {
// eslint-disable-next-line no-await-in-loop
await deployAlteration(pool, alteration, 'down');
}
};
const alteration: CommandModule<unknown, { action: string; target?: string }> = {
command: ['alteration <action> [target]', 'alt', 'alter'],
describe: 'Perform database alteration',
@ -101,56 +120,64 @@ const alteration: CommandModule<unknown, { action: string; target?: string }> =
describe: 'The target Logto version for alteration',
type: 'string',
}),
handler: async ({ action, target }) => {
if (action === 'list') {
const files = await getAlterationFiles();
switch (action) {
case 'list': {
const files = await getAlterationFiles();
for (const file of files) {
consoleLog.plain(file.filename);
for (const file of files) {
consoleLog.plain(file.filename);
}
break;
}
} else if (action === 'deploy') {
const pool = await createPoolFromConfig();
const alterations = await chooseAlterationsByVersion(
await getAvailableAlterations(pool),
target
);
case 'deploy': {
const pool = await createPoolFromConfig();
const alterations = await chooseAlterationsByVersion(
await getAvailableAlterations(pool),
target
);
consoleLog.info(
`Found ${alterations.length} alteration${conditionalString(
alterations.length > 1 && 's'
)} to deploy`
);
consoleLog.info(
`Found ${alterations.length} alteration${conditionalString(
alterations.length > 1 && 's'
)} to deploy`
);
// The await inside the loop is intended, alterations should run in order
for (const alteration of alterations) {
// eslint-disable-next-line no-await-in-loop
await deployAlteration(pool, alteration);
// The await inside the loop is intended, alterations should run in order
for (const alteration of alterations) {
// eslint-disable-next-line no-await-in-loop
await deployAlteration(pool, alteration);
}
await pool.end();
break;
}
case 'rollback':
case 'r': {
const pool = await createPoolFromConfig();
const alterations = await chooseRevertAlterationsByVersion(
await getAvailableAlterations(pool, 'lte'),
target ?? ''
);
await pool.end();
} else if (['rollback', 'r'].includes(action)) {
const pool = await createPoolFromConfig();
const alterations = await chooseRevertAlterationsByVersion(
await getAvailableAlterations(pool, 'lte'),
target ?? ''
);
consoleLog.info(
`Found ${alterations.length} alteration${conditionalString(
alterations.length > 1 && 's'
)} to revert`
);
// The await inside the loop is intended, alterations should run in order
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
for (const alteration of alterations.slice().reverse()) {
// eslint-disable-next-line no-await-in-loop
await deployAlteration(pool, alteration, 'down');
await revertAlterations(alterations, pool);
await pool.end();
break;
}
case 'rollback-to-timestamp': {
const pool = await createPoolFromConfig();
const alterations = await chooseRevertAlterationsByTimestamp(target ?? '');
await pool.end();
} else {
consoleLog.fatal('Unsupported action');
await revertAlterations(alterations, pool);
await pool.end();
break;
}
default: {
consoleLog.fatal('Unsupported action');
}
}
},
};

View file

@ -55,3 +55,14 @@ export const getAlterationFiles = async (): Promise<AlterationFile[]> => {
.sort((file1, file2) => getTimestampFromFilename(file1) - getTimestampFromFilename(file2))
.map((filename) => ({ path: path.join(localAlterationDirectory, filename), filename }));
};
export const chooseRevertAlterationsByTimestamp = async (target: string) => {
const files = await getAlterationFiles();
const targetTimestamp = Number(target);
if (Number.isNaN(targetTimestamp)) {
return [];
}
return files.filter(({ filename }) => getTimestampFromFilename(filename) > targetTimestamp);
};