aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/populate/docs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/populate/docs')
-rw-r--r--pkgs/populate/docs/design.md140
1 files changed, 76 insertions, 64 deletions
diff --git a/pkgs/populate/docs/design.md b/pkgs/populate/docs/design.md
index 639d15b..cf9d356 100644
--- a/pkgs/populate/docs/design.md
+++ b/pkgs/populate/docs/design.md
@@ -2,27 +2,33 @@
Populate delivers sources to a target directory. It consists of two parts:
-- Nix code, which turns populate's arguments into a spec: the sources as
- `evalSource` returns them, the target, and the options, as JSON. It
- checks the spec during evaluation, so mistakes show early.
-- A program, which reads a spec and delivers the sources. It knows
- nothing about Nix expressions: whatever produces a spec can drive it.
+- Nix code, which turns populate's arguments into a command line: the
+ sources as `evalSource` returns them, as JSON, the target, and the
+ options. It checks the arguments during evaluation, so mistakes show
+ early.
+- A program, which reads the sources and delivers them to the target its
+ command line names. It knows nothing about Nix expressions: whatever
+ produces sources as JSON can drive it.
So there is one program for each version of populate, instead of a script
for each set of arguments, and the program can be written in any language.
This directory holds the program in Rust. Another implementation, e.g. in
-Haskell, reads the same spec, prints the same messages, and passes the same
-tests.
+Haskell, takes the same command line, prints the same messages, and passes
+the same tests.
## Nix side
`default.nix` is `pkgs.populate`. Given populate's arguments, `{ backup ?
false, force ? false, prune ? false, source, target }`, it checks them with
-`checks.nix`, turns them into a spec, and returns a script running the
-program with the spec on file descriptor 3, so the commands populate runs
-keep standard input. The script puts the tools populate runs in front of
-`PATH`, and depends on the store paths in the spec, so building it builds
-them. `rustProgram` is the program, by default built by `program.nix`.
+`checks.nix`, and returns a script running the program with the sources as
+JSON on file descriptor 3, so the commands populate runs keep standard
+input. A target string goes to the program as it is. A target attribute
+set, as `mkTarget` returns it, goes part by part, as options, with
+`--local` or `--remote` as krops' lib's `isLocalTarget` decides during
+evaluation, like the other functions of krops. The script puts the tools
+populate runs in front of `PATH`, and depends on the store paths in the
+sources, so building it builds them. `rustProgram` is the program, by
+default built by `program.nix`.
`writeDeploy` and the other functions of `pkgs.krops` use `pkgs.populate`,
so their arguments stay as they are.
@@ -30,73 +36,80 @@ so their arguments stay as they are.
## Invocation
```
-populate SPEC
+populate [OPTION]... SOURCES [TARGET]
```
-`SPEC` is the path of a file containing the spec, or `-` for standard
-input. Exit status:
+`SOURCES` is the path of a file containing the sources, or `-` for
+standard input. `TARGET` is `[USER@][HOST][:PORT][PATH]`, parsed like
+krops' lib's `mkTarget` parses it: the user up to the first `@`, the host
+up to a `:` or a `/`, the port up to a `/`, and the path from there. What
+it leaves out gets filled in when the program runs, as `mkTarget` would:
+the user from `LOGNAME`, the host with the machine's name, and the path
+with `/var/src`. An empty user means ssh's default.
+
+| Option | Meaning |
+|-----------------|---------------------------------------------------------|
+| `--backup` | Copy the target directory to a backup first. |
+| `--force` | Create the target directory's sentinel if it's missing. |
+| `--prune` | Remove what isn't a source from the target directory. |
+| `--sudo` | Deliver as root, via sudo on the target. |
+| `--ssh-arg ARG` | Pass `ARG` to ssh; repeatable. |
+| `--user USER` | The target's user, instead of `TARGET`'s. |
+| `--host HOST` | The target's host, e.g. one `TARGET` can't express. |
+| `--port PORT` | The port ssh connects to. |
+| `--path PATH` | The target directory. |
+| `--local` | The target is the machine populate runs on. |
+| `--remote` | The target isn't, even if it looks so. |
+| `-h`, `--help` | Print the usage and exit. |
+| `-V`, `--version` | Print populate's version and exit. |
+
+Options take their argument as the next argument or after `=`, and may
+come before, between, or after `SOURCES` and `TARGET`, up to `--`. Exit
+status:
- 0: populate delivered all sources.
-- 1: a check of the spec failed, or delivering did, with a message of its
- own.
+- 1: a check of the sources or the target failed, or delivering did, with
+ a message of its own.
- The exit status of the command that failed, if delivering failed with
that, like the shell implementation does: e.g. 255 if ssh can't reach
the target, or 128 if git fetch fails.
-- 2: the arguments are wrong, or the spec can't be read, isn't JSON, is of
- another version, or doesn't have the spec's shape.
+- 2: the command line is wrong, or the sources can't be read, aren't JSON,
+ or aren't sources.
- 129, 130, 143: populate got SIGHUP, SIGINT, or SIGTERM, and stopped.
-## Spec, version 1
-
-The spec is a JSON object:
-
-| Field | Type | Default | Meaning |
-|-----------|----------------------|---------|-----------------------------------------------|
-| `version` | number | | The version of the spec: 1. |
-| `backup` | boolean | false | Copy the sources to backup first. |
-| `force` | boolean | false | Create the sentinel if it's missing. |
-| `prune` | boolean | false | Remove what isn't a source. |
-| `source` | object of sources | | The sources, by name. |
-| `target` | object | | Where to deliver them. |
-
-`target` has these fields:
-
-| Field | Type | Default | Meaning |
-|----------------|------------------|---------|------------------------------------------|
-| `user` | string or null | null | The user to log in as. |
-| `host` | string | | The host to deliver to. |
-| `port` | string or null | null | The port ssh connects to. |
-| `path` | string | | The target directory. |
-| `sudo` | boolean | false | Deliver as root, via sudo. |
-| `extraOptions` | array of strings | [] | More arguments for ssh. |
-| `local` | boolean or null | null | Whether the target is local. |
+## Sources
-Each source is an object with the field `type`, the source's type, and a
-field named after the type, holding the source's settings:
+The sources are a JSON object, of sources by name. Each source is an
+object with the field `type`, the source's type, and a field named after
+the type, holding the source's settings:
```json
-{ "type": "git", "git": { "url": "https://...", "ref": "main", ... } }
+{ "nixpkgs": { "type": "git", "git": { "url": "https://...", "ref": "main" } } }
```
The types and their settings are those of `lib/types/populate.nix`, as
`evalSource` returns them: with shorthands expanded, e.g. `file =
"/etc/x"` as `{ "path": "/etc/x" }`, and without settings that are null.
-The source types are `derivation`, `file`, `flake`, `flake-input`, `git`,
-`pass`, `passage`, `pipe`, and `symlink`.
+Settings with defaults may be left out. The source types are
+`derivation`, `file`, `flake`, `flake-input`, `git`, `pass`, `passage`,
+`pipe`, and `symlink`.
-Fields the spec doesn't define are errors, so a misspelt option doesn't go
-unnoticed, and a spec of another version gets refused before anything
-else. A new version of the spec adds fields or changes their meaning; the
-program reads the versions it knows.
+Fields and settings populate doesn't know are errors, so a misspelt option
+doesn't go unnoticed. The sources carry no version: this is version 0, and
+a later version of populate that reads them differently will mark them.
-Store paths in the spec, e.g. of a `file` source that is a derivation, must
-exist when the program runs. The script `default.nix` writes depends on them.
-Something producing a spec at run time has to build them first.
+Store paths in the sources, e.g. of a `file` source that is a derivation,
+must exist when the program runs. The script `default.nix` writes depends
+on them. Something producing sources at run time has to build them first.
## Checks
-The program checks the spec as `checks.nix` does, with the same messages:
+The program checks the sources and the target as `checks.nix` does, with
+the same messages:
+- The target's host isn't empty, and neither it nor its user starts with
+ `-`, so ssh can't take them for options. `checks.nix` doesn't check
+ this, since krops' targets come from the configuration.
- Source names are portable file names: letters, digits, and `._-`, not
starting with `-`, and neither `.`, `..`, nor `.populate`.
- The target path is absolute, not the root directory, and contains
@@ -134,7 +147,7 @@ units of 1024.
## Safety
Populate never changes or removes anything outside the target directory,
-whatever the spec and whatever it finds on the target:
+whatever the sources and whatever it finds on the target:
- The checks above keep source names and the target path inside it.
- A symlink at the target path of a source gets replaced, never written
@@ -152,13 +165,12 @@ places populate must not touch, check this after every phase.
## Execution
-Whether a target is local is what the target's `local` says, or, if
-that's null, gets decided when the program runs, like krops' lib's
-`isLocalTarget` does: it is, if its user is `LOGNAME`, or null if that's
-empty or unset, and its host is `localhost` or the host's name, from
-`HOSTNAME`, or, if that's empty or unset, the only line of `/etc/hostname`
-that is a label. `default.nix` decides during evaluation, like the other
-functions of krops. For a local target, populate runs commands directly.
+Whether a target is local is what `--local` or `--remote` says, or else
+gets decided when the program runs, like krops' lib's `isLocalTarget`
+does: it is, if its user is `LOGNAME`, or null if that's empty or unset,
+and its host is `localhost` or the machine's name, from `HOSTNAME`, or, if
+that's empty or unset, the only line of `/etc/hostname` that is a label.
+For a local target, populate runs commands directly.
For a remote target, populate runs commands over ssh, sharing one master
connection per target. The commands are fixed POSIX shell snippets, with