299 -> 103, with the --with-server half in lib/joplin_server.sh. First
of the 84 linux_configuration files, done slowly to settle the pattern.
Three things bash -n and shellcheck do not catch, all found here:
- SCRIPT_DIR from "$0" breaks when the entry script is itself sourced.
Use "${BASH_SOURCE[0]}"; keep readlink -f for the one symlinked script.
- A lib needs a shebang AND +x: without the shebang shellcheck raises
SC2148, with it the shebang-executable hook fails. Match common.sh.
- Only a stubbed run proves the source line resolves. Copy entry+lib to
a mktemp -d, sed `main` into a declare -F probe, run that. No
installer step executes.
Also enumerates which scripts escape the repo: three copied out by
install_pacman_hooks.sh, one symlink, everything else generated. All
recorded in docs/shell-split-recipes.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
4.1 KiB
Shell split recipes for the 250-line cap
Split out of refactor_claude_todo_resume.md (which is itself capped).
Applies to linux_configuration/ and phone_focus_mode/.
linux_configuration and phone_focus_mode have no test suite and no
coverage gate, so none of the above transfers. Decision 6 governs:
bash -n + shellcheck + systemctl cat path checks, and never execute an
enforcement script that mutates the live system or the phone.
- After each split,
bash -nandshellcheckthe entry script and every new lib. Neither catches asourceline that resolves to the wrong path — both passed clean on a split whose lib was unreachable at runtime. - So the real check is a stubbed run: copy the entry script and its libs to
a
mktemp -d,sedthe finalmaininvocation into adeclare -Fprobe, and run it. That executes thesourcelines and nothing else. This caught the bug below; no static check did.
Use ${BASH_SOURCE[0]}, not $0
The repo's existing convention is
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")", which works only while the
script is executed directly. $0 is the sourcing script, so the moment an
entry script is itself sourced, SCRIPT_DIR points somewhere else and the lib
is not found. Write new ones as:
SCRIPT_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
readlink -f is still required — it is what makes the one symlinked script
(/usr/local/bin/start-player2) resolve back into the repo.
Shape of a new lib (two hooks disagree unless you get this right)
Follow scripts/lib/common.sh: shebang present, file executable. The two
obvious-looking alternatives each fail a hook:
- No shebang →
shellcheckSC2148 ("target shell unknown"). - Shebang but not executable →
check-shebang-scripts-are-executable.
So: #!/bin/bash, chmod +x, and no set -euo pipefail — a sourced lib
inherits the caller's strict mode, and redeclaring it is duplicated
boilerplate that feeds jscpd. Say so in the lib's header comment.
-
Two live units point at repo paths and break if a file moves or is renamed.
systemctl catthem after touching either:/etc/systemd/system/dns-blocklist-refresh.service→scripts/single_use/features/setup_dns_blocker.sh refresh/etc/systemd/system/media-organizer.service→scripts/single_use/utils/organize_downloads.sh
Every other in-repo unit references an installed copy under
/usr/local/, so those are only at risk if the installer is what you split.
Which scripts escape the repo (enumerated 2026-08-16)
A split is only safe if the file still resolves its libs from wherever it actually runs. Two ways a script leaves the repo, both silent when broken:
- Copied out — only three, all under
scripts/periodic_background/hosts/guard/pacman-hooks/, installed to/usr/local/share/hosts-guard/byhosts/guard/install_pacman_hooks.sh. Splitting any of those three means the installer must copy the new libs too, in the same commit — the same trap asphone_focus_mode/deploy.sh. - Symlinked in — exactly one:
/usr/local/bin/start-player2→scripts/gaming/start-player2.sh.${BASH_SOURCE[0]%/*}resolves to/usr/local/binthere, so a lib path must go throughreadlink -f "${BASH_SOURCE[0]}"first.
Everything else under /usr/local/ is generated by an installer (heredoc
or cat >), not copied, so splitting the generator is safe as long as the
generated content is unchanged. Re-run both checks if that assumption is ever
in doubt:
find /usr/local/bin /usr/local/sbin -maxdepth 1 \( -type l -o -name '*.sh' \) \
-exec readlink -f {} \; | grep testsAndMisc
grep -rn -E '^\s*(sudo )?(install|cp)\s.*(SCRIPT_DIR|REPO).*/usr/local/' \
linux_configuration --include='*.sh'
- Prefer files that nothing references first — pure lint risk.
- Watch jscpd on the first multi-lib commit: repeated
set -euo pipefail+ source-guard headers across several new libs is exactly what pushes duplication toward the 2% threshold, and it only fires on the realgit commit.