Skip to content

Merge pull request #717 from sjanel/feature/access-log-config-flush-t… #1729

Merge pull request #717 from sjanel/feature/access-log-config-flush-t…

Merge pull request #717 from sjanel/feature/access-log-config-flush-t… #1729

name: Benchmarks and GitHub pages Sync
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Weekly scheduled run on main to produce more stable benchmark results
schedule:
- cron: '0 3 * * 0' # every Sunday at 03:00 UTC
# Allow manual runs via the Actions UI with an optional duration override
workflow_dispatch:
inputs:
bench_duration:
description: 'Per-sample benchmark duration (e.g. 90s). Empty = per-job default.'
required: false
default: ''
bench_repeat:
description: 'Samples per case; the median is reported (weekly quality). HTTP/1 + H2 only.'
required: false
default: '3'
h2_protocol:
description: 'H2 protocol filter: h2c, h2-tls, or both'
required: false
default: 'both'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ------------------------------------------------------------------ #
# Build: compile the scripted server + client benchmark binaries ONCE.#
# bench / bench-h2 / bench-ws all need the exact same Release build #
# (identical CMake flags) - they only differ in which runtime params #
# they pass to wrk/h2load/k6. Building once and sharing the binaries #
# avoids recompiling aeronet plus every fetched competitor framework #
# (drogon, pistache, crow, beast, uWebSockets, ...) up to 7x per run. #
# ------------------------------------------------------------------ #
build:
name: Build scripted benchmarks
runs-on: ubuntu-latest
steps:
- name: Checkout current main
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Install build dependencies
run: |
sudo apt update
sudo apt install -y build-essential libjsoncpp-dev uuid-dev libssl-dev libcurl4-openssl-dev
- name: Configure benchmark build
run: |
./scripts/retry.sh cmake -S . -B build-pages -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DAERONET_BUILD_TESTS=OFF \
-DAERONET_BUILD_EXAMPLES=OFF \
-DAERONET_BUILD_BENCHMARKS=ON \
-DAERONET_ENABLE_ASAN=OFF \
-DAERONET_ENABLE_CLANG_TIDY=OFF \
-DAERONET_ENABLE_SPDLOG=ON \
-DAERONET_ENABLE_OPENSSL=ON \
-DAERONET_ENABLE_HTTP2=ON \
-DAERONET_ENABLE_BROTLI=ON \
-DAERONET_ENABLE_ZLIB=ON \
-DAERONET_ENABLE_ZSTD=ON \
-DAERONET_ENABLE_OPENTELEMETRY=OFF \
-DAERONET_ENABLE_WEBSOCKET=ON \
-DAERONET_ENABLE_ASYNC_HANDLERS=ON
- name: Build
run: |
cmake --build build-pages --parallel
- name: Package build output
run: |
# Some competitor drivers/servers (drogon + its trantor submodule) are built as
# SHARED libraries under build-pages/_deps/ and dynamically linked into the
# drogon-bench-{server,client} binaries. The bench jobs only receive this tarball,
# not the full build tree, so gather every shared object into a dedicated dir that
# travels with the archive. The bench jobs add it to LD_LIBRARY_PATH so the loader
# can resolve libdrogon.so.* / libtrantor.so.* at runtime (otherwise: exit 127,
# "libdrogon.so.1: cannot open shared object file").
mkdir -p build-pages/benchmarks/shared-libs
find build-pages -path build-pages/benchmarks/shared-libs -prune -o \
\( -name '*.so' -o -name '*.so.*' \) -print0 \
| xargs -0 -r cp -a -t build-pages/benchmarks/shared-libs/
# Preserve symlinks (run_benchmarks.py etc. are symlinked back into the
# checked-out source tree by CMake) and executable bits: a plain tar
# survives actions/upload-artifact intact, unlike zip-based repacking.
tar -czf bench-build-output.tar.gz \
build-pages/benchmarks/scripted-servers \
build-pages/benchmarks/scripted-clients \
build-pages/benchmarks/shared-libs
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: bench-build-output
path: bench-build-output.tar.gz
retention-days: 1
# ------------------------------------------------------------------ #
# Bench: two parallel runs with different wrk connection counts. #
# Both use GCC; low connections measures latency accurately, high #
# connections stresses max throughput. #
# ------------------------------------------------------------------ #
bench:
name: Bench (${{ matrix.conn_label }})
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Low connections: 10/thread - accurate latency, moderate throughput
- conn_label: low
connections_per_thread: 10
# High connections: 50/thread - higher throughput, higher latency
- conn_label: high
connections_per_thread: 100
steps:
- name: Checkout current main
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Install runtime dependencies
run: |
sudo apt update
sudo apt install -y wrk libjsoncpp-dev uuid-dev libssl-dev libcurl4-openssl-dev
pip install uvicorn starlette
- name: Download build artifact
uses: actions/download-artifact@v8
with:
name: bench-build-output
path: .
- name: Extract build artifact
run: tar -xzf bench-build-output.tar.gz
- name: Run benchmarks
working-directory: ${{github.workspace}}/build-pages/benchmarks/scripted-servers
env:
# Resolve the shared libs (libdrogon.so.* / libtrantor.so.*) bundled by the build job.
LD_LIBRARY_PATH: ${{github.workspace}}/build-pages/benchmarks/shared-libs
run: |
# Decide duration, threads and sample count based on event type and inputs.
# Defaults:
# - pull_request / non-main push => 5s, 1 thread, 1 sample (fast)
# - push to main => 30s, 2 threads, 1 sample
# - schedule / workflow_dispatch => 45s x 3-sample median, 2 threads (weekly
# quality). Several shorter samples + median beat one long sample on noisy
# shared runners; DUR x REPEAT is kept within the 6h per-job budget across
# all servers x scenarios. Both are overridable via workflow_dispatch inputs.
EVENT_NAME=${GITHUB_EVENT_NAME:-}
REF=${GITHUB_REF:-}
WARMUP=5s
THREADS=1
REPEAT=1
CPT=${{ matrix.connections_per_thread }}
CONN=$((THREADS * CPT))
if [ "$EVENT_NAME" = "push" ] && [ "$REF" = "refs/heads/main" ]; then
DUR=30s
elif [ "$EVENT_NAME" = "workflow_dispatch" ] || [ "$EVENT_NAME" = "schedule" ]; then
REPEAT="${{ github.event.inputs.bench_repeat || '3' }}"
if [ -n "${{ github.event.inputs.bench_duration }}" ]; then
DUR="${{ github.event.inputs.bench_duration }}"
else
DUR=45s
fi
else
# pull_request and non-main pushes
DUR=5s
WARMUP=2s
CONN=$((THREADS * CPT))
fi
echo "Event: $EVENT_NAME ref: $REF -> ${DUR}x${REPEAT}, $THREADS threads, $CONN connections ($CPT/thread)"
BENCH_WRK_TIMEOUT=${BENCH_WRK_TIMEOUT:-10s}
BODY_CODEC_MAX_PAYLOAD_KB=${BODY_CODEC_MAX_PAYLOAD_KB:-4096}
export BENCH_WRK_TIMEOUT BODY_CODEC_MAX_PAYLOAD_KB
echo "Using BENCH_WRK_TIMEOUT=$BENCH_WRK_TIMEOUT BODY_CODEC_MAX_PAYLOAD_KB=$BODY_CODEC_MAX_PAYLOAD_KB"
./run_benchmarks.py --duration "$DUR" --threads "$THREADS" --warmup "$WARMUP" --connections "$CONN" --repeat "$REPEAT"
- name: Upload benchmark results
uses: actions/upload-artifact@v7
with:
name: bench-results-${{ matrix.conn_label }}
path: build-pages/benchmarks/scripted-servers/results/benchmark_latest.json
retention-days: 3
- name: Upload badge (high-connections run only)
if: matrix.conn_label == 'high'
uses: actions/upload-artifact@v7
with:
name: bench-badge
path: build-pages/benchmarks/scripted-servers/results/benchmark_badge.json
retention-days: 3
if-no-files-found: ignore
# ------------------------------------------------------------------ #
# HTTP/2 bench: h2c/h2-tls × low/high connections via h2load. #
# ------------------------------------------------------------------ #
bench-h2:
name: H2 Bench (${{ matrix.protocol }}, ${{ matrix.conn_label }})
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- protocol: h2c
conn_label: low
connections_per_thread: 10
- protocol: h2c
conn_label: high
connections_per_thread: 100
- protocol: h2-tls
conn_label: low
connections_per_thread: 10
- protocol: h2-tls
conn_label: high
connections_per_thread: 100
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Filter protocol (workflow_dispatch)
id: filter
run: |
INPUT_PROTO="${{ github.event.inputs.h2_protocol || 'both' }}"
MATRIX_PROTO="${{ matrix.protocol }}"
if [[ "$INPUT_PROTO" != "both" ]] && [[ "$INPUT_PROTO" != "$MATRIX_PROTO" ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Install dependencies
if: steps.filter.outputs.skip != 'true'
run: |
sudo apt update
sudo apt install -y nghttp2-client libjsoncpp-dev uuid-dev libssl-dev libcurl4-openssl-dev
pip install uvicorn starlette hypercorn[h2]
h2load --version
- name: Download build artifact
if: steps.filter.outputs.skip != 'true'
uses: actions/download-artifact@v8
with:
name: bench-build-output
path: .
- name: Extract build artifact
if: steps.filter.outputs.skip != 'true'
run: tar -xzf bench-build-output.tar.gz
- name: Run H2 benchmarks
if: steps.filter.outputs.skip != 'true'
working-directory: ${{github.workspace}}/build-pages/benchmarks/scripted-servers
env:
# Resolve the shared libs (libdrogon.so.* / libtrantor.so.*) bundled by the build job.
LD_LIBRARY_PATH: ${{github.workspace}}/build-pages/benchmarks/shared-libs
run: |
EVENT_NAME=${GITHUB_EVENT_NAME:-}
REF=${GITHUB_REF:-}
WARMUP=5s
THREADS=1
REPEAT=1
CPT=${{ matrix.connections_per_thread }}
CONN=$((THREADS * CPT))
H2_STREAMS=10
# See the HTTP/1 job for the event -> duration/repeat rationale. schedule /
# workflow_dispatch take a 3-sample median at a shorter per-sample duration.
if [ "$EVENT_NAME" = "push" ] && [ "$REF" = "refs/heads/main" ]; then
DUR=30s
elif [ "$EVENT_NAME" = "workflow_dispatch" ] || [ "$EVENT_NAME" = "schedule" ]; then
REPEAT="${{ github.event.inputs.bench_repeat || '3' }}"
if [ -n "${{ github.event.inputs.bench_duration }}" ]; then
DUR="${{ github.event.inputs.bench_duration }}"
else
DUR=45s
fi
else
DUR=5s
WARMUP=2s
CONN=$((THREADS * CPT))
fi
echo "H2 bench: protocol=${{ matrix.protocol }} dur=${DUR}x${REPEAT} threads=$THREADS conn=$CONN streams=$H2_STREAMS"
./run_benchmarks.py \
--protocol "${{ matrix.protocol }}" \
--duration "$DUR" \
--threads "$THREADS" \
--warmup "$WARMUP" \
--connections "$CONN" \
--h2-streams "$H2_STREAMS" \
--repeat "$REPEAT"
- name: Upload H2 results
if: steps.filter.outputs.skip != 'true'
uses: actions/upload-artifact@v7
with:
name: h2-results-${{ matrix.protocol }}-${{ matrix.conn_label }}
path: build-pages/benchmarks/scripted-servers/results/benchmark_latest.json
retention-days: 3
- name: Upload H2 badge (h2-tls high-connections only)
if: steps.filter.outputs.skip != 'true' && matrix.protocol == 'h2-tls' && matrix.conn_label == 'high'
uses: actions/upload-artifact@v7
with:
name: h2-bench-badge
path: build-pages/benchmarks/scripted-servers/results/benchmark_badge.json
retention-days: 3
if-no-files-found: ignore
- name: Upload H2 badge (h2c high-connections only)
if: steps.filter.outputs.skip != 'true' && matrix.protocol == 'h2c' && matrix.conn_label == 'high'
uses: actions/upload-artifact@v7
with:
name: h2-bench-badge-h2c
path: build-pages/benchmarks/scripted-servers/results/benchmark_badge.json
retention-days: 3
if-no-files-found: ignore
# ------------------------------------------------------------------ #
# WebSocket bench: k6-based WebSocket scenarios. #
# ------------------------------------------------------------------ #
bench-ws:
name: WS Bench
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y libjsoncpp-dev uuid-dev libssl-dev libcurl4-openssl-dev
- name: Install k6
run: |
K6_VERSION=v0.55.0
curl -fsSL "https://github.com/grafana/k6/releases/download/${K6_VERSION}/k6-${K6_VERSION}-linux-amd64.tar.gz" \
| tar xzf - --strip-components=1 -C /usr/local/bin "k6-${K6_VERSION}-linux-amd64/k6"
k6 version
- name: Download build artifact
uses: actions/download-artifact@v8
with:
name: bench-build-output
path: .
- name: Extract build artifact
run: tar -xzf bench-build-output.tar.gz
- name: Run WebSocket benchmarks
working-directory: ${{github.workspace}}/build-pages/benchmarks/scripted-servers
env:
# Resolve the shared libs (libdrogon.so.* / libtrantor.so.*) bundled by the build job.
LD_LIBRARY_PATH: ${{github.workspace}}/build-pages/benchmarks/shared-libs
run: |
EVENT_NAME=${GITHUB_EVENT_NAME:-}
REF=${GITHUB_REF:-}
VUS=50
WARMUP=5s
if [ "$EVENT_NAME" = "push" ] && [ "$REF" = "refs/heads/main" ]; then
DUR=30s
elif [ "$EVENT_NAME" = "workflow_dispatch" ] || [ "$EVENT_NAME" = "schedule" ]; then
if [ -n "${{ github.event.inputs.bench_duration }}" ]; then
DUR="${{ github.event.inputs.bench_duration }}"
else
DUR=90s
fi
else
# pull_request and non-main pushes
DUR=5s
WARMUP=2s
VUS=10
fi
echo "WS bench: dur=$DUR vus=$VUS warmup=$WARMUP"
./run_ws_benchmarks.py \
--vus "$VUS" \
--duration "$DUR" \
--warmup "$WARMUP" \
--output ./ws-results
- name: Upload WS results
uses: actions/upload-artifact@v7
with:
name: ws-bench-results
path: build-pages/benchmarks/scripted-servers/ws-results/ws_benchmark_latest.json
retention-days: 3
- name: Upload WS badge
uses: actions/upload-artifact@v7
with:
name: ws-bench-badge
path: build-pages/benchmarks/scripted-servers/ws-results/ws_benchmark_badge.json
retention-days: 3
if-no-files-found: ignore
- name: Upload WS HTML report
uses: actions/upload-artifact@v7
with:
name: ws-bench-html
path: build-pages/benchmarks/scripted-servers/ws-results/ws_benchmark_*.html
retention-days: 3
if-no-files-found: ignore
# ------------------------------------------------------------------ #
# Client bench: aeronet HttpClient vs libcurl / drogon / beast, once #
# per protocol (http1, h2c, h2-tls). Split out into its own job (used #
# to run as a trailing step inside `bench` / `bench-h2`) so it runs #
# in parallel with the server-side matrices instead of stacking #
# after them on the same runner. #
# ------------------------------------------------------------------ #
bench-client:
name: Client Bench (${{ matrix.protocol }})
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- protocol: http1
artifact_suffix: ''
file_suffix: ''
- protocol: h2c
artifact_suffix: '-h2c'
file_suffix: '_h2c'
- protocol: h2-tls
artifact_suffix: '-h2-tls'
file_suffix: '_h2-tls'
steps:
- name: Checkout current main
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Filter protocol (workflow_dispatch)
id: filter
run: |
INPUT_PROTO="${{ github.event.inputs.h2_protocol || 'both' }}"
MATRIX_PROTO="${{ matrix.protocol }}"
if [[ "$MATRIX_PROTO" != "http1" ]] && [[ "$INPUT_PROTO" != "both" ]] && [[ "$INPUT_PROTO" != "$MATRIX_PROTO" ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Install runtime dependencies
if: steps.filter.outputs.skip != 'true'
run: |
sudo apt update
sudo apt install -y libjsoncpp-dev uuid-dev libssl-dev libcurl4-openssl-dev
- name: Download build artifact
if: steps.filter.outputs.skip != 'true'
uses: actions/download-artifact@v8
with:
name: bench-build-output
path: .
- name: Extract build artifact
if: steps.filter.outputs.skip != 'true'
run: tar -xzf bench-build-output.tar.gz
- name: Run client benchmarks
if: steps.filter.outputs.skip != 'true'
working-directory: ${{github.workspace}}/build-pages/benchmarks/scripted-clients
env:
# Resolve the shared libs (libdrogon.so.* / libtrantor.so.*) bundled by the build job.
LD_LIBRARY_PATH: ${{github.workspace}}/build-pages/benchmarks/shared-libs
run: |
EVENT_NAME=${GITHUB_EVENT_NAME:-}
REF=${GITHUB_REF:-}
WARMUP=5s
THREADS=1
if [ "$EVENT_NAME" = "push" ] && [ "$REF" = "refs/heads/main" ]; then
DUR=30s
elif [ "$EVENT_NAME" = "workflow_dispatch" ] || [ "$EVENT_NAME" = "schedule" ]; then
if [ -n "${{ github.event.inputs.bench_duration }}" ]; then
DUR="${{ github.event.inputs.bench_duration }}"
else
DUR=90s
fi
else
# pull_request and non-main pushes
DUR=5s
WARMUP=2s
fi
echo "Client bench: protocol=${{ matrix.protocol }} dur=$DUR threads=$THREADS warmup=$WARMUP"
./run_client_benchmarks.py \
--protocol "${{ matrix.protocol }}" \
--duration "$DUR" \
--threads "$THREADS" \
--warmup "$WARMUP" \
--output "$PWD/results"
- name: Upload client results
if: steps.filter.outputs.skip != 'true'
uses: actions/upload-artifact@v7
with:
name: client-bench-results${{ matrix.artifact_suffix }}
path: build-pages/benchmarks/scripted-clients/results/client_benchmark_latest${{ matrix.file_suffix }}.json
retention-days: 3
- name: Upload client badge
if: steps.filter.outputs.skip != 'true'
uses: actions/upload-artifact@v7
with:
name: client-bench-badge${{ matrix.artifact_suffix }}
path: build-pages/benchmarks/scripted-clients/results/client_benchmark_badge${{ matrix.file_suffix }}.json
retention-days: 3
if-no-files-found: ignore
# ------------------------------------------------------------------ #
# Publish: aggregate all benchmark results and upload the assembled #
# site as a GitHub Pages artifact (deployed by the `deploy` job). #
# ------------------------------------------------------------------ #
publish:
name: Publish Pages
runs-on: ubuntu-latest
needs: [bench, bench-h2, bench-ws, bench-client]
if: always() && !cancelled()
steps:
- name: Checkout current main
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Assemble site content from main
run: |
rm -rf /tmp/site
python3 -m pip install --disable-pip-version-check --quiet --requirement requirements-docs.txt
python3 -m mkdocs build --strict --site-dir /tmp/site
# Retain the source Markdown at its historical URLs. The generated site
# is served at the Pages root; benchmark dashboards are added below.
rsync -a docs/ /tmp/site/docs/
rsync -a resources/ /tmp/site/resources/
- name: Download all benchmark results
uses: actions/download-artifact@v8
with:
path: /tmp/bench-artifacts
- name: Render HTML for Pages
run: |
rm -fr /tmp/site/benchmarks
mkdir -p /tmp/site/benchmarks /tmp/site/benchmarks/h2
# --- HTTP/1.1 results ---
INPUTS=()
for label in low high; do
json="/tmp/bench-artifacts/bench-results-${label}/benchmark_latest.json"
if [[ -f "$json" ]]; then
INPUTS+=("--input" "$json")
cp "$json" "/tmp/site/benchmarks/benchmark_${label}.json"
fi
done
if [[ ${#INPUTS[@]} -gt 0 ]]; then
benchmarks/scripted-servers/render_benchmarks_html.py \
"${INPUTS[@]}" \
--output /tmp/site/benchmarks/index.html
else
echo "WARNING: No HTTP/1.1 benchmark results found; skipping render"
fi
# Copy HTTP/1.1 badge if available
if [[ -f /tmp/bench-artifacts/bench-badge/benchmark_badge.json ]]; then
cp /tmp/bench-artifacts/bench-badge/benchmark_badge.json /tmp/site/benchmarks/benchmark_badge.json
fi
# --- HTTP/2 results ---
for proto in h2c h2-tls; do
H2_INPUTS=()
for label in low high; do
json="/tmp/bench-artifacts/h2-results-${proto}-${label}/benchmark_latest.json"
if [[ -f "$json" ]]; then
H2_INPUTS+=("--input" "$json")
cp "$json" "/tmp/site/benchmarks/h2/benchmark_${proto}_${label}.json"
fi
done
if [[ ${#H2_INPUTS[@]} -gt 0 ]]; then
benchmarks/scripted-servers/render_benchmarks_html.py \
"${H2_INPUTS[@]}" \
--output "/tmp/site/benchmarks/h2/benchmarks_${proto}.html"
fi
done
# Copy H2 badges if available
if [[ -f /tmp/bench-artifacts/h2-bench-badge/benchmark_badge.json ]]; then
cp /tmp/bench-artifacts/h2-bench-badge/benchmark_badge.json /tmp/site/benchmarks/h2/benchmark_badge.json
fi
if [[ -f /tmp/bench-artifacts/h2-bench-badge-h2c/benchmark_badge.json ]]; then
cp /tmp/bench-artifacts/h2-bench-badge-h2c/benchmark_badge.json /tmp/site/benchmarks/h2/benchmark_badge_h2c.json
fi
# --- WebSocket results ---
mkdir -p /tmp/site/benchmarks/ws
WS_JSON="/tmp/bench-artifacts/ws-bench-results/ws_benchmark_latest.json"
if [[ -f "$WS_JSON" ]]; then
cp "$WS_JSON" /tmp/site/benchmarks/ws/ws_benchmark_latest.json
python3 benchmarks/scripted-servers/render_benchmarks_html.py \
--input "$WS_JSON" \
--output /tmp/site/benchmarks/ws/index.html
else
echo "WARNING: No WebSocket benchmark results found; skipping render"
fi
# Copy WS badge if available
if [[ -f /tmp/bench-artifacts/ws-bench-badge/ws_benchmark_badge.json ]]; then
cp /tmp/bench-artifacts/ws-bench-badge/ws_benchmark_badge.json /tmp/site/benchmarks/ws/ws_benchmark_badge.json
fi
# Copy WS HTML report if available
for f in /tmp/bench-artifacts/ws-bench-html/ws_benchmark_*.html; do
[[ -f "$f" ]] && cp "$f" /tmp/site/benchmarks/ws/ || true
done
# --- HTTP client results (aeronet HttpClient vs libcurl/drogon/beast) ---
# Rendered by the very same render_benchmarks_html.py: the client orchestrator emits the
# identical summary schema (clients on the 'servers' axis).
mkdir -p /tmp/site/benchmarks/clients
CLIENT_JSON="/tmp/bench-artifacts/client-bench-results/client_benchmark_latest.json"
if [[ -f "$CLIENT_JSON" ]]; then
cp "$CLIENT_JSON" /tmp/site/benchmarks/clients/client_benchmark_latest.json
benchmarks/scripted-servers/render_benchmarks_html.py \
--input "$CLIENT_JSON" \
--output /tmp/site/benchmarks/clients/index.html
else
echo "WARNING: No client benchmark results found; skipping render"
fi
# Copy client badge if available
if [[ -f /tmp/bench-artifacts/client-bench-badge/client_benchmark_badge.json ]]; then
cp /tmp/bench-artifacts/client-bench-badge/client_benchmark_badge.json /tmp/site/benchmarks/clients/client_benchmark_badge.json
fi
# --- HTTP/2 client results (h2c / h2-tls) ---
# Same renderer as the HTTP/1.1 client dashboard; one HTML page + badge per protocol.
for proto in h2c h2-tls; do
CJSON="/tmp/bench-artifacts/client-bench-results-${proto}/client_benchmark_latest_${proto}.json"
if [[ -f "$CJSON" ]]; then
cp "$CJSON" "/tmp/site/benchmarks/clients/client_benchmark_latest_${proto}.json"
benchmarks/scripted-servers/render_benchmarks_html.py \
--input "$CJSON" \
--output "/tmp/site/benchmarks/clients/client_benchmark_${proto}.html"
else
echo "WARNING: No ${proto} client benchmark results found; skipping render"
fi
CBADGE="/tmp/bench-artifacts/client-bench-badge-${proto}/client_benchmark_badge_${proto}.json"
if [[ -f "$CBADGE" ]]; then
cp "$CBADGE" "/tmp/site/benchmarks/clients/client_benchmark_badge_${proto}.json"
fi
done
- name: Upload Pages artifact
if: ${{ github.event_name != 'pull_request' }}
uses: actions/upload-pages-artifact@v5
with:
path: /tmp/site
# ------------------------------------------------------------------ #
# Deploy: publish the uploaded artifact to GitHub Pages natively via #
# actions/deploy-pages (no gh-pages branch). Skipped for PRs. #
# ------------------------------------------------------------------ #
deploy:
name: Deploy Pages
runs-on: ubuntu-latest
needs: publish
if: ${{ github.event_name != 'pull_request' }}
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
env:
# Silence the Node DEP0040 punycode deprecation warning emitted from within the action.
NODE_OPTIONS: --no-deprecation