diff options
| author | Danilo M. <danix@danix.xyz> | 2026-06-24 10:19:30 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-06-24 10:19:30 +0200 |
| commit | 02a5ae2adc2df39fc78fd7e29904ff473e9266a0 (patch) | |
| tree | bbe270a6302526476147430ce7614e17ac7cc1df /TODO.md | |
| parent | 345968271dec38b33a32649d8cfb767c2806330d (diff) | |
| download | gitctl-02a5ae2adc2df39fc78fd7e29904ff473e9266a0.tar.gz gitctl-02a5ae2adc2df39fc78fd7e29904ff473e9266a0.zip | |
feat: repo delete - remove repo, move bare repo to trash
gitctl repo delete <name> removes a repo from all three places it lives,
the inverse of repo create:
- gitolite stanza via remove_stanza (inverse of add_stanza), committed
and pushed FIRST so gitolite stops serving the repo before its bytes
move.
- cgit block via the new delete-repo helper verb (remove_repo_block,
inverse of insert_repo_block), backed up and synced.
- the bare repo is MOVED to TRASH_DIR (/var/lib/gitolite3/trash,
timestamped) with a .trashinfo breadcrumb, not rm, so it is
recoverable. A realpath check keeps the move one level under REPO_BASE.
Always confirms; the global -y is deliberately not honored (cmd_delete
never reads a.yes). Idempotent: a piece already gone is skipped.
Partial failure is resumable by re-running, like create. Both inverse
functions are pinned by add/insert-then-remove round-trip self-tests
plus middle/last/missing cases; trash_repo and do_delete_repo run on
temp dirs. Verified live: create then delete round-trips against the
real gitolite.conf, bare repo lands in the trash dir intact.
Helper changed: redeploy gitctl-helper to the server.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'TODO.md')
| -rw-r--r-- | TODO.md | 64 |
1 files changed, 22 insertions, 42 deletions
@@ -4,51 +4,31 @@ Planned features. Each note captures the non-obvious bits so the work does not re-discover them. Keep the hard rules from CLAUDE.md (stdlib only, single-file scripts, single-writer repo.desc, idempotent, no em dashes, work on master). -## 1. delete repo +## 1. delete repo -- DONE -`gitctl repo delete <name>` - remove a repo from all three places it lives. +`gitctl repo delete <name>` is implemented. It removes a repo from all three +places it lives, the inverse of `repo create`: +- gitolite stanza -> client `remove_stanza` (inverse of `add_stanza`), commit + + push (needs the SSH key / card, like create). PUSHED FIRST so gitolite stops + serving the repo before its bytes move. +- cgit block -> helper verb `delete-repo`, `remove_repo_block` (inverse of + `insert_repo_block`); deletes the repo.url/path/owner block + its spacer + blanks, leaves the section header. Backed up by write_cgitrc_lines, sync after. +- bare repo -> `trash_repo` MOVES it to `TRASH_DIR` (`/var/lib/gitolite3/trash`, + outside REPO_BASE, timestamped name) instead of rm, with a `.trashinfo` + breadcrumb. shutil.move, with a realpath check that the source is one level + under REPO_BASE (defense in depth on top of validate_url). -Touches three writers, so it is the inverse of `repo create`: -- gitolite.conf stanza -> client edits the admin clone, pushes (needs the SSH - key / card, like create). -- cgit block in /etc/cgitrc -> new helper verb, the inverse of - `insert_repo_block`: find the repo's `repo.url=` line and delete its block up - to the next `repo.url=`, `section=`, banner, or EOF. Back up first - (write_cgitrc_lines already does). Run sync after. -- the bare repo on disk -> gitolite does NOT delete it when the stanza goes - away. The helper does NOT rm it. Instead it MOVES the repo to a trash dir - (see below), so deletion is reversible and you review before anything is - really gone. +The helper exposes ONE verb (`delete-repo`) doing cgit-block removal + move + +.trashinfo. Always confirms (the global `-y` is deliberately NOT read by +cmd_delete). Idempotent: a piece already gone is skipped, not an error. Partial +failure (push ok, helper fails, or commit ok but push fails) is resumable by +re-running, same as create. Self-tests: remove_repo_block and remove_stanza both +pinned by an insert/add-then-remove round-trip plus middle/last/missing cases; +trash_repo and do_delete_repo exercised on temp dirs. -Trash dir (the chosen design, not rm -rf): -- New constant `TRASH_DIR`, OUTSIDE `REPO_BASE` so gitolite and cgit never see - it (e.g. `/var/lib/gitolite3/trash`). git-writable. -- Delete = `mv REPO_BASE/<name>.git TRASH_DIR/<name>.git.<timestamp>`. Atomic - within one filesystem; if TRASH_DIR is on a different mount, fall back to - copy+remove or just require same-fs. -- validate_url first; double-check the resolved source path is under REPO_BASE - before moving (defense in depth, even though validate_url blocks `..`/`/`). -- Leave a note for the user: print where it went and that it needs manual - review/removal. Optionally drop a short TRASH_DIR/README or a per-entry - `.trashinfo` (origin path, timestamp, who) so a later cleanup tool has - context. -- FUTURE: a helper verb like `gc-trash --older-than <days>` that prunes - TRASH_DIR entries past a retention window, runnable from cron. The move-based - design makes this trivial later; do not build it now (YAGNI), just keep - TRASH_DIR layout cron-friendly (timestamp in the name). - -Design notes: -- DESTRUCTIVE (even as a move - it disappears from the live tree). Always - confirm, show what will be removed and where it goes. The `-y` flag from - item 3 must NOT bypass the delete confirmation (or gate it behind a separate, - louder opt-in). Deleting is not the same as auto-confirming an additive diff. -- Idempotent-ish: if the repo is already gone from a given place, skip that - step and report, do not error. -- Recoverable: the bare repo sits in TRASH_DIR until you remove it; the cgitrc - backup covers the cgit block. Nothing is irreversibly destroyed by gitctl. -- Order: remove cgit block + move bare repo via helper first, then the gitolite - stanza push? Or stanza first? Decide and document - partial failure (push - fails after the move) should leave a clear, resumable state like create does. +FUTURE (not built, YAGNI): a `gc-trash --older-than <days>` prune verb for cron. +The timestamped TRASH_DIR layout already supports it. ## 2. list repos -- DONE |
