Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 97 additions & 23 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ jobs:
exit 1
fi

# Generate notes against the previous tag from the same release channel.
# Channel previous tag (stable↔stable / preview↔preview) for the Full Changelog link.
if [[ "$RELEASE_VERSION" == *-preview.* ]]; then
previous_tag="$(
git tag --merged HEAD --list 'v[0-9]*' --sort=-v:refname |
Expand Down Expand Up @@ -329,47 +329,121 @@ jobs:
# tag need not exist yet. Preflight already rejects an existing GitHub Release for
# non-dry runs, so this step only creates.
notes_file="$(mktemp)"
pr_notes=""
if [ -n "$previous_tag" ]; then
carried_file="$(mktemp)"
delta_file="$(mktemp)"
commits_file="$(mktemp)"
: > "$carried_file"
: > "$delta_file"
: > "$commits_file"

# Stable releases after matching previews: aggregate every matching preview
# changelog (oldest→newest; each preview body is incremental), then only
# generate-notes / commits for the post-preview delta when the newest
# *successfully carried* preview tag is an ancestor of this commit. Never
# advance the baseline to a later preview that is missing/empty — that would
# drop the gap between the last carried preview and that later tag.
notes_range_start="$previous_tag"
if [[ "$RELEASE_VERSION" != *-preview.* ]]; then
preview_carry_tags="$(
git tag --list "v${RELEASE_VERSION}-preview.*" |
bun scripts/release-notes.ts matching-preview-tags "$RELEASE_VERSION"
)"
newest_carried_preview_tag=""
carried_part_files=()
# Probe repo readability once so a token/permission 404 cannot be
# mistaken for "this preview tag has no release".
gh api "repos/${GITHUB_REPOSITORY}" --jq '.full_name' > /dev/null
view_err="$(mktemp)"
while IFS= read -r preview_carry_tag; do
[ -n "$preview_carry_tag" ] || continue
: > "$view_err"
set +e
# Prefer HTTP status over stderr prose: auth failures also say "Not Found".
gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${preview_carry_tag}" \
--jq '.body' > "${carried_file}.raw" 2>"$view_err"
view_status=$?
set -e
if [ "$view_status" -eq 0 ]; then
bun scripts/release-notes.ts strip-carried "${carried_file}.raw" > "${carried_file}.one"
if bun scripts/release-notes.ts has-meaningful "${carried_file}.one"; then
part_file="${carried_file}.part.${#carried_part_files[@]}"
cp "${carried_file}.one" "$part_file"
carried_part_files+=("$part_file")
newest_carried_preview_tag="$preview_carry_tag"
echo "::notice::Carrying preview release notes from ${preview_carry_tag} into ${release_tag}"
else
echo "::notice::Preview release ${preview_carry_tag} has no usable changelog after strip; leaving carried baseline unchanged for this tag"
fi
elif grep -qE 'HTTP 404' "$view_err"; then
echo "::notice::Matching preview tag ${preview_carry_tag} has no GitHub Release; skipping"
else
echo "::error::Failed to look up GitHub Release for ${preview_carry_tag} (operational error, not a missing release)"
cat "$view_err" >&2 || true
exit 1
fi
done <<< "$preview_carry_tags"

if [ "${#carried_part_files[@]}" -gt 0 ]; then
bun scripts/release-notes.ts join-carried --out "$carried_file" "${carried_part_files[@]}"
fi

if [ -n "$newest_carried_preview_tag" ]; then
if git merge-base --is-ancestor "$newest_carried_preview_tag" "$GITHUB_SHA"; then
notes_range_start="$newest_carried_preview_tag"
echo "::notice::Using preview tag ${newest_carried_preview_tag} as notes/commits baseline (ancestor of ${GITHUB_SHA})"
else
echo "::notice::Preview tag ${newest_carried_preview_tag} is not an ancestor of ${GITHUB_SHA}; keeping channel baseline ${previous_tag:-none} for generate-notes/commits"
fi
fi
fi

if [ -n "$notes_range_start" ]; then
generate_notes_api=(
"repos/${GITHUB_REPOSITORY}/releases/generate-notes"
-f "tag_name=${release_tag}"
-f "target_commitish=${GITHUB_SHA}"
-f "previous_tag_name=${previous_tag}"
-f "previous_tag_name=${notes_range_start}"
)
# Fail closed: missing PR categories is a broken release note, not a soft skip.
pr_notes="$(gh api "${generate_notes_api[@]}" --jq '.body')"
# Drop generate-notes' trailing compare link; we re-append it after the commit list.
pr_notes="$(printf '%s\n' "$pr_notes" | sed '/^\*\*Full Changelog\*\*:/d')"
printf '%s\n' "$pr_notes" | sed '/^\*\*Full Changelog\*\*:/d' > "$delta_file"
else
# First release on this channel: never call generate-notes without previous_tag_name.
# GitHub would baseline the newest repo tag, which may belong to the other channel.
echo "::notice::No previous channel tag; skipping generate-notes (commits-only notes)"
fi

if [ -n "$previous_tag" ]; then
commit_range="${previous_tag}..${GITHUB_SHA}"
commits="$(git log --pretty=format:'- %s (%h)' "$commit_range")"
if [ -n "$notes_range_start" ]; then
commit_range="${notes_range_start}..${GITHUB_SHA}"
git log --pretty=format:'- %s (%h)' "$commit_range" > "$commits_file"
Comment on lines +417 to +419

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid two-dot logs from a divergent preview tag

When the selected preview tag is not an ancestor of the stable commit—a case the workflow explicitly says is common—this two-dot range is not a chronological preview-to-stable delta. Under Git's revision-range semantics, A..B includes every commit reachable from B but not A, so the release's ## Commits section can contain unrelated main-side history all the way back to the branches' merge base. Use the preview tag as the commit baseline only after git merge-base --is-ancestor succeeds; otherwise retain an ancestor such as the previous stable tag.

Useful? React with 👍 / 👎.

else
# First release on a channel has no previous tag; cap history so the notes
# body stays within GitHub release size limits.
commits="$(git log --pretty=format:'- %s (%h)' --max-count=100 "$GITHUB_SHA")"
git log --pretty=format:'- %s (%h)' --max-count=100 "$GITHUB_SHA" > "$commits_file"
fi
# `--pretty=format:` omits the trailing newline; normalize for downstream readers.
if [ -s "$commits_file" ]; then
printf '\n' >> "$commits_file"
fi

{
printf '%s\n\n' "$npm_metadata"
if [ -n "$(printf '%s' "$pr_notes" | tr -d '[:space:]')" ]; then
printf '%s\n\n' "$pr_notes"
fi
if [ -n "$commits" ]; then
printf '## Commits\n\n'
printf '%s\n\n' "$commits"
fi
if [ -n "$previous_tag" ]; then
printf '**Full Changelog**: https://github.com/%s/compare/%s...%s\n' \
"$GITHUB_REPOSITORY" "$previous_tag" "$release_tag"
fi
} > "$notes_file"
assemble_args=(
bun scripts/release-notes.ts assemble
--npm-metadata "$npm_metadata"
--carried "$carried_file"
--delta "$delta_file"
--commits "$commits_file"
--out "$notes_file"
--compare-to "$release_tag"
--repository "$GITHUB_REPOSITORY"
)
# Prefer the stable-channel previous tag for the compare link when present.
if [ -n "$previous_tag" ]; then
assemble_args+=(--compare-from "$previous_tag")
elif [ -n "$notes_range_start" ]; then
assemble_args+=(--compare-from "$notes_range_start")
fi
"${assemble_args[@]}"

if [ -z "$existing_tag_sha" ]; then
git tag "$release_tag" "$GITHUB_SHA"
Expand Down
Loading
Loading