mas: add module for Mac App Store management
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
This commit is contained in:
parent
a1fa429e94
commit
deadc7204c
6 changed files with 344 additions and 0 deletions
|
|
@ -112,6 +112,7 @@
|
|||
./programs/fish.nix
|
||||
./programs/gnupg.nix
|
||||
./programs/man.nix
|
||||
./programs/mas.nix
|
||||
./programs/info
|
||||
./programs/nix-index
|
||||
./programs/ssh.nix
|
||||
|
|
|
|||
209
modules/programs/mas.nix
Normal file
209
modules/programs/mas.nix
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
attrValues
|
||||
concatStringsSep
|
||||
escapeShellArg
|
||||
getExe
|
||||
literalExpression
|
||||
mapAttrsToList
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkOptionDefault
|
||||
mkPackageOption
|
||||
optionalString
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.programs.mas;
|
||||
|
||||
apps = mapAttrsToList (name: id: { inherit name id; }) cfg.packages;
|
||||
|
||||
desiredIds = map (app: toString app.id) apps;
|
||||
homebrewIds = map toString (attrValues config.homebrew.masApps);
|
||||
|
||||
hasWork = cfg.update || cfg.packages != { } || cfg.cleanup || homebrewIds != [ ];
|
||||
|
||||
activationScript =
|
||||
if hasWork then
|
||||
''
|
||||
echo >&2 "setting up App Store apps (mas)..."
|
||||
|
||||
runAsUser() {
|
||||
sudo \
|
||||
--preserve-env=PATH \
|
||||
--set-home \
|
||||
--user=${escapeShellArg cfg.user} \
|
||||
"$@"
|
||||
}
|
||||
|
||||
listStatus=0
|
||||
listOutput=$(
|
||||
runAsUser ${getExe cfg.package} list 2>&1
|
||||
) || listStatus=$?
|
||||
|
||||
if (( listStatus != 0 )); then
|
||||
echo >&2 "warning: mas list failed (exit ''${listStatus}):"
|
||||
echo >&2 "''${listOutput}"
|
||||
if echo "''${listOutput}" | grep -qi "not signed in"; then
|
||||
echo >&2 "login required; skipping App Store installs/updates/cleanup"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only emit cleanup-only shell variables when cleanup is enabled; otherwise shellcheck
|
||||
# treats them as unused and fails the activation script build.
|
||||
installedIds=()
|
||||
${if cfg.cleanup then
|
||||
''
|
||||
# Parse mas list output: "ID AppName (version)"
|
||||
declare -A installedApps
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
line="''${line#"''${line%%[![:space:]]*}"}"
|
||||
id="''${line%% *}"
|
||||
rest="''${line#"$id"}"
|
||||
rest="''${rest#"''${rest%%[![:space:]]*}"}"
|
||||
name="''${rest% (*}"
|
||||
name="''${name%"''${name##*[![:space:]]}"}"
|
||||
[[ -n "$id" ]] && {
|
||||
installedIds+=( "$id" )
|
||||
installedApps["$id"]="$name"
|
||||
}
|
||||
done <<<"$listOutput"
|
||||
''
|
||||
else
|
||||
''
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
line="''${line#"''${line%%[![:space:]]*}"}"
|
||||
id="''${line%% *}"
|
||||
[[ -n "$id" ]] && installedIds+=( "$id" )
|
||||
done <<<"$listOutput"
|
||||
''}
|
||||
|
||||
${optionalString cfg.update ''
|
||||
runAsUser ${getExe cfg.package} update || true
|
||||
''}
|
||||
|
||||
desiredIds=(
|
||||
${concatStringsSep "\n " desiredIds}
|
||||
)
|
||||
|
||||
is_installed() {
|
||||
local needle=$1
|
||||
for id in "''${installedIds[@]}"; do
|
||||
if [[ "$id" == "$needle" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
${optionalString (cfg.packages != { }) ''
|
||||
for appId in "''${desiredIds[@]}"; do
|
||||
if is_installed "$appId"; then
|
||||
continue
|
||||
fi
|
||||
runAsUser ${getExe cfg.package} install "$appId" || true
|
||||
done
|
||||
''}
|
||||
|
||||
${optionalString cfg.cleanup ''
|
||||
homebrewIds=(
|
||||
${concatStringsSep "\n " homebrewIds}
|
||||
)
|
||||
|
||||
keepIds=( "''${desiredIds[@]}" "''${homebrewIds[@]}" )
|
||||
|
||||
for installedId in "''${installedIds[@]}"; do
|
||||
keep=false
|
||||
for keepId in "''${keepIds[@]}"; do
|
||||
if [[ "$installedId" == "$keepId" ]]; then
|
||||
keep=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $keep; then
|
||||
appName="''${installedApps[$installedId]:-$installedId}"
|
||||
echo >&2 "removing $appName from App Store"
|
||||
runAsUser ${getExe cfg.package} uninstall "$installedId" || true
|
||||
fi
|
||||
done
|
||||
''}
|
||||
''
|
||||
else
|
||||
"";
|
||||
in
|
||||
{
|
||||
options.programs.mas = {
|
||||
enable = mkEnableOption "managing Mac App Store apps with mas";
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = config.system.primaryUser;
|
||||
defaultText = literalExpression "config.system.primaryUser";
|
||||
description = ''
|
||||
The user account that runs {command}`mas`. This user must be signed into the Mac App Store
|
||||
for installs or updates to succeed.
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkPackageOption pkgs "mas" { };
|
||||
|
||||
packages = mkOption {
|
||||
type = types.attrsOf types.ints.positive;
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
Xcode = 497799835;
|
||||
"1Password for Safari" = 1569813296;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Applications to install from the Mac App Store. Attribute names are only for readability;
|
||||
values must be the numeric identifiers used by {command}`mas`.
|
||||
'';
|
||||
};
|
||||
|
||||
update = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to run {command}`mas update` during system activation in addition to installing the
|
||||
configured apps.
|
||||
'';
|
||||
};
|
||||
|
||||
cleanup = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to uninstall Mac App Store apps that are currently installed but not listed in
|
||||
{option}`programs.mas.packages`. Apps listed in {option}`homebrew.masApps` are also preserved.
|
||||
This runs before install/update; any app id not in either set will be removed.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
system.requiresPrimaryUser =
|
||||
mkIf (cfg.enable && options.programs.mas.user.highestPrio == (mkOptionDefault { }).priority)
|
||||
[
|
||||
"programs.mas.enable"
|
||||
];
|
||||
|
||||
environment.systemPackages = mkIf cfg.enable [ cfg.package ];
|
||||
|
||||
system.activationScripts.mas.text = mkIf cfg.enable activationScript;
|
||||
};
|
||||
}
|
||||
|
|
@ -134,6 +134,7 @@ in
|
|||
${cfg.activationScripts.keyboard.text}
|
||||
${cfg.activationScripts.fonts.text}
|
||||
${cfg.activationScripts.nvram.text}
|
||||
${cfg.activationScripts.mas.text}
|
||||
${cfg.activationScripts.homebrew.text}
|
||||
|
||||
${cfg.activationScripts.postActivation.text}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue