From 9c301a8eac944083556b3dce297764b4fca7d49a Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 24 Jun 2026 10:28:04 +0200 Subject: docs: bring CLAUDE.md, README, TODO current after the three features - CLAUDE.md: new hard rule that removal functions are exact inverses of their add counterparts (round-trip self-tests); TRASH_DIR added to the server constants; verified-live list extended with list-repos and a full delete cycle. - README: TRASH_DIR in the server-install constants and its dir note. - TODO: items 1-3 already marked done; cross-cutting work folded in; remaining leftovers listed (repo-name completion, the cosmetic No changes. echo, and the YAGNI gc-trash prune verb). - Removed all pre-existing em dashes from CLAUDE.md and README. Docs only, no code change. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 24 +++++++++++++++--------- README.md | 10 ++++++---- TODO.md | 30 +++++++++++++++++++++++------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a56836c..7146290 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,9 +8,9 @@ a SlackBuild. Two single-file scripts, no shared module: -- `gitctl` — client, runs on the laptop. Dumb caller: loads TOML config, edits +- `gitctl`: client, runs on the laptop. Dumb caller: loads TOML config, edits the local gitolite-admin clone and pushes, shells out to the helper over SSH. -- `gitctl-helper` — server helper, runs as the git user over a `command=`-restricted +- `gitctl-helper`: server helper, runs as the git user over a `command=`-restricted SSH key. Holds all parsing-heavy logic. Dispatches on `SSH_ORIGINAL_COMMAND`. The restricted key forces `gitctl-helper` regardless of the command the client @@ -19,8 +19,8 @@ fed to argparse. **No shell string interpolation, ever.** The client mirrors thi with `shlex.quote`; the round-trip is pinned in both self-tests. Two distinct SSH aliases, enforced different at config load: -- `push_ssh_alias` (e.g. `git_push`) — normal unrestricted git pushes -- `helper_ssh_alias` (e.g. `git_helper`) — restricted helper key, also `User git` +- `push_ssh_alias` (e.g. `git_push`): normal unrestricted git pushes +- `helper_ssh_alias` (e.g. `git_helper`): restricted helper key, also `User git` The helper runs as the **git** user, not root (the target server has `PermitRootLogin no`). git owns the bare repos' `description` files already; for @@ -29,7 +29,7 @@ git cannot create temp inodes in `/etc`, cgitrc is written **in place** (not via atomic rename), with a pre-write backup to `BACKUP_DIR` (`/var/lib/gitolite3/cgitrc-backups`). -The helper key does NOT go in git's normal `~/.ssh/authorized_keys` — gitolite +The helper key does NOT go in git's normal `~/.ssh/authorized_keys`; gitolite owns and regenerates that file, and an existing gitolite key would match first and run `gitolite-shell` (`FATAL: unknown git/gitolite command`). It lives in a separate `AuthorizedKeysFile` (`~git/.ssh/gitctl_keys`) wired via a @@ -47,9 +47,13 @@ separate `AuthorizedKeysFile` (`~git/.ssh/gitctl_keys`) wired via a - **cgitrc section blocks are flush-left and positional.** `section=` is positional, so a new repo block must be inserted at the end of the section's span: before the next `section=` line OR the next banner comment - (`# ---------- Name ------------#`), whichever comes first — a banner belongs + (`# ---------- Name ------------#`), whichever comes first; a banner belongs to the section it precedes (`insert_repo_block`, `BANNER_RE`). Ending the span only at `section=` drops the block under the following section's banner. +- **Removal functions are exact inverses of their add counterparts.** + `remove_repo_block` undoes `insert_repo_block`, `remove_stanza` undoes + `add_stanza`: insert/add then remove must return the original. This is pinned + by round-trip self-tests; keep them passing when touching either side. - **`description` file is owned `git:gitolite3`** (system user git, group gitolite3, no gitolite3 user). `set-desc` writes in place (truncate-write) to preserve owner, with `os.chown` back as insurance. @@ -71,10 +75,12 @@ rejection paths) then `self-test OK`. Run both after any change. ## Server constants (top of gitctl-helper) -`REPO_BASE`, `CGITRC`, `SYNC_SCRIPT`, `BACKUP_DIR`, `DEFAULT_OWNER` — must match -the actual server. Confirmed against the real `/etc/cgitrc` and +`REPO_BASE`, `CGITRC`, `SYNC_SCRIPT`, `BACKUP_DIR`, `TRASH_DIR`, `DEFAULT_OWNER` +must match the actual server (`TRASH_DIR` is where `delete` moves bare repos, +outside `REPO_BASE`). Confirmed against the real `/etc/cgitrc` and `sync-cgit-descs.py`. Verified end to end on the live server (read, section add, -two-phase repo create, desc, sync, idempotency, backups). +two-phase repo create, desc, sync, idempotency, backups, list-repos, and a full +delete cycle with the bare repo landing in `TRASH_DIR`). ## Docs / design diff --git a/README.md b/README.md index cc25bb5..dbdaf1a 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ chmod 644 /etc/cgitrc ``` Now authorize the helper key. **Do NOT add it to the git user's normal -`~/.ssh/authorized_keys`** — gitolite owns and regenerates that file from its +`~/.ssh/authorized_keys`**: gitolite owns and regenerates that file from its keydir, so a hand-added line is wiped and, worse, an existing gitolite key may match first and run `gitolite-shell` instead of the helper (you get `FATAL: unknown git/gitolite command`). @@ -130,9 +130,11 @@ ssh git_push info # gitolite access check sudo install -m 755 -o root -g root gitctl-helper /usr/local/bin/gitctl-helper ``` 2. Confirm the constants at the top of `gitctl-helper` match your server: - `REPO_BASE`, `CGITRC`, `SYNC_SCRIPT`, `BACKUP_DIR`, `DEFAULT_OWNER`. The git - user must be able to create `BACKUP_DIR` (default - `/var/lib/gitolite3/cgitrc-backups`); it is made on first write. + `REPO_BASE`, `CGITRC`, `SYNC_SCRIPT`, `BACKUP_DIR`, `TRASH_DIR`, + `DEFAULT_OWNER`. The git user must be able to create `BACKUP_DIR` (default + `/var/lib/gitolite3/cgitrc-backups`) and `TRASH_DIR` (default + `/var/lib/gitolite3/trash`, where `repo delete` moves bare repos); both are + made on first write. 3. Authorize the helper key (see SSH setup, step 2). 4. Self-test the helper: ``` diff --git a/TODO.md b/TODO.md index 7889779..9bb4ad8 100644 --- a/TODO.md +++ b/TODO.md @@ -65,10 +65,26 @@ leading `-y` when resolving group/action. ## Cross-cutting -- Bash completion: once `repo list` exists, complete repo names for `desc`, - `delete`, `add-remote` (like `--section` is completed today). One ssh call, - only when completing a repo-name argument. -- Skill: update both SKILL.md files (repo skills/gitctl + ~/.claude) to mention - delete (and its destructive, confirm-always nature) and `-y`'s real limit. -- Tests: each new helper verb gets self-test coverage (cgit block removal is the - risky one - mirror the insert tests: first/middle/last/missing). +Done as part of items 1-3: both SKILL.md files updated (delete + `-y` limit), +self-test coverage for every new helper verb, completion actions for `list` and +`delete`, README and CLAUDE.md brought current. + +## Remaining (not started) + +These are the leftovers after the three features shipped. None block anything. + +1. Repo-name bash completion. Now that `repo list` exists, complete repo NAMES + for `desc`, `delete`, and `add-remote` (the way `--section` is completed from + the server today). One ssh call (`gitctl repo list`, cut field 1), only when + completing the repo-name argument, so a stray Tab does not hit the network. + +2. Quiet the stray `No changes.` line. In `repo create` and `repo delete` the + `list-sections` / `add-section` helper passthrough prints a `No changes.` + line out of order, before the cgit-block message. Cosmetic only. Fix by + calling those helper steps with `quiet=True` (the flag already exists) and + printing a clean status, or by suppressing that specific echo. + +3. FUTURE / YAGNI: `gc-trash --older-than ` helper verb to prune + `TRASH_DIR` entries past a retention window, runnable from cron. The + timestamped trash layout already supports it; do not build until there is an + actual need to reclaim space. -- cgit v1.2.3