diff --git a/.github/scripts/render-downstream-tracker.py b/.github/scripts/render-downstream-tracker.py new file mode 100755 index 00000000000..5624f4d6cb8 --- /dev/null +++ b/.github/scripts/render-downstream-tracker.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""Render the downstream-bump tracking issue body from docs/downstreams.yml. + +Called from .github/workflows/release-downstreams.yml. Kept as a +standalone script (rather than inline yaml-munging in the workflow) so +the format is easy to tweak without re-running CI to eyeball it โ€” run +locally with: + + python3 .github/scripts/render-downstream-tracker.py \\ + docs/downstreams.yml 2.6.1 ether/etherpad-lite + +Usage: render-downstream-tracker.py +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import yaml + +GROUPS: list[tuple[str, str]] = [ + ("automatic", "๐Ÿš€ Automatic (this repo handles it)"), + ("manual_ci", "๐Ÿงฉ Manual bump in this repo"), + ("external_auto", "๐Ÿค– Externally automated"), + ("external_pr", "โœ‰๏ธ Needs a PR we send"), + ("external_issue", "๐Ÿ“จ Needs an issue we file"), + ("external_maintainer", "๐Ÿค Maintained externally โ€” poke if stale"), + ("stale", "โš ๏ธ Known stale โ€” informational only"), +] + + +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", []) + + 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)) + + return "\n".join(out) + + +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: + link = f" โ€” [`{target_repo}`]({base})" + if workflow: + workflow_url = f"https://github.com/{repo}/blob/develop/{workflow}" + link += f" ยท [workflow]({workflow_url})" + + lines = [f"- [ ] **{name}**{link}"] + if notes: + # Indent notes under the checkbox so GitHub renders them as part + # of the list item rather than a sibling paragraph. + for note_line in notes.splitlines(): + lines.append(f" {note_line}") + lines.append("") + return "\n".join(lines) + + +def main() -> int: + if len(sys.argv) != 4: + print(__doc__, file=sys.stderr) + return 2 + catalog_path = Path(sys.argv[1]) + version = sys.argv[2] + repo = sys.argv[3] + print(render(catalog_path, version, repo)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/release-downstreams.yml b/.github/workflows/release-downstreams.yml new file mode 100644 index 00000000000..a08a7e8ea8f --- /dev/null +++ b/.github/workflows/release-downstreams.yml @@ -0,0 +1,77 @@ +name: Release โ€” downstream bump tracker + +# Opens a single tracking issue on every release that lists every +# downstream distribution of Etherpad (see docs/downstreams.yml) with +# hints on whether they update automatically or need a manual PR. +# +# Rationale: external catalogs (CasaOS, TrueCharts, BBB, Unraid, โ€ฆ) go +# stale because nobody remembers to poke them at release time. This +# workflow turns "remember to update every downstream" into a checklist +# we can close methodically. +# +# To test before a real release: run `Actions โ†’ Release โ€” downstream +# bump tracker โ†’ Run workflow`, supply a version (e.g. `2.6.1-test`). + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: Version string to use (e.g. 2.6.1). Defaults to the release tag. + required: true + +permissions: + contents: read + issues: write + +jobs: + open-tracking-issue: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Resolve version + id: v + env: + TAG: ${{ github.event.release.tag_name }} + # `inputs.version` only exists on workflow_dispatch; reading it on + # release events can yield an empty string or a context-evaluation + # error depending on GitHub Actions behavior. Pull the dispatch + # payload directly via github.event.inputs so release runs stay + # clean. + INPUT: ${{ github.event.inputs.version }} + EVENT: ${{ github.event_name }} + run: | + VERSION="${TAG:-$INPUT}" + VERSION="${VERSION#v}" + if [ -z "${VERSION}" ]; then + echo "Could not determine version (event=${EVENT})." >&2 + exit 1 + fi + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + + - name: Render issue body + id: render + env: + VERSION: ${{ steps.v.outputs.version }} + run: | + python3 -m pip install --quiet pyyaml + BODY=$(mktemp) + python3 .github/scripts/render-downstream-tracker.py \ + docs/downstreams.yml \ + "$VERSION" \ + "$GITHUB_REPOSITORY" \ + > "$BODY" + echo "body-path=$BODY" >> "$GITHUB_OUTPUT" + + - 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 }}' diff --git a/docs/downstreams.yml b/docs/downstreams.yml new file mode 100644 index 00000000000..a9b2ba9757c --- /dev/null +++ b/docs/downstreams.yml @@ -0,0 +1,172 @@ +# Downstream distribution catalog for Etherpad. +# +# This file is the single source of truth for every place Etherpad is +# packaged or listed outside this repository. It is consumed by +# .github/workflows/release-downstreams.yml, which opens a tracking issue +# on every release so maintainers can work through the list and keep +# downstream catalogs from going stale. +# +# When you add a new downstream, put it here first โ€” the release +# workflow will then remind us to update it on the next tag. +# +# update_type values: +# automatic โ€” handled by a workflow in this repo on tag push +# (Docker Hub, Snap, Debian .deb, Home Assistant +# add-on image). No manual step required; the +# checklist item is a smoke-test verification. +# manual_ci โ€” controlled by a file in this repo that a human +# needs to bump (e.g. the HA add-on config.yaml +# `version:` field). The workflow hints which +# file to edit. +# external_auto โ€” a downstream repo's own automation (Renovate, +# a similar bot, or a runtime `check_for_gh_release` +# call) detects new Docker/GitHub tags. No action +# required unless the bot falls behind. +# external_pr โ€” a downstream repo requires a manual PR to bump +# a pinned version. A maintainer opens that PR. +# external_issue โ€” a downstream repo accepts issues rather than PRs +# for version bumps (maintainer-driven). +# external_maintainer โ€” entirely out of our hands; the downstream has an +# owner we can ping but not patch. Keep the entry +# as a reminder to poke them. +# stale โ€” the downstream hasn't tracked upstream in a long +# time. Kept in the list so nobody re-discovers it +# and assumes it's current. + +downstreams: + # ---- In-repo, fully automated ----------------------------------------- + - name: Official Docker image + repo: ether/etherpad-lite + update_type: automatic + workflow: .github/workflows/docker.yml + notes: | + Smoke test: `docker pull etherpad/etherpad:` works and the + container exposes `/health` on 9001 after boot. + + - name: Snap (snapcraft.io) + repo: ether/etherpad-lite + update_type: automatic + workflow: .github/workflows/snap-publish.yml + notes: | + Tag triggers edge publish; stable promotion requires a manual + approval in the `snap-store-stable` GitHub Environment. Smoke test: + `snap info etherpad-lite` shows the new version on the `stable` + channel within 24h. + + - name: Debian .deb (GitHub Releases) + repo: ether/etherpad-lite + update_type: automatic + workflow: .github/workflows/deb-package.yml + notes: | + Tag triggers build; artefacts are attached to the GitHub Release. + Smoke test: `dpkg -i etherpad-lite__amd64.deb` on a clean + Ubuntu 24.04 VM, `curl /health` returns 200. + + - name: Home Assistant add-on (GHCR images) + repo: ether/etherpad-lite + update_type: manual_ci + file: packaging/home-assistant/etherpad/config.yaml + workflow: .github/workflows/hassio-addon.yml + notes: | + Bump the `version:` field in `packaging/home-assistant/etherpad/config.yaml` + and merge to develop. The workflow multi-arch builds and pushes to + GHCR. If the upstream Docker image changes base OS, verify the + add-on Dockerfile's `COPY --from=upstream` still works. + + # ---- External, auto-tracking ------------------------------------------ + - name: Umbrel App Store + repo: getumbrel/umbrel-apps + path: etherpad + update_type: external_auto + notes: | + Renovate tracks the Docker image tag. No action needed if the app + bumps within a week of release; otherwise file an issue. + + - name: TrueCharts + repo: trueforge-org/truecharts + path: charts/stable/etherpad + update_type: external_auto + notes: | + Renovate tracks the official `docker.io/etherpad/etherpad` image + (after PR 47234 merged). Verify the chart catalog shows the new + `appVersion:` within a week. + + - name: Proxmox VE Helper-Script + repo: community-scripts/ProxmoxVED + path: ct/etherpad.sh + update_type: external_auto + notes: | + The script uses `check_for_gh_release` against `ether/etherpad-lite` + at user runtime, so there's no per-release bump โ€” users re-running + the helper script pick up the latest GitHub release automatically. + + # ---- External, maintainer-driven -------------------------------------- + - name: Cloudron + repo: cloudron-io/etherpad.cloudronapp + update_type: external_maintainer + notes: | + Cloudron's automation typically publishes within a week. If the + store page lags by more than 2 weeks, post on + . + + - name: YunoHost + repo: YunoHost-Apps/etherpad_ynh + update_type: external_maintainer + notes: | + Actively maintained; usually bumps automatically. If no release + appears within 2 weeks, file an issue on the repo tagging the + current maintainer. + + - name: Nextcloud Ownpad + repo: otetard/ownpad + update_type: external_maintainer + notes: | + External integration plugin; generally forward-compatible with new + Etherpad versions. Only relevant if a breaking API change lands. + + # ---- External, per-release PR needed ---------------------------------- + - name: CasaOS App Store + repo: IceWhaleTech/CasaOS-AppStore + path: Apps/Etherpad/docker-compose.yml + update_type: external_pr + notes: | + Docker image tag is pinned in the compose file. File a PR bumping + `image: etherpad/etherpad:` and the `version` string in + the `x-casaos` metadata. + + - name: BigBlueButton + repo: bigbluebutton/bigbluebutton + path: bbb-etherpad.placeholder.sh + update_type: external_pr + notes: | + Pinned to a specific tag in the placeholder clone script. Major + version bumps may require additional changes in + `build/packages-template/bbb-etherpad/build.sh` (npm โ†’ pnpm, + Node engine bump). File a PR or issue against `develop`. + + - name: Unraid Community Apps + repo: selfhosters/unRAID-CA-templates + update_type: external_maintainer + notes: | + Community-maintained XML templates. If the template falls behind, + file an issue or PR on the template repo, or contact the + maintainer in the Unraid forum thread for Etherpad. + + # ---- Known stale / low-signal ----------------------------------------- + - name: Sandstorm + repo: sandstorm-io/sandstorm + update_type: stale + notes: | + Market listing hasn't moved since 2015. A full repack requires + vagga-based Sandstorm packaging. Noted here so nobody tries the + app expecting it to be current; removing the listing is a separate + conversation with the Sandstorm team. + + - name: TrueNAS SCALE (legacy) + repo: trueforge-org/truecharts + update_type: stale + notes: | + The TrueNAS SCALE app catalog (iXsystems) was historically sourced + from TrueCharts; both have since diverged. TrueCharts' own + catalog is the authoritative source now; iXsystems ships their + own apps independently.