fix(config): preserve explicit git_master overrides during merge
Prevent project config defaults from re-enabling git_master fields that users explicitly disabled. Rebuild git_master from explicit user and project keys so include_co_authored_by and commit_footer merge predictably. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -423,4 +423,92 @@ describe("loadPluginConfig", () => {
|
|||||||
expect(existsSync(canonicalConfigPath)).toBe(true)
|
expect(existsSync(canonicalConfigPath)).toBe(true)
|
||||||
expect(config.agents?.oracle?.model).toBe("openai/gpt-5.4")
|
expect(config.agents?.oracle?.model).toBe("openai/gpt-5.4")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should preserve explicit user git_master settings when project config omits git_master", async () => {
|
||||||
|
// given
|
||||||
|
const rootDir = mkdtempSync(join(tmpdir(), "omo-plugin-config-git-master-user-"))
|
||||||
|
const userConfigDir = join(rootDir, "user-config")
|
||||||
|
const projectDir = join(rootDir, "project")
|
||||||
|
const projectConfigDir = join(projectDir, ".opencode")
|
||||||
|
|
||||||
|
tempDirs.push(rootDir)
|
||||||
|
mkdirSync(userConfigDir, { recursive: true })
|
||||||
|
mkdirSync(projectConfigDir, { recursive: true })
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
join(userConfigDir, "oh-my-openagent.jsonc"),
|
||||||
|
JSON.stringify({
|
||||||
|
git_master: {
|
||||||
|
commit_footer: false,
|
||||||
|
include_co_authored_by: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
join(projectConfigDir, "oh-my-openagent.jsonc"),
|
||||||
|
JSON.stringify({
|
||||||
|
agents: {
|
||||||
|
hephaestus: { model: "openai/gpt-5.4" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
process.env.OPENCODE_CONFIG_DIR = userConfigDir
|
||||||
|
|
||||||
|
// when
|
||||||
|
const { loadPluginConfig } = await importFreshPluginConfigModule()
|
||||||
|
const config = loadPluginConfig(projectDir, {})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(config.git_master).toEqual({
|
||||||
|
commit_footer: false,
|
||||||
|
include_co_authored_by: false,
|
||||||
|
git_env_prefix: "GIT_MASTER=1",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should merge explicit git_master keys from user and project configs", async () => {
|
||||||
|
// given
|
||||||
|
const rootDir = mkdtempSync(join(tmpdir(), "omo-plugin-config-git-master-merge-"))
|
||||||
|
const userConfigDir = join(rootDir, "user-config")
|
||||||
|
const projectDir = join(rootDir, "project")
|
||||||
|
const projectConfigDir = join(projectDir, ".opencode")
|
||||||
|
|
||||||
|
tempDirs.push(rootDir)
|
||||||
|
mkdirSync(userConfigDir, { recursive: true })
|
||||||
|
mkdirSync(projectConfigDir, { recursive: true })
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
join(userConfigDir, "oh-my-openagent.jsonc"),
|
||||||
|
JSON.stringify({
|
||||||
|
git_master: {
|
||||||
|
commit_footer: false,
|
||||||
|
include_co_authored_by: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
join(projectConfigDir, "oh-my-openagent.jsonc"),
|
||||||
|
JSON.stringify({
|
||||||
|
git_master: {
|
||||||
|
commit_footer: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
process.env.OPENCODE_CONFIG_DIR = userConfigDir
|
||||||
|
|
||||||
|
// when
|
||||||
|
const { loadPluginConfig } = await importFreshPluginConfigModule()
|
||||||
|
const config = loadPluginConfig(projectDir, {})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(config.git_master).toEqual({
|
||||||
|
commit_footer: true,
|
||||||
|
include_co_authored_by: false,
|
||||||
|
git_env_prefix: "GIT_MASTER=1",
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,26 @@ import {
|
|||||||
import { migrateLegacyConfigFile } from "./shared/migrate-legacy-config-file";
|
import { migrateLegacyConfigFile } from "./shared/migrate-legacy-config-file";
|
||||||
import { CONFIG_BASENAME, LEGACY_CONFIG_BASENAME } from "./shared/plugin-identity";
|
import { CONFIG_BASENAME, LEGACY_CONFIG_BASENAME } from "./shared/plugin-identity";
|
||||||
|
|
||||||
|
function loadExplicitGitMasterOverrides(configPath: string): Record<string, unknown> | undefined {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = fs.readFileSync(configPath, "utf-8")
|
||||||
|
const rawConfig = parseJsonc<Record<string, unknown>>(content)
|
||||||
|
const gitMaster = rawConfig.git_master
|
||||||
|
|
||||||
|
if (gitMaster && typeof gitMaster === "object" && !Array.isArray(gitMaster)) {
|
||||||
|
return gitMaster as Record<string, unknown>
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
const PARTIAL_STRING_ARRAY_KEYS = new Set([
|
const PARTIAL_STRING_ARRAY_KEYS = new Set([
|
||||||
"disabled_mcps",
|
"disabled_mcps",
|
||||||
"disabled_agents",
|
"disabled_agents",
|
||||||
@@ -229,15 +249,29 @@ export function loadPluginConfig(
|
|||||||
|
|
||||||
// Load user config first (base). Parse empty config through Zod to apply field defaults.
|
// Load user config first (base). Parse empty config through Zod to apply field defaults.
|
||||||
const userConfig = loadConfigFromPath(userConfigPath, ctx)
|
const userConfig = loadConfigFromPath(userConfigPath, ctx)
|
||||||
|
const userGitMasterOverrides = loadExplicitGitMasterOverrides(userConfigPath)
|
||||||
let config: OhMyOpenCodeConfig =
|
let config: OhMyOpenCodeConfig =
|
||||||
userConfig ?? OhMyOpenCodeConfigSchema.parse({});
|
userConfig ?? OhMyOpenCodeConfigSchema.parse({});
|
||||||
|
|
||||||
// Override with project config
|
// Override with project config
|
||||||
|
const defaultGitMaster = OhMyOpenCodeConfigSchema.parse({}).git_master
|
||||||
const projectConfig = loadConfigFromPath(projectConfigPath, ctx);
|
const projectConfig = loadConfigFromPath(projectConfigPath, ctx);
|
||||||
|
const projectGitMasterOverrides = loadExplicitGitMasterOverrides(projectConfigPath)
|
||||||
if (projectConfig) {
|
if (projectConfig) {
|
||||||
config = mergeConfigs(config, projectConfig);
|
config = mergeConfigs(config, projectConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userGitMasterOverrides || projectGitMasterOverrides) {
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
git_master: {
|
||||||
|
...defaultGitMaster,
|
||||||
|
...(userGitMasterOverrides ?? {}),
|
||||||
|
...(projectGitMasterOverrides ?? {}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
...config,
|
...config,
|
||||||
mcp_env_allowlist: userConfig?.mcp_env_allowlist ?? [],
|
mcp_env_allowlist: userConfig?.mcp_env_allowlist ?? [],
|
||||||
|
|||||||
Reference in New Issue
Block a user