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 working-directory: ../cli-test
- name: Run migrations - name: Run migrations
run: npx medusa db:migrate run: yarn medusa db:migrate
working-directory: ../cli-test working-directory: ../cli-test
- name: Create admin user - 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 working-directory: ../cli-test
- name: Run development server - name: Run development server
run: npx medusa develop & run: yarn medusa develop &
working-directory: ../cli-test working-directory: ../cli-test
- name: Testing development server - name: Testing development server
uses: ./.github/actions/test-server uses: ./.github/actions/test-server
- name: Starting medusa - name: Starting medusa
run: npx medusa start & run: yarn medusa start &
working-directory: ../cli-test working-directory: ../cli-test
- name: Testing server - 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 { defineConfig } = require("@medusajs/framework/utils")
const DB_HOST = process.env.DB_HOST 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 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({ module.exports = defineConfig({
admin: { admin: {
disable: true, disable: true,
@@ -17,10 +31,5 @@ module.exports = defineConfig({
jwtSecret: "secret", jwtSecret: "secret",
}, },
}, },
modules: [ modules,
{
key: "custom",
resolve: "src/modules/custom",
},
],
}) })

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

View File

@@ -17,7 +17,7 @@ import {
ContainerRegistrationKeys, ContainerRegistrationKeys,
createMedusaContainer, createMedusaContainer,
defineJoinerConfig, defineJoinerConfig,
discoverFeatureFlagsFromDir, discoverAndRegisterFeatureFlags,
DmlEntity, DmlEntity,
dynamicImport, dynamicImport,
FeatureFlag, FeatureFlag,
@@ -28,7 +28,6 @@ import {
MedusaModuleType, MedusaModuleType,
Modules, Modules,
ModulesSdkUtils, ModulesSdkUtils,
registerFeatureFlag,
stringifyCircular, stringifyCircular,
toMikroOrmEntities, toMikroOrmEntities,
} from "@medusajs/utils" } from "@medusajs/utils"
@@ -579,7 +578,6 @@ export async function loadResources({
} }
const flagDir = resolve(normalizedPath) const flagDir = resolve(normalizedPath)
const discovered = await discoverFeatureFlagsFromDir(flagDir, 1)
const configModule = container.resolve( const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE, ContainerRegistrationKeys.CONFIG_MODULE,
@@ -588,14 +586,13 @@ export async function loadResources({
} }
) as ConfigModule ) as ConfigModule
for (const def of discovered) { await discoverAndRegisterFeatureFlags({
registerFeatureFlag({ flagDir,
flag: def, projectConfigFlags: configModule?.featureFlags ?? {},
projectConfigFlags: configModule?.featureFlags ?? {}, router: FeatureFlag,
router: FeatureFlag, logger,
logger, maxDepth: 1,
}) })
}
const [moduleService, services, models, repositories] = await Promise.all([ const [moduleService, services, models, repositories] = await Promise.all([
dynamicImport(modulePath).then((moduleExports) => { 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-feature-flags"
export * from "./discover-and-register-feature-flags"
export * from "./flag-router" export * from "./flag-router"
export * from "./register-flag" 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( export async function configLoaderOverride(
entryDirectory: string, entryDirectory: string,
@@ -6,6 +10,14 @@ export async function configLoaderOverride(
) { ) {
const { configManager } = await import("@medusajs/framework/config") const { configManager } = await import("@medusajs/framework/config")
const { logger } = await import("@medusajs/framework") const { logger } = await import("@medusajs/framework")
await discoverAndRegisterFeatureFlags({
flagDir: entryDirectory,
projectConfigFlags: {},
router: FeatureFlag,
logger,
})
const { configModule, error } = await getConfigFile< const { configModule, error } = await getConfigFile<
ReturnType<typeof configManager.loadConfig> ReturnType<typeof configManager.loadConfig>
>(entryDirectory, "medusa-config") >(entryDirectory, "medusa-config")

View File

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