Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,12 @@ source by.
| `caisson-facter` | none | the hardware of a machine, a nixos-facter report checked against the report versions caisson knows and extended with a project's own options, with the NixOS configurations that run on it declared inside it; a machine gets the report through nixpkgs' `hardware.facter` |
| `facter` | none | a `facter.json` as nixos-facter writes it, with the NixOS configurations that run on it declared inside it; a machine gets it as `hardware.facter.reportPath` |
| `treefmt` | `treefmt-nix` | the formatters of a tree, as treefmt-nix modules; the configuration named `default` is published as the `formatter.<system>` that `nix fmt` runs |
| `nvf` | `nvf` | a Neovim written as nvf modules, published as `nvfConfigurations.<system>.<name>`, in the shape `neovimConfiguration` returns |
| `nixvim` | `nixvim` | a Neovim written as nixvim modules, published as `nixvimConfigurations.<system>.<name>` |
| `nvf` | `nvf` | a Neovim written as nvf modules, published as `nvfConfigurations.<system>.<name>`, in the shape `neovimConfiguration` returns, kept in a Nix profile of its own that the machine, home or hjem configuration it is declared inside activates |
| `nixvim` | `nixvim` | a Neovim written as nixvim modules, published as `nixvimConfigurations.<system>.<name>`, kept in a profile as an nvf configuration is |
| `topology` | `nix-topology` | the diagram of the NixOS configurations declared inside it, as nix-topology renders it; the configuration named `default` is published as `topology.<system>` |
| `kubenix` | `kubenix` | Kubernetes resources written as kubenix modules, published as `kubenixConfigurations.<system>.<name>`, the evaluation, whose `config.kubernetes.result` is the JSON `kubectl apply -f` reads |
| `nixidy` | `nixidy` | an Argo CD environment written as nixidy modules, evaluated with nixidy's `mkEnv` and published as the `nixidyEnvs.<system>.<name>` the nixidy CLI reads |
| `system-manager` | `system-manager` | the systems of machines running another Linux distribution, published as the `systemConfigs` that `system-manager switch` reads; they activate the homes and hjem configurations declared inside them |
| `system-manager` | `system-manager` | the systems of machines running another Linux distribution, published as the `systemConfigs` that `system-manager switch` reads; they activate the homes and hjem configurations declared inside them, and the profiles of the nvf and nixvim configurations declared inside them |
| `structural` | none | a configuration that only exports and holds configurations beneath it: the top of a repository whose point is what it exports, such as caisson itself, or a layer at any depth |

Each integration finds its ecosystem in this order: the `ecosystemSrc`
Expand Down
10 changes: 5 additions & 5 deletions lib-overlays/home-manager/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -517,15 +517,15 @@
defaultPkgs = args.defaultPkgs or null;
inherit exportsTo;
# A home is activated for its user. Its identity is its
# activation package; its marker is a file in the state
# directory of the user, which the home-manager module
# activation package; its marker is a file under
# `$HOME/.local/state`, named by `$HOME` and not by
# `$XDG_STATE_HOME`, which a unit of the machine and a shell
# may see differently. The home-manager module
# `activation-marker` writes. The configurations with an
# activation declared inside the home check it.
activation = {
identity = manifest: manifest.outputs.activationPackage.outPath;
marker =
manifest:
"\${XDG_STATE_HOME:-$HOME/.local/state}/caisson/markers/${selection.markerFileName manifest}";
marker = manifest: "$HOME/.local/state/caisson/markers/${selection.markerFileName manifest}";
};
evaluate = evaluation variant args;
};
Expand Down
183 changes: 183 additions & 0 deletions lib-overlays/integrations/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,184 @@
fi
'';

# A name a configuration is declared under, with the characters
# the shell would read inside double quotes, and the path
# separator, replaced, for use in a path.
safeName =
name:
builtins.replaceStrings [ "/" "\"" "$" "`" "\\" ] [ "_" "_" "_" "_" "_" ] (
if name == null then "default" else name
);

# A configuration that is a program a person runs, such as a
# Neovim, kept in a Nix profile of its own, the way a home keeps
# its generations in the home-manager profile.
#
# `activationPackage` holds `activate`, which checks for drift
# against the nearest configuration above with an activation
# (`driftCheckOf`, when `warn`), and points the profile at
# `package` with `nix-env --profile`, which also makes the
# profile a garbage-collector root. When the profile already
# points at `package`, it changes nothing, so activating the same
# generation again adds no generation to the profile.
#
# Where the profile is depends on the configuration above. Inside
# a machine it is a profile of the system,
# `/nix/var/nix/profiles/caisson/<kind>/<name>`, which the machine
# activates as root. Inside a home or a hjem configuration, or as
# a top, it is a profile of the user,
# `$HOME/.local/state/caisson/profiles/<kind>/<name>`. The path
# names `$HOME` and not `$XDG_STATE_HOME`: the units of a machine
# run without `$XDG_STATE_HOME`, and a shell may set it, and the
# profile must be the same file for both. The
# configuration above activates it each time it is activated
# itself, so activating the configuration above puts the profile
# back to what that configuration declares.
#
# `profile` is what the configuration above reads from the
# manifest: the shell text of the path of the profile, whether it
# is a profile of the system or of the user, the `package` the
# profile points at, the `programs` the
# package has in `bin`, and `variable`, the name of the variable a
# shell sets to choose which profile of this sort it runs
# (`profileShimsOf`).
profileOf =
{
pkgs,
manifest,
kind,
package,
programs,
variable,
warn ? true,
}:
let
ancestor = activatingAncestorOf manifest;
ofSystem = ancestor != null && (ancestor.activation.standsAlone or false);
name = safeName (manifest.name or null);
path =
if ofSystem then
"/nix/var/nix/profiles/caisson/${kind}/${name}"
else
"$HOME/.local/state/caisson/profiles/${kind}/${name}";
check =
if warn then
driftCheckOf {
jq = "${pkgs.jq}/bin/jq";
optionText = "`caisson.drift.warn`";
} manifest
else
"";
in
{
activationPackage = pkgs.writeTextFile {
name = "${kind}-${name}-activation";
destination = "/activate";
executable = true;
text = ''
#!${pkgs.runtimeShell}
set -eu
${check}
profile="${path}"
${pkgs.coreutils}/bin/mkdir -p "$(${pkgs.coreutils}/bin/dirname "$profile")"
if [ "$(${pkgs.coreutils}/bin/readlink -f "$profile" 2>/dev/null || true)" != '${package}' ]; then
${pkgs.nix}/bin/nix-env --profile "$profile" --set '${package}'
fi
'';
};
profile = {
inherit
path
programs
variable
kind
name
package
;
scope = if ofSystem then "system" else "user";
};
};

# The entries of `caisson.exports.configurations` of the
# configuration of `manifest` that it activates as profiles
# (`profileOf`): those with a `profile`, evaluated for the system
# of the configuration, whose nearest configuration above with an
# activation is this one. A profile inside a home inside this
# machine is the home's to activate, and is left out.
profilesActivatedBy =
manifest: configurations:
builtins.filter (
entry:
(entry.manifest.outputs or { }) ? profile
&& (entry.manifest.system or null) == (manifest.system or null)
&& checksMarkerOf manifest entry
) configurations;

# For each variable the profiles in `entries` are chosen with, the
# profile a configuration runs when a shell sets no other, and the
# programs of all of them. The profile is the one `selected` names
# for the variable, as `<kind>/<name>`, or the only one. `what`
# names the configuration, for the message when several profiles
# share a variable and `selected` names none of them.
profileChoicesOf =
{ selected, what }:
entries:
let
variables = unique (builtins.map (entry: entry.manifest.outputs.profile.variable) entries);
choiceOf =
variable:
let
sharing = builtins.filter (entry: entry.manifest.outputs.profile.variable == variable) entries;
nameOf = entry: "${entry.manifest.outputs.profile.kind}/${entry.manifest.outputs.profile.name}";
named = builtins.filter (entry: nameOf entry == selected.${variable}) sharing;
chosen =
if selected ? ${variable} then
if named != [ ] then
builtins.head named
else
throw "caisson: ${what} selects `${selected.${variable}}` with `caisson.profiles.selected.${variable}`, and its profiles for that variable are ${builtins.concatStringsSep ", " (builtins.map nameOf sharing)}."
else if builtins.length sharing == 1 then
builtins.head sharing
else
throw "caisson: ${what} activates several profiles chosen with `${variable}` (${builtins.concatStringsSep ", " (builtins.map nameOf sharing)}). Set `caisson.profiles.selected.${variable}` to the one a shell runs when it sets no other.";
in
{
inherit chosen;
programs = unique (builtins.concatMap (entry: entry.manifest.outputs.profile.programs) sharing);
};
in
genAttrs variables choiceOf;

# The programs with which a configuration that activates profiles
# (`profileOf`) lets a shell choose among them: for each program,
# an executable of that name that runs the program of the profile
# the variable names, read each time it runs, and of `fallback`
# when the variable is unset. A configuration sets the variable to
# its default, a configuration declared inside it sets it to its
# own, and a shell can `export` another. `fallback` is shell text,
# which may name variables such as `$HOME`.
profileShimsOf =
{
pkgs,
variable,
programs,
fallback,
}:
pkgs.runCommand "caisson-profile-shims-${variable}" { } (
''
mkdir -p "$out/bin"
''
+ builtins.concatStringsSep "" (
builtins.map (program: ''
cat > "$out/bin/${program}" <<'EOF'
#!${pkgs.runtimeShell}
exec "''${${variable}:-${fallback}}/bin/${program}" "$@"
EOF
chmod +x "$out/bin/${program}"
'') programs
)
);

isManifest = value: builtins.isAttrs value && (value._type or null) == "caisson-manifest";

# How a configuration is published: the exports its manifest
Expand Down Expand Up @@ -1057,6 +1235,11 @@
markerOf
checksMarkerOf
driftCheckOf
safeName
profileOf
profileShimsOf
profilesActivatedBy
profileChoicesOf
mkEvaluation
publish
topValue
Expand Down
31 changes: 30 additions & 1 deletion lib-overlays/nixvim/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
# configuration is published as `nixvimConfigurations.<system>.<name>`,
# the evaluated configuration, under the name nixvim's flake publishes
# its own configurations under.
#
# A nixvim configuration is kept in a Nix profile of its own, as an nvf
# configuration is (`lib.caisson.integrations.profileOf`): its output
# `activationPackage` holds `activate`, and the configuration it is
# declared inside activates it with the module `profile-activation` of
# its class.
{
mkLibOverlay,
...
Expand Down Expand Up @@ -132,10 +138,27 @@
}
// (if (args.ecosystemArgs or null) != null then args.ecosystemArgs else { })
);
package = value.config.build.package;
kept = selection.profileOf {
inherit pkgs manifest package;
kind = "nixvim";
# The programs the Neovim has in `bin`: `nvim`, and `vi`
# and `vim` when nixvim adds them.
programs = [
"nvim"
]
++ (if value.config.viAlias or false then [ "vi" ] else [ ])
++ (if value.config.vimAlias or false then [ "vim" ] else [ ]);
variable = "CAISSON_NVIM_PROFILE";
warn = value.config.caisson.drift.warn or true;
};
in
{
inherit value;
outputs.package = value.config.build.package;
outputs = {
inherit package;
inherit (kept) activationPackage profile;
};
};

# A nixvim configuration is published as
Expand All @@ -156,6 +179,12 @@
perSystem = true;
defaultPkgs = args.defaultPkgs or null;
inherit exportsTo;
# A nixvim configuration is activated into its profile. Its
# identity is its Neovim.
activation = {
identity = manifest: manifest.outputs.package.outPath;
marker = manifest: "${manifest.outputs.profile.path}.marker.json";
};
evaluate = evaluate args;
};

Expand Down
34 changes: 33 additions & 1 deletion lib-overlays/nvf/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
# `neovimConfiguration` returns. A configuration is published as
# `nvfConfigurations.<system>.<name>`, in the shape `neovimConfiguration`
# returns: `neovim`, `config` and `options`.
#
# An nvf configuration is kept in a Nix profile of its own, the way a
# home keeps its generations (`lib.caisson.integrations.profileOf`):
# its output `activationPackage` holds `activate`, which points the
# profile at the Neovim, and `profile` says where the profile is. A
# machine, a home or a hjem configuration activates the nvf
# configurations declared inside it each time it is activated itself,
# with the module `profile-activation` of its class.
{
mkLibOverlay,
...
Expand Down Expand Up @@ -141,10 +149,28 @@
}
// (if (args.ecosystemArgs or null) != null then args.ecosystemArgs else { })
);
neovim = value.config.vim.build.finalPackage;
kept = selection.profileOf {
inherit pkgs manifest;
kind = "nvf";
package = neovim;
# The programs the Neovim has in `bin`: `nvim`, and `vi`
# and `vim` when nvf adds them.
programs = [
"nvim"
]
++ (if value.config.vim.viAlias then [ "vi" ] else [ ])
++ (if value.config.vim.vimAlias then [ "vim" ] else [ ]);
variable = "CAISSON_NVIM_PROFILE";
warn = value.config.caisson.drift.warn or true;
};
in
{
inherit value;
outputs.neovim = value.config.vim.build.finalPackage;
outputs = {
inherit neovim;
inherit (kept) activationPackage profile;
};
};

# An nvf configuration is published as
Expand All @@ -168,6 +194,12 @@
perSystem = true;
defaultPkgs = args.defaultPkgs or null;
inherit exportsTo;
# An nvf configuration is activated into its profile. Its
# identity is its Neovim.
activation = {
identity = manifest: manifest.outputs.neovim.outPath;
marker = manifest: "${manifest.outputs.profile.path}.marker.json";
};
evaluate = evaluate args;
};

Expand Down
1 change: 1 addition & 0 deletions modules/darwin/default/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
(mkModule ./caisson)
closure-lib.caisson.modules.darwin.home-manager-activation
closure-lib.caisson.modules.darwin.hjem-activation
closure-lib.caisson.modules.darwin.profile-activation
closure-lib.caisson.modules.darwin.activation-marker
]
else
Expand Down
Loading
Loading