Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 7m27s
Build Packages / build:windows:nocuda (push) Successful in 15m41s
Build Packages / build:rugnux:windows (push) Successful in 16m37s
Build Packages / build:windows:cuda (push) Successful in 19m33s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 16m47s
Build Packages / build:viewer-tgz:cpu (push) Successful in 19m39s
Build Packages / build:viewer-tgz:cuda (push) Successful in 21m23s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 22m3s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 26m46s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 28m19s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 20m58s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 21m29s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 26m25s
Build Packages / build:rpm (rocky9) (push) Successful in 22m46s
Build Packages / build:rpm (rocky8) (push) Successful in 28m2s
Build Packages / Generate python client (push) Successful in 42s
Build Packages / Build documentation (push) Successful in 1m13s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (ubuntu2404) (push) Successful in 23m33s
Build Packages / XDS test (durin plugin) (push) Successful in 11m44s
Build Packages / DIALS test (push) Successful in 25m14s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 27m29s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 11m9s
Build Packages / XDS test (neggia plugin) (push) Successful in 9m44s
Build Packages / Unit tests (push) Successful in 1h25m11s
The rugnux tarball built and packaged correctly; the step that verifies it did not. Two shell mistakes, both mine, both of the same family. `! find /tmp/rgx -name 'libcufft*'` can never pass: find exits 0 whether or not it matched anything, so the negation always fails. That is what broke the x86_64 job after a successful build. The aarch64 checks were worse, in the way that matters. Piping a large producer into `grep -q` kills the producer with SIGPIPE as soon as grep exits on its first match, and `set -o pipefail` promotes that to the pipeline's status: on `cuobjdump --list-elf | grep -q sm_90` over a 225 MB binary it fails a check that should pass, and on `! ... | grep -q "x86-64"` the leading `!` inverts it into a PASS -- so an x86 file leaking into an aarch64 tarball, the exact thing that check exists to catch, would have been reported as clean. Both now capture each producer's output to a file and grep the file, with explicit if/exit rather than exit-status negation. Verified by running the checks verbatim against the real artifacts rather than only reading them: both tarballs pass, and the checks were confirmed to FAIL when they should -- an x86_64 file planted in the extracted tree is caught, and an architecture absent from the fatbin is reported missing. The toolchain file now comes from the checkout rather than /opt/cross in the image. It describes how to build this source, so it belongs with the source: baked into the image, the Eigen fix in the previous commit could not reach CI without rebuilding and re-pushing the image, and appeared to have no effect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQjneRUssfhi1k9rq8Ts3h
705 lines
31 KiB
YAML
705 lines
31 KiB
YAML
name: Build Packages
|
|
|
|
env:
|
|
# Architecture and link-time optimisation for every Linux configure. Both are deliberately absent
|
|
# from CMakeLists.txt so a site can pick its own (x86-64-v4 on an AVX-512 cluster, -march=native,
|
|
# or the plain baseline); CI pins what it ships.
|
|
#
|
|
# -flto=auto needs GCC >= 10 and both CI images put gcc-toolset-13 on PATH. Measured on rugnux
|
|
# against an otherwise identical build: 7-10% fewer retired instructions and a 9% smaller binary,
|
|
# but only ~1.5% off the wall clock, because the offline pipeline is GPU- and I/O-bound rather
|
|
# than CPU-instruction-bound. It costs about 3x on an INCREMENTAL rebuild (one file plus link,
|
|
# 9.8 s -> 30.1 s), which is why it belongs here and not in CMakeLists: CI always builds from
|
|
# scratch and ships the result, so it pays the link once and a developer never pays it at all.
|
|
#
|
|
# Not CMAKE_INTERPROCEDURAL_OPTIMIZATION, which is the tidier spelling and would cover MSVC too:
|
|
# built and linked fine (CUDA included, and it does not reach .cu either way), but measured 3%
|
|
# MORE retired instructions than -flto=auto - 396.9 G vs 384.9 G over three runs each against a
|
|
# 0.45% run-to-run spread, so the gap is real. It also leaves two dependencies (FFTW, libzmq) out
|
|
# of LTO that the flag reaches, and it did not switch CMAKE_AR to gcc-ar here, so the one
|
|
# correctness argument for it did not materialise either.
|
|
LINUX_CMAKE_FLAGS: '-DCMAKE_CXX_FLAGS="-march=x86-64-v3 -flto=auto" -DCMAKE_C_FLAGS="-march=x86-64-v3 -flto=auto"'
|
|
# MSVC has no spelling for the x86-64-v2 level; /arch:AVX is the nearest and implies SSE4.1/4.2,
|
|
# which is what matters here - without it Eigen has no vectorised round and falls back to a libm
|
|
# call per element. AVX is Sandy Bridge (2011) and up, a safe floor for a desktop viewer.
|
|
MSVC_ARCH_CMAKE_FLAGS: '-DCMAKE_CXX_FLAGS="/arch:AVX" -DCMAKE_C_FLAGS="/arch:AVX"'
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- '**'
|
|
tags:
|
|
- '**'
|
|
pull_request:
|
|
workflow_dispatch:
|
|
inputs:
|
|
create_release:
|
|
type: boolean
|
|
description: 'Create release'
|
|
required: false
|
|
default: false
|
|
|
|
jobs:
|
|
unit-tests:
|
|
name: Unit tests
|
|
runs-on: jfjoch_rocky8
|
|
# Not on a tag, on purpose. Releasing is a two-step dance because artifacts do not carry between
|
|
# runs: the manual dispatch below runs the whole CI (this job included) and then creates the tag,
|
|
# and the tag push re-runs the workflow only to rebuild and upload. The tag run has therefore
|
|
# already been tested by the dispatch run it came from.
|
|
if: github.ref_type != 'tag'
|
|
container:
|
|
image: gitea.psi.ch/leonarski_f/jfjoch_rocky8:2511
|
|
options: --gpus all
|
|
timeout-minutes: 90
|
|
env:
|
|
CTEST_OUTPUT_ON_FAILURE: '1'
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Configure auth and fetch LFS
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
git lfs install --local
|
|
AUTH=$(git config --local http.${{ github.server_url }}/.extraheader)
|
|
git config --local --unset http.${{ github.server_url }}/.extraheader
|
|
git config --local http.${{ github.server_url }}/${{ github.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH"
|
|
git lfs pull
|
|
- name: Build tests
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ${{ env.LINUX_CMAKE_FLAGS }} ..
|
|
ninja -j48 jfjoch_test jfjoch_hdf5_test jfjoch_hdf5_enospc_test enospc_shim
|
|
- name: Run unit tests
|
|
shell: bash
|
|
run: |
|
|
cd build/tests
|
|
./jfjoch_test
|
|
- name: HDF5 ENOSPC test
|
|
shell: bash
|
|
run: |
|
|
cd build/tests
|
|
LD_PRELOAD=./enospc_shim.so ./jfjoch_hdf5_enospc_test HDF5File_enospc
|
|
LD_PRELOAD=./enospc_shim.so ./jfjoch_hdf5_enospc_test FileWriter_enospc
|
|
- name: Run hdf5 test
|
|
shell: bash
|
|
run: |
|
|
cd build/tools
|
|
./jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5
|
|
build-windows:
|
|
name: build:windows:${{ matrix.variant }}
|
|
runs-on: windows-11-cuda-qt
|
|
timeout-minutes: 120
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- variant: cuda
|
|
use_cuda: 'ON'
|
|
- variant: nocuda
|
|
use_cuda: 'OFF'
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Configure viewer build
|
|
shell: cmd
|
|
run: |
|
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VSPATH=%%i"
|
|
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat"
|
|
cmake -G Ninja -B build -DJFJOCH_USE_CUDA=${{ matrix.use_cuda }} -DCMAKE_BUILD_TYPE=Release ${{ env.MSVC_ARCH_CMAKE_FLAGS }} -DCMAKE_PREFIX_PATH="C:/deps;C:/Qt/6.11.1/msvc2022_64"
|
|
- name: Build viewer
|
|
shell: cmd
|
|
run: |
|
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VSPATH=%%i"
|
|
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat"
|
|
cmake --build build
|
|
- name: Build installer (NSIS)
|
|
shell: cmd
|
|
run: |
|
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VSPATH=%%i"
|
|
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat"
|
|
cd build
|
|
cpack
|
|
- name: Upload installer to release
|
|
if: github.ref_type == 'tag'
|
|
shell: powershell
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
# NSIS installer named jfjoch-viewer-<version>-win64-{cuda<major>|cpu}.exe (see CMakeLists.txt).
|
|
$files = Get-ChildItem -Path build -Filter 'jfjoch-viewer-*-win64-*.exe'
|
|
if ($files.Count -eq 0) { throw 'No Windows installer found in build/' }
|
|
foreach ($file in $files) {
|
|
python gitea_upload_file.py $file.FullName
|
|
if ($LASTEXITCODE -ne 0) { throw "Upload failed for $($file.Name)" }
|
|
}
|
|
build-viewer-tgz:
|
|
name: build:viewer-tgz:${{ matrix.variant }}
|
|
runs-on: jfjoch_rocky8
|
|
timeout-minutes: 120
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- variant: cuda
|
|
use_cuda: 'ON'
|
|
- variant: cpu
|
|
use_cuda: 'OFF'
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Clean previous build
|
|
shell: bash
|
|
run: rm -rf build # self-hosted runners reuse the workspace volume; a stale build dir
|
|
# can leave the viewer linking a system libcurl instead of the vendored one
|
|
- name: Configure viewer build
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DJFJOCH_VIEWER_ONLY=ON -DJFJOCH_USE_CUDA=${{ matrix.use_cuda }} ${{ env.LINUX_CMAKE_FLAGS }} -DCMAKE_BUILD_TYPE=Release ..
|
|
- name: Build viewer tgz
|
|
shell: bash
|
|
run: |
|
|
cd build
|
|
# The "viewer" component is now jfjoch_viewer alone -- rugnux ships as its own artifact
|
|
# (build:rugnux-tgz) and the two CLI tools are no longer packaged at all. The full tree is
|
|
# still built because the viewer links the Rugnux library for its in-process jobs.
|
|
ninja -j16
|
|
cpack
|
|
- name: Upload viewer tgz to release
|
|
if: github.ref_type == 'tag'
|
|
shell: bash
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
# CMakeLists.txt names the archive jfjoch_viewer-<version>-linux-{cuda<major>|cpu}.tar.gz;
|
|
# rename to .tgz (matching the Windows installer's release naming) before upload.
|
|
files=(build/jfjoch_viewer-*.tar.gz)
|
|
if [ ${#files[@]} -eq 0 ]; then
|
|
echo "No viewer archive found in build/"
|
|
exit 1
|
|
fi
|
|
for file in "${files[@]}"; do
|
|
tgz="${file%.tar.gz}.tgz"
|
|
mv "$file" "$tgz"
|
|
python3 gitea_upload_file.py "$tgz"
|
|
done
|
|
build-rugnux-tgz:
|
|
name: build:rugnux-tgz (x86_64)
|
|
runs-on: jfjoch_rocky8
|
|
timeout-minutes: 120
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Clean previous build
|
|
shell: bash
|
|
run: rm -rf build-rugnux # self-hosted runners reuse the workspace volume
|
|
- name: Configure rugnux build
|
|
shell: bash
|
|
run: |
|
|
cmake -G Ninja -S . -B build-rugnux -DJFJOCH_RUGNUX_ONLY=ON -DJFJOCH_USE_CUDA=ON \
|
|
${{ env.LINUX_CMAKE_FLAGS }} -DCMAKE_BUILD_TYPE=Release
|
|
- name: Build rugnux tgz
|
|
shell: bash
|
|
run: cmake --build build-rugnux --target package -j16
|
|
- name: Verify the artifact is self-contained
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
rm -rf /tmp/rgx && mkdir -p /tmp/rgx
|
|
tar xf build-rugnux/rugnux-*.tar.gz -C /tmp/rgx
|
|
bin=$(find /tmp/rgx -name rugnux -type f)
|
|
# Each producer writes to a file and the greps read files, rather than piping into
|
|
# `grep -q`: grep exits at the first match, the producer then dies of SIGPIPE, and under
|
|
# `set -o pipefail` that becomes the pipeline's status -- which a leading `!` would turn
|
|
# into a pass, hiding the very thing being tested.
|
|
ldd "$bin" > /tmp/rgx.ldd 2>&1 || true
|
|
if grep -q cufft /tmp/rgx.ldd; then echo "cuFFT is linked dynamically"; exit 1; fi
|
|
find /tmp/rgx -name 'libcufft*' > /tmp/rgx.cufft
|
|
if [ -s /tmp/rgx.cufft ]; then echo "a libcufft is shipped in the tarball"; exit 1; fi
|
|
echo "self-contained: static cuFFT, nothing to ship beside it"
|
|
- name: Upload rugnux tgz to release
|
|
if: github.ref_type == 'tag'
|
|
shell: bash
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
# CMakeLists.txt names the archive rugnux-<version>-linux-{cuda<major>|cpu}.tar.gz;
|
|
# rename to .tgz before upload, as the viewer archive does.
|
|
files=(build-rugnux/rugnux-*.tar.gz)
|
|
if [ ${#files[@]} -eq 0 ]; then
|
|
echo "No rugnux archive found in build-rugnux/"
|
|
exit 1
|
|
fi
|
|
for file in "${files[@]}"; do
|
|
tgz="${file%.tar.gz}.tgz"
|
|
mv "$file" "$tgz"
|
|
python3 gitea_upload_file.py "$tgz"
|
|
done
|
|
|
|
build-rugnux-windows:
|
|
name: build:rugnux:windows
|
|
runs-on: windows-11-cuda-qt
|
|
timeout-minutes: 120
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# cmd, not bash: the Windows runner has no bash, and every cmake invocation needs the MSVC
|
|
# environment from vcvars64.bat first -- same shape as build-windows above. No Qt in
|
|
# CMAKE_PREFIX_PATH: JFJOCH_RUGNUX_ONLY is the portable subtree minus viewer/, so C:/deps
|
|
# (Eigen, ZLIB) is all it needs.
|
|
- name: Configure rugnux build
|
|
shell: cmd
|
|
run: |
|
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VSPATH=%%i"
|
|
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat"
|
|
cmake -G Ninja -B build-rugnux -DJFJOCH_RUGNUX_ONLY=ON -DJFJOCH_USE_CUDA=ON -DCMAKE_BUILD_TYPE=Release ${{ env.MSVC_ARCH_CMAKE_FLAGS }} -DCMAKE_PREFIX_PATH="C:/deps"
|
|
- name: Build rugnux
|
|
shell: cmd
|
|
run: |
|
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VSPATH=%%i"
|
|
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat"
|
|
cmake --build build-rugnux
|
|
- name: Build rugnux zip
|
|
shell: cmd
|
|
run: |
|
|
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VSPATH=%%i"
|
|
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat"
|
|
cd build-rugnux
|
|
cpack
|
|
- name: Upload rugnux zip to release
|
|
if: github.ref_type == 'tag'
|
|
shell: powershell
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
# Zip named rugnux-<version>-win64-{cuda<major>|cpu}.zip (see CMakeLists.txt).
|
|
$files = Get-ChildItem -Path build-rugnux -Filter 'rugnux-*-win64-*.zip'
|
|
if ($files.Count -eq 0) { throw 'No rugnux zip found in build-rugnux/' }
|
|
foreach ($file in $files) {
|
|
python gitea_upload_file.py $file.FullName
|
|
if ($LASTEXITCODE -ne 0) { throw "Upload failed for $($file.Name)" }
|
|
}
|
|
|
|
build-rugnux-arm:
|
|
name: build:rugnux:aarch64 (cross)
|
|
# Cross-compiled on an x86_64 runner: nvcc, gcc and cmake all run native and only emit aarch64,
|
|
# so this costs about what a normal build costs. Uses the ordinary ubuntu2404 image, which now
|
|
# also carries the aarch64 toolchain and the CUDA sbsa cross tree.
|
|
#
|
|
# The toolchain file comes from the CHECKOUT, not from the image. It describes how to build this
|
|
# source, so it belongs with the source: baked into the image it could only be changed by
|
|
# rebuilding and re-pushing the image, and a fix to it would appear to have no effect.
|
|
runs-on: jfjoch_ubuntu2404
|
|
container:
|
|
image: gitea.psi.ch/leonarski_f/jfjoch_ubuntu2404:2508
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Clean previous build
|
|
shell: bash
|
|
run: rm -rf build-aarch64 # self-hosted runners reuse the workspace volume
|
|
- name: Configure (cross to aarch64 SBSA)
|
|
shell: bash
|
|
# NOT ${{ env.LINUX_CMAKE_FLAGS }}: that pins -march=x86-64-v3, which an aarch64 compiler
|
|
# rejects. The aarch64 baseline already has NEON and a vectorised round, so the Eigen gap
|
|
# that flag closes on x86 does not exist here.
|
|
# sm_90 = GH200 (Grace Hopper, e.g. ALPS), sm_121 = GB10 (DGX Spark). One tarball, both.
|
|
run: |
|
|
cmake -G Ninja -S . -B build-aarch64 \
|
|
-DCMAKE_TOOLCHAIN_FILE="$PWD/docker/ubuntu2404/aarch64-sbsa.cmake" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DJFJOCH_RUGNUX_ONLY=ON \
|
|
-DJFJOCH_USE_CUDA=ON \
|
|
-DJFJOCH_CUDA_ARCHITECTURES="90;121"
|
|
- name: Build and package
|
|
shell: bash
|
|
run: cmake --build build-aarch64 --target package -j16
|
|
# A cross build that silently produced x86_64 still packages cleanly, so assert the artifact.
|
|
- name: Verify the tarball is really aarch64
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tgz=$(ls build-aarch64/rugnux-*.tar.gz | head -1)
|
|
echo "checking $tgz"
|
|
rm -rf /tmp/tgzcheck && mkdir -p /tmp/tgzcheck
|
|
tar xf "$tgz" -C /tmp/tgzcheck
|
|
bin=$(find /tmp/tgzcheck -name rugnux -type f | head -1)
|
|
# Everything is captured to a file first and the greps read files. Piping a large
|
|
# producer into `grep -q` makes it die of SIGPIPE once grep exits, which `set -o pipefail`
|
|
# turns into a failure -- and on a negated check into a spurious PASS.
|
|
file "$bin" > /tmp/tc.bin
|
|
cat /tmp/tc.bin
|
|
if ! grep -q "ARM aarch64" /tmp/tc.bin; then echo "not an aarch64 binary"; exit 1; fi
|
|
# nothing x86 may have leaked in (libjpeg-turbo's ExternalProject is the classic culprit)
|
|
find /tmp/tgzcheck -type f -exec file {} + > /tmp/tc.arch
|
|
if grep -q "x86-64" /tmp/tc.arch; then echo "an x86-64 file leaked into the tarball"; exit 1; fi
|
|
# cuFFT must be linked statically: nothing to ship beside the binary, nothing to find
|
|
ldd "$bin" > /tmp/tc.ldd 2>&1 || true
|
|
if grep -q cufft /tmp/tc.ldd; then echo "cuFFT is linked dynamically"; exit 1; fi
|
|
# both GPU targets present in the fatbin: sm_90 = GH200, sm_121 = GB10
|
|
cuobjdump --list-elf "$bin" > /tmp/tc.elf
|
|
for arch in sm_90 sm_121; do
|
|
if ! grep -q "$arch" /tmp/tc.elf; then echo "fatbin is missing $arch"; exit 1; fi
|
|
done
|
|
echo "aarch64, self-contained, both GPU targets present"
|
|
- name: Upload tarball to release
|
|
if: github.ref_type == 'tag'
|
|
shell: bash
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
files=(build-aarch64/rugnux-*.tar.gz)
|
|
if [ ${#files[@]} -eq 0 ]; then
|
|
echo "No rugnux archive found in build-aarch64/"
|
|
exit 1
|
|
fi
|
|
for file in "${files[@]}"; do
|
|
tgz="${file%.tar.gz}.tgz"
|
|
mv "$file" "$tgz"
|
|
python3 gitea_upload_file.py "$tgz"
|
|
done
|
|
|
|
build-rpm:
|
|
name: build:rpm (${{ matrix.distro }})
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: jfjoch_rocky8
|
|
distro: rocky8
|
|
cmake_flags: -DJFJOCH_USE_CUDA=ON
|
|
pkg_glob: "*.rpm"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/rpm/centos/el8/slsdet8-cuda12/upload
|
|
- runner: jfjoch_rocky8
|
|
distro: rocky8_sls9
|
|
cmake_flags: -DJFJOCH_USE_CUDA=ON -DSLS9=ON
|
|
pkg_glob: "*.rpm"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/rpm/centos/el8/slsdet9-cuda12/upload
|
|
- runner: jfjoch_rocky8
|
|
distro: rocky8_nocuda
|
|
cmake_flags: -DJFJOCH_USE_CUDA=OFF
|
|
pkg_glob: "*.rpm"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/rpm/centos/el8/slsdet8-nocuda/upload
|
|
- runner: jfjoch_rocky9
|
|
distro: rocky9
|
|
cmake_flags: -DJFJOCH_USE_CUDA=ON
|
|
pkg_glob: "*.rpm"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/rpm/centos/el9/slsdet8-cuda13/upload
|
|
- runner: jfjoch_rocky9
|
|
distro: rocky9_sls9
|
|
cmake_flags: -DJFJOCH_USE_CUDA=ON -DSLS9=ON
|
|
pkg_glob: "*.rpm"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/rpm/centos/el9/slsdet9-cuda13/upload
|
|
- runner: jfjoch_rocky9
|
|
distro: rocky9_nocuda
|
|
cmake_flags: -DJFJOCH_USE_CUDA=OFF
|
|
pkg_glob: "*.rpm"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/rpm/centos/el9/slsdet8-nocuda/upload
|
|
- runner: jfjoch_ubuntu2204
|
|
distro: ubuntu2204
|
|
cmake_flags: -DJFJOCH_USE_CUDA=ON
|
|
pkg_glob: "*.deb"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/debian/pool/jammy/cuda13/upload
|
|
- runner: jfjoch_ubuntu2204
|
|
distro: ubuntu2204_nocuda
|
|
cmake_flags: -DJFJOCH_USE_CUDA=OFF
|
|
pkg_glob: "*.deb"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/debian/pool/jammy/nocuda/upload
|
|
- runner: jfjoch_ubuntu2404
|
|
distro: ubuntu2404
|
|
cmake_flags: -DJFJOCH_USE_CUDA=ON
|
|
pkg_glob: "*.deb"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/debian/pool/noble/cuda13/upload
|
|
- runner: jfjoch_ubuntu2404
|
|
distro: ubuntu2404_nocuda
|
|
cmake_flags: -DJFJOCH_USE_CUDA=OFF
|
|
pkg_glob: "*.deb"
|
|
upload_url: https://gitea.psi.ch/api/packages/mx/debian/pool/noble/nocuda/upload
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Clean previous build
|
|
shell: bash
|
|
run: rm -rf build # self-hosted runners reuse the workspace volume; a stale build dir
|
|
# can leave the viewer linking a system libcurl instead of the vendored one
|
|
- name: Setup build (cmake)
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DJFJOCH_INSTALL_DRIVER_SOURCE=ON -DJFJOCH_VIEWER_BUILD=ON ${{ env.LINUX_CMAKE_FLAGS }} -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_flags }} ..
|
|
- name: Build packages
|
|
shell: bash
|
|
run: |
|
|
cd build
|
|
ninja frontend
|
|
ninja -j16 package
|
|
- name: Upload packages
|
|
if: github.ref_type == 'tag'
|
|
shell: bash
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
cd build
|
|
shopt -s nullglob
|
|
files=(${{ matrix.pkg_glob }})
|
|
if [ ${#files[@]} -eq 0 ]; then
|
|
echo "No package files found for pattern: ${{ matrix.pkg_glob }}"
|
|
exit 1
|
|
fi
|
|
url="${{ matrix.upload_url }}"
|
|
if [[ "$url" == *"/rpm/"* ]]; then
|
|
url="${url}?sign=true"
|
|
fi
|
|
for file in "${files[@]}"; do
|
|
echo "Uploading $file -> $url"
|
|
curl --fail --user __token__:"$TOKEN" --upload-file "$file" "$url"
|
|
done
|
|
|
|
cd ..
|
|
|
|
# The viewer is shipped to the release as a Linux-version-agnostic .tgz by the
|
|
# build-viewer-tgz job; only the writer (and the XDS plugin .so on rocky8) go to the
|
|
# release as distro packages here.
|
|
if [ "${{ matrix.distro }}" = "rocky8_nocuda" ]; then
|
|
for file in build/jfjoch-writer*rpm build/xds-plugin/libjfjoch_xds_plugin.so.*; do
|
|
python3 gitea_upload_file.py "$file"
|
|
done
|
|
elif [ "${{ matrix.distro }}" = "rocky9_nocuda" ]; then
|
|
for file in build/jfjoch-writer*rpm; do
|
|
python3 gitea_upload_file.py "$file"
|
|
done
|
|
elif [ "${{ matrix.distro }}" = "ubuntu2204_nocuda" ]; then
|
|
for file in build/jfjoch*writer*.deb; do
|
|
python3 gitea_upload_file.py "$file"
|
|
done
|
|
fi
|
|
dials-test:
|
|
name: DIALS test
|
|
runs-on: jfjoch_rocky9
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Build processing test
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ${{ env.LINUX_CMAKE_FLAGS }} ..
|
|
ninja -j16 jfjoch_hdf5_test
|
|
# dials.import before xia2.ssx, checking the image COUNT: dxtbx reports a short scan rather
|
|
# than failing when it cannot reach every image, and xia2.ssx still succeeds on the images it
|
|
# did get, so an exit code alone does not notice. Multi-file NXmxLegacy is exercised by the
|
|
# XDS jobs below but deliberately not here: dxtbx reads only the first data file of a legacy
|
|
# master (it takes one dataset out of NXdata and indexes it globally), so that combination
|
|
# cannot pass until the limitation is fixed upstream.
|
|
- name: Run DIALS processing on legacy format
|
|
shell: bash
|
|
run: |
|
|
source /opt/dials-v3-27-0/dials_env.sh
|
|
mkdir -p test01
|
|
cd test01
|
|
../build/tools/jfjoch_hdf5_test ../tests/test_data/compression_benchmark.h5 -n100 -o legacy
|
|
dials.import legacy_master.h5 | tee import.log
|
|
grep -q "num images: 100" import.log || { echo "DIALS did not read all 100 images"; exit 1; }
|
|
xia2.ssx image=legacy_master.h5 space_group=P43212 unit_cell=78.551,78.551,36.914,90.000,90.000,90.000
|
|
- name: Run DIALS processing on VDS (master + 4 linked image files)
|
|
shell: bash
|
|
run: |
|
|
source /opt/dials-v3-27-0/dials_env.sh
|
|
mkdir -p test02
|
|
cd test02
|
|
../build/tools/jfjoch_hdf5_test ../tests/test_data/compression_benchmark.h5 -n100 -f25 -V -o vds
|
|
dials.import vds_master.h5 | tee import.log
|
|
grep -q "num images: 100" import.log || { echo "DIALS did not read all 100 images"; exit 1; }
|
|
xia2.ssx image=vds_master.h5 space_group=P43212 unit_cell=78.551,78.551,36.914,90.000,90.000,90.000
|
|
- name: Run DIALS processing on single file format
|
|
shell: bash
|
|
run: |
|
|
source /opt/dials-v3-27-0/dials_env.sh
|
|
mkdir -p test03
|
|
cd test03
|
|
# -S writes <prefix>_master.h5, not <prefix>.h5 - naming this wrong meant xia2.ssx was
|
|
# pointed at a file that never existed, so the integrated format went untested.
|
|
../build/tools/jfjoch_hdf5_test ../tests/test_data/compression_benchmark.h5 -n100 -S -o single
|
|
dials.import single_master.h5 | tee import.log
|
|
grep -q "num images: 100" import.log || { echo "DIALS did not read all 100 images"; exit 1; }
|
|
xia2.ssx image=single_master.h5 space_group=P43212 unit_cell=78.551,78.551,36.914,90.000,90.000,90.000
|
|
# Everything above writes an UNTILTED detector, where every plausible encoding of the tilt
|
|
# agrees exactly - so none of it constrains the NXmx transformation chain. This does.
|
|
- name: Check tilted detector geometry against DIALS
|
|
shell: bash
|
|
run: |
|
|
source /opt/dials-v3-27-0/dials_env.sh
|
|
mkdir -p test04
|
|
cd test04
|
|
../build/tools/jfjoch_hdf5_test ../tests/test_data/compression_benchmark.h5 -n1 -S -o geom
|
|
dials.python ../tests/nxmx_geometry_dials_test.py geom_master.h5
|
|
xds-durin-test:
|
|
name: XDS test (durin plugin)
|
|
runs-on: jfjoch_rocky9
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Build processing test
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ${{ env.LINUX_CMAKE_FLAGS }} ..
|
|
ninja -j16 jfjoch_hdf5_test
|
|
- name: Run XDS with Durin and legacy HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds_durin
|
|
rm -rf *.h5 *.LP *.HKL
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -f10
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
- name: Run XDS with Durin and VDS HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds_durin
|
|
rm -rf *.h5 *.LP *.HKL
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -f10 -V
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
- name: Run XDS with Durin and integrated HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds_durin
|
|
rm -rf *.h5 *.LP *.HKL
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -S
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
xds-test:
|
|
name: XDS test (JFJoch plugin)
|
|
runs-on: jfjoch_rocky9
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Build processing test
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ${{ env.LINUX_CMAKE_FLAGS }} ..
|
|
ninja -j16 jfjoch_hdf5_test
|
|
ninja -j16 jfjoch_xds_plugin
|
|
- name: Run XDS with legacy HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -f10
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
- name: Run XDS with VDS HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds
|
|
rm -f *.h5 *.LP *.HKL
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -f10 -V
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
- name: Run XDS with single HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds
|
|
rm -f *.h5 *.LP *.HKL
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -S
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
xds-neggia-test:
|
|
name: XDS test (neggia plugin)
|
|
runs-on: jfjoch_rocky9
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Build processing test
|
|
shell: bash
|
|
run: |
|
|
mkdir -p build
|
|
cd build
|
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ${{ env.LINUX_CMAKE_FLAGS }} ..
|
|
ninja -j16 jfjoch_hdf5_test
|
|
- name: Run XDS with Neggia and legacy HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds_neggia
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -f10
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
- name: Run XDS with Neggia and single HDF5 format
|
|
shell: bash
|
|
run: |
|
|
cd tests/xds_neggia
|
|
rm -f *.h5 *.LP *.HKL
|
|
../../build/tools/jfjoch_hdf5_test ../../tests/test_data/compression_benchmark.h5 -n25 -S
|
|
/opt/xds/xds_par
|
|
test -f IDXREF.LP
|
|
python-client:
|
|
name: Generate python client
|
|
runs-on: jfjoch_rocky8
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Build python client
|
|
shell: bash
|
|
run: bash gen_python_client.sh
|
|
- name: Upload Package to Gitea PyPI
|
|
if: github.ref_type == 'tag'
|
|
run: |
|
|
twine upload -u __token__ -p ${{ secrets.CI_PYPI_TOKEN }} --skip-existing dist/*
|
|
twine upload -u __token__ -p ${{ secrets.PIP_REPOSITORY_API_TOKEN }} --repository-url https://gitea.psi.ch/api/packages/mx/pypi dist/*
|
|
documentation:
|
|
name: Build documentation
|
|
runs-on: jfjoch_rocky8
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Build documentation
|
|
shell: bash
|
|
run: bash make_doc.sh
|
|
- name: Upload documentation
|
|
if: github.ref_type == 'tag'
|
|
shell: bash
|
|
run: |
|
|
git config --global user.email "filip.leonarski@psi.ch"
|
|
git config --global user.name "Filip Leonarski (Gitea)"
|
|
git checkout --orphan gitea-pages
|
|
git reset --hard
|
|
# Remove everything except .git and public
|
|
git clean -fdx -e public -e .git
|
|
|
|
# Move public contents to root and remove the now-empty public dir
|
|
shopt -s dotglob nullglob
|
|
mv public/* .
|
|
rm -rf public
|
|
git add .
|
|
git commit -m "Deploy site"
|
|
git push -f https://${{secrets.GITHUB_TOKEN}}@gitea.psi.ch/${{ github.repository }}.git gitea-pages
|
|
create-release:
|
|
name: Create release
|
|
runs-on: jfjoch_rocky8
|
|
if: github.event_name == 'workflow_dispatch' && (github.event.inputs.create_release == 'true' || github.event.inputs.create_release == true)
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
persist-credentials: 'true' # Optional; should be the default
|
|
- name: Configure auth and fetch LFS
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
run: |
|
|
git lfs install --local
|
|
AUTH=$(git config --local http.${{ github.server_url }}/.extraheader)
|
|
git config --local --unset http.${{ github.server_url }}/.extraheader
|
|
git config --local http.${{ github.server_url }}/${{ github.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH"
|
|
git lfs pull
|
|
- name: Create release
|
|
env:
|
|
TOKEN: ${{ secrets.PIP_REPOSITORY_API_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
python3 gitea_create_release.py |