Skip to content

Release

Release #26

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
version:
description: "Version to release (e.g., 0.1.0, 0.2.0-beta.1)"
required: true
type: string
permissions:
contents: write
id-token: write
concurrency:
group: release
cancel-in-progress: false
env:
CARGO_INCREMENTAL: 0
CARGO_TERM_COLOR: always
jobs:
validate:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Validate version
id: version
run: |
VERSION="${{ inputs.version }}"
VERSION="${VERSION#v}"
if ! npx --yes semver "$VERSION" > /dev/null 2>&1; then
echo "::error::Invalid semver: $VERSION"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Check tag doesn't exist
run: |
if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
echo "::error::Tag v${{ steps.version.outputs.version }} already exists"
exit 1
fi
- name: Check on main branch
run: |
if [ "${{ github.ref_name }}" != "main" ]; then
echo "::error::Releases must be triggered from main branch"
exit 1
fi
test:
needs: validate
uses: ./.github/workflows/test.yml
prepare:
needs: [validate, test]
runs-on: ubuntu-latest
outputs:
version: ${{ needs.validate.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Update Cargo.toml version
run: |
sed -i 's/^version = ".*"/version = "${{ needs.validate.outputs.version }}"/' Cargo.toml
- name: Update Cargo.lock
run: cargo check --quiet
- name: Update npm package versions
run: node npm/scripts/update-versions.mjs ${{ needs.validate.outputs.version }}
- name: Commit and tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Cargo.toml Cargo.lock npm/
git commit -m "release: v${{ needs.validate.outputs.version }}"
git tag "v${{ needs.validate.outputs.version }}"
git push origin main
git push origin "v${{ needs.validate.outputs.version }}"
build:
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
target: x86_64-apple-darwin
platform: darwin-x64
- os: macos-latest
target: aarch64-apple-darwin
platform: darwin-arm64
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
platform: linux-x64
- os: ubuntu-22.04
target: aarch64-unknown-linux-gnu
platform: linux-arm64
cross: true
- os: ubuntu-22.04
target: x86_64-unknown-linux-musl
platform: linux-x64-musl
cross: true
- os: ubuntu-22.04
target: aarch64-unknown-linux-musl
platform: linux-arm64-musl
cross: true
- os: windows-latest
target: x86_64-pc-windows-msvc
platform: win32-x64
- os: windows-latest
target: aarch64-pc-windows-msvc
platform: win32-arm64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
ref: v${{ needs.prepare.outputs.version }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross
if: matrix.cross
uses: taiki-e/install-action@cross
- name: Build (cross)
if: matrix.cross
run: cross build --release --target ${{ matrix.target }}
env:
RUSTFLAGS: "-C strip=symbols"
- name: Build (native)
if: "!matrix.cross"
run: cargo build --release --target ${{ matrix.target }}
env:
RUSTFLAGS: "-C strip=symbols"
- name: Prepare artifact (Unix)
if: runner.os != 'Windows'
run: |
mkdir -p dist
cp target/${{ matrix.target }}/release/ryu dist/
chmod +x dist/ryu
- name: Sign binary (macOS)
if: runner.os == 'macOS'
run: codesign --force --sign - dist/ryu
- name: Prepare artifact (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist
Copy-Item "target/${{ matrix.target }}/release/ryu.exe" -Destination "dist/"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.platform }}
path: dist/
if-no-files-found: error
release:
needs: [prepare, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: v${{ needs.prepare.outputs.version }}
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create release archives
run: |
mkdir -p release
for platform in artifacts/*/; do
name=$(basename "$platform")
if [[ "$name" == win32-* ]]; then
(cd "$platform" && zip -r "../../release/ryu-${name}.zip" .)
else
(cd "$platform" && tar -czvf "../../release/ryu-${name}.tar.gz" .)
fi
done
- name: Generate checksums
run: |
cd release
sha256sum * > checksums-sha256.txt
cat checksums-sha256.txt
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.prepare.outputs.version }}
files: release/*
generate_release_notes: true
publish-npm:
needs: [prepare, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: v${{ needs.prepare.outputs.version }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare platform packages
run: |
VERSION=${{ needs.prepare.outputs.version }}
for platform in darwin-arm64 darwin-x64 linux-arm64 linux-x64 linux-arm64-musl linux-x64-musl win32-arm64 win32-x64; do
mkdir -p npm/$platform
if [[ "$platform" == win32-* ]]; then
cp artifacts/$platform/ryu.exe npm/$platform/
else
cp artifacts/$platform/ryu npm/$platform/
chmod +x npm/$platform/ryu
fi
node npm/scripts/generate-platform-package.mjs $platform $VERSION
done
- name: Publish platform packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
VERSION=${{ needs.prepare.outputs.version }}
# Skip win32 packages for now (npm spam detection issue)
for platform in darwin-arm64 darwin-x64 linux-arm64 linux-x64 linux-arm64-musl linux-x64-musl; do
echo "Publishing jj-ryu-$platform..."
npm publish ./npm/$platform --access public --provenance || {
if npm view "jj-ryu-$platform@$VERSION" > /dev/null 2>&1; then
echo "Version already published, skipping"
else
exit 1
fi
}
done
- name: Publish main package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
VERSION=${{ needs.prepare.outputs.version }}
echo "Publishing jj-ryu..."
npm publish ./npm/jj-ryu --access public --provenance || {
if npm view "jj-ryu@$VERSION" > /dev/null 2>&1; then
echo "Version already published, skipping"
else
exit 1
fi
}
publish-crates:
needs: [prepare, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: v${{ needs.prepare.outputs.version }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
set +e
OUTPUT=$(cargo publish 2>&1)
EXIT_CODE=$?
set -e
if [ $EXIT_CODE -eq 0 ]; then
echo "Published successfully"
elif echo "$OUTPUT" | grep -q "already exists"; then
echo "Version already published, skipping"
else
echo "$OUTPUT"
exit $EXIT_CODE
fi
homebrew:
needs: [prepare, release]
runs-on: ubuntu-latest
steps:
- name: Get tarball SHA256
id: sha
run: |
sha=$(curl -sL https://github.com/dmmulroy/jj-ryu/archive/refs/tags/v${{ needs.prepare.outputs.version }}.tar.gz | shasum -a 256 | cut -d' ' -f1)
echo "sha256=$sha" >> $GITHUB_OUTPUT
- name: Checkout homebrew-tap
uses: actions/checkout@v4
with:
repository: dmmulroy/homebrew-tap
token: ${{ secrets.TAP_GITHUB_TOKEN }}
- name: Update formula
run: |
sed -i 's|url ".*"|url "https://github.com/dmmulroy/jj-ryu/archive/refs/tags/v${{ needs.prepare.outputs.version }}.tar.gz"|' Formula/jj-ryu.rb
sed -i 's|sha256 ".*"|sha256 "${{ steps.sha.outputs.sha256 }}"|' Formula/jj-ryu.rb
- name: Create PR
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.TAP_GITHUB_TOKEN }}
commit-message: "Update jj-ryu to v${{ needs.prepare.outputs.version }}"
title: "Update jj-ryu to v${{ needs.prepare.outputs.version }}"
body: "Automated update from [jj-ryu release](https://github.com/dmmulroy/jj-ryu/releases/tag/v${{ needs.prepare.outputs.version }})"
branch: update-jj-ryu-${{ needs.prepare.outputs.version }}
base: main
summary:
needs: [prepare, release, publish-npm, publish-crates, homebrew]
runs-on: ubuntu-latest
if: always()
steps:
- name: Summary
run: |
echo "## Release v${{ needs.prepare.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Component | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| GitHub Release | ${{ needs.release.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| npm | ${{ needs.publish-npm.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| crates.io | ${{ needs.publish-crates.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Homebrew | ${{ needs.homebrew.result }} |" >> $GITHUB_STEP_SUMMARY