Files
chiasson-nix/modules/system/flatpak.nix
T

167 lines
6.3 KiB
Nix

{ ... }: {
flake.nixosModules.systemFlatpak =
{ config, lib, pkgs, ... }:
let
cfg = config.chiasson.system.flatpak;
flathubForSystem =
lib.filter
(
app:
if lib.elem app cfg.flathub.x86Only
then pkgs.stdenv.hostPlatform.system == "x86_64-linux"
else true
)
cfg.flathub.appIds;
bundleAppIds = map (b: b.appId) cfg.bundles;
allowedAppIds = cfg.flathub.appIds ++ cfg.unmanaged.appIds ++ bundleAppIds;
# Bump in Nix when allowlists change; used to skip slow `flatpak update` / `uninstall --unused` on no-op rebuilds.
declarativeHash = builtins.hashString "sha256" (
lib.concatStringsSep "\n" (
[ "allowed" ]
++ lib.sort lib.lessThan allowedAppIds
++ [ "flathub" ]
++ lib.sort lib.lessThan flathubForSystem
++ [ "bundles" ]
++ map (b: "${b.appId}@${b.bundle.outPath}") cfg.bundles
)
);
stateFile = "/var/lib/nixos/flatpak-declarative.hash";
in
{
options.chiasson.system.flatpak = {
enable = lib.mkEnableOption ''
System Flatpak (`services.flatpak`) plus an activation script: Flathub remote, allowlisted apps,
and removals for anything else. User Flatpak stays in Home Manager.
'';
runHeavyMaintenanceEverySwitch = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
When false (default), `flatpak update` and `flatpak uninstall --unused` run only if the
allowlists changed (hash in `/var/lib/nixos/flatpak-declarative.hash`). Remotes, removals,
and per-app `install` still run every activation. True = full update/unused every switch.
'';
};
flathub.appIds = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Flathub app IDs to install system-wide.";
};
flathub.x86Only = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Subset of `flathub.appIds` only installed on x86_64-linux.";
};
unmanaged.appIds = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "com.hypixel.HytaleLauncher" ];
description = ''
Installed from elsewhere (not Flathub); never auto-installed and never removed by the script.
'';
};
bundles = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
appId = lib.mkOption {
type = lib.types.str;
description = "Flatpak application ID inside the bundle (e.g. org.moonfin.linux).";
};
bundle = lib.mkOption {
type = lib.types.package;
description = "Upstream .flatpak bundle file (e.g. pkgs.fetchurl { ... }).";
};
};
}
);
default = [ ];
description = ''
System-wide installs from upstream Flatpak bundles (not Flathub).
Reinstalled on switch when the bundle path or appId changes.
'';
};
};
config = lib.mkIf cfg.enable {
services.flatpak.enable = true;
system.activationScripts.flatpakManagement = {
text = ''
STATE_FILE=${lib.escapeShellArg stateFile}
WANT_HASH=${lib.escapeShellArg declarativeHash}
HEAVY=${if cfg.runHeavyMaintenanceEverySwitch then "1" else "0"}
mkdir -p /var/lib/nixos
# Never remote-delete flathub here interactive and breaks unattended rebuilds.
${pkgs.flatpak}/bin/flatpak --system remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/ || true
${pkgs.flatpak}/bin/flatpak --system remote-modify flathub \
--url=https://dl.flathub.org/repo/ 2>/dev/null || true
allowed=( ${lib.concatStringsSep " " (map lib.escapeShellArg allowedAppIds)} )
installedFlatpaks=$(${pkgs.flatpak}/bin/flatpak --system list --app --columns=application)
for installed in $installedFlatpaks; do
keep=0
for a in "''${allowed[@]}"; do
if [ "$installed" = "$a" ]; then
keep=1
break
fi
done
if [ "$keep" -eq 0 ]; then
echo "flatpak: removing $installed (not in allowed list)."
${pkgs.flatpak}/bin/flatpak --system uninstall -y --noninteractive "$installed"
fi
done
for app in ${lib.escapeShellArgs flathubForSystem}; do
echo "flatpak: ensuring $app (Flathub)."
if ! ${pkgs.flatpak}/bin/flatpak --system install -y --noninteractive flathub "$app"; then
echo "flatpak: WARN failed to install $app (skip)." >&2
fi
done
${lib.concatStringsSep "\n" (
map (
b: ''
echo "flatpak: ensuring ${b.appId} from bundle."
if ! ${pkgs.flatpak}/bin/flatpak --system install \
--assumeyes \
--noninteractive \
--reinstall \
--bundle \
${lib.escapeShellArg b.bundle.outPath}; then
echo "flatpak: WARN failed to install ${b.appId} from bundle (skip)." >&2
fi
''
) cfg.bundles
)}
run_heavy=0
if [ "$HEAVY" -eq 1 ]; then
run_heavy=1
elif [ ! -f "$STATE_FILE" ] || [ "$(cat "$STATE_FILE")" != "$WANT_HASH" ]; then
run_heavy=1
fi
if [ "$run_heavy" -eq 1 ]; then
${pkgs.flatpak}/bin/flatpak --system uninstall --unused -y --noninteractive || true
${pkgs.flatpak}/bin/flatpak --system update -y --noninteractive || true
printf '%s' "$WANT_HASH" > "$STATE_FILE"
else
echo "flatpak: allowlists unchanged skipping update and uninstall --unused."
fi
'';
};
};
};
}