- 2025-08-15
- 更新为更完善的版本
- 2025-06-16
- 发布文章。
如果使用 Home Manager 配置 VSCode 的设置的话,因为 VSCode 启动时会写入 settings.json,导致每次打开 VSCode 都会弹出一个 settings.json 无法保存的弹窗。最简单的解决方案是直接不用 Nix 配置 VSCode。好处是简单粗暴,但是这样就需要用其他方法管理 VSCode 的配置了,和 Nix 的哲学不符。
通过搜索,我找到了一个 21 年的相关 issue #1800,里面获赞最多的是一个 gist,给出了一个模块,可以让 home.file 引入的文件变得可写。但是我更想使用 Home Manager 提供的 profile 选项来配置我的 VSCode,于是我注意到了该 issue 下还有一个比较简短的评论,给出了第三种解决方案的可能。
显然,一条 21 年的评论里的代码是没法直接用的,于是我顺藤摸瓜找到了评论者的 Nix 配置仓库(顺带一提,这位也是一个相当好用的 Nix 格式化软件 Alejandra 的作者),在里面找到了他最新的 VSCode 配置代码。简单总结一下,就是改了一下 VSCode 的配置和扩展文件存储位置,再在开机时清理掉该位置的文件,把 Home Manager 生成的配置和扩展文件复制过去。这种方法既可以避免弹窗,还快速调试 VSCode 的配置,而不需要改一两行配置就要 nix-rebuild;同时也保证了 Nix 配置的稳定性。
但是由于用到了这位作者自己定义的一些库,并且他也不是为了 Home Manager 管理的配置编写的,这个配置代码也是不能直接用的。我这里给一个我的 NixOS 25.05 版本的 Home Manager 模块配置,以供参考:
{ config, lib, pkgs, ...}: let inherit (lib.lists) concatLists;
homeDir = config.home.homeDirectory; userDataDir = "${homeDir}/.data/vscode/data"; extensionsDir = "${homeDir}/.data/vscode/extensions";
pkg = pkgs.vscode.override { commandLineArgs = concatLists [ ["--extensions-dir" extensionsDir] ["--user-data-dir" userDataDir] ]; };in { imports = [./profiles];
programs.vscode = { enable = true; package = pkg; mutableExtensionsDir = false; };
# To solve VSCode wants to write settings.json # VSCode will reset per reboot/rebuild. systemd.user.services.vscode-setup = { Unit = { Description = "VSCode Setup service"; After = ["graphical-session-pre.target"]; Wants = ["graphical-session-pre.target"]; }; Install.WantedBy = ["graphical-session.target"]; Service = { Type = "oneshot"; ExecStart = lib.getExe (pkgs.writeShellApplication { name = "vscode-setup"; text = '' rm -rf "${userDataDir}/User" rm -rf "${extensionsDir}"
mkdir -p "${userDataDir}" mkdir -p "${extensionsDir}"
cp -r --dereference --no-preserve=mode,ownership \ "${homeDir}/.config/Code/User" "${userDataDir}/User" cp -r --dereference --no-preserve=mode,ownership \ "${homeDir}/.vscode/extensions/." "${extensionsDir}" ''; }); }; };}第一版缺陷很多,所以我编写了第二版:
{ config, lib, pkgs, ...}: let homeDir = config.home.homeDirectory; dataDir = config.xdg.dataHome; userDataDir = "${dataDir}/vscode/data"; extensionsDir = "${dataDir}/vscode/extensions";in { programs.vscode = { enable = true; mutableExtensionsDir = false; };
# To solve VSCode wants to write settings.json # VSCode will reset per reboot/rebuild. systemd.user.services.vscode-setup = { Unit = { Description = "VSCode Setup service"; After = ["graphical-session-pre.target"]; Wants = ["graphical-session-pre.target"]; }; Install.WantedBy = ["graphical-session.target"]; Service = { Type = "oneshot"; UMask = "0022"; ExecStart = lib.getExe (pkgs.writeShellApplication { name = "vscode-setup"; runtimeInputs = with pkgs; [coreutils gnutar jq]; text = let userSrc = "${homeDir}/.config/Code/User"; userDst = "${userDataDir}/User"; extSrc = "${homeDir}/.vscode/extensions"; extDst = extensionsDir;
dirsToPreserve = [ "workspaceStorage" "History" ]; backupCmds = builtins.concatStringsSep "\n" (map (dir: '' if [ -e "${userDst}/${dir}" ]; then echo "Backing up data/User/${dir}..." mv "${userDst}/${dir}" "/tmp/vscode-${dir}-$$" fi '') (dirsToPreserve ++ ["globalStorage"])); restoreCmds = builtins.concatStringsSep "\n" (map (dir: '' if [ -e "/tmp/vscode-${dir}-$$" ]; then echo "Restoring data/User/${dir}..." mv "/tmp/vscode-${dir}-$$" "${userDst}/${dir}" fi '') dirsToPreserve); in '' set -eu
${backupCmds}
echo "Cleaning old directories..." rm -rf "${userDst}" rm -rf "${extDst}"
mkdir -p "${userDataDir}" mkdir -p "${extDst}"
echo "Copying user settings from ${userSrc}..." cp -r --dereference --no-preserve=mode,ownership ${userSrc} "${userDst}"
echo "Copying extensions from ${extSrc}..." tar -h -C "${extSrc}" -cf - . | tar -C "${extDst}" -xf - --no-same-owner --no-same-permissions --mode='u=rX,go=rX' chmod u+w -R "${extDst}" 2>/dev/null || true
${restoreCmds}
echo "Restoring and merging data/User/globalStorage..." if [ -e "/tmp/vscode-globalStorage-$$" ]; then cp -rT "/tmp/vscode-globalStorage-$$" "${userDst}/globalStorage"
src_storage_json="${userSrc}/globalStorage/storage.json" dst_storage_json="${userDst}/globalStorage/storage.json"
if [ -f "$src_storage_json" ] && [ -f "$dst_storage_json" ]; then echo "Merging data/globalStorage/storage.json with new data..." merged_json=$(mktemp) jq -s '.[0] * .[1]' "$dst_storage_json" "$src_storage_json" > "$merged_json" mv "$merged_json" "$dst_storage_json" echo "Merge complete." else echo "Skipping storage.json merge: one or both files do not exist." fi else echo "No backed-up globalStorage found to restore." fi
echo "VSCode setup complete." ''; }); }; };}你也可以直接查看我的 Nix 配置文件仓库。