aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CLAUDE.md24
-rw-r--r--README.md10
-rw-r--r--TODO.md30
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 <days>` 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.