Skip to content

Commit e0420c0

Browse files
authored
Merge pull request #1486 from EliahKagan/releasing
Modernize release CI workflow and include universal binaries and checksums
2 parents 12313f2 + c307b72 commit e0420c0

File tree

1 file changed

+208
-77
lines changed

1 file changed

+208
-77
lines changed

.github/workflows/release.yml

Lines changed: 208 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# This is largely adapted from past and recent versions of the ripgrep release workflow.
1+
# Much of this workflow is adapted from the ripgrep release workflow.
22
# https://github.com/BurntSushi/ripgrep/blob/master/.github/workflows/release.yml
33

44
name: release
@@ -12,59 +12,64 @@ on:
1212
tags:
1313
- 'v*'
1414

15-
env:
16-
RUST_BACKTRACE: 1
17-
CARGO_TERM_COLOR: always
18-
CLICOLOR: 1
19-
2015
defaults:
2116
run:
2217
shell: bash
2318

2419
jobs:
25-
# The create-release job runs purely to initialize the GitHub release itself,
26-
# and names the release after the version tag that was pushed. It's separate
27-
# from building the release so that we only create the release once.
20+
# Create a draft release, initially with no binary assets attached.
2821
create-release:
29-
name: create-release
3022
runs-on: ubuntu-latest
23+
3124
# env:
3225
# # Set to force version number, e.g., when no tag exists.
3326
# VERSION: TEST-0.0.0
27+
3428
steps:
35-
- name: Create artifacts directory
36-
run: mkdir artifacts
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
3731

3832
- name: Get the release version from the tag
3933
if: env.VERSION == ''
40-
run: echo 'VERSION=${{ github.ref_name }}' >> "$GITHUB_ENV"
34+
run: echo "VERSION=$REF_NAME" >> "$GITHUB_ENV"
35+
env:
36+
REF_NAME: ${{ github.ref_name }}
4137

42-
- name: Create GitHub release
43-
id: release
44-
uses: ncipollo/release-action@v1
45-
with:
46-
tag: ${{ env.VERSION }}
47-
name: ${{ env.VERSION }}
48-
allowUpdates: true
49-
omitBody: true
50-
omitPrereleaseDuringUpdate: true
51-
token: ${{ secrets.GITHUB_TOKEN }}
38+
- name: Validate version against Cargo.toml
39+
run: |
40+
manifest_version="$(yq -r .package.version Cargo.toml)"
41+
echo "version to name the release: $VERSION"
42+
echo "version Cargo.toml suggests: v$manifest_version"
5243
53-
- name: Save release upload URL to artifact
54-
run: echo '${{ steps.release.outputs.upload_url }}' > artifacts/release-upload-url
44+
case "$VERSION" in
45+
"v$manifest_version" )
46+
echo 'OK: Release name/version agrees with Cargo.toml version.'
47+
;;
48+
TEST-* | *-DO-NOT-USE )
49+
echo 'OK: Release name/version is strange but marked as such.'
50+
;;
51+
"$manifest_version" )
52+
echo 'STOPPING: Release name/version is missing the leading "v".'
53+
exit 1
54+
;;
55+
* )
56+
echo 'STOPPING: Release name/version and Cargo.toml version do not match.'
57+
echo 'STOPPING: Usually this means either a wrong tag name or wrong version in Cargo.toml.'
58+
echo 'STOPPING: If intended, prepend `TEST-` or append `-DO-NOT-USE` to the release name.'
59+
exit 1
60+
;;
61+
esac
5562
56-
- name: Save version number to artifact
57-
run: echo "$VERSION" > artifacts/release-version
63+
- name: Create GitHub release
64+
run: gh release create "$VERSION" --title="$VERSION" --draft
65+
env:
66+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5867

59-
- name: Upload artifacts
60-
uses: actions/upload-artifact@v4
61-
with:
62-
name: artifacts
63-
path: artifacts
68+
outputs:
69+
version: ${{ env.VERSION }}
6470

71+
# Build for a particular feature and target, and attach an archive for it.
6572
build-release:
66-
name: build-release
67-
6873
needs: [ create-release ]
6974

7075
strategy:
@@ -78,11 +83,8 @@ jobs:
7883
- x86_64-pc-windows-gnu
7984
- i686-pc-windows-msvc
8085
- aarch64-pc-windows-msvc
81-
feature:
82-
- small
83-
- lean
84-
- max
85-
- max-pure
86+
# When changing these features, make the same change in build-macos-universal2-release.
87+
feature: [ small, lean, max, max-pure ]
8688
include:
8789
- target: x86_64-unknown-linux-musl
8890
os: ubuntu-latest
@@ -128,21 +130,28 @@ jobs:
128130
runs-on: ${{ matrix.os }}
129131

130132
env:
131-
CARGO: cargo # On Linux, this will be changed to `cross` later.
133+
RUST_BACKTRACE: '1' # Emit backtraces on panics.
134+
CARGO_TERM_COLOR: always
135+
CLICOLOR: '1'
136+
CARGO: cargo # On Linux, this will be changed to `cross` in a later step.
137+
FEATURE: ${{ matrix.feature }}
138+
VERSION: ${{ needs.create-release.outputs.version }}
139+
TARGET: ${{ matrix.target }}
132140
TARGET_FLAGS: --target=${{ matrix.target }}
133-
TARGET_DIR: ./target/${{ matrix.target }}
134-
RUST_BACKTRACE: 1 # Emit backtraces on panics.
141+
TARGET_DIR: target/${{ matrix.target }}
135142

136143
steps:
137144
- name: Checkout repository
138145
uses: actions/checkout@v4
139146

140147
- name: Install packages (Ubuntu)
141-
# Because openssl doesn't work on musl by default, we resort to max-pure. And that won't need any dependency, so we can skip this.continue-on-error
148+
# Because openssl doesn't work on musl by default, we resort to max-pure.
149+
# And that won't need any dependency, so we can skip this or use `continue-on-error`.
142150
# Once we want to support better zlib performance, we might have to re-add it.
143151
if: matrix.os == 'ubuntu-latest-disabled'
144152
run: |
145-
sudo apt-get update && sudo apt-get install -y --no-install-recommends xz-utils liblz4-tool musl-tools
153+
sudo apt-get update
154+
sudo apt-get install -y --no-install-recommends xz-utils liblz4-tool musl-tools
146155
147156
- name: Install Rust
148157
uses: dtolnay/rust-toolchain@master
@@ -162,20 +171,9 @@ jobs:
162171
echo "target flag is: $TARGET_FLAGS"
163172
echo "target dir is: $TARGET_DIR"
164173
165-
- name: Get release download URL
166-
uses: actions/download-artifact@v4
167-
with:
168-
name: artifacts
169-
path: artifacts
170-
171-
- name: Set release upload URL and release version
172-
run: |
173-
echo "UPLOAD_URL=$(< artifacts/release-upload-url)" >> "$GITHUB_ENV"
174-
echo "VERSION=$(< artifacts/release-version)" >> "$GITHUB_ENV"
175-
176174
- name: Build release binary
177175
run: |
178-
"$CARGO" build --verbose --release "$TARGET_FLAGS" --no-default-features --features ${{ matrix.feature }}
176+
"$CARGO" build --verbose --release "$TARGET_FLAGS" --no-default-features --features "$FEATURE"
179177
180178
- name: Strip release binary (x86-64 Linux, and all macOS)
181179
if: matrix.target == 'x86_64-unknown-linux-musl' || matrix.os == 'macos-latest'
@@ -191,31 +189,164 @@ jobs:
191189
/target/arm-unknown-linux-gnueabihf/release/ein \
192190
/target/arm-unknown-linux-gnueabihf/release/gix
193191
194-
- name: Build archive
192+
- name: Determine archive basename
193+
run: echo "ARCHIVE=gitoxide-$FEATURE-$VERSION-$TARGET" >> "$GITHUB_ENV"
194+
195+
- name: Pre-populate directory for archive
195196
run: |
196-
staging='gitoxide-${{ matrix.feature }}-${{ env.VERSION }}-${{ matrix.target }}'
197-
mkdir -p -- "$staging"
197+
mkdir -- "$ARCHIVE"
198+
cp {README.md,LICENSE-*,CHANGELOG.md} "$ARCHIVE/"
198199
199-
cp {README.md,LICENSE-*,CHANGELOG.md} "$staging/"
200+
- name: Build archive (Windows)
201+
if: matrix.os == 'windows-latest'
202+
run: |
203+
file -- "$TARGET_DIR"/release/{ein,gix}.exe
204+
cp -- "$TARGET_DIR"/release/{ein,gix}.exe "$ARCHIVE/"
205+
7z a "$ARCHIVE.zip" "$ARCHIVE"
206+
/usr/bin/core_perl/shasum --algorithm=256 --binary "$ARCHIVE.zip" > "$ARCHIVE.zip.sha256"
207+
echo "ASSET=$ARCHIVE.zip" >> "$GITHUB_ENV"
208+
echo "ASSET_SUM=$ARCHIVE.zip.sha256" >> "$GITHUB_ENV"
200209
201-
if [ '${{ matrix.os }}' = 'windows-latest' ]; then
202-
file -- "$TARGET_DIR"/release/{ein,gix}.exe
203-
cp -- "$TARGET_DIR"/release/{ein,gix}.exe "$staging/"
204-
7z a "$staging.zip" "$staging"
205-
echo "ASSET=$staging.zip" >> "$GITHUB_ENV"
206-
else
207-
file -- "$TARGET_DIR"/release/{ein,gix}
208-
cp -- "$TARGET_DIR"/release/{ein,gix} "$staging/"
209-
tar czf "$staging.tar.gz" "$staging"
210-
echo "ASSET=$staging.tar.gz" >> "$GITHUB_ENV"
211-
fi
210+
- name: Build archive (Unix)
211+
if: matrix.os != 'windows-latest'
212+
run: |
213+
file -- "$TARGET_DIR"/release/{ein,gix}
214+
cp -- "$TARGET_DIR"/release/{ein,gix} "$ARCHIVE/"
215+
tar czf "$ARCHIVE.tar.gz" "$ARCHIVE"
216+
shasum --algorithm=256 --binary "$ARCHIVE.tar.gz" > "$ARCHIVE.tar.gz.sha256"
217+
echo "ASSET=$ARCHIVE.tar.gz" >> "$GITHUB_ENV"
218+
echo "ASSET_SUM=$ARCHIVE.tar.gz.sha256" >> "$GITHUB_ENV"
212219
213220
- name: Upload release archive
214-
uses: actions/upload-release-asset@v1.0.2
221+
run: gh release upload "$VERSION" "$ASSET" "$ASSET_SUM"
222+
env:
223+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
224+
225+
# Add a macOS universal binary archive for a feature using its built aarch64 and x86_64 assets.
226+
build-macos-universal2-release:
227+
runs-on: macos-latest
228+
229+
needs: [ create-release, build-release ]
230+
231+
strategy:
232+
matrix:
233+
# These features need to be exactly the same as the features in build-release.
234+
feature: [ small, lean, max, max-pure ]
235+
236+
env:
237+
BASH_ENV: ./helpers.sh
238+
REPOSITORY: ${{ github.repository }}
239+
FEATURE: ${{ matrix.feature }}
240+
VERSION: ${{ needs.create-release.outputs.version }}
241+
242+
steps:
243+
- name: Define helper function
244+
run: |
245+
name() { echo "gitoxide-$FEATURE-$VERSION-$1-apple-darwin"; }
246+
declare -f name >> "$BASH_ENV"
247+
248+
- name: Obtain single-architecture releases
249+
run: |
250+
gh release --repo="$REPOSITORY" download "$VERSION" \
251+
--pattern="$(name aarch64).tar.gz" --pattern="$(name aarch64).tar.gz.sha256" \
252+
--pattern="$(name x86_64).tar.gz" --pattern="$(name x86_64).tar.gz.sha256"
253+
env:
254+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
255+
256+
- name: Unpack single-architecture releases
257+
run: |
258+
shasum --check "$(name aarch64).tar.gz.sha256" "$(name x86_64).tar.gz.sha256"
259+
tar xf "$(name aarch64).tar.gz"
260+
tar xf "$(name x86_64).tar.gz"
261+
262+
- name: Determine archive basename
263+
run: echo "ARCHIVE=$(name universal)" >> "$GITHUB_ENV"
264+
265+
- name: Pre-populate directory for archive
266+
run: |
267+
cp -R -- "$(name aarch64)" "$ARCHIVE"
268+
rm -- "$ARCHIVE"/{ein,gix}
269+
270+
- name: Create Universal 2 binaries
271+
run: |
272+
for bin in ein gix; do
273+
lipo -create "$(name aarch64)/$bin" "$(name x86_64)/$bin" -output "$ARCHIVE/$bin"
274+
file -- "$ARCHIVE/$bin"
275+
done
276+
277+
- name: Build archive
278+
run: |
279+
tar czf "$ARCHIVE.tar.gz" "$ARCHIVE"
280+
shasum --algorithm=256 --binary "$ARCHIVE.tar.gz" > "$ARCHIVE.tar.gz.sha256"
281+
echo "ASSET=$ARCHIVE.tar.gz" >> "$GITHUB_ENV"
282+
echo "ASSET_SUM=$ARCHIVE.tar.gz.sha256" >> "$GITHUB_ENV"
283+
284+
- name: Upload release archive
285+
run: gh release --repo="$REPOSITORY" upload "$VERSION" "$ASSET" "$ASSET_SUM"
286+
env:
287+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
288+
289+
# Check for some problems, consolidate checksum files into one, and mark the release non-draft.
290+
publish-release:
291+
runs-on: ubuntu-latest
292+
293+
needs: [ create-release, build-release, build-macos-universal2-release ]
294+
295+
env:
296+
REPOSITORY: ${{ github.repository }}
297+
VERSION: ${{ needs.create-release.outputs.version }}
298+
299+
steps:
300+
- name: Discover assets
301+
run: |
302+
gh release --repo="$REPOSITORY" view "$VERSION" --json assets --jq '.assets.[].name' > assets.txt
303+
env:
304+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
305+
306+
- name: Show all individual asset names
307+
run: cat assets.txt
308+
309+
# The `features` array is repeated because GHA doen't support YAML anchors.
310+
# We will check that the macOS `universal` features match the others exactly.
311+
# In the future this and the next step may be removed, or expanded to do more validation.
312+
- name: Extract macOS asset names by architecture
313+
run: |
314+
for arch in aarch64 x86_64 universal; do
315+
grep -Fw "$arch-apple-darwin" assets.txt | sort | tee -- "$arch.txt"
316+
done
317+
318+
- name: Check macOS archive features
319+
run: |
320+
mask() { sed -r 's/\w+-apple-darwin/<arch>-apple-darwin/' -- "$1.txt"; }
321+
diff -- <(mask aarch64) <(mask universal)
322+
diff -- <(mask x86_64) <(mask universal)
323+
324+
- name: Clean up local temporary macOS asset list files
325+
run: rm {assets,aarch64,x86_64,universal}.txt
326+
327+
- name: Retrieve all individual checksums
328+
run: gh release --repo="$REPOSITORY" download "$VERSION" --pattern='gitoxide-*.sha256'
329+
env:
330+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
331+
332+
- name: Concatenate checksums into one file
333+
run: cat gitoxide-*.sha256 > hashes.sha256
334+
335+
- name: Upload the combined checksum file
336+
run: gh release --repo="$REPOSITORY" upload "$VERSION" hashes.sha256
337+
env:
338+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
339+
340+
# If any step of any job fails before this, the draft still has the individual checksum files.
341+
- name: Remove the individual checksum file assets
342+
run: |
343+
for sumfile in gitoxide-*.sha256; do
344+
gh release --repo="$REPOSITORY" delete-asset "$VERSION" "$sumfile" --yes
345+
done
346+
env:
347+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
348+
349+
- name: Publish the release
350+
run: gh release --repo="$REPOSITORY" edit "$VERSION" --draft=false
215351
env:
216352
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
217-
with:
218-
upload_url: ${{ env.UPLOAD_URL }}
219-
asset_path: ${{ env.ASSET }}
220-
asset_name: ${{ env.ASSET }}
221-
asset_content_type: application/octet-stream

0 commit comments

Comments
 (0)