chore(): Accept an extra agument 'all-or-nothing' on the migrate command (#14262)

* chore(): Accept an extra agument 'all-or-nothing' on the migrate command

* Create rich-camels-brush.md

* chore(): Accept an extra agument 'all-or-nothing' on the migrate command

* chore(): Accept an extra agument 'all-or-nothing' on the migrate command

* chore(): Accept an extra agument 'all-or-nothing' on the migrate command

* chore(): fix broken down migrations

* chore(): update changeset
This commit is contained in:
Adrien de Peretti
2025-12-10 09:23:41 +01:00
committed by GitHub
parent 9bcfb990bf
commit 356283c359
13 changed files with 139 additions and 56 deletions
@@ -48,6 +48,14 @@ type ModuleResource = {
type MigrationFunction = (
options: LoaderOptions<any>,
moduleDeclaration?: InternalModuleDeclaration
) => Promise<{ name: string; path: string }[]>
type RevertMigrationFunction = (
options: LoaderOptions<any> & { migrationNames?: string[] },
moduleDeclaration?: InternalModuleDeclaration
) => Promise<void>
type GenerateMigrationFunction = (
options: LoaderOptions<any>,
moduleDeclaration?: InternalModuleDeclaration
) => Promise<void>
type ResolvedModule = ModuleExports & {
@@ -390,8 +398,8 @@ export async function loadModuleMigrations(
moduleExports?: ModuleExports
): Promise<{
runMigrations?: MigrationFunction
revertMigration?: MigrationFunction
generateMigration?: MigrationFunction
revertMigration?: RevertMigrationFunction
generateMigration?: GenerateMigrationFunction
}> {
const runMigrationsFn: ((...args) => Promise<any>)[] = []
const revertMigrationFn: ((...args) => Promise<any>)[] = []
@@ -488,9 +496,12 @@ export async function loadModuleMigrations(
}
const runMigrations = async (...args) => {
let result: { name: string; path: string }[] = []
for (const migration of runMigrationsFn.filter(Boolean)) {
await migration.apply(migration, args)
const res = await migration.apply(migration, args)
result.push(...res)
}
return result
}
const revertMigration = async (...args) => {
for (const migration of revertMigrationFn.filter(Boolean)) {
+60 -12
View File
@@ -47,7 +47,9 @@ import { MODULE_SCOPE } from "./types"
const LinkModulePackage = MODULE_PACKAGE_NAMES[Modules.LINK]
export type RunMigrationFn = () => Promise<void>
export type RunMigrationFn = (options?: {
allOrNothing?: boolean
}) => Promise<void>
export type RevertMigrationFn = (moduleNames: string[]) => Promise<void>
export type GenerateMigrations = (moduleNames: string[]) => Promise<void>
export type GetLinkExecutionPlanner = () => ILinkMigrationsPlanner
@@ -498,10 +500,12 @@ async function MedusaApp_({
const applyMigration = async ({
modulesNames,
action = "run",
allOrNothing = false,
}: {
modulesNames: string[]
action?: "run" | "revert" | "generate"
}) => {
allOrNothing?: boolean
}): Promise<{ name: string; path: string }[] | void> => {
const moduleResolutions = Array.from(new Set(modulesNames)).map(
(moduleName) => {
return {
@@ -527,7 +531,11 @@ async function MedusaApp_({
throw error
}
const run = async ({ resolution: moduleResolution }) => {
let executedResolutions: [any, string[]][] = [] // [moduleResolution, migration names[]]
const run = async (
{ resolution: moduleResolution },
migrationNames?: string[]
) => {
if (
!moduleResolution.options?.database &&
moduleResolution.moduleDeclaration?.scope === MODULE_SCOPE.INTERNAL
@@ -550,24 +558,62 @@ async function MedusaApp_({
}
if (action === "revert") {
await MedusaModule.migrateDown(migrationOptions)
await MedusaModule.migrateDown(migrationOptions, migrationNames)
} else if (action === "run") {
await MedusaModule.migrateUp(migrationOptions)
const ranMigrationsResult = await MedusaModule.migrateUp(
migrationOptions
)
// Store for revert if anything goes wrong later
executedResolutions.push([
moduleResolution,
ranMigrationsResult?.map((r) => r.name) ?? [],
])
} else {
await MedusaModule.migrateGenerate(migrationOptions)
}
}
const concurrency = parseInt(process.env.DB_MIGRATION_CONCURRENCY ?? "1")
await executeWithConcurrency(
moduleResolutions.map((a) => () => run(a)),
concurrency
)
try {
const results = await executeWithConcurrency(
moduleResolutions.map((a) => () => run(a)),
concurrency
)
const rejections = results.filter(
(result) => result.status === "rejected"
)
if (rejections.length) {
throw new Error(
`Some migrations failed to ${action}: ${rejections
.map((r) => r.reason)
.join(", ")}`
)
}
} catch (error) {
if (allOrNothing) {
action = "revert"
await executeWithConcurrency(
executedResolutions.map(
([resolution, migrationNames]) =>
() =>
run({ resolution }, migrationNames)
),
concurrency
)
}
throw error
}
}
const runMigrations: RunMigrationFn = async (): Promise<void> => {
const runMigrations: RunMigrationFn = async (
{ allOrNothing = false }: { allOrNothing?: boolean } = {
allOrNothing: false,
}
): Promise<void> => {
await applyMigration({
modulesNames: Object.keys(allModules),
allOrNothing,
})
}
@@ -638,7 +684,7 @@ export async function MedusaApp(
}
export async function MedusaAppMigrateUp(
options: MedusaAppOptions = {}
options: MedusaAppOptions & { allOrNothing?: boolean } = {}
): Promise<void> {
const migrationOnly = true
@@ -647,7 +693,9 @@ export async function MedusaAppMigrateUp(
migrationOnly,
})
await runMigrations().finally(MedusaModule.clearInstances)
await runMigrations({ allOrNothing: options.allOrNothing }).finally(
MedusaModule.clearInstances
)
}
export async function MedusaAppMigrateDown(
+18 -10
View File
@@ -828,7 +828,7 @@ class MedusaModule {
moduleKey,
modulePath,
cwd,
}: MigrationOptions): Promise<void> {
}: MigrationOptions): Promise<{ name: string; path: string }[]> {
const moduleResolutions = registerMedusaModule({
moduleKey,
moduleDeclaration: {
@@ -846,6 +846,7 @@ class MedusaModule {
container ??= createMedusaContainer()
let result: { name: string; path: string }[] = []
for (const mod in moduleResolutions) {
const { runMigrations } = await loadModuleMigrations(
container,
@@ -854,23 +855,29 @@ class MedusaModule {
)
if (typeof runMigrations === "function") {
await runMigrations({
const res = await runMigrations({
options,
container: container!,
logger: logger_,
})
result.push(...res)
}
}
return result
}
public static async migrateDown({
options,
container,
moduleExports,
moduleKey,
modulePath,
cwd,
}: MigrationOptions): Promise<void> {
public static async migrateDown(
{
options,
container,
moduleExports,
moduleKey,
modulePath,
cwd,
}: MigrationOptions,
migrationNames?: string[]
): Promise<void> {
const moduleResolutions = registerMedusaModule({
moduleKey,
moduleDeclaration: {
@@ -900,6 +907,7 @@ class MedusaModule {
options,
container: container!,
logger: logger_,
migrationNames,
})
}
}