sync-upstream-version #29
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: sync-upstream-version | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "0 12 * * 1" | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| sync: | |
| # Runs by default. Set the repository variable DISABLE_UPSTREAM_VERSION_SYNC | |
| # to 'true' to stop it. | |
| if: vars.DISABLE_UPSTREAM_VERSION_SYNC != 'true' | |
| runs-on: ubuntu-latest | |
| env: | |
| # Only whether the webhook is configured belongs at job scope: a step | |
| # `if:` cannot read the `secrets` context, but the secret itself has no | |
| # business in the environment of steps that install and run upstream | |
| # code. The notification steps map the secret in themselves. | |
| DISCORD_CONFIGURED: ${{ secrets.DISCORD_WEBHOOK != '' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Fetch latest upstream release | |
| id: upstream | |
| run: | | |
| set -euo pipefail | |
| api="https://api.github.com/repos/caronc/apprise/releases/latest" | |
| tag="$(curl -sSL "$api" | jq -r '.tag_name')" | |
| if [ -z "$tag" ] || [ "$tag" = "null" ]; then | |
| echo "Failed to read latest upstream release tag." >&2 | |
| exit 1 | |
| fi | |
| version="${tag#v}" | |
| pinned="$(sed -n 's/^var UpstreamVersion = "\([^"]*\)"/\1/p' internal/version/version.go)" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "pinned=$pinned" >> "$GITHUB_OUTPUT" | |
| if [ "$version" = "$pinned" ]; then | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| echo "Already pinned to $pinned." >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Update UpstreamVersion | |
| if: steps.upstream.outputs.changed == 'true' | |
| env: | |
| UPSTREAM_VERSION: ${{ steps.upstream.outputs.version }} | |
| run: | | |
| python - <<'PY' | |
| import os | |
| import re | |
| from pathlib import Path | |
| path = Path("internal/version/version.go") | |
| data = path.read_text() | |
| version = os.environ["UPSTREAM_VERSION"] | |
| pattern = r'(var\s+UpstreamVersion\s*=\s*")([^"]+)(")' | |
| updated, count = re.subn(pattern, r'\g<1>' + version + r'\g<3>', data) | |
| if count == 0: | |
| raise SystemExit("UpstreamVersion not found in version.go") | |
| if updated != data: | |
| path.write_text(updated) | |
| PY | |
| # The bump alone only moves a constant. Measuring the gap it opens is the | |
| # point: without this the PR looks trivial and the work stays invisible | |
| # until someone reads a red parity run. | |
| - name: Check out the new upstream release | |
| if: steps.upstream.outputs.changed == 'true' | |
| env: | |
| UPSTREAM_TAG: ${{ steps.upstream.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| git -C .. clone --depth 1 --branch "$UPSTREAM_TAG" https://github.com/caronc/apprise apprise | |
| - name: Set up Go | |
| if: steps.upstream.outputs.changed == 'true' | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| - name: Set up Python | |
| if: steps.upstream.outputs.changed == 'true' | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.14" | |
| - name: Install parity dependencies | |
| if: steps.upstream.outputs.changed == 'true' | |
| run: bash scripts/ci/setup_parity_env.sh | |
| # The report is written whether or not the suite passes, so a red run | |
| # still names the providers and schemas that need porting. | |
| - name: Measure the gap the new release opens | |
| if: steps.upstream.outputs.changed == 'true' | |
| continue-on-error: true | |
| run: bash scripts/ci/run_parity_tests.sh | |
| - name: Publish gap summary | |
| if: steps.upstream.outputs.changed == 'true' | |
| run: cat reports/parity_report.md >> "$GITHUB_STEP_SUMMARY" | |
| - name: Compose pull request body | |
| if: steps.upstream.outputs.changed == 'true' | |
| env: | |
| UPSTREAM_TAG: ${{ steps.upstream.outputs.tag }} | |
| PINNED: ${{ steps.upstream.outputs.pinned }} | |
| run: | | |
| { | |
| echo "Syncs \`internal/version.UpstreamVersion\` from \`$PINNED\` to upstream \`$UPSTREAM_TAG\`." | |
| echo | |
| echo "Moving the pin does not port anything: it points the parity suite at the new" | |
| echo "release so the work becomes visible. The gap measured against this release is" | |
| echo "below. Parity CI on this PR will be red until every row is closed." | |
| echo | |
| sed -n '/## Summary/,$p' reports/parity_report.md | |
| } > reports/pr_body.md | |
| - name: Create pull request | |
| id: pr | |
| if: steps.upstream.outputs.changed == 'true' | |
| uses: peter-evans/create-pull-request@v8 | |
| with: | |
| branch: chore/sync-upstream-version | |
| delete-branch: true | |
| commit-message: "chore: sync upstream apprise version to ${{ steps.upstream.outputs.tag }}" | |
| title: "chore: sync upstream apprise version to ${{ steps.upstream.outputs.tag }}" | |
| body-path: reports/pr_body.md | |
| add-paths: internal/version/version.go | |
| # A sync PR nobody looks at is the same as no sync at all, so the release | |
| # and the gap it opens get announced rather than left in the PR list. The | |
| # payload is built with jq because the report text is not ours to trust | |
| # as JSON. | |
| - name: Announce the new upstream release | |
| if: steps.upstream.outputs.changed == 'true' && env.DISCORD_CONFIGURED == 'true' | |
| env: | |
| DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} | |
| UPSTREAM_TAG: ${{ steps.upstream.outputs.tag }} | |
| PINNED: ${{ steps.upstream.outputs.pinned }} | |
| PR_URL: ${{ steps.pr.outputs.pull-request-url }} | |
| run: | | |
| set -euo pipefail | |
| summary="" | |
| if [ -f reports/parity_report.md ]; then | |
| summary="$(sed -n '/## Summary/,$p' reports/parity_report.md | head -c 1200)" | |
| fi | |
| if [ -z "$summary" ]; then | |
| summary="Parity report not generated; see the run log." | |
| fi | |
| content="$(printf 'upstream apprise %s is out (pinned: %s).\n%s\n\n%s' \ | |
| "$UPSTREAM_TAG" "$PINNED" "${PR_URL:-no sync PR was opened}" "$summary")" | |
| jq -n --arg content "$content" '{content: $content}' \ | |
| | curl -sS -X POST -H "Content-Type: application/json" -d @- "$DISCORD_WEBHOOK" | |
| # Without this the checker can break silently and the pin just quietly | |
| # stops moving, which is the failure the checker exists to prevent. | |
| - name: Notify Discord on sync failure | |
| if: failure() && env.DISCORD_CONFIGURED == 'true' | |
| env: | |
| DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} | |
| run: | | |
| run_url="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| payload=$(printf '{"content":"upstream version sync failed for %s: %s"}' "$GITHUB_REPOSITORY" "$run_url") | |
| curl -sS -X POST -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK" |