Sweep across the three script directories: features/scripts/utils,
core/system/scripts, themes/engine/scripts. 142 of 169 bash scripts
gained `set -e`; 27 already had it; the one Python helper
(nomarchy-haptic-touchpad) was skipped via shebang detection.
Why: bash's default behavior is to continue past a failed command,
which means a script that does "do A; do B; do C" leaves the system
in a half-applied state when B fails - and the user gets no signal.
Several recent fix commits (theme partial-apply, waybar reload race,
installer prewipe silent failures) all trace back to this. set -e
turns silent corruption into a loud abort the user can act on.
The 11 scripts with explicit `|| true` markers stay safe under set -e
because || true coerces the exit to zero; the markers continue to
mean "I deliberately tolerate this failure here."
Deliberate exception: nomarchy-menu runs WITHOUT set -e. It is an
interactive UX loop where action branches do `cmd; back_to <self>`
so a failed action would abort the script under set -e and the menu
would disappear without feedback. Soft-failure - menu re-displays,
user picks again - is the right semantic. Documented inline.
Validation: bash -n on every modified script (zero failures). The
new pre-commit hook (27f5663) was just updated to filter by shebang
so it doesn't try to bash-syntax-check the Python helper - that
filter was uncovered by this sweep.
Risk: set -e can surface latent bugs in scripts that previously
relied on silent continuation. If anything breaks, it's a real bug
that was already broken and is now visible. Easy per-script revert
if any UX glitches show up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
254 lines
9.2 KiB
Bash
Executable File
254 lines
9.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -e
|
|
# Generator tolerates "no matches" exit codes from grep | sort.
|
|
# pipefail and -e off; -u stays.
|
|
set -u
|
|
|
|
# nomarchy-docs-scripts
|
|
#
|
|
# Regenerates docs/SCRIPTS.md from the repo state. Produces:
|
|
# 1. Header + status legend + regen instructions.
|
|
# 2. Table of every nomarchy-* script (location, callers, status).
|
|
# 3. Table of every menu entry in features/scripts/utils/nomarchy-menu
|
|
# (submenu, label, target command, status).
|
|
# 4. Snapshot list of orphaned references (called somewhere, no script).
|
|
#
|
|
# Status heuristic in Phase A:
|
|
# kept — file exists AND is called from at least one *.nix / *.conf /
|
|
# shell file outside its own directory.
|
|
# unused? — file exists but no caller found. Phase B decides delete-dead
|
|
# vs intentional public API.
|
|
# missing — referenced but no file. Phase B decides port-from-omarchy
|
|
# vs delete-dead vs stub-with-notify.
|
|
#
|
|
# nomarchy-docs-scripts # write to stdout
|
|
# nomarchy-docs-scripts --out docs/SCRIPTS.md
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
cd "$repo_root"
|
|
|
|
# --- Inventory -------------------------------------------------------------
|
|
|
|
# Where scripts live, in render order.
|
|
declare -A loc_label=(
|
|
["features/scripts/utils"]="features/scripts/utils"
|
|
["core/system/scripts"]="core/system/scripts"
|
|
["themes/engine/scripts"]="themes/engine/scripts"
|
|
)
|
|
script_dirs=(features/scripts/utils core/system/scripts themes/engine/scripts)
|
|
|
|
# Build name → location map (associative array of basename → repo-relative dir).
|
|
declare -A script_loc
|
|
for dir in "${script_dirs[@]}"; do
|
|
[[ -d "$dir" ]] || continue
|
|
while IFS= read -r f; do
|
|
script_loc["$(basename "$f")"]="$dir"
|
|
done < <(find "$dir" -maxdepth 1 -type f -name 'nomarchy-*')
|
|
done
|
|
|
|
# Find every nomarchy-* token referenced anywhere outside the script dirs.
|
|
# (We exclude the script files themselves so they don't list themselves as
|
|
# their own caller.)
|
|
# File types we search for references. *.md catches docs and README;
|
|
# branding/hook/extension files have varied or no extensions.
|
|
# *.lua catches elephant providers; *.ini catches mako on-button-* hooks;
|
|
# *.desktop catches MimeType-registered URL handlers.
|
|
grep_includes=(
|
|
--include='*.nix' --include='*.conf' --include='*.sh' --include='*.md'
|
|
--include='nomarchy-*' --include='*.jsonc' --include='*.json'
|
|
--include='*.toml' --include='*.ini' --include='*.lua'
|
|
--include='*.desktop' --include='*.txt' --include='*.sample'
|
|
)
|
|
search_dirs=(core features themes installer hosts bin lib README.md)
|
|
|
|
# Files whose mentions of nomarchy-* are documentation about the scripts,
|
|
# not real callers. Excluded from caller discovery so they don't promote
|
|
# every script to `kept`.
|
|
self_refs=(docs/SCRIPTS.md docs/ROADMAP.md docs/AGENT.md)
|
|
|
|
ref_files_per_cmd() {
|
|
local cmd="$1"
|
|
local self_pattern
|
|
self_pattern=$(IFS='|'; echo "${self_refs[*]}")
|
|
grep -rlE "\\b${cmd}\\b" \
|
|
"${grep_includes[@]}" \
|
|
"${search_dirs[@]}" 2>/dev/null \
|
|
| grep -vE "^(features/scripts/utils|core/system/scripts|themes/engine/scripts)/${cmd}$" \
|
|
| grep -vE "^(${self_pattern})$" \
|
|
| sort -u
|
|
}
|
|
|
|
# All distinct nomarchy-* tokens we see anywhere in the repo.
|
|
# Final char must be alphanumeric — dropping trailing-dash matches like
|
|
# `nomarchy-pkg-` that come from glob references (`for c in nomarchy-pkg-*`).
|
|
# Restrict to grep_includes so binaries / tmpfiles don't pollute the set.
|
|
all_refs=$(grep -rohE 'nomarchy-[a-z0-9]([a-z0-9-]*[a-z0-9])?' \
|
|
"${grep_includes[@]}" \
|
|
"${search_dirs[@]}" 2>/dev/null \
|
|
| sort -u)
|
|
|
|
# --- Render: header --------------------------------------------------------
|
|
|
|
main() {
|
|
cat <<'HEADER'
|
|
# Nomarchy Script & Menu Audit
|
|
|
|
Auto-generated table for [Pillar 3 of the roadmap](ROADMAP.md#3-pillar-script--menu-audit).
|
|
**Do not edit by hand.** Regenerate after script or menu changes:
|
|
|
|
```bash
|
|
./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md
|
|
```
|
|
|
|
The status column uses a Phase A heuristic — `kept` / `unused?` / `missing`.
|
|
Phase B (per-batch PRs) refines those into `port-from-omarchy`,
|
|
`delete-dead`, or `stub-with-notify` and updates the rows.
|
|
|
|
## Status legend
|
|
|
|
- `kept` — script exists and is called from somewhere outside its own directory.
|
|
- `unused?` — script exists but no caller was found. Could be dead, could be
|
|
intentional public API. Phase B triage decides.
|
|
- `missing` — referenced from code but no script file exists. Phase B triage
|
|
decides whether to port from Omarchy upstream, delete the caller, or stub
|
|
with `notify-send`.
|
|
- `port-from-omarchy` — Phase B verdict: lift the upstream Omarchy script,
|
|
rewrite for NixOS paths.
|
|
- `delete-dead` — Phase B verdict: remove and update callers.
|
|
- `stub-with-notify` — Phase B verdict: temporary `notify-send` stub.
|
|
|
|
HEADER
|
|
|
|
# --- Render: scripts table ----------------------------------------------
|
|
printf '## Scripts (%d)\n\n' "${#script_loc[@]}"
|
|
printf '| Script | Location | Callers | Status | Notes |\n'
|
|
printf '| --- | --- | --- | --- | --- |\n'
|
|
|
|
# Sort scripts by name.
|
|
for name in $(printf '%s\n' "${!script_loc[@]}" | sort); do
|
|
local dir="${script_loc[$name]}"
|
|
local callers status callers_str
|
|
callers=$(ref_files_per_cmd "$name")
|
|
if [[ -z "$callers" ]]; then
|
|
status='`unused?`'
|
|
callers_str='—'
|
|
else
|
|
status='`kept`'
|
|
# Trim caller list to 2 entries + count.
|
|
local count
|
|
count=$(printf '%s\n' "$callers" | wc -l)
|
|
if (( count > 2 )); then
|
|
callers_str=$(printf '%s\n' "$callers" | head -2 | paste -sd, -)
|
|
callers_str="$callers_str, +$((count - 2)) more"
|
|
else
|
|
callers_str=$(printf '%s\n' "$callers" | paste -sd, -)
|
|
fi
|
|
fi
|
|
printf '| `%s` | `%s` | %s | %s | |\n' \
|
|
"$name" "$dir" "$callers_str" "$status"
|
|
done
|
|
echo
|
|
|
|
# --- Render: missing references -----------------------------------------
|
|
printf '## Missing references\n\n'
|
|
printf 'Tokens grepped from `core/`, `features/`, `themes/`, `installer/`, `hosts/`, `bin/`, `lib/` that have no matching script file.\n\n'
|
|
printf '| Token | Referenced in | Status |\n'
|
|
printf '| --- | --- | --- |\n'
|
|
while IFS= read -r token; do
|
|
[[ -z "$token" ]] && continue
|
|
[[ -n "${script_loc[$token]:-}" ]] && continue
|
|
local refs
|
|
self_pattern=$(IFS='|'; echo "${self_refs[*]}")
|
|
refs=$(grep -rlE "\\b${token}\\b" \
|
|
"${grep_includes[@]}" \
|
|
"${search_dirs[@]}" 2>/dev/null \
|
|
| grep -vE "^(${self_pattern})$" \
|
|
| sort -u)
|
|
[[ -z "$refs" ]] && continue
|
|
local count refs_str
|
|
count=$(printf '%s\n' "$refs" | wc -l)
|
|
if (( count > 2 )); then
|
|
refs_str=$(printf '%s\n' "$refs" | head -2 | paste -sd, -)
|
|
refs_str="$refs_str, +$((count - 2)) more"
|
|
else
|
|
refs_str=$(printf '%s\n' "$refs" | paste -sd, -)
|
|
fi
|
|
printf '| `%s` | %s | `missing` |\n' "$token" "$refs_str"
|
|
done <<<"$all_refs"
|
|
echo
|
|
|
|
# --- Render: menu items -------------------------------------------------
|
|
printf '## Menu items\n\n'
|
|
printf 'Walked from `features/scripts/utils/nomarchy-menu`. Each `case` arm in a `show_*_menu` function becomes one row.\n\n'
|
|
printf '| Submenu | Entry | Calls | Status |\n'
|
|
printf '| --- | --- | --- | --- |\n'
|
|
|
|
awk '
|
|
/^show_[a-z_]+_menu\(\) {/ { sub(/\(\) {/, ""); current=$1; in_func=1; next }
|
|
/^[a-z_]+\(\) {/ && !/^show_/ { current=""; in_func=0; next }
|
|
/^}$/ { current=""; in_func=0; next }
|
|
!in_func { next }
|
|
/^ case \$\(menu / {
|
|
# extract the menu title between the first pair of double quotes
|
|
match($0, /menu "[^"]+" "[^"]+"/);
|
|
if (RSTART == 0) next;
|
|
title=substr($0, RSTART, RLENGTH);
|
|
# second quoted string is the option list
|
|
n=split(title, parts, "\"");
|
|
title=parts[2];
|
|
options=parts[4];
|
|
# split options on \n
|
|
split(options, opts, "\\\\n");
|
|
pending_submenu=current;
|
|
pending_title=title;
|
|
for (i=1;i<=length(opts);i++) pending_opts[i]=opts[i];
|
|
pending_count=length(opts);
|
|
next
|
|
}
|
|
/^ \*[A-Za-z]/ {
|
|
# case arm — extract pattern between the first * and the closing )
|
|
match($0, /\*[^)]*\)/);
|
|
if (RSTART == 0) next;
|
|
arm=substr($0, RSTART, RLENGTH);
|
|
gsub(/[*)]/, "", arm);
|
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", arm);
|
|
# action follows the )
|
|
rest=substr($0, RSTART+RLENGTH);
|
|
sub(/^[[:space:]]+/, "", rest);
|
|
sub(/[[:space:]]*;;[[:space:]]*$/, "", rest);
|
|
# match the first nomarchy-* token in the action
|
|
cmd=""
|
|
if (match(rest, /nomarchy-[a-z0-9-]+/)) {
|
|
cmd=substr(rest, RSTART, RLENGTH);
|
|
}
|
|
printf "%s|%s|%s\n", pending_submenu, arm, cmd;
|
|
}
|
|
' features/scripts/utils/nomarchy-menu > /tmp/nomarchy-menu-rows.$$
|
|
|
|
while IFS='|' read -r submenu entry cmd; do
|
|
[[ -z "$entry" ]] && continue
|
|
[[ "$entry" =~ ^\) ]] && continue
|
|
status='`kept`'
|
|
if [[ -n "$cmd" ]]; then
|
|
if [[ -z "${script_loc[$cmd]:-}" ]]; then
|
|
status='`missing`'
|
|
fi
|
|
else
|
|
cmd='_(inline)_'
|
|
fi
|
|
printf '| `%s` | %s | `%s` | %s |\n' "$submenu" "$entry" "$cmd" "$status"
|
|
done < /tmp/nomarchy-menu-rows.$$
|
|
rm -f /tmp/nomarchy-menu-rows.$$
|
|
echo
|
|
}
|
|
|
|
out=""
|
|
if [[ "${1:-}" == "--out" ]]; then
|
|
out="${2:?--out needs a path}"; shift 2
|
|
fi
|
|
if [[ -n "$out" ]]; then
|
|
main >"$out"
|
|
else
|
|
main
|
|
fi
|