Skip to content

Release smoke test

Release smoke test #21

Workflow file for this run

name: Release smoke test
# Verifies a PUBLISHED release on Maven Central actually loads and runs on every
# supported OS/arch — one runner per native classifier. Pulls the release jars
# from Central (no source build, no submodule, no zig), so it tests exactly what
# users consume. Trigger manually and pass the version to check.
on:
workflow_dispatch:
inputs:
version:
description: Version to smoke-test. Leave blank to use the latest release on Maven Central.
required: false
default: ""
schedule:
# Weekly, Mondays 06:00 UTC. No input on scheduled runs, so the resolve job
# picks the latest release on Maven Central — catches Central / runner-image
# drift against the published release without a manual trigger.
- cron: "0 6 * * 1"
jobs:
resolve:
name: resolve version
runs-on: ubuntu-latest
permissions: {}
outputs:
version: ${{ steps.pick.outputs.version }}
steps:
- name: Resolve version (input, else latest on Maven Central)
id: pick
shell: bash
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
version="$INPUT_VERSION"
if [ -z "$version" ]; then
# <release> in maven-metadata.xml is the latest non-snapshot release.
version=$(curl -fsSL --proto '=https' --tlsv1.2 https://repo1.maven.org/maven2/io/github/dfa1/zstd/zstd/maven-metadata.xml \
| grep -oE '<release>[^<]+</release>' | sed -E 's/<\/?release>//g')
fi
if [ -z "$version" ]; then
echo "could not resolve a version from Maven Central" >&2
exit 1
fi
echo "Smoke-testing version: $version"
echo "version=$version" >> "$GITHUB_OUTPUT"
smoke:
name: ${{ matrix.classifier }}${{ matrix.label && format(' ({0})', matrix.label) || '' }}
needs: resolve
# Every leg gates the workflow, including the 8 container legs — the
# library claims to be portable across distros/libcs, so a failure on any
# of them is a real regression, not a shrug.
strategy:
fail-fast: false
matrix:
# distribution is per-leg for clarity and easy override. Zulu ships JDK 25
# for all targets (incl. Windows/ARM64, which Temurin does not yet) and
# matches publish.yml. Legs with `container` run the linux native inside a
# container to check compatibility on other distributions and libcs.
include:
- { os: ubuntu-latest, classifier: linux-x86_64, distribution: zulu }
- { os: ubuntu-24.04-arm, classifier: linux-aarch64, distribution: zulu }
- { os: macos-14, classifier: osx-aarch64, distribution: zulu }
- { os: windows-latest, classifier: windows-x86_64, distribution: zulu }
- { os: windows-11-arm, classifier: windows-aarch64, distribution: zulu }
# musl (Alpine) — glibc natives on a musl libc
- { os: ubuntu-latest, classifier: linux-x86_64, distribution: zulu, container: "amazoncorretto:25-alpine", label: musl }
- { os: ubuntu-24.04-arm, classifier: linux-aarch64, distribution: zulu, container: "amazoncorretto:25-alpine", label: musl }
# Ubuntu 22.04 (Jammy) — glibc 2.35; eclipse-temurin has no bookworm tag for JDK 25
- { os: ubuntu-latest, classifier: linux-x86_64, distribution: zulu, container: "eclipse-temurin:25-jdk-jammy", label: jammy }
- { os: ubuntu-24.04-arm, classifier: linux-aarch64, distribution: zulu, container: "eclipse-temurin:25-jdk-jammy", label: jammy }
# Red Hat UBI 10 minimal — glibc 2.34 (RHEL-like)
- { os: ubuntu-latest, classifier: linux-x86_64, distribution: zulu, container: "eclipse-temurin:25-jdk-ubi10-minimal", label: ubi }
- { os: ubuntu-24.04-arm, classifier: linux-aarch64, distribution: zulu, container: "eclipse-temurin:25-jdk-ubi10-minimal", label: ubi }
# Amazon Linux 2023 (RHEL-like) — glibc 2.34
- { os: ubuntu-latest, classifier: linux-x86_64, distribution: zulu, container: "amazoncorretto:25-al2023", label: al2023 }
- { os: ubuntu-24.04-arm, classifier: linux-aarch64, distribution: zulu, container: "amazoncorretto:25-al2023", label: al2023 }
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- name: Checkout (smoke sources only)
uses: actions/checkout@v7
# Host JDK is only needed for the native runner path — the container
# path runs its own build entirely inside the target image (see below).
- name: Set up JDK 25 (host)
if: ${{ !matrix.container }}
uses: actions/setup-java@v5
with:
distribution: ${{ matrix.distribution }}
java-version: '25'
# --- native runner path (glibc / macOS / Windows): a real `mvn test`
# against .github/smoke/pom.xml (JUnit 5 via Surefire), via the
# vendored Maven Wrapper so no preinstalled Maven is required.
# -Dzstd.version and -Dzstd.classifier pin the release and native
# jar under test. ---
- name: Smoke-test ${{ needs.resolve.outputs.version }} on ${{ matrix.classifier }}
if: ${{ !matrix.container }}
shell: bash
env:
VERSION: ${{ needs.resolve.outputs.version }}
run: |
sh .github/smoke/mvnw -f .github/smoke/pom.xml --no-transfer-progress \
-Dzstd.version="$VERSION" -Dzstd.classifier=${{ matrix.classifier }} \
test
# --- container leg: extraction needs unzip, which some minimal images
# (al2023) ship without any package manager at all to install it
# with. Sidestep that entirely: extract Maven once on the host
# (ubuntu-latest always has curl+unzip) from the same pinned
# distributionUrl the wrapper uses, then mount it read-only into the
# container. The container only needs its own `java` on PATH — no
# unzip/tar/package manager required inside it at all. ---
- name: Extract Maven (host, mounted read-only into the container)
if: ${{ matrix.container }}
shell: bash
run: |
distributionUrl=$(sed -n 's/^distributionUrl=//p' .github/smoke/.mvn/wrapper/maven-wrapper.properties)
curl -fsSL --proto '=https' --tlsv1.2 "$distributionUrl" -o "$RUNNER_TEMP/maven.zip"
unzip -q "$RUNNER_TEMP/maven.zip" -d "$RUNNER_TEMP/maven-extract"
mv "$RUNNER_TEMP"/maven-extract/apache-maven-*/ "$RUNNER_TEMP/maven"
# The same Maven build runs *inside* the target container (its own JDK,
# no host JDK involved), so dependency resolution, compilation, and
# native-lib loading all happen under that distro's libc. Only
# `docker run` itself, plus the Maven extraction above, run on the host.
- name: Smoke-test ${{ needs.resolve.outputs.version }} on ${{ matrix.classifier }} (${{ matrix.label }})
if: ${{ matrix.container }}
shell: bash
env:
VERSION: ${{ needs.resolve.outputs.version }}
run: |
docker run --rm -v "$PWD:/w" -v "$RUNNER_TEMP/maven:/opt/maven:ro" -w /w "${{ matrix.container }}" \
/opt/maven/bin/mvn -f .github/smoke/pom.xml --no-transfer-progress \
-Dzstd.version="$VERSION" -Dzstd.classifier=${{ matrix.classifier }} \
test
results:
name: results
needs: [resolve, smoke]
if: always()
runs-on: ubuntu-latest
permissions:
actions: read
steps:
# Per-leg results aren't visible to a needs-context (needs.smoke.result is
# one aggregate string for the whole matrix), so pull the real per-job
# conclusions from the Actions API and render one table up front instead
# of making readers open all 13 jobs to see what actually passed.
- name: Build results table
env:
GH_TOKEN: ${{ github.token }}
run: |
jobs=$(gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs?per_page=100" \
--jq '[.jobs[] | select(.name != "resolve version" and .name != "results")]')
{
echo "### Release smoke — version ${{ needs.resolve.outputs.version }}"
echo
echo "Every host below gates the workflow — a failure on any one of them is a real portability regression."
echo
echo "| Host | Result | Duration |"
echo "|---|---|---|"
} >> "$GITHUB_STEP_SUMMARY"
echo "$jobs" | jq -r '.[] |
(if .conclusion == "success" then "✅ pass"
elif .conclusion == "skipped" then "⏭️ skipped"
else "❌ **" + (.conclusion // "unknown") + "**" end) as $result |
(((.completed_at | fromdateiso8601) - (.started_at | fromdateiso8601))) as $dur |
(($dur / 60) | floor) as $m | ($dur % 60) as $s |
(if $m > 0 then ($m | tostring) + "m " + ($s | tostring) + "s" else ($s | tostring) + "s" end) as $duration |
"| " + .name + " | " + $result + " | " + $duration + " |"' >> "$GITHUB_STEP_SUMMARY"
total=$(echo "$jobs" | jq 'length')
pass=$(echo "$jobs" | jq '[.[] | select(.conclusion == "success")] | length')
{
echo
echo "**$pass/$total passed**"
} >> "$GITHUB_STEP_SUMMARY"