Skip to content

Commit eb70834

Browse files
authored
ci: Include Syncthing changelog in published release notes (#300)
1 parent 651eb79 commit eb70834

1 file changed

Lines changed: 111 additions & 3 deletions

File tree

‎.github/workflows/build-syncthing-macos.yml‎

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,121 @@ jobs:
164164
echo "published=false" >> "$GITHUB_OUTPUT"
165165
exit 0
166166
fi
167-
notes="Bundles Syncthing v${stver}.
167+
# ------------------------------------------------------------------
168+
# Release notes
169+
#
170+
# The GitHub release body is what users actually read:
171+
# cmd/ghreleases2appcast renders it into the appcast <description>,
172+
# which is exactly what the Sparkle update dialog shows. So inline the
173+
# Syncthing changelog here rather than only linking to it.
174+
# ------------------------------------------------------------------
168175
169-
Upstream release notes: https://github.com/syncthing/syncthing/releases/tag/v${stver}"
176+
# True when version $1 is not newer than version $2.
177+
ver_le() { [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -1)" = "$1" ]; }
178+
179+
# Syncthing version bundled by the latest release of this repository,
180+
# empty when there is no previous release.
181+
previously_bundled_version() {
182+
local tag
183+
tag="$(gh release list --repo "$GH_REPO" --limit 1 \
184+
--json tagName --jq '.[0].tagName // ""' 2>/dev/null || true)"
185+
tag="${tag#v}"
186+
printf '%s' "${tag%-*}"
187+
}
188+
189+
# Stable upstream versions newer than $1, up to and including $2,
190+
# newest first. A bundle does not always advance by one version
191+
# (v2.1.2-1 -> v2.1.5-1 skipped upstream v2.1.3), and the notes of a
192+
# skipped release would otherwise never reach users.
193+
upstream_versions_since() {
194+
local prev="$1" cur="$2" v picked=""
195+
for v in $(gh api --paginate repos/syncthing/syncthing/releases \
196+
--jq '.[] | select(.draft == false and .prerelease == false) | .tag_name' \
197+
2>/dev/null | sed 's/^v//' || true); do
198+
ver_le "$v" "$cur" || continue
199+
if [ -n "$prev" ] && ver_le "$v" "$prev"; then
200+
continue
201+
fi
202+
picked="$picked $v"
203+
done
204+
printf '%s\n' $picked | sort -rV
205+
}
206+
207+
# Release notes of upstream Syncthing $1, empty when unavailable.
208+
upstream_release_body() {
209+
gh release view "v$1" --repo syncthing/syncthing \
210+
--json body --jq '.body // ""' 2>/dev/null || true
211+
}
212+
213+
# Append a changelog link and the release notes of every version in $@
214+
# to $links_file and $notes_file, then echo the versions that fit.
215+
# GitHub caps a release body at 125 kB, so stop well short of that.
216+
collect_changelogs() {
217+
local v body per_version_headings=no included=""
218+
if [ $# -gt 1 ]; then
219+
per_version_headings=yes
220+
fi
221+
for v in "$@"; do
222+
body="$(upstream_release_body "$v")"
223+
[ -n "$body" ] || continue
224+
if [ $(( $(wc -c < "$notes_file") + ${#body} )) -gt 100000 ]; then
225+
printf '\n---\n\nOlder release notes: %s\n' \
226+
"https://github.com/syncthing/syncthing/releases" >> "$notes_file"
227+
break
228+
fi
229+
{
230+
printf '\n---\n\n'
231+
# h1: upstream bodies use h2 as their top level.
232+
if [ "$per_version_headings" = yes ]; then
233+
printf '# Syncthing v%s\n\n' "$v"
234+
fi
235+
printf '%s\n' "$body"
236+
} >> "$notes_file"
237+
printf -- '- [Syncthing v%s changelog](https://github.com/syncthing/syncthing/releases/tag/v%s)\n' \
238+
"$v" "$v" >> "$links_file"
239+
included="$included $v"
240+
done
241+
printf '%s\n' $included
242+
}
243+
244+
links_file=changelog-links.md
245+
notes_file=upstream-notes.md
246+
: > "$links_file"
247+
: > "$notes_file"
248+
249+
# Version lists are whitespace separated, hence the unquoted expansions.
250+
versions="$(upstream_versions_since "$(previously_bundled_version)" "$stver")"
251+
[ -n "$versions" ] || versions="$stver"
252+
included="$(collect_changelogs $versions)"
253+
newest="$(printf '%s\n' $included | head -1)"
254+
oldest="$(printf '%s\n' $included | tail -1)"
255+
256+
# The DMG is already built, signed and notarized at this point, so an
257+
# unreachable GitHub API must not fail the job: publish with a plain
258+
# link to the release notes instead.
259+
if [ ! -s "$notes_file" ]; then
260+
echo "::warning::Could not read upstream release notes for v${stver}; publishing with the link only."
261+
fi
262+
263+
{
264+
echo "Bundles Syncthing v${stver}."
265+
echo
266+
if [ -s "$notes_file" ]; then
267+
if [ "$newest" = "$oldest" ]; then
268+
echo "Here is what's new in it:"
269+
else
270+
echo "Here is what's new in v${oldest}–v${newest}:"
271+
fi
272+
echo
273+
cat "$links_file" "$notes_file"
274+
else
275+
echo "Release notes: https://github.com/syncthing/syncthing/releases/tag/v${stver}"
276+
fi
277+
} > release-notes.md
170278
gh release create "$tag" \
171279
--target "$GITHUB_SHA" \
172280
--title "$tag" \
173-
--notes "$notes" \
281+
--notes-file release-notes.md \
174282
"$dmg"
175283
echo "published=true" >> "$GITHUB_OUTPUT"
176284

0 commit comments

Comments
 (0)