Skip to content

Commit ee35223

Browse files
committed
Add cargo-dist for automated binary distribution
Configure cross-platform release builds using cargo-dist: - Target x86_64/aarch64 for Linux and macOS - Generate shell and PowerShell installers - Enable GitHub attestations for build provenance - Separate release workflow from CI checks The new release.yml workflow handles: - Planning release artifacts - Building per-platform binaries - Generating global artifacts (installers) - Publishing to GitHub Releases on tag push This replaces the previous manual release workflow in ci.yml.
1 parent b8923dc commit ee35223

4 files changed

Lines changed: 251 additions & 114 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -53,117 +53,5 @@ jobs:
5353
- name: Run cargo clippy
5454
run: cargo clippy --all-targets --all-features
5555

56-
release:
57-
runs-on: macos-latest
58-
needs:
59-
- test
60-
- lints
61-
- check
62-
outputs:
63-
new_version: ${{ steps.check_for_version_changes.outputs.new_version }}
64-
changed: ${{ steps.check_for_version_changes.outputs.changed }}
65-
if: github.ref == 'refs/heads/main'
66-
steps:
67-
- uses: actions/checkout@v3
68-
with:
69-
# https://stackoverflow.com/questions/65944700/how-to-run-git-diff-in-github-actions
70-
# TLDR – By default this action fetches no history.
71-
# We need a bit of history to be able to check if we've recently updated the version in Cargo.toml
72-
fetch-depth: 2
73-
- name: Toolchain info
74-
run: |
75-
cargo --version --verbose
76-
rustc --version
77-
cargo clippy --version
78-
- name: Build
79-
run: cargo build --release --target aarch64-apple-darwin --target x86_64-apple-darwin
80-
- name: Check for version changes in Cargo.toml
81-
id: check_for_version_changes
82-
run: |
83-
# When there are no changes, VERSION_CHANGES will be empty
84-
# Without the echo, this command would exit with a 1, causing the GitHub Action to fail
85-
# Instead, we want it to succeed, but just evaluate `changed=false` in the other branch of the conditional
86-
VERSION_CHANGES=$(git diff HEAD~1 HEAD Cargo.toml | grep "\+version" || echo "")
87-
if [[ -n $VERSION_CHANGES ]]; then
88-
NEW_VERSION=$(echo $VERSION_CHANGES | awk -F'"' '{print $2}')
89-
echo "changed=true" >> $GITHUB_OUTPUT
90-
echo "new_version=v$NEW_VERSION" >> $GITHUB_OUTPUT
91-
else
92-
echo "changed=false" >> $GITHUB_OUTPUT
93-
fi
94-
95-
- name: Create GitHub Release if current commit has updated the version in Cargo.toml
96-
if: steps.check_for_version_changes.outputs.changed == 'true'
97-
run: |
98-
gh release create ${{steps.check_for_version_changes.outputs.new_version}} --target "${{ github.sha }}" --generate-notes
99-
env:
100-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
101-
upload-mac-universal-bin:
102-
needs: release
103-
runs-on: macos-latest
104-
if: ${{needs.release.outputs.new_version}}
105-
steps:
106-
- uses: actions/checkout@v3
107-
- name: Build
108-
run: cargo build --release --target aarch64-apple-darwin --target x86_64-apple-darwin
109-
110-
- name: Upload mac universal binary
111-
run: |
112-
# This combines the intel and m1 binaries into a single binary
113-
lipo -create -output target/pks target/aarch64-apple-darwin/release/pks target/x86_64-apple-darwin/release/pks
114-
115-
# Creates artifact for homebrew. -C means run from `target` directory
116-
tar -czf target/pks-mac.tar.gz -C target pks
117-
118-
# This tarball is a binary that is executable
119-
gh release upload $NEW_VERSION target/pks-mac.tar.gz
120-
121-
env:
122-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123-
NEW_VERSION: ${{ needs.release.outputs.new_version }}
124-
125-
upload-linux-bin:
126-
needs: release
127-
if: ${{needs.release.outputs.new_version}}
128-
runs-on: ubuntu-latest
129-
steps:
130-
- uses: actions/checkout@v4
131-
- name: Update local toolchain
132-
run: |
133-
cargo install cross
134-
- name: Build linux binaries
135-
run: |
136-
cross build --release --target x86_64-unknown-linux-gnu
137-
cross build --release --target aarch64-unknown-linux-gnu
138-
- name: Upload linux binaries
139-
run: |
140-
tar -czf target/x86_64-unknown-linux-gnu.tar.gz -C target/x86_64-unknown-linux-gnu/release pks
141-
tar -czf target/aarch64-unknown-linux-gnu.tar.gz -C target/aarch64-unknown-linux-gnu/release pks
142-
gh release upload $NEW_VERSION target/x86_64-unknown-linux-gnu.tar.gz
143-
gh release upload $NEW_VERSION target/aarch64-unknown-linux-gnu.tar.gz
144-
env:
145-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
146-
NEW_VERSION: ${{ needs.release.outputs.new_version }}
147-
148-
generate-dotslash-files:
149-
name: Generating and uploading DotSlash files
150-
needs:
151-
- release
152-
- upload-linux-bin
153-
- upload-mac-universal-bin
154-
if: success() && ${{needs.release.outputs.new_version}}
155-
runs-on: ubuntu-latest
156-
157-
steps:
158-
- uses: facebook/dotslash-publish-release@v1
159-
# This is necessary because the action uses
160-
# `gh release upload` to publish the generated DotSlash file(s)
161-
# as part of the release.
162-
env:
163-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
164-
with:
165-
# Additional file that lives in your repo that defines
166-
# how your DotSlash file(s) should be generated.
167-
config: .github/workflows/dotslash-config.json
168-
# Tag for the release to target.
169-
tag: ${{ needs.release.outputs.new_version }}
56+
# NOTE: Release builds are handled by release.yml using cargo-dist
57+
# This workflow focuses on CI checks only

.github/workflows/release.yml

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Copyright 2022-2024, axodotdev
2+
# SPDX-License-Identifier: MIT or Apache-2.0
3+
#
4+
# CI that:
5+
#
6+
# * checks for a Git Tag that looks like a release
7+
# * builds artifacts with cargo-dist (archives, installers, hashes)
8+
# * uploads those artifacts to temporary workflow zip
9+
# * on success, uploads the artifacts to a GitHub Release
10+
#
11+
# Note that the GitHub Release will be created with a generated
12+
# temporary title/body if the Tag Body is empty. You should update
13+
# the Release notes before announcing it.
14+
15+
name: Release
16+
17+
permissions:
18+
contents: write
19+
# For GitHub attestations
20+
id-token: write
21+
attestations: write
22+
23+
# This task will run whenever you push a tag to your repo
24+
# that looks like a version number.
25+
on:
26+
push:
27+
tags:
28+
- 'v[0-9]+.*'
29+
pull_request:
30+
31+
env:
32+
CARGO_TERM_COLOR: always
33+
34+
jobs:
35+
# Run 'cargo dist plan' to determine what tasks we need to do
36+
plan:
37+
runs-on: ubuntu-latest
38+
outputs:
39+
val: ${{ steps.plan.outputs.manifest }}
40+
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
41+
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
42+
publishing: ${{ !github.event.pull_request }}
43+
steps:
44+
- uses: actions/checkout@v4
45+
with:
46+
submodules: recursive
47+
- name: Install cargo-dist
48+
uses: taiki-e/install-action@v2
49+
with:
50+
tool: cargo-dist@0.27.0
51+
- name: Cache cargo registry
52+
uses: actions/cache@v4
53+
with:
54+
path: |
55+
~/.cargo/registry/index/
56+
~/.cargo/registry/cache/
57+
~/.cargo/git/db/
58+
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
59+
- id: plan
60+
run: |
61+
cargo dist plan ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} --output-format=json > plan-dist-manifest.json
62+
echo "manifest=$(jq -c '.' plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
63+
- name: Upload dist-manifest.json
64+
uses: actions/upload-artifact@v4
65+
with:
66+
name: artifacts-plan-dist-manifest
67+
path: plan-dist-manifest.json
68+
69+
# Build and upload artifacts for each platform
70+
build-local-artifacts:
71+
name: build-local-artifacts (${{ matrix.targets }})
72+
needs: plan
73+
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
74+
strategy:
75+
fail-fast: false
76+
matrix:
77+
include: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include }}
78+
runs-on: ${{ matrix.runner }}
79+
env:
80+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
81+
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
82+
steps:
83+
- uses: actions/checkout@v4
84+
with:
85+
submodules: recursive
86+
- name: Install cargo-dist
87+
uses: taiki-e/install-action@v2
88+
with:
89+
tool: cargo-dist@0.27.0
90+
- name: Install cross for Linux ARM builds
91+
if: ${{ contains(matrix.targets, 'aarch64-unknown-linux') }}
92+
run: cargo install cross --git https://github.com/cross-rs/cross
93+
- name: Cache cargo registry
94+
uses: actions/cache@v4
95+
with:
96+
path: |
97+
~/.cargo/registry/index/
98+
~/.cargo/registry/cache/
99+
~/.cargo/git/db/
100+
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
101+
- name: Build artifacts
102+
run: |
103+
cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
104+
echo "dist manifest: $(cat dist-manifest.json)"
105+
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
106+
- name: Upload artifacts
107+
uses: actions/upload-artifact@v4
108+
with:
109+
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
110+
path: |
111+
target/distrib/*
112+
113+
# Build and upload global artifacts
114+
build-global-artifacts:
115+
needs:
116+
- plan
117+
- build-local-artifacts
118+
runs-on: ubuntu-latest
119+
env:
120+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
121+
steps:
122+
- uses: actions/checkout@v4
123+
with:
124+
submodules: recursive
125+
- name: Install cargo-dist
126+
uses: taiki-e/install-action@v2
127+
with:
128+
tool: cargo-dist@0.27.0
129+
- name: Download local artifacts
130+
uses: actions/download-artifact@v4
131+
with:
132+
pattern: artifacts-*
133+
path: target/distrib/
134+
merge-multiple: true
135+
- name: Build global artifacts
136+
run: |
137+
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
138+
cat dist-manifest.json
139+
- name: Upload artifacts
140+
uses: actions/upload-artifact@v4
141+
with:
142+
name: artifacts-build-global
143+
path: |
144+
target/distrib/*
145+
146+
# Generate GitHub attestations for build provenance
147+
generate-attestations:
148+
needs:
149+
- plan
150+
- build-local-artifacts
151+
- build-global-artifacts
152+
if: needs.plan.outputs.publishing == 'true'
153+
runs-on: ubuntu-latest
154+
env:
155+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
156+
steps:
157+
- name: Download all artifacts
158+
uses: actions/download-artifact@v4
159+
with:
160+
pattern: artifacts-*
161+
path: target/distrib/
162+
merge-multiple: true
163+
- name: Generate attestations
164+
uses: actions/attest-build-provenance@v2
165+
with:
166+
subject-path: |
167+
target/distrib/*.tar.gz
168+
target/distrib/*.zip
169+
target/distrib/*.ps1
170+
target/distrib/*.sh
171+
172+
# Publish to GitHub Releases
173+
publish:
174+
needs:
175+
- plan
176+
- build-local-artifacts
177+
- build-global-artifacts
178+
- generate-attestations
179+
if: needs.plan.outputs.publishing == 'true'
180+
runs-on: ubuntu-latest
181+
env:
182+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
183+
steps:
184+
- uses: actions/checkout@v4
185+
- name: Download artifacts
186+
uses: actions/download-artifact@v4
187+
with:
188+
pattern: artifacts-*
189+
path: target/distrib/
190+
merge-multiple: true
191+
- name: Publish to GitHub Releases
192+
run: |
193+
gh release create ${{ needs.plan.outputs.tag }} target/distrib/* --title "${{ needs.plan.outputs.tag }}" --generate-notes

Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,19 @@ rusty-hook = "^0.11.2" # git hooks
5656
predicates = "3.0.2" # kind of like rspec assertions
5757
pretty_assertions = "1.3.0" # Shows a more readable diff when comparing objects
5858
serial_test = "3.1.1" # Run specific tests in serial
59+
60+
# Config for 'cargo dist'
61+
[package.metadata.dist]
62+
# The preferred cargo-dist version to use (should match dist-workspace.toml)
63+
cargo-dist-version = "0.27.0"
64+
# CI backends to support
65+
ci = "github"
66+
# Target platforms
67+
targets = [
68+
"aarch64-apple-darwin",
69+
"x86_64-apple-darwin",
70+
"aarch64-unknown-linux-gnu",
71+
"x86_64-unknown-linux-gnu",
72+
]
73+
# Installers to generate
74+
installers = ["shell", "powershell"]

dist-workspace.toml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# cargo-dist workspace configuration
2+
# https://opensource.axo.dev/cargo-dist/book/reference/config.html
3+
4+
[workspace]
5+
members = ["./"]
6+
7+
[dist]
8+
# The preferred cargo-dist version to use in CI
9+
cargo-dist-version = "0.27.0"
10+
11+
# CI backends to support
12+
ci = "github"
13+
14+
# Target platforms for release builds
15+
targets = [
16+
"aarch64-apple-darwin",
17+
"x86_64-apple-darwin",
18+
"aarch64-unknown-linux-gnu",
19+
"x86_64-unknown-linux-gnu",
20+
]
21+
22+
# Installers to generate
23+
installers = [
24+
"shell",
25+
"powershell",
26+
]
27+
28+
# Enable GitHub attestations for enhanced security
29+
github-attestations = true
30+
31+
# Archive format for each platform
32+
unix-archive = ".tar.gz"
33+
windows-archive = ".zip"
34+
35+
# Release branch configuration
36+
pr-run-mode = "skip"
37+
allow-dirty = ["ci"]
38+
39+
# Build configuration
40+
precise-builds = true

0 commit comments

Comments
 (0)