python312Packages.dissect-extfs: 3.11 -> 3.12
[NixPkgs.git] / nixos / modules / services / system / self-deploy.nix
blob06d1644fec646732b852b6bd6bcfe2b774ddb9a0
1 { config, lib, pkgs, ... }:
3 let
4   cfg = config.services.self-deploy;
6   workingDirectory = "/var/lib/nixos-self-deploy";
7   repositoryDirectory = "${workingDirectory}/repo";
8   outPath = "${workingDirectory}/system";
10   gitWithRepo = "git -C ${repositoryDirectory}";
12   renderNixArgs = args:
13     let
14       toArg = key: value:
15         if builtins.isString value
16         then " --argstr ${lib.escapeShellArg key} ${lib.escapeShellArg value}"
17         else " --arg ${lib.escapeShellArg key} ${lib.escapeShellArg (toString value)}";
18     in
19     lib.concatStrings (lib.mapAttrsToList toArg args);
21   isPathType = x: lib.types.path.check x;
25   options.services.self-deploy = {
26     enable = lib.mkEnableOption "self-deploy";
28     nixFile = lib.mkOption {
29       type = lib.types.path;
31       default = "/default.nix";
33       description = ''
34         Path to nix file in repository. Leading '/' refers to root of
35         git repository.
36       '';
37     };
39     nixAttribute = lib.mkOption {
40       type = with lib.types; nullOr str;
42       default = null;
44       description = ''
45         Attribute of `nixFile` that builds the current system.
46       '';
47     };
49     nixArgs = lib.mkOption {
50       type = lib.types.attrs;
52       default = { };
54       description = ''
55         Arguments to `nix-build` passed as `--argstr` or `--arg` depending on
56         the type.
57       '';
58     };
60     switchCommand = lib.mkOption {
61       type = lib.types.enum [ "boot" "switch" "dry-activate" "test" ];
63       default = "switch";
65       description = ''
66         The `switch-to-configuration` subcommand used.
67       '';
68     };
70     repository = lib.mkOption {
71       type = with lib.types; oneOf [ path str ];
73       description = ''
74         The repository to fetch from. Must be properly formatted for git.
76         If this value is set to a path (must begin with `/`) then it's
77         assumed that the repository is local and the resulting service
78         won't wait for the network to be up.
80         If the repository will be fetched over SSH, you must add an
81         entry to `programs.ssh.knownHosts` for the SSH host for the fetch
82         to be successful.
83       '';
84     };
86     sshKeyFile = lib.mkOption {
87       type = with lib.types; nullOr path;
89       default = null;
91       description = ''
92         Path to SSH private key used to fetch private repositories over
93         SSH.
94       '';
95     };
97     branch = lib.mkOption {
98       type = lib.types.str;
100       default = "master";
102       description = ''
103         Branch to track
105         Technically speaking any ref can be specified here, as this is
106         passed directly to a `git fetch`, but for the use-case of
107         continuous deployment you're likely to want to specify a branch.
108       '';
109     };
111     startAt = lib.mkOption {
112       type = with lib.types; either str (listOf str);
114       default = "hourly";
116       description = ''
117         The schedule on which to run the `self-deploy` service. Format
118         specified by `systemd.time 7`.
120         This value can also be a list of `systemd.time 7` formatted
121         strings, in which case the service will be started on multiple
122         schedules.
123       '';
124     };
125   };
127   config = lib.mkIf cfg.enable {
128     systemd.services.self-deploy = rec {
129       inherit (cfg) startAt;
131       serviceConfig.Type = "oneshot";
133       requires = lib.mkIf (!(isPathType cfg.repository)) [ "network-online.target" ];
135       after = requires;
137       environment.GIT_SSH_COMMAND = lib.mkIf (cfg.sshKeyFile != null)
138         "${pkgs.openssh}/bin/ssh -i ${lib.escapeShellArg cfg.sshKeyFile}";
140       restartIfChanged = false;
142       path = with pkgs; [
143         git
144         gnutar
145         gzip
146         nix
147       ] ++ lib.optionals (cfg.switchCommand == "boot") [ systemd ];
149       script = ''
150         if [ ! -e ${repositoryDirectory} ]; then
151           mkdir --parents ${repositoryDirectory}
152           git init ${repositoryDirectory}
153         fi
155         ${gitWithRepo} fetch ${lib.escapeShellArg cfg.repository} ${lib.escapeShellArg cfg.branch}
157         ${gitWithRepo} checkout FETCH_HEAD
159         nix-build${renderNixArgs cfg.nixArgs} ${lib.cli.toGNUCommandLineShell { } {
160           attr = cfg.nixAttribute;
161           out-link = outPath;
162         }} ${lib.escapeShellArg "${repositoryDirectory}${cfg.nixFile}"}
164         ${lib.optionalString (cfg.switchCommand != "test")
165           "nix-env --profile /nix/var/nix/profiles/system --set ${outPath}"}
167         ${outPath}/bin/switch-to-configuration ${cfg.switchCommand}
169         rm ${outPath}
171         ${gitWithRepo} gc --prune=all
173         ${lib.optionalString (cfg.switchCommand == "boot") "systemctl reboot"}
174       '';
175     };
176   };