Skip to content

Commit ac20a7b

Browse files
committed
ci(process): enforce changelog update on every commit
1 parent 008eb47 commit ac20a7b

File tree

6 files changed

+88
-58
lines changed

6 files changed

+88
-58
lines changed

.github/scripts/check-unreleased-changelog.sh

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ cd "$ROOT_DIR"
66

77
BASE_REF="${GITHUB_BASE_REF:-}"
88
HEAD_REF="${GITHUB_HEAD_REF:-}"
9+
REQUIRE_CHANGELOG_ALWAYS="${REQUIRE_CHANGELOG_ALWAYS:-false}"
10+
ENFORCE_UNRELEASED_BULLET="${ENFORCE_UNRELEASED_BULLET:-false}"
911

1012
if [[ -n "$BASE_REF" ]]; then
1113
git fetch --no-tags --depth=1 origin "$BASE_REF" >/dev/null 2>&1 || true
@@ -26,60 +28,64 @@ if [[ ${#CHANGED_FILES[@]} -eq 0 ]]; then
2628
exit 0
2729
fi
2830

29-
TRIGGERS=(
30-
'^src/'
31-
'^tests/'
32-
'^vscode-fscript/'
33-
'^docs/'
34-
'^samples/'
35-
)
36-
37-
EXEMPTS=(
38-
'^\.github/'
39-
'^LICENSE$'
40-
'^README\.md$'
41-
'^AGENTS\.md$'
42-
'^CHANGELOG\.md$'
43-
'^\.gitignore$'
44-
)
45-
46-
needs_changelog=false
47-
for file in "${CHANGED_FILES[@]}"; do
48-
is_trigger=false
49-
for pat in "${TRIGGERS[@]}"; do
50-
if [[ "$file" =~ $pat ]]; then
51-
is_trigger=true
52-
break
53-
fi
54-
done
55-
56-
if [[ "$is_trigger" == false ]]; then
57-
continue
58-
fi
59-
60-
is_exempt=false
61-
for pat in "${EXEMPTS[@]}"; do
62-
if [[ "$file" =~ $pat ]]; then
63-
is_exempt=true
64-
break
31+
if ! printf '%s\n' "${CHANGED_FILES[@]}" | grep -qx 'CHANGELOG.md'; then
32+
if [[ "$REQUIRE_CHANGELOG_ALWAYS" == "true" ]]; then
33+
echo "ERROR: CHANGELOG.md must be updated in every commit."
34+
exit 1
35+
else
36+
TRIGGERS=(
37+
'^src/'
38+
'^tests/'
39+
'^vscode-fscript/'
40+
'^docs/'
41+
'^samples/'
42+
)
43+
44+
EXEMPTS=(
45+
'^\.github/'
46+
'^LICENSE$'
47+
'^README\.md$'
48+
'^AGENTS\.md$'
49+
'^CHANGELOG\.md$'
50+
'^\.gitignore$'
51+
)
52+
53+
needs_changelog=false
54+
for file in "${CHANGED_FILES[@]}"; do
55+
is_trigger=false
56+
for pat in "${TRIGGERS[@]}"; do
57+
if [[ "$file" =~ $pat ]]; then
58+
is_trigger=true
59+
break
60+
fi
61+
done
62+
63+
if [[ "$is_trigger" == false ]]; then
64+
continue
65+
fi
66+
67+
is_exempt=false
68+
for pat in "${EXEMPTS[@]}"; do
69+
if [[ "$file" =~ $pat ]]; then
70+
is_exempt=true
71+
break
72+
fi
73+
done
74+
75+
if [[ "$is_exempt" == false ]]; then
76+
needs_changelog=true
77+
break
78+
fi
79+
done
80+
81+
if [[ "$needs_changelog" == true ]]; then
82+
echo "ERROR: Functional changes detected but CHANGELOG.md was not updated."
83+
echo "Add a short one-line bullet under ## [Unreleased]."
84+
exit 1
6585
fi
66-
done
67-
68-
if [[ "$is_exempt" == false ]]; then
69-
needs_changelog=true
70-
break
86+
echo "No changelog-required files changed."
87+
exit 0
7188
fi
72-
done
73-
74-
if [[ "$needs_changelog" == false ]]; then
75-
echo "No changelog-required files changed."
76-
exit 0
77-
fi
78-
79-
if ! printf '%s\n' "${CHANGED_FILES[@]}" | grep -qx 'CHANGELOG.md'; then
80-
echo "ERROR: Functional changes detected but CHANGELOG.md was not updated."
81-
echo "Add a short one-line bullet under ## [Unreleased]."
82-
exit 1
8389
fi
8490

8591
if ! grep -q '^## \[Unreleased\]' CHANGELOG.md; then
@@ -98,9 +104,11 @@ if [[ -z "${UNRELEASED_BLOCK//$'\n'/}" ]]; then
98104
exit 1
99105
fi
100106

101-
if ! printf '%s\n' "$UNRELEASED_BLOCK" | grep -qE '^- '; then
102-
echo "ERROR: ## [Unreleased] must contain at least one bullet line starting with '- '."
103-
exit 1
107+
if [[ "$ENFORCE_UNRELEASED_BULLET" == "true" ]]; then
108+
if ! printf '%s\n' "$UNRELEASED_BLOCK" | grep -qE '^- '; then
109+
echo "ERROR: ## [Unreleased] must contain at least one bullet line starting with '- '."
110+
exit 1
111+
fi
104112
fi
105113

106114
echo "Changelog gate passed."

.github/workflows/ci-main.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ jobs:
1717
steps:
1818
- name: Checkout
1919
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Enforce changelog updates on every commit
24+
env:
25+
REQUIRE_CHANGELOG_ALWAYS: "true"
26+
run: .github/scripts/check-unreleased-changelog.sh
2027

2128
- name: Setup .NET
2229
uses: actions/setup-dotnet@v4

.github/workflows/ci-pr.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020
fetch-depth: 0
2121

2222
- name: Enforce unreleased changelog updates
23+
env:
24+
REQUIRE_CHANGELOG_ALWAYS: "true"
2325
run: .github/scripts/check-unreleased-changelog.sh
2426

2527
- name: Setup .NET

AGENTS.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ Targeted test suites (when working on specific areas):
4242
- `**Full Changelog**: https://github.com/MagnusOpera/FScript/compare/<previous-tag>...<new-tag>`
4343
- When publishing the GitHub release, include that same compare link in the release notes body.
4444

45+
## Commit Gate (Hard Requirement)
46+
47+
- Every commit that targets `main` must update `CHANGELOG.md`.
48+
- Required format for regular commits:
49+
- add at least one short, single-line bullet under `## [Unreleased]`.
50+
- Scope is strict (no exceptions for docs/process/policy/chore/dependency-only commits).
51+
- Local preflight command:
52+
- `make verify-changelog`
53+
- CI enforces this on both PRs and direct pushes to `main`.
54+
4555
## Release Process (Tags and GitHub Draft)
4656

4757
Follow this exact sequence for every release:
@@ -81,5 +91,4 @@ Rules:
8191
- Committing directly to `main` follows the same quality bar as a PR.
8292
- All checklist items above still apply (build/test/spec/docs/changelog).
8393
- Documentation and release notes must be updated in the same change set.
84-
- Every direct-to-main commit must add at least one `CHANGELOG.md` `## [Unreleased]` bullet in the same commit.
85-
- This includes documentation/process/policy-only commits (not just runtime/language code changes).
94+
- Direct-to-main commits are blocked by the changelog gate if `CHANGELOG.md` is not updated.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All notable changes to FScript are documented in this file.
55
## [Unreleased]
66

77
- Updated runtime/test project package versions after NuGet publish verification.
8+
- Enforced strict changelog gates in CI (PR + main) and added `make verify-changelog` local preflight.
89

910
## [0.32.0]
1011

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build test smoke-tests clean publish publish-darwin publish-linux publish-windows pack-nuget publish-all
1+
.PHONY: build test smoke-tests verify-changelog clean publish publish-darwin publish-linux publish-windows pack-nuget publish-all
22

33
build:
44
dotnet build FScript.sln -c Release
@@ -14,6 +14,9 @@ smoke-tests:
1414
done; \
1515
echo "All smoke tests passed."
1616

17+
verify-changelog:
18+
REQUIRE_CHANGELOG_ALWAYS=true .github/scripts/check-unreleased-changelog.sh
19+
1720
clean:
1821
dotnet clean FScript.sln -c Release
1922
find . -type d -name bin -o -name obj | xargs rm -rf

0 commit comments

Comments
 (0)