name: CI # Pushes to main are deliberately excluded: build-prerelease.yml runs # its own lint/test jobs there (gating publish), and running both # workflows on the same push made them queue against each other on the # same runner labels — ~12 minutes of added latency per deploy. Feature # branches, PRs to main, and release tags keep the full gate here. on: push: branches-ignore: [main] tags: ["v*"] pull_request: branches: [main] # Coalesce same-ref pushes; a newer push supersedes the in-flight run. # (The old shared `cortex-runner-pool` group with build-prerelease.yml # is gone — the workflows no longer trigger on the same refs, and # ephemeral one-VM-per-job runners removed the shared-workspace race # that group existed to serialize.) concurrency: group: ci-${{ github.ref }} cancel-in-progress: true env: CARGO_INCREMENTAL: "0" 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 }} # fmt, clippy, and test all run in parallel on the same `rust` runner # and would otherwise share /root/.cache/act//hostexecutor/target/, # racing each other's cargo temp files (.tmpXXXXXX) and failing builds # mid-compile. Give each job its own target directory so the invocations # don't collide. sccache still backs the actual rustc cache, so the # rebuild penalty is small. CARGO_TARGET_DIR: target-${{ github.job }} jobs: fmt: name: Format timeout-minutes: 15 runs-on: rust steps: - uses: actions/checkout@v4 - run: cargo fmt --check --all clippy: name: Clippy timeout-minutes: 25 runs-on: rust steps: - uses: actions/checkout@v4 # Failure-aware sccache escalation lives in the shared script (kept # in sync with build-prerelease.yml): a signal death (rustc SIGSEGV # / OOM-kill) keeps the cache and fails fast instead of an uncached # rebuild; only a real sccache fault drops the cache. - name: Clippy (sccache escalation) run: script/ci-cargo-escalate.sh cargo clippy --workspace -- -D warnings test: name: Test timeout-minutes: 25 runs-on: rust steps: - uses: actions/checkout@v4 # See script/ci-cargo-escalate.sh for the escalation rationale. - name: Test (sccache escalation) run: script/ci-cargo-escalate.sh cargo test --workspace # Type-check the CUDA-only code path. Borrow-check-only — we # never run the tests here (the runner has no GPU). This catches # the category of bug where a refactor compiles fine under the # default feature set (which is what the `clippy` and `test` jobs # exercise) but fails inside a `#[cfg(feature = "cuda")]` block. # `runs-on: cuda-13.0` selects the runner that ships nvcc / # cudarc's build prerequisites. The generic `rust` and `rpm` # runners don't have them (the previous label `rpm` was tried # first and tripped cudarc's `nvcc --version` build script — # see commit history). cuda-check: name: CUDA type-check timeout-minutes: 35 runs-on: cuda-13.0 # The workflow-level env sets `RUSTC_WRAPPER: sccache` # unconditionally, which hard-fails cargo if the CUDA image # doesn't ship sccache. Clear it at job level; the "Enable # sccache when available" step opts back in only after probing # for the binary. SCCACHE_*/AWS creds stay set — harmless when # the wrapper is off, required when it's on. env: RUSTC_WRAPPER: "" # candle-kernels' build script falls back to `nvidia-smi` for # compute-cap detection when this is unset — and the GPU-less # builder image doesn't ship nvidia-smi. Any valid cap works for # a borrow-check; the real per-flavour caps live in # build-prerelease.yml's matrix. CUDA_COMPUTE_CAP: "86" steps: - uses: actions/checkout@v4 # sccache probing + failure classification lives in the shared # script (see build-prerelease.yml's neuron build for the same # pattern). It probes for sccache and, on a rustc SIGSEGV / OOM, # keeps the cache and fails fast rather than rebuilding uncached. - name: cargo check --features cuda (sccache escalation) run: | # act launches the step shell without /etc/profile, so the # gitea_runner user's inherited PATH lacks /usr/local/cuda-13.0/bin. # cudarc's build.rs shells out to `nvcc --version` (the neuron # crate enables cuda-version-from-build-system) and panics with # ENOENT if nvcc isn't resolvable — keep this export in sync # with build-prerelease.yml. export PATH="/usr/local/cuda-13.0/bin:${PATH}" export LD_LIBRARY_PATH="/usr/local/cuda-13.0/targets/x86_64-linux/lib:/usr/local/cuda-13.0/lib64:${LD_LIBRARY_PATH:-}" export LIBRARY_PATH="/usr/local/cuda-13.0/targets/x86_64-linux/lib:/usr/local/cuda-13.0/lib64:${LIBRARY_PATH:-}" script/ci-cargo-escalate.sh cargo check -p neuron --features cuda --all-targets srpm-cortex: name: Build cortex SRPM timeout-minutes: 25 runs-on: rpm needs: [fmt, clippy, test, cuda-check] if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Determine version id: version run: | VERSION="${GITHUB_REF#refs/tags/v}" echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT" - name: Stamp version run: | VERSION="${{ steps.version.outputs.VERSION }}" sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml sed -i "s/^Version:.*/Version: ${VERSION}/" cortex.spec - name: Generate changelog entry uses: https://git.lair.cafe/actions/rpm-changelog@v1 with: spec: cortex.spec version: ${{ steps.version.outputs.VERSION }} - name: Generate source tarball run: | set -ex VERSION="${{ steps.version.outputs.VERSION }}" tar czf /tmp/cortex-${VERSION}.tar.gz \ --transform "s,^\.,cortex-${VERSION}," \ --exclude='./target' \ --exclude='./.git' \ --exclude='*.tar.gz' \ --exclude='*.src.rpm' \ . mv /tmp/cortex-${VERSION}.tar.gz . - name: Vendor Rust dependencies run: | VERSION="${{ steps.version.outputs.VERSION }}" cargo vendor vendor/ tar czf cortex-${VERSION}-vendor.tar.gz vendor/ rm -rf vendor/ - name: Build SRPM run: | rpmbuild -bs cortex.spec \ --define "_sourcedir $(pwd)" \ --define "_srcrpmdir $(pwd)" - name: Upload SRPM artifact uses: actions/upload-artifact@v3 with: name: srpm-cortex path: "*.src.rpm" srpm-neuron: name: Build neuron SRPM timeout-minutes: 25 runs-on: rpm needs: [fmt, clippy, test, cuda-check] if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Determine version id: version run: | VERSION="${GITHUB_REF#refs/tags/v}" echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT" - name: Stamp version run: | VERSION="${{ steps.version.outputs.VERSION }}" sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml sed -i "s/^Version:.*/Version: ${VERSION}/" helexa-neuron.spec - name: Generate changelog entry uses: https://git.lair.cafe/actions/rpm-changelog@v1 with: spec: helexa-neuron.spec version: ${{ steps.version.outputs.VERSION }} - name: Generate source tarball run: | set -ex VERSION="${{ steps.version.outputs.VERSION }}" tar czf /tmp/helexa-neuron-${VERSION}.tar.gz \ --transform "s,^\.,helexa-neuron-${VERSION}," \ --exclude='./target' \ --exclude='./.git' \ --exclude='*.tar.gz' \ --exclude='*.src.rpm' \ . mv /tmp/helexa-neuron-${VERSION}.tar.gz . - name: Vendor Rust dependencies run: | VERSION="${{ steps.version.outputs.VERSION }}" cargo vendor vendor/ tar czf helexa-neuron-${VERSION}-vendor.tar.gz vendor/ rm -rf vendor/ - name: Build SRPM run: | rpmbuild -bs helexa-neuron.spec \ --define "_sourcedir $(pwd)" \ --define "_srcrpmdir $(pwd)" - name: Upload SRPM artifact uses: actions/upload-artifact@v3 with: name: srpm-neuron path: "*.src.rpm" copr-cortex: name: Publish cortex to COPR timeout-minutes: 60 runs-on: fedora-43 needs: srpm-cortex steps: - name: Download SRPM uses: actions/download-artifact@v3 with: name: srpm-cortex - name: Publish to COPR uses: https://git.lair.cafe/actions/copr-publish@v1 with: project: helexa/helexa srpm: "*.src.rpm" copr-config: ${{ secrets.COPR_CONFIG }} copr-neuron: name: Publish neuron to COPR timeout-minutes: 60 runs-on: fedora-43 needs: srpm-neuron steps: - name: Download SRPM uses: actions/download-artifact@v3 with: name: srpm-neuron - name: Publish to COPR uses: https://git.lair.cafe/actions/copr-publish@v1 with: project: helexa/helexa srpm: "*.src.rpm" copr-config: ${{ secrets.COPR_CONFIG }} bump-version: name: Bump version in source timeout-minutes: 15 runs-on: rust needs: [copr-cortex, copr-neuron] steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Determine version id: version run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT" - name: Stamp version run: | VERSION="${{ steps.version.outputs.VERSION }}" sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml sed -i "s/^Version:.*/Version: ${VERSION}/" cortex.spec sed -i "s/^Version:.*/Version: ${VERSION}/" helexa-neuron.spec cargo check --workspace 2>/dev/null || true - name: Generate cortex changelog entry uses: https://git.lair.cafe/actions/rpm-changelog@v1 with: spec: cortex.spec version: ${{ steps.version.outputs.VERSION }} - name: Generate helexa-neuron changelog entry uses: https://git.lair.cafe/actions/rpm-changelog@v1 with: spec: helexa-neuron.spec version: ${{ steps.version.outputs.VERSION }} - name: Commit and push env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | VERSION="${{ steps.version.outputs.VERSION }}" git config user.name "Gitea Actions" git config user.email "actions@git.lair.cafe" git add Cargo.toml Cargo.lock cortex.spec helexa-neuron.spec if git diff --cached --quiet; then echo "Nothing to commit for ${VERSION}" else git commit -m "chore: bump version to ${VERSION}" git remote set-url origin "https://gitea-actions:${GITEA_TOKEN}@git.lair.cafe/${{ github.repository }}.git" git push origin HEAD:main fi