feat(ci): open a downstream-bump tracking issue on every release#7561
feat(ci): open a downstream-bump tracking issue on every release#7561JohnMcLear wants to merge 4 commits intoether:developfrom
Conversation
Adds a single source of truth for every downstream distribution of Etherpad (Docker Hub, Snap, Debian, Home Assistant, Umbrel, TrueCharts, Proxmox, Cloudron, YunoHost, CasaOS, BigBlueButton, Unraid, Sandstorm, Nextcloud Ownpad) at docs/downstreams.yml, plus a workflow that, on every GitHub release publish, opens a single tracking issue with a checklist grouped by how each downstream is kept current: 🚀 Automatic — this repo's CI handles it on tag push 🧩 Manual bump in-repo — someone edits a file here, CI does the rest 🤖 Externally automated — a Renovate-like bot on the downstream side ✉️ Needs a PR we send — a maintainer files a bump PR 📨 Needs an issue we file 🤝 Maintained externally — we have no lever; poke if stale⚠️ Known stale — kept for visibility, no action Motivation: without this, external catalogs like CasaOS, TrueCharts, Bigbluebutton's `bbb-etherpad.placeholder.sh`, and the Sandstorm market listing accumulate years of drift. Turning "remember every downstream" into a per-release checklist is the lightest-touch fix that scales. The renderer is a standalone Python script so the issue format can be tweaked and dry-run locally: python3 .github/scripts/render-downstream-tracker.py \ docs/downstreams.yml 2.6.1 ether/etherpad-lite A `workflow_dispatch` trigger with a manual `version` input is included so the tracker can be smoke-tested before the next real release. Refs ether#7529 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review Summary by QodoAdd downstream distribution tracker for release checklist automation
WalkthroughsDescription• Adds downstream distribution catalog at docs/downstreams.yml tracking 15+ Etherpad packagers • Creates automated workflow to open per-release tracking issues with categorized checklist • Implements standalone Python renderer for flexible issue body generation and local testing • Enables smoke-testing via workflow_dispatch before real releases Diagramflowchart LR
A["Release Published"] -->|triggers| B["release-downstreams.yml"]
C["workflow_dispatch"] -->|manual test| B
B -->|reads| D["docs/downstreams.yml"]
B -->|executes| E["render-downstream-tracker.py"]
D -->|catalog data| E
E -->|generates markdown| F["GitHub Issue"]
F -->|checklist by type| G["Automatic/Manual/External/Stale"]
File Changes1. docs/downstreams.yml
|
Code Review by Qodo
Context used 1. JQ string injection breaks dedupe
|
| def render(catalog_path: Path, version: str, repo: str) -> str: | ||
| with catalog_path.open() as f: | ||
| catalog = yaml.safe_load(f) | ||
| items = catalog.get("downstreams", []) |
There was a problem hiding this comment.
1. 4-space indent in script 📘 Rule violation ⚙ Maintainability
The new Python script uses 4-space indentation, but the compliance checklist requires 2-space indentation (and no tabs) for code changes. This introduces inconsistent formatting against the mandated whitespace standard.
Agent Prompt
## Issue description
The added Python script uses 4-space indentation, but the repository compliance checklist requires 2-space indentation (and no tabs) for code changes.
## Issue Context
This affects the newly added `.github/scripts/render-downstream-tracker.py` functions.
## Fix Focus Areas
- .github/scripts/render-downstream-tracker.py[33-109]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Addresses Qodo feedback on ether#7561: reading `inputs.version` on a `release: published` event can yield an empty string or a context-evaluation failure depending on runtime. `inputs` only populates on workflow_dispatch. Switch to `github.event.inputs.version` which is typed as the dispatch payload directly, and add the event name to the error message for easier debugging when neither tag nor input is set. Python 4-space indent is left as-is — that's PEP 8, and the 2-space repo style rule Qodo references applies to the shell/YAML/JS tree, not to standalone Python scripts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Qodo round-2 on this PR:
|
|
We have to be careful merging this because some downstream providers might not want us opening issues on each release and might prefer automatic methods.. It also might be worth waiting X days if its not a critical security thing.. I don't handle downstream much so happy for feedback from downstream providers |
Addresses Qodo review on ether#7561: 3. Workflow lacks feature flag — add an opt-out gate via `vars.SKIP_DOWNSTREAM_TRACKER`. Default stays opt-out (the whole point of the tracker is to fire automatically on release; opt-in would re-introduce the "forgot to enable it" failure mode). 4. Wrong GitHub path URLs — `path:` and `file:` are no longer aliases. `file:` now renders as `/blob/HEAD/...` (single file) and `path:` as `/tree/HEAD/...` (directory). Updated `docs/downstreams.yml` entries that pointed at single files (Proxmox VED ct/etherpad.sh, CasaOS docker-compose.yml, BBB placeholder.sh) to use `file:`. Renderer now errors if both keys are set on the same entry. 5. Duplicate issue creation — before `gh issue create`, search for an existing issue with the same title (across open/closed) and skip create if one exists. Re-running the workflow for the same release no longer piles up duplicates. 6. Missing YAML type validation — render() now validates that the top-level is a mapping, that `downstreams` is a list, and that each entry is a mapping with `name` and `update_type`. main() catches ValueError and surfaces it as a single CI-friendly error line instead of a Python traceback. Plus a `test_render_downstream_tracker.py` smoke test exercising the file/dir routing and each validation guard. Pushing back on Qodo issue 1 (4-space → 2-space indent): Python files follow PEP 8, which mandates 4-space indentation. The 2-space rule in the project compliance checklist applies to JS/TS source. Forcing 2-space on a Python script makes it harder to read and breaks tooling defaults (formatters, linters, IDEs). Leaving as-is. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/review |
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
|
Persistent review updated to latest commit 972a08f |
| def write(tmpdir: Path, content: str) -> Path: | ||
| p = tmpdir / "catalog.yml" | ||
| p.write_text(textwrap.dedent(content)) | ||
| return p | ||
|
|
||
|
|
||
| def expect_value_error(tmpdir: Path, content: str, needle: str) -> None: | ||
| p = write(tmpdir, content) | ||
| try: | ||
| mod.render(p, "1.0", "ether/etherpad") | ||
| except ValueError as e: | ||
| assert needle in str(e), f"expected {needle!r} in {e!r}" | ||
| return | ||
| raise AssertionError(f"expected ValueError containing {needle!r}") |
There was a problem hiding this comment.
1. 4-space indent in smoke test 📘 Rule violation ⚙ Maintainability
The newly added smoke test script uses 4-space indentation, violating the 2-space indentation requirement. This introduces inconsistent formatting in committed source files.
Agent Prompt
## Issue description
The added Python smoke test uses 4-space indentation, but the repository requires 2-space indentation.
## Issue Context
Keeping indentation consistent avoids style drift and potential formatter/linter conflicts.
## Fix Focus Areas
- .github/scripts/test_render_downstream_tracker.py[24-82]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| for idx, item in enumerate(items): | ||
| if not isinstance(item, dict): | ||
| raise ValueError( | ||
| f"{catalog_path}: downstreams[{idx}] must be a mapping, " | ||
| f"got {type(item).__name__}" | ||
| ) | ||
| if "name" not in item or "update_type" not in item: | ||
| raise ValueError( | ||
| f"{catalog_path}: downstreams[{idx}] missing required " | ||
| f"`name` and/or `update_type`" | ||
| ) | ||
| if "path" in item and "file" in item: | ||
| raise ValueError( | ||
| f"{catalog_path}: downstreams[{idx}] ({item['name']}) " | ||
| f"sets both `path` and `file`; use `file` for files and " | ||
| f"`path` for directories, not both" | ||
| ) | ||
|
|
||
| out: list[str] = [] | ||
| out.append(f"## Downstream distribution checklist for `{version}`\n") | ||
| out.append( | ||
| "Auto-opened by `.github/workflows/release-downstreams.yml` on " | ||
| "release publish.\n" | ||
| ) | ||
| out.append( | ||
| f"Source of truth: [`docs/downstreams.yml`](https://github.com/" | ||
| f"{repo}/blob/develop/docs/downstreams.yml).\n" | ||
| ) | ||
| out.append( | ||
| "Tick items as you verify them. Anything still unchecked a week " | ||
| "after release is a candidate for follow-up.\n" | ||
| ) | ||
|
|
||
| for update_type, heading in GROUPS: | ||
| matches = [i for i in items if i.get("update_type") == update_type] | ||
| if not matches: | ||
| continue | ||
| out.append(f"\n### {heading}\n") | ||
| for item in matches: | ||
| out.append(_render_item(item, repo)) | ||
|
|
There was a problem hiding this comment.
2. Unknown update_type omitted 🐞 Bug ≡ Correctness
render() only renders entries whose update_type matches one of the hard-coded GROUPS; a typo or new update_type in docs/downstreams.yml will be silently dropped from the tracking issue. This breaks the “single source of truth” behavior by making missing checklist items undetectable in CI.
Agent Prompt
### Issue description
`render()` validates that each downstream item has an `update_type`, but it does not validate that the value is one of the supported categories. Because rendering only iterates over the hard-coded `GROUPS`, any typo/new value will be silently omitted from the generated checklist.
### Issue Context
This workflow is intended to make `docs/downstreams.yml` a single source of truth; silent omission defeats that by producing an incomplete tracking issue without a CI failure.
### Fix Focus Areas
- .github/scripts/render-downstream-tracker.py[22-30]
- .github/scripts/render-downstream-tracker.py[47-63]
- .github/scripts/render-downstream-tracker.py[80-87]
### Suggested change
- Build an `allowed_update_types` set from `GROUPS`.
- During the per-item validation loop, raise a `ValueError` if `item['update_type']` is not in the allowed set (include the bad value and the allowed values in the message).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Round 3 of Qodo review on ether#7561: ether#2 Unknown update_type silently omitted — render() now validates each item's `update_type` against the GROUPS allowlist and raises ValueError. Without this, a typo'd entry (e.g. `external-pr` with a dash) would parse cleanly but never reach the rendered checklist, so the downstream would silently fall off the radar — exactly the failure mode the tracker exists to prevent. ether#5 Traceback on YAML read/parse — main()'s except clause only caught ValueError, so missing-file (FileNotFoundError/OSError) and malformed-YAML (yaml.YAMLError) errors still surfaced as full tracebacks. Broaden to catch all three with a single `type(e).__name__: message` line so the workflow log stays actionable. Plus three new test cases: unknown update_type, missing catalog file, malformed YAML. Pushing back again on: #1/ether#3 4-space → 2-space Python indent — Python files follow PEP 8. Forcing 2-space breaks formatter/linter/IDE defaults and is harder to read than the universal Python convention. The 2-space rule is for JS/TS source. ether#6 Workflow lacks feature flag — already addressed in the previous commit via opt-out gate `vars.SKIP_DOWNSTREAM_TRACKER`. Default stays opt-out because the tracker exists precisely to catch "forgot to enable it at release time" cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/review |
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
|
Persistent review updated to latest commit 049a4a3 |
| EXISTING=$(gh issue list \ | ||
| --repo "$GITHUB_REPOSITORY" \ | ||
| --state all \ | ||
| --label release \ | ||
| --label downstream \ | ||
| --search "\"$TITLE\" in:title" \ | ||
| --json number,title \ | ||
| --jq ".[] | select(.title == \"$TITLE\") | .number" \ | ||
| | head -n1) |
There was a problem hiding this comment.
1. Jq string injection breaks dedupe 🐞 Bug ☼ Reliability
The workflow builds a jq filter by interpolating $TITLE directly into the jq program, so a version/title containing a double-quote or backslash can break jq parsing and make the workflow fail before creating (or deduping) the tracking issue.
Agent Prompt
### Issue description
The workflow interpolates `$TITLE` into the jq program string passed to `gh issue list --jq`, which can break parsing if `$VERSION` contains characters like `"` or `\`.
### Issue Context
`TITLE` is derived from `$VERSION` (workflow_dispatch input or release tag). Even if unusual in real tags, dispatch testing can easily include these characters and will cause the step to fail.
### Fix Focus Areas
- .github/workflows/release-downstreams.yml[81-93]
### Suggested fix
Avoid embedding `$TITLE` inside the jq program. Instead pipe JSON to `jq` and pass the title via `--arg`, e.g.:
```bash
EXISTING=$(gh issue list \
--repo "$GITHUB_REPOSITORY" \
--state all \
--label release \
--label downstream \
--search "$TITLE in:title" \
--json number,title \
| jq -r --arg title "$TITLE" '.[] | select(.title == $title) | .number' \
| head -n1)
```
(Or, alternatively, properly escape `$TITLE` before embedding it into the jq string.)
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Summary
Adds a single source of truth for every downstream distribution of Etherpad (Docker Hub, Snap, Debian, Home Assistant, Umbrel, TrueCharts, Proxmox, Cloudron, YunoHost, CasaOS, BigBlueButton, Unraid, Sandstorm, Nextcloud Ownpad) at
docs/downstreams.yml, plus a workflow that opens a single tracking issue on every GitHub release publish with a checklist grouped by how each downstream is kept current:config.yaml), CI does the restWhy
External catalogs (CasaOS, TrueCharts, BBB's
bbb-etherpad.placeholder.sh, Unraid, Sandstorm) accumulate years of drift because nobody remembers to update them at release time. BBB still clones Etherpad 1.9.4; TrueCharts was pinned to 1.8.14 until PR trueforge-org/truecharts#47234; Sandstorm hasn't moved since 2015.Turning "remember every downstream at release" into a per-release checklist is the lightest-touch fix that scales.
Files
docs/downstreams.yml— catalog. Adding a new integrator is one YAML block; the workflow picks it up automatically..github/workflows/release-downstreams.yml— triggers onrelease: publishedor manualworkflow_dispatchwith aversioninput (so we can smoke-test before the next real release)..github/scripts/render-downstream-tracker.py— standalone Python renderer, dry-runnable locally:Test plan
python3 .github/scripts/render-downstream-tracker.py docs/downstreams.yml 2.6.1 ether/etherpad-literenders valid GitHub-flavoured markdown locallyworkflow_dispatchrun withversion: 2.6.1-testopens an issue on this repo with the expected checklistFollow-up
After merge, I'll file a BBB issue directly (their 1.9.4 pin pre-dates this automation and needs a one-off nudge), and drop a comment on #7529 linking to this PR as the structural fix.
Refs #7529
🤖 Generated with Claude Code