diff options
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/cmail-setup-finish.sh | 56 | ||||
| -rwxr-xr-x | scripts/zz-bluetooth-resume | 86 |
2 files changed, 119 insertions, 23 deletions
diff --git a/scripts/cmail-setup-finish.sh b/scripts/cmail-setup-finish.sh index 949023f..8c27eda 100755 --- a/scripts/cmail-setup-finish.sh +++ b/scripts/cmail-setup-finish.sh @@ -1,32 +1,37 @@ #!/usr/bin/env bash # SPDX-License-Identifier: GPL-3.0-or-later -# cmail-setup-finish.sh — finish Proton Mail Bridge + cmail-action setup after -# Bridge first-run. Idempotent; safe to re-run after a Bridge cert rotation or -# a claude-templates re-clone. +# cmail-setup-finish.sh — finish Proton Mail Bridge setup after Bridge +# first-run. Idempotent; safe to re-run after a Bridge cert rotation. # # Pre-reqs (the script aborts if any are missing): # - protonmail-bridge installed (archsetup handles it) # - You have run 'protonmail-bridge --cli', logged in, and quit at least once # (the script looks for state at ~/.config/protonmail/bridge-v3/) -# - claude-templates cloned at ~/projects/claude-templates # - dotfiles stowed (~/.config/.cmailpass.gpg present) # +# Not a pre-req, but checked and warned about: cmail-action on PATH. rulesets' +# `make install` links it, and session start runs that, so on a machine that +# runs agent sessions it arrives without anyone asking. On one that doesn't, +# it needs the command by hand. The script never invokes it either way. +# # What it does: # 1. Decrypts ~/.config/.cmailpass.gpg → ~/.config/.cmailpass (mode 0600) # 2. Copies Bridge's self-signed cert → ~/.config/protonbridge.pem -# 3. Symlinks ~/projects/claude-templates/.ai/scripts/cmail-action.py -# → ~/.local/bin/cmail-action -# 4. Removes the leftover ~/.config/autostart/Proton Mail Bridge.desktop +# 3. Removes the leftover ~/.config/autostart/Proton Mail Bridge.desktop # stub (it double-launches Bridge alongside the systemd user service # and throws an "orphan instance" dialog every login) -# 5. Installs a wait-for-dns drop-in so Bridge doesn't spam +# 4. Installs a wait-for-dns drop-in so Bridge doesn't spam # name-resolution errors during the early-boot DNS race -# 6. Enables + starts the protonmail-bridge user service -# 7. Verifies Bridge is listening on 127.0.0.1:1143 / :1025 +# 5. Enables + starts the protonmail-bridge user service +# 6. Verifies Bridge is listening on 127.0.0.1:1143 / :1025 +# +# It no longer installs cmail-action. That moved to rulesets +# (claude-templates/bin/), whose `make install` owns the symlink. set -euo pipefail err() { printf 'error: %s\n' "$*" >&2; exit 1; } +warn() { printf 'warning: %s\n' "$*" >&2; } info() { printf '==> %s\n' "$*"; } ok() { printf ' %s\n' "$*"; } @@ -47,9 +52,20 @@ bridge_state="$HOME/.config/protonmail/bridge-v3" [ -d "$bridge_state" ] \ || err "Bridge has no state at $bridge_state — run 'protonmail-bridge --cli' and log in first" -cmail_action_src="$HOME/projects/claude-templates/.ai/scripts/cmail-action.py" -[ -f "$cmail_action_src" ] \ - || err "cmail-action.py not found at $cmail_action_src — clone claude-templates first" +# cmail-action is no longer this script's to install. It lives in rulesets at +# claude-templates/bin/, and rulesets' `make install` links everything there +# into ~/.local/bin. Session start runs that, so on a machine that runs agent +# sessions the symlink arrives on its own; on one that doesn't, it needs the +# command below. +# +# A warning rather than an abort, because this script never invokes the tool. +# Its job is to leave Bridge working, and it can finish that whether or not a +# mail client has been linked yet. Aborting here would make Bridge setup +# depend on rulesets being cloned and installed first, an ordering neither +# repo otherwise needs, and would strand a fresh machine with Bridge ready and +# the script refusing to configure it. +command -v cmail-action >/dev/null 2>&1 \ + || warn "cmail-action not on PATH — run 'make -C ~/code/rulesets install' before sending mail" cmailpass_enc="$HOME/.config/.cmailpass.gpg" [ -f "$cmailpass_enc" ] \ @@ -69,13 +85,7 @@ cert_dst="$HOME/.config/protonbridge.pem" cp "$cert_src" "$cert_dst" ok "copied $cert_src → $cert_dst" -# 4. Symlink cmail-action -info "symlinking cmail-action" -mkdir -p "$HOME/.local/bin" -ln -sf "$cmail_action_src" "$HOME/.local/bin/cmail-action" -ok "linked $HOME/.local/bin/cmail-action → $cmail_action_src" - -# 5. Remove leftover XDG autostart stub +# 4. Remove leftover XDG autostart stub # The systemd --user service is the canonical launcher. The autostart .desktop # starts a second Bridge instance that can't get the lock and pops up an # "orphan instance" dialog every login. @@ -88,7 +98,7 @@ else ok "no autostart stub present" fi -# 6. Install wait-for-dns drop-in +# 5. Install wait-for-dns drop-in # User-instance systemd doesn't carry network-online.target / nss-lookup.target, # so the packaged unit's After=network.target doesn't imply DNS readiness. # Bridge starts before the resolver is up and its first API calls all fail @@ -107,7 +117,7 @@ ok "wrote $dropin_file" systemctl --user daemon-reload ok "reloaded systemd user units" -# 7. Enable + start systemd user service +# 6. Enable + start systemd user service info "enabling protonmail-bridge user service" was_active=0 systemctl --user is-active --quiet protonmail-bridge.service && was_active=1 @@ -119,7 +129,7 @@ else ok "service active" fi -# 8. Verify +# 7. Verify info "verifying Bridge is listening" listening="$(ss -ltn 2>/dev/null || true)" missing="" diff --git a/scripts/zz-bluetooth-resume b/scripts/zz-bluetooth-resume new file mode 100755 index 0000000..4273339 --- /dev/null +++ b/scripts/zz-bluetooth-resume @@ -0,0 +1,86 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-3.0-or-later +# zz-bluetooth-resume - put bluetooth back after a sleep cycle. +# +# A systemd-sleep hook. Two things break bluetooth across sleep on a TLP +# laptop, and nothing else on the machine fixes either one. +# +# 1. The rfkill soft-block is not restored. systemd-rfkill would do it, and +# it is masked here deliberately -- it fights TLP's radio handling, so +# configure_tlp_power masks it and TLP owns radios instead. TLP's own +# sleep hook runs `tlp resume`, but its setting is +# DEVICES_TO_ENABLE_ON_STARTUP: startup, not resume. TLP has no ON_RESUME +# at all, so the resume edge has no owner. WiFi survives only because +# NetworkManager unblocks itself; bluetooth has no equivalent. +# +# 2. The controller comes back wedged from a hibernate. It reports powered +# and unblocked while scanning finds nothing whatever -- zero devices +# where the same room gave seventeen a minute later -- and bluetoothd +# logs "Failed to set mode" and "Failed to add device <mac>" at the +# instant of resume. Reloading btusb clears it. +# +# Both observed on velox 2026-08-21, on the first suspend-then-hibernate cycle +# after hibernate was switched back on. The second symptom is why unblocking +# alone is not enough: rfkill was cleared by hand and scanning still returned +# nothing until the driver was reloaded. +# +# The hook re-asserts TLP's own declared intent rather than inventing a policy. +# A machine whose TLP config does not ask for bluetooth keeps it off, which is +# what stops this from overriding a deliberate block at every wakeup. +# +# The zz- prefix orders it after TLP's own hook, so `tlp resume` has finished +# before this runs. +# +# Test seams: BTR_RFKILL, BTR_MODPROBE, BTR_TLP_CONF, BTR_TLP_CONF_DIR, +# BTR_SETTLE (seconds to wait between driver unload and load). + +set -u + +RFKILL="${BTR_RFKILL:-rfkill}" +MODPROBE="${BTR_MODPROBE:-modprobe}" +TLP_CONF="${BTR_TLP_CONF:-/etc/tlp.conf}" +TLP_CONF_DIR="${BTR_TLP_CONF_DIR:-/etc/tlp.d}" +SETTLE="${BTR_SETTLE:-1}" + +# post only. The pre phase has nothing to do, and acting there would fight the +# suspend it is about to run. +[ "${1:-}" = "post" ] || exit 0 + +# Does TLP ask for bluetooth on this machine? Comments are stripped first, so a +# commented-out example in the stock config cannot be read as a policy. Both +# the main file and any drop-in count, and the last assignment wins the same +# way TLP itself resolves them. +wants_bluetooth() { + cat "$TLP_CONF" "$TLP_CONF_DIR"/*.conf 2>/dev/null \ + | sed 's/#.*//' \ + | awk -F= '/DEVICES_TO_ENABLE_ON_STARTUP/ { v = $2 } END { print v }' \ + | tr -d '"' \ + | tr ' ' '\n' \ + | grep -qx "bluetooth" +} + +wants_bluetooth || exit 0 + +# The wedge follows a hibernate, which reinitialises the controller from a +# saved image. A plain suspend brings USB back intact, so reloading there would +# tear down a working adapter for nothing. +# +# suspend-then-hibernate reports that name whether or not it reached the +# hibernate stage, so this reloads on a cycle that only suspended. That is the +# cheap side of the trade: a couple of seconds against an adapter that answers +# nothing until someone notices and reloads it by hand. +case "${2:-}" in + hibernate|suspend-then-hibernate) + "$MODPROBE" -r btusb 2>/dev/null || true + [ "$SETTLE" = "0" ] || sleep "$SETTLE" + "$MODPROBE" btusb 2>/dev/null || true + ;; +esac + +# After the reload, not before: a freshly loaded btusb can come up soft-blocked +# and would undo an earlier unblock. +"$RFKILL" unblock bluetooth 2>/dev/null || true + +# Never fail. systemd-sleep logs a failing hook, and that noise outlives the +# cause it describes; nothing here is worth alarming a resume over. +exit 0 |
