Skip to content

Merge branch 'staging' into release #21

Merge branch 'staging' into release

Merge branch 'staging' into release #21

Workflow file for this run

name: Package
on:
push:
tags: ['v*']
permissions:
contents: write
env:
# Opt into Node 24 runtime for JavaScript actions (default flips 2026-06-02).
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
package:
name: ${{ matrix.os }} / ${{ matrix.format }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
format: DEB
generators: DEB
deb_arch: amd64
rpm_arch: x86_64
asset_suffix: Linux-amd64
- os: ubuntu-24.04
format: RPM
generators: RPM
deb_arch: amd64
rpm_arch: x86_64
asset_suffix: Linux-x86_64
- os: ubuntu-24.04-arm
format: DEB
generators: DEB
deb_arch: arm64
rpm_arch: aarch64
asset_suffix: Linux-arm64
- os: ubuntu-24.04-arm
format: RPM
generators: RPM
deb_arch: arm64
rpm_arch: aarch64
asset_suffix: Linux-aarch64
- os: windows-latest
format: NSIS
generators: NSIS
asset_suffix: win64
- os: macos-15
format: productbuild
generators: productbuild
asset_suffix: Darwin
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Resolve version from tag
shell: bash
run: |
ref="${GITHUB_REF_NAME}"
if [[ "$ref" == v* ]]; then
echo "H5CPP_VERSION=${ref#v}" >> "$GITHUB_ENV"
else
echo "H5CPP_VERSION=1.12.99-test" >> "$GITHUB_ENV"
fi
- name: Install LLVM toolchain (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
set -euxo pipefail
apt_opts=(
-o Acquire::Retries=5
-o Acquire::http::Timeout=30
-o Acquire::https::Timeout=30
-o Acquire::ForceIPv4=true
)
apt_update() {
sudo apt-get "${apt_opts[@]}" update
}
apt_install() {
sudo env DEBIAN_FRONTEND=noninteractive apt-get "${apt_opts[@]}" install -y --no-install-recommends "$@"
}
apt_update
apt_install wget gnupg lsb-release ca-certificates
codename=$(lsb_release -cs)
llvm_version="20"
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key \
| sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/llvm-snapshot.gpg
echo "deb [signed-by=/etc/apt/trusted.gpg.d/llvm-snapshot.gpg] https://apt.llvm.org/${codename}/ llvm-toolchain-${codename}-${llvm_version} main" \
| sudo tee /etc/apt/sources.list.d/llvm-${llvm_version}.list
apt_update
apt_install \
llvm-${llvm_version}-dev \
libclang-${llvm_version}-dev \
clang-${llvm_version} \
cmake \
ninja-build \
rpm \
libzstd-dev
llvm_cmakedir="$(llvm-config-${llvm_version} --cmakedir)"
clang_cmakedir="$(dirname "${llvm_cmakedir}")/clang"
echo "LLVM_DIR=${llvm_cmakedir}" >> "$GITHUB_ENV"
echo "Clang_DIR=${clang_cmakedir}" >> "$GITHUB_ENV"
- name: Install LLVM toolchain (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
set -euxo pipefail
brew update
brew install --quiet cmake ninja llvm@20
llvm_prefix="$(brew --prefix llvm@20)"
echo "CC=/usr/bin/clang" >> "$GITHUB_ENV"
echo "CXX=/usr/bin/clang++" >> "$GITHUB_ENV"
echo "LLVM_DIR=${llvm_prefix}/lib/cmake/llvm" >> "$GITHUB_ENV"
echo "Clang_DIR=${llvm_prefix}/lib/cmake/clang" >> "$GITHUB_ENV"
- name: Install LLVM toolchain (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$ver = "19.1.7"
$archive = "clang+llvm-${ver}-x86_64-pc-windows-msvc.tar.xz"
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${ver}/${archive}"
Invoke-WebRequest -Uri $url -OutFile $archive -UseBasicParsing
tar -xf $archive
$src = (Get-ChildItem -Directory "clang+llvm-*")[0].FullName
New-Item -ItemType Directory -Force "C:\Program Files\LLVM" | Out-Null
Copy-Item "$src\*" "C:\Program Files\LLVM\" -Recurse -Force
choco install ninja -y --no-progress
choco install nsis -y --no-progress
$llvm_prefix = "C:\Program Files\LLVM"
"LLVM_DIR=$llvm_prefix\lib\cmake\llvm" >> $env:GITHUB_ENV
"Clang_DIR=$llvm_prefix\lib\cmake\clang" >> $env:GITHUB_ENV
"$llvm_prefix\bin" >> $env:GITHUB_PATH
- name: Patch LLVM cmake for DIA SDK (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
# LLVM 18.1.8 was built with VS 2019; its cmake exports hardcode the VS 2019 DIA SDK path.
# This step runs every time (not gated on cache) and rewrites that path to wherever
# diaguids.lib actually lives on this runner.
$old_path = "C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/DIA SDK/lib/amd64/diaguids.lib"
$dia_file = Get-ChildItem "C:\Program Files\Microsoft Visual Studio" -Filter "diaguids.lib" `
-Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -like "*amd64*" } |
Select-Object -First 1
if (-not $dia_file) {
Write-Warning "diaguids.lib not found — linker may fail for PDB targets"
exit 0
}
$actual = $dia_file.FullName -replace '\\', '/'
Write-Host "diaguids.lib found at: $actual"
$patched = 0
Get-ChildItem "C:\Program Files\LLVM\lib\cmake" -Filter "*.cmake" -Recurse |
ForEach-Object {
$text = [System.IO.File]::ReadAllText($_.FullName)
if ($text.Contains($old_path)) {
Write-Host "Patching $($_.Name)"
$text = $text.Replace($old_path, $actual)
[System.IO.File]::WriteAllText($_.FullName, $text)
$patched++
}
}
Write-Host "Patched $patched cmake file(s)"
- name: Configure (Linux / macOS)
if: runner.os != 'Windows'
shell: bash
run: |
set -euxo pipefail
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_DIR="$LLVM_DIR" \
-DClang_DIR="$Clang_DIR" \
-DH5CPP_STATIC_LINK_LLVM=ON \
-DH5CPP_VERSION_OVERRIDE="${H5CPP_VERSION:-}"
- name: Configure (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
cmake -S . -B build `
-G "Visual Studio 17 2022" -A x64 `
-DCMAKE_BUILD_TYPE=Release `
-DLLVM_DIR="$env:LLVM_DIR" `
-DClang_DIR="$env:Clang_DIR" `
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded `
-DH5CPP_VERSION_OVERRIDE="$env:H5CPP_VERSION"
- name: Build
shell: bash
run: cmake --build build --parallel --config Release
- name: Package
shell: bash
run: |
set -euxo pipefail
cd build
cpack -G "${{ matrix.generators }}" \
-D "CPACK_PACKAGE_FILE_NAME=h5cpp-compiler-${H5CPP_VERSION}-${{ matrix.asset_suffix }}" \
--config CPackConfig.cmake
# macOS guard: productbuild must produce a real payload
if [[ "${{ matrix.generators }}" == "productbuild" ]]; then
pkg="h5cpp-compiler-${H5CPP_VERSION}-${{ matrix.asset_suffix }}.pkg"
size=$(stat -f%z "$pkg" 2>/dev/null || stat -c%s "$pkg" 2>/dev/null)
if (( size < 100000 )); then
echo "macOS package is suspiciously small (${size} bytes) — likely empty" >&2
exit 1
fi
fi
env:
CPACK_DEBIAN_PACKAGE_ARCHITECTURE: ${{ matrix.deb_arch }}
CPACK_RPM_PACKAGE_ARCHITECTURE: ${{ matrix.rpm_arch }}
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: h5cpp-compiler-${{ matrix.os }}-${{ matrix.format }}
if-no-files-found: error
path: |
build/h5cpp-compiler-*.deb
build/h5cpp-compiler-*.rpm
build/h5cpp-compiler-*.exe
build/h5cpp-compiler-*.pkg
musl:
name: musl-static / x86_64
runs-on: ubuntu-24.04
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Resolve version from tag
shell: bash
run: |
ref="${GITHUB_REF_NAME}"
echo "H5CPP_VERSION=${ref#v}" >> "$GITHUB_ENV"
- name: Build musl static binary
shell: bash
run: |
set -euxo pipefail
docker build -f Dockerfile.musl -t h5cpp-musl .
mkdir -p dist
docker create --name extract h5cpp-musl
docker cp extract:/src/build/h5cpp "dist/h5cpp"
docker rm extract
chmod +x "dist/h5cpp"
tar czf "dist/h5cpp-compiler-${H5CPP_VERSION}-Linux-musl-x86_64.tar.gz" -C dist h5cpp
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: h5cpp-compiler-musl
if-no-files-found: error
path: dist/*.tar.gz
wine-smoke-test:
name: Wine smoke-test / Windows NSIS
needs: package
runs-on: ubuntu-24.04
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Install Wine
shell: bash
run: |
set -euxo pipefail
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y --no-install-recommends wine64 wine32 xvfb
- name: Download Windows artifact
uses: actions/download-artifact@v8
with:
name: h5cpp-compiler-windows-latest-NSIS
path: windows-artifact
- name: Smoke-test under Wine
shell: bash
run: |
set -euxo pipefail
export WINEPREFIX=/tmp/wine-smoke
export WINEARCH=win64
# Initialize Wine prefix silently
xvfb-run -a wine64 wineboot --init 2>/dev/null || true
# Locate the NSIS installer
installer=( windows-artifact/h5cpp-compiler-*.exe )
if [[ ! -f "${installer[0]}" ]]; then
echo "No Windows installer found in artifact" >&2
exit 1
fi
# Silent install
xvfb-run -a wine64 "${installer[0]}" /S
# Locate installed binary (glob handles versioned install dir)
h5cpp_exe=( /tmp/wine-smoke/drive_c/Program\ Files/h5cpp-compiler/*/bin/h5cpp.exe )
if [[ ! -f "${h5cpp_exe[0]}" ]]; then
echo "h5cpp.exe not found after NSIS install" >&2
exit 1
fi
# Run smoke test
output=$(xvfb-run -a wine64 "${h5cpp_exe[0]}" --version 2>&1)
echo "$output"
# Validate expected output
if ! grep -q "H5CPP:" <<< "$output"; then
echo "Smoke test failed: missing H5CPP banner" >&2
exit 1
fi
if ! grep -q "LLVM version" <<< "$output"; then
echo "Smoke test failed: missing LLVM version string" >&2
exit 1
fi
echo "Wine smoke-test passed"
publish:
name: Publish Release
needs: [package, musl]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download all artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
pattern: h5cpp-compiler-*
- name: Create Release and upload assets
shell: bash
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME}"
# Ensure release exists. Idempotent: create only if missing,
# so a re-run of the workflow against the same tag does not 422.
if ! gh release view "$tag" >/dev/null 2>&1; then
gh release create "$tag" \
--title "h5cpp-compiler-${tag}" \
--generate-notes
fi
# Collect installer artifacts only. Defensive against stray
# CMake-generated files (e.g. h5cpp-compiler-dev.sln on Windows).
shopt -s globstar nullglob
files=( artifacts/**/*.deb artifacts/**/*.rpm artifacts/**/*.exe artifacts/**/*.pkg artifacts/**/*.tar.gz )
if (( ${#files[@]} == 0 )); then
echo "no installer artifacts found under artifacts/" >&2
exit 1
fi
# Upload each file with bounded retries. uploads.github.com is
# eventually consistent right after release creation and
# occasionally 404s on first POST.
failed=()
for f in "${files[@]}"; do
[[ -f "$f" ]] || continue
echo "::group::upload $(basename "$f")"
ok=0
for attempt in 1 2 3 4 5; do
if gh release upload "$tag" "$f" --clobber; then
ok=1
break
fi
backoff=$(( attempt * attempt * 2 ))
echo "attempt ${attempt} failed; retrying in ${backoff}s"
sleep "${backoff}"
done
echo "::endgroup::"
(( ok == 1 )) || failed+=( "$f" )
done
if (( ${#failed[@]} > 0 )); then
echo "Upload failed for:" >&2
printf ' - %s\n' "${failed[@]}" >&2
exit 1
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}