Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 49 additions & 15 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ env:
jobs:
npm:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.version.outputs.tag }}
version: ${{ steps.version.outputs.version }}
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0

- name: Read version
id: version
Expand Down Expand Up @@ -122,9 +121,53 @@ jobs:
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
tag_commit="$(git rev-parse "$TAG^{commit}")"
head_commit="$(git rev-parse HEAD)"
if [[ "$tag_commit" != "$head_commit" ]]; then
echo "::error::Tag $TAG already points to $tag_commit, not $head_commit"
exit 1
fi
exit 0
fi

git tag -a "$TAG" -m "$TAG"
git push origin "$TAG"

- name: Resolve release notes range
if: github.event_name == 'push' || inputs.publish_to_npm == true
id: release-notes-range
env:
RELEASE_TAG: ${{ steps.version.outputs.tag }}
run: |
set -euo pipefail
previous_stable="$(
git tag --list 'v[0-9]*.[0-9]*.[0-9]*' \
--sort=-v:refname \
| awk -v release_tag="$RELEASE_TAG" '
/^v[0-9]+\.[0-9]+\.[0-9]+$/ && $0 != release_tag {
print
exit
}
'
)"
if [[ -z "$previous_stable" ]]; then
first_commit="$(git rev-list --max-parents=0 HEAD | tail -n 1)"
echo "args=--tag ${RELEASE_TAG} --strip header ${first_commit}..HEAD" >> "$GITHUB_OUTPUT"
else
echo "args=--tag ${RELEASE_TAG} --strip header ${previous_stable}..HEAD" >> "$GITHUB_OUTPUT"
fi

- name: Generate release notes from Conventional Commits
if: github.event_name == 'push' || inputs.publish_to_npm == true
uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4.8.0
with:
config: cliff.toml
args: ${{ steps.release-notes-range.outputs.args }}
env:
OUTPUT: NOTES.md

- name: Create or update GitHub release
if: github.event_name == 'push' || inputs.publish_to_npm == true
env:
Expand All @@ -148,19 +191,10 @@ jobs:

if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
gh release upload "$RELEASE_TAG" "${assets[@]}" --clobber
gh release edit "$RELEASE_TAG" --notes-file NOTES.md
else
gh release create "$RELEASE_TAG" "${assets[@]}" \
"${prerelease_flag[@]}" \
--title "$RELEASE_TAG"
--title "$RELEASE_TAG" \
--notes-file NOTES.md
fi

update-changelog:
name: Update CHANGELOG
needs: npm
if: github.event_name == 'push' || inputs.publish_to_npm == true
uses: stella/.github/.github/workflows/changelog-update.yml@d11bdc933dec609e291f6685f470b039d8342b6a
with:
tag: ${{ needs.npm.outputs.tag }}
permissions:
contents: write
pull-requests: write
61 changes: 61 additions & 0 deletions cliff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# git-cliff configuration: https://git-cliff.org/docs/configuration
#
# Generates GitHub release notes from Conventional Commits since the
# previous tag. Used by `publish.yml` to write `NOTES.md` and pass it
# to `gh release create --notes-file`. The repo enforces Conventional
# Commits in PR titles (`feat`, `fix`, `chore`, `docs`); anything that
# slips through unconventional is filtered out below.

[changelog]
header = ""
body = """
{% if version %}\
{% else %}\
## Unreleased
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | split(pat=":") | last }}
{% for commit in commits %}
- {% if commit.scope %}**{{ commit.scope }}**: {% endif %}{{ commit.message | split(pat="\n") | first | trim }}\
{% endfor %}
{% endfor %}\n
"""
footer = ""
trim = true
postprocessors = [
{ pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/stella/tooling/pull/${1}))" },
]

[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
filter_commits = false
tag_pattern = "^v[0-9]+\\.[0-9]+\\.[0-9]+(-rc\\.[0-9]+)?$"
sort_commits = "newest"

# Order matters twice over:
# - First matching parser wins (so put more specific patterns first,
# e.g. `chore(deps)` before `chore`).
# - Rendered section order follows the lexicographic order of `group`.
# The two-digit `NN:` prefix pins the order (00 → 99); the body
# template above strips everything before the colon, so the prefix
# never reaches the rendered markdown — keeps GitHub release notes,
# stll.app/changelog, and any other downstream renderer clean. Two
# digits leave headroom for new sections without breaking sort
# (single-digit `10` would otherwise sort before `2`).
commit_parsers = [
{ message = "^feat", group = "00:🚀 Features" },
{ message = "^fix", group = "01:🐛 Bug Fixes" },
{ message = "^perf", group = "02:⚡ Performance" },
{ message = "^refactor", group = "03:♻️ Refactor" },
{ message = "^docs", group = "04:📚 Documentation" },
{ message = "^test", group = "05:🧪 Tests" },
{ message = "^chore\\(deps[^)]*\\)", group = "06:📦 Dependencies" },
{ message = "^chore\\(release\\)", skip = true },
{ message = "^chore", group = "07:⚙️ Miscellaneous" },
Comment thread
jan-kubica marked this conversation as resolved.
{ message = "^style", skip = true },
{ message = "^ci", skip = true },
{ message = "^build", skip = true },
{ message = "^revert", group = "08:⏪ Reverts" },
]