minor fixups #19
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Release + Build + Publish | |
# Workflow Overview | |
# | |
# This workflow is triggered when: | |
# - A commit is pushed to the master branch that changes Cargo.toml's version | |
# - A user manually triggers the workflow | |
# | |
# If that's so, then we: | |
# - Determine if we need to release by seeing if there's a git tag with the | |
# cargo version. (If that tag exists, we don't need to release, presumably | |
# because this workflow has already run.) | |
# - If we need to release: | |
# - Build the project into binaries for all platforms | |
# - Create a GitHub release with the binaries as assets | |
# - Publish to crates.io | |
on: | |
push: | |
branches: | |
- master | |
paths: | |
- "Cargo.toml" | |
workflow_dispatch: | |
jobs: | |
tag: | |
permissions: | |
contents: write | |
runs-on: ubuntu-latest | |
outputs: | |
name: ${{ steps.create_tag.outputs.name }} | |
needs_build: ${{ steps.create_tag.outputs.needs_build }} | |
cargo_version: ${{ steps.extract_version.outputs.cargo_version }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-tags: true | |
fetch-depth: 0 | |
- name: Extract version from Cargo.toml | |
id: extract_version | |
run: | | |
CARGO_METADATA=$(cargo metadata --format-version 1 --no-deps) | |
CARGO_VERSION=$(echo "${CARGO_METADATA}" | jq -r '.packages[] | select(.name == "nextver") | .version') | |
if [ -z "${CARGO_VERSION}" ]; then | |
echo "CARGO_VERSION should not be empty, likely problem with metadata or jq command" | |
exit 1 | |
fi | |
echo "Extracted version: ${CARGO_VERSION}" | |
echo "::notice title=Cargo Version::${CARGO_VERSION}" | |
echo "cargo_version=${CARGO_VERSION}" >> "$GITHUB_OUTPUT" | |
# some possibility for race condition here, but not a problem unless we're spamming this action | |
- name: Create tag | |
# env: | |
# GH_TOKEN: ${{ github.token }} | |
id: create_tag | |
run: | | |
TAG_NAME="v${{ steps.extract_version.outputs.cargo_version }}" | |
echo "name=${TAG_NAME}" >> "$GITHUB_OUTPUT" | |
echo "::notice title=Tag Name::${TAG_NAME}" | |
REF="refs/tags/${TAG_NAME}" | |
if git rev-parse -q --verify "${REF}"; then | |
NEEDS_BUILD=false | |
echo "Tag v$VERSION already exists, skipping release" | |
else | |
NEEDS_BUILD=true | |
git config --global user.name "github-actions[bot]" | |
git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
git tag -a -m "Release ${TAG_NAME}" "${TAG_NAME}" | |
git push origin "${TAG_NAME}" | |
fi | |
echo "::notice title=Needs Build::${NEEDS_BUILD}" | |
echo "needs_build=${NEEDS_BUILD}" >> "$GITHUB_OUTPUT" | |
# Note that we just use the rust tooling that exists by default on the runners. | |
# See https://github.com/actions/runner-images?tab=readme-ov-file#available-images | |
build: | |
runs-on: ${{ matrix.os }} | |
needs: tag | |
if: needs.tag.outputs.needs_build == 'true' | |
strategy: | |
matrix: | |
include: | |
- os: ubuntu-latest | |
triple: x86_64-unknown-linux-gnu | |
bin_ext: "" | |
archive_ext: .tgz | |
- os: windows-latest | |
triple: x86_64-pc-windows-msvc | |
bin_ext: .exe | |
archive_ext: .zip | |
- os: macos-latest | |
triple: x86_64-apple-darwin | |
bin_ext: "" | |
archive_ext: .zip | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ needs.tag.outputs.name }} | |
- name: Add target triple | |
run: | | |
rustup target add ${{ matrix.triple }} | |
- name: Cache cargo registry and target | |
uses: actions/cache@v4 | |
with: | |
path: | | |
~/.cargo/registry | |
target/ | |
key: nextver-${{ matrix.triple }}-${{needs.tag.outputs.name}} | |
restore-keys: | | |
nextver-${{ matrix.triple }}- | |
nextver- | |
- name: Test | |
run: cargo test --target ${{ matrix.triple }} | |
- name: Build | |
run: cargo build --release --target ${{ matrix.triple }} | |
- name: Make paths | |
id: make_paths | |
shell: bash | |
run: | | |
# the relative path of the directory of the binary | |
BINARY_DIR=target/${{ matrix.triple }}/release | |
echo "binary_dir=${BINARY_DIR}" >> "$GITHUB_OUTPUT" | |
# the filename of the binary (inside BINARY_DIR) | |
BINARY_FILENAME=nextver${{ matrix.bin_ext }} | |
echo "binary_filename=${BINARY_FILENAME}" >> "$GITHUB_OUTPUT" | |
# the filename of the archive to create | |
ARCHIVE_FILENAME=nextver-${{ matrix.triple }}-${{ needs.tag.outputs.name }}${{ matrix.archive_ext }} | |
echo "archive_filename=${ARCHIVE_FILENAME}" >> "$GITHUB_OUTPUT" | |
# the relative path of the archive to create | |
ARCHIVE_PATH=${BINARY_DIR}/${ARCHIVE_FILENAME} | |
echo "archive_path=${ARCHIVE_PATH}" >> "$GITHUB_OUTPUT" | |
# in the following steps, we make archives containing the binary. each | |
# platform has different archive expectations (e.g. Linux uses tar, | |
# Windows/macOS use zip) and also has different tooling/invocations to | |
# make those archives. | |
- name: Archive (Linux) | |
id: archive_linux | |
if: runner.os == 'Linux' | |
run: | | |
tar -cf - \ | |
-C "${{ steps.make_paths.outputs.binary_dir }}" \ | |
"${{ steps.make_paths.outputs.binary_filename}}" \ | |
| gzip -9 > "${{ steps.make_paths.outputs.archive_path}}" | |
- name: Archive (Windows) | |
if: runner.os == 'Windows' | |
shell: bash | |
run: | | |
7z a -mx9 "${{ steps.make_paths.outputs.archive_path}}" \ | |
"${{ steps.make_paths.outputs.binary_dir }}/${{ steps.make_paths.outputs.binary_filename }}" | |
- name: Archive (macOS) | |
if: runner.os == 'macOS' | |
run: | | |
zip -r -9 "${{ steps.make_paths.outputs.archive_path}}" \ | |
"${{ steps.make_paths.outputs.binary_dir }}/${{ steps.make_paths.outputs.binary_filename }}" | |
- name: Upload artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
# this naming is important: it'll be used as a prefix pattern in the | |
# download action | |
name: ${{ steps.make_paths.outputs.archive_filename}} | |
path: ${{ steps.make_paths.outputs.archive_path}} | |
gh_release_and_crates_publish: | |
runs-on: ubuntu-latest | |
needs: | |
- tag | |
- build | |
permissions: | |
contents: write | |
steps: | |
- name: Collect artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
path: binary-archives | |
pattern: nextver-* # this is the common prefix from the upload action | |
merge-multiple: true | |
- name: Generate checksums | |
run: | | |
cd binary-archives | |
sha256sum * > sha256sums.txt | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ needs.tag.outputs.name }} | |
# put this in a separate dir to keep it clean, so cargo publish | |
# doesn't complain about dirty | |
path: source | |
- name: Create GitHub release | |
env: | |
GH_TOKEN: ${{ github.token }} | |
run: | | |
cd source | |
TAG_NAME=${{ needs.tag.outputs.name }} | |
URL=$(gh release create "${TAG_NAME}" \ | |
-t "Release ${TAG_NAME}" \ | |
--verify-tag \ | |
--generate-notes) | |
echo "::notice title=Release URL::${URL}" | |
gh release upload "${TAG_NAME}" ../binary-archives/* | |
- name: Publish to crates.io | |
run: | | |
cd source | |
# pass --no-verify because we've already run tests and built binaries. | |
# its good, i promise. | |
cargo publish --no-verify | |
env: | |
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
- name: Show crates.io URL | |
run: | | |
echo "::notice title=crates.io URL::https://crates.io/crates/nextver/${{ needs.tag.outputs.cargo_version }}" |