Skip to content

feat(ci): open a downstream-bump tracking issue on every release#7561

Open
JohnMcLear wants to merge 2 commits intoether:developfrom
JohnMcLear:chore/release-downstream-tracker
Open

feat(ci): open a downstream-bump tracking issue on every release#7561
JohnMcLear wants to merge 2 commits intoether:developfrom
JohnMcLear:chore/release-downstream-tracker

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

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:

  • 🚀 Automatic — this repo's CI handles it on tag push (Docker Hub, Snap, Debian .deb)
  • 🧩 Manual bump in-repo — someone edits a file here (HA add-on config.yaml), CI does the rest
  • 🤖 Externally automated — a Renovate-like bot or runtime check on the downstream side
  • ✉️ Needs a PR we send — a maintainer files a bump PR (CasaOS, BigBlueButton)
  • 📨 Needs an issue we file — maintainer-driven with no PR mechanism
  • 🤝 Maintained externally — we have no lever; poke if stale (Cloudron, YunoHost, Ownpad)
  • ⚠️ Known stale — kept for visibility, no action (Sandstorm)

Why

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 on release: published or manual workflow_dispatch with a version input (so we can smoke-test before the next real release).
  • .github/scripts/render-downstream-tracker.py — standalone Python renderer, dry-runnable locally:
    python3 .github/scripts/render-downstream-tracker.py \
        docs/downstreams.yml 2.6.1 ether/etherpad-lite
    

Test plan

  • python3 .github/scripts/render-downstream-tracker.py docs/downstreams.yml 2.6.1 ether/etherpad-lite renders valid GitHub-flavoured markdown locally
  • workflow_dispatch run with version: 2.6.1-test opens an issue on this repo with the expected checklist
  • First real release after merge opens the tracking issue automatically

Follow-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

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>
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Add downstream distribution tracker for release checklist automation

✨ Enhancement

Grey Divider

Walkthroughs

Description
• 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
Diagram
flowchart 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"]
Loading

Grey Divider

File Changes

1. docs/downstreams.yml ⚙️ Configuration changes +172/-0

Downstream distribution catalog with metadata

• Defines 15+ downstream distributions (Docker Hub, Snap, Debian, Home Assistant, Umbrel,
 TrueCharts, Proxmox, Cloudron, YunoHost, CasaOS, BigBlueButton, Unraid, Sandstorm, Nextcloud Ownpad,
 TrueNAS)
• Categorizes each by update mechanism: automatic, manual_ci, external_auto, external_pr,
 external_issue, external_maintainer, stale
• Includes repository links, file paths, workflow references, and maintenance notes for each
 downstream
• Serves as single source of truth for release tracking workflow

docs/downstreams.yml


2. .github/workflows/release-downstreams.yml ✨ Enhancement +71/-0

Workflow to open downstream tracking issues

• Triggers on GitHub release publish or manual workflow_dispatch with optional version input
• Resolves version from release tag or manual input, strips leading 'v' prefix
• Installs PyYAML dependency and invokes Python renderer script
• Creates GitHub issue with title, labels (release, downstream), and rendered checklist body
• Requires contents: read and issues: write permissions

.github/workflows/release-downstreams.yml


3. .github/scripts/render-downstream-tracker.py ✨ Enhancement +109/-0

Python renderer for downstream checklist markdown

• Standalone Python script consuming docs/downstreams.yml catalog and version string
• Groups downstreams by update type with emoji-prefixed headings (🚀 Automatic, 🧩 Manual, etc.)
• Generates GitHub-flavored markdown checklist with deep links to downstream repos and workflow
 files
• Supports local dry-run execution for format testing without CI re-runs
• Handles optional notes indentation for proper GitHub list item rendering

.github/scripts/render-downstream-tracker.py


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects bot commented Apr 19, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. 4-space indent in script 📘 Rule violation ⚙ Maintainability
Description
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.
Code

.github/scripts/render-downstream-tracker.py[R33-36]

+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", [])
Evidence
PR Compliance ID 8 requires 2-space indentation. In the added Python code, function bodies are
indented by 4 spaces (e.g., the render() function).

.github/scripts/render-downstream-tracker.py[33-36]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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


2. inputs context unavailable🐞 Bug ≡ Correctness
Description
The workflow reads ${{ inputs.version }} even when triggered by release: published, where the
inputs context is not guaranteed to exist, risking expression evaluation failure or an empty
VERSION and a failed run.
Code

.github/workflows/release-downstreams.yml[R35-47]

+      - name: Resolve version
+        id: v
+        env:
+          TAG: ${{ github.event.release.tag_name }}
+          INPUT: ${{ inputs.version }}
+        run: |
+          VERSION="${TAG:-$INPUT}"
+          VERSION="${VERSION#v}"
+          if [ -z "${VERSION}" ]; then
+            echo "Could not determine version." >&2
+            exit 1
+          fi
+          echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
Evidence
The workflow always sets INPUT: ${{ inputs.version }} and then uses it to compute VERSION, but
the job is also triggered by release: published (not just workflow_dispatch). On non-dispatch
events the inputs context is not guaranteed, so this can break the workflow or force it into the
"Could not determine version" failure path.

.github/workflows/release-downstreams.yml[15-23]
.github/workflows/release-downstreams.yml[35-47]
Best Practice: GitHub Actions docs

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`.github/workflows/release-downstreams.yml` references `${{ inputs.version }}` even for `release` events. Depending on GitHub Actions evaluation rules, this can fail evaluation or yield an empty string and break version resolution.
### Issue Context
The job is triggered by both `release.published` and `workflow_dispatch`. `inputs.version` should only be read for dispatch runs.
### Fix Focus Areas
- .github/workflows/release-downstreams.yml[15-47]
### Suggested fix
- Set `INPUT` from `github.event.inputs.version` (dispatch-only payload) and/or gate it behind an event-name check, e.g.:
- `INPUT: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}`
- or avoid `inputs` entirely: `INPUT: ${{ github.event.inputs.version }}`
- Optionally improve the error message to include `github.event_name` when version cannot be determined.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Workflow lacks feature flag 📘 Rule violation ☼ Reliability
Description
The new workflow runs automatically on release: published without a disable/enable mechanism,
which conflicts with the requirement that new features be behind a feature flag and disabled by
default. This can cause unintended behavior (auto-creating issues) immediately after merge.
Code

.github/workflows/release-downstreams.yml[R15-18]

+on:
+  release:
+    types: [published]
+  workflow_dispatch:
Evidence
PR Compliance ID 6 requires new features to be behind a feature flag and disabled by default. The
workflow is configured to trigger automatically on published releases, with no gating flag or opt-in
control shown in the added configuration.

.github/workflows/release-downstreams.yml[15-18]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow auto-runs on `release: published` and creates issues without an opt-in/feature-flag style control.
## Issue Context
Compliance requires new features to be behind a flag and disabled by default; for CI, this can be implemented as an explicit repository variable/secret check, an `if:` condition, or by limiting automatic triggers.
## Fix Focus Areas
- .github/workflows/release-downstreams.yml[15-18]
- .github/workflows/release-downstreams.yml[28-71]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Wrong GitHub path URLs 🐞 Bug ≡ Correctness
Description
The renderer always deep-links path/file using .../blob/HEAD/..., which breaks directory links
(they require .../tree/...) and will produce 404s for catalog entries that point to directories.
Code

.github/scripts/render-downstream-tracker.py[R64-81]

+def _render_item(item: dict, repo: str) -> str:
+    name = item["name"]
+    target_repo = item.get("repo")
+    # `path` and `file` are aliases that point at a specific file/dir
+    # inside the downstream repo (or inside this repo for `manual_ci`).
+    path = item.get("path") or item.get("file")
+    workflow = item.get("workflow")
+    notes = item.get("notes", "").strip()
+
+    # Primary link: deep-link to the file/dir if we know one, otherwise
+    # to the repo root. `HEAD` avoids pinning to a stale default-branch
+    # name (`main` vs `master` vs `develop`).
+    link = ""
+    if target_repo:
+        base = f"https://github.com/{target_repo}"
+        if path:
+            link = f" — [`{target_repo}/{path}`]({base}/blob/HEAD/{path})"
+        else:
Evidence
_render_item() explicitly treats path and file as aliases for a file/dir but always formats
the URL as /blob/HEAD/{path}. The catalog includes values that are clearly directory paths (e.g.
charts/stable/etherpad), so those links will be incorrect in the generated tracking issue.

.github/scripts/render-downstream-tracker.py[67-81]
docs/downstreams.yml[76-92]
.github/scripts/render-downstream-tracker.py[67-69]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The markdown renderer uses GitHub `/blob/` links for both files and directories. Directory targets should use `/tree/`, otherwise the issue body contains broken links.
### Issue Context
The catalog currently treats `path` and `file` as interchangeable. Several entries use `path` for directories (e.g. `charts/stable/etherpad`).
### Fix Focus Areas
- .github/scripts/render-downstream-tracker.py[64-86]
- docs/downstreams.yml[36-172]
### Suggested fix
- Stop treating `path` and `file` as aliases:
- Interpret `file:` as a file target → use `{base}/blob/HEAD/{file}`
- Interpret `path:` as a directory target → use `{base}/tree/HEAD/{path}`
- Update `docs/downstreams.yml` so file targets use `file:` (e.g., entries currently using `path:` for a single file like `ct/etherpad.sh` and `bbb-etherpad.placeholder.sh`).
- Add a small validation in the renderer that errors if both `path` and `file` are set.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Duplicate issue creation 🐞 Bug ☼ Reliability
Description
The workflow always runs gh issue create with no deduplication, so reruns and repeated
workflow_dispatch executions for the same version will create duplicate tracking issues.
Code

.github/workflows/release-downstreams.yml[R63-71]

+      - name: Open tracking issue
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          gh issue create \
+            --repo "$GITHUB_REPOSITORY" \
+            --title "Downstream bumps for ${{ steps.v.outputs.version }}" \
+            --label "release,downstream" \
+            --body-file '${{ steps.render.outputs.body-path }}'
Evidence
There is no step to search for an existing issue by title/label/version before creating a new one,
and the final step unconditionally creates a new issue each run.

.github/workflows/release-downstreams.yml[63-71]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Re-running the workflow (or manually dispatching it multiple times with the same version) will open multiple tracking issues for the same release.
### Issue Context
This workflow is intended to create "a single tracking issue on every release".
### Fix Focus Areas
- .github/workflows/release-downstreams.yml[63-71]
### Suggested fix
- Before `gh issue create`, query for an existing issue (open or recent) matching the version and labels, e.g.:
- `gh issue list --label release --label downstream --search "\"Downstream bumps for ${VERSION}\" in:title" --json number --jq '.[0].number'`
- If found, either:
- comment on the existing issue, or
- skip creation and output a message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
6. Missing YAML type validation 🐞 Bug ☼ Reliability
Description
If docs/downstreams.yml is empty or not a mapping, the renderer will crash (catalog.get(...))
and fail the workflow with a Python traceback instead of a clear validation error.
Code

.github/scripts/render-downstream-tracker.py[R33-37]

+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", [])
+
Evidence
yaml.safe_load() may return None or a non-dict type, but the code assumes a dict and calls
.get(). This can fail the release workflow if the YAML is accidentally malformed.

.github/scripts/render-downstream-tracker.py[33-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The renderer assumes the YAML top-level is a dict with a `downstreams` list of dict items. If the YAML is empty or malformed, it throws an unhelpful exception.
### Issue Context
This script is run in CI during releases; failures should be actionable.
### Fix Focus Areas
- .github/scripts/render-downstream-tracker.py[33-60]
### Suggested fix
- Add explicit type checks and raise a clear `ValueError`, e.g.:
- ensure `catalog` is a `dict`
- ensure `items` is a `list`
- ensure each item is a `dict` and has required keys like `name` and `update_type`
- In `main()`, catch `ValueError` and print a concise error to stderr before returning non-zero.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +33 to +36
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", [])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Comment thread .github/workflows/release-downstreams.yml
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>
@JohnMcLear
Copy link
Copy Markdown
Member Author

Qodo round-2 on this PR:

  1. inputs.version context unavailable — already addressed on current HEAD. The workflow reads github.event.inputs.version (dispatch-only payload), not inputs.version. See release-downstreams.yml:44.
  2. Python 4-space indent — intentional, this is PEP 8. The repo's 2-space style rule applies to JS/TS/YAML; applying it to Python would violate the language-wide convention every Python tool enforces (Black, Ruff, pycodestyle). Leaving as-is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant