chore(framework,medusa): load custom flags before medusa config (#13312)

* chore(framework,medusa): load custom flags before medusa config

* test

* test runner

* changeset

* check manager featureFlags

* discover and register flags

* rm comments

* update changeset

* changeset

* use local cli

* execute from local medusa command

---------

Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
This commit is contained in:
Carlos R. L. Rodrigues
2025-09-01 11:04:43 -03:00
committed by GitHub
parent 2a94dbd243
commit b4c0f131b7
10 changed files with 118 additions and 36 deletions

View File

@@ -0,0 +1,9 @@
---
"@medusajs/framework": patch
"@medusajs/medusa": patch
"@medusajs/test-utils": patch
"@medusajs/modules-sdk": patch
"@medusajs/utils": patch
---
chore(framework): load custom flags before medusa config

View File

@@ -71,22 +71,22 @@ jobs:
working-directory: ../cli-test
- name: Run migrations
run: npx medusa db:migrate
run: yarn medusa db:migrate
working-directory: ../cli-test
- name: Create admin user
run: npx medusa user -e test@test.com -p password -i admin_123
run: yarn medusa user -e test@test.com -p password -i admin_123
working-directory: ../cli-test
- name: Run development server
run: npx medusa develop &
run: yarn medusa develop &
working-directory: ../cli-test
- name: Testing development server
uses: ./.github/actions/test-server
- name: Starting medusa
run: npx medusa start &
run: yarn medusa start &
working-directory: ../cli-test
- name: Testing server

View File

@@ -1,3 +1,7 @@
import { InputConfigModules } from "@medusajs/types"
import { FeatureFlag } from "@medusajs/utils"
import { EnvFeatureFlag } from "./src/feature-flags/env-ff"
const { defineConfig } = require("@medusajs/framework/utils")
const DB_HOST = process.env.DB_HOST
@@ -8,6 +12,16 @@ const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}`
process.env.DATABASE_URL = DB_URL
const modules = [] as InputConfigModules
// The custom feature is available here and has default value set to true
if (FeatureFlag.isFeatureEnabled(EnvFeatureFlag.key)) {
modules.push({
key: "custom",
resolve: "src/modules/custom",
})
}
module.exports = defineConfig({
admin: {
disable: true,
@@ -17,10 +31,5 @@ module.exports = defineConfig({
jwtSecret: "secret",
},
},
modules: [
{
key: "custom",
resolve: "src/modules/custom",
},
],
modules,
})

View File

@@ -0,0 +1,8 @@
import { FlagSettings } from "@medusajs/framework/feature-flags"
export const EnvFeatureFlag: FlagSettings = {
key: "env_ff",
default_val: true,
env_key: "ENV_FF",
description: "Environment feature flag",
}

View File

@@ -1,16 +1,15 @@
import { trackFeatureFlag } from "@medusajs/telemetry"
import {
ContainerRegistrationKeys,
discoverFeatureFlagsFromDir,
discoverAndRegisterFeatureFlags,
FeatureFlag,
FlagRouter,
registerFeatureFlag,
} from "@medusajs/utils"
import { asFunction } from "awilix"
import { normalize } from "path"
import { configManager } from "../config"
import { container } from "../container"
import { FlagSettings } from "./types"
import { logger as defaultLogger } from "../logger"
container.register(
ContainerRegistrationKeys.FEATURE_FLAG_ROUTER,
@@ -24,7 +23,11 @@ container.register(
export async function featureFlagsLoader(
sourcePath?: string
): Promise<FlagRouter> {
const { featureFlags: projectConfigFlags = {}, logger } = configManager.config
const confManager = !!configManager.baseDir
? configManager.config
: { featureFlags: {}, logger: defaultLogger }
const { featureFlags: projectConfigFlags = {}, logger } = confManager
if (!sourcePath) {
return FeatureFlag
@@ -32,16 +35,13 @@ export async function featureFlagsLoader(
const flagDir = normalize(sourcePath)
const discovered = await discoverFeatureFlagsFromDir(flagDir)
for (const def of discovered) {
registerFeatureFlag({
flag: def as FlagSettings,
projectConfigFlags,
router: FeatureFlag,
logger,
track: (key) => trackFeatureFlag(key),
})
}
await discoverAndRegisterFeatureFlags({
flagDir,
projectConfigFlags,
router: FeatureFlag,
logger,
track: (key) => trackFeatureFlag(key),
})
return FeatureFlag
}

View File

@@ -17,7 +17,7 @@ import {
ContainerRegistrationKeys,
createMedusaContainer,
defineJoinerConfig,
discoverFeatureFlagsFromDir,
discoverAndRegisterFeatureFlags,
DmlEntity,
dynamicImport,
FeatureFlag,
@@ -28,7 +28,6 @@ import {
MedusaModuleType,
Modules,
ModulesSdkUtils,
registerFeatureFlag,
stringifyCircular,
toMikroOrmEntities,
} from "@medusajs/utils"
@@ -579,7 +578,6 @@ export async function loadResources({
}
const flagDir = resolve(normalizedPath)
const discovered = await discoverFeatureFlagsFromDir(flagDir, 1)
const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE,
@@ -588,14 +586,13 @@ export async function loadResources({
}
) as ConfigModule
for (const def of discovered) {
registerFeatureFlag({
flag: def,
projectConfigFlags: configModule?.featureFlags ?? {},
router: FeatureFlag,
logger,
})
}
await discoverAndRegisterFeatureFlags({
flagDir,
projectConfigFlags: configModule?.featureFlags ?? {},
router: FeatureFlag,
logger,
maxDepth: 1,
})
const [moduleService, services, models, repositories] = await Promise.all([
dynamicImport(modulePath).then((moduleExports) => {

View File

@@ -0,0 +1,43 @@
import { FlagSettings, Logger } from "@medusajs/types"
import { discoverFeatureFlagsFromDir } from "./discover-feature-flags"
import { FlagRouter } from "./flag-router"
import { registerFeatureFlag } from "./register-flag"
export interface DiscoverAndRegisterOptions {
flagDir: string
projectConfigFlags?: Record<string, any>
router: FlagRouter
logger?: Logger
track?: (key: string) => void
maxDepth?: number
}
/**
* Utility function to discover and register feature flags from a directory
*/
export async function discoverAndRegisterFeatureFlags(
options: DiscoverAndRegisterOptions
): Promise<void> {
const {
flagDir,
projectConfigFlags = {},
router,
logger,
track,
maxDepth,
} = options
const discovered = await discoverFeatureFlagsFromDir(flagDir, maxDepth)
for (const def of discovered) {
const registerOptions: Parameters<typeof registerFeatureFlag>[0] = {
flag: def as FlagSettings,
projectConfigFlags,
router,
logger,
track,
}
registerFeatureFlag(registerOptions)
}
}

View File

@@ -1,3 +1,4 @@
export * from "./discover-feature-flags"
export * from "./discover-and-register-feature-flags"
export * from "./flag-router"
export * from "./register-flag"

View File

@@ -1,4 +1,8 @@
import { getConfigFile } from "@medusajs/framework/utils"
import {
FeatureFlag,
getConfigFile,
discoverAndRegisterFeatureFlags,
} from "@medusajs/framework/utils"
export async function configLoaderOverride(
entryDirectory: string,
@@ -6,6 +10,14 @@ export async function configLoaderOverride(
) {
const { configManager } = await import("@medusajs/framework/config")
const { logger } = await import("@medusajs/framework")
await discoverAndRegisterFeatureFlags({
flagDir: entryDirectory,
projectConfigFlags: {},
router: FeatureFlag,
logger,
})
const { configModule, error } = await getConfigFile<
ReturnType<typeof configManager.loadConfig>
>(entryDirectory, "medusa-config")

View File

@@ -139,7 +139,10 @@ export async function initializeContainer(
skipDbConnection?: boolean
}
): Promise<MedusaContainer> {
// custom flags from medusa project
await featureFlagsLoader(rootDirectory)
const configDir = await configLoader(rootDirectory, "medusa-config")
// core flags
await featureFlagsLoader(join(__dirname, ".."))
const customLogger = configDir.logger ?? defaultLogger