Python CLI that inventories Jira issues for large/old attachments, lets you mark them for removal, and then deletes them while tracking progress (TODO → IN_PROGRESS → DONE/ERROR). If an attachment cannot be deleted because Jira reports it still has related comments, the tool removes any comments that reference the attachment and tries again.
- Python 3.9+
- Jira Cloud (or Server/DC with the v3 REST API)
- A Jira API token with permission to read/delete attachments and delete comments
Install dependencies:
python -m venv .venv
source .venv/bin/activate
pip install -e .Copy config.example.json to config.json and fill in your Jira details:
{
"base_url": "https://your-domain.atlassian.net",
"email": "you@example.com",
"api_token": "atlassian_api_token",
"project_keys": ["OPS", "PLAT"],
"verify_ssl": true,
"page_size": 50
}project_keys: default project scope. Override with--projectswhen needed. You can leave the list empty if you primarily use the interactive picker— it will fetch available projects directly from Jira.page_size: batch size when paging through Jira search results.- Store the file securely; it contains secrets.
State is stored in state/state.json (configurable via --state-file). The CLI creates the folder automatically.
Each command accepts --config to point at a specific config file.
jira-utils list --before 2023-01-01 --min-size-mb 5Outputs a table with project, issue key, attachment name/id, size, creation date, and whether it is already tracked in the state file. Filtering options:
--projects OPS PLAT--issue-types Story Task--min-size-mb 10--before 2022-12-31(required)--max-issues 100(useful while testing)--verboseprints why attachments are skipped (date/size) so you can tune filters.
Re-run the same filters, optionally narrowing to explicit attachment IDs you copied from the list output:
# Mark every attachment that matches the filters
jira-utils mark --before 2023-01-01 --min-size-mb 5
# Mark individual attachments
jira-utils mark --before 2023-01-01 --min-size-mb 5 \
--attachment-id 12345 --attachment-id 67890Marked attachments are saved with status TODO inside the state file.
jira-utils process # only TODO items
jira-utils process --retry-errors # include previous failures
jira-utils process --limit 5For each attachment, the CLI:
- Updates status to
IN_PROGRESS. - Calls the Jira delete attachment API.
- On specific failure codes, scans issue comments for references to the attachment (
id, filename, or wiki-style[^name]markers), deletes those comments, and retries deletion. - Marks the attachment
DONEorERROR(with the error text).
jira-utils statusDisplays every tracked attachment with status and last error (if any).
Launch an ncurses UI to review attachments, select the ones you want to delete, and watch their status change (TODO → IN_PROGRESS → DONE/ERROR) as the tool processes them sequentially:
jira-utils interactive --before 2023-01-01 --min-size-mb 5Filter overview:
- Interactive mode now opens on a unified filter screen. Press
Enterto load attachments,pto adjust projects,tto pick issue types (Story, Task, Bug, etc.),mto edit the minimum attachment size, andbto change the date cutoff (before). - Leaving either the project or issue-type selection empty includes everything you can access, which is handy when you want to review all projects or all issue types at once.
Project / issue-type pickers:
- Use
Spaceto toggle an item,ato select/deselect all,cto clear,mto tweak the minimum size,bto edit the date, andEnterto return to the filter overview.
Attachment list controls:
- Arrow keys (or
j/k) move the cursor; holdShiftwith the arrows for range selects. Spacetoggles the current row.aselects/clears all.cclears the current selection.- Press
mto adjust the minimum size threshold orbto change the cutoff date; if deletions are running the reload happens once processing ends. - Press
d(orEnter) to delete the selected attachments. Processing happens in the background, so you can keep scrolling while items are deleted and watch statuses update in real time. Attachments already marked asDONEare skipped automatically. - Use
pto jump straight to the project picker,tfor the issue-type picker, orfto revisit the filter overview without exiting the app.q(orEsc) still exits (double-press while deletions are running).
The interactive mode requires curses, which ships with macOS/Linux Python builds. On Windows, use WSL or another environment that provides curses.
- Use narrow filters when testing to avoid hitting Jira rate limits.
- Comment cleanup is aggressive: any issue comment containing the attachment name/ID is removed. Review filters before running
processin production scenarios. - The CLI purposefully deletes attachments sequentially so progress is easy to follow and rollback is simple.
- If you need different retention policies per project, run
list/mark/processper project with project-specific arguments. - For end-to-end debugging, opt into the live pytest by setting
JIRA_LIVE_CONFIG(and optionallyJIRA_LIVE_PROJECTS,JIRA_LIVE_BEFORE,JIRA_LIVE_MIN_SIZE_MB) before runningpytest tests/test_live_search.py -k live -s. The test will call Jira, list the first few attachments it sees, and fail if no candidates are found.