name: Shared soft3 candidate
on:
workflow_call:
inputs:
component:
required: true
type: string
manager_ref:
required: true
type: string
candidate:
default: ''
type: string
cut:
default: false
type: boolean
permissions:
contents: read
jobs:
snapshot:
runs-on: ubuntu-24.04
outputs:
candidate: ${{ steps.capture.outputs.candidate }}
matrix: ${{ steps.capture.outputs.matrix }}
defaults:
run:
shell: nu {0}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
repository: cyberia-to/soft3
ref: ${{ inputs.manager_ref }}
persist-credentials: false
- uses: hustcer/setup-nu@9215c80adb545a85c419d5085b76eb6d082f49e7
with:
version: '0.112.2'
- name: Capture default branches and immutable inputs
id: capture
env:
GH_TOKEN: ${{ github.token }}
COMPONENT: ${{ inputs.component }}
CANDIDATE: ${{ inputs.candidate }}
run: |
let name = if ($env.CANDIDATE | is-empty) { $"candidate-(date now | format date '%Y%m%d').1" } else { $env.CANDIDATE }
python3 release/train.py snapshot --manager . --component $env.COMPONENT --candidate $name --output snapshot
if $env.LAST_EXIT_CODE != 0 { error make {msg: "source snapshot failed"} }
let rows = [
{runner: ubuntu-24.04, target: stack}
{runner: ubuntu-24.04, target: x86_64-unknown-linux-gnu}
{runner: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu}
{runner: macos-14, target: aarch64-apple-darwin}
{runner: macos-15-intel, target: x86_64-apple-darwin}
]
let rows = if $env.COMPONENT == cyb { $rows | append {runner: ubuntu-24.04, target: aarch64-linux-android} } else { $rows }
$"candidate=($name)\n" | save --append $env.GITHUB_OUTPUT
$"matrix=({include: $rows} | to json --raw)\n" | save --append $env.GITHUB_OUTPUT
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: source-snapshot
path: snapshot/sources.json
if-no-files-found: error
qualify:
needs: snapshot
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.snapshot.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
defaults:
run:
shell: nu {0}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
repository: cyberia-to/soft3
ref: ${{ inputs.manager_ref }}
persist-credentials: false
- uses: hustcer/setup-nu@9215c80adb545a85c419d5085b76eb6d082f49e7
with:
version: '0.112.2'
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: source-snapshot
path: snapshot
- name: Install Rust
run: |
rustup toolchain install 1.95.0 --profile minimal --component rustfmt
if $env.LAST_EXIT_CODE != 0 { error make {msg: "Rust installation failed"} }
rustup default 1.95.0
- name: Desktop Linux libraries
if: inputs.component == 'cyb' && startsWith(matrix.runner, 'ubuntu') && matrix.target != 'stack'
run: |
sudo apt-get update
if $env.LAST_EXIT_CODE != 0 { error make {msg: "apt update failed"} }
sudo apt-get install -y libasound2-dev libudev-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libwayland-dev libxkbcommon-dev libgtk-3-dev libayatana-appindicator3-dev mesa-vulkan-drivers xvfb
- name: Android toolchain
if: matrix.target == 'aarch64-linux-android'
run: |
rustup target add aarch64-linux-android
if $env.LAST_EXIT_CODE != 0 { error make {msg: "Android Rust target unavailable"} }
sdkmanager 'platforms;android-35' 'build-tools;35.0.0' 'ndk;27.2.12479018'
- name: Qualify stack and product
env:
TARGET: ${{ matrix.target }}
RUSTUP_TOOLCHAIN: '1.95.0'
CARGO_BUILD_JOBS: '2'
PYTHONUNBUFFERED: '1'
run: |
let args = if $env.TARGET == stack { [--stack] } else { [] }
python3 release/train.py build --snapshot snapshot/sources.json --checkout ($env.RUNNER_TEMP | path join sources) --output ($env.RUNNER_TEMP | path join receipt) --target $env.TARGET ...$args
if $env.LAST_EXIT_CODE != 0 { error make {msg: "qualification engine failed; receipts retained"} }
- name: Retain every result
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: candidate-${{ matrix.target }}
path: |
${{ runner.temp }}/*.tar.gz
${{ runner.temp }}/receipt/
if-no-files-found: warn
- name: Report the verdict
run: |
let receipt = (open ($env.RUNNER_TEMP | path join receipt release-validation.json))
if $receipt.result != green { error make {msg: "RED candidate gate; see retained release-validation.json"} }
collect:
if: always() && needs.snapshot.result == 'success'
needs: [snapshot, qualify]
runs-on: ubuntu-24.04
permissions:
contents: write
defaults:
run:
shell: nu {0}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
repository: cyberia-to/soft3
ref: ${{ inputs.manager_ref }}
persist-credentials: false
- uses: hustcer/setup-nu@9215c80adb545a85c419d5085b76eb6d082f49e7
with:
version: '0.112.2'
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: source-snapshot
path: snapshot
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
continue-on-error: true
with:
pattern: candidate-*
path: artifacts
- name: Assemble green and red evidence
run: python3 release/train.py collect --snapshot snapshot/sources.json --artifacts artifacts --output assembled
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: assembled-candidate
path: assembled/
if-no-files-found: error
retention-days: 90
- name: Create draft candidate
if: inputs.cut && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
env:
GH_TOKEN: ${{ github.token }}
run: python3 release/train.py draft --output assembled