Skip to content

Commit 5e3d408

Browse files
committed
fix(macos): allow no-write installs with multiline TOML
1 parent a1c48b3 commit 5e3d408

3 files changed

Lines changed: 106 additions & 12 deletions

File tree

macos/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
### 修复
2020

21+
- 当前版本没有计划写入外观键时,macOS 安装不再解析或备份 `config.toml` 的 TOML 结构。包含多行 MCP `args` 或多行字符串的配置可以原样安装,旧备份的恢复流程仍保留严格检查。
2122
- 保留 Codex 原生固定顶栏的定位与层级,避免打开任务侧边面板后开关被推出主区、导致面板无法关闭。
2223
- 修复亮色背景图在 ChatGPT/Codex 暗色模式下错误生成浅色皮肤壳的问题。`appearance=auto` 现在跟随原生/系统外观,避免白字叠在浅色面板上导致界面不可读。
2324
- 修复从“设置 > 外观”返回“已安排的任务”等无输入框路由后,验证器因找不到 composer 而拒绝合法 Codex 主界面的问题。

macos/scripts/theme-config.mjs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import path from "node:path";
33
import { randomUUID } from "node:crypto";
44

55
const [mode, configPath, backupPath] = process.argv.slice(2);
6-
// Backup these keys so Restore can put them back. Do NOT force dark —
7-
// Dream Skin CSS auto-adapts to light/dark via data-dream-shell.
6+
// Non-null entries are managed settings that install must back up for Restore.
7+
// Keep these null because Dream Skin auto-adapts via data-dream-shell.
88
const settings = new Map([
99
["appearanceTheme", null],
1010
["appearanceDarkCodeThemeId", null],
@@ -229,6 +229,20 @@ async function main() {
229229
if (originalStat.isSymbolicLink() || !originalStat.isFile()) {
230230
throw new Error("Codex config must be a regular file, not a symbolic link.");
231231
}
232+
const hasPlannedSettingWrites = [...settings.values()].some((line) => line !== null);
233+
if (!hasPlannedSettingWrites) {
234+
if (mode === "install") {
235+
console.log("Left Codex appearance settings unchanged; no config backup was needed.");
236+
return;
237+
}
238+
try {
239+
await fs.access(backupPath);
240+
} catch (error) {
241+
if (error.code !== "ENOENT") throw error;
242+
console.log("No selective theme backup is available; config.toml was left unchanged.");
243+
return;
244+
}
245+
}
232246
if (content.includes('"""') || content.includes("'''")) {
233247
throw new Error("Refusing to rewrite TOML containing multiline strings.");
234248
}

macos/tests/run-tests.sh

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -647,21 +647,59 @@ BACKUP="$TMP/theme-backup.json"
647647
/bin/cp "$CONFIG" "$TMP/original.toml"
648648
"$NODE" "$ROOT/scripts/theme-config.mjs" install "$CONFIG" "$BACKUP" >/dev/null
649649
/usr/bin/cmp -s "$CONFIG" "$TMP/original.toml"
650+
[ ! -e "$BACKUP" ]
651+
"$NODE" "$ROOT/scripts/theme-config.mjs" restore "$CONFIG" "$BACKUP" >/dev/null
652+
/usr/bin/cmp -s "$CONFIG" "$TMP/original.toml"
653+
654+
# Backups from releases that managed appearance keys must still restore through
655+
# the strict parser and disappear only after a successful round trip.
650656
"$NODE" -e '
651-
const backup = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
652-
if (backup.values.appearanceTheme !== `appearanceTheme = "system"`) process.exit(1);
653-
if (backup.values.appearanceDarkCodeThemeId !== `appearanceDarkCodeThemeId = "vscode-dark"`) process.exit(1);
654-
' "$BACKUP"
657+
const fs = require("node:fs");
658+
const [file, configPath] = process.argv.slice(1);
659+
fs.writeFileSync(file, `${JSON.stringify({
660+
schemaVersion: 1,
661+
platform: "darwin",
662+
configPath,
663+
values: {
664+
appearanceTheme: `appearanceTheme = "system"`,
665+
appearanceDarkCodeThemeId: `appearanceDarkCodeThemeId = "vscode-dark"`,
666+
},
667+
}, null, 2)}\n`);
668+
' "$BACKUP" "$CONFIG"
669+
/usr/bin/printf '%s\n' \
670+
'model = "gpt-5"' \
671+
'project = "中文项目"' \
672+
'' \
673+
'[desktop]' \
674+
'appearanceTheme = "dark"' \
675+
'appearanceDarkCodeThemeId = "monokai"' \
676+
'keepMe = true' > "$CONFIG"
655677
"$NODE" "$ROOT/scripts/theme-config.mjs" restore "$CONFIG" "$BACKUP" >/dev/null
656678
/usr/bin/cmp -s "$CONFIG" "$TMP/original.toml"
679+
[ ! -e "$BACKUP" ]
680+
681+
write_null_theme_backup() {
682+
local backup="$1"
683+
local config="$2"
684+
"$NODE" -e '
685+
const fs = require("node:fs");
686+
const [file, configPath] = process.argv.slice(1);
687+
fs.writeFileSync(file, `${JSON.stringify({
688+
schemaVersion: 1,
689+
platform: "darwin",
690+
configPath,
691+
values: { appearanceTheme: null, appearanceDarkCodeThemeId: null },
692+
}, null, 2)}\n`);
693+
' "$backup" "$config"
694+
}
657695

658696
assert_theme_config_restore_rejected() {
659697
local label="$1"
660698
local config="$2"
661699
local backup="$3"
662700
/bin/cp "$config" "$config.original"
663701
if "$NODE" "$ROOT/scripts/theme-config.mjs" restore "$config" "$backup" >/dev/null 2>&1; then
664-
printf 'theme-config unexpectedly accepted invalid %s backup.\n' "$label" >&2
702+
printf 'theme-config unexpectedly accepted unsafe %s restore.\n' "$label" >&2
665703
exit 1
666704
fi
667705
/usr/bin/cmp -s "$config" "$config.original"
@@ -699,6 +737,7 @@ NO_DESKTOP_BACKUP="$TMP/theme-backup-without-desktop.json"
699737
"$NODE" "$ROOT/scripts/theme-config.mjs" install "$NO_DESKTOP_CONFIG" "$NO_DESKTOP_BACKUP" >/dev/null
700738
"$NODE" "$ROOT/scripts/theme-config.mjs" restore "$NO_DESKTOP_CONFIG" "$NO_DESKTOP_BACKUP" >/dev/null
701739
/usr/bin/cmp -s "$NO_DESKTOP_CONFIG" "$TMP/original-without-desktop.toml"
740+
[ ! -e "$NO_DESKTOP_BACKUP" ]
702741

703742
INVALID_UTF_CONFIG="$TMP/config-invalid-utf8.toml"
704743
INVALID_UTF_BACKUP="$TMP/config-invalid-utf8-backup.json"
@@ -710,6 +749,11 @@ if "$NODE" "$ROOT/scripts/theme-config.mjs" install \
710749
printf 'theme-config unexpectedly accepted invalid UTF-8.\n' >&2
711750
exit 1
712751
fi
752+
if "$NODE" "$ROOT/scripts/theme-config.mjs" restore \
753+
"$INVALID_UTF_CONFIG" "$INVALID_UTF_BACKUP" >/dev/null 2>&1; then
754+
printf 'theme-config restore unexpectedly accepted invalid UTF-8.\n' >&2
755+
exit 1
756+
fi
713757
/usr/bin/cmp -s "$INVALID_UTF_CONFIG" "$TMP/original-invalid-utf8.toml"
714758
[ ! -e "$INVALID_UTF_BACKUP" ]
715759
[ ! -e "$INVALID_UTF_CONFIG.dream-skin.lock" ]
@@ -723,11 +767,28 @@ assert_theme_config_install_rejected() {
723767
printf 'theme-config unexpectedly accepted invalid %s config.\n' "$label" >&2
724768
exit 1
725769
fi
770+
if "$NODE" "$ROOT/scripts/theme-config.mjs" restore "$config" "$backup" >/dev/null 2>&1; then
771+
printf 'theme-config restore unexpectedly accepted invalid %s config.\n' "$label" >&2
772+
exit 1
773+
fi
726774
/usr/bin/cmp -s "$config" "$config.original"
727775
[ ! -e "$backup" ]
728776
[ ! -e "$config.dream-skin.lock" ]
729777
}
730778

779+
assert_theme_config_install_noop() {
780+
local label="$1"
781+
local config="$2"
782+
local backup="$3"
783+
/bin/cp "$config" "$config.original"
784+
"$NODE" "$ROOT/scripts/theme-config.mjs" install "$config" "$backup" >/dev/null
785+
/usr/bin/cmp -s "$config" "$config.original"
786+
[ ! -e "$backup" ]
787+
"$NODE" "$ROOT/scripts/theme-config.mjs" restore "$config" "$backup" >/dev/null
788+
/usr/bin/cmp -s "$config" "$config.original"
789+
[ ! -e "$config.dream-skin.lock" ]
790+
}
791+
731792
SYMLINK_CONFIG_TARGET="$TMP/config-symlink-target.toml"
732793
SYMLINK_CONFIG_PATH="$TMP/config-symlink.toml"
733794
/usr/bin/printf '%s\n' '[desktop]' 'appearanceTheme = "system"' > "$SYMLINK_CONFIG_TARGET"
@@ -745,20 +806,37 @@ assert_theme_config_install_rejected nul "$NUL_CONFIG" "$TMP/config-nul-backup.j
745806
DUPLICATE_DESKTOP_CONFIG="$TMP/config-duplicate-desktop.toml"
746807
/usr/bin/printf '%s\n' '[desktop]' 'keep = 1' '[desktop]' 'keep = 2' \
747808
> "$DUPLICATE_DESKTOP_CONFIG"
748-
assert_theme_config_install_rejected duplicate-desktop "$DUPLICATE_DESKTOP_CONFIG" \
809+
assert_theme_config_install_noop duplicate-desktop "$DUPLICATE_DESKTOP_CONFIG" \
749810
"$TMP/config-duplicate-desktop-backup.json"
750811

751812
MULTILINE_CONFIG="$TMP/config-multiline.toml"
752813
/usr/bin/printf '%s\n' 'note = """value' 'continued"""' '[desktop]' 'keep = true' \
753814
> "$MULTILINE_CONFIG"
754-
assert_theme_config_install_rejected multiline "$MULTILINE_CONFIG" \
755-
"$TMP/config-multiline-backup.json"
815+
MULTILINE_BACKUP="$TMP/config-multiline-backup.json"
816+
assert_theme_config_install_noop multiline "$MULTILINE_CONFIG" "$MULTILINE_BACKUP"
817+
818+
PRESERVED_BACKUP="$TMP/config-preserved-backup.json"
819+
write_null_theme_backup "$PRESERVED_BACKUP" "$MULTILINE_CONFIG"
820+
/bin/cp "$PRESERVED_BACKUP" "$PRESERVED_BACKUP.original"
821+
"$NODE" "$ROOT/scripts/theme-config.mjs" install \
822+
"$MULTILINE_CONFIG" "$PRESERVED_BACKUP" >/dev/null
823+
/usr/bin/cmp -s "$PRESERVED_BACKUP" "$PRESERVED_BACKUP.original"
756824

757825
MULTILINE_ARRAY_CONFIG="$TMP/config-multiline-array.toml"
758826
/usr/bin/printf '%s\n' '[desktop]' 'rows = [' ' ["one", "two"],' ']' \
759827
'appearanceTheme = "system"' > "$MULTILINE_ARRAY_CONFIG"
760-
assert_theme_config_install_rejected multiline-array "$MULTILINE_ARRAY_CONFIG" \
761-
"$TMP/config-multiline-array-backup.json"
828+
MULTILINE_ARRAY_BACKUP="$TMP/config-multiline-array-backup.json"
829+
assert_theme_config_install_noop multiline-array \
830+
"$MULTILINE_ARRAY_CONFIG" "$MULTILINE_ARRAY_BACKUP"
831+
832+
write_null_theme_backup "$MULTILINE_BACKUP" "$MULTILINE_CONFIG"
833+
assert_theme_config_restore_rejected multiline-layout "$MULTILINE_CONFIG" \
834+
"$MULTILINE_BACKUP"
835+
/bin/rm -f "$MULTILINE_BACKUP"
836+
write_null_theme_backup "$MULTILINE_ARRAY_BACKUP" "$MULTILINE_ARRAY_CONFIG"
837+
assert_theme_config_restore_rejected multiline-array-layout \
838+
"$MULTILINE_ARRAY_CONFIG" "$MULTILINE_ARRAY_BACKUP"
839+
/bin/rm -f "$MULTILINE_ARRAY_BACKUP"
762840

763841
CRLF_CONFIG="$TMP/config-crlf.toml"
764842
CRLF_BACKUP="$TMP/config-crlf-backup.json"
@@ -768,6 +846,7 @@ CRLF_BACKUP="$TMP/config-crlf-backup.json"
768846
"$NODE" "$ROOT/scripts/theme-config.mjs" install "$CRLF_CONFIG" "$CRLF_BACKUP" >/dev/null
769847
"$NODE" "$ROOT/scripts/theme-config.mjs" restore "$CRLF_CONFIG" "$CRLF_BACKUP" >/dev/null
770848
/usr/bin/cmp -s "$CRLF_CONFIG" "$TMP/original-crlf.toml"
849+
[ ! -e "$CRLF_BACKUP" ]
771850

772851
/usr/bin/env -u HOME /bin/bash -c '. "$1/scripts/common-macos.sh"; [ -n "$HOME" ] && [ "$SKIN_VERSION" = "1.2.0" ]' _ "$ROOT"
773852
"$ROOT/scripts/doctor-macos.sh" >/dev/null

0 commit comments

Comments
 (0)