diff options
| author | tv <tv@krebsco.de> | 2026-11-14 00:24:45 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-14 01:43:19 +0100 |
| commit | 8f08426ae3a2e5f0ecc5d010bc6469b41e3d7678 (patch) | |
| tree | f6573ff55af0b9eacf77e7d58d3668c1129a589a /pkgs/populate/default.nix | |
| parent | 57d47a36129b459f0fac519a460c581870a54bbf (diff) | |
populate: a command line instead of a spec
The program takes populate's arguments on its command line, like other
programs: populate [OPTION]... SOURCES [TARGET], with the sources as
JSON in the file SOURCES, or on standard input for -. TARGET has the
syntax of krops' lib's mkTarget, [USER@][HOST][:PORT][PATH], parsed the
same way, and what it leaves out gets filled in when populate runs, as
mkTarget would, from LOGNAME and the machine's name. Options set what
the syntax can't express, or override its parts: --sudo, --ssh-arg,
--user, --host, --port, --path, and --local or --remote for whether the
target is the machine populate runs on, which populate otherwise
decides like isLocalTarget. The sources carry no version.
pkgs.populate passes a target attribute set, as mkTarget returns it,
part by part, with --local or --remote as evaluation decides, so
nothing changes for krops. A target string goes to the program as it
is.
The program also refuses target hosts and users starting with -, which
ssh would take for options, now that a command line can give them.
Diffstat (limited to 'pkgs/populate/default.nix')
| -rw-r--r-- | pkgs/populate/default.nix | 67 |
1 files changed, 42 insertions, 25 deletions
diff --git a/pkgs/populate/default.nix b/pkgs/populate/default.nix index c846826..bd8e772 100644 --- a/pkgs/populate/default.nix +++ b/pkgs/populate/default.nix @@ -1,6 +1,6 @@ # Populate, which delivers sources to a target directory. Given populate's # arguments, it checks them, and returns a script running the program in -# this directory with them as JSON. rustProgram is that program, e.g. one +# this directory with them, the sources as JSON. rustProgram is that program, e.g. one # built with cargo during development. { coreutils, findutils, git, nix, openssh, pass, passage, rsync, writers , rustPlatform ? null @@ -21,40 +21,57 @@ let "writers" "rustPlatform" "rustProgram" ] ++ unused)); + # The parts of a target string, like mkTarget parses it, without its + # defaults, which populate fills in when it runs. + parseTarget = s: let + parts = match "(([^@]*)@)?(([^:/]+))?(:([^/]+))?(/.*)?" s; + in + if parts == null then throw "krops: invalid target ${toJSON s}" + else { host = elemAt parts 3; path = elemAt parts 6; }; + populate = { backup ? false, force ? false, prune ? false, source, target }: - assert checks.checkTargetPath target.path; - assert all checks.checkSourceName (attrNames source); - assert checks.checkInputs source; - assert checks.checkCopyingWithSudo target source; let - # Store paths in the arguments stay dependencies of the script, so - # building it builds them. - spec = toJSON { - version = 1; - inherit backup force prune source; - target = { - inherit (target) host path; - user = target.user or null; - port = if target.port or null == null then null else toString target.port; - sudo = target.sudo or false; - extraOptions = target.extraOptions or []; - # Decided during evaluation, like the other functions of krops do. - local = isLocalTarget target; - }; - }; + # A target string goes to populate as it is, so populate decides what + # it leaves out when it runs. An attribute set, as mkTarget returns + # it, goes part by part, with whether it's local decided now, like the + # other functions of krops do. + parsed = parseTarget target; + path = if isString target then (if parsed.path != null then parsed.path else "/var/src") else target.path; + host = if isString target then (if parsed.host != null then parsed.host else "localhost") else target.host; + targetArgs = + if isString target then [ target ] + else [ + "--user" (if target.user or null == null then "" else target.user) + "--host" target.host + "--path" target.path + ] ++ optionals (target.port or null != null) [ + "--port" (toString target.port) + ] ++ optional (target.sudo or false) "--sudo" + ++ concatMap (arg: [ "--ssh-arg" arg ]) (flatten (target.extraOptions or [])) + ++ [ (if isLocalTarget target then "--local" else "--remote") ]; in - writers.writeDash "populate.${target.host}" /* sh */ '' + assert checks.checkTargetPath path; + assert all checks.checkSourceName (attrNames source); + assert checks.checkInputs source; + assert isString target || checks.checkCopyingWithSudo target source; + writers.writeDash "populate.${host}" /* sh */ '' set -efu echo + "$0" >&2 PATH=${makeBinPath [ coreutils findutils git nix openssh pass passage rsync ]}''${PATH:+:$PATH} export PATH - # On a file descriptor of its own, so the commands populate runs keep - # standard input. The JSON is a single line, which can't be the delimiter. - exec ${rustProgram}/bin/populate /dev/fd/3 3<<'EOF' - ${spec} + # The sources on a file descriptor of their own, so the commands + # populate runs keep standard input. Store paths in them stay + # dependencies of this script, so building it builds them. The JSON is + # a single line, which can't be the delimiter. + exec ${rustProgram}/bin/populate ${escapeShellArgs ( + optional backup "--backup" ++ + optional force "--force" ++ + optional prune "--prune" + )} /dev/fd/3 ${escapeShellArgs targetArgs} 3<<'EOF' + ${toJSON source} EOF ''; |
