Skip to content

Commit 73c261d

Browse files
authored
Update release drafter to be more aligned with repo vision (#165)
## Description Update release drafter to be more like what I want. ## Related Issue <!-- Link to the issue this PR addresses --> Fixes # ## Type of Change <!-- Check all that apply --> - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Refactor (code change that neither fixes a bug nor adds a feature) ## Checklist <!-- Ensure all items are completed before requesting review --> - [ ] I have added tests that prove my fix is effective or my feature works - [ ] I have updated the documentation accordingly - [ ] I have updated the [CHANGELOG](../CHANGELOG.md) - [x] My changes do not introduce breaking changes, or breaking changes are documented
1 parent bd1ed72 commit 73c261d

File tree

9 files changed

+942
-39
lines changed

9 files changed

+942
-39
lines changed

.github/release-drafter.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
name-template: "v$RESOLVED_VERSION"
2-
tag-template: "v$RESOLVED_VERSION"
1+
name-template: "$RESOLVED_VERSION"
2+
tag-template: "$RESOLVED_VERSION"
33

44
categories:
55
- title: "🚀 Features"
@@ -83,7 +83,7 @@ autolabeler:
8383
- "/^.+!:/i"
8484

8585
template: |
86-
## What's Changed
86+
## Pull Requests
8787
8888
$CHANGES
8989

.github/workflows/release-drafter.yml

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,196 @@ permissions:
1717
contents: write
1818
pull-requests: write
1919

20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.ref }}
22+
# Use false for release workflows to prevent incomplete releases from cancellation
23+
cancel-in-progress: false
24+
2025
jobs:
2126
update_release_draft:
27+
# Only run on the main repository, not forks
28+
if: github.repository == 'wallstop/unity-helpers'
2229
runs-on: ubuntu-latest
30+
timeout-minutes: 10
31+
outputs:
32+
version: ${{ steps.changelog.outputs.version }}
33+
release-id: ${{ steps.release_drafter.outputs.id }}
34+
release-url: ${{ steps.release_drafter.outputs.html_url }}
2335
steps:
24-
- uses: release-drafter/release-drafter@v6
36+
- name: Checkout repository
37+
uses: actions/checkout@v4
38+
with:
39+
fetch-depth: 1 # Only need current commit for package.json and CHANGELOG.md
40+
41+
- name: Create changelog extraction script
42+
run: |
43+
set -euo pipefail
44+
cat > "${RUNNER_TEMP}/extract-changelog.awk" << '__AWKSCRIPT_HEREDOC_DELIMITER__'
45+
BEGIN { found = 0 }
46+
/^## \[/ {
47+
if (found) exit
48+
# Extract version from header: ## [X.Y.Z] or ## [X.Y.Z-prerelease+build]
49+
if (match($0, /\[[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?\]/)) {
50+
header_ver = substr($0, RSTART + 1, RLENGTH - 2)
51+
if (header_ver == target_ver) {
52+
found = 1
53+
next
54+
}
55+
}
56+
}
57+
found { print }
58+
__AWKSCRIPT_HEREDOC_DELIMITER__
59+
60+
- name: Extract changelog for version
61+
id: changelog
62+
run: |
63+
set -euo pipefail
64+
65+
# Validate required files exist
66+
if [ ! -f "package.json" ]; then
67+
echo "::error::package.json not found"
68+
exit 1
69+
fi
70+
71+
if [ ! -f "CHANGELOG.md" ]; then
72+
echo "::error::CHANGELOG.md not found"
73+
exit 1
74+
fi
75+
76+
if [ ! -f "${RUNNER_TEMP}/extract-changelog.awk" ]; then
77+
echo "::error::AWK script not found at ${RUNNER_TEMP}/extract-changelog.awk"
78+
exit 1
79+
fi
80+
81+
# Get the version from package.json with validation
82+
VERSION=$(jq -r '.version // empty' package.json)
83+
84+
if [ -z "$VERSION" ]; then
85+
echo "::error::Version field missing or empty in package.json"
86+
exit 1
87+
fi
88+
89+
# Validate semver format (X.Y.Z with optional prerelease/build metadata)
90+
if ! printf '%s' "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'; then
91+
echo "::error::Invalid semver format: $VERSION (expected X.Y.Z)"
92+
exit 1
93+
fi
94+
95+
echo "Extracting changelog for version: $VERSION"
96+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
97+
98+
# Extract changelog section using external AWK script with exact version matching
99+
awk -v target_ver="$VERSION" -f "${RUNNER_TEMP}/extract-changelog.awk" CHANGELOG.md > "${RUNNER_TEMP}/changelog_section.md"
100+
101+
# Check if we extracted any content
102+
if [ ! -s "${RUNNER_TEMP}/changelog_section.md" ]; then
103+
echo "::warning::No changelog section found for version $VERSION"
104+
fi
105+
106+
# Set multiline output using a random delimiter to prevent injection
107+
# Generate unique delimiter using date + random to avoid content collision
108+
DELIMITER="__CHANGELOG_EOF_$(date +%s%N)_${RANDOM}__"
109+
{
110+
printf 'content<<%s\n' "$DELIMITER"
111+
cat "${RUNNER_TEMP}/changelog_section.md"
112+
printf '%s\n' "$DELIMITER"
113+
} >> "$GITHUB_OUTPUT"
114+
115+
echo "Changelog section extracted successfully"
116+
117+
- name: Draft release
118+
id: release_drafter
119+
uses: release-drafter/release-drafter@v6
25120
with:
26121
config-name: release-drafter.yml
122+
version: ${{ steps.changelog.outputs.version }}
123+
tag: ${{ steps.changelog.outputs.version }}
27124
env:
28125
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
126+
127+
- name: Update release with changelog
128+
env:
129+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
130+
VERSION: ${{ steps.changelog.outputs.version }}
131+
CHANGELOG_CONTENT: ${{ steps.changelog.outputs.content }}
132+
RELEASE_ID: ${{ steps.release_drafter.outputs.id }}
133+
run: |
134+
set -euo pipefail
135+
136+
# Write changelog content to file using printf (avoids heredoc injection)
137+
# Using environment variable avoids shell interpolation issues
138+
printf '%s\n' "$CHANGELOG_CONTENT" > "${RUNNER_TEMP}/changelog_content.md"
139+
140+
API_STDERR="${RUNNER_TEMP}/gh_api_stderr.log"
141+
142+
# Validate release ID from release-drafter action
143+
if [ -z "$RELEASE_ID" ]; then
144+
echo "::error::release-drafter did not return a release ID"
145+
exit 1
146+
fi
147+
148+
echo "Using release ID $RELEASE_ID from release-drafter action"
149+
150+
# Fetch current release body and write to file for safe handling
151+
# Capture stderr to temp file for debugging instead of suppressing
152+
echo "Fetching current release body..."
153+
if ! gh api "repos/${{ github.repository }}/releases/$RELEASE_ID" \
154+
--jq '.body // ""' > "${RUNNER_TEMP}/current_body.md" 2>"$API_STDERR"; then
155+
echo "::warning::Failed to fetch current release body, using empty body"
156+
if [ -s "$API_STDERR" ]; then
157+
echo "::debug::gh api stderr: $(cat "$API_STDERR")"
158+
fi
159+
: > "${RUNNER_TEMP}/current_body.md"
160+
fi
161+
162+
# Check if changelog section already exists to prevent duplicate additions
163+
# Use -E for extended regex, -i for case-insensitive, allow optional leading whitespace
164+
if grep -qiE '^\s*## Changelog' "${RUNNER_TEMP}/current_body.md"; then
165+
echo "::notice::Changelog section already exists in release body, skipping update"
166+
echo "Release $RELEASE_ID already contains changelog for version $VERSION"
167+
exit 0
168+
fi
169+
170+
# Build new body using file concatenation (no embedded whitespace issues)
171+
{
172+
echo "## Changelog"
173+
echo ""
174+
cat "${RUNNER_TEMP}/changelog_content.md"
175+
echo ""
176+
cat "${RUNNER_TEMP}/current_body.md"
177+
} > "${RUNNER_TEMP}/new_body.md"
178+
179+
echo "Updating release with changelog..."
180+
181+
# Use file-based body to handle special characters safely
182+
# Retry with exponential backoff for transient API failures
183+
MAX_ATTEMPTS=3
184+
DELAY=2
185+
for attempt in $(seq 1 $MAX_ATTEMPTS); do
186+
if gh api "repos/${{ github.repository }}/releases/$RELEASE_ID" \
187+
-X PATCH \
188+
-F body=@"${RUNNER_TEMP}/new_body.md"; then
189+
echo "Release API update succeeded on attempt $attempt"
190+
break
191+
fi
192+
if [ "$attempt" -eq "$MAX_ATTEMPTS" ]; then
193+
echo "::error::Failed to update release body for release ID $RELEASE_ID after $MAX_ATTEMPTS attempts"
194+
exit 1
195+
fi
196+
echo "::warning::Attempt $attempt failed, retrying in ${DELAY}s..."
197+
sleep "$DELAY"
198+
DELAY=$((DELAY * 2))
199+
done
200+
201+
echo "Release $RELEASE_ID updated successfully with changelog for version $VERSION"
202+
203+
# Add step summary for workflow run visibility
204+
{
205+
echo "### ✅ Release Draft Updated"
206+
echo ""
207+
echo "| Property | Value |"
208+
echo "|----------|-------|"
209+
echo "| **Version** | \`$VERSION\` |"
210+
echo "| **Release ID** | \`$RELEASE_ID\` |"
211+
echo "| **Release URL** | ${{ steps.release_drafter.outputs.html_url }} |"
212+
} >> "$GITHUB_STEP_SUMMARY"

.llm/context.md

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -83,45 +83,48 @@ Invoke these skills for specific tasks.
8383
**Regenerate with**: `pwsh -NoProfile -File scripts/generate-skills-index.ps1`
8484

8585
<!-- BEGIN GENERATED SKILLS INDEX -->
86-
<!-- Generated: 2026-01-09 18:37:57 UTC -->
86+
<!-- Generated: 2026-01-21 11:38:27 UTC -->
8787
<!-- Command: pwsh -NoProfile -File scripts/generate-skills-index.ps1 -->
8888

8989
### Core Skills (Always Consider)
9090

91-
| Skill | When to Use |
92-
| ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
93-
| [avoid-magic-strings](./skills/avoid-magic-strings.md) | ALL code - use nameof() not strings |
94-
| [avoid-reflection](./skills/avoid-reflection.md) | ALL code - never reflect on our own types |
95-
| [create-csharp-file](./skills/create-csharp-file.md) | Creating any new .cs file |
96-
| [create-editor-tool](./skills/create-editor-tool.md) | Creating Editor windows and inspectors |
97-
| [create-enum](./skills/create-enum.md) | Creating a new enum type |
98-
| [create-property-drawer](./skills/create-property-drawer.md) | Creating PropertyDrawers for custom attributes |
99-
| [create-scriptable-object](./skills/create-scriptable-object.md) | Creating ScriptableObject data assets |
100-
| [create-test](./skills/create-test.md) | Writing or modifying test files |
101-
| [create-unity-meta](./skills/create-unity-meta.md) | After creating ANY new file or folder |
102-
| [defensive-editor-programming](./skills/defensive-editor-programming.md) | Editor code - handle Unity Editor edge cases |
103-
| [defensive-programming](./skills/defensive-programming.md) | ALL code - never throw, handle gracefully |
104-
| [editor-caching-patterns](./skills/editor-caching-patterns.md) | Caching strategies for Editor code |
105-
| [formatting](./skills/formatting.md) | After ANY file change (CSharpier/Prettier) |
106-
| [git-hook-patterns](./skills/git-hook-patterns.md) | Pre-commit hook safety and configuration |
107-
| [git-safe-operations](./skills/git-safe-operations.md) | Scripts or hooks that interact with git index |
108-
| [git-staging-helpers](./skills/git-staging-helpers.md) | PowerShell/Bash helpers for safe git staging |
109-
| [high-performance-csharp](./skills/high-performance-csharp.md) | ALL code - zero allocation patterns |
110-
| [investigate-test-failures](./skills/investigate-test-failures.md) | ANY test failure - investigate before fixing |
111-
| [linter-reference](./skills/linter-reference.md) | Detailed linter commands, configurations |
112-
| [manage-skills](./skills/manage-skills.md) | Creating, updating, splitting, consolidating, or removing skills |
113-
| [markdown-reference](./skills/markdown-reference.md) | Link formatting, escaping, linting rules |
114-
| [no-regions](./skills/no-regions.md) | ALL C# code - never use #region/#endregion |
115-
| [prefer-logging-extensions](./skills/prefer-logging-extensions.md) | Unity logging in UnityEngine.Object classes |
116-
| [search-codebase](./skills/search-codebase.md) | Finding code, files, or patterns |
117-
| [test-data-driven](./skills/test-data-driven.md) | Data-driven testing with TestCase and TestCaseSource |
118-
| [test-naming-conventions](./skills/test-naming-conventions.md) | Test method and TestName naming rules |
119-
| [test-odin-drawers](./skills/test-odin-drawers.md) | Odin Inspector drawer testing patterns |
120-
| [test-parallelization-rules](./skills/test-parallelization-rules.md) | Unity Editor test threading constraints |
121-
| [test-unity-lifecycle](./skills/test-unity-lifecycle.md) | Track(), DestroyImmediate, object cleanup |
122-
| [update-documentation](./skills/update-documentation.md) | After ANY feature/bug fix/API change |
123-
| [validate-before-commit](./skills/validate-before-commit.md) | Before completing any task (run linters!) |
124-
| [validation-troubleshooting](./skills/validation-troubleshooting.md) | Common validation errors, CI failures, fixes |
91+
| Skill | When to Use |
92+
| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
93+
| [avoid-magic-strings](./skills/avoid-magic-strings.md) | ALL code - use nameof() not strings |
94+
| [avoid-reflection](./skills/avoid-reflection.md) | ALL code - never reflect on our own types |
95+
| [create-csharp-file](./skills/create-csharp-file.md) | Creating any new .cs file |
96+
| [create-editor-tool](./skills/create-editor-tool.md) | Creating Editor windows and inspectors |
97+
| [create-enum](./skills/create-enum.md) | Creating a new enum type |
98+
| [create-property-drawer](./skills/create-property-drawer.md) | Creating PropertyDrawers for custom attributes |
99+
| [create-scriptable-object](./skills/create-scriptable-object.md) | Creating ScriptableObject data assets |
100+
| [create-test](./skills/create-test.md) | Writing or modifying test files |
101+
| [create-unity-meta](./skills/create-unity-meta.md) | After creating ANY new file or folder |
102+
| [defensive-editor-programming](./skills/defensive-editor-programming.md) | Editor code - handle Unity Editor edge cases |
103+
| [defensive-programming](./skills/defensive-programming.md) | ALL code - never throw, handle gracefully |
104+
| [editor-caching-patterns](./skills/editor-caching-patterns.md) | Caching strategies for Editor code |
105+
| [formatting](./skills/formatting.md) | After ANY file change (CSharpier/Prettier) |
106+
| [git-hook-patterns](./skills/git-hook-patterns.md) | Pre-commit hook safety and configuration |
107+
| [git-safe-operations](./skills/git-safe-operations.md) | Scripts or hooks that interact with git index |
108+
| [git-staging-helpers](./skills/git-staging-helpers.md) | PowerShell/Bash helpers for safe git staging |
109+
| [github-actions-shell-foundations](./skills/github-actions-shell-foundations.md) | Core shell scripting safety for GitHub Actions |
110+
| [github-actions-shell-scripting](./skills/github-actions-shell-scripting.md) | Shell scripting best practices for GitHub Actions |
111+
| [github-actions-shell-workflow-patterns](./skills/github-actions-shell-workflow-patterns.md) | Workflow integration patterns for GitHub Actions shell steps |
112+
| [high-performance-csharp](./skills/high-performance-csharp.md) | ALL code - zero allocation patterns |
113+
| [investigate-test-failures](./skills/investigate-test-failures.md) | ANY test failure - investigate before fixing |
114+
| [linter-reference](./skills/linter-reference.md) | Detailed linter commands, configurations |
115+
| [manage-skills](./skills/manage-skills.md) | Creating, updating, splitting, consolidating, or removing skills |
116+
| [markdown-reference](./skills/markdown-reference.md) | Link formatting, escaping, linting rules |
117+
| [no-regions](./skills/no-regions.md) | ALL C# code - never use #region/#endregion |
118+
| [prefer-logging-extensions](./skills/prefer-logging-extensions.md) | Unity logging in UnityEngine.Object classes |
119+
| [search-codebase](./skills/search-codebase.md) | Finding code, files, or patterns |
120+
| [test-data-driven](./skills/test-data-driven.md) | Data-driven testing with TestCase and TestCaseSource |
121+
| [test-naming-conventions](./skills/test-naming-conventions.md) | Test method and TestName naming rules |
122+
| [test-odin-drawers](./skills/test-odin-drawers.md) | Odin Inspector drawer testing patterns |
123+
| [test-parallelization-rules](./skills/test-parallelization-rules.md) | Unity Editor test threading constraints |
124+
| [test-unity-lifecycle](./skills/test-unity-lifecycle.md) | Track(), DestroyImmediate, object cleanup |
125+
| [update-documentation](./skills/update-documentation.md) | After ANY feature/bug fix/API change |
126+
| [validate-before-commit](./skills/validate-before-commit.md) | Before completing any task (run linters!) |
127+
| [validation-troubleshooting](./skills/validation-troubleshooting.md) | Common validation errors, CI failures, fixes |
125128

126129
### Performance Skills
127130

.llm/skills/github-actions-script-pattern.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ If you answered "yes" to any of these, create a standalone script.
179179
180180
## Related Skills
181181
182+
- [github-actions-shell-scripting](./github-actions-shell-scripting.md) — Inline shell safety and workflow patterns
182183
- [validate-before-commit](./validate-before-commit.md) — Ensure scripts pass linting
183184
- [create-test](./create-test.md) — Write comprehensive tests for scripts
184185
- [search-codebase](./search-codebase.md) — Find existing scripts to extend

0 commit comments

Comments
 (0)