Files

331 lines
9.9 KiB
YAML

name: Release
on:
workflow_dispatch:
inputs:
version:
description: Version from moli/Cargo.toml (for example, 0.1.1)
required: true
type: string
prerelease:
description: Mark this release as a prerelease
required: true
default: false
type: boolean
draft:
description: Create a draft instead of publishing immediately
required: true
default: false
type: boolean
concurrency:
group: release-${{ inputs.version }}
cancel-in-progress: false
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: safe.directory
GIT_CONFIG_VALUE_0: ${{ github.workspace }}
jobs:
validate:
name: Validate release request
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
tag: ${{ steps.release.outputs.tag }}
version: ${{ steps.release.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
- name: Validate version and tag
id: release
shell: bash
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
version="${INPUT_VERSION#v}"
semver_pattern='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'
if [[ ! "$version" =~ $semver_pattern ]]; then
echo "Invalid semantic version: $INPUT_VERSION" >&2
exit 1
fi
manifest_version=$(awk '$1 == "version" && $2 == "=" { gsub(/"/, "", $3); print $3; exit }' moli/Cargo.toml)
if [[ "$version" != "$manifest_version" ]]; then
echo "Requested version $version does not match moli/Cargo.toml ($manifest_version)." >&2
exit 1
fi
tag="v$version"
if git show-ref --verify --quiet "refs/tags/$tag"; then
echo "Tag $tag already exists." >&2
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
build-linux:
name: Build Linux ${{ matrix.arch }}
needs: validate
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact: release-linux-x86_64
- arch: aarch64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
artifact: release-linux-aarch64
container: rust:1.96.1-bookworm
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install packaging dependencies
shell: bash
run: |
apt-get update
apt-get install --yes --no-install-recommends binutils cmake libclang-dev python3
- name: Validate shell installer
shell: bash
run: sh -n scripts/install.sh
- name: Build and package
shell: bash
run: >-
python3 scripts/release.py
--version "${{ needs.validate.outputs.version }}"
--expected-target "${{ matrix.target }}"
- name: Upload Linux artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.artifact }}
path: dist/
if-no-files-found: error
compression-level: 0
retention-days: 7
build-macos:
name: Build macOS ${{ matrix.arch }}
needs: validate
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: macos-15-intel
target: x86_64-apple-darwin
artifact: release-macos-x86_64
- arch: aarch64
runner: macos-15
target: aarch64-apple-darwin
artifact: release-macos-aarch64
env:
MACOSX_DEPLOYMENT_TARGET: "13.0"
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install pinned Rust toolchain
shell: bash
run: |
set -euo pipefail
toolchain=$(tr -d '[:space:]' < rust-toolchain)
rustup toolchain install "$toolchain" --profile minimal --no-self-update
rustc --version
- name: Build and package
shell: bash
run: >-
python3 scripts/release.py
--version "${{ needs.validate.outputs.version }}"
--expected-target "${{ matrix.target }}"
- name: Upload macOS artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.artifact }}
path: dist/
if-no-files-found: error
compression-level: 0
retention-days: 7
build-windows:
name: Build Windows ${{ matrix.arch }}
needs: validate
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
runner: windows-2025
target: x86_64-pc-windows-msvc
artifact: release-windows-x86_64
- arch: aarch64
runner: windows-11-arm
target: aarch64-pc-windows-msvc
artifact: release-windows-aarch64
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install pinned Rust toolchain
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$toolchain = (Get-Content rust-toolchain -Raw).Trim()
rustup toolchain install $toolchain --profile minimal --no-self-update
rustc --version
- name: Validate PowerShell installer
shell: pwsh
run: |
$tokens = $null
$parseErrors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
(Resolve-Path scripts/install.ps1),
[ref] $tokens,
[ref] $parseErrors
) | Out-Null
if ($parseErrors.Count -ne 0) {
$parseErrors | ForEach-Object { Write-Error $_ }
exit 1
}
- name: Build and package
shell: pwsh
run: >-
python scripts/release.py
--version "${{ needs.validate.outputs.version }}"
--expected-target "${{ matrix.target }}"
- name: Upload Windows artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.artifact }}
path: dist/
if-no-files-found: error
compression-level: 0
retention-days: 7
publish:
name: Create GitHub Release
needs:
- validate
- build-linux
- build-macos
- build-windows
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Download packaged artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: release-*
path: dist
merge-multiple: true
- name: Add stable installers
shell: bash
run: |
install -m 0755 scripts/install.sh dist/moli-installer.sh
install -m 0644 scripts/install.ps1 dist/moli-installer.ps1
- name: Verify release assets
shell: bash
run: |
set -euo pipefail
expected_archives=(
"moli-x86_64-unknown-linux-gnu.tar.gz"
"moli-aarch64-unknown-linux-gnu.tar.gz"
"moli-x86_64-apple-darwin.tar.gz"
"moli-aarch64-apple-darwin.tar.gz"
"moli-x86_64-pc-windows-msvc.zip"
"moli-aarch64-pc-windows-msvc.zip"
)
for archive in "${expected_archives[@]}"; do
test -f "dist/$archive"
done
test -f dist/moli-installer.sh
test -f dist/moli-installer.ps1
sh -n dist/moli-installer.sh
artifact_count=$(find dist -maxdepth 1 -type f | wc -l)
if [[ "$artifact_count" -ne 8 ]]; then
echo "Expected 8 release assets, found $artifact_count." >&2
find dist -maxdepth 1 -type f -print >&2
exit 1
fi
- name: Create GitHub Release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.validate.outputs.tag }}
RELEASE_DRAFT: ${{ inputs.draft }}
RELEASE_PRERELEASE: ${{ inputs.prerelease }}
run: |
set -euo pipefail
assets=(dist/*)
args=(
release create "$RELEASE_TAG"
"${assets[@]}"
--repo "$GITHUB_REPOSITORY"
--target "$GITHUB_SHA"
--title "Moli $RELEASE_TAG"
--generate-notes
)
if [[ "$RELEASE_PRERELEASE" == true ]]; then
args+=(--prerelease)
elif [[ "$RELEASE_DRAFT" != true ]]; then
args+=(--latest)
fi
if [[ "$RELEASE_DRAFT" == true ]]; then
args+=(--draft)
fi
release_url=$(gh "${args[@]}")
printf 'Created %s\n' "$release_url"
printf '### Release created\n\n[%s](%s)\n' \
"$RELEASE_TAG" "$release_url" >> "$GITHUB_STEP_SUMMARY"