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