Releases: bgorzelic/gmail-cleanup
Release list
v0.6.1 — stop on an exhausted Gmail quota
Fixed
- An exhausted Gmail quota stops the run instead of being retried. A 403
dailyLimitExceeded(orquotaExceeded) now raises a clear error and exits
with status 2. v0.6.0 treatedquotaExceededas a rate limit and retried it.
v0.6.0 — installable: pipx install gmail-inbox-cleanup
The "installable" release. First release on PyPI, as gmail-inbox-cleanup
(pipx install gmail-inbox-cleanup); the command is still gmail-cleanup. The
gmail-cleanup name on PyPI belongs to an unrelated project.
⚠️ Upgrading from 0.5.x
- Your lists moved. Lists now ship inside the package as seeds
(gmail_cleanup/lists/), merged with your own copies in~/.gmail_cli/lists/.
Onlykeep.yamlships populated;humans,killandunsubbedship empty.
If you edited the repo'slists/*.yaml, copyhumans.yaml,kill.yamland
unsubbed.yamlinto~/.gmail_cli/lists/before upgrading. - Successful unsubscribes are now appended to
~/.gmail_cli/lists/unsubbed.yaml
(previously the repo/package copy).
Fixed
verifyno longer flags senders for mail sent before you unsubscribed.
It counted every message in the last 14 days, including mail that arrived
before the unsubscribe, soautopilot --escalaterun daily would block-filter
senders unsubscribed the day before. Entries with anunsubscribed_at
timestamp now count only mail after that time plus a grace period
(--grace-days, default 2 — Google's bulk-sender deadline). Senders still
inside the grace period are reported as PENDING and are never escalated.- List and state writers: a sender could be silently dropped after an
unterminated header comment; concurrent runs (scheduled + manual) lost records
and crashed on rename; a failed write could strand the newer data; event
deltas could overwrite a record's timestamp; malformed state files raised. - Wheel no longer installs a top-level
lists/directory into site-packages,
and the sdist no longer sweeps in stray checkouts. humans.yamlsenders are never unsubscribed or archived. The docs promised
this, butunsubscribenever checked the list. Humans now win over the kill
list too.- Removed the
has:listcatch-all filter.has:listis not a Gmail search
operator — the filter matched nothing. Delete it from your Gmail filters if a
previous version created it. filters applyreplaces a grown list's filter instead of stacking another.
Each run that added unsubscribed senders used to leave the older filter behind.
Older filters are removed only when the new one covers every sender in them.filters applyskips filters whose list is empty (Gmail rejects an emptyfrom:).statusmatches Gmail's UI: inbox conversations and inbox-only unread, from
one API call. It used to page through up to 10,000 message IDs twice and count
messages and all-mail unread.- Rate-limited or failed API calls are retried and reported instead of silently
returning nothing.
Added
autopilot --days Nand--min-count Kto tune the unsubscribe phase
(defaults unchanged: 30 days, 2 messages).autopilot --email-summaryemails the run's report to the account itself —
e.g. a scheduled--dry-runpreview.verify --grace-days N.- Much faster scans that respect Gmail's quota.
unsubscribefetches headers
in HTTP batches (25 per request) paced to the documented per-user limit
(100 quota units/s), and no longer fetches each sender a second time. - Rate-limit handling: honors
Retry-After, backs off 5→80 s with jitter,
re-sends throttled items in smaller batches, and remembers a throttle in
~/.gmail_cli/soautopilotskips its run until it expires. - Progress lines for non-terminal runs (logs, launchd).
- Autopilot webhook notification (
notify.webhook_urlin config). unsubbed.yamlmapping format{sender, unsubscribed_at}; bare-string
entries are still read.- User override lists in
~/.gmail_cli/lists/merged with the packaged seeds. - Tests for the scheduler, setup wizard, progress UI, list layout, verify
windows and autopilot (200+ tests). Tests run with a sandboxedHOME. - CI gates on
ruff checkbefore pytest; PyPI trusted-publishing workflow.
v0.5.2 — README polish
Documentation patch release. No behavior changes.
What's new
- README rewrite with hero badges (CI, release, Python, license), sample autopilot output, comparison table vs. other tools, three-bucket diagram, pipeline diagram, cleaner section structure with emoji anchors.
- Stale references fixed: the setup wizard was described as "6-step" in three places. It's been 7-step since v0.5.1.
- HANDOFF.md version line updated.
Same as v0.5.1
- 84 tests passing
- Setup wizard handles OAuth consent screen
- TROUBLESHOOTING.md covers common first-run issues
Install
pipx install git+https://github.com/bgorzelic/gmail-cleanup.git
Full changelog: CHANGELOG.md
v0.5.1 — partner-handoff polish
Patch release. Closes the one real first-run gap from v0.5.0 and adds onboarding documentation.
What's new
- Setup wizard adds Step 3/7: OAuth consent screen. Previously the wizard jumped from "enable Gmail API" straight to "create OAuth client", but Google requires the consent screen to be configured first — and unverified apps need the user added as a Test User. Without this step, new users hit "Access blocked: This app's request is invalid" on first run.
- TROUBLESHOOTING.md covers the 6 issues new users hit most often: consent-screen errors, missing credentials, scope limits, scheduler on Linux/Windows, 7-day token expiry for unverified apps, PATH issues with pipx.
- ARCHITECTURE.md — 5-minute tour of the codebase for new contributors.
- CI workflow runs pytest matrix across Python 3.11/3.12/3.13 on every push and PR.
- Issue templates for bug reports and feature requests.
Changed
- README install section now leads with
pipx install git+https://github.com/bgorzelic/gmail-cleanup.git(the right choice for end users) instead of the clone-and-pip-install path (which stays, for contributors). - CONTRIBUTING.md adds a "First-time onboarding" section pointing new collaborators at ARCHITECTURE.md and the safety invariants.
Compatibility
No behavior changes for existing v0.5.0 users. 84 tests still passing.
v0.5.0 — Swiss army knife release
The "Swiss army knife" release. Eight new components across four layers — config foundation, quick wins, features, and onboarding.
What's new
gmail-cleanup setup— interactive 6-step OAuth onboarding (open GCP → poll Downloads → smoke test → register account). Reduces first-run friction by ~80%.gmail-cleanup autopilot --all-accounts— runs the full pipeline across every configured account with partial-failure semantics.gmail-cleanup status— inbox health dashboard: live counts, filter inventory, list sizes, 7-day history.gmail-cleanup attachments— storage cleanup. Finds oversized old emails, ranks senders by bytes attributed.gmail-cleanup schedule install— daily launchd-scheduled autopilot (macOS).gmail-cleanup config / accounts— manage~/.gmail_cli/config.yaml.--emailbecomes optional once configured.- Rich progress UI + global
--quiet(cron-safe) and--verbose(debug) flags. - Auto-close-the-loop on unsubscribes — successful unsubs auto-append to
lists/unsubbed.yaml.
Under the hood
- Package refactor:
gmail_cli.py→gmail_cleanup/package (7 modules, kept the same console-script entry). - Per-account state file at
~/.gmail_cli/state_<email>.jsonwith 30-entry capped history. - New CI workflow runs pytest on Python 3.11/3.12/3.13 for every PR.
Tests
84 passing (was 52 at v0.4.0). +32 new tests covering config, accounts, state, atomic writes, attachments size parser, multi-account dispatch.
Install
```bash
pip install git+https://github.com/bgorzelic/gmail-cleanup.git
gmail-cleanup setup # first-time wizard
gmail-cleanup autopilot # daily cleanup
```
Full changelog: CHANGELOG.md
Architecture tour: ARCHITECTURE.md
v0.4.0 — autopilot + mark-read
The "Swiss army knife with an autopilot button" release.
gmail-cleanup autopilot— runs filters → unsubscribe → mark-read → verify in one shot. Idempotent, safe to run repeatedly.gmail-cleanup mark-read— bulk-mark by query. Cleared 4,710 archived-unreads on the maintainer's account in one shot.- Filter upgrades now also remove UNREAD so categorized mail counts toward read.
- Kill list additions: skool.com, invitations@linkedin.com, noreply@discord.com.
Full notes: CHANGELOG.md
v0.3.1 — credential search UX fix
Clean-room install audit found that the missing-credentials error said "place credentials.json in project dir" — meaningless when pip-installed (resolves inside site-packages). Fixed to search 4 locations in precedence order and print all checked paths + the GCP console URL.
Full notes: CHANGELOG.md
v0.3.0 — buttoned-up release
Repo turns from a working script into a productizable tool. Lists move to YAML, test suite lands (46 tests), verify subcommand audits stickiness, block-filter escalation, packaging as gmail-cleanup console script, CONTRIBUTING + CODE_OF_CONDUCT shipped.
Full notes: CHANGELOG.md
v0.2.0 — filters and verification
Declarative Gmail filter management via filters apply. Two-step approach: upgrades existing label-only filters with archive action, then creates the standard preset (human whitelist, has:list catch-all, previously-unsubscribed, killlist). Verification of 2026-05-14 unsubscribe cohort: 90% stickiness.
Full notes: CHANGELOG.md