-
Notifications
You must be signed in to change notification settings - Fork 0
119 lines (106 loc) · 5.04 KB
/
Copy pathrelease_main.yaml
File metadata and controls
119 lines (106 loc) · 5.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
name: Release on push to main
# Cuts a GitHub Release (and its tag) every time main advances — i.e. when a
# development -> main pull request is merged (a promotion to production).
#
# The version is DERIVED, not stored: this reads the highest existing v* release
# tag and bumps it. There is no version-bump commit pushed to any branch, so no
# bot needs write access to a protected branch.
#
# - First release ever (no v* tag yet): seed from package.json. With
# package.json at 0.1.1-alpha.0 this publishes v0.1.1-alpha.
# - Every release after that: bump the latest tag. Default is a patch bump
# (v0.1.1-alpha -> v0.1.2-alpha). The development -> main merge commit can
# opt into a larger bump via trigger words:
# "BREAKING CHANGE" / "type!:" / "[major]" -> major (v0.1.1-alpha -> v1.0.0-alpha)
# "feat:" / "feat(scope):" / "[minor]" -> minor (v0.1.1-alpha -> v0.2.0-alpha)
# anything else (default) -> patch (v0.1.1-alpha -> v0.1.2-alpha)
#
# npm keeps a numeric prerelease iterator internally (e.g. -alpha.0); it is
# stripped for the tag/release name only (v0.1.2-alpha.0 -> v0.1.2-alpha).
#
# package.json's version is no longer the source of truth after the first
# release — the release tags are. It is kept only to seed that first release.
#
# Idempotent: if the current commit already carries a v* tag, or the computed
# tag already exists, the run is a no-op. Safe to re-run via workflow_dispatch.
on:
push:
branches: main
workflow_dispatch:
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout (full history + tags)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
- name: Derive next version and create the release
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Untrusted input — read from env, never inline into the script.
HEAD_COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
set -e
# Never release the same commit twice (idempotent re-runs / manual dispatch).
EXISTING_TAG="$(git tag -l 'v*' --points-at HEAD | head -n1)"
if [ -n "$EXISTING_TAG" ]; then
echo "Commit already released as ${EXISTING_TAG} — nothing to do."
exit 0
fi
# Resolve the bump level from the (merge) commit message; default patch.
LEVEL="patch"
if printf '%s' "$HEAD_COMMIT_MSG" | grep -qiE 'BREAKING CHANGE|\[major\]|(^|[^a-z])[a-z]+(\([^)]*\))?!:'; then
LEVEL="major"
elif printf '%s' "$HEAD_COMMIT_MSG" | grep -qiE '\[minor\]|(^|[^a-z])feat(\([^)]*\))?:'; then
LEVEL="minor"
fi
echo "Bump level resolved from commit message: ${LEVEL}"
# Find the highest existing release tag (version-aware sort).
LATEST_TAG="$(git tag -l 'v*' --sort=-v:refname | head -n1)"
if [ -z "$LATEST_TAG" ]; then
# First release: seed from package.json. The version is carried as-is
# here; the iterator strip below turns 0.1.1-alpha.0 into the
# v0.1.1-alpha tag.
NEXT="$(node -p "require('./package.json').version")"
echo "No prior release tag — seeding first release from package.json: ${NEXT}"
else
# Derive the next version by bumping the latest tag with npm's semver engine.
BASE="${LATEST_TAG#v}"
TMP="$(mktemp -d)"
printf '{"name":"x","version":"%s"}\n' "$BASE" > "$TMP/package.json"
( cd "$TMP" && npm version "pre${LEVEL}" --preid=alpha --no-git-tag-version >/dev/null )
NEXT="$(node -p "require('${TMP}/package.json').version")"
rm -rf "$TMP"
echo "Latest release ${LATEST_TAG} -> next version ${NEXT}"
fi
# Strip the numeric prerelease iterator for the tag only (-alpha.0 -> -alpha).
TAG_VERSION="$NEXT"
if [[ "$NEXT" =~ -[0-9A-Za-z-]+[.][0-9]+$ ]]; then
TAG_VERSION="${NEXT%.*}"
fi
TAG="v${TAG_VERSION}"
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists — nothing to do."
exit 0
fi
# Publish as the repo's "Latest" release rather than a GitHub
# pre-release. While in early access every promotion is an -alpha, and
# "Latest" is the signal consumers actually follow — so the newest
# promotion must own it. The overall pre-release/early-access state is
# carried by the separate `early-access` tag/release, not by flagging
# each version as a GitHub pre-release. --latest is explicit so the new
# release always takes the badge from the prior one.
gh release create "$TAG" \
--target "$GITHUB_SHA" \
--title "$TAG" \
--generate-notes \
--latest
echo "Published release $TAG"