Skip to content

Instantly share code, notes, and snippets.

@piousdeer
Last active November 2, 2024 16:00
Show Gist options
  • Save piousdeer/b29c272eaeba398b864da6abf6cb5daa to your computer and use it in GitHub Desktop.
Save piousdeer/b29c272eaeba398b864da6abf6cb5daa to your computer and use it in GitHub Desktop.
Create mutable files with home-manager and Nix
{
home.file."test-file" = {
text = "Hello world";
force = true;
mutable = true;
};
}
# This module extends home.file, xdg.configFile and xdg.dataFile with the `mutable` option.
{ config, lib, ... }:
let
fileOptionAttrPaths =
[ [ "home" "file" ] [ "xdg" "configFile" ] [ "xdg" "dataFile" ] ];
in {
options = let
mergeAttrsList = builtins.foldl' (lib.mergeAttrs) { };
fileAttrsType = lib.types.attrsOf (lib.types.submodule ({ config, ... }: {
options.mutable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to copy the file without the read-only attribute instead of
symlinking. If you set this to `true`, you must also set `force` to
`true`. Mutable files are not removed when you remove them from your
configuration.
This option is useful for programs that don't have a very good
support for read-only configurations.
'';
};
}));
in mergeAttrsList (map (attrPath:
lib.setAttrByPath attrPath (lib.mkOption { type = fileAttrsType; }))
fileOptionAttrPaths);
config = {
home.activation.mutableFileGeneration = let
allFiles = (builtins.concatLists (map
(attrPath: builtins.attrValues (lib.getAttrFromPath attrPath config))
fileOptionAttrPaths));
filterMutableFiles = builtins.filter (file:
(file.mutable or false) && lib.assertMsg file.force
"if you specify `mutable` to `true` on a file, you must also set `force` to `true`");
mutableFiles = filterMutableFiles allFiles;
toCommand = (file:
let
source = lib.escapeShellArg file.source;
target = lib.escapeShellArg file.target;
in ''
$VERBOSE_ECHO "${source} -> ${target}"
$DRY_RUN_CMD cp --remove-destination --no-preserve=mode ${source} ${target}
'');
command = ''
echo "Copying mutable home files for $HOME"
'' + lib.concatLines (map toCommand mutableFiles);
in (lib.hm.dag.entryAfter [ "linkGeneration" ] command);
};
}
{ config, pkgs, lib, ... }:
let
# Path logic from:
# https://github.com/nix-community/home-manager/blob/3876cc613ac3983078964ffb5a0c01d00028139e/modules/programs/vscode.nix
cfg = config.programs.vscode;
vscodePname = cfg.package.pname;
configDir = {
"vscode" = "Code";
"vscode-insiders" = "Code - Insiders";
"vscodium" = "VSCodium";
}.${vscodePname};
userDir = if pkgs.stdenv.hostPlatform.isDarwin then
"Library/Application Support/${configDir}/User"
else
"${config.xdg.configHome}/${configDir}/User";
configFilePath = "${userDir}/settings.json";
tasksFilePath = "${userDir}/tasks.json";
keybindingsFilePath = "${userDir}/keybindings.json";
snippetDir = "${userDir}/snippets";
pathsToMakeWritable = lib.flatten [
(lib.optional (cfg.userTasks != { }) tasksFilePath)
(lib.optional (cfg.userSettings != { }) configFilePath)
(lib.optional (cfg.keybindings != [ ]) keybindingsFilePath)
(lib.optional (cfg.globalSnippets != { })
"${snippetDir}/global.code-snippets")
(lib.mapAttrsToList (language: _: "${snippetDir}/${language}.json")
cfg.languageSnippets)
];
in {
home.file = lib.genAttrs pathsToMakeWritable (_: {
force = true;
mutable = true;
});
}
@becknik
Copy link

becknik commented Apr 6, 2024

Thanks for your super helpful module.
Are you able to use the mutable attribute on xdg.configFile apps? I'm getting an error like the following when I try to.

       error: The option `home-manager.users.jnnk.xdg.configFile."mimeapps.list".mutable' does not exist. Definition values:
       - In `/nix/store/llwv93n27s5z4g781rf68qw8z4272w24-home-manager/desktop-env/xdg-mime.nix': true

@j4t1nd3r
Copy link

j4t1nd3r commented Apr 17, 2024

Hello, I am not sure if this is the solution I am looking for and would like some guidance.
I initially tried symlinking my file ../configs/vscode-settings.json to /.config/Code/User/settings.json but had the unable to write to file issue. Original symlink:

".config/Code/User/settings.json".source = ../configs/vscode-settings.json;

Looking for a way for it to be writable and update the file: ../configs/vscode-settings.json I have tried using this module like this:

      ".config/Code/User/settings.json" = {
        source = ../configs/vscode-settings.json;
        force = true;
        mutable = true;
      };

The settings.json is now writable. I can see the updates appear in .config/Code/User/settings.json but not in vscode-settings.json.
Am I missing something here?

@piousdeer
Copy link
Author

@becknik I can reproduce this issue, specifically with xdg.configFile only. Though I don't currently have time to investigate, so I'm hoping someone else will chip in

@piousdeer
Copy link
Author

@j4t1nd3r It's the expected behavior because the file is copied, not symlinked. I believe mkOutOfStoreSymlink achieves exactly what you want

@j4t1nd3r
Copy link

Thank you, I saw this in places but without context. I am struggling to find good documentation on commands / options.
Even for home.file in the home-manager documentation. It seems looking at nix related github issues, searching discord history in nix related channels is the way to go or reaching out directly.

@becknik
Copy link

becknik commented Nov 2, 2024

Since a few days, I'm getting the following errors when trying to nixos-rebuild:

error:
       … while calling the 'head' builtin
         at /nix/store/z7j461v8da67nwrad4xz50dx2l5wk0wz-source/lib/attrsets.nix:1575:11:
         1574|         || pred here (elemAt values 1) (head values) then
         1575|           head values
             |           ^
         1576|         elsewhile evaluating the attribute 'value'
         at /nix/store/z7j461v8da67nwrad4xz50dx2l5wk0wz-source/lib/modules.nix:809:9:
          808|     in warnDeprecation opt //
          809|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
             |         ^
          810|         inherit (res.defsFinal') highestPrio;

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: getting status of '/nix/store/34ww4vpn116rriqbhzh4snayh7837k7v-mutability.nix': No such file or directory

I have no clue why this is happening, since I haven't changed anything related to this part of the system for a while. Does anybody have an idea?

@j4t1nd3r
Copy link

j4t1nd3r commented Nov 2, 2024

To help solve the issue, I suggest sharing your nix configuration, ideally a public repository link if your configuration is saved in a repo.

@becknik
Copy link

becknik commented Nov 2, 2024

@j4t1nd3r My intention was to reach people having this snippet in their config, since my problem might also be caused by something else.

If you want to take a look anyways: https://github.com/becknik/dotfiles.nix
Every help is greatly appreciated :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment