ci: automate releases with release-plz
Replace the 326-line semantic-release/npm workflow with release-plz, which handles the whole chain the old one only half did: - versioning and CHANGELOG from the conventional commits already in use - a `chore: release vX.Y.Z` PR as the gate; merging it tags and releases - publishes both crates to crates.io in dependency order, waiting for the index between them (RELEASE.md used to ask you to do this by hand) - release-plz.toml keeps the two crates on one version, one tag, one changelog Binaries now cover six targets instead of four. Linux switches from gnu to musl so the downloads are statically linked and stop caring about the host's glibc version; linux-aarch64 and windows-aarch64 are new. Archives carry sha256 checksums plus README and LICENSE. CI drops twelve hand-rolled actions/cache steps for Swatinem/rust-cache.
This commit is contained in:
@@ -22,23 +22,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
|
|
||||||
- name: Cache cargo registry
|
- uses: Swatinem/rust-cache@v2
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/registry
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo index
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/git
|
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo build
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: target
|
|
||||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
@@ -72,23 +56,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
|
|
||||||
- name: Cache cargo registry
|
- uses: Swatinem/rust-cache@v2
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/registry
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo index
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/git
|
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo build
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: target
|
|
||||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --verbose
|
run: cargo test --verbose
|
||||||
@@ -102,17 +70,7 @@ jobs:
|
|||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
- name: Cache cargo registry
|
- uses: Swatinem/rust-cache@v2
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/registry
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo build
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: target
|
|
||||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Install SQLite runtime/dev libraries
|
- name: Install SQLite runtime/dev libraries
|
||||||
run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev
|
run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev
|
||||||
|
|||||||
+92
-295
@@ -1,325 +1,122 @@
|
|||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
release_type:
|
|
||||||
description: 'Release type (patch, minor, major)'
|
|
||||||
required: false
|
|
||||||
default: 'auto'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- auto
|
|
||||||
- patch
|
|
||||||
- minor
|
|
||||||
- major
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-release:
|
# Opens (or updates) a "chore: release vX.Y.Z" PR with the version bump and
|
||||||
name: Check for Release
|
# changelog derived from the conventional commits on master. Merging it is the
|
||||||
|
# release trigger.
|
||||||
|
release-pr:
|
||||||
|
name: Release PR
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
concurrency:
|
||||||
should_release: ${{ steps.check.outputs.should_release }}
|
group: release-pr-${{ github.ref }}
|
||||||
version: ${{ steps.check.outputs.version }}
|
cancel-in-progress: false
|
||||||
notes: ${{ steps.check.outputs.notes }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node.js
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
uses: actions/setup-node@v4
|
|
||||||
|
- uses: release-plz/action@v0.5
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
command: release-pr
|
||||||
|
|
||||||
- name: Install semantic-release
|
|
||||||
run: |
|
|
||||||
npm install -g semantic-release@23 \
|
|
||||||
@semantic-release/commit-analyzer@13 \
|
|
||||||
@semantic-release/release-notes-generator@14
|
|
||||||
|
|
||||||
- name: Check if release needed
|
|
||||||
id: check
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
# If a specific release type is requested (not 'auto'), always proceed
|
|
||||||
if [ "${{ github.event.inputs.release_type }}" != "auto" ] && [ -n "${{ github.event.inputs.release_type }}" ]; then
|
|
||||||
echo "Manual release type specified: ${{ github.event.inputs.release_type }}"
|
|
||||||
echo "should_release=true" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Get current version from Cargo.toml
|
# No-op until the release PR lands; then tags, publishes both crates to
|
||||||
CURRENT_VERSION=$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)
|
# crates.io in dependency order, and creates the GitHub release.
|
||||||
echo "Current version: $CURRENT_VERSION"
|
release:
|
||||||
|
name: Tag and publish
|
||||||
# Calculate next version based on release type
|
runs-on: ubuntu-latest
|
||||||
IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
|
concurrency:
|
||||||
case "${{ github.event.inputs.release_type }}" in
|
group: release-${{ github.ref }}
|
||||||
major)
|
cancel-in-progress: false
|
||||||
VERSION="$((major + 1)).0.0"
|
outputs:
|
||||||
;;
|
created: ${{ steps.release-plz.outputs.releases_created }}
|
||||||
minor)
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
VERSION="${major}.$((minor + 1)).0"
|
|
||||||
;;
|
|
||||||
patch)
|
|
||||||
VERSION="${major}.${minor}.$((patch + 1))"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "Manual version to release: $VERSION"
|
|
||||||
else
|
|
||||||
# Auto mode - use semantic-release to determine if release is needed
|
|
||||||
cat > .releaserc.json << 'EOF'
|
|
||||||
{
|
|
||||||
"branches": ["master"],
|
|
||||||
"plugins": [
|
|
||||||
"@semantic-release/commit-analyzer",
|
|
||||||
"@semantic-release/release-notes-generator"
|
|
||||||
],
|
|
||||||
"dryRun": true
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
OUTPUT=$(npx semantic-release --dry-run 2>&1 || true)
|
|
||||||
echo "$OUTPUT"
|
|
||||||
|
|
||||||
if echo "$OUTPUT" | grep -q "There is no relevant changes"; then
|
|
||||||
echo "should_release=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "No relevant changes found for automatic release"
|
|
||||||
else
|
|
||||||
VERSION=$(echo "$OUTPUT" | grep -oP "next release version is \K[0-9]+\.[0-9]+\.[0-9]+" | head -1)
|
|
||||||
if [ -n "$VERSION" ]; then
|
|
||||||
echo "should_release=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "Auto version to release: $VERSION"
|
|
||||||
else
|
|
||||||
echo "should_release=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "Could not determine version for automatic release"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
build-binaries:
|
|
||||||
name: Build ${{ matrix.target }}
|
|
||||||
needs: check-release
|
|
||||||
if: needs.check-release.outputs.should_release == 'true'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
binary_name: soulseek-rs
|
|
||||||
tarball_name: soulseek-rs-linux-x86_64.tar.gz
|
|
||||||
- os: macos-latest
|
|
||||||
target: x86_64-apple-darwin
|
|
||||||
binary_name: soulseek-rs
|
|
||||||
tarball_name: soulseek-rs-macos-x86_64.tar.gz
|
|
||||||
- os: macos-latest
|
|
||||||
target: aarch64-apple-darwin
|
|
||||||
binary_name: soulseek-rs
|
|
||||||
tarball_name: soulseek-rs-macos-aarch64.tar.gz
|
|
||||||
- os: windows-latest
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
binary_name: soulseek-rs.exe
|
|
||||||
tarball_name: soulseek-rs-windows-x86_64.zip
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Rust
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
|
- uses: release-plz/action@v0.5
|
||||||
|
id: release-plz
|
||||||
|
with:
|
||||||
|
command: release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
# Read the tag out via jq rather than fromJSON: `releases` is an empty
|
||||||
|
# string on the runs where nothing was released, and fromJSON chokes on it.
|
||||||
|
- name: Resolve tag
|
||||||
|
id: tag
|
||||||
|
if: steps.release-plz.outputs.releases_created == 'true'
|
||||||
|
env:
|
||||||
|
RELEASES: ${{ steps.release-plz.outputs.releases }}
|
||||||
|
run: echo "tag=$(jq -r '.[0].tag' <<< "$RELEASES")" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
# Attaches downloadable binaries to the release GitHub just created.
|
||||||
|
binaries:
|
||||||
|
name: ${{ matrix.target }}
|
||||||
|
needs: release
|
||||||
|
if: needs.release.outputs.created == 'true'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
# One flaky cross-linker must not hold back the five targets that built.
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- target: x86_64-unknown-linux-musl
|
||||||
|
os: ubuntu-latest
|
||||||
|
- target: aarch64-unknown-linux-musl
|
||||||
|
os: ubuntu-24.04-arm
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
os: macos-latest
|
||||||
|
- target: aarch64-apple-darwin
|
||||||
|
os: macos-latest
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
os: windows-latest
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
os: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.release.outputs.tag }}
|
||||||
|
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Cache cargo registry
|
- name: Install musl toolchain
|
||||||
uses: actions/cache@v4
|
if: endsWith(matrix.target, '-musl')
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y musl-tools
|
||||||
|
|
||||||
|
# Builds, strips, archives (.tar.gz / .zip on Windows), writes a .sha256
|
||||||
|
# and uploads the lot to the release.
|
||||||
|
- uses: taiki-e/upload-rust-binary-action@v1
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/registry
|
bin: soulseek-rs
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
target: ${{ matrix.target }}
|
||||||
|
archive: $bin-$tag-$target
|
||||||
- name: Cache cargo index
|
include: README.md,LICENSE
|
||||||
uses: actions/cache@v4
|
checksum: sha256
|
||||||
with:
|
ref: refs/tags/${{ needs.release.outputs.tag }}
|
||||||
path: ~/.cargo/git
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Cache cargo build
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: target
|
|
||||||
key: ${{ runner.os }}-${{ matrix.target }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: cargo build --release --target ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Strip binary (Linux)
|
|
||||||
if: matrix.os == 'ubuntu-latest'
|
|
||||||
run: strip target/${{ matrix.target }}/release/${{ matrix.binary_name }}
|
|
||||||
|
|
||||||
- name: Strip binary (macOS)
|
|
||||||
if: matrix.os == 'macos-latest'
|
|
||||||
run: strip target/${{ matrix.target }}/release/${{ matrix.binary_name }}
|
|
||||||
|
|
||||||
- name: Create tarball (Unix)
|
|
||||||
if: matrix.os != 'windows-latest'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd target/${{ matrix.target }}/release
|
|
||||||
tar -czf ../../../${{ matrix.tarball_name }} ${{ matrix.binary_name }}
|
|
||||||
|
|
||||||
- name: Create zip (Windows)
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd target/${{ matrix.target }}/release
|
|
||||||
7z a -tzip ../../../${{ matrix.tarball_name }} ${{ matrix.binary_name }}
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ matrix.tarball_name }}
|
|
||||||
path: ${{ matrix.tarball_name }}
|
|
||||||
|
|
||||||
create-release:
|
|
||||||
name: Create Release
|
|
||||||
needs: [check-release, build-binaries]
|
|
||||||
if: needs.check-release.outputs.should_release == 'true'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
persist-credentials: true
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 'lts/*'
|
|
||||||
|
|
||||||
- name: Install semantic-release
|
|
||||||
run: |
|
|
||||||
npm install -g semantic-release@23 \
|
|
||||||
@semantic-release/git@10 \
|
|
||||||
@semantic-release/changelog@6 \
|
|
||||||
@semantic-release/exec@6 \
|
|
||||||
@semantic-release/github@11
|
|
||||||
|
|
||||||
- name: Download all artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: artifacts
|
|
||||||
|
|
||||||
- name: Display artifact structure
|
|
||||||
run: ls -R artifacts
|
|
||||||
|
|
||||||
- name: Prepare artifacts for release
|
|
||||||
run: |
|
|
||||||
mkdir -p release-assets
|
|
||||||
find artifacts -type f -name "*.tar.gz" -exec cp {} release-assets/ \;
|
|
||||||
find artifacts -type f -name "*.zip" -exec cp {} release-assets/ \;
|
|
||||||
ls -lh release-assets/
|
|
||||||
|
|
||||||
- name: Create semantic-release config
|
|
||||||
run: |
|
|
||||||
# Check if we need to force a specific version for manual releases
|
|
||||||
if [ "${{ github.event.inputs.release_type }}" != "auto" ] && [ -n "${{ github.event.inputs.release_type }}" ]; then
|
|
||||||
# For manual releases, we'll bypass semantic-release version detection
|
|
||||||
cat > .releaserc.json << 'EOF'
|
|
||||||
{
|
|
||||||
"branches": ["master"],
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"@semantic-release/commit-analyzer",
|
|
||||||
{
|
|
||||||
"releaseRules": [
|
|
||||||
{"breaking": true, "release": "${{ github.event.inputs.release_type }}"},
|
|
||||||
{"revert": true, "release": "patch"},
|
|
||||||
{"type": "feat", "release": "${{ github.event.inputs.release_type }}"},
|
|
||||||
{"type": "fix", "release": "${{ github.event.inputs.release_type }}"},
|
|
||||||
{"type": "perf", "release": "${{ github.event.inputs.release_type }}"},
|
|
||||||
{"scope": "no-release", "release": false}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@semantic-release/release-notes-generator",
|
|
||||||
"@semantic-release/changelog",
|
|
||||||
[
|
|
||||||
"@semantic-release/exec",
|
|
||||||
{
|
|
||||||
"prepareCmd": "sed -i 's/^version = \".*\"/version = \"${{ needs.check-release.outputs.version }}\"/' Cargo.toml"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@semantic-release/git",
|
|
||||||
{
|
|
||||||
"assets": ["CHANGELOG.md", "Cargo.toml"],
|
|
||||||
"message": "chore(release): ${{ needs.check-release.outputs.version }} [skip ci]\n\n${nextRelease.notes}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@semantic-release/github",
|
|
||||||
{
|
|
||||||
"assets": [
|
|
||||||
{"path": "release-assets/*.tar.gz"},
|
|
||||||
{"path": "release-assets/*.zip"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
# For automatic releases, use standard semantic-release
|
|
||||||
cat > .releaserc.json << 'EOF'
|
|
||||||
{
|
|
||||||
"branches": ["master"],
|
|
||||||
"plugins": [
|
|
||||||
"@semantic-release/commit-analyzer",
|
|
||||||
"@semantic-release/release-notes-generator",
|
|
||||||
"@semantic-release/changelog",
|
|
||||||
[
|
|
||||||
"@semantic-release/exec",
|
|
||||||
{
|
|
||||||
"prepareCmd": "sed -i 's/^version = \".*\"/version = \"${nextRelease.version}\"/' Cargo.toml"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@semantic-release/git",
|
|
||||||
{
|
|
||||||
"assets": ["CHANGELOG.md", "Cargo.toml"],
|
|
||||||
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@semantic-release/github",
|
|
||||||
{
|
|
||||||
"assets": [
|
|
||||||
{"path": "release-assets/*.tar.gz"},
|
|
||||||
{"path": "release-assets/*.zip"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Run semantic-release
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event.inputs.release_type }}" != "auto" ] && [ -n "${{ github.event.inputs.release_type }}" ]; then
|
|
||||||
# For manual releases, we need to create the release directly
|
|
||||||
echo "Creating manual release version ${{ needs.check-release.outputs.version }}"
|
|
||||||
npx semantic-release --no-ci
|
|
||||||
else
|
|
||||||
npx semantic-release
|
|
||||||
fi
|
|
||||||
|
|||||||
+45
-102
@@ -1,122 +1,65 @@
|
|||||||
# Release Guide
|
# Releasing
|
||||||
|
|
||||||
This guide explains how to publish the `soulseek-rs-lib` and `soulseek-rs` crates to crates.io.
|
Releases are automated by [release-plz](https://release-plz.dev). You do not run
|
||||||
|
`cargo publish`, edit versions, write changelog entries, or push tags by hand.
|
||||||
|
|
||||||
## Prerequisites
|
## How it works
|
||||||
|
|
||||||
1. Create an account on [crates.io](https://crates.io/)
|
1. Merge a PR to `master` using [conventional commits](https://www.conventionalcommits.org)
|
||||||
2. Generate an API token: https://crates.io/me
|
(`feat:`, `fix:`, `perf:`, `refactor:`, `docs:`, `chore:`, or a `!` / `BREAKING CHANGE:`
|
||||||
3. Login to cargo: `cargo login <your-token>`
|
footer for a major bump).
|
||||||
|
2. `.github/workflows/release.yml` opens or updates a **`chore: release vX.Y.Z`** PR. It
|
||||||
|
contains the semver bump in `[workspace.package]`, the refreshed `Cargo.lock`, and the
|
||||||
|
generated `CHANGELOG.md` entry. Review it like any other PR.
|
||||||
|
3. Merging that PR releases:
|
||||||
|
- tags `vX.Y.Z` and creates the GitHub release with the changelog as its notes,
|
||||||
|
- publishes `soulseek-rs-lib` then `soulseek-rs` to crates.io (dependency order, waiting
|
||||||
|
for the index between them),
|
||||||
|
- builds and attaches binaries with `.sha256` checksums for six targets:
|
||||||
|
|
||||||
## Publishing Process
|
| Archive | Notes |
|
||||||
|
| ------------------------------------------------ | ------------------------------ |
|
||||||
|
| `soulseek-rs-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz` | static, any glibc |
|
||||||
|
| `soulseek-rs-vX.Y.Z-aarch64-unknown-linux-musl.tar.gz` | static, any glibc |
|
||||||
|
| `soulseek-rs-vX.Y.Z-x86_64-apple-darwin.tar.gz` | Intel Mac |
|
||||||
|
| `soulseek-rs-vX.Y.Z-aarch64-apple-darwin.tar.gz` | Apple Silicon |
|
||||||
|
| `soulseek-rs-vX.Y.Z-x86_64-pc-windows-msvc.zip` | |
|
||||||
|
| `soulseek-rs-vX.Y.Z-aarch64-pc-windows-msvc.zip` | |
|
||||||
|
|
||||||
### Step 1: Publish the Library First
|
Both crates always share one version, one tag, and one changelog — see `release-plz.toml`.
|
||||||
|
|
||||||
The client depends on the library, so the library must be published first.
|
## One-time repository setup
|
||||||
|
|
||||||
```bash
|
- **Settings → Actions → General → Workflow permissions**: enable *Allow GitHub Actions to
|
||||||
cd soulseek-rs-lib
|
create and approve pull requests*. Without it the release PR is never opened.
|
||||||
cargo publish --dry-run # Test the publish
|
- **Settings → Secrets and variables → Actions**: add `CARGO_REGISTRY_TOKEN`, a
|
||||||
cargo publish # Actually publish
|
[crates.io token](https://crates.io/settings/tokens) scoped to `publish-new` and
|
||||||
```
|
`publish-update`. Without it the tag and GitHub release still happen, but the crates.io
|
||||||
|
publish fails.
|
||||||
|
|
||||||
### Step 2: Wait for crates.io to Index
|
## Notes
|
||||||
|
|
||||||
After publishing the library, wait 1-2 minutes for crates.io to index it before publishing the client.
|
- A merge with no releasable commits (only `docs:`/`chore:`/`test:`) opens no release PR.
|
||||||
|
That is intended.
|
||||||
|
- The release PR does not run CI: GitHub does not trigger workflows for PRs opened with
|
||||||
|
`GITHUB_TOKEN`. It only touches `Cargo.toml`, `Cargo.lock`, and `CHANGELOG.md`; CI already
|
||||||
|
ran on the commits it is releasing.
|
||||||
|
- If one target's build fails, the other five still upload (`fail-fast: false`). Re-run just
|
||||||
|
that matrix job from the Actions UI.
|
||||||
|
- To skip a release for a commit that would otherwise trigger one, use a `chore:` type.
|
||||||
|
|
||||||
### Step 3: Update Client Dependency (for releases)
|
## Installing a release
|
||||||
|
|
||||||
When publishing to crates.io, the client's `Cargo.toml` should reference the published library version, not the local path.
|
|
||||||
|
|
||||||
Edit `soulseek-rs/Cargo.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
# For local development:
|
|
||||||
soulseek-rs-lib = { version = "0.1.0", path = "../soulseek-rs-lib" }
|
|
||||||
|
|
||||||
# For publishing (comment out path):
|
|
||||||
# soulseek-rs-lib = "0.1.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use both with optional features:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
soulseek-rs-lib = "0.1.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
soulseek-rs-lib = { version = "0.1.0", path = "../soulseek-rs-lib" }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 4: Publish the Client
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ../soulseek-rs
|
|
||||||
cargo publish --dry-run # Test the publish
|
|
||||||
cargo publish # Actually publish
|
|
||||||
```
|
|
||||||
|
|
||||||
## Version Management
|
|
||||||
|
|
||||||
When bumping versions:
|
|
||||||
|
|
||||||
1. Update the version in root `Cargo.toml` under `[workspace.package]`
|
|
||||||
2. Both crates will inherit this version automatically
|
|
||||||
3. Publish library first, then client
|
|
||||||
|
|
||||||
## Automated Releases (Optional)
|
|
||||||
|
|
||||||
You can automate this with GitHub Actions. Create `.github/workflows/release.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
|
|
||||||
- name: Publish library
|
|
||||||
run: |
|
|
||||||
cd soulseek-rs-lib
|
|
||||||
cargo publish --token ${{ secrets.CARGO_TOKEN }}
|
|
||||||
|
|
||||||
- name: Wait for crates.io
|
|
||||||
run: sleep 60
|
|
||||||
|
|
||||||
- name: Publish client
|
|
||||||
run: |
|
|
||||||
cd soulseek-rs
|
|
||||||
cargo publish --token ${{ secrets.CARGO_TOKEN }}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Installation
|
|
||||||
|
|
||||||
After publishing, test the installation:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install soulseek-rs
|
cargo install soulseek-rs
|
||||||
soulseek-rs --help # Should work
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## For Developers Building Custom Clients
|
Or download an archive from the [releases page](https://github.com/michel/soulseek-rs/releases),
|
||||||
|
verify it against the adjacent `.sha256`, and extract the binary.
|
||||||
|
|
||||||
Developers can use the library by adding to their `Cargo.toml`:
|
## Using the library
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
soulseek-rs-lib = "0.1.0"
|
soulseek-rs-lib = "5"
|
||||||
```
|
```
|
||||||
|
|
||||||
They'll get access to all the types and functionality exported in `lib.rs`.
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Both crates inherit `version.workspace`, so they always move together. This
|
||||||
|
# config makes release-plz treat them as one release: a single `vX.Y.Z` tag, one
|
||||||
|
# GitHub release, one root CHANGELOG.md covering both. release-plz still
|
||||||
|
# publishes them to crates.io in dependency order (lib first) and waits for the
|
||||||
|
# index, which is what RELEASE.md used to ask you to do by hand.
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
git_tag_name = "v{{ version }}"
|
||||||
|
git_release_name = "v{{ version }}"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soulseek-rs"
|
||||||
|
version_group = "workspace"
|
||||||
|
changelog_path = "./CHANGELOG.md"
|
||||||
|
changelog_include = ["soulseek-rs-lib"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soulseek-rs-lib"
|
||||||
|
version_group = "workspace"
|
||||||
|
changelog_update = false
|
||||||
|
git_tag_enable = false
|
||||||
|
git_release_enable = false
|
||||||
Reference in New Issue
Block a user