codex: add profile config support
Codex 0.134.0 stopped reading profile settings from [profiles.<name>] in config.toml and no longer supports the top-level profile selector. Add a dedicated programs.codex.profiles option so Home Manager can write CODEX_HOME/<name>.config.toml files that match the current CLI --profile behavior. For Codex 0.134.0 and later, legacy programs.codex.settings.profiles entries are migrated into profile files with a warning, and removed legacy profile keys are omitted from config.toml so strict config loading keeps working. Older Codex versions keep the legacy config shape.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-08T12:00:00+00:00";
|
||||
condition = config.programs.codex.enable;
|
||||
message = ''
|
||||
The `programs.codex.profiles` option was added to manage Codex CLI
|
||||
profile files under `CODEX_HOME`.
|
||||
|
||||
These files are selected with `codex --profile <name>`, matching the
|
||||
profile behavior used by Codex 0.134.0 and later.
|
||||
'';
|
||||
}
|
||||
@@ -13,8 +13,11 @@ let
|
||||
yamlFormat = pkgs.formats.yaml { };
|
||||
jsonFormat = pkgs.formats.json { };
|
||||
|
||||
packageVersion = if cfg.package != null then lib.getVersion cfg.package else "0.94.0";
|
||||
isTomlConfig = lib.versionAtLeast packageVersion "0.2.0";
|
||||
# A null package has no detectable version, so assume the latest Codex and
|
||||
# enable version-gated behavior by default.
|
||||
atLeast = version: cfg.package == null || lib.versionAtLeast (lib.getVersion cfg.package) version;
|
||||
isTomlConfig = atLeast "0.2.0";
|
||||
migrateLegacyProfiles = atLeast "0.134.0";
|
||||
settingsFormat = if isTomlConfig then tomlFormat else yamlFormat;
|
||||
in
|
||||
{
|
||||
@@ -82,6 +85,32 @@ in
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
profiles = lib.mkOption {
|
||||
type = lib.types.attrsOf tomlFormat.type;
|
||||
default = { };
|
||||
description = ''
|
||||
Named Codex configuration profiles written to
|
||||
{file}`CODEX_HOME/<name>.config.toml`.
|
||||
|
||||
These profiles are selected with {command}`codex --profile <name>`.
|
||||
Codex 0.134.0 and later no longer reads profile settings from
|
||||
{option}`programs.codex.settings.profiles`, and the top-level
|
||||
{option}`programs.codex.settings.profile` selector is no longer
|
||||
supported.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
deep-review = {
|
||||
model = "gpt-5.5";
|
||||
model_reasoning_effort = "xhigh";
|
||||
approval_policy = "on-request";
|
||||
sandbox_mode = "workspace-write";
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
context = lib.mkOption {
|
||||
type = lib.types.either lib.types.lines lib.types.path;
|
||||
description = ''
|
||||
@@ -336,6 +365,12 @@ in
|
||||
};
|
||||
category = "Productivity";
|
||||
};
|
||||
mkProfileEntry =
|
||||
name: settings:
|
||||
lib.nameValuePair "${configDir}/${name}.config.toml" {
|
||||
source = tomlFormat.generate "codex-${name}-config" settings;
|
||||
};
|
||||
|
||||
transformedMcpServers = lib.optionalAttrs (cfg.enableMcpIntegration && config.programs.mcp.enable) (
|
||||
lib.mapAttrs (
|
||||
name: server:
|
||||
@@ -358,6 +393,23 @@ in
|
||||
) config.programs.mcp.servers
|
||||
);
|
||||
|
||||
# TODO: remove this migration block in a future stateVersion once the
|
||||
# Codex 0.134 profile transition window has passed.
|
||||
hasLegacyProfileSettings =
|
||||
migrateLegacyProfiles && ((rawSettings ? profile) || (rawSettings ? profiles));
|
||||
legacyProfiles = lib.optionalAttrs (
|
||||
hasLegacyProfileSettings && builtins.isAttrs (rawSettings.profiles or null)
|
||||
) rawSettings.profiles;
|
||||
mergedProfiles = legacyProfiles // cfg.profiles;
|
||||
baseSettings =
|
||||
if hasLegacyProfileSettings then
|
||||
lib.removeAttrs rawSettings [
|
||||
"profile"
|
||||
"profiles"
|
||||
]
|
||||
else
|
||||
rawSettings;
|
||||
|
||||
generatedPluginSettings =
|
||||
lib.optionalAttrs (cfg.plugins != [ ] || cfg.marketplaces != { }) {
|
||||
features.plugins = true;
|
||||
@@ -368,14 +420,23 @@ in
|
||||
// lib.optionalAttrs (cfg.marketplaces != { }) {
|
||||
marketplaces = lib.mapAttrs mkMarketplaceConfigEntry cfg.marketplaces;
|
||||
};
|
||||
mergedSettingsWithoutMcp = lib.recursiveUpdate rawSettings generatedPluginSettings;
|
||||
mergedSettingsWithoutMcp = lib.recursiveUpdate baseSettings generatedPluginSettings;
|
||||
settingMcpServers = lib.attrByPath [ "mcp_servers" ] { } mergedSettingsWithoutMcp;
|
||||
mergedMcpServers = transformedMcpServers // settingMcpServers;
|
||||
|
||||
mergedSettings =
|
||||
mergedSettingsWithoutMcp
|
||||
// lib.optionalAttrs (mergedMcpServers != { }) { mcp_servers = mergedMcpServers; };
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
warnings = lib.optional hasLegacyProfileSettings ''
|
||||
`programs.codex.settings.profile` and `programs.codex.settings.profiles`
|
||||
are no longer supported by Codex 0.134.0 and later. Home Manager
|
||||
now writes entries from `programs.codex.settings.profiles` to
|
||||
`CODEX_HOME/<name>.config.toml`. Move them to
|
||||
`programs.codex.profiles` and remove `programs.codex.settings.profile`.
|
||||
'';
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = (cfg.plugins == [ ] && cfg.marketplaces == { }) || isTomlConfig;
|
||||
@@ -448,6 +509,7 @@ in
|
||||
text = cfg.context;
|
||||
};
|
||||
}
|
||||
// lib.mapAttrs' mkProfileEntry mergedProfiles
|
||||
// lib.mapAttrs' mkSkillEntry skillSources
|
||||
// lib.listToAttrs (map mkPluginFileEntry cfg.plugins)
|
||||
// lib.mapAttrs' mkRuleEntry cfg.rules;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
codex-mcp-integration = ./mcp-integration.nix;
|
||||
codex-mcp-integration-with-override = ./mcp-integration-with-override.nix;
|
||||
codex-plugins = ./plugins.nix;
|
||||
codex-profiles-legacy-old-version = ./profiles-legacy-old-version.nix;
|
||||
codex-profiles-legacy-warning = ./profiles-legacy-warning.nix;
|
||||
codex-profiles = ./profiles.nix;
|
||||
codex-rules = ./rules.nix;
|
||||
codex-skills-inline = ./skills-inline.nix;
|
||||
codex-skills-inline-null-package = ./skills-inline-null-package.nix;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
approval_policy = "never"
|
||||
sandbox_mode = "workspace-write"
|
||||
@@ -0,0 +1 @@
|
||||
model = "gpt-5"
|
||||
@@ -0,0 +1,4 @@
|
||||
approval_policy = "on-request"
|
||||
model = "gpt-5.5"
|
||||
model_reasoning_effort = "xhigh"
|
||||
sandbox_mode = "workspace-write"
|
||||
@@ -0,0 +1,31 @@
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
codexPackage = pkgs.runCommand "codex-0.133.0" { } ''
|
||||
mkdir -p $out/bin
|
||||
echo '#!/bin/sh' > $out/bin/codex
|
||||
chmod +x $out/bin/codex
|
||||
'';
|
||||
in
|
||||
{
|
||||
programs.codex = {
|
||||
enable = true;
|
||||
package = codexPackage;
|
||||
settings = {
|
||||
model = "gpt-5";
|
||||
profile = "deep-review";
|
||||
profiles.deep-review = {
|
||||
approval_policy = "on-request";
|
||||
sandbox_mode = "workspace-write";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test.asserts.warnings.expected = [ ];
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.codex/config.toml
|
||||
assertFileContent home-files/.codex/config.toml \
|
||||
${./profiles-legacy-old-version.toml}
|
||||
assertPathNotExists home-files/.codex/deep-review.config.toml
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
model = "gpt-5"
|
||||
profile = "deep-review"
|
||||
|
||||
[profiles.deep-review]
|
||||
approval_policy = "on-request"
|
||||
sandbox_mode = "workspace-write"
|
||||
@@ -0,0 +1,2 @@
|
||||
approval_policy = "on-request"
|
||||
sandbox_mode = "workspace-write"
|
||||
@@ -0,0 +1,41 @@
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
codexPackage = pkgs.runCommand "codex-0.134.0" { } ''
|
||||
mkdir -p $out/bin
|
||||
echo '#!/bin/sh' > $out/bin/codex
|
||||
chmod +x $out/bin/codex
|
||||
'';
|
||||
in
|
||||
{
|
||||
programs.codex = {
|
||||
enable = true;
|
||||
package = codexPackage;
|
||||
settings = {
|
||||
model = "gpt-5";
|
||||
profile = "deep-review";
|
||||
profiles.deep-review = {
|
||||
approval_policy = "on-request";
|
||||
sandbox_mode = "workspace-write";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test.asserts.warnings.expected = [
|
||||
''
|
||||
`programs.codex.settings.profile` and `programs.codex.settings.profiles`
|
||||
are no longer supported by Codex 0.134.0 and later. Home Manager
|
||||
now writes entries from `programs.codex.settings.profiles` to
|
||||
`CODEX_HOME/<name>.config.toml`. Move them to
|
||||
`programs.codex.profiles` and remove `programs.codex.settings.profile`.
|
||||
''
|
||||
];
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.codex/config.toml
|
||||
assertFileContent home-files/.codex/config.toml \
|
||||
${./profiles-legacy-warning.toml}
|
||||
assertFileExists home-files/.codex/deep-review.config.toml
|
||||
assertFileContent home-files/.codex/deep-review.config.toml \
|
||||
${./profiles-legacy-warning-deep-review.toml}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
model = "gpt-5"
|
||||
@@ -0,0 +1,43 @@
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
codexPackage = pkgs.runCommand "codex-0.134.0" { } ''
|
||||
mkdir -p $out/bin
|
||||
echo '#!/bin/sh' > $out/bin/codex
|
||||
chmod +x $out/bin/codex
|
||||
'';
|
||||
in
|
||||
{
|
||||
programs.codex = {
|
||||
enable = true;
|
||||
package = codexPackage;
|
||||
settings = {
|
||||
model = "gpt-5";
|
||||
};
|
||||
profiles = {
|
||||
deep-review = {
|
||||
approval_policy = "on-request";
|
||||
model = "gpt-5.5";
|
||||
model_reasoning_effort = "xhigh";
|
||||
sandbox_mode = "workspace-write";
|
||||
};
|
||||
ci = {
|
||||
approval_policy = "never";
|
||||
sandbox_mode = "workspace-write";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test.asserts.warnings.expected = [ ];
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.codex/config.toml
|
||||
assertFileContent home-files/.codex/config.toml \
|
||||
${./profiles-config.toml}
|
||||
assertFileExists home-files/.codex/deep-review.config.toml
|
||||
assertFileContent home-files/.codex/deep-review.config.toml \
|
||||
${./profiles-deep-review.toml}
|
||||
assertFileExists home-files/.codex/ci.config.toml
|
||||
assertFileContent home-files/.codex/ci.config.toml \
|
||||
${./profiles-ci.toml}
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user