38 Commits
Author SHA1 Message Date
Domen KožarandGitHub 57a3171f94 programs/devenv: init module (#1834) 2026-07-19 22:34:20 +00:00
Anish Pallati c1403be257 programs/devenv: init module
Signed-off-by: Anish Pallati <i@anish.land>
2026-07-19 00:17:39 -04:00
SamandGitHub b4cccbd4bc darwin-rebuild: add flake options to zsh completions (#1669) 2026-07-18 08:19:57 +00:00
Angel J 1543ae0852 darwin-rebuild: add flake options to zsh completions
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
2026-07-17 22:20:27 -07:00
Michael HoangandGitHub a4cf1d1085 mas: add module for Mac App Store management (#1668) 2026-07-15 13:58:56 +00:00
SamandGitHub d5bd9cd77a bump nixpkgs to fix manual/website builds (#1822) 2026-07-07 03:45:56 +00:00
zowoq b2217f8051 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/8c3cede7ddc26bd659d2d383b5610efbd2c7a16e?narHash=sha256-rppURzHviaQN131F%2BnLiLdGfcb0uCd9gGP0E5%2Biw9MI%3D' (2026-06-08)
  → 'github:NixOS/nixpkgs/f205b5574fd0cb7da5b702a2da51507b7f4fdd1b?narHash=sha256-/NAkDSsve%2BGNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s%3D' (2026-07-05)
2026-07-07 13:03:24 +10:00
SamandGitHub 08920bcfd1 manualHTML: adopt to nixos/nixpkgs#537810 (#1818) 2026-07-07 01:06:54 +00:00
Yifei Sun 320cbf535b manualHTML: adopt to nixos/nixpkgs#537810 2026-07-04 12:51:44 +02:00
Angel J deadc7204c mas: add module for Mac App Store management
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
2026-06-25 18:43:32 -07:00
SamandGitHub a1fa429e94 homebrew: pass extraEnv to cleanup checks (#1804) 2026-06-18 05:49:52 +00:00
Bryan Lai d8a6661f78 homebrew: use brewBundleCmd also for system.checks 2026-06-18 11:00:29 +08:00
Bryan Lai 5138eaf896 homebrew: mv PATH & sudo setup to brewBundleCmd
This should be a trivial refactor, which enables us to reuse
`brewBundleCmd` in the future for `brew bundle cleanup` as well.
2026-06-18 09:35:18 +08:00
Bryan Lai e636bf1664 homebrew: pass onActivation.extraEnv to checks
When onActivation.cleanup == "check", a separate `brew bundle cleanup`
command is run in the system.checks phase before the actual activation.

This `brew bundle cleanup` invocation should use the same `extraEnv`
as the main `brew bundle [install]` command, for consistency.

For example, when $XDG_CONFIG_HOME is set in `extraEnv`, it should also
be picked up by `brew bundle cleanup` in the system.checks phase.
2026-06-18 09:35:18 +08:00
SamandGitHub ee9c7b96c9 homebrew: address new CLI flag requirements (#1789) 2026-06-17 16:57:21 +00:00
Yifei Sun cd7cf09aa3 homebrew: set trusted flag in Brewfile directly
Suggested-by: Austin Horstman <khaneliman12@gmail.com>
Reviewed-by: Sam <30577766+Samasaur1@users.noreply.github.com>
2026-06-17 18:23:19 +02:00
SamandGitHub ee20edc445 users: realpath home dirs before comparison (#1803) 2026-06-17 16:21:08 +00:00
Yifei Sun bb9c29c193 homebrew: address bundle command new CLI flag requirements 2026-06-17 18:15:07 +02:00
Ihar Hrachyshka 08278afff6 users: realpath home dirs before comparison
When we create users with homedir under /var, they end up with
NFSHomeDirectory under /private/var because /var is a symlink. This
breaks consequent activations and requires that user overrides their
home dir attribute with /private/var/... to pass the activation
precheck.

`realpath` should normalize the discrepancy.
2026-06-14 20:54:47 -10:00
SamandGitHub aabb2037ed flake.lock: update nixpkgs (#1799) 2026-06-12 05:33:53 +00:00
SamandGitHub 0bddd7a5ed time: remove double condition (#1800) 2026-06-12 05:32:59 +00:00
SandroandGitHub 9f8122dd2b time: remove double condition 2026-06-11 20:52:34 +02:00
ben 818b88b75f flake.lock: update nixpkgs 2026-06-11 10:06:13 -07:00
SamandGitHub d0d0978f34 Fix typo (#1798) 2026-06-11 16:43:09 +00:00
SandroandGitHub ace757665e Fix typo 2026-06-11 17:58:55 +02:00
SamandGitHub d9ffa378a6 github-runner: remove node20 runtime from defaults (#1796) 2026-06-11 15:00:05 +00:00
Yuriy Taraday b317d77bbf github-runner: remove node20 runtime from defaults
It's marked as insecure starting with 26.05, and now github-runner package defaults to supporting only node24
See https://github.com/NixOS/nixpkgs/pull/524856
2026-06-11 14:02:39 +02:00
SamandGitHub f73cbf1f65 (feature) Add swapEscapeAndCapsLock and swapRightCommandAndRightOption (#1706) 2026-06-11 06:46:22 +00:00
SamandGitHub 958c16ba9a spacebar: add default package (#1786) 2026-06-11 05:59:41 +00:00
Ethan Carter Edwards 4bcdd687ca spacebar: add default package
Upstream should be the default.

Signed-off-by: Ethan Carter Edwards <ethan@ethancedwards.com>
2026-06-08 11:41:05 -07:00
Gaetan Lepage 3f0f0ece39 feat(keyboard): add swapRightCommandAndRightOption 2026-06-08 10:42:29 +02:00
Gaetan Lepage 57ff7cbd20 feat(keyboard): add swapCapsLockAndEscape 2026-06-08 10:42:29 +02:00
SamandGitHub 6a771120d6 version: bump to 26.11 (#1782) 2026-06-07 01:23:23 +00:00
Sam f8531f95fe tests/github-runners: limit to supported node runtimes 2026-05-26 08:50:22 -07:00
Sam 7c3d11b160 modules/terminfo: update list of packages with terminfo 2026-05-26 08:29:42 -07:00
Sam 8073b91aa3 scripts/release: init
This script has not yet been tested in its entirety, but the file
editing operations were used for #1781 and #1782
2026-05-26 08:29:18 -07:00
Sam ae3e2b18d3 flake.lock: update
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/af84f9d270d404c17699522fab95bbf928a2d92f' (2025-12-17)
  → 'github:NixOS/nixpkgs/d849bb215dcdf71bce3e686839ccdb4219e84b2f' (2026-05-24)
2026-05-25 13:16:30 -07:00
Sam 82325d90a1 version: bump to 26.11 2026-05-25 13:08:31 -07:00
24 changed files with 688 additions and 64 deletions
+1 -1
View File
@@ -156,7 +156,7 @@
`nix.daemonIONice` was renamed to `nix.daemonIOLowPriority`, and
`nix.daemonNiceLevel` was removed in favor a new option
`nix.nix.daemonProcessType`.
`nix.daemonProcessType`.
2021-01-16
- Added `homebrew` module, to manage formulas installed by Homebrew via `brew bundle`.
+8 -8
View File
@@ -39,8 +39,8 @@ cd /etc/nix-darwin
# To use Nixpkgs unstable:
nix flake init -t nix-darwin/master
# To use Nixpkgs 25.11:
nix flake init -t nix-darwin/nix-darwin-25.11
# To use Nixpkgs 26.05:
nix flake init -t nix-darwin/nix-darwin-26.05
sed -i '' "s/simple/$(scutil --get LocalHostName)/" flake.nix
```
@@ -60,9 +60,9 @@ Add the following to `flake.nix` in the same folder as `configuration.nix`:
description = "John's darwin system";
inputs = {
# Use `github:NixOS/nixpkgs/nixpkgs-25.11-darwin` to use Nixpkgs 25.11.
# Use `github:NixOS/nixpkgs/nixpkgs-26.05-darwin` to use Nixpkgs 26.05.
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# Use `github:nix-darwin/nix-darwin/nix-darwin-25.11` to use Nixpkgs 25.11.
# Use `github:nix-darwin/nix-darwin/nix-darwin-26.05` to use Nixpkgs 26.05.
nix-darwin.url = "github:nix-darwin/nix-darwin/master";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
};
@@ -88,8 +88,8 @@ Unlike NixOS, `nix-darwin` does not have an installer, you can just run `darwin-
```bash
# To use Nixpkgs unstable:
sudo nix run nix-darwin/master#darwin-rebuild -- switch
# To use Nixpkgs 25.11:
sudo nix run nix-darwin/nix-darwin-25.11#darwin-rebuild -- switch
# To use Nixpkgs 26.05:
sudo nix run nix-darwin/nix-darwin-26.05#darwin-rebuild -- switch
```
### Step 3. Using `nix-darwin`
@@ -132,8 +132,8 @@ Copy the [simple](./modules/examples/simple.nix) example to `/etc/nix-darwin/con
```bash
# If you use Nixpkgs unstable (the default):
sudo nix-channel --add https://github.com/nix-darwin/nix-darwin/archive/master.tar.gz darwin
# If you use Nixpkgs 25.11:
sudo nix-channel --add https://github.com/nix-darwin/nix-darwin/archive/nix-darwin-25.11.tar.gz darwin
# If you use Nixpkgs 26.05:
sudo nix-channel --add https://github.com/nix-darwin/nix-darwin/archive/nix-darwin-26.05.tar.gz darwin
sudo nix-channel --update
```
+1 -2
View File
@@ -94,8 +94,7 @@ in rec {
--stylesheet highlightjs/mono-blue.css \
--script ./highlightjs/highlight.pack.js \
--script ./highlightjs/loader.js \
--toc-depth 1 \
--chunk-toc-depth 1 \
--sidebar-depth 1 \
./manual.md \
$dst/index.html
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1765934234,
"narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=",
"lastModified": 1783279667,
"narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "af84f9d270d404c17699522fab95bbf928a2d92f",
"rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b",
"type": "github"
},
"original": {
+3 -2
View File
@@ -42,16 +42,17 @@
with pkgs.pkgsBuildBuild;
[
alacritty
alacritty-graphics
ghostty-bin
kitty
mtm
rio
rxvt-unicode-unwrapped
rxvt-unicode-unwrapped-emoji
st
termite
tmux
wezterm
] ++ lib.optional (pkgs ? ghostty-bin) ghostty-bin
]
)
);
+88 -27
View File
@@ -151,7 +151,7 @@ let
};
description = ''
Extra environment variables to set when {command}`nix-darwin` invokes
{command}`brew bundle [install]` during system activation.
{command}`brew bundle [install]` during system checks and activation.
Useful for setting Homebrew's `HOMEBREW_NO_*` variables (e.g.,
`HOMEBREW_NO_ENV_HINTS`, `HOMEBREW_NO_ANALYTICS`, `HOMEBREW_NO_UPDATE_REPORT_NEW`)
@@ -172,18 +172,31 @@ let
'';
};
brewBundleCmd = mkInternalOption { type = types.str; };
brewBundleCmd = mkInternalOption { type = types.functionTo types.str; };
};
config = {
brewBundleCmd = concatStringsSep " " (
optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1"
brewBundleCmd = { onlyCheck }: concatStringsSep " " (
[
''PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH"''
"sudo"
"--preserve-env=PATH"
"--user=${escapeShellArg cfg.user}"
"--set-home"
"env"
]
++ optional (onlyCheck || !config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1"
++ mapAttrsToList (k: v: "${k}=${escapeShellArg v}") config.extraEnv
++ [ "brew bundle --file='${brewfileFile}'" ]
++ optional (!config.upgrade) "--no-upgrade"
++ optional (config.cleanup == "uninstall") "--cleanup"
++ optional (config.cleanup == "zap") "--cleanup --zap"
++ config.extraFlags
++ (
if onlyCheck then
[ "cleanup 2>&1" ]
else
optional (!config.upgrade) "--no-upgrade"
++ optional (config.cleanup == "uninstall") "--force-cleanup"
++ optional (config.cleanup == "zap") "--zap --force-cleanup"
++ config.extraFlags
)
);
};
};
@@ -274,6 +287,21 @@ let
for current behavior.
'';
};
trusted = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether to trust this tap during {command}`nix-darwin` system activation, by adding the
`trusted: true` option to its {command}`brew bundle` Brewfile entry.
Homebrew 6.0.0 enabled `HOMEBREW_REQUIRE_TAP_TRUST` by default, which refuses to
load formulae/casks from non-official taps that haven't been trusted, aborting activation.
Set this to `true` for non-official taps you control so their formulae and casks are
installed during activation. Official taps are always trusted, so this has no effect only
them.
'';
};
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
};
@@ -287,7 +315,8 @@ let
"tap ${sCfg.name}"
+ optionalString (sCfg ? clone_target) ", ${sCfg.clone_target}"
+ optionalString (sCfg ? force_auto_update)
", force_auto_update: ${sCfg.force_auto_update}";
", force_auto_update: ${sCfg.force_auto_update}"
+ optionalString config.trusted ", trusted: true";
};
};
@@ -514,6 +543,27 @@ let
# `version_file` is intentionally not exposed: it writes the installed version to a file
# path relative to the `brew bundle` working directory, which is not meaningful during
# nix-darwin system activation.
trusted = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to trust this formula during {command}`nix-darwin` system activation, by adding the
`trusted: true` option to its {command}`brew bundle` Brewfile entry.
Homebrew 6.0.0 enabled `HOMEBREW_REQUIRE_TAP_TRUST` by default, which refuses to
load formulae from non-official taps that haven't been trusted, aborting activation. Set
this to `true` for a formula from a non-official tap you control so it is installed during
activation.
This only takes effect when {option}`name` is a fully-qualified name (`user/repo/formula`),
since only fully-qualified names map to a tap and can be trusted (and installed) on their
own. When {option}`name` is a plain formula name, it is resolved through your tapped
repositories, so trust must instead come from the containing tap being marked as trusted
(see [](#opt-homebrew.taps)). Official taps are always trusted, so this has no effect on
them.
'';
};
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
};
@@ -521,14 +571,15 @@ let
config =
let
sCfg = mkProcessedSubmodConfig config;
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" "link" ];
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" "link" "trusted" ];
in
{
brewfileLine =
"brew ${sCfg.name}"
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}"
+ mkBrewfileLineBoolOrSymbolString "link" config sCfg
+ mkBrewfileLineBoolOrSymbolString "restart_service" config sCfg;
+ mkBrewfileLineBoolOrSymbolString "restart_service" config sCfg
+ optionalString config.trusted ", trusted: true";
};
};
@@ -562,6 +613,27 @@ let
on every {command}`brew bundle` run.
'';
};
trusted = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to trust this cask during {command}`nix-darwin` system activation, by adding the
`trusted: true` option to its {command}`brew bundle` Brewfile entry.
Homebrew 6.0.0 enabled `HOMEBREW_REQUIRE_TAP_TRUST` by default, which refuses to
load casks from non-official taps that haven't been trusted, silently skipping them. Set
this to `true` for a cask from a non-official tap you control so it is installed during
activation.
This only takes effect when {option}`name` is a fully-qualified name (`user/repo/cask`),
since only fully-qualified names map to a tap and can be trusted (and installed) on their
own. When {option}`name` is a plain cask name, it is resolved through your tapped
repositories, so trust must instead come from the containing tap being marked as trusted
(see [](#opt-homebrew.taps)). Official taps are always trusted, so this has no effect on
them.
'';
};
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
};
@@ -569,12 +641,13 @@ let
config =
let
sCfg = mkProcessedSubmodConfig config;
sCfgSubset = removeAttrs sCfg [ "name" ];
sCfgSubset = removeAttrs sCfg [ "name" "trusted" ];
in
{
brewfileLine =
"cask ${sCfg.name}"
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}";
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}"
+ optionalString config.trusted ", trusted: true";
};
};
in
@@ -935,13 +1008,7 @@ in
system.checks.text = mkIf (cfg.enable && cfg.onActivation.cleanup == "check") ''
if [ -f "${cfg.prefix}/bin/brew" ]; then
homebrewCleanupExitCode=0
homebrewCleanupResult=$(PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH" \
sudo \
--preserve-env=PATH \
--user=${escapeShellArg cfg.user} \
--set-home \
env HOMEBREW_NO_AUTO_UPDATE=1 \
brew bundle cleanup --file='${brewfileFile}' 2>&1) || homebrewCleanupExitCode=$?
homebrewCleanupResult=$(${cfg.onActivation.brewBundleCmd { onlyCheck = true; }}) || homebrewCleanupExitCode=$?
if [ "$homebrewCleanupExitCode" -eq 1 ]; then
printf >&2 '\e[1;31merror: found Homebrew packages not listed in the Brewfile, aborting activation\e[0m\n'
printf >&2 '%s\n' "$homebrewCleanupResult"
@@ -963,13 +1030,7 @@ in
# Homebrew Bundle
echo >&2 "Homebrew bundle..."
if [ -f "${cfg.prefix}/bin/brew" ]; then
PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH" \
sudo \
--preserve-env=PATH \
--user=${escapeShellArg cfg.user} \
--set-home \
env \
${cfg.onActivation.brewBundleCmd}
${cfg.onActivation.brewBundleCmd { onlyCheck = false; }}
else
echo -e "\e[1;31merror: Homebrew is not installed, skipping...\e[0m" >&2
fi
+2
View File
@@ -108,10 +108,12 @@
./programs/_1password-gui.nix
./programs/arqbackup.nix
./programs/bash
./programs/devenv.nix
./programs/direnv.nix
./programs/fish.nix
./programs/gnupg.nix
./programs/man.nix
./programs/mas.nix
./programs/info
./programs/nix-index
./programs/ssh.nix
+53
View File
@@ -0,0 +1,53 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.devenv;
hook = shell: "${lib.getExe cfg.package} hook ${shell}";
in
{
meta.maintainers = [
lib.maintainers.domenkozar or "domenkozar"
lib.maintainers.sandydoo or "sandydoo"
lib.maintainers.anish or "anish"
];
options.programs.devenv = {
enable = lib.mkEnableOption "devenv, fast, declarative, reproducible and composable developer environments";
package = lib.mkPackageOption pkgs "devenv" { };
enableBashIntegration = lib.mkEnableOption "auto-activation of devenv environments in Bash" // {
default = true;
};
enableFishIntegration = lib.mkEnableOption "auto-activation of devenv environments in Fish" // {
default = true;
};
enableZshIntegration = lib.mkEnableOption "auto-activation of devenv environments in Zsh" // {
default = true;
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
programs.bash.interactiveShellInit = lib.mkIf cfg.enableBashIntegration ''
eval "$(${hook "bash"})"
'';
programs.fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
${hook "fish"} | source
'';
programs.zsh.interactiveShellInit = lib.mkIf cfg.enableZshIntegration ''
eval "$(${hook "zsh"})"
'';
};
}
+209
View 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;
};
}
+1 -1
View File
@@ -263,7 +263,7 @@ in
nodeRuntimes = mkOption {
type = with types; nonEmptyListOf (enum [ "node20" "node24" ]);
default = [ "node20" "node24" ];
default = [ "node24" ];
description = ''
List of Node.js runtimes the runner should support.
'';
+1
View File
@@ -27,6 +27,7 @@ in
services.spacebar.package = mkOption {
type = path;
default = pkgs.spacebar;
description = "The spacebar package to use.";
};
+1
View File
@@ -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}
+28
View File
@@ -38,6 +38,18 @@ in
description = "Whether to swap the left Command key and left Alt key.";
};
system.keyboard.swapRightCommandAndRightOption = mkOption {
type = types.bool;
default = false;
description = "Whether to swap the right Command key and right Option key.";
};
system.keyboard.swapCapsLockAndEscape = mkOption {
type = types.bool;
default = false;
description = "Whether to swap the Caps Lock key and Escape key.";
};
system.keyboard.swapLeftCtrlAndFn = mkOption {
type = types.bool;
default = false;
@@ -64,6 +76,14 @@ in
(mkIf cfg.remapCapsLockToControl { HIDKeyboardModifierMappingSrc = 30064771129; HIDKeyboardModifierMappingDst = 30064771296; })
(mkIf cfg.remapCapsLockToEscape { HIDKeyboardModifierMappingSrc = 30064771129; HIDKeyboardModifierMappingDst = 30064771113; })
(mkIf cfg.nonUS.remapTilde { HIDKeyboardModifierMappingSrc = 30064771172; HIDKeyboardModifierMappingDst = 30064771125; })
(mkIf cfg.swapCapsLockAndEscape {
HIDKeyboardModifierMappingSrc = 30064771129;
HIDKeyboardModifierMappingDst = 30064771113;
})
(mkIf cfg.swapCapsLockAndEscape {
HIDKeyboardModifierMappingSrc = 30064771113;
HIDKeyboardModifierMappingDst = 30064771129;
})
(mkIf cfg.swapLeftCommandAndLeftAlt {
HIDKeyboardModifierMappingSrc = 30064771299;
HIDKeyboardModifierMappingDst = 30064771298;
@@ -72,6 +92,14 @@ in
HIDKeyboardModifierMappingSrc = 30064771298;
HIDKeyboardModifierMappingDst = 30064771299;
})
(mkIf cfg.swapRightCommandAndRightOption {
HIDKeyboardModifierMappingSrc = 30064771303;
HIDKeyboardModifierMappingDst = 30064771302;
})
(mkIf cfg.swapRightCommandAndRightOption {
HIDKeyboardModifierMappingSrc = 30064771302;
HIDKeyboardModifierMappingDst = 30064771303;
})
(mkIf cfg.swapLeftCtrlAndFn {
HIDKeyboardModifierMappingSrc = 30064771296;
HIDKeyboardModifierMappingDst = 1095216660483;
+6 -13
View File
@@ -3,19 +3,8 @@
with lib;
let
cfg = config.time;
timeZone = optionalString (cfg.timeZone != null) ''
if ! systemsetup -listtimezones | grep -q "^ ${cfg.timeZone}$"; then
echo "${cfg.timeZone} is not a valid timezone. The command 'listtimezones' will show a list of valid time zones." >&2
false
fi
systemsetup -settimezone "${cfg.timeZone}" 2>/dev/null 1>&2
'';
in
{
options = {
@@ -37,8 +26,12 @@ in
system.activationScripts.time.text = mkIf (cfg.timeZone != null) ''
# Set defaults
echo "configuring time..." >&2
${timeZone}
if ! systemsetup -listtimezones | grep -q "^ ${cfg.timeZone}$"; then
echo "${cfg.timeZone} is not a valid timezone. The command 'listtimezones' will show a list of valid time zones." >&2
false
fi
systemsetup -settimezone "${cfg.timeZone}" 2>/dev/null 1>&2
'';
};
+3 -1
View File
@@ -203,7 +203,9 @@ in
else
homeDirectory=$(dscl . -read ${dsclUser} NFSHomeDirectory)
homeDirectory=''${homeDirectory#NFSHomeDirectory: }
if [[ ${escapeShellArg v.home} != "$homeDirectory" ]]; then
configuredHomeDirectory=$(realpath ${escapeShellArg v.home})
homeDirectory=$(realpath "$homeDirectory")
if [[ "$configuredHomeDirectory" != "$homeDirectory" ]]; then
printf >&2 '\e[1;31merror: config contains the wrong home directory for %s, aborting activation\e[0m\n' ${name}
printf >&2 'nix-darwin does not support changing the home directory of existing users.\n'
printf >&2 '\n'
@@ -21,6 +21,17 @@ _arguments \
'(--keep-going -k)'{--keep-going,-k}"[keep going until all builds are finished]"\
'(--keep-failed -K)'{--keep-failed,-K}'[keep failed builds (usually in /tmp)]'\
'--fallback[If binary download fails, fall back on building from source]'\
'--flake[Build from the given flake URI]:flake uri:'\
'--no-flake[Disable implicit flake detection]'\
'(-L --print-build-logs)'{-L,--print-build-logs}'[Print build logs during the build]'\
'--refresh[Refresh flake inputs]'\
'--impure[Allow impure evaluation for flakes]'\
'--recreate-lock-file[Recreate flake.lock from scratch]'\
'*--update-input[Update a specific flake input]:input name:'\
'*--override-input[Override a flake input]:input name: :flake url: '\
'--offline[Do not access the network]'\
'--dry-run[Show what would be done without making changes]'\
'-Q[Only print the resulting store path]'\
'--show-trace[Print stack trace of evaluation errors]'\
'*--option[set Nix configuration option]:options:_nix_options:value:_nix_options_value'\
'*--arg[argument to pass to the Nix function]:Name:_nix_complete_function_arg:Value: '\
+3
View File
@@ -94,7 +94,10 @@ in {
tests.nix-enable = makeTest ./tests/nix-enable.nix;
tests.nixpkgs-overlays = makeTest ./tests/nixpkgs-overlays.nix;
tests.nixpkgs-config-allow-unfree = makeTest ./tests/nixpkgs-config-allow-unfree.nix;
tests.programs-devenv = makeTest ./tests/programs-devenv.nix;
tests.programs-gnupg = makeTest ./tests/programs-gnupg.nix;
tests.programs-mas = makeTest ./tests/programs-mas.nix;
tests.programs-mas-no-cleanup = makeTest ./tests/programs-mas-no-cleanup.nix;
tests.programs-ssh = makeTest ./tests/programs-ssh.nix;
tests.programs-tmux = makeTest ./tests/programs-tmux.nix;
tests.programs-zsh = makeTest ./tests/programs-zsh.nix;
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
git checkout master
git pull
version=$(jq -r .release version.json)
# branch off ${version}
echo "Cutting nix-darwin-${version} release branch"
git checkout -b "nix-darwin-${version}"
sed -i -e "s!- master!- nix-darwin-${version}!" .github/workflows/test.yml
sed -i -e "s!NIXPKGS_BRANCH: nixpkgs-unstable!NIXPKGS_BRANCH: nixpkgs-${version}-darwin!" .github/workflows/test.yml
sed -i -e "s!nixpkgs-unstable!nixpkgs-${version}-darwin!" modules/examples/flake/flake.nix
sed -i -e "s!github:nix-darwin/nix-darwin/master!github:nix-darwin/nix-darwin/nix-darwin-${version}!" modules/examples/flake/flake.nix
sed -i -e "s!nixpkgs/unstable!nixpkgs/stable!g" modules/nix/nixpkgs.nix
sed -i -e "s!nixpkgs-unstable!nixpkgs-${version}-darwin!" flake.nix
nix flake lock
cat <<EOF > README.md
# nix-darwin
This is the ${version} release branch of nix-darwin. See [the main readme](https://github.com/nix-darwin/nix-darwin#readme) for documentation
EOF
cat <<EOF > version.json
{
"release": "${version}",
"isReleaseBranch": true
}
EOF
git add .
git commit -m "version: branch off ${version}"
# update master
echo "Updating master to point to next version"
git checkout master
## update version.json to point to the next version
IFS='.' read -r major minor <<< "$version"
if [[ "$minor" = "11" ]]; then
major=$(( major + 1 ))
minor="05"
else
minor="11"
fi
cat <<EOF > version.json
{
"release": "${major}.${minor}",
"isReleaseBranch": false
}
EOF
## update readme so that instructions refer to the version we're cutting (our supported stable version)
sed -i -e "s![0-9][0-9]\.[0-9][0-9]!${version}!g" README.md
git add .
git commit -m "version: bump to ${major}.${minor}"
nix flake update
git add .
git commit -m "flake.lock: update"
+9 -5
View File
@@ -1,5 +1,9 @@
{ config, ... }:
let
brewBundleInstallCmd = config.homebrew.onActivation.brewBundleCmd { onlyCheck = false; };
in
{
homebrew.enable = true;
homebrew.user = "test-homebrew-user";
@@ -7,12 +11,12 @@
test = ''
echo "checking that cleanup check is present in system checks" >&2
grep 'brew bundle cleanup --file=' ${config.out}/activate
grep "brew bundle --file='.*-Brewfile' cleanup" ${config.out}/activate
echo "checking that brew bundle command does not have --cleanup flag" >&2
if echo "${config.homebrew.onActivation.brewBundleCmd}" | grep -F -- '--cleanup' > /dev/null; then
echo "Expected no --cleanup flag in brewBundleCmd"
echo "Actual: ${config.homebrew.onActivation.brewBundleCmd}"
echo "checking that brew bundle [install] command does not have --cleanup flag" >&2
if echo "${brewBundleInstallCmd}" | grep -F -- '--cleanup' > /dev/null; then
echo "Expected no --cleanup flag in brewBundleInstallCmd"
echo "Actual: ${brewBundleInstallCmd}"
exit 1
fi
'';
+42
View File
@@ -0,0 +1,42 @@
{
config,
lib,
pkgs,
...
}:
let
devenv =
pkgs.runCommand "devenv-0.0.0" { } ''
mkdir -p $out/bin
cat > $out/bin/devenv <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x $out/bin/devenv
''
// {
meta.mainProgram = "devenv";
};
in
{
programs.bash.enable = true;
programs.zsh.enable = true;
programs.fish.enable = true;
programs.devenv = {
enable = true;
package = devenv;
};
test = ''
echo >&2 "checking devenv bash integration in /etc/bashrc"
grep 'devenv hook bash' ${config.out}/etc/bashrc
echo >&2 "checking devenv zsh integration in /etc/zshrc"
grep 'devenv hook zsh' ${config.out}/etc/zshrc
echo >&2 "checking devenv fish integration in /etc/fish/config.fish"
grep 'devenv hook fish' ${config.out}/etc/fish/config.fish
'';
}
+60
View File
@@ -0,0 +1,60 @@
{
config,
pkgs,
...
}:
let
mas =
pkgs.runCommand "mas-0.0.0" { } ''
mkdir -p $out/bin
cat > $out/bin/mas <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x $out/bin/mas
''
// {
meta.mainProgram = "mas";
};
in
{
system.primaryUser = "primary-mas-user";
programs.mas = {
enable = true;
user = "test-mas-user";
package = mas;
update = true;
cleanup = false;
packages = {
Xcode = 497799835;
};
};
homebrew.masApps = {
"KeepFromHomebrew" = 424242;
};
test = ''
echo "checking mas present in systemPackages" >&2
test -x ${config.out}/sw/bin/mas
echo "checking mas activation script still installs and updates apps" >&2
grep 'desiredIds=(' ${config.out}/activate
grep '497799835' ${config.out}/activate
grep 'mas install \"$appId\"' ${config.out}/activate
grep 'mas update' ${config.out}/activate
echo "checking cleanup-only variables are omitted" >&2
if grep 'declare -A installedApps' ${config.out}/activate; then
echo "unexpected installedApps declaration when cleanup is disabled" >&2
exit 1
fi
if grep 'keepIds=(' ${config.out}/activate; then
echo "unexpected keepIds declaration when cleanup is disabled" >&2
exit 1
fi
'';
}
+71
View File
@@ -0,0 +1,71 @@
{
config,
lib,
pkgs,
...
}:
let
mas =
pkgs.runCommand "mas-0.0.0" { } ''
mkdir -p $out/bin
cat > $out/bin/mas <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x $out/bin/mas
''
// {
meta.mainProgram = "mas";
};
in
{
system.primaryUser = "primary-mas-user";
programs.mas = {
enable = true;
user = "test-mas-user";
package = mas;
update = true;
cleanup = true;
packages = {
"1Password for Safari" = 1569813296;
Xcode = 497799835;
};
};
homebrew.masApps = {
"KeepFromHomebrew" = 424242;
};
test = ''
echo "checking mas present in systemPackages" >&2
test -x ${config.out}/sw/bin/mas
echo "checking mas activation script uses requested user" >&2
grep -- '--user=test-mas-user' ${config.out}/activate
echo "checking mas desired ids are present" >&2
grep 'desiredIds=(' ${config.out}/activate
grep '1569813296' ${config.out}/activate
grep '497799835' ${config.out}/activate
echo "checking mas install loop exists" >&2
grep 'mas install \"$appId\"' ${config.out}/activate
echo "checking mas update is triggered" >&2
grep 'mas update' ${config.out}/activate
echo "checking mas parses installedApps from mas list" >&2
grep 'declare -A installedApps' ${config.out}/activate
grep 'installedApps\[' ${config.out}/activate
echo "checking mas cleanup log and uninstall" >&2
grep 'removing .* from App Store' ${config.out}/activate
grep 'runAsUser .*/mas uninstall' ${config.out}/activate
echo "checking homebrew.masApps ids are kept during cleanup" >&2
grep 'homebrewIds=(' ${config.out}/activate
grep '424242' ${config.out}/activate
'';
}
+8
View File
@@ -5,20 +5,28 @@
system.keyboard.remapCapsLockToControl = true;
system.keyboard.remapCapsLockToEscape = true;
system.keyboard.nonUS.remapTilde = true;
system.keyboard.swapCapsLockAndEscape = true;
system.keyboard.swapLeftCommandAndLeftAlt = true;
system.keyboard.swapRightCommandAndRightOption = true;
system.keyboard.swapLeftCtrlAndFn = true;
test = ''
echo checking keyboard mappings in /activate >&2
grep "hidutil property --set '{\"UserKeyMapping\":.*}'" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771129" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771113" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771172" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771113" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771125" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771129" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771296" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771298" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771299" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771296" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771303" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771302" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771302" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771303" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":1095216660483" ${config.out}/activate
'';
}
+1 -1
View File
@@ -1,4 +1,4 @@
{
"release": "26.05",
"release": "26.11",
"isReleaseBranch": false
}