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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ fish-test-dev: ## Run fish tests inside the Nix dev shell (mirrors CI).
.PHONY: shell-check
shell-check: ## Run ShellCheck on shell scripts.
@echo "🔍 Running ShellCheck..."
@find . -name '*.sh' -not -path './node_modules/*' -not -path './.git/*' -not -path './result/*' -not -path './.venv/*' -not -path './.claude/*' -not -path './dotagents/.claude/*' -not -path './dotagents/.codex/*' -print0 | xargs -0 shellcheck
@find . -name '*.sh' -not -path './node_modules/*' -not -path './.git/*' -not -path './.worktrees/*' -not -path './result/*' -not -path './.venv/*' -not -path './.claude/*' -not -path './dotagents/.claude/*' -not -path './dotagents/.codex/*' -print0 | xargs -0 shellcheck

.PHONY: shell-check-dev
shell-check-dev: ## Run ShellCheck inside the Nix dev shell (mirrors CI).
Expand Down
4 changes: 3 additions & 1 deletion nix-darwin/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ let
networking = import ./config/networking.nix;
nix = import ./config/nix.nix;
security = import ./config/security.nix { inherit username; };
serviceModules = import ./services { inherit lib isRunner pkgs; };
system = import ./config/system.nix { inherit isRunner pkgs username; };
time = import ./config/time.nix;
in
Expand All @@ -27,6 +28,7 @@ in
security
system
time
];
]
++ serviceModules;

}
11 changes: 11 additions & 0 deletions nix-darwin/services/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
lib,
isRunner,
pkgs,
}:
let
pmsetBatteryPolicyModule = import ./pmset-battery-policy { inherit lib isRunner pkgs; };
in
[
pmsetBatteryPolicyModule
]
28 changes: 28 additions & 0 deletions nix-darwin/services/pmset-battery-policy/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
lib,
isRunner,
pkgs,
}:
let
highBatteryThreshold = 30;
highBatterySleepMinutes = 300;
lowBatterySleepMinutes = 30;
pmsetBatteryPolicyScript = builtins.readFile (
pkgs.replaceVars ./power-policy.sh {
awkBin = "/usr/bin/awk";
inherit highBatteryThreshold highBatterySleepMinutes lowBatterySleepMinutes;
pmsetBin = "/usr/bin/pmset";
}
);
in
lib.mkIf (!isRunner) {
system.activationScripts.pmsetBatteryPolicy.text = lib.mkAfter pmsetBatteryPolicyScript;

launchd.daemons."com.shunkakinoki.pmset-battery-policy" = {
script = pmsetBatteryPolicyScript;
serviceConfig = {
RunAtLoad = true;
StartInterval = 86400;
};
};
}
26 changes: 26 additions & 0 deletions nix-darwin/services/pmset-battery-policy/power-policy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -eu

pmset_bin="@pmsetBin@"
awk_bin="@awkBin@"
high_battery_threshold=@highBatteryThreshold@
high_battery_sleep_minutes=@highBatterySleepMinutes@
low_battery_sleep_minutes=@lowBatterySleepMinutes@

# Idle sleep timers do not override the hardware lid-close sleep path.
# shellcheck disable=SC2016
battery_percentage_awk='NR == 2 { if (match($0, /[0-9]+%/)) print substr($0, RSTART, RLENGTH - 1); exit }'
batteryPercentage="$($pmset_bin -g batt | $awk_bin "$battery_percentage_awk")"
batterySleepMinutes=$low_battery_sleep_minutes
batteryPowerNap=0

$pmset_bin -c sleep 0
$pmset_bin -c powernap 1

if [ -n "$batteryPercentage" ] && [ "$batteryPercentage" -ge "$high_battery_threshold" ]; then
batterySleepMinutes=$high_battery_sleep_minutes
batteryPowerNap=1
fi

$pmset_bin -b sleep "$batterySleepMinutes"
$pmset_bin -b powernap "$batteryPowerNap"
5 changes: 5 additions & 0 deletions spec/coverage_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ End
It 'has spec file for home-manager/services/docker/docker-setup.sh'
The path "spec/docker_setup_wrapper_spec.sh" should be exist
End
It 'has spec file for nix-darwin/services/pmset-battery-policy/power-policy.sh'
The path "spec/pmset_battery_policy_spec.sh" should be exist
End

End

Describe 'fish shortcut declarations'
Expand Down Expand Up @@ -244,6 +248,7 @@ named-hosts/kyber/rekey-galactica.sh
named-hosts/kyber/setup.sh
named-hosts/matic/falcon-init.sh
named-hosts/matic/pam-gnome-keyring-tpm-unlock.sh
nix-darwin/services/pmset-battery-policy/power-policy.sh
scripts/check-nix-inline-scripts.sh
scripts/fishtape-wrapper.sh
scripts/llm-update.sh
Expand Down
71 changes: 71 additions & 0 deletions spec/pmset_battery_policy_spec.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# shellcheck disable=SC2329

Describe 'nix-darwin/services/pmset-battery-policy/power-policy.sh'
SCRIPT="$PWD/nix-darwin/services/pmset-battery-policy/power-policy.sh"

Describe 'script properties'
It 'uses bash shebang'
When run bash -c "head -1 '$SCRIPT'"
The output should include '#!/usr/bin/env bash'
End

It 'passes bash syntax check after replacing placeholders'
When run bash -c "sed -e 's|@pmsetBin@|/usr/bin/pmset|g' -e 's|@awkBin@|/usr/bin/awk|g' -e 's|@highBatteryThreshold@|30|g' -e 's|@highBatterySleepMinutes@|300|g' -e 's|@lowBatterySleepMinutes@|30|g' '$SCRIPT' | bash -n"
The status should be success
End
End

setup_policy_script() {
TEST_DIR=$(mktemp -d)
PMSET_LOG="$TEST_DIR/pmset.log"
PMSET_BIN="$TEST_DIR/pmset"
PREPROCESSED_SCRIPT="$TEST_DIR/power-policy.sh"
: >"$PMSET_LOG"

cat >"$PMSET_BIN" <<'EOF'
#!/usr/bin/env bash
set -eu
printf '%s\n' "$*" >>"$PMSET_LOG"
if [ "$1" = "-g" ] && [ "$2" = "batt" ]; then
printf "Now drawing from 'Battery Power'\n -InternalBattery-0 (id=1)\t%s%%; discharging; 4:00 remaining present: true\n" "$BATTERY_PERCENTAGE"
fi
EOF
chmod +x "$PMSET_BIN"

sed \
-e "s|@pmsetBin@|$PMSET_BIN|g" \
-e 's|@awkBin@|/usr/bin/awk|g' \
-e 's|@highBatteryThreshold@|30|g' \
-e 's|@highBatterySleepMinutes@|300|g' \
-e 's|@lowBatterySleepMinutes@|30|g' \
"$SCRIPT" >"$PREPROCESSED_SCRIPT"
chmod +x "$PREPROCESSED_SCRIPT"
}

cleanup_policy_script() {
rm -rf "$TEST_DIR"
}

Describe 'percentage-based policy'
Before 'setup_policy_script'
After 'cleanup_policy_script'

It 'extends battery sleep at or above the threshold'
When run bash -c ': >"'"$PMSET_LOG"'"; BATTERY_PERCENTAGE=35 PMSET_LOG="'"$PMSET_LOG"'" "'"$PREPROCESSED_SCRIPT"'" >/dev/null && cat "'"$PMSET_LOG"'"'
The status should be success
The output should include '-c sleep 0'
The output should include '-c powernap 1'
The output should include '-b sleep 300'
The output should include '-b powernap 1'
End

It 'keeps the conservative battery sleep below the threshold'
When run bash -c ': >"'"$PMSET_LOG"'"; BATTERY_PERCENTAGE=12 PMSET_LOG="'"$PMSET_LOG"'" "'"$PREPROCESSED_SCRIPT"'" >/dev/null && cat "'"$PMSET_LOG"'"'
The status should be success
The output should include '-b sleep 30'
The output should include '-b powernap 0'
End
End

End
Loading