Skip to content

Commit e2a4c7d

Browse files
committed
Add AGENTS guide and enforce Unreleased changelog updates
1 parent 7f72d30 commit e2a4c7d

4 files changed

Lines changed: 163 additions & 0 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd)
5+
cd "$ROOT_DIR"
6+
7+
BASE_REF="${GITHUB_BASE_REF:-}"
8+
HEAD_REF="${GITHUB_HEAD_REF:-}"
9+
10+
if [[ -n "$BASE_REF" ]]; then
11+
git fetch --no-tags --depth=1 origin "$BASE_REF" >/dev/null 2>&1 || true
12+
DIFF_RANGE="origin/${BASE_REF}...HEAD"
13+
else
14+
if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then
15+
DIFF_RANGE="HEAD~1...HEAD"
16+
else
17+
echo "No comparable git range available; skipping changelog check."
18+
exit 0
19+
fi
20+
fi
21+
22+
mapfile -t CHANGED_FILES < <(git diff --name-only "$DIFF_RANGE")
23+
24+
if [[ ${#CHANGED_FILES[@]} -eq 0 ]]; then
25+
echo "No changed files detected; skipping changelog check."
26+
exit 0
27+
fi
28+
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
65+
fi
66+
done
67+
68+
if [[ "$is_exempt" == false ]]; then
69+
needs_changelog=true
70+
break
71+
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
83+
fi
84+
85+
if ! grep -q '^## \[Unreleased\]' CHANGELOG.md; then
86+
echo "ERROR: CHANGELOG.md must contain a top-level '## [Unreleased]' section."
87+
exit 1
88+
fi
89+
90+
UNRELEASED_BLOCK=$(awk '
91+
/^## \[Unreleased\]/{in_block=1; next}
92+
/^## \[/{if(in_block){exit}}
93+
in_block{print}
94+
' CHANGELOG.md)
95+
96+
if [[ -z "${UNRELEASED_BLOCK//$'\n'/}" ]]; then
97+
echo "ERROR: ## [Unreleased] section is empty."
98+
exit 1
99+
fi
100+
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
104+
fi
105+
106+
echo "Changelog gate passed."

.github/workflows/ci-pr.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ jobs:
1616
steps:
1717
- name: Checkout
1818
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Enforce unreleased changelog updates
23+
run: .github/scripts/check-unreleased-changelog.sh
1924

2025
- name: Setup .NET
2126
uses: actions/setup-dotnet@v4

AGENTS.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# AGENTS
2+
3+
This file defines contributor expectations for building, testing, regression safety, and release-note hygiene.
4+
5+
## Build, Test, and Non-Regression
6+
7+
Use these commands before opening or updating a PR:
8+
9+
- Build: `make build`
10+
- Full test suite: `make test`
11+
- Non-regression smoke tests (all samples): `make smoke-tests`
12+
13+
Equivalent direct commands:
14+
15+
- `dotnet build FScript.sln -c Release`
16+
- `dotnet test FScript.sln -c Release`
17+
18+
Targeted test suites (when working on specific areas):
19+
20+
- Language: `dotnet test tests/FScript.Language.Tests/FScript.Language.Tests.fsproj -c Release`
21+
- Runtime: `dotnet test tests/FScript.Runtime.Tests/FScript.Runtime.Tests.fsproj -c Release`
22+
- Language Server / VS Code features: `dotnet test tests/FScript.LanguageServer.Tests/FScript.LanguageServer.Tests.fsproj -c Release`
23+
24+
## Test Quality Policy
25+
26+
- Every new feature must include automated test coverage.
27+
- Every bug fix must include a regression test reproducing the prior failure mode.
28+
- Add tests in the suite matching the change surface:
29+
- Parser/type/eval semantics -> `tests/FScript.Language.Tests`
30+
- Runtime and host behavior -> `tests/FScript.Runtime.Tests`
31+
- LSP/editor behavior -> `tests/FScript.LanguageServer.Tests`
32+
- If language behavior, includes, or sample contracts change, run `make smoke-tests`.
33+
34+
## Release Notes (Unreleased)
35+
36+
- `CHANGELOG.md` must keep a top `## [Unreleased]` section.
37+
- Each new feature/fix entry must be a short, single-line bullet.
38+
- Write entries in user-facing terms (what changed), not implementation detail.
39+
- At release time, move unreleased entries to the versioned section and reset `Unreleased`.
40+
41+
## PR Checklist
42+
43+
- Build passes.
44+
- Relevant test suite(s) pass.
45+
- New behavior is test covered.
46+
- Regression risk is covered by tests (including smoke tests when relevant).
47+
- `CHANGELOG.md` `## [Unreleased]` has concise one-line entries for the change.

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
All notable changes to FScript are documented in this file.
44

5+
## [Unreleased]
6+
7+
- Added contributor policy in `AGENTS.md` for build/test/non-regression workflow.
8+
- Added CI changelog gate requiring one-line `## [Unreleased]` entries for functional PR changes.
9+
510
## [0.29.0]
611

712
- Improved LSP inlay/type rendering for map-related inference (`int|string` map-key domain and `unknown map` display).

0 commit comments

Comments
 (0)