Skip to content

Benchmark Release

Benchmark Release #150

name: Benchmark Release
on:
workflow_dispatch:
inputs:
benchmark_uuid:
description: "Benchmark UUID to release (optional; leave empty to auto-discover)."
required: false
default: ""
run_id:
description: "Run ID to release (optional; leave empty to auto-discover)."
required: false
default: ""
report_budget_hours:
description: "Override report budget hours (optional)."
required: false
default: ""
exclude_fuzzers:
description: "Comma-separated fuzzers to exclude (optional)."
required: false
default: ""
force_reanalyze:
description: "Re-run analysis and re-upload artifacts even if release tag exists."
required: false
default: "false"
schedule:
- cron: "0 * * * *"
permissions:
actions: write
contents: write
jobs:
discover:
runs-on: ubuntu-latest
outputs:
has_runs: ${{ steps.discover.outputs.has_runs }}
matrix: ${{ steps.discover.outputs.matrix }}
env:
AWS_REGION: ${{ secrets.AWS_REGION != '' && secrets.AWS_REGION || 'us-east-1' }}
SCFUZZBENCH_BUCKET: ${{ secrets.SCFUZZBENCH_BUCKET }}
steps:
- name: Ensure AWS credentials are configured
run: |
if [ -z "${{ secrets.AWS_ACCESS_KEY_ID }}" ] || [ -z "${{ secrets.AWS_SECRET_ACCESS_KEY }}" ]; then
echo "Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY secrets." >&2
exit 1
fi
if [ -z "${AWS_REGION}" ]; then
echo "Set AWS_REGION secret." >&2
exit 1
fi
if [ -z "${SCFUZZBENCH_BUCKET}" ]; then
echo "Set SCFUZZBENCH_BUCKET secret." >&2
exit 1
fi
- name: Configure AWS credentials (keys)
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Discover runs
id: discover
env:
BENCHMARK_UUID: ${{ inputs.benchmark_uuid }}
RUN_ID: ${{ inputs.run_id }}
REPORT_BUDGET_HOURS: ${{ inputs.report_budget_hours }}
GRACE_SECONDS: "3600"
run: |
set -euo pipefail
if [[ -n "${BENCHMARK_UUID}" || -n "${RUN_ID}" ]]; then
if [[ -z "${BENCHMARK_UUID}" || -z "${RUN_ID}" ]]; then
echo "Both benchmark_uuid and run_id are required when manually specifying a run." >&2
exit 1
fi
matrix=$(python3 - <<'PY'
import json
import os
entry = {
"benchmark_uuid": os.environ["BENCHMARK_UUID"],
"run_id": os.environ["RUN_ID"],
}
report_budget = os.environ.get("REPORT_BUDGET_HOURS", "").strip()
if report_budget:
entry["report_budget_hours"] = report_budget
print(json.dumps({"include": [entry]}, separators=(",", ":")))
PY
)
echo "has_runs=true" >> "${GITHUB_OUTPUT}"
echo "matrix=${matrix}" >> "${GITHUB_OUTPUT}"
exit 0
fi
matrix=$(python3 - <<'PY'
import json
import os
import subprocess
import time
bucket = os.environ["SCFUZZBENCH_BUCKET"]
grace = int(os.environ.get("GRACE_SECONDS", "3600"))
now = int(time.time())
def aws(*args: str) -> str:
return subprocess.check_output(["aws", *args], text=True)
def list_prefixes(prefix: str) -> list[str]:
data = json.loads(
aws(
"s3api",
"list-objects-v2",
"--bucket",
bucket,
"--prefix",
prefix,
"--delimiter",
"/",
"--output",
"json",
)
)
return [entry["Prefix"] for entry in data.get("CommonPrefixes", [])]
runs: list[dict] = []
for run_prefix in list_prefixes("logs/"):
parts = run_prefix.strip("/").split("/")
if len(parts) < 2:
continue
run_id = parts[1]
for bench_prefix in list_prefixes(f"logs/{run_id}/"):
bench_parts = bench_prefix.strip("/").split("/")
if len(bench_parts) < 3:
continue
benchmark_uuid = bench_parts[2]
manifest_key = f"logs/{run_id}/{benchmark_uuid}/manifest.json"
try:
manifest_raw = aws("s3", "cp", f"s3://{bucket}/{manifest_key}", "-")
manifest = json.loads(manifest_raw)
except Exception:
continue
try:
timeout_hours = float(manifest.get("timeout_hours", 24))
except Exception:
timeout_hours = 24.0
try:
run_start = int(run_id)
except ValueError:
continue
if now >= run_start + int(timeout_hours * 3600) + grace:
runs.append(
{
"benchmark_uuid": benchmark_uuid,
"run_id": run_id,
"timeout_hours": timeout_hours,
}
)
print(json.dumps({"include": runs}, separators=(",", ":")))
PY
)
if [[ "${matrix}" == '{"include":[]}' ]]; then
echo "has_runs=false" >> "${GITHUB_OUTPUT}"
else
echo "has_runs=true" >> "${GITHUB_OUTPUT}"
fi
echo "matrix=${matrix}" >> "${GITHUB_OUTPUT}"
release:
needs: discover
if: ${{ needs.discover.outputs.has_runs == 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.discover.outputs.matrix) }}
env:
AWS_REGION: ${{ secrets.AWS_REGION != '' && secrets.AWS_REGION || 'us-east-1' }}
SCFUZZBENCH_BUCKET: ${{ secrets.SCFUZZBENCH_BUCKET }}
steps:
- name: Checkout (tarball)
timeout-minutes: 5
env:
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
url="https://codeload.github.com/${REPO}/tar.gz/${SHA}"
curl -fsSL "${url}" -o repo.tar.gz
topdir="$(tar -tzf repo.tar.gz | sed -n '1p' | cut -d/ -f1)"
tar -xzf repo.tar.gz
shopt -s dotglob
mv "${topdir}"/* .
rm -rf "${topdir}" repo.tar.gz
- name: Configure AWS credentials (keys)
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Check for existing release
id: release_check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
tag="scfuzzbench-${{ matrix.benchmark_uuid }}-${{ matrix.run_id }}"
if gh release view "${tag}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
echo "exists=true" >> "${GITHUB_OUTPUT}"
else
echo "exists=false" >> "${GITHUB_OUTPUT}"
fi
echo "tag=${tag}" >> "${GITHUB_OUTPUT}"
- name: Setup Python
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: Install analysis dependencies
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
run: make analysis-venv
- name: Fetch manifest
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
id: manifest
env:
REPORT_BUDGET_HOURS: ${{ matrix.report_budget_hours }}
run: |
set -euo pipefail
dest_root="${GITHUB_WORKSPACE}/analysis-artifacts/${{ matrix.run_id }}"
analysis_dir="${dest_root}/analysis"
mkdir -p "${analysis_dir}"
logs_manifest_key="logs/${{ matrix.run_id }}/${{ matrix.benchmark_uuid }}/manifest.json"
runs_manifest_key="runs/${{ matrix.run_id }}/${{ matrix.benchmark_uuid }}/manifest.json"
if aws s3 cp "s3://${SCFUZZBENCH_BUCKET}/${logs_manifest_key}" "${analysis_dir}/manifest.json"; then
echo "Fetched manifest from ${logs_manifest_key}"
else
aws s3 cp "s3://${SCFUZZBENCH_BUCKET}/${runs_manifest_key}" "${analysis_dir}/manifest.json"
echo "Fetched manifest from ${runs_manifest_key}"
fi
timeout_hours="${{ matrix.timeout_hours }}"
if [[ -z "${timeout_hours}" || "${timeout_hours}" == "null" ]]; then
timeout_hours=$(ANALYSIS_DIR="${analysis_dir}" python3 - <<'PY'
import json
import os
from pathlib import Path
manifest_path = Path(os.environ["ANALYSIS_DIR"]) / "manifest.json"
manifest = json.loads(manifest_path.read_text())
value = manifest.get("timeout_hours", 24)
print(value)
PY
)
fi
report_budget="${REPORT_BUDGET_HOURS}"
if [[ -z "${report_budget}" ]]; then
report_budget="${timeout_hours}"
fi
{
echo "timeout_hours=${timeout_hours}"
echo "report_budget_hours=${report_budget}"
echo "dest_root=${dest_root}"
echo "analysis_dir=${analysis_dir}"
} >> "${GITHUB_OUTPUT}"
- name: Run analysis
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
env:
BUCKET: ${{ env.SCFUZZBENCH_BUCKET }}
RUN_ID: ${{ matrix.run_id }}
BENCHMARK_UUID: ${{ matrix.benchmark_uuid }}
ARTIFACT_CATEGORY: both
DEST: ${{ steps.manifest.outputs.dest_root }}
DURATION_HOURS: ${{ steps.manifest.outputs.report_budget_hours }}
REPORT_BUDGET: ${{ steps.manifest.outputs.report_budget_hours }}
EXCLUDE_FUZZERS: ${{ inputs.exclude_fuzzers }}
run: |
set -euo pipefail
make results-analyze-all
- name: Collect analysis artifacts
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
run: |
set -euo pipefail
dest_root="${{ steps.manifest.outputs.dest_root }}"
analysis_dir="${{ steps.manifest.outputs.analysis_dir }}"
# Website + release notes expect these at the top-level.
cp "${dest_root}/data/REPORT.md" "${analysis_dir}/REPORT.md"
cp "${dest_root}/images/bugs_over_time.png" "${analysis_dir}/bugs_over_time.png"
cp "${dest_root}/images/time_to_k.png" "${analysis_dir}/time_to_k.png"
cp "${dest_root}/images/final_distribution.png" "${analysis_dir}/final_distribution.png"
cp "${dest_root}/images/plateau_and_late_share.png" "${analysis_dir}/plateau_and_late_share.png"
cp "${dest_root}/images/invariant_overlap_upset.png" "${analysis_dir}/invariant_overlap_upset.png"
cp "${dest_root}/images/cpu_usage_over_time.png" "${analysis_dir}/cpu_usage_over_time.png"
cp "${dest_root}/images/memory_usage_over_time.png" "${analysis_dir}/memory_usage_over_time.png"
for optional_chart in \
tx_per_second_over_time.png \
gas_per_second_over_time.png \
seq_per_second_over_time.png \
coverage_proxy_over_time.png \
corpus_size_over_time.png \
favored_items_over_time.png \
failure_rate_over_time.png \
progress_metrics_levels.png \
progress_metrics_availability.png
do
if [[ -f "${dest_root}/images/${optional_chart}" ]]; then
cp "${dest_root}/images/${optional_chart}" "${analysis_dir}/${optional_chart}"
fi
done
cp "${dest_root}/data/broken_invariants.md" "${analysis_dir}/broken_invariants.md"
cp "${dest_root}/data/broken_invariants.csv" "${analysis_dir}/broken_invariants.csv"
cp "${dest_root}/data/runner_resource_usage.md" "${analysis_dir}/runner_resource_usage.md"
cp "${dest_root}/data/runner_resource_summary.csv" "${analysis_dir}/runner_resource_summary.csv"
cp "${dest_root}/data/runner_resource_timeseries.csv" "${analysis_dir}/runner_resource_timeseries.csv"
if [[ -f "${dest_root}/data/throughput_summary.csv" ]]; then
cp "${dest_root}/data/throughput_summary.csv" "${analysis_dir}/throughput_summary.csv"
fi
if [[ -f "${dest_root}/data/progress_metrics_summary.csv" ]]; then
cp "${dest_root}/data/progress_metrics_summary.csv" "${analysis_dir}/progress_metrics_summary.csv"
fi
for differential_csv in \
differential_coverage_summary.csv \
differential_coverage_relscores.csv \
differential_coverage_relcov.csv
do
if [[ -f "${dest_root}/data/${differential_csv}" ]]; then
cp "${dest_root}/data/${differential_csv}" "${analysis_dir}/${differential_csv}"
fi
done
if [[ -f "${dest_root}/data/showmap_campaign_manifest.json" ]]; then
cp "${dest_root}/data/showmap_campaign_manifest.json" "${analysis_dir}/showmap_campaign_manifest.json"
fi
if [[ -d "${dest_root}/data/showmap_campaigns" ]]; then
rm -rf "${analysis_dir}/showmap_campaigns"
cp -R "${dest_root}/data/showmap_campaigns" "${analysis_dir}/showmap_campaigns"
fi
# Optional: include raw CSV outputs in the analysis bundle for debugging.
mkdir -p "${analysis_dir}/data"
cp "${dest_root}/data/"*.csv "${analysis_dir}/data/" || true
- name: Build release bundles
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
id: bundles
run: |
set -euo pipefail
dest_root="${{ steps.manifest.outputs.dest_root }}"
analysis_dir="${{ steps.manifest.outputs.analysis_dir }}"
release_dir="${dest_root}/release"
mkdir -p "${release_dir}"
(cd "${analysis_dir}" && zip -r "${release_dir}/analysis.zip" .)
if compgen -G "${dest_root}/logs/zips/*.zip" > /dev/null; then
(cd "${dest_root}/logs/zips" && zip -r "${release_dir}/logs.zip" .)
echo "has_logs_zip=true" >> "${GITHUB_OUTPUT}"
else
echo "has_logs_zip=false" >> "${GITHUB_OUTPUT}"
fi
if compgen -G "${dest_root}/corpus/zips/*.zip" > /dev/null; then
(cd "${dest_root}/corpus/zips" && zip -r "${release_dir}/corpus.zip" .)
echo "has_corpus_zip=true" >> "${GITHUB_OUTPUT}"
else
echo "has_corpus_zip=false" >> "${GITHUB_OUTPUT}"
fi
echo "release_dir=${release_dir}" >> "${GITHUB_OUTPUT}"
- name: Upload analysis artifacts to S3
if: ${{ steps.release_check.outputs.exists != 'true' || inputs.force_reanalyze == 'true' }}
run: |
set -euo pipefail
analysis_dir="${{ steps.manifest.outputs.analysis_dir }}"
release_dir="${{ steps.bundles.outputs.release_dir }}"
s3_prefix="analysis/${{ matrix.benchmark_uuid }}/${{ matrix.run_id }}"
aws s3 cp "${analysis_dir}/REPORT.md" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/REPORT.md" --content-type text/markdown
aws s3 cp "${analysis_dir}/bugs_over_time.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/bugs_over_time.png" --content-type image/png
aws s3 cp "${analysis_dir}/time_to_k.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/time_to_k.png" --content-type image/png
aws s3 cp "${analysis_dir}/final_distribution.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/final_distribution.png" --content-type image/png
aws s3 cp "${analysis_dir}/plateau_and_late_share.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/plateau_and_late_share.png" --content-type image/png
aws s3 cp "${analysis_dir}/invariant_overlap_upset.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/invariant_overlap_upset.png" --content-type image/png
aws s3 cp "${analysis_dir}/cpu_usage_over_time.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/cpu_usage_over_time.png" --content-type image/png
aws s3 cp "${analysis_dir}/memory_usage_over_time.png" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/memory_usage_over_time.png" --content-type image/png
for optional_chart in \
tx_per_second_over_time.png \
gas_per_second_over_time.png \
seq_per_second_over_time.png \
coverage_proxy_over_time.png \
corpus_size_over_time.png \
favored_items_over_time.png \
failure_rate_over_time.png \
progress_metrics_levels.png \
progress_metrics_availability.png
do
if [[ -f "${analysis_dir}/${optional_chart}" ]]; then
aws s3 cp "${analysis_dir}/${optional_chart}" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/${optional_chart}" --content-type image/png
fi
done
aws s3 cp "${analysis_dir}/broken_invariants.md" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/broken_invariants.md" --content-type text/markdown
aws s3 cp "${analysis_dir}/broken_invariants.csv" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/broken_invariants.csv" --content-type text/csv
aws s3 cp "${analysis_dir}/runner_resource_usage.md" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/runner_resource_usage.md" --content-type text/markdown
aws s3 cp "${analysis_dir}/runner_resource_summary.csv" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/runner_resource_summary.csv" --content-type text/csv
aws s3 cp "${analysis_dir}/runner_resource_timeseries.csv" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/runner_resource_timeseries.csv" --content-type text/csv
if [[ -f "${analysis_dir}/throughput_summary.csv" ]]; then
aws s3 cp "${analysis_dir}/throughput_summary.csv" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/throughput_summary.csv" --content-type text/csv
fi
if [[ -f "${analysis_dir}/progress_metrics_summary.csv" ]]; then
aws s3 cp "${analysis_dir}/progress_metrics_summary.csv" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/progress_metrics_summary.csv" --content-type text/csv
fi
for differential_csv in \
differential_coverage_summary.csv \
differential_coverage_relscores.csv \
differential_coverage_relcov.csv
do
if [[ -f "${analysis_dir}/${differential_csv}" ]]; then
aws s3 cp "${analysis_dir}/${differential_csv}" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/${differential_csv}" --content-type text/csv
fi
done
if [[ -f "${analysis_dir}/showmap_campaign_manifest.json" ]]; then
aws s3 cp "${analysis_dir}/showmap_campaign_manifest.json" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/showmap_campaign_manifest.json" --content-type application/json
fi
if [[ -d "${analysis_dir}/showmap_campaigns" ]]; then
aws s3 cp "${analysis_dir}/showmap_campaigns" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/showmap_campaigns" --recursive
fi
aws s3 cp "${analysis_dir}/manifest.json" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/manifest.json" --content-type application/json
aws s3 cp "${release_dir}/analysis.zip" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/bundles/analysis.zip" --content-type application/zip
if [[ "${{ steps.bundles.outputs.has_logs_zip }}" == "true" ]]; then
aws s3 cp "${release_dir}/logs.zip" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/bundles/logs.zip" --content-type application/zip
fi
if [[ "${{ steps.bundles.outputs.has_corpus_zip }}" == "true" ]]; then
aws s3 cp "${release_dir}/corpus.zip" "s3://${SCFUZZBENCH_BUCKET}/${s3_prefix}/bundles/corpus.zip" --content-type application/zip
fi
- name: Compose release body
if: ${{ steps.release_check.outputs.exists != 'true' }}
id: body
run: |
set -euo pipefail
analysis_dir="${{ steps.manifest.outputs.analysis_dir }}"
report_path="${analysis_dir}/REPORT.md"
release_body="${analysis_dir}/RELEASE.md"
cp "${report_path}" "${release_body}"
base_url="https://${SCFUZZBENCH_BUCKET}.s3.${AWS_REGION}.amazonaws.com"
analysis_base="${base_url}/analysis/${{ matrix.benchmark_uuid }}/${{ matrix.run_id }}"
logs_base="${base_url}/logs/${{ matrix.run_id }}/${{ matrix.benchmark_uuid }}"
corpus_base="${base_url}/corpus/${{ matrix.run_id }}/${{ matrix.benchmark_uuid }}"
bundles_base="${analysis_base}/bundles"
{
echo ""
echo "## Artifacts"
echo ""
echo "Charts:"
echo "![Bugs Over Time](${analysis_base}/bugs_over_time.png)"
echo "![Time To K](${analysis_base}/time_to_k.png)"
echo "![Final Distribution](${analysis_base}/final_distribution.png)"
echo "![Plateau And Late Share](${analysis_base}/plateau_and_late_share.png)"
for optional_chart in \
tx_per_second_over_time.png \
gas_per_second_over_time.png \
seq_per_second_over_time.png \
coverage_proxy_over_time.png \
corpus_size_over_time.png \
favored_items_over_time.png \
failure_rate_over_time.png \
progress_metrics_levels.png \
progress_metrics_availability.png
do
if [[ -f "${analysis_dir}/${optional_chart}" ]]; then
chart_label="${optional_chart%.png}"
chart_label="${chart_label//_/ }"
echo "![${chart_label^}](${analysis_base}/${optional_chart})"
fi
done
echo "![Invariant Overlap (UpSet)](${analysis_base}/invariant_overlap_upset.png)"
echo "![CPU Usage Over Time](${analysis_base}/cpu_usage_over_time.png)"
echo "![Memory Usage Over Time](${analysis_base}/memory_usage_over_time.png)"
echo ""
echo "Downloads:"
echo "- Analysis bundle: ${bundles_base}/analysis.zip"
echo "- Broken invariants (Markdown): ${analysis_base}/broken_invariants.md"
echo "- Broken invariants (CSV): ${analysis_base}/broken_invariants.csv"
if [[ -f "${analysis_dir}/throughput_summary.csv" ]]; then
echo "- Throughput summary (CSV): ${analysis_base}/throughput_summary.csv"
fi
if [[ -f "${analysis_dir}/progress_metrics_summary.csv" ]]; then
echo "- Progress metrics summary (CSV): ${analysis_base}/progress_metrics_summary.csv"
fi
if [[ -f "${analysis_dir}/differential_coverage_summary.csv" ]]; then
echo "- Differential coverage summary (CSV): ${analysis_base}/differential_coverage_summary.csv"
fi
if [[ -f "${analysis_dir}/differential_coverage_relscores.csv" ]]; then
echo "- Differential coverage relscores (CSV): ${analysis_base}/differential_coverage_relscores.csv"
fi
if [[ -f "${analysis_dir}/differential_coverage_relcov.csv" ]]; then
echo "- Differential coverage relcov (CSV): ${analysis_base}/differential_coverage_relcov.csv"
fi
if [[ -f "${analysis_dir}/showmap_campaign_manifest.json" ]]; then
echo "- Showmap campaign manifest: ${analysis_base}/showmap_campaign_manifest.json"
fi
if [[ -d "${analysis_dir}/showmap_campaigns" ]]; then
echo "- Showmap campaigns prefix: ${analysis_base}/showmap_campaigns/"
fi
echo "- Runner resource usage (Markdown): ${analysis_base}/runner_resource_usage.md"
echo "- Runner resource summary (CSV): ${analysis_base}/runner_resource_summary.csv"
echo "- Runner resource timeseries (CSV): ${analysis_base}/runner_resource_timeseries.csv"
if [[ "${{ steps.bundles.outputs.has_logs_zip }}" == "true" ]]; then
echo "- Logs bundle: ${bundles_base}/logs.zip"
fi
if [[ "${{ steps.bundles.outputs.has_corpus_zip }}" == "true" ]]; then
echo "- Corpus bundle: ${bundles_base}/corpus.zip"
fi
echo "- Raw logs prefix: ${logs_base}/"
if [[ "${{ steps.bundles.outputs.has_corpus_zip }}" == "true" ]]; then
echo "- Raw corpus prefix: ${corpus_base}/"
fi
} >> "${release_body}"
echo "release_body=${release_body}" >> "${GITHUB_OUTPUT}"
- name: Create GitHub release
if: ${{ steps.release_check.outputs.exists != 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
tag="${{ steps.release_check.outputs.tag }}"
gh release create "${tag}" --repo "${GITHUB_REPOSITORY}" --title "${tag}" --notes-file "${{ steps.body.outputs.release_body }}"
refresh_docs:
needs: [discover, release]
if: ${{ needs.discover.outputs.has_runs == 'true' && needs.release.result == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Trigger docs refresh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
gh workflow run "Docs Site" --repo "${GITHUB_REPOSITORY}" --ref main