All checks were successful
build-prerelease / Resolve version stamps + change detection (push) Successful in 12s
build-prerelease / Lint (fmt + clippy) (push) Successful in 2m21s
build-prerelease / Build neuron-blackwell (push) Successful in 1m40s
build-prerelease / Build helexa-bench binary (push) Successful in 1m36s
build-prerelease / Build cortex binary (push) Successful in 2m2s
build-prerelease / Build neuron-ampere (push) Successful in 2m25s
build-prerelease / Build neuron-ada (push) Successful in 2m27s
build-prerelease / Build helexa-upstream binary (push) Successful in 2m23s
build-prerelease / Test (push) Successful in 5m32s
build-prerelease / Package helexa-bench RPM (push) Successful in 20s
build-prerelease / Package helexa-upstream RPM (push) Successful in 20s
build-prerelease / Package cortex RPM (push) Successful in 25s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 44s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 44s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 49s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 18s
git log --date=format: renders each commit's timestamp in that commit's OWN recorded timezone. Local commits (UTC+3) and Gitea server-side merge commits (UTC) therefore produced non-monotonic release stamps: the 2026-07-01 bench-config commit stamped 235959 (local) while the later F1 merges stamped 2123xx/2154xx (UTC), so RPM EVR comparison ranked the older build newest. The deploy gate correctly detected the mismatch (it compares by buildTime) but dnf upgrade refused the "downgrade" and silently restarted the stale binary fleet-wide. TZ=UTC0 + --date=format-local: pins the stamp to UTC for every commit. Hosts currently pinned on the 235959 build self-heal on the first build after 2026-07-02 00:00 UTC; beast is downgraded manually (it needs the F1 #92 build for the 80B bring-up). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TczcGF7JSjJs8r15RSSGpx
710 lines
28 KiB
YAML
710 lines
28 KiB
YAML
name: build-prerelease
|
|
|
|
# Builds CUDA-flavoured neuron binaries (and a single cortex binary),
|
|
# packages each as a Fedora RPM, signs them, and publishes to the
|
|
# `unstable` channel at rpm.lair.cafe.
|
|
#
|
|
# Change-aware: the `prepare` job diffs HEAD against the git sha
|
|
# embedded in the most recently *published* unstable RPM (per package)
|
|
# and skips builds whose inputs didn't change. Docs-only commits build
|
|
# nothing; gateway-only commits skip the 3 CUDA builds (and, via
|
|
# deploy.yml's own check-update gate, the neuron restarts + model
|
|
# cold-loads). Diffing against the published sha — not the previous
|
|
# push — means a failed run can never cause a change to be missed.
|
|
#
|
|
# Lint (fmt+clippy) and test run here as parallel jobs and gate
|
|
# `publish`; ci.yml no longer runs on pushes to main (see its trigger
|
|
# comment), so the two workflows stop competing for the same runners.
|
|
#
|
|
# The published packages are versioned as e.g.
|
|
# helexa-neuron-blackwell-0.1.16-0.1.20260518T140530.gitabcdef0.fc43.x86_64
|
|
# ^^^^^^^^^^^^^^^^^^ ^^^^^^^^
|
|
# commit time (s) commit sha
|
|
# so they sort BELOW the eventual 0.1.16-1 stable release, and so two
|
|
# commits on the same day are still strictly ordered by their commit
|
|
# timestamps (rather than by RPM-vercmp's alpha-vs-digit precedence
|
|
# on the SHA fragment).
|
|
|
|
on:
|
|
# Auto-build on every push to main so the unstable channel tracks
|
|
# head without a manual dispatch step.
|
|
push:
|
|
branches: [main]
|
|
# Manual dispatch still available to build from a non-main ref.
|
|
# Dispatched runs skip change detection and build everything.
|
|
workflow_dispatch:
|
|
inputs:
|
|
ref:
|
|
description: "Git ref to build (branch / tag / commit). Defaults to the workflow's branch."
|
|
required: false
|
|
default: ""
|
|
|
|
# Coalesce same-ref pushes: a newer push cancels the older in-flight
|
|
# run — the newest commit is the one we want on the fleet. The publish
|
|
# job keeps its own `rpm-publish` group (cancel=false) so an in-flight
|
|
# repo update is never interrupted. Runners are ephemeral (one VM per
|
|
# job) so concurrent runs no longer race on a shared workspace; the
|
|
# old shared `cortex-runner-pool` group with ci.yml is gone.
|
|
concurrency:
|
|
group: build-prerelease-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
CARGO_INCREMENTAL: "0"
|
|
CARGO_TERM_COLOR: "always"
|
|
|
|
jobs:
|
|
prepare:
|
|
name: Resolve version stamps + change detection
|
|
timeout-minutes: 10
|
|
runs-on: rust
|
|
outputs:
|
|
version: ${{ steps.info.outputs.version }}
|
|
release: ${{ steps.info.outputs.release }}
|
|
short_sha: ${{ steps.info.outputs.short_sha }}
|
|
commit_timestamp: ${{ steps.info.outputs.commit_timestamp }}
|
|
build_cortex: ${{ steps.changes.outputs.build_cortex }}
|
|
build_neuron: ${{ steps.changes.outputs.build_neuron }}
|
|
build_bench: ${{ steps.changes.outputs.build_bench }}
|
|
build_upstream: ${{ steps.changes.outputs.build_upstream }}
|
|
check_rust: ${{ steps.changes.outputs.check_rust }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
fetch-depth: 0
|
|
|
|
- id: info
|
|
run: |
|
|
set -eux
|
|
VERSION=$(awk -F\" '/^version[[:space:]]*=/ { print $2; exit }' Cargo.toml)
|
|
SHORT_SHA=$(git rev-parse --short=7 HEAD)
|
|
# Second-precise commit timestamp gives the release stamp a
|
|
# strictly monotonic numeric prefix. The earlier %Y%m%d-only
|
|
# form let same-day builds be ordered by RPM's rpmvercmp
|
|
# rules over the SHA, which is non-chronological — e.g.
|
|
# "git602e8e1" sorts newer than "gitf9f5fa4" purely because
|
|
# rpmvercmp ranks digit-prefixed segments above alpha ones.
|
|
# The SHA stays only as a debug identifier; sort order is
|
|
# decided entirely by the timestamp.
|
|
# format-local + TZ=UTC0 renders the stamp in UTC regardless
|
|
# of the committer's recorded timezone. Plain `format:` uses
|
|
# each commit's own TZ offset — local commits (UTC+3) and
|
|
# Gitea server-side merge commits (UTC) interleaved
|
|
# non-monotonically, letting an older build out-rank newer
|
|
# ones in RPM EVR comparison (the 2026-07-01 "235959" stamp
|
|
# that froze the fleet on a stale build).
|
|
COMMIT_TIMESTAMP=$(TZ=UTC0 git log -1 --format=%cd --date=format-local:%Y%m%d%H%M%S HEAD)
|
|
RELEASE="0.1.${COMMIT_TIMESTAMP}.git${SHORT_SHA}"
|
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "release=${RELEASE}" >> "$GITHUB_OUTPUT"
|
|
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
|
echo "commit_timestamp=${COMMIT_TIMESTAMP}" >> "$GITHUB_OUTPUT"
|
|
|
|
- id: changes
|
|
run: |
|
|
set -ux
|
|
# Default: build everything. Detection only ever narrows
|
|
# this, and any failure along the way (manifest unreachable,
|
|
# unparsable, sha not in history after a force-push) leaves
|
|
# the full build in place. Manual dispatches always build
|
|
# everything — predictable when building odd refs.
|
|
BUILD_CORTEX=true
|
|
BUILD_NEURON=true
|
|
BUILD_BENCH=true
|
|
BUILD_UPSTREAM=true
|
|
CHECK_RUST=true
|
|
|
|
if [ "${GITHUB_EVENT_NAME}" = "push" ]; then
|
|
MANIFEST_URL="https://rpm.lair.cafe/fedora/43/x86_64/unstable/packages.json"
|
|
if curl -fsS --max-time 20 -o /tmp/packages.json "$MANIFEST_URL"; then
|
|
# Latest published sha per package, by buildTime.
|
|
base_for() {
|
|
python3 - "$1" <<'PY'
|
|
import json, re, sys
|
|
name = sys.argv[1]
|
|
try:
|
|
with open("/tmp/packages.json") as f:
|
|
pkgs = json.load(f)["packages"]
|
|
cands = [p for p in pkgs if p.get("name") == name]
|
|
if cands:
|
|
latest = max(cands, key=lambda p: p.get("buildTime", 0))
|
|
m = re.search(r"git\.?([0-9a-f]{7,40})", latest.get("release", ""))
|
|
if m:
|
|
print(m.group(1))
|
|
except Exception:
|
|
pass
|
|
PY
|
|
}
|
|
|
|
# true if no usable base, else true iff the diff since
|
|
# the published sha touches the given path pattern.
|
|
decide() {
|
|
local base="$1" pattern="$2"
|
|
if [ -z "$base" ] \
|
|
|| ! git cat-file -e "${base}^{commit}" 2>/dev/null \
|
|
|| ! git merge-base --is-ancestor "$base" HEAD 2>/dev/null; then
|
|
echo true; return
|
|
fi
|
|
if git diff --name-only "${base}..HEAD" | grep -qE "$pattern"; then
|
|
echo true
|
|
else
|
|
echo false
|
|
fi
|
|
}
|
|
|
|
# cortex-core is shared by both binaries; Cargo.{toml,lock}
|
|
# affect both; this workflow file affects both.
|
|
NEURON_RE='^crates/neuron/|^crates/cortex-core/|^Cargo\.toml$|^Cargo\.lock$|^rpm/helexa-neuron-prerelease\.spec$|^data/neuron|^neuron\.example\.toml$|^\.gitea/workflows/build-prerelease\.yml$'
|
|
CORTEX_RE='^crates/cortex-gateway/|^crates/cortex-cli/|^crates/cortex-core/|^Cargo\.toml$|^Cargo\.lock$|^rpm/cortex-prerelease\.spec$|^data/cortex|^cortex\.example\.toml$|^models\.example\.toml$|^\.gitea/workflows/build-prerelease\.yml$'
|
|
BENCH_RE='^crates/helexa-bench/|^crates/cortex-core/|^Cargo\.toml$|^Cargo\.lock$|^rpm/helexa-bench-prerelease\.spec$|^data/helexa-bench|^helexa-bench\.example\.toml$|^\.gitea/workflows/build-prerelease\.yml$'
|
|
UPSTREAM_RE='^crates/helexa-upstream/|^crates/cortex-core/|^Cargo\.toml$|^Cargo\.lock$|^rpm/helexa-upstream-prerelease\.spec$|^data/helexa-upstream|^helexa-upstream\.example\.toml$|^\.gitea/workflows/build-prerelease\.yml$'
|
|
# Any Rust change (incl. crates not packaged here, e.g.
|
|
# helexa-acp) still needs lint+test on main.
|
|
RUST_RE='\.rs$|^crates/|Cargo\.toml$|^Cargo\.lock$'
|
|
|
|
CORTEX_BASE=$(base_for cortex)
|
|
NEURON_BASE=$(base_for helexa-neuron-blackwell)
|
|
BENCH_BASE=$(base_for helexa-bench)
|
|
UPSTREAM_BASE=$(base_for helexa-upstream)
|
|
BUILD_CORTEX=$(decide "$CORTEX_BASE" "$CORTEX_RE")
|
|
BUILD_NEURON=$(decide "$NEURON_BASE" "$NEURON_RE")
|
|
BUILD_BENCH=$(decide "$BENCH_BASE" "$BENCH_RE")
|
|
BUILD_UPSTREAM=$(decide "$UPSTREAM_BASE" "$UPSTREAM_RE")
|
|
if [ "$BUILD_CORTEX" = "true" ] || [ "$BUILD_NEURON" = "true" ] || [ "$BUILD_BENCH" = "true" ] || [ "$BUILD_UPSTREAM" = "true" ]; then
|
|
CHECK_RUST=true
|
|
else
|
|
CHECK_RUST=$(decide "$CORTEX_BASE" "$RUST_RE")
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
echo "build_cortex=${BUILD_CORTEX}" >> "$GITHUB_OUTPUT"
|
|
echo "build_neuron=${BUILD_NEURON}" >> "$GITHUB_OUTPUT"
|
|
echo "build_bench=${BUILD_BENCH}" >> "$GITHUB_OUTPUT"
|
|
echo "build_upstream=${BUILD_UPSTREAM}" >> "$GITHUB_OUTPUT"
|
|
echo "check_rust=${CHECK_RUST}" >> "$GITHUB_OUTPUT"
|
|
echo "### change detection: build_cortex=${BUILD_CORTEX} build_neuron=${BUILD_NEURON} build_bench=${BUILD_BENCH} build_upstream=${BUILD_UPSTREAM} check_rust=${CHECK_RUST}"
|
|
|
|
# fmt + clippy + test moved here from ci.yml for main pushes so the
|
|
# two workflows stop queueing against each other (ci.yml's checks
|
|
# used to delay build-cortex by ~12 minutes on the shared runner
|
|
# pool). They run in parallel with the builds and gate `publish`,
|
|
# not the builds themselves — a clippy warning still can't reach the
|
|
# fleet, but it also doesn't serialize the pipeline.
|
|
lint:
|
|
name: Lint (fmt + clippy)
|
|
timeout-minutes: 25
|
|
needs: prepare
|
|
if: needs.prepare.outputs.check_rust == 'true'
|
|
runs-on: rust
|
|
env:
|
|
RUSTC_WRAPPER: sccache
|
|
SCCACHE_BUCKET: sccache
|
|
SCCACHE_ENDPOINT: http://caveman.kosherinata.internal:9000
|
|
SCCACHE_REGION: auto
|
|
SCCACHE_S3_USE_SSL: "false"
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
- run: cargo fmt --check --all
|
|
# Failure-aware sccache escalation lives in the shared script: a
|
|
# signal death (rustc SIGSEGV / OOM-kill) keeps the cache and fails
|
|
# fast instead of triggering a slower uncached rebuild; only a real
|
|
# sccache fault drops the cache. See script/ci-cargo-escalate.sh.
|
|
- name: Clippy (sccache escalation)
|
|
run: script/ci-cargo-escalate.sh cargo clippy --workspace -- -D warnings
|
|
|
|
test:
|
|
name: Test
|
|
timeout-minutes: 25
|
|
needs: prepare
|
|
if: needs.prepare.outputs.check_rust == 'true'
|
|
runs-on: rust
|
|
env:
|
|
RUSTC_WRAPPER: sccache
|
|
SCCACHE_BUCKET: sccache
|
|
SCCACHE_ENDPOINT: http://caveman.kosherinata.internal:9000
|
|
SCCACHE_REGION: auto
|
|
SCCACHE_S3_USE_SSL: "false"
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
# See script/ci-cargo-escalate.sh for the escalation rationale.
|
|
- name: Test (sccache escalation)
|
|
run: script/ci-cargo-escalate.sh cargo test --workspace
|
|
|
|
build-cortex:
|
|
name: Build cortex binary
|
|
timeout-minutes: 25
|
|
needs: prepare
|
|
if: needs.prepare.outputs.build_cortex == 'true'
|
|
# runner-rust image already provides rust/cargo/clippy/rustfmt via
|
|
# dnf — no rustup install step needed.
|
|
runs-on: rust
|
|
env:
|
|
RUSTC_WRAPPER: sccache
|
|
SCCACHE_BUCKET: sccache
|
|
SCCACHE_ENDPOINT: http://caveman.kosherinata.internal:9000
|
|
SCCACHE_REGION: auto
|
|
SCCACHE_S3_USE_SSL: "false"
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
# See script/ci-cargo-escalate.sh for the escalation rationale.
|
|
- name: Build cortex (release, sccache escalation)
|
|
run: script/ci-cargo-escalate.sh cargo build --release -p cortex-cli
|
|
|
|
- name: Stage binary
|
|
run: |
|
|
mkdir --parents artifacts
|
|
cp target/release/cortex artifacts/cortex
|
|
./artifacts/cortex --version || true
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: cortex-fc43
|
|
path: artifacts/cortex
|
|
retention-days: 1
|
|
|
|
build-bench:
|
|
name: Build helexa-bench binary
|
|
timeout-minutes: 25
|
|
needs: prepare
|
|
if: needs.prepare.outputs.build_bench == 'true'
|
|
# Pure-Rust, non-CUDA binary — same runner as cortex.
|
|
runs-on: rust
|
|
env:
|
|
RUSTC_WRAPPER: sccache
|
|
SCCACHE_BUCKET: sccache
|
|
SCCACHE_ENDPOINT: http://caveman.kosherinata.internal:9000
|
|
SCCACHE_REGION: auto
|
|
SCCACHE_S3_USE_SSL: "false"
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- name: Build helexa-bench (release, sccache escalation)
|
|
run: |
|
|
# Stamp the SHA helexa-bench records as bench_sha against every
|
|
# run (option_env! in sweep.rs reads it at compile time).
|
|
export HELEXA_BUILD_SHA="$(git rev-parse HEAD)"
|
|
script/ci-cargo-escalate.sh cargo build --release -p helexa-bench
|
|
|
|
- name: Stage binary
|
|
run: |
|
|
mkdir --parents artifacts
|
|
cp target/release/helexa-bench artifacts/helexa-bench
|
|
./artifacts/helexa-bench --version || true
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: bench-fc43
|
|
path: artifacts/helexa-bench
|
|
retention-days: 1
|
|
|
|
build-upstream:
|
|
name: Build helexa-upstream binary
|
|
timeout-minutes: 25
|
|
needs: prepare
|
|
if: needs.prepare.outputs.build_upstream == 'true'
|
|
# Pure-Rust, non-CUDA binary — same runner as cortex/bench.
|
|
runs-on: rust
|
|
env:
|
|
RUSTC_WRAPPER: sccache
|
|
SCCACHE_BUCKET: sccache
|
|
SCCACHE_ENDPOINT: http://caveman.kosherinata.internal:9000
|
|
SCCACHE_REGION: auto
|
|
SCCACHE_S3_USE_SSL: "false"
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_KEY }}
|
|
# helexa-upstream uses the sqlx runtime query API (no compile-time
|
|
# query macros), so it builds without a database or a .sqlx cache.
|
|
# Set OFFLINE defensively so a stray macro can never reach for a DB.
|
|
SQLX_OFFLINE: "true"
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- name: Build helexa-upstream (release, sccache escalation)
|
|
run: script/ci-cargo-escalate.sh cargo build --release -p helexa-upstream
|
|
|
|
- name: Stage binary
|
|
run: |
|
|
mkdir --parents artifacts
|
|
cp target/release/helexa-upstream artifacts/helexa-upstream
|
|
./artifacts/helexa-upstream --version || true
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: upstream-fc43
|
|
path: artifacts/helexa-upstream
|
|
retention-days: 1
|
|
|
|
build-neuron:
|
|
name: Build neuron-${{ matrix.flavour }}
|
|
timeout-minutes: 35
|
|
needs: prepare
|
|
if: needs.prepare.outputs.build_neuron == 'true'
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- flavour: ampere
|
|
compute_cap: "86"
|
|
runner: cuda-13.0
|
|
cuda_home: /usr/local/cuda-13.0
|
|
build_jobs: 8
|
|
nvcc_threads: 4
|
|
cargo_features: "cuda cudnn"
|
|
- flavour: ada
|
|
compute_cap: "89"
|
|
runner: cuda-13.0
|
|
cuda_home: /usr/local/cuda-13.0
|
|
build_jobs: 8
|
|
nvcc_threads: 4
|
|
cargo_features: "cuda cudnn"
|
|
- flavour: blackwell
|
|
compute_cap: "120"
|
|
runner: cuda-13.0
|
|
cuda_home: /usr/local/cuda-13.0
|
|
build_jobs: 8
|
|
nvcc_threads: 4
|
|
cargo_features: "cuda cudnn"
|
|
runs-on: ${{ matrix.runner }}
|
|
env:
|
|
SCCACHE_BUCKET: sccache
|
|
SCCACHE_ENDPOINT: http://caveman.kosherinata.internal:9000
|
|
SCCACHE_REGION: auto
|
|
SCCACHE_S3_USE_SSL: "false"
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.SCCACHE_S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCCACHE_S3_SECRET_KEY }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
# sccache handling + failure classification lives in
|
|
# script/ci-cargo-escalate.sh: it probes for sccache (the CUDA
|
|
# image may not ship it — a missing binary degrades to an uncached
|
|
# build rather than failing at `sccache rustc -vV`), and a rustc
|
|
# SIGSEGV / OOM-kill keeps the cache and fails fast instead of
|
|
# escalating to a slower uncached rebuild. The cache covers the
|
|
# ~600-crate host-side dep tree (the bulk of the 10-14 min build),
|
|
# shared across all three flavours, so even one run seeds the next.
|
|
- name: Build neuron with CUDA (${{ matrix.flavour }})
|
|
run: |
|
|
export PATH="${{ matrix.cuda_home }}/bin:${PATH}"
|
|
export LD_LIBRARY_PATH="${{ matrix.cuda_home }}/targets/x86_64-linux/lib:${{ matrix.cuda_home }}/lib64:${LD_LIBRARY_PATH:-}"
|
|
export LIBRARY_PATH="${{ matrix.cuda_home }}/targets/x86_64-linux/lib:${{ matrix.cuda_home }}/lib64:${LIBRARY_PATH:-}"
|
|
# Pin the build SHA neuron reports from GET /version. The git
|
|
# fallback in build.rs would also work on a full checkout, but
|
|
# injecting the exact checked-out commit is unambiguous under
|
|
# shallow/detached states and makes the artifact self-describing.
|
|
export HELEXA_BUILD_SHA="$(git rev-parse HEAD)"
|
|
script/ci-cargo-escalate.sh cargo build --release -p neuron --features "${{ matrix.cargo_features }}"
|
|
env:
|
|
CUDA_COMPUTE_CAP: ${{ matrix.compute_cap }}
|
|
CARGO_BUILD_JOBS: ${{ matrix.build_jobs }}
|
|
NVCC_THREADS: ${{ matrix.nvcc_threads }}
|
|
|
|
- name: Stage binary
|
|
run: |
|
|
mkdir --parents artifacts
|
|
cp target/release/neuron artifacts/neuron-${{ matrix.flavour }}
|
|
file "artifacts/neuron-${{ matrix.flavour }}"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: neuron-${{ matrix.flavour }}-fc43
|
|
path: artifacts/neuron-${{ matrix.flavour }}
|
|
retention-days: 1
|
|
|
|
package-cortex:
|
|
name: Package cortex RPM
|
|
timeout-minutes: 20
|
|
needs: [prepare, build-cortex]
|
|
runs-on: rpm
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- uses: actions/download-artifact@v3
|
|
with:
|
|
name: cortex-fc43
|
|
path: artifacts/
|
|
|
|
- name: Build RPM
|
|
run: |
|
|
set -eux
|
|
rm -f ~/.rpmmacros
|
|
rpmdev-setuptree
|
|
cp artifacts/cortex ~/rpmbuild/SOURCES/
|
|
cp data/cortex.service ~/rpmbuild/SOURCES/
|
|
cp data/cortex-sysusers.conf ~/rpmbuild/SOURCES/
|
|
cp data/cortex-firewalld.xml ~/rpmbuild/SOURCES/
|
|
cp cortex.example.toml ~/rpmbuild/SOURCES/
|
|
cp models.example.toml ~/rpmbuild/SOURCES/
|
|
cp LICENSE ~/rpmbuild/SOURCES/
|
|
rpmbuild -bb rpm/cortex-prerelease.spec \
|
|
--define "cortex_version ${{ needs.prepare.outputs.version }}" \
|
|
--define "cortex_prerelease ${{ needs.prepare.outputs.release }}" \
|
|
--undefine dist \
|
|
--define "dist .fc43"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: rpm-cortex-fc43
|
|
path: ~/rpmbuild/RPMS/x86_64/*.rpm
|
|
retention-days: 7
|
|
|
|
package-bench:
|
|
name: Package helexa-bench RPM
|
|
timeout-minutes: 20
|
|
needs: [prepare, build-bench]
|
|
runs-on: rpm
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- uses: actions/download-artifact@v3
|
|
with:
|
|
name: bench-fc43
|
|
path: artifacts/
|
|
|
|
- name: Build RPM
|
|
run: |
|
|
set -eux
|
|
rm -f ~/.rpmmacros
|
|
rpmdev-setuptree
|
|
cp artifacts/helexa-bench ~/rpmbuild/SOURCES/
|
|
cp data/helexa-bench.service ~/rpmbuild/SOURCES/
|
|
cp data/helexa-bench-sysusers.conf ~/rpmbuild/SOURCES/
|
|
cp data/helexa-bench-firewalld.xml ~/rpmbuild/SOURCES/
|
|
cp helexa-bench.example.toml ~/rpmbuild/SOURCES/
|
|
cp LICENSE ~/rpmbuild/SOURCES/
|
|
rpmbuild -bb rpm/helexa-bench-prerelease.spec \
|
|
--define "bench_version ${{ needs.prepare.outputs.version }}" \
|
|
--define "bench_prerelease ${{ needs.prepare.outputs.release }}" \
|
|
--undefine dist \
|
|
--define "dist .fc43"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: rpm-bench-fc43
|
|
path: ~/rpmbuild/RPMS/x86_64/*.rpm
|
|
retention-days: 7
|
|
|
|
package-upstream:
|
|
name: Package helexa-upstream RPM
|
|
timeout-minutes: 20
|
|
needs: [prepare, build-upstream]
|
|
runs-on: rpm
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- uses: actions/download-artifact@v3
|
|
with:
|
|
name: upstream-fc43
|
|
path: artifacts/
|
|
|
|
- name: Build RPM
|
|
run: |
|
|
set -eux
|
|
rm -f ~/.rpmmacros
|
|
rpmdev-setuptree
|
|
cp artifacts/helexa-upstream ~/rpmbuild/SOURCES/
|
|
cp data/helexa-upstream.service ~/rpmbuild/SOURCES/
|
|
cp data/helexa-upstream-sysusers.conf ~/rpmbuild/SOURCES/
|
|
cp data/helexa-upstream-firewalld.xml ~/rpmbuild/SOURCES/
|
|
cp helexa-upstream.example.toml ~/rpmbuild/SOURCES/
|
|
cp LICENSE ~/rpmbuild/SOURCES/
|
|
rpmbuild -bb rpm/helexa-upstream-prerelease.spec \
|
|
--define "upstream_version ${{ needs.prepare.outputs.version }}" \
|
|
--define "upstream_prerelease ${{ needs.prepare.outputs.release }}" \
|
|
--undefine dist \
|
|
--define "dist .fc43"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: rpm-upstream-fc43
|
|
path: ~/rpmbuild/RPMS/x86_64/*.rpm
|
|
retention-days: 7
|
|
|
|
package-neuron:
|
|
name: Package helexa-neuron-${{ matrix.flavour }} RPM
|
|
timeout-minutes: 20
|
|
needs: [prepare, build-neuron]
|
|
runs-on: rpm
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- flavour: ampere
|
|
- flavour: ada
|
|
- flavour: blackwell
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- uses: actions/download-artifact@v3
|
|
with:
|
|
name: neuron-${{ matrix.flavour }}-fc43
|
|
path: artifacts/
|
|
|
|
- name: Build RPM
|
|
run: |
|
|
set -eux
|
|
rm -f ~/.rpmmacros
|
|
rpmdev-setuptree
|
|
cp artifacts/neuron-${{ matrix.flavour }} ~/rpmbuild/SOURCES/
|
|
cp data/neuron.service ~/rpmbuild/SOURCES/
|
|
cp data/neuron-sysusers.conf ~/rpmbuild/SOURCES/
|
|
cp data/neuron-firewalld.xml ~/rpmbuild/SOURCES/
|
|
cp neuron.example.toml ~/rpmbuild/SOURCES/
|
|
cp LICENSE ~/rpmbuild/SOURCES/
|
|
rpmbuild -bb rpm/helexa-neuron-prerelease.spec \
|
|
--define "neuron_version ${{ needs.prepare.outputs.version }}" \
|
|
--define "neuron_flavour ${{ matrix.flavour }}" \
|
|
--define "neuron_prerelease ${{ needs.prepare.outputs.release }}" \
|
|
--undefine dist \
|
|
--define "dist .fc43"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: rpm-neuron-${{ matrix.flavour }}-fc43
|
|
path: ~/rpmbuild/RPMS/x86_64/*.rpm
|
|
retention-days: 7
|
|
|
|
publish:
|
|
name: Publish to rpm.lair.cafe (unstable)
|
|
timeout-minutes: 25
|
|
needs: [lint, test, package-cortex, package-neuron, package-bench, package-upstream]
|
|
# Runs when at least one package was built and nothing failed.
|
|
# lint/test may be skipped (docs-only refs never get here because
|
|
# no packages build), but a real failure in any blocks the
|
|
# fleet from receiving the RPMs.
|
|
if: >-
|
|
${{
|
|
!cancelled()
|
|
&& (needs.lint.result == 'success' || needs.lint.result == 'skipped')
|
|
&& (needs.test.result == 'success' || needs.test.result == 'skipped')
|
|
&& (needs.package-cortex.result == 'success' || needs.package-neuron.result == 'success' || needs.package-bench.result == 'success' || needs.package-upstream.result == 'success')
|
|
&& needs.package-cortex.result != 'failure'
|
|
&& needs.package-neuron.result != 'failure'
|
|
&& needs.package-bench.result != 'failure'
|
|
&& needs.package-upstream.result != 'failure'
|
|
}}
|
|
runs-on: rpm
|
|
concurrency:
|
|
group: rpm-publish
|
|
cancel-in-progress: false
|
|
env:
|
|
RPM_REPO_HOST: oolon.kosherinata.internal
|
|
FEDORA_VERSION: "43"
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- name: Download all built RPMs
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
path: rpms/
|
|
pattern: rpm-*-fc43
|
|
|
|
- name: Flatten RPM artifacts
|
|
run: |
|
|
set -eux
|
|
find rpms/ -name '*.rpm' -exec mv --target-directory=rpms/ {} +
|
|
find rpms/ -mindepth 1 -type d -empty -delete
|
|
ls -la rpms/
|
|
|
|
- name: Check for sequoia-sq
|
|
run: |
|
|
if ! command -v sq &> /dev/null; then
|
|
echo "ERROR: sequoia-sq is not installed. Install with: sudo dnf install sequoia-sq"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Import signing key
|
|
env:
|
|
# Pass secrets via env so values stay out of the rendered shell
|
|
# script (which Gitea includes in step logs). Template
|
|
# expansion of ${{ secrets.X }} inside `run:` writes the literal
|
|
# value into the script and depends on Gitea's log masker to
|
|
# scrub it — fragile for multi-line keys.
|
|
RPM_SIGNING_KEY: ${{ secrets.RPM_SIGNING_KEY }}
|
|
RPM_SIGNING_KEY_ID: ${{ secrets.RPM_SIGNING_KEY_ID }}
|
|
run: |
|
|
echo "$RPM_SIGNING_KEY" | gpg --batch --import
|
|
fpr=$(gpg --batch --with-colons --list-keys "$RPM_SIGNING_KEY_ID" | awk -F: '/^fpr:/ { print $10; exit }')
|
|
echo "${fpr}:6:" | gpg --batch --import-ownertrust
|
|
sed "s/@GPG_NAME@/$RPM_SIGNING_KEY_ID/" rpm/rpmmacros > ~/.rpmmacros
|
|
|
|
- name: Sign RPMs
|
|
run: |
|
|
set -eux
|
|
for rpm in rpms/*.rpm; do
|
|
echo "signing ${rpm}..."
|
|
rpm --addsign "${rpm}"
|
|
done
|
|
|
|
- name: Set up SSH for rsync
|
|
run: |
|
|
install --directory --mode 700 ~/.ssh
|
|
echo "${RSYNC_SSH_KEY}" | install --mode 600 /dev/stdin ~/.ssh/id_ed25519
|
|
env:
|
|
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
|
|
|
|
- name: Test SSH connectivity
|
|
run: |
|
|
ssh -o StrictHostKeyChecking=accept-new "gitea_ci@${RPM_REPO_HOST}" exit
|
|
|
|
- name: Ensure unstable repo directory exists
|
|
run: |
|
|
ssh "gitea_ci@${RPM_REPO_HOST}" \
|
|
"mkdir --parents /var/www/rpm/fedora/${FEDORA_VERSION}/x86_64/unstable"
|
|
|
|
- name: Sync RPMs to unstable repo
|
|
run: |
|
|
rsync \
|
|
--archive \
|
|
--verbose \
|
|
--chmod D755,F644 \
|
|
rpms/*.rpm \
|
|
"gitea_ci@${RPM_REPO_HOST}:/var/www/rpm/fedora/${FEDORA_VERSION}/x86_64/unstable/"
|
|
|
|
- name: Update unstable repo metadata
|
|
run: |
|
|
ssh "gitea_ci@${RPM_REPO_HOST}" \
|
|
"cd /var/www/rpm/fedora/${FEDORA_VERSION}/x86_64/unstable && createrepo_c --update ."
|
|
|
|
- name: Generate packages.json manifest
|
|
run: |
|
|
scp script/generate-packages-json.py "gitea_ci@${RPM_REPO_HOST}:/tmp/"
|
|
ssh "gitea_ci@${RPM_REPO_HOST}" \
|
|
"python3 /tmp/generate-packages-json.py \
|
|
--repodata-dir /var/www/rpm/fedora/${FEDORA_VERSION}/x86_64/unstable/repodata \
|
|
--output /var/www/rpm/fedora/${FEDORA_VERSION}/x86_64/unstable/packages.json \
|
|
--base-url https://rpm.lair.cafe/fedora/${FEDORA_VERSION}/x86_64/unstable"
|