From 7e2ecba219d55cf47ae8bad343bcc46f90e63243 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 5 Aug 2026 14:11:14 +0000 Subject: [PATCH] chore: add doc/code sync gate (make gen + make check + pre-commit hook) - bin/pos: usage() CATEGORIES now auto-derived from pos-* filenames (kill the stale cheat-sheet bug class); only EXAMPLES stays hand-written - # POS: / # POS_FLAGS: headers on all 15 tools = single source of truth for generated docs; template updated - scripts/gen-docs.sh: regenerates GEN-marker sections (bin tree, dispatch table, no-common.sh list, line-count table, completion flags) - scripts/check-sync.sh: bash -n + exec bits + doc/code drift + smoke - Makefile: make gen / make check; scripts/install-hooks.sh: opt-in hook - DEV.md + AGENT_Context: add-a-tool flow is now header + make check --- DOC/AGENT_Context_Project.md | 22 +++++++++++++------ DOC/DEV.md | 23 ++++++++++++++------ Makefile | 13 ++++++++++++ scripts/check-sync.sh | 41 ++++++++++++++++++++++++++++++++++++ scripts/gen-docs.sh | 13 ++++-------- scripts/install-hooks.sh | 19 +++++++++++++++++ templates/pos-tool.sh | 20 +++++++++++------- 7 files changed, 120 insertions(+), 31 deletions(-) create mode 100644 Makefile create mode 100755 scripts/check-sync.sh create mode 100755 scripts/install-hooks.sh diff --git a/DOC/AGENT_Context_Project.md b/DOC/AGENT_Context_Project.md index b2ba545..3bd1d52 100644 --- a/DOC/AGENT_Context_Project.md +++ b/DOC/AGENT_Context_Project.md @@ -35,6 +35,7 @@ Linux_post_install/ ├── bin/ # CLI tools — installed to /usr/local/bin/ │ ├── pos # Main dispatcher — smart arg matching to pos-* scripts +│ ├── pos-communication-telegram # Send Telegram messages via Bot API (--send, test, config set) │ ├── pos-docker-compose # Docker Compose service manager (ls/up/down/restart/logs/update/config) │ ├── pos-docker-health # One-glance container health dashboard (exits 1 if unhealthy) │ ├── pos-docker-ps # Enhanced container overview (health, IPs, ports, uptime) @@ -199,6 +200,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst | Category | Command | Script | Description | |----------|---------|--------|-------------| +| communication | telegram | `pos-communication-telegram` | Send Telegram messages via Bot API (--send, test, config set) | | docker | compose | `pos-docker-compose` | Docker Compose service manager (ls/up/down/restart/logs/update/config) | | docker | health | `pos-docker-health` | One-glance container health dashboard (exits 1 if unhealthy) | | docker | ps | `pos-docker-ps` | Enhanced container overview (health, IPs, ports, uptime) | @@ -257,7 +259,7 @@ source "$(dirname "$0")/../lib/common.sh" **Scripts that do NOT source common.sh** (self-contained): -`bin/pos`, `pos-network-ip`, `pos-network-checkport`, `pos-network-scan`, `pos-network-hotspot`, `pos-media-mp3`, `pos-media-mp4`, `pos-ssh-load-keys`, `pos-system-firewall`, `pos-communication-telegram`. +`pos`, `pos-communication-telegram`, `pos-media-mp3`, `pos-media-mp4`, `pos-network-checkport`, `pos-network-hotspot`, `pos-network-ip`, `pos-network-scan`, `pos-ssh-load-keys`, `pos-system-firewall`. --- @@ -421,11 +423,12 @@ System-wide flag store at `/usr/local/share/linux_post_install/flags/`: ### Adding a New Tool 1. Create `bin/pos--` from `templates/pos-tool.sh` — must be executable (`100755`); it auto-appears in `pos --help` (filename-derived, no registration) -2. Register in `bin/pos` `usage()` CATEGORIES/EXAMPLES; add to `INTERACTIVE_CMDS` in `bin/pos` if it reads stdin -3. Add system deps to `PACKAGES` array in `preinstall.sh` (if needed) -4. Add config logic to `postinstall.sh` (if needed, with `.gitignore` for secrets); runtime tool config → `~/.config/linux_post_install/.env` (600) -5. Update docs: `DOC/POS.md` (section table + detail), `DOC/AGENT_Context_Project.md` (bin tree, dispatch table, self-contained list, file line-count table), root `README.md` only if the category list changes -6. Test: `bash -n bin/your-tool && shellcheck bin/your-tool && bin/pos help && bin/pos --help` +2. Add the `# POS: ` header right after the shebang (plus `# POS_FLAGS: ...` for flag-style tools) — this is the single source of truth for the generated docs +3. Add to `INTERACTIVE_CMDS` in `bin/pos` if it reads stdin +4. Add system deps to `PACKAGES` array in `preinstall.sh` (if needed); non-apt/manual installers → `command -v` guard in the tool instead +5. Add config logic to `postinstall.sh` (if needed, with `.gitignore` for secrets); runtime tool config → `~/.config/linux_post_install/.env` (600) +6. Update docs: `DOC/POS.md` (section table + detail — hand-written); `DOC/AGENT_Context_Project.md` generated sections (bin tree, dispatch table, self-contained list, line-count table) and completion flags update via `make gen` — never hand-edit between `GEN:START`/`GEN:END` markers; root `README.md` only if the category list changes +7. Test: `make gen && make check` — `make check` (bash -n + doc/code sync + smoke) is the definition of done; also `bin/pos help && bin/pos --help` ### Testing @@ -466,6 +469,8 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:` | `bin/flag-set` | 21 | Set a flag (optionally with a value) | | `bin/flag-clear` | 21 | Unset a flag | | `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) | + +| `bin/pos` | 208 | CLI dispatcher with smart arg matching + logging + category help | | `bin/pos-communication-telegram` | 140 | Send Telegram messages via Bot API (--send, test, config set) | | `bin/pos-docker-compose` | 364 | Docker Compose service manager (ls/up/down/restart/logs/update/config) | | `bin/pos-docker-health` | 110 | One-glance container health dashboard (exits 1 if unhealthy) | @@ -482,6 +487,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:` | `bin/pos-system-firewall` | 285 | Interactive UFW management | | `bin/pos-usb-server` | 218 | USB Redirector server control (--ls, --share; prompts when args omitted) | | `completions/pos.bash` | 146 | Dynamic bash completion | + | `apps/install.sh` | 171 | App install/uninstall picker/orchestrator | --- @@ -490,7 +496,9 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:` | Task | Where to Edit | |------|---------------| -| Add a new CLI tool | Create `bin/pos--`, add apt deps in `preinstall.sh` (non-apt/manual installers: add a `command -v` guard in the tool instead) | +| Add a new CLI tool | Create `bin/pos--` with a `# POS:` header, chmod +x, add deps (apt → `preinstall.sh` PACKAGES; non-apt → `command -v` guard), then `make gen && make check` | +| Regenerate doc tables / completion flags | `make gen` (see `scripts/gen-docs.sh`; never hand-edit between `GEN:START`/`GEN:END` markers) | +| Verify repo self-consistency | `make check` (runs `scripts/check-sync.sh`; also the pre-commit hook after `make hook`) | Add a new app installer | Create `apps/.sh` (auto-discovered) | | Add a feature | Create `features/.sh` (installed on demand via `./install.sh --feature`) | | Add a systemd service | Create `systemd/.service` (auto-installed by postinstall; gate on a flag if it backs a feature) | diff --git a/DOC/DEV.md b/DOC/DEV.md index 34bd373..46d192e 100644 --- a/DOC/DEV.md +++ b/DOC/DEV.md @@ -51,9 +51,9 @@ All non-interactive commands log to `~/.local/share/linux_post_install/logs/`. `pos help ` shows a tool's help, e.g. `pos help communication telegram` (all words joined with dashes → `pos-communication-telegram --help`). `pos ` or `pos --help` shows a category's subcommands (derived from the `pos--*` filenames in `bin/` — no script execution, so it works even for root-only/interactive tools like `system-firewall`). -**When adding a command, `bin/pos` itself has two things to keep in sync:** +**When adding a command, `bin/pos` itself has one thing to keep in sync:** -- **The usage text** (`usage()` function) — the CATEGORIES and EXAMPLES blocks are the built-in cheat-sheet (`pos --help`). Add the new command there or it stays invisible. +- **The usage text** (`usage()` function) — the CATEGORIES block is **auto-derived** from the `pos-*` filenames in `bin/` (no manual edit, can't drift). The EXAMPLES block is the only hand-maintained part: add a line there only if you want the tool showcased in `pos --help`. - **`INTERACTIVE_CMDS`** (space-separated list above the dispatch loop) — commands that **read stdin** (password prompts, selection menus: `media-mp4`, `system-backup`, `usb-server`) must be added here. Everything else is piped through `tee` for logging, which would hang or swallow an interactive prompt. sudo's own password prompt is unaffected — it reads from `/dev/tty`. Trade-off: it's all-or-nothing **per script** — adding a flag-style tool with *any* prompting subcommand (e.g. `usb-server --share`) means *every* subcommand of that script skips output logging (e.g. `usb server --ls` loses the `tee` log too). ### Shared Library (`lib/common.sh`) @@ -114,15 +114,20 @@ esac warn() { echo "[!] $*"; } err() { echo "ERROR: $*" >&2; exit 1; } ``` - If you skip `common.sh`, add the tool to the "Scripts that do NOT source common.sh" list in `DOC/AGENT_Context_Project.md`. + If you skip `common.sh`, `make gen` adds the tool to the "Scripts that do NOT source common.sh" list in `DOC/AGENT_Context_Project.md` automatically. ### 2. Make it discoverable - The dispatcher auto-discovers executable `bin/pos-*` files — no registration needed. The file **must be executable** (`chmod +x`, committed as mode `100755`); the dispatcher and `install.sh` skip non-executables. - `pos --help` (and bare `pos `) is derived from the `pos--*` filenames too — a new tool appears in its category's help automatically, with no registration (see [The `pos` CLI](#the-pos-cli)). -- Add the command to the `usage()` CATEGORIES/EXAMPLES blocks in `bin/pos` (see [The `pos` CLI](#the-pos-cli)). +- **Add the `# POS:` header** (single source of truth for the docs) right after the shebang/strict-mode lines: + ```bash + # POS: — one-line description rendered by `make gen` + # POS_FLAGS: --flag1 --flag2 # ONLY for flag-style tools + ``` + The description feeds the dispatch table, bin tree and file table in `DOC/AGENT_Context_Project.md`; `POS_FLAGS` feeds flag completion in `completions/pos.bash`. Both update via `make gen`. +- Optionally add an EXAMPLES line in `bin/pos` `usage()` to showcase the tool in `pos --help`. - If the command **reads stdin** (prompts/selection), add it to `INTERACTIVE_CMDS` in `bin/pos` — see [The `pos` CLI](#the-pos-cli). -- If it takes flag-style args (e.g. `--send "text"`), consider extending `completions/pos.bash`; category/subcommand names are auto-discovered from the filename. ### 3. Add system dependencies @@ -150,8 +155,8 @@ Place public keys in `config/authorized_keys` (one per line). `postinstall.sh` r ### 6. Update the docs -- `DOC/POS.md`: add the command to the section table + a detail block (commands, behavior, configuration). -- `DOC/AGENT_Context_Project.md`: update the bin tree, the dispatch table, the "scripts that do NOT source common.sh" list (if applicable), and the file line-count table. +- `DOC/POS.md`: add the command to the section table + a detail block (commands, behavior, configuration). This is the one hand-written doc. +- `DOC/AGENT_Context_Project.md` generated sections (bin tree, dispatch table, no-common.sh list, line-count table) and the `completions/pos.bash` flags block are produced by `make gen` — do **not** hand-edit between the `GEN:START`/`GEN:END` markers. - Root `README.md`: only if the `pos` category list in the help text changes. ### 7. Test @@ -163,8 +168,12 @@ shellcheck bin/your-tool ./bin/your-tool --help bin/pos help # confirm dispatch works bin/pos --help # confirm category listing includes the new tool (first tool in a new category) +make gen # regenerate doc tables + completion flags +make check # full self-consistency gate (syntax, exec bits, doc/code sync, smoke) ``` +`make check` is the definition of done — the same check runs as a pre-commit hook once you've run `make hook`. + --- ## Adding an Optional App diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2d3853a --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +.PHONY: check gen hook + +## check — verify repo self-consistency (syntax, exec bits, doc/code sync, smoke) +check: + ./scripts/check-sync.sh + +## gen — regenerate code-derived doc sections + completion flags +gen: + ./scripts/gen-docs.sh + +## hook — install the opt-in pre-commit hook (runs `make check`) +hook: + ./scripts/install-hooks.sh diff --git a/scripts/check-sync.sh b/scripts/check-sync.sh new file mode 100755 index 0000000..73216bc --- /dev/null +++ b/scripts/check-sync.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail +# Repo self-consistency check — run via `make check` (also as a pre-commit hook). +# bash -n every script, executability, doc<->code sync (gen-docs --check), +# and a dispatch smoke test. Exit 1 on any failure. + +root="$(cd "$(dirname "$0")/.." && pwd)" +cd "$root" + +fail=0 +note() { echo " ✗ $*"; } + +# ── 1. Syntax ─────────────────────────────────────────────────── +for f in bin/pos bin/pos-* lib/*.sh install.sh preinstall.sh postinstall.sh \ + completions/pos.bash scripts/*.sh; do + [ -f "$f" ] || continue + bash -n "$f" 2>/dev/null || { note "syntax error in $f"; fail=1; } +done + +# ── 2. Executability ──────────────────────────────────────────── +for f in bin/pos bin/pos-*; do + [ -x "$f" ] || { note "not executable: $f"; fail=1; } +done + +# ── 3. Doc <-> code sync ──────────────────────────────────────── +if ! bash scripts/gen-docs.sh --check >/dev/null 2>&1; then + note "doc/code drift — run 'make gen' and commit the regenerated files" + fail=1 +fi + +# ── 4. Dispatch smoke test ────────────────────────────────────── +bash bin/pos --help >/dev/null 2>&1 || { note "pos --help failed"; fail=1; } +bash bin/pos docker --help >/dev/null 2>&1 || { note "pos docker --help failed"; fail=1; } +bash bin/pos bogus >/dev/null 2>&1 && { note "pos bogus should have failed"; fail=1; } + +if [ "$fail" -eq 0 ]; then + echo "check-sync: OK" +else + echo "check-sync: FAILED" >&2 + exit 1 +fi diff --git a/scripts/gen-docs.sh b/scripts/gen-docs.sh index a051fd9..47050ab 100755 --- a/scripts/gen-docs.sh +++ b/scripts/gen-docs.sh @@ -90,18 +90,13 @@ gen_posflags() { } # ── Replace (write) or verify (check) one marker block ────────── -markers() { - # bash files use '#' comment markers, markdown uses HTML comments - case "$1" in - *.bash|*.sh) echo "# GEN:START $2" "# GEN:END $2" ;; - *) echo "" "" ;; - esac -} - regen_block() { local file="$1" name="$2" local start end newfile tmp - read -r start end <<<"$(markers "$file" "$name")" + case "$file" in + *.bash|*.sh) start="# GEN:START $name"; end="# GEN:END $name" ;; + *) start=""; end="" ;; + esac newfile="$(mktemp)" "gen_$name" > "$newfile" diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100755 index 0000000..e4d0857 --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail +# Opt-in git pre-commit hook that runs `make check` before every commit. +# Run `make hook` (or ./scripts/install-hooks.sh) once per clone. +# +# Skips itself when the commit is purely merge/conflict-resolution driven. + +root="$(cd "$(dirname "$0")/.." && pwd)" +hook="$root/.git/hooks/pre-commit" + +cat > "$hook" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +root="$(git rev-parse --show-toplevel)" +cd "$root" +exec make check +EOF +chmod +x "$hook" +echo "pre-commit hook installed: $hook" diff --git a/templates/pos-tool.sh b/templates/pos-tool.sh index 2541fc2..85ebcd6 100755 --- a/templates/pos-tool.sh +++ b/templates/pos-tool.sh @@ -5,15 +5,19 @@ set -euo pipefail # TEMPLATE — new `pos` CLI tool # # 1. Copy: cp templates/pos-tool.sh bin/pos-- -# 2. Exec bit: chmod +x bin/pos-- -# 3. Register: add the command to usage() CATEGORIES/EXAMPLES in bin/pos -# If it reads stdin (password/selection prompts), also add it to +# 2. Header: add a `# POS:` line right after the shebang/strict-mode +# lines (single source of truth for generated docs): +# # POS: — one-line description +# # POS_FLAGS: --flag1 --flag2 (flag-style tools only) +# 3. Exec bit: chmod +x bin/pos-- +# 4. If it reads stdin (password/selection prompts), add it to # INTERACTIVE_CMDS in bin/pos or its prompt breaks under the log tee. -# 4. Docs: DOC/POS.md section table + detail block, -# DOC/AGENT_Context_Project.md (bin tree, dispatch table, -# self-contained list, file line-count table), root README.md only -# when the category list changes. -# 5. Deps: add packages to PACKAGES in preinstall.sh if needed. +# 5. Docs: DOC/POS.md section table + detail block (hand-written). +# The AGENT_Context tables + completion flags come from +# `make gen` — never hand-edit between GEN markers. +# 6. Deps: add apt packages to PACKAGES in preinstall.sh; non-apt +# installers → `command -v || err "install from "`. +# 7. Done: `make gen && make check` # # Invoked as: pos [args] # ────────────────────────────────────────────────────────────────