Files

400 lines
13 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-22.04
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: &linux-matrix
include:
- arch: x86_64
runner: ubuntu-22.04
target: x86_64-unknown-linux-gnu
artifact: release-linux-x86_64
- arch: aarch64
runner: ubuntu-22.04-arm
target: aarch64-unknown-linux-gnu
artifact: release-linux-aarch64
# Pin the userspace as well as the runner: the build's libc determines the
# binary's minimum runtime, even on a newer self-hosted Docker host.
container: ubuntu:22.04
steps:
- name: Install build and packaging dependencies
shell: bash
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt-get update
apt-get install --yes --no-install-recommends \
ca-certificates curl git binutils build-essential cmake libclang-dev \
pkg-config zlib1g-dev libfontconfig1-dev python3 python3-tomli
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install pinned Rust toolchain
shell: bash
env:
CARGO_HOME: /usr/local/cargo
RUSTUP_HOME: /usr/local/rustup
run: |
set -euo pipefail
toolchain=$(tr -d '[:space:]' < rust-toolchain)
export PATH="$CARGO_HOME/bin:$PATH"
curl --proto '=https' --tlsv1.2 --fail --silent --show-error \
https://sh.rustup.rs -o "$RUNNER_TEMP/moli-rustup-init.sh"
sh "$RUNNER_TEMP/moli-rustup-init.sh" -y --profile minimal \
--default-toolchain "$toolchain" --no-modify-path
echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV"
echo "RUSTUP_HOME=$RUSTUP_HOME" >> "$GITHUB_ENV"
echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
rustc --version
- 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
verify-linux:
name: Verify Ubuntu 22.04 ${{ matrix.arch }}
needs: [validate, build-linux]
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix: *linux-matrix
# A fresh runtime, without compilers or build dependencies, catches missing
# shared libraries that could be hidden by the build environment.
container: ubuntu:22.04
steps:
- name: Install runtime and inspection tools
shell: bash
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt-get update
apt-get install --yes --no-install-recommends \
ca-certificates git binutils python3 libstdc++6 zlib1g libfontconfig1
- name: Checkout compatibility check
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Download Linux artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ matrix.artifact }}
path: dist
- name: Check packaged ELF and run HTTP / JavaScript smoke test
shell: bash
env:
RELEASE_VERSION: ${{ needs.validate.outputs.version }}
RELEASE_TARGET: ${{ matrix.target }}
run: |
set -euo pipefail
mkdir -p target/linux-compat
tar -xzf "dist/moli-$RELEASE_TARGET.tar.gz" -C target/linux-compat
python3 scripts/check_linux_compat.py \
"target/linux-compat/moli-v$RELEASE_VERSION-$RELEASE_TARGET/moli" --smoke
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
- verify-linux
- build-macos
- build-windows
runs-on: ubuntu-22.04
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"