From 52d3dc021d72528a90bd88b9316bd7cda0390d2c Mon Sep 17 00:00:00 2001 From: oobabooga <112222186+oobabooga@users.noreply.github.com> Date: Sun, 14 Jun 2026 00:25:55 -0300 Subject: [PATCH 1/2] Show PR titles in automated release notes (follow-up to #24) --- .github/workflows/unsloth-prebuilt.yml | 30 +++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/unsloth-prebuilt.yml b/.github/workflows/unsloth-prebuilt.yml index 512363ae9895..227bbc99ef73 100644 --- a/.github/workflows/unsloth-prebuilt.yml +++ b/.github/workflows/unsloth-prebuilt.yml @@ -165,9 +165,14 @@ jobs: [[ "$url" =~ $URL_RE ]] || { echo "refusing malformed PR url '$url' (expected https://github.com/ggml-org/llama.cpp/pull//commits/<40-hex-sha>)" >&2; exit 1; } NUM="${BASH_REMATCH[1]}"; SHA="${BASH_REMATCH[2]}" # plain assignment first so a gh failure aborts the run (set -e) - # instead of being swallowed by read and silently skipping the PR - STATE_HEAD="$(gh api "repos/ggml-org/llama.cpp/pulls/${NUM}" --jq '.state + " " + .head.sha + " " + (.commits|tostring)')" - read -r STATE HEAD N_COMMITS <<<"$STATE_HEAD" + # instead of being swallowed by a later read's exit status. Title can + # contain spaces, so it can't ride a space-delimited read -- pull each + # field out on its own. + PR_JSON="$(gh api "repos/ggml-org/llama.cpp/pulls/${NUM}")" + STATE="$(jq -r '.state' <<<"$PR_JSON")" + HEAD="$(jq -r '.head.sha' <<<"$PR_JSON")" + N_COMMITS="$(jq -r '.commits' <<<"$PR_JSON")" + TITLE="$(jq -r '.title' <<<"$PR_JSON")" if [ "$STATE" != "open" ]; then echo "skipping PR #${NUM} (${STATE}): $url" continue @@ -184,7 +189,7 @@ jobs: fi [ "$SHA" = "$HEAD" ] || echo "note: PR #${NUM} is pinned to ${SHA} but its head has moved to ${HEAD}" echo "including PR #${NUM} @ ${SHA}" - PRS="$(jq -c --arg n "$NUM" --arg s "$SHA" --arg u "$url" '. + [{number: ($n|tonumber), sha: $s, url: $u}]' <<<"$PRS")" + PRS="$(jq -c --arg n "$NUM" --arg s "$SHA" --arg u "$url" --arg t "$TITLE" '. + [{number: ($n|tonumber), sha: $s, url: $u, title: $t}]' <<<"$PRS")" done SRC_ARTIFACT="" @@ -500,18 +505,19 @@ jobs: PRS='${{ needs.resolve.outputs.prs }}' BASE='${{ needs.resolve.outputs.base }}' # Link the base to the upstream release tag (always resolves). Each PR - # is a bare ggml-org/llama.cpp# reference, which GitHub renders as a - # link to the upstream PR (shown inline as ggml-org#, title/state on - # hover) that resolves to upstream no matter which repo hosts the - # release; the short SHA links to that pin's commit-in-PR URL. We - # deliberately do not link the merged commit: in mix mode it is a - # throwaway merge made on the runner and never pushed anywhere, so any - # repo@sha URL for it 404s. + # line is " (#<n>, commit <sha>)": GitHub does not expand a bare + # reference into the PR title inline (only on hover), so we bake the + # title in ourselves. #<n> links to the upstream PR (full URL, so it + # resolves to upstream no matter which repo hosts the release, and + # GitHub still attaches its hovercard), and <sha> links to that pin's + # commit-in-PR URL. We deliberately do not link the merged commit: in + # mix mode it is a throwaway merge made on the runner and never pushed + # anywhere, so any repo@sha URL for it 404s. NOTES="Automated Unsloth llama.cpp CUDA (Linux + Windows) + ROCm + macOS prebuild for upstream [${BASE}](https://github.com/ggml-org/llama.cpp/releases/tag/${BASE})" if [ "$(jq length <<<"$PRS")" = 0 ]; then NOTES="${NOTES}." else - PR_LIST="$(jq -r 'map("- ggml-org/llama.cpp#\(.number) @ [\(.sha[0:7])](\(.url))") | join("\n")' <<<"$PRS")" + PR_LIST="$(jq -r 'map("- \(.title) ([#\(.number)](https://github.com/ggml-org/llama.cpp/pull/\(.number)), commit [\(.sha[0:7])](\(.url)))") | join("\n")' <<<"$PRS")" NOTES="$(printf '%s, merged with:\n\n%s' "$NOTES" "$PR_LIST")" fi From 7145458355aac290897d3fba89b7f7343908a014 Mon Sep 17 00:00:00 2001 From: oobabooga <112222186+oobabooga@users.noreply.github.com> Date: Sun, 14 Jun 2026 00:56:53 -0300 Subject: [PATCH 2/2] Pass PR JSON through env to keep release steps shell-safe --- .github/workflows/unsloth-prebuilt.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unsloth-prebuilt.yml b/.github/workflows/unsloth-prebuilt.yml index 227bbc99ef73..a59927d8edae 100644 --- a/.github/workflows/unsloth-prebuilt.yml +++ b/.github/workflows/unsloth-prebuilt.yml @@ -438,6 +438,11 @@ jobs: fi - name: Generate manifest + sha256 index + # prs carries upstream PR titles (arbitrary text), so pass it through env + # rather than inlining it: a title with a quote would otherwise break out + # of the shell command. Same handling in the publish step below. + env: + PRS_JSON: ${{ needs.resolve.outputs.prs }} run: | set -eux python3 tooling/scripts/unsloth/assemble_metadata.py \ @@ -445,7 +450,7 @@ jobs: --ref '${{ needs.resolve.outputs.ref }}' \ --source-repo '${{ needs.resolve.outputs.repo }}' \ --base-tag '${{ needs.resolve.outputs.base }}' \ - --pr-set '${{ needs.resolve.outputs.prs }}' \ + --pr-set "$PRS_JSON" \ --commit '${{ needs.resolve.outputs.commit }}' \ --dist dist --out dist \ --publish-repo "$GITHUB_REPOSITORY" @@ -498,11 +503,16 @@ jobs: - name: Publish GitHub release if: ${{ (github.event_name == 'schedule' || inputs.publish) && needs.resolve.outputs.exists != 'true' }} + # prs carries upstream PR titles (arbitrary text), so pass it through env + # rather than inlining it: a title with a quote would otherwise break out + # of the shell command. + env: + PRS_JSON: ${{ needs.resolve.outputs.prs }} run: | set -eux TAG='${{ needs.resolve.outputs.tag }}' REPO="$GITHUB_REPOSITORY" - PRS='${{ needs.resolve.outputs.prs }}' + PRS="$PRS_JSON" BASE='${{ needs.resolve.outputs.base }}' # Link the base to the upstream release tag (always resolves). Each PR # line is "<title> (#<n>, commit <sha>)": GitHub does not expand a bare