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
This commit is contained in:
@@ -35,6 +35,7 @@ Linux_post_install/
|
||||
├── bin/ # CLI tools — installed to /usr/local/bin/
|
||||
│ ├── pos # Main dispatcher — smart arg matching to pos-* scripts
|
||||
<!-- GEN:START tree -->
|
||||
│ ├── 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 |
|
||||
|----------|---------|--------|-------------|
|
||||
<!-- GEN:START dispatch -->
|
||||
| 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):
|
||||
<!-- GEN:START selfcontained -->
|
||||
`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`.
|
||||
<!-- GEN:END selfcontained -->
|
||||
|
||||
---
|
||||
@@ -421,11 +423,12 @@ System-wide flag store at `/usr/local/share/linux_post_install/flags/`:
|
||||
### Adding a New Tool
|
||||
|
||||
1. Create `bin/pos-<category>-<command>` from `templates/pos-tool.sh` — must be executable (`100755`); it auto-appears in `pos <category> --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/<tool>.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 <full command> && bin/pos <category> --help`
|
||||
2. Add the `# POS: <cat> <cmd> — <one-line description>` 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/<tool>.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 <full command> && bin/pos <category> --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) |
|
||||
<!-- GEN:START filetable -->
|
||||
| `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 |
|
||||
<!-- GEN:END filetable -->
|
||||
| `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-<cat>-<cmd>`, 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-<cat>-<cmd>` 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/<name>.sh` (auto-discovered) |
|
||||
| Add a feature | Create `features/<name>.sh` (installed on demand via `./install.sh --feature`) |
|
||||
| Add a systemd service | Create `systemd/<name>.service` (auto-installed by postinstall; gate on a flag if it backs a feature) |
|
||||
|
||||
+16
-7
@@ -51,9 +51,9 @@ All non-interactive commands log to `~/.local/share/linux_post_install/logs/`.
|
||||
|
||||
`pos help <full command>` shows a tool's help, e.g. `pos help communication telegram` (all words joined with dashes → `pos-communication-telegram --help`). `pos <category>` or `pos <category> --help` shows a category's subcommands (derived from the `pos-<category>-*` 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 <category> --help` (and bare `pos <category>`) is derived from the `pos-<category>-*` 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: <category> <command> — 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 <full command> # confirm dispatch works
|
||||
bin/pos <category> --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
|
||||
|
||||
@@ -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
|
||||
Executable
+41
@@ -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
|
||||
+4
-9
@@ -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 "<!-- GEN:START $2 -->" "<!-- GEN:END $2 -->" ;;
|
||||
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="<!-- GEN:START $name -->"; end="<!-- GEN:END $name -->" ;;
|
||||
esac
|
||||
newfile="$(mktemp)"
|
||||
"gen_$name" > "$newfile"
|
||||
|
||||
|
||||
Executable
+19
@@ -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"
|
||||
+12
-8
@@ -5,15 +5,19 @@ set -euo pipefail
|
||||
# TEMPLATE — new `pos` CLI tool
|
||||
#
|
||||
# 1. Copy: cp templates/pos-tool.sh bin/pos-<category>-<command>
|
||||
# 2. Exec bit: chmod +x bin/pos-<category>-<command>
|
||||
# 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: <category> <command> — one-line description
|
||||
# # POS_FLAGS: --flag1 --flag2 (flag-style tools only)
|
||||
# 3. Exec bit: chmod +x bin/pos-<category>-<command>
|
||||
# 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 <bin> || err "install from <URL>"`.
|
||||
# 7. Done: `make gen && make check`
|
||||
#
|
||||
# Invoked as: pos <category> <command> [args]
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user