Skip to content

Commit 784bc53

Browse files
authored
Create workflow for cutting a release (#3270)
1 parent 4942418 commit 784bc53

1 file changed

Lines changed: 220 additions & 0 deletions

File tree

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
name: Create Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: 'The new version to tag, ex: 1.0.5'
8+
required: true
9+
type: string
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
15+
jobs:
16+
create-release:
17+
runs-on: macos-15
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Set up Bazelisk
25+
uses: bazelbuild/setup-bazelisk@v3
26+
27+
- name: Resolve previous tag
28+
id: tags
29+
run: |
30+
TAG="${{ inputs.tag }}"
31+
git fetch --tags --force
32+
33+
PREVIOUS_TAG="$(git tag --merged HEAD --sort=-v:refname | head -n 1)"
34+
if [[ -z "$PREVIOUS_TAG" ]]; then
35+
echo "No existing tags found; unable to determine previous tag." >&2
36+
exit 1
37+
fi
38+
39+
if [[ "$PREVIOUS_TAG" == "$TAG" ]]; then
40+
PREVIOUS_TAG="$(git tag --merged HEAD --sort=-v:refname | sed -n '2p')"
41+
fi
42+
if [[ -z "$PREVIOUS_TAG" ]]; then
43+
echo "Unable to determine previous tag." >&2
44+
exit 1
45+
fi
46+
47+
echo "previous_tag=$PREVIOUS_TAG" >> "$GITHUB_OUTPUT"
48+
49+
- name: Build release archive
50+
env:
51+
BUILDBUDDY_RBE_API_KEY: ${{ secrets.BUILDBUDDY_RBE_API_KEY }}
52+
run: |
53+
bazel build \
54+
--config=remote \
55+
--remote_header="x-buildbuddy-api-key=${BUILDBUDDY_RBE_API_KEY}" \
56+
//distribution:release
57+
58+
- name: Compute integrity
59+
id: integrity
60+
run: |
61+
SHA_PATH="bazel-bin/distribution/release.tar.gz.sha256"
62+
if [[ ! -f "$SHA_PATH" ]]; then
63+
echo "Missing $SHA_PATH" >&2
64+
ls -la bazel-bin/distribution >&2 || true
65+
exit 1
66+
fi
67+
68+
INTEGRITY="$(cat "$SHA_PATH" \
69+
| cut -d ' ' -f 1 \
70+
| xxd -r -p \
71+
| openssl base64 -A \
72+
| sed 's/^/sha256-/')"
73+
echo "integrity=$INTEGRITY" >> "$GITHUB_OUTPUT"
74+
75+
- name: Generate release notes
76+
run: |
77+
TAG="${{ inputs.tag }}"
78+
PREVIOUS_TAG="${{ steps.tags.outputs.previous_tag }}"
79+
INTEGRITY="${{ steps.integrity.outputs.integrity }}"
80+
INTEGRITY_ESCAPED="$(printf '%s' "$INTEGRITY" | sed -e 's/[\\/&|]/\\&/g')"
81+
82+
sed -e "s/%CURRENT_TAG%/${TAG}/g" \
83+
-e "s/%PREVIOUS_TAG%/${PREVIOUS_TAG}/g" \
84+
-e "s|%INTEGRITY%|${INTEGRITY_ESCAPED}|g" \
85+
distribution/release_notes_template.md > release_notes.md
86+
87+
- name: Create annotated tag
88+
run: |
89+
TAG="${{ inputs.tag }}"
90+
git config user.name "github-actions[bot]"
91+
git config user.email "github-actions[bot]@users.noreply.github.com"
92+
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
93+
echo "Tag $TAG already exists." >&2
94+
exit 1
95+
fi
96+
97+
git tag -a "$TAG" -m "Release $TAG"
98+
git push origin "$TAG"
99+
100+
- name: Create draft release
101+
env:
102+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
103+
run: |
104+
TAG="${{ inputs.tag }}"
105+
gh release create "$TAG" \
106+
--title "$TAG" \
107+
--draft \
108+
--notes-file release_notes.md \
109+
"bazel-bin/distribution/release.tar.gz" \
110+
"bazel-bin/distribution/release.tar.gz.sha256"
111+
112+
- name: Update changelog and open draft PR
113+
env:
114+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
115+
run: |
116+
set -euo pipefail
117+
118+
TAG="${{ inputs.tag }}"
119+
PREVIOUS_TAG="${{ steps.tags.outputs.previous_tag }}"
120+
TODAY="$(date -u +%Y-%m-%d)"
121+
BRANCH="release-notes-${TAG}"
122+
BASE_BRANCH="${GITHUB_REF_NAME}"
123+
124+
export TAG
125+
export PREVIOUS_TAG
126+
export TODAY
127+
export BASE_BRANCH
128+
129+
git checkout -b "$BRANCH"
130+
131+
python3 - <<'PY'
132+
import os
133+
import re
134+
from pathlib import Path
135+
136+
tag = os.environ["TAG"]
137+
previous_tag = os.environ["PREVIOUS_TAG"]
138+
today = os.environ["TODAY"]
139+
140+
path = Path("CHANGELOG.md")
141+
text = path.read_text()
142+
143+
begin = "BEGIN_UNRELEASED_TEMPLATE"
144+
end = "END_UNRELEASED_TEMPLATE"
145+
begin_idx = text.find(begin)
146+
end_idx = text.find(end)
147+
if begin_idx == -1 or end_idx == -1:
148+
raise SystemExit("Unreleased template markers not found in CHANGELOG.md")
149+
150+
template_block = text[begin_idx:end_idx].splitlines()
151+
template_lines = []
152+
in_block = False
153+
for line in template_block:
154+
if line.strip() == begin:
155+
in_block = True
156+
continue
157+
if in_block:
158+
template_lines.append(line)
159+
template = "\n".join(template_lines).strip("\n")
160+
if not template:
161+
raise SystemExit("Unreleased template content is empty")
162+
163+
search_start = end_idx
164+
unreleased_match = re.search(
165+
r'<a id="unreleased"></a>\n## \[Unreleased\]\n',
166+
text[search_start:],
167+
)
168+
if not unreleased_match:
169+
raise SystemExit("Unreleased section not found in CHANGELOG.md")
170+
unreleased_start = search_start + unreleased_match.start()
171+
172+
next_anchor = re.search(r'\n<a id="[^"]+"></a>\n## \[', text[unreleased_start + 1 :])
173+
if not next_anchor:
174+
raise SystemExit("Unable to find end of Unreleased section")
175+
unreleased_end = unreleased_start + 1 + next_anchor.start()
176+
177+
unreleased_section = text[unreleased_start:unreleased_end].strip("\n")
178+
179+
release_section = unreleased_section
180+
release_section = release_section.replace('<a id="unreleased"></a>', f'<a id="{tag}"></a>', 1)
181+
release_section = release_section.replace('## [Unreleased]', f'## [{tag}] - {today}', 1)
182+
release_section = re.sub(
183+
r'\[Unreleased\]: .*',
184+
f'[{tag}]: https://github.com/MobileNativeFoundation/rules_xcodeproj/compare/{previous_tag}...{tag}',
185+
release_section,
186+
count=1,
187+
)
188+
189+
new_unreleased = template.replace("%PREVIOUS_TAG%", tag)
190+
191+
new_text = (
192+
text[:unreleased_start].rstrip("\n")
193+
+ "\n\n"
194+
+ new_unreleased.strip("\n")
195+
+ "\n\n"
196+
+ release_section.strip("\n")
197+
+ "\n"
198+
+ text[unreleased_end:].lstrip("\n")
199+
)
200+
201+
path.write_text(new_text)
202+
PY
203+
204+
git add CHANGELOG.md
205+
if git diff --cached --quiet; then
206+
echo "No changelog changes to commit." >&2
207+
exit 0
208+
fi
209+
210+
git config user.name "github-actions[bot]"
211+
git config user.email "github-actions[bot]@users.noreply.github.com"
212+
git commit -m "Update CHANGELOG for ${TAG}"
213+
git push origin "$BRANCH"
214+
215+
gh pr create \
216+
--draft \
217+
--title "Update CHANGELOG for ${TAG}" \
218+
--body "Updates CHANGELOG.md for ${TAG} and resets the Unreleased section." \
219+
--base "$BASE_BRANCH" \
220+
--head "$BRANCH"

0 commit comments

Comments
 (0)