Repository navigation
135 lines (123 loc) · 6.73 KB
/
Copy pathrelease.yml
File metadata and controls
135 lines (123 loc) · 6.73 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
name: release
run-name: Release the Action from main
on:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: release
cancel-in-progress: false
jobs:
release:
name: release
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
actions: read
checks: read
contents: write
steps:
- name: Require promoted main qualified against a stable Rootform release
id: qualification
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
fail() { echo "::error::$1"; exit 1; }
[[ "$GITHUB_REF" == refs/heads/main ]] || fail "Release the Action from main: gh workflow run release.yml --ref main."
main="$(gh api "repos/$GITHUB_REPOSITORY/branches/main" --jq .commit.sha)"
[[ "$main" == "$GITHUB_SHA" ]] || fail "main moved to $main after this run started. Dispatch again."
quality="$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/check-runs?check_name=quality&per_page=100" \
--jq '[.check_runs[] | select(.app.slug == "github-actions")] | sort_by(.started_at) | last | .conclusion // "missing"')"
[[ "$quality" == success ]] ||
fail "quality has not passed on $GITHUB_SHA (newest result: $quality). main changes only through a promotion pull request."
integration="$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/published-release-integration.yml/runs?head_sha=$GITHUB_SHA&status=success&per_page=100" \
--jq '[.workflow_runs[] | select(.display_title | test("^Install published Rootform v?[0-9]+[.][0-9]+[.][0-9]+$"))] | first | .html_url // empty')"
[[ -n "$integration" ]] ||
fail "No successful published release integration against a stable Rootform release on $GITHUB_SHA. Run gh workflow run published-release-integration.yml --ref main -f version=<stable Rootform version> first."
echo "integration=$integration" >> "$GITHUB_OUTPUT"
echo "main at \`$GITHUB_SHA\` passed quality and $integration." >> "$GITHUB_STEP_SUMMARY"
- name: Check out full history
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Install exact Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.4.0
- name: Install the Node runtime semantic-release requires
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24.20.0
- name: Install exact foundation dependencies
run: bun install --frozen-lockfile
- name: Publish the release for this commit
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node ./node_modules/semantic-release/bin/semantic-release.js
- name: Confirm the release of this commit
id: release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
fail() { echo "::error::$1"; exit 1; }
tag="$(git tag --points-at "$GITHUB_SHA" --list 'v*.*.*' | sort -V | tail -n 1)"
if [[ -z "$tag" ]]; then
echo "No feat, fix or breaking change since the previous release: nothing was published." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
if ! release="$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$tag" 2>/dev/null)"; then
# semantic-release pushes the tag before it publishes the release; finish an interrupted run.
gh release create "$tag" --repo "$GITHUB_REPOSITORY" --verify-tag --generate-notes --title "$tag"
release="$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$tag")"
fi
jq -e '.draft == false' <<<"$release" >/dev/null || fail "$tag is still a draft."
[[ "$(gh api "repos/$GITHUB_REPOSITORY/commits/$tag" --jq .sha)" == "$GITHUB_SHA" ]] || fail "$tag does not name $GITHUB_SHA."
# The repository enforces immutable releases. GitHub can report the lock a few seconds after publication.
for attempt in 1 2 3 4 5 6; do
jq -e '.immutable == true' <<<"$release" >/dev/null && break
[[ "$attempt" -lt 6 ]] ||
fail "$tag is published but not immutable, so the major tag was not moved. Enable release immutability, delete this release (keep its tag) and rerun: the run republishes it immutable."
sleep $((attempt * 5))
release="$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/$tag")"
done
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "Published immutable $(jq -r .html_url <<<"$release") at \`$GITHUB_SHA\`." >> "$GITHUB_STEP_SUMMARY"
- name: Point the major tag at this release
if: steps.release.outputs.tag != ''
env:
GH_TOKEN: ${{ github.token }}
INTEGRATION: ${{ steps.qualification.outputs.integration }}
RELEASE: ${{ steps.release.outputs.tag }}
run: |
set -euo pipefail
fail() { echo "::error::$1"; exit 1; }
if [[ ! "$RELEASE" =~ ^v([1-9][0-9]*)[.][0-9]+[.][0-9]+$ ]]; then
echo "$RELEASE is a 0.x or prerelease version: no major tag names it." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
major="v${BASH_REMATCH[1]}"
current=''
if ref="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$major" 2>/dev/null)"; then
current="$(jq -r .object.sha <<<"$ref")"
if [[ "$(jq -r .object.type <<<"$ref")" == tag ]]; then
current="$(gh api "repos/$GITHUB_REPOSITORY/git/tags/$current" --jq .object.sha)"
fi
fi
if [[ "$current" == "$GITHUB_SHA" ]]; then
echo "$major already names $RELEASE." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
if [[ -n "$current" ]]; then
status="$(gh api "repos/$GITHUB_REPOSITORY/compare/$current...$GITHUB_SHA" --jq .status)"
[[ "$status" == ahead ]] ||
fail "$major names $current, which $RELEASE does not descend from (comparison: $status). A major tag only moves forward."
gh api -X PATCH "repos/$GITHUB_REPOSITORY/git/refs/tags/$major" -f sha="$GITHUB_SHA" -F force=true >/dev/null
else
gh api -X POST "repos/$GITHUB_REPOSITORY/git/refs" -f ref="refs/tags/$major" -f sha="$GITHUB_SHA" >/dev/null
fi
[[ "$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$major" --jq .object.sha)" == "$GITHUB_SHA" ]] ||
fail "$major does not name $GITHUB_SHA after the update."
echo "$major now names $RELEASE at \`$GITHUB_SHA\` (previously \`${current:-absent}\`), qualified by $INTEGRATION." >> "$GITHUB_STEP_SUMMARY"