All checks were successful
build-prerelease / Resolve version stamps + change detection (push) Successful in 30s
build-prerelease / Lint (fmt + clippy) (push) Successful in 2m23s
build-prerelease / Build cortex binary (push) Successful in 2m29s
build-prerelease / Build helexa-bench binary (push) Successful in 2m34s
build-prerelease / Test (push) Successful in 4m33s
build-prerelease / Build neuron-blackwell (push) Successful in 1m31s
build-prerelease / Build neuron-ada (push) Successful in 2m13s
build-prerelease / Build neuron-ampere (push) Successful in 2m50s
build-prerelease / Package helexa-bench RPM (push) Successful in 1m17s
build-prerelease / Package cortex RPM (push) Successful in 1m27s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 1m38s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 1m42s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 1m44s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 55s
A neuron-blackwell build hung ~90 min (siblings finished in 2) and there
was no job timeout to kill it, so it sat burning a runner. Root cause of
the hang: the inline retry loop treated every failure identically and, on
its final attempt, rebuilt with sccache disabled. When the real failure
is a rustc SIGSEGV or an OOM-kill, an uncached rebuild does *more* work
under the same memory pressure — turning one transient compiler crash
into a wedged job.
Two fixes:
1. timeout-minutes on every job in build-prerelease.yml and ci.yml
(builds 25, neuron CUDA build/cuda-check 35, packaging 20, COPR 60,
fast jobs 10-15). A hang now dies in minutes, not hours.
2. New script/ci-cargo-escalate.sh replaces the five (prerelease) + three
(ci) inline escalation loops. It classifies the failure:
- signal death (exit >=128, or cargo reporting `signal: N`/SIGSEGV/
SIGKILL) → compiler crash, NOT an sccache fault: keep the cache,
one warm retry, then fail fast. Never escalate to uncached.
- sccache fault (recognisable sccache error) → restart the server,
retry, then one final uncached attempt.
- deterministic compile/test error → fail fast (no wasteful retry).
It also folds in the CUDA-image sccache probe the neuron/cuda-check
jobs did inline. Classification verified locally against success,
plain failure, exit-139, and the cargo-wrapped `signal: 11` form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
619 lines
24 KiB
YAML
619 lines
24 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 }}
|
|
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.
|
|
COMMIT_TIMESTAMP=$(git log -1 --format=%cd --date=format:%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
|
|
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$'
|
|
# 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)
|
|
BUILD_CORTEX=$(decide "$CORTEX_BASE" "$CORTEX_RE")
|
|
BUILD_NEURON=$(decide "$NEURON_BASE" "$NEURON_RE")
|
|
BUILD_BENCH=$(decide "$BENCH_BASE" "$BENCH_RE")
|
|
if [ "$BUILD_CORTEX" = "true" ] || [ "$BUILD_NEURON" = "true" ] || [ "$BUILD_BENCH" = "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 "check_rust=${CHECK_RUST}" >> "$GITHUB_OUTPUT"
|
|
echo "### change detection: build_cortex=${BUILD_CORTEX} build_neuron=${BUILD_NEURON} build_bench=${BUILD_BENCH} 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-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-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]
|
|
# 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-cortex.result != 'failure'
|
|
&& needs.package-neuron.result != 'failure'
|
|
&& needs.package-bench.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"
|