-
Notifications
You must be signed in to change notification settings - Fork 6
192 lines (173 loc) · 7.92 KB
/
Copy pathsync-upstream-version.yml
File metadata and controls
192 lines (173 loc) · 7.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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: TEMP failing step with continue-on-error
continue-on-error: true
run: exit 1
- name: TEMP does failure() fire
if: failure()
run: echo "FAILURE-CONTEXT-FIRED"
- 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'
# The pin has moved and the PR is open by this point, so a webhook that
# rejects us is not a failed sync. It also must not reach the failure
# notifier below, which posts to this same webhook and would fail the
# same way.
continue-on-error: 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
# Truncated after capture, not through `head`: closing the pipe
# early sends sed a SIGPIPE, which pipefail turns into a failed
# step once the report outgrows the pipe buffer -- that is, exactly
# when the gap is big enough to be worth announcing.
summary="$(sed -n '/## Summary/,$p' reports/parity_report.md)"
summary="${summary:0: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 --fail --connect-timeout 10 --max-time 30 \
-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 --fail --connect-timeout 10 --max-time 30 \
-X POST -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK"