Skip to content

Commit 145cbb6

Browse files
authored
Merge pull request #6 from A3S-Lab/release/publish-runtime-0.2.0-20260723
ci: add reproducible crate publishing workflow
2 parents e2f5d0b + 74dc83a commit 145cbb6

1 file changed

Lines changed: 141 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
name: Publish crate
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: Exact crate version without a v prefix (for example, 0.2.0)
8+
required: true
9+
type: string
10+
release_ref:
11+
description: Exact 40-character commit SHA to publish
12+
required: true
13+
type: string
14+
15+
permissions:
16+
contents: write
17+
18+
concurrency:
19+
group: publish-a3s-runtime-${{ inputs.version }}
20+
cancel-in-progress: false
21+
22+
env:
23+
CARGO_TERM_COLOR: always
24+
RELEASE_REF: ${{ inputs.release_ref }}
25+
RELEASE_VERSION: ${{ inputs.version }}
26+
27+
jobs:
28+
publish:
29+
name: Publish a3s-runtime
30+
runs-on: ubuntu-24.04
31+
timeout-minutes: 20
32+
steps:
33+
- name: Validate inputs
34+
run: |
35+
set -euo pipefail
36+
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
37+
echo "::error::Expected a stable SemVer version, got '$RELEASE_VERSION'"
38+
exit 1
39+
fi
40+
if [[ ! "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]]; then
41+
echo "::error::Expected an exact 40-character commit SHA"
42+
exit 1
43+
fi
44+
45+
- name: Check out exact release commit
46+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
47+
with:
48+
ref: ${{ env.RELEASE_REF }}
49+
50+
- name: Install Rust 1.85
51+
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4
52+
with:
53+
toolchain: "1.85.0"
54+
components: rustfmt
55+
56+
- name: Verify release source
57+
run: |
58+
set -euo pipefail
59+
if [ "$(git rev-parse HEAD)" != "$RELEASE_REF" ]; then
60+
echo "::error::Checkout does not match the requested release commit"
61+
exit 1
62+
fi
63+
PACKAGE_VERSION="$(
64+
cargo metadata --locked --no-deps --format-version 1 |
65+
jq -r '.packages[] | select(.name == "a3s-runtime") | .version'
66+
)"
67+
if [ "$PACKAGE_VERSION" != "$RELEASE_VERSION" ]; then
68+
echo "::error::Cargo version $PACKAGE_VERSION does not match $RELEASE_VERSION"
69+
exit 1
70+
fi
71+
cargo fmt --all --check
72+
cargo test --locked --all-targets
73+
cargo publish --locked --dry-run
74+
75+
- name: Publish and verify crate
76+
env:
77+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
78+
run: |
79+
set -euo pipefail
80+
CRATE_URL="https://crates.io/api/v1/crates/a3s-runtime/$RELEASE_VERSION"
81+
USER_AGENT="A3S-Lab-Runtime-release-publisher/$RELEASE_VERSION (https://github.com/A3S-Lab/Runtime)"
82+
83+
crate_status() {
84+
curl --retry 5 --retry-all-errors --silent --show-error \
85+
--location --user-agent "$USER_AGENT" \
86+
--output /dev/null --write-out '%{http_code}' "$CRATE_URL"
87+
}
88+
89+
STATUS="$(crate_status)"
90+
case "$STATUS" in
91+
200)
92+
echo "a3s-runtime@$RELEASE_VERSION already exists; skipping upload"
93+
;;
94+
404)
95+
cargo publish --locked
96+
;;
97+
*)
98+
echo "::error::crates.io returned HTTP $STATUS before publication"
99+
exit 1
100+
;;
101+
esac
102+
103+
for attempt in $(seq 1 18); do
104+
STATUS="$(crate_status)"
105+
if [ "$STATUS" = "200" ]; then
106+
echo "Verified a3s-runtime@$RELEASE_VERSION on crates.io"
107+
exit 0
108+
fi
109+
if [ "$STATUS" != "404" ]; then
110+
echo "::error::crates.io returned HTTP $STATUS during verification"
111+
exit 1
112+
fi
113+
if [ "$attempt" -lt 18 ]; then
114+
sleep 10
115+
fi
116+
done
117+
echo "::error::a3s-runtime@$RELEASE_VERSION did not become visible on crates.io"
118+
exit 1
119+
120+
- name: Create GitHub release
121+
env:
122+
GH_TOKEN: ${{ github.token }}
123+
run: |
124+
set -euo pipefail
125+
TAG="v$RELEASE_VERSION"
126+
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
127+
TAG_COMMIT="$(
128+
gh api "repos/$GITHUB_REPOSITORY/commits/$TAG" --jq '.sha'
129+
)"
130+
if [ "$TAG_COMMIT" != "$RELEASE_REF" ]; then
131+
echo "::error::$TAG already exists at a different commit"
132+
exit 1
133+
fi
134+
echo "$TAG release already exists; skipping creation"
135+
else
136+
gh release create "$TAG" \
137+
--repo "$GITHUB_REPOSITORY" \
138+
--target "$RELEASE_REF" \
139+
--title "a3s-runtime $RELEASE_VERSION" \
140+
--generate-notes
141+
fi

0 commit comments

Comments
 (0)