aboutsummaryrefslogtreecommitdiffstats
path: root/TODO.md
blob: c457702099378ad8539264826edafcfef6dbcf78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# gitctl TODO

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

`gitctl repo delete <name>` - remove a repo from all three places it lives.

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.

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.

## 2. list repos -- DONE

`gitctl repo list` is implemented. It lists EVERY bare repo on disk (the full
set, public and private), not just the cgit-exposed ones, because cgit
auto-discovery is off and private repos are not in cgitrc. Source of truth is
`REPO_BASE/*.git`, not cgitrc; cgitrc membership only tags a repo PUB vs PRIV
and supplies its section. Description comes from each repo's `description` file
(truncated 60). Client renders a table, green public / dim private on a TTY.
Helper verb `list-repos`; primitive `list_repos()` + `repo_sections_from()`
(the latter dedupes the old `find_repo_section`). Self-tests cover both the
cgitrc parse and a temp-FS scan.

Still open from the original plan: extend bash completion to complete repo
NAMES (for `desc` / `delete` / `add-remote`) now that a list verb exists. The
`repo list` action itself is already in completion.

## 3. `-y` / `--yes` flag -- DONE

`-y` / `--yes` is implemented as a GLOBAL flag (on the top-level parser), so it
goes before the subcommand: `gitctl -y repo create ...`. Today the only y/N it
skips is the phase-1 push confirmation in `repo create`; the diff is still
printed and an "auto-confirmed" note shown. It does NOT bypass SSH key auth
(password / smartcard touch), so creation is never fully unattended. Section
auto-create was already silent, so behavior stays consistent.

Hard wall for delete (item 1): `-y` must NEVER be honored by a destructive
delete. The flag help says so, this note records it, and the global -y comment
in the client points at item 1. When delete is built, it reads its own
confirmation and ignores `a.yes`.

Bash completion offers `-y`/`--yes` at the top level and transparently skips a
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).