Files
NodeFlare/.github/workflows/release.yml
T
2026-08-27 21:27:40 +08:00

336 lines
12 KiB
YAML

name: CI and tagged release
on:
push:
branches:
- main
tags:
- "v*.*.*"
pull_request:
workflow_dispatch:
inputs:
version:
description: Tag version with a v prefix, for example v0.0.1
required: true
default: v0.0.1
permissions:
contents: read
jobs:
metadata:
name: Resolve build metadata
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
version: ${{ steps.version.outputs.version }}
release: ${{ steps.version.outputs.release }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- id: version
name: Resolve release version
shell: bash
env:
RAW_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.version || startsWith(github.ref, 'refs/tags/') && github.ref_name || 'v0.0.0' }}
RELEASE_BUILD: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') }}
run: |
if [ "$RELEASE_BUILD" = "true" ] && [[ ! "$RAW_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Tag must use the v-prefixed format v1.2.3" >&2
exit 1
fi
version="${RAW_VERSION#v}"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "release=$RELEASE_BUILD" >> "$GITHUB_OUTPUT"
quality:
name: Quality checks
needs: metadata
runs-on: ubuntu-latest
timeout-minutes: 30
env:
NODEFLARE_VERSION: ${{ needs.metadata.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
components: clippy, rustfmt
targets: wasm32-unknown-unknown
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.4.0
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Typecheck frontend
run: bunx --bun tsc -b frontend
- name: Test frontend
run: bun test --cwd frontend
- name: Build frontend
run: bun run build:frontend
- name: Check Rust formatting
run: cargo fmt --all -- --check
- name: Check Agent formatting
run: cargo fmt --manifest-path agent/Cargo.toml -- --check
- name: Lint Worker
run: cargo clippy --locked --all-targets -- -D warnings
- name: Lint Agent
run: cargo clippy --locked --manifest-path agent/Cargo.toml --all-targets -- -D warnings
- name: Test Worker
run: cargo test --locked --lib
- name: Test Agent
run: cargo test --locked --manifest-path agent/Cargo.toml
- name: Build Worker
run: NODEFLARE_ADMIN_READY=1 sh scripts/build-worker.sh
- name: Smoke test Worker and D1
shell: bash
run: |
set -euo pipefail
state_dir="$RUNNER_TEMP/nodeflare-state"
log_file="$RUNNER_TEMP/nodeflare-dev.log"
export XDG_CONFIG_HOME="$RUNNER_TEMP/wrangler-config"
mkdir -p "$XDG_CONFIG_HOME"
bunx wrangler d1 migrations apply DB --local --persist-to "$state_dir"
NODEFLARE_ADMIN_READY=1 NODEFLARE_WORKER_READY=1 \
bunx wrangler dev --local --persist-to "$state_dir" --port 8787 \
--var ADMIN_USERNAME:smoke --var ADMIN_PASSWORD:SmokePassword123 \
>"$log_file" 2>&1 &
dev_pid=$!
trap 'kill "$dev_pid" 2>/dev/null || true' EXIT
ready=0
for _ in $(seq 1 30); do
if curl --fail --silent http://127.0.0.1:8787/api/bootstrap >/dev/null; then
ready=1
break
fi
sleep 1
done
if [ "$ready" != 1 ]; then
cat "$log_file"
exit 1
fi
if ! MONITOR_BASE_URL=http://127.0.0.1:8787 \
MONITOR_ADMIN_USERNAME=smoke MONITOR_ADMIN_PASSWORD=SmokePassword123 \
sh scripts/smoke-test.sh; then
cat "$log_file"
exit 1
fi
agent:
name: Agent (${{ matrix.platform }})
needs: metadata
if: needs.metadata.outputs.release == 'true'
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
permissions:
attestations: write
contents: read
id-token: write
env:
CARGO_TARGET_DIR: target/agent
NODEFLARE_VERSION: ${{ needs.metadata.outputs.version }}
strategy:
fail-fast: false
matrix:
include:
- platform: Linux x64
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
asset: agent-linux-x64
extension: ""
cross: true
static_linux: true
- platform: Linux ARM64
runner: ubuntu-latest
target: aarch64-unknown-linux-musl
asset: agent-linux-aarch64
extension: ""
cross: true
static_linux: true
- platform: Windows x64
runner: windows-latest
target: x86_64-pc-windows-msvc
asset: agent-windows-x64.exe
extension: .exe
cross: false
- platform: macOS ARM64
runner: macos-14
target: aarch64-apple-darwin
asset: agent-macos-aarch64
extension: ""
cross: false
- platform: FreeBSD x64
runner: ubuntu-latest
target: x86_64-unknown-freebsd
asset: agent-freebsd-x64
extension: ""
cross: true
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
targets: ${{ matrix.target }}
- name: Install cross compiler
if: matrix.cross
run: cargo install cross --version 0.2.5 --locked
- name: Build Rust Agent with cross
if: matrix.cross
run: CROSS_CONFIG=Cross.toml cross build --manifest-path agent/Cargo.toml --release --locked --target ${{ matrix.target }}
- name: Build Rust Agent with Cargo
if: matrix.cross == false
run: cargo build --manifest-path agent/Cargo.toml --release --locked --target ${{ matrix.target }}
- name: Stage Agent binary
shell: bash
run: |
mkdir -p release
cp "target/agent/${{ matrix.target }}/release/nodeflare-agent${{ matrix.extension }}" "release/${{ matrix.asset }}"
chmod +x "release/${{ matrix.asset }}"
- name: Install ARM64 emulator
if: matrix.target == 'aarch64-unknown-linux-musl'
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends qemu-user-static
- name: Verify Linux Agent version
if: matrix.cross && matrix.target == 'x86_64-unknown-linux-musl'
shell: bash
run: |
version_output=$("release/${{ matrix.asset }}" --version)
actual_version="${version_output##* }"
if [ "$actual_version" != "${{ needs.metadata.outputs.version }}" ]; then
echo "Agent version $actual_version does not match tag ${{ needs.metadata.outputs.version }}" >&2
exit 1
fi
- name: Verify Linux ARM64 Agent version
if: matrix.target == 'aarch64-unknown-linux-musl'
shell: bash
run: |
version_output=$(qemu-aarch64-static "release/${{ matrix.asset }}" --version)
actual_version="${version_output##* }"
if [ "$actual_version" != "${{ needs.metadata.outputs.version }}" ]; then
echo "Agent version $actual_version does not match tag ${{ needs.metadata.outputs.version }}" >&2
exit 1
fi
- name: Verify Agent version
if: matrix.cross == false
shell: bash
run: |
version_output=$("release/${{ matrix.asset }}" --version)
actual_version="${version_output##* }"
if [ "$actual_version" != "${{ needs.metadata.outputs.version }}" ]; then
echo "Agent version $actual_version does not match tag ${{ needs.metadata.outputs.version }}" >&2
exit 1
fi
- name: Verify static Linux binary
if: matrix.static_linux
shell: bash
run: |
if readelf -l "release/${{ matrix.asset }}" | grep -q 'Requesting program interpreter'; then
echo "Linux Agent must be statically linked" >&2
exit 1
fi
- name: Verify FreeBSD Agent in FreeBSD
if: matrix.target == 'x86_64-unknown-freebsd'
uses: vmactions/freebsd-vm@83b151f58c6047089f4c80eb5ba2039d158ce093 # v1.5.3
with:
release: "15"
envs: NODEFLARE_VERSION
copyback: false
run: |
set -eu
version_output=$(./release/agent-freebsd-x64 --version)
actual_version=${version_output##* }
if [ "$actual_version" != "$NODEFLARE_VERSION" ]; then
echo "Agent version $actual_version does not match tag $NODEFLARE_VERSION" >&2
exit 1
fi
- uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
with:
subject-path: release/${{ matrix.asset }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.asset }}
path: release/${{ matrix.asset }}
if-no-files-found: error
frontend:
name: Frontend
needs: metadata
if: needs.metadata.outputs.release == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
attestations: write
contents: read
id-token: write
env:
NODEFLARE_VERSION: ${{ needs.metadata.outputs.version }}
VITE_NODEFLARE_VERSION: ${{ needs.metadata.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.4.0
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build public frontend
run: bun run --cwd frontend build:public
- name: Package frontend
run: tar -czf "nodeflare-frontend-${{ needs.metadata.outputs.version }}.tar.gz" -C frontend/dist .
- uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
with:
subject-path: nodeflare-frontend-${{ needs.metadata.outputs.version }}.tar.gz
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: frontend
path: nodeflare-frontend-${{ needs.metadata.outputs.version }}.tar.gz
if-no-files-found: error
release:
name: Publish GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
needs: [metadata, quality, agent, frontend]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
actions: read
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Build changelog from commits
shell: bash
run: |
previous_tag=$(git describe --tags --abbrev=0 "${GITHUB_SHA}^" 2>/dev/null || true)
if [ -n "$previous_tag" ]; then
commit_range="${previous_tag}..${GITHUB_SHA}"
else
commit_range="${GITHUB_SHA}"
fi
git log "$commit_range" --no-merges --pretty=format:'- %s' > CHANGELOG.md
if [ ! -s CHANGELOG.md ]; then
git log -1 --pretty=format:'- %s' "$GITHUB_SHA" > CHANGELOG.md
fi
printf '\n' >> CHANGELOG.md
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: release-assets
merge-multiple: true
- uses: softprops/action-gh-release@fe965f7af51af5f2602596916f38a38df2e33de0 # v3.0.2
with:
body_path: CHANGELOG.md
files: release-assets/*
fail_on_unmatched_files: true