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 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'CLAUDE.md') 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 -- cgit v1.2.3