Skip to content

Commit 94f6d54

Browse files
committed
ci(release): adopt changelog fragments + release automation
Import the changie fragment workflow and release automation to the v0.9 branch (independently from the main-branch PR #1839, whose commits would not cherry-pick cleanly here). - Add .changie.yaml, .changes/header.tpl.md, hack/changelog-fold.sh and the changelog* Makefile targets (changie install, changelog, changelog-preview, changelog-release). - Migrate the three pending ## [Unreleased] entries to fragments under .changes/unreleased/ and drop the Unreleased block; CHANGELOG.md is now the source of truth for released versions, folded in place at release time. - Add release-prepare.yaml (dispatch -> prepare PR) and release-publish.yaml (auto tag + GitHub Release on merge, via KAIBOT_TOKEN so push-artifacts.yaml and on-release.yaml fire), plus changelog-comment.yaml. - Switch validate-changelog.yaml to require a fragment instead of a CHANGELOG.md edit. - Update CONTRIBUTING.md wording. Action pins kept at the branch's house style (actions/checkout@v6, setup-go@v5). Signed-off-by: SiorMeir <msior@nvidia.com>
1 parent 16836ab commit 94f6d54

14 files changed

Lines changed: 360 additions & 21 deletions

.changes/header.tpl.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

.changes/unreleased/.gitkeep

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Fixed
2+
body: |-
3+
Block NaN value for fraction in the pod admission [#1798](https://github.com/kai-scheduler/KAI-Scheduler/issues/1798) [davidLif](https://github.com/davidLif)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Fixed
2+
body: |-
3+
In the fractional admission checks, check that the fractional value can be parsed as a quantity. [#1798](https://github.com/kai-scheduler/KAI-Scheduler/issues/1798) [davidLif](https://github.com/davidLif)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Fixed
2+
body: |-
3+
Fixed GPU-sharing pods with dotted pod names generating invalid ConfigMap-backed volume names. Volume names are now sanitized to valid DNS labels while preserving original ConfigMap references used for shared-GPU injection.

.changie.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
changesDir: .changes
2+
unreleasedDir: unreleased
3+
headerPath: header.tpl.md
4+
changelogPath: CHANGELOG.md
5+
versionExt: md
6+
# Timestamped, non-serial filenames so concurrent PRs and backports never collide.
7+
fragmentFileFormat: '{{.Kind}}-{{.Time.Format "20060102-150405"}}'
8+
versionFormat: '## [{{.Version}}] - {{.Time.Format "2006-01-02"}}'
9+
kindFormat: '### {{.Kind}}'
10+
changeFormat: '- {{.Body}}{{if .Custom.Issue}} [#{{.Custom.Issue}}](https://github.com/kai-scheduler/KAI-Scheduler/issues/{{.Custom.Issue}}){{end}}{{if .Custom.Author}} [{{.Custom.Author}}](https://github.com/{{.Custom.Author}}){{end}}'
11+
kinds:
12+
- label: Added
13+
- label: Changed
14+
- label: Fixed
15+
- label: Removed
16+
custom:
17+
- key: Issue
18+
label: Issue or PR number (optional, e.g. 1817)
19+
type: string
20+
optional: true
21+
- key: Author
22+
label: GitHub username to credit (optional)
23+
type: string
24+
optional: true
25+
newlines:
26+
afterChangelogHeader: 0
27+
beforeChangelogVersion: 1
28+
beforeKind: 1
29+
endOfVersion: 1
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2026 NVIDIA CORPORATION
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# After a PR that added changelog fragment(s) is merged, KAIBOT comments on the PR with a
5+
# link to each fragment. Pending fragments are folded into CHANGELOG.md at release time
6+
# (`make changelog-release`), so this confirms to the contributor that their entry was
7+
# recorded even though CHANGELOG.md did not change in the PR.
8+
9+
name: Changelog Comment
10+
11+
on:
12+
pull_request_target:
13+
types: [closed]
14+
branches:
15+
- main
16+
- 'v*.*'
17+
paths:
18+
- '.changes/unreleased/**'
19+
20+
permissions:
21+
pull-requests: write
22+
23+
jobs:
24+
comment:
25+
name: Comment changelog fragment link
26+
if: github.event.pull_request.merged == true
27+
runs-on: ubuntu-latest
28+
steps:
29+
- name: Comment with fragment link(s)
30+
env:
31+
GH_TOKEN: ${{ secrets.KAIBOT_TOKEN }}
32+
REPO: ${{ github.repository }}
33+
PR: ${{ github.event.pull_request.number }}
34+
SHA: ${{ github.event.pull_request.merge_commit_sha }}
35+
run: |
36+
set -euo pipefail
37+
files=$(gh api "repos/$REPO/pulls/$PR/files" --paginate \
38+
--jq '.[] | select(.status=="added") | .filename' \
39+
| grep -E '^\.changes/unreleased/[^/]+\.ya?ml$' || true)
40+
if [ -z "$files" ]; then
41+
echo "No changelog fragment added by this PR; skipping comment."
42+
exit 0
43+
fi
44+
{
45+
echo "### 📝 Changelog fragment recorded"
46+
echo ""
47+
echo "Thanks! This PR added the changelog fragment(s) below. Pending fragments are folded into \`CHANGELOG.md\` at **release time**, so it was intentionally not modified by this PR — your entry will appear in the next release:"
48+
echo ""
49+
while IFS= read -r f; do
50+
[ -z "$f" ] && continue
51+
echo "- [\`$f\`](https://github.com/$REPO/blob/$SHA/$f)"
52+
done <<< "$files"
53+
} > comment.md
54+
gh pr comment "$PR" --repo "$REPO" --body-file comment.md
55+
echo "Posted changelog fragment comment."
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright 2026 NVIDIA CORPORATION
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Release, step 1 of 2 — Prepare Changelog (manual dispatch).
5+
#
6+
# Folds the pending changie fragments (.changes/unreleased/*.yaml) into CHANGELOG.md as a new
7+
# "## [VERSION]" section, clears the fragments, and opens a PR against the branch this workflow
8+
# was dispatched on (main for minors/majors, a v*.* branch for patches).
9+
#
10+
# A maintainer reviews and merges that PR. Merging it triggers release-publish.yaml (step 2),
11+
# which tags the version and creates the GitHub Release.
12+
13+
name: Release — Prepare Changelog
14+
15+
on:
16+
workflow_dispatch:
17+
inputs:
18+
version:
19+
description: 'Version to release, e.g. v0.17.0 (minors/majors from main, patches from a v*.* branch)'
20+
required: true
21+
type: string
22+
23+
permissions:
24+
contents: write
25+
pull-requests: write
26+
27+
jobs:
28+
prepare:
29+
name: Fold changelog & open PR
30+
runs-on: ubuntu-latest
31+
steps:
32+
- name: Checkout ${{ github.ref_name }}
33+
uses: actions/checkout@v6
34+
with:
35+
fetch-depth: 0
36+
token: ${{ secrets.KAIBOT_TOKEN }}
37+
38+
- name: Set up Go
39+
uses: actions/setup-go@v5
40+
with:
41+
go-version: '1.25.6'
42+
43+
- name: Fold fragments into CHANGELOG.md
44+
run: make changelog-release VERSION="${{ inputs.version }}"
45+
46+
- name: Open release PR
47+
env:
48+
GH_TOKEN: ${{ secrets.KAIBOT_TOKEN }}
49+
VERSION: ${{ inputs.version }}
50+
BASE: ${{ github.ref_name }}
51+
run: |
52+
set -euo pipefail
53+
BRANCH="release/prepare-$VERSION"
54+
55+
git config --local user.name "kai-release-bot"
56+
git config --local user.email "action@github.com"
57+
58+
git switch -c "$BRANCH"
59+
git add -A
60+
git commit -s -m "chore(release): prepare changelog for $VERSION"
61+
git push --force-with-lease -u origin "$BRANCH"
62+
63+
BODY="${RUNNER_TEMP:-/tmp}/pr-body.md"
64+
{
65+
echo "Automated by the **Release — Prepare Changelog** workflow."
66+
echo
67+
echo "Folds the pending \`.changes/unreleased/\` fragments into \`CHANGELOG.md\` as \`## [$VERSION]\` and clears them. \`CHANGELOG.md\` is the source of truth."
68+
echo
69+
echo "**Merging this PR** triggers \`release-publish.yaml\`, which tags \`$VERSION\` and creates the GitHub Release from this changelog section."
70+
echo
71+
echo "- Review the new \`## [$VERSION]\` section for accuracy."
72+
echo "- Target branch: \`$BASE\`."
73+
} > "$BODY"
74+
75+
gh pr create \
76+
--base "$BASE" \
77+
--head "$BRANCH" \
78+
--title "chore(release): prepare changelog for $VERSION" \
79+
--label skip-changelog \
80+
--body-file "$BODY"
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Copyright 2026 NVIDIA CORPORATION
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Release, step 2 of 2 — Tag & Publish (automatic).
5+
#
6+
# Fires when a "release/prepare-*" PR from release-prepare.yaml is merged into main or a v*.*
7+
# branch. Reads the freshly-folded top section of CHANGELOG.md, creates the git tag, and creates
8+
# the GitHub Release with that section as the notes.
9+
10+
name: Release — Tag & Publish
11+
12+
on:
13+
pull_request:
14+
types: [closed]
15+
branches:
16+
- main
17+
- 'v*.*'
18+
19+
permissions:
20+
contents: write
21+
22+
concurrency:
23+
group: release-publish
24+
cancel-in-progress: false
25+
26+
jobs:
27+
publish:
28+
name: Tag & create GitHub Release
29+
if: >-
30+
github.event.pull_request.merged == true &&
31+
startsWith(github.event.pull_request.head.ref, 'release/prepare-')
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Checkout merged ${{ github.event.pull_request.base.ref }}
35+
uses: actions/checkout@v6
36+
with:
37+
ref: ${{ github.event.pull_request.base.ref }}
38+
fetch-depth: 0
39+
fetch-tags: true
40+
token: ${{ secrets.KAIBOT_TOKEN }}
41+
42+
- name: Derive version and release notes from CHANGELOG.md
43+
id: notes
44+
env:
45+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
46+
run: |
47+
set -euo pipefail
48+
49+
# Top "## [VERSION] - date" heading is the version this PR just folded in.
50+
VERSION="$(grep -m1 -oE '^## \[[^]]+\]' CHANGELOG.md | sed -E 's/^## \[(.+)\]/\1/')"
51+
BRANCH_VERSION="${HEAD_REF#release/prepare-}"
52+
if [ -z "$VERSION" ]; then
53+
echo "::error::Could not read a version heading from CHANGELOG.md"; exit 1
54+
fi
55+
if [ "$VERSION" != "$BRANCH_VERSION" ]; then
56+
echo "::error::CHANGELOG top version ($VERSION) != branch version ($BRANCH_VERSION)"; exit 1
57+
fi
58+
if git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then
59+
echo "::error::Tag $VERSION already exists"; exit 1
60+
fi
61+
62+
# Extract the body of the top section (everything after its heading, up to the next
63+
# "## [" heading), trimming leading blank lines.
64+
awk '
65+
/^## \[/ { n++; if (n==1) next; if (n==2) exit }
66+
n==1 { print }
67+
' CHANGELOG.md | sed '/./,$!d' > "${RUNNER_TEMP:-/tmp}/notes.md"
68+
69+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
70+
echo "Release $VERSION notes:"; cat "${RUNNER_TEMP:-/tmp}/notes.md"
71+
72+
- name: Create and push tag
73+
env:
74+
VERSION: ${{ steps.notes.outputs.version }}
75+
run: |
76+
set -euo pipefail
77+
git config --local user.name "kai-release-bot"
78+
git config --local user.email "action@github.com"
79+
git tag "$VERSION"
80+
git push origin "refs/tags/$VERSION"
81+
82+
- name: Create GitHub Release
83+
env:
84+
GH_TOKEN: ${{ secrets.KAIBOT_TOKEN }}
85+
VERSION: ${{ steps.notes.outputs.version }}
86+
run: |
87+
set -euo pipefail
88+
gh release create "$VERSION" \
89+
--title "$VERSION" \
90+
--notes-file "${RUNNER_TEMP:-/tmp}/notes.md"

.github/workflows/validate-changelog.yaml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,32 @@ on:
99
branches:
1010
- main
1111
- 'v*.*'
12+
merge_group:
13+
types: [checks_requested]
1214

1315
jobs:
1416
validate-changelog:
1517
name: Validate Changelog
1618
runs-on: ubuntu-latest
17-
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-changelog') }}
1819
steps:
19-
- uses: actions/checkout@v4
20+
# For merge queue: changelog was already validated, just succeed
21+
- name: Skip validation for merge queue
22+
if: github.event_name == 'merge_group'
23+
run: echo "Changelog was already validated before entering merge queue"
24+
25+
- uses: actions/checkout@v6
26+
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip-changelog') && !contains(github.event.pull_request.labels.*.name, 'dependencies')
2027
with:
2128
fetch-depth: 0
2229

23-
- name: Check CHANGELOG.md updated
30+
# Changelog entries are managed as changie fragments (see CONTRIBUTING.md).
31+
# A behavior-changing PR must add a fragment under .changes/unreleased/ (run `make changelog`).
32+
- name: Check changelog fragment added
33+
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'skip-changelog') && !contains(github.event.pull_request.labels.*.name, 'dependencies')
2434
run: |
25-
if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -q '^CHANGELOG.md$'; then
26-
echo "CHANGELOG.md has been updated."
35+
if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -qE '^\.changes/unreleased/[^/]+\.yaml$'; then
36+
echo "Changelog fragment added."
2737
else
28-
echo "::error::CHANGELOG.md has not been updated. Please update the changelog or add the 'skip-changelog' label to opt out."
38+
echo "::error::No changelog fragment found. Run 'make changelog' to add one, or add the 'skip-changelog' label to opt out."
2939
exit 1
3040
fi
31-

0 commit comments

Comments
 (0)