SuperAPI AI Directory

installer-quality-audit

Testing & quality

View repository

Updated 10-15 17:29

About this skill

Installer Workmanship

Use this skill to write or repair an install script so a stranger can run it on their machine, run it again, and undo it — without losing config, getting a surprise password prompt, or being forced to reboot. An installer is a guest in someone's home directory; it behaves accordingly.

⚠️ Critical Constraints

  • Idempotent: re-running changes nothing the second time. Every action checks state before acting. Why: users re-run installers to repair or upgrade; a non-idempotent script duplicates lines, restacks PATH, or fails.
    • WRONG: echo 'export PATH=$PATH:/opt/tool/bin' >> ~/.zshrc
    • CORRECT: grep -qF '/opt/tool/bin' ~/.zshrc || echo '...' >> ~/.zshrc
  • Additive: never clobber the user's existing config. Append, merge, or write a new file — never overwrite one you did not create. Why: a single cp config ~/.config/app/config can erase hours of someone's customization.
    • WRONG: cp ./gitconfig ~/.gitconfig
    • CORRECT: back up first (cp ~/.gitconfig ~/.gitconfig.bak-$(date +%s)), then merge or write only the keys you own.
  • No surprise sudo or reboots. Detect when privilege is needed, explain why, and ask — never wrap the whole script in sudo. Why: a script that silently escalates or reboots is indistinguishable from malware to a wary user.
  • Preflight before any mutation. Check OS, shell, required tools, disk, network, and write permissions up front; fail with a clear message before touching anything. Why: a half-applied install is worse than a refused one.
  • Fail loud, leave clean. Use set -euo pipefail; on error the host is in a known state (untouched or rolled back), never half-configured.

Why This Exists

Most install scripts are written for the author's machine on the happy path: they assume bash, assume sudo, assume nothing already exists, and assume one OS. The first time a real user re-runs one, or runs it on a slightly different system, it duplicates config, clobbers a dotfile, hangs on a password prompt, or dies halfway and leaves a mess with no way back. This skill is the forcing function for the trustworthy installer: safe to re-run, safe on a config that already exists, honest about privilege, and reversible.

Quick Start

  1. Locate or create the installer (install.sh, setup.sh, bootstrap.sh).
  2. Inventory what it touches: files written, PATH/RC edits, packages, services.
  3. For each action, confirm it is guarded (state-checked) and additive.
  4. Add a preflight block at the top and an uninstall counterpart.
  5. Run the four-way check: fresh run, re-run, partial-failure, uninstall.
  6. Run bash scripts/validate.sh and report the verdict.

The Five Properties of a Trustworthy Installer

1. Idempotent (safe to re-run)

Every mutating step is preceded by a check. The pattern is test, then act:

command -v tool >/dev/null 2>&1 || install_tool          # tool present?
line='export PATH="$PATH:/opt/tool/bin"'
grep -qF "$line" "$RC" || printf '%s\n' "$line" >> "$RC"  # line in rc file?
[ -d "$DEST" ] || mkdir -p "$DEST"                        # dir exists?
[ "$(readlink "$LINK" 2>/dev/null)" = "$TARGET" ] || ln -sfn "$TARGET" "$LINK"

A second run prints "already installed / up to date" and exits 0.

2. Additive (no clobbering)

You own only the files and config keys you create. For anything pre-existing, back it up before touching it, and prefer merge over overwrite.

backup() { [ -e "$1" ] && cp -a "$1" "$1.bak-$(date +%Y%m%d-%H%M%S)"; }
backup "$HOME/.gitconfig"

Use idempotent markers so a managed block can be updated in place without eating user edits around it:

# >>> tool managed block >>>
...your lines...
# <<< tool managed block <<<

Re-runs replace only the text between the markers.

3. Cross-platform

Detect, do not assume. Branch on OS and package manager; pick the user's real shell rc, not a hardcoded one. Fail closed for unknown platforms.

case "$(uname -s)" in
  Darwin) PKG="brew install" ;;
  Linux)  command -v apt >/dev/null && PKG="sudo apt-get install -y" \
            || { command -v dnf >/dev/null && PKG="sudo dnf install -y"; } ;;
  *) echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;;
esac

Avoid bashisms if the shebang is #!/bin/sh; quote every path (spaces, ~).

4. No surprise privilege or reboots

Run as the invoking user by default. When a step genuinely needs root, isolate it, explain it, and let the user decline:

need_sudo() {
  command -v sudo >/dev/null || { echo "This step needs root; re-run as root." >&2; return 1; }
  echo "About to install system packages (requires sudo): $*"
  sudo "$@"
}

Never call reboot; if a change needs a restart or a new shell, print the instruction and let the user choose when.

5. Recovery and uninstall

Ship the undo with the install. A clear uninstall.sh (or install.sh --uninstall) removes what you added — and only what you added — restoring backups where they exist. Print where backups went so recovery is obvious. Use atomic writes for generated files (write to a temp path, validate, then mv into place) so an interrupted run never leaves a half-written file.

Preflight Checks

Run before any mutation; fail fast with a precise message:

  • OS / arch supported (uname -s, uname -m).
  • Required tools present (command -v git curl ...), or installable.
  • Write permission to every target dir ([ -w "$DEST" ]).
  • Disk space / network if the install needs a download or sizeable write.
  • Existing install detected → switch to upgrade/repair, do not duplicate.

Audit Flow (for an existing installer)

  1. List every side effect: file write, RC edit, package, service, symlink, download, permission change.
  2. For each side effect, ask: is it guarded (idempotent)? Is it additive (backs up / merges)? Rewrite any that are not.
  3. Confirm a preflight block exists and covers OS, tools, and permissions.
  4. Model at least three runs: fresh install, repeat install, and partial install after an early failure. Each must leave a sane state.
  5. Check privilege: is sudo scoped to the steps that need it, with an explanation? Is there any silent reboot?
  6. Confirm an uninstall/recovery path exists and reverses exactly the install; confirm verification proves the thing works, not just that files exist.

Output Specification

Return a workmanship report (also applied to the installer when repairing):

  • Verdict: pass, needs work, or blocked.
  • Findings: ordered by severity, each tied to one of the five properties.
  • Changes: what was edited and why.
  • Verification: the commands run (fresh / re-run / uninstall) and results.
  • Residual risk: untested platforms, unbacked-up files, irreversible steps.

Quality Rubric

  • Running the installer twice in a row leaves the host byte-identical after the first run and exits 0 both times (idempotency proven, not asserted).
  • No pre-existing user file is overwritten without a timestamped backup, and no sudo or reboot runs without an explicit, declinable prompt.
  • An uninstall path exists, removes only what the installer added, and a preflight block fails clearly before any mutation on an unmet requirement.

Examples

Turning a clobbering, non-idempotent fragment into a workmanlike one:

  • Input:
    echo 'alias g=git' >> ~/.bashrc
    cp ./config.toml ~/.config/app/config.toml
    
  • Output:
    grep -qxF 'alias g=git' "$RC" || echo 'alias g=git' >> "$RC"
    dest="$HOME/.config/app/config.toml"
    [ -e "$dest" ] && cp -a "$dest" "$dest.bak-$(date +%s)"
    mkdir -p "$(dirname "$dest")"; install -m 0644 ./config.toml "$dest"
    

Troubleshooting

Symptom Likely cause Fix
Re-running duplicates PATH/alias lines Unguarded >> appends Guard with grep -qF ... || echo ... or a managed marker block.
User lost their dotfile customizations cp/overwrite of an existing file Back up before writing; merge or write only owned keys.
Script hangs on a password prompt Whole script wrapped in sudo Scope sudo to the steps that need it; explain and let the user decline.
Works on the author's Mac, fails on Linux Hardcoded paths / package manager Detect OS + package manager; pick the real shell rc; quote paths.
Half-installed mess after an error No set -e / no rollback Add set -euo pipefail; trap errors; restore backups or leave untouched.
No way to undo the install No uninstall path shipped Add uninstall.sh that reverses exactly what install added.

See Also

  • scripts/validate.sh — structural self-check for this skill.
  • changelog-md-workmanship — sibling workmanship skill for release history.