feat(config): menu-driven multi-account wizard, v2 schema, config-time Launchpad OAuth

Address review of PR #1 (Valentin): replace the linear config walk-through
and single-section schema.

- weekly-activity config becomes a menu loop: Current sources (masked) ->
  Add a new source (pick kind, repeatable) / Remove a source / Exit; the
  file is written only when something changed
- new versioned config layout (version = 2): [[kind.accounts]] arrays so
  several accounts or instances per provider coexist; optional display
  labels; strict unknown-key/type validation kept (errors carry account
  position, e.g. [github.accounts[1]])
- legacy v1 files (no version key, one section per source) migrate
  transparently in memory on load; saving from the wizard persists v2;
  the file itself is never rewritten by load_config
- non-anonymous Launchpad accounts now authenticate at config time:
  constructing LaunchpadSource runs launchpadlib's browser OAuth and the
  token lands in the system keyring before any report; an explicit
  credentials_file stays supported as an additive opt-out
- report aggregates every configured account in stable order with failure
  isolation intact; headings add the account label when it disambiguates
  (source_label shared between aggregate and wizard)
- GitHub accounts gain optional token_env resolved lazily against the
  environment before falling back to GITHUB_TOKEN / `gh auth token`
This commit is contained in:
2026-08-26 22:53:17 +00:00
parent 6d8cc3d817
commit 5f4487f3ad
6 changed files with 801 additions and 261 deletions
+34 -15
View File
@@ -67,35 +67,54 @@ Rolling 7 days ending today (inclusive). Override with `--since` / `--until` (IS
## Configuration
`weekly-activity config` walks through each known source and writes a TOML
file. Tokens are stored as provided (plaintext), but the file is created with
owner-only permissions (`0600`) and secrets are never echoed back. Use
`--config <path>` (also accepted by `report`) to keep configs elsewhere.
`weekly-activity config` opens a small menu-driven editor: it lists your
current sources (secrets masked), then lets you **Add a new source**
(choose the kind, repeat as often as you like), **Remove a source**, or
**Exit** (writes the file only when something changed). Several accounts
of the same provider can live side by side. Tokens are stored as provided
(plaintext), but the file is created with owner-only permissions (`0600`)
and secrets are never echoed back. Use `--config <path>` (also accepted by
`report`) to keep configs elsewhere.
Default location: `${XDG_CONFIG_HOME:-~/.config}/weekly-activity.toml`.
```toml
[github]
username = "octocat"
token = "" # empty -> fall back to $GITHUB_TOKEN / `gh auth token`
version = 2
[launchpad]
[[launchpad.accounts]]
username = "jane"
mode = "credentials" # "anonymous" (public data only) | "credentials"
credentials_file = "" # empty -> system keyring; OAuth runs at report time
mode = "credentials" # "anonymous" (public data only) | "credentials"
# credentials_file = "~/lp-creds.json" # omit -> system keyring
[gitlab]
[[github.accounts]]
name = "personal" # optional display label
username = "octocat"
token = "" # empty -> $token_env / GITHUB_TOKEN / `gh auth token`
[[github.accounts]]
name = "work"
username = "acme-jane"
token_env = "WORK_GH_TOKEN"
[[gitlab.accounts]]
name = "salsa"
url = "https://salsa.debian.org"
username = "jane"
token_env = "SALSA_TOKEN"
[bts]
[[bts.accounts]]
email = "jane@example.org"
```
An absent `[section]` disables that source. Re-running `weekly-activity
config` shows the current settings and offers to redo the wizard with the
existing values as defaults.
An absent section disables that source; an empty one is not written.
When a non-anonymous Launchpad account is added without a credentials
file, the wizard runs launchpadlib's OAuth right there — a browser opens
once and the token is kept in your system keyring, so `report` never has
to authenticate.
Legacy configs without the `version` key (one single-account section per
source) keep working: they are migrated transparently when loaded and
rewritten in the v2 shape the next time the wizard saves.
## Architecture