-
Notifications
You must be signed in to change notification settings - Fork 39
201 lines (171 loc) · 6.87 KB
/
Copy pathrelease.yml
File metadata and controls
201 lines (171 loc) · 6.87 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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
name: Release
on:
workflow_dispatch:
inputs:
bump:
description: Version bump to release
type: choice
required: true
options:
- patch
- minor
- major
concurrency:
group: release-${{ github.ref_name }}
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
jobs:
publish-rust:
name: Release Rust Crate
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
environment: Production
env:
CRATE_NAME: tinycortex
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: true
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: .
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run tests
run: cargo test --all-features
- name: Build documentation
run: RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps
- name: Compute next version
id: version
shell: bash
run: |
set -euo pipefail
current_version="$(cargo metadata --format-version 1 --no-deps | jq -r --arg crate "$CRATE_NAME" '.packages[] | select(.name == $crate) | .version')"
if [[ -z "$current_version" ]]; then
echo "Could not resolve current version for crate $CRATE_NAME" >&2
exit 1
fi
IFS=. read -r major minor patch <<< "$current_version"
case "${{ inputs.bump }}" in
major)
major=$((major + 1))
minor=0
patch=0
;;
minor)
minor=$((minor + 1))
patch=0
;;
patch)
patch=$((patch + 1))
;;
*)
echo "Unsupported bump: ${{ inputs.bump }}" >&2
exit 1
;;
esac
next_version="${major}.${minor}.${patch}"
tag="v${next_version}"
git fetch --tags origin
if git rev-parse --verify --quiet "refs/tags/${tag}"; then
echo "Tag ${tag} already exists" >&2
exit 1
fi
{
echo "current_version=${current_version}"
echo "next_version=${next_version}"
echo "tag=${tag}"
} >> "$GITHUB_OUTPUT"
- name: Update crate version
env:
NEXT_VERSION: ${{ steps.version.outputs.next_version }}
run: |
perl -0pi -e 's/(\[package\][\s\S]*?\nversion = ")[^"]+(")/$1$ENV{NEXT_VERSION}$2/' Cargo.toml
cargo update -p "$CRATE_NAME" --precise "$NEXT_VERSION"
# `main` is protected by a repository ruleset that requires all
# changes to land through a pull request (no direct pushes), and the
# workflow's GITHUB_TOKEN is not on that ruleset's bypass list. So the
# version-bump commit is pushed to a throwaway release branch and
# landed on `main` via an auto-merged PR instead of `git push`ing
# `HEAD` straight at `main` (which the ruleset rejects with GH013).
- name: Commit version bump
id: commit
env:
NEXT_VERSION: ${{ steps.version.outputs.next_version }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
release_branch="release/${RELEASE_TAG}"
git checkout -b "${release_branch}"
git add Cargo.toml Cargo.lock
git commit -m "Release ${RELEASE_TAG}"
# A previous failed run can leave this throwaway branch behind, which
# would make the retry's push a non-fast-forward. The branch only ever
# holds the version bump for this exact tag, so overwriting it is safe.
git push --force origin "${release_branch}"
echo "branch=${release_branch}" >> "$GITHUB_OUTPUT"
# `$CRATE_NAME` (and its path dependency `tinycortex-api`) both carry
# `publish = false` right now: `tinycortex-api` depends on `tinymemory-api`
# by git rev, and cargo refuses to package/publish a crate whose
# dependency graph contains an unpublished git/path dependency (see the
# `publish = false` comments in Cargo.toml and api/Cargo.toml, tracked as
# tinymemory#18 §A1). Packaging or publishing while that holds always
# fails, so skip both steps until the crate is actually publishable
# instead of hard-failing the whole release (which also blocks the
# version-bump tag from ever being pushed).
- name: Check crates.io publishability
id: publishable
run: |
set -euo pipefail
publish="$(cargo metadata --no-deps --format-version 1 | jq -r --arg crate "$CRATE_NAME" '.packages[] | select(.name == $crate) | .publish')"
if [[ "$publish" == "[]" ]]; then
echo "publishable=false" >> "$GITHUB_OUTPUT"
echo "::notice::${CRATE_NAME} has publish = false; skipping crates.io package/publish steps."
else
echo "publishable=true" >> "$GITHUB_OUTPUT"
fi
- name: Package crate
if: steps.publishable.outputs.publishable == 'true'
run: cargo package --locked -p "$CRATE_NAME"
- name: Open and merge release PR
id: merge
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
RELEASE_BRANCH: ${{ steps.commit.outputs.branch }}
run: |
set -euo pipefail
pr_url="$(gh pr create \
--base "${GITHUB_REF_NAME}" \
--head "${RELEASE_BRANCH}" \
--title "Release ${RELEASE_TAG}" \
--body "Automated version bump for ${RELEASE_TAG}.")"
# This repository allows merge commits only (squash and rebase are
# both disabled), so the release PR must be merged with --merge.
gh pr merge "${pr_url}" --merge --delete-branch
git fetch origin "${GITHUB_REF_NAME}"
merge_sha="$(git rev-parse "origin/${GITHUB_REF_NAME}")"
echo "sha=${merge_sha}" >> "$GITHUB_OUTPUT"
- name: Tag and push release
env:
RELEASE_TAG: ${{ steps.version.outputs.tag }}
MERGE_SHA: ${{ steps.merge.outputs.sha }}
run: |
set -euo pipefail
git tag -a "${RELEASE_TAG}" -m "Release ${RELEASE_TAG}" "${MERGE_SHA}"
git push origin "${RELEASE_TAG}"
- name: Publish to crates.io
if: steps.publishable.outputs.publishable == 'true'
run: cargo publish --locked -p "$CRATE_NAME"
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}