kosmos daf0a203a0
CI / check (push) Successful in 32s
CI / release (push) Skipped
refactor(wizard): replace hand-rolled arrow-key menu with questionary
The reviewer asked for a known-good interactive-menu library instead of
the custom termios/select key reader; arrow keys had failed in their
terminal. The TTY path now delegates to questionary.select (pointer ">",
bold highlight via pointer/selected styles), keeping choice values mapped
to list indices. Cancellable menus graft an eager Escape binding onto the
prompt's own key-binding registry so Esc backs out while Ctrl-C still
aborts fixed menus and backs out of cancellable ones. The non-TTY
numbered fallback is untouched, and hand-rolled internals
(_arrow_menu/_read_keypress/_option_line/_draw_menu_rows) are gone.
2026-08-27 08:36:56 +00:00

weekly-activity

Aggregate weekly activity across development platforms (Launchpad, GitHub, GitLab, Debian BTS) into a single text report.

Usage

# Interactive setup: pick sources, store identities/tokens once
uv run weekly-activity config

# Aggregated report across every configured source
uv run weekly-activity report      # bare `weekly-activity` does the same

Single sources (ad-hoc, no config needed)

# Launchpad (uses system keyring for OAuth; --anonymous for public data only)
uv run weekly-activity launchpad <username>
uv run weekly-activity launchpad --anonymous <username>

# GitHub (uses GITHUB_TOKEN env var or `gh auth token`; anonymous works but rate-limited)
uv run weekly-activity github <username>
uv run weekly-activity github --token ghp_xxx <username>

# GitLab (gitlab.com by default; any self-hosted instance via --url, e.g. Debian Salsa;
# uses GITLAB_TOKEN / SALSA_TOKEN env vars, anonymous works but rate-limited)
uv run weekly-activity gitlab <username>
uv run weekly-activity gitlab --url https://salsa.debian.org <username>
uv run weekly-activity gitlab --url https://salsa.debian.org --token glpat-xxx <username>

# Debian BTS (bugs.debian.org; identify by the email used on bugs)
uv run weekly-activity bts <email>

# Custom date range (ISO dates, UTC)
uv run weekly-activity launchpad --since 2025-08-01 --until 2025-08-15 <username>
uv run weekly-activity github --since 2025-08-01 --until 2025-08-15 <username>
uv run weekly-activity gitlab --since 2025-08-01 --until 2025-08-15 <username>
uv run weekly-activity bts --since 2025-08-01 --until 2025-08-15 <email>

GitLab instances

Any GitLab instance works via --url. Token discovery order:

  1. --token flag
  2. env var named by --token-env
  3. <HOST>_TOKEN derived from the instance host (e.g. SALSA_TOKEN for salsa.debian.org)
  4. GITLAB_TOKEN

What it reports

Section Launchpad GitHub GitLab Debian BTS
Merge proposals / PRs authored getMergeProposals (created in range) /search/issues (author: + type:pr) /merge_requests (author_id, created range)
Review requests received getRequestedReviews (filtered by date_review_requested) /search/issues (reviewed-by: + type:pr)
Reviews performed getRequestedReviews (filtered by date_reviewed) (same query) events (action=approved)
Issues / Bugs searchTasks (reporter, commenter, assignee) /search/issues (author, commenter, assignee) /issues (author_id, assignee_id) get_bugs (submitter) + get_status (created in range)
Bugs owned, updated get_bugs (owner) + get_status (log_modified)
Repositories modified git_repositories.getRepositories (modified_since_date) /users/{user}/repos (filtered by pushed_at) — (covered by push events)
Commits pushed — (not in Launchpad REST API) /repos/{owner}/{repo}/commits (per active repo) events (action=pushed)

Default time window

Rolling 7 days ending today (inclusive). Override with --since / --until (ISO dates, UTC).

Configuration

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.

version = 2

[[launchpad.accounts]]
username = "jane"
mode = "credentials"   # "anonymous" (public data only) | "credentials"
# credentials_file = "~/lp-creds.json"   # omit -> system keyring

[[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.accounts]]
email = "jane@example.org"

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

src/weekly_activity/
  model.py            ActivityReport, Section, last_week_window
  report.py           format_report() — source-agnostic text renderer
  cli.py              subcommand dispatch (launchpad | github | gitlab | bts | report | config)
  aggregate.py        collect_specs()/format_combined() — one text report across sources
  config.py           ActivityConfig schema + TOML load/save (tomllib read, hand-written)
  wizard.py           interactive `config` onboarding
  sources/
    __init__.py       ActivitySource protocol
    launchpad.py      LaunchpadSource (launchpadlib)
    github.py         GitHubSource (httpx)
    gitlab.py         GitLabSource (httpx, REST v4 — gitlab.com or self-hosted)
    debian_bts.py     DebianBtsSource (python-debianbts SOAP)

Each source implements ActivitySource — a name and a collect(username, since, until) -> ActivityReport method. Sources keep their own dataclasses internally; the shared contract is Section (title + pre-formatted lines) and ActivityReport (metadata + sections + warnings). The report formatter (report.py) is source-agnostic.

Per-query failures (rate limits, server timeouts) are caught per-section — a failing endpoint records a warning and degrades to an empty section rather than killing the whole report.

Development

uv sync                    # install deps + dev tooling
uv run ruff format src     # format
uv run ruff check src      # lint
uv run ty check            # type check

CI / releases

Gitea Actions (.gitea/workflows/ci.yml) runs two jobs:

  • check — format, lint, and type check on every push to main
  • release — on v* tags: builds a standalone Linux executable with PyInstaller, smoke-tests it, creates a Gitea release, and uploads the binary as an asset

Cut a release with:

git tag vX.Y.Z && git push origin vX.Y.Z

Dependencies

  • launchpadlib — Launchpad REST API client
  • httpx — GitHub & GitLab REST API client
  • python-debianbts — Debian BTS SOAP client
  • ruff — format + lint (dev)
  • ty — type check (dev)
S
Description
No description provided
Readme
258 KiB
v0.3.0
Latest
2026-08-25 18:45:27 +00:00
Languages
Python 100%