Skip to content

Nightly Report

Nightly Report #120

Workflow file for this run

name: Nightly Report
on:
schedule:
- cron: "0 22 * * *" # 22:00 UTC daily, ~4h after TileOPs nightly (18:00 UTC)
workflow_dispatch:
inputs:
run_id:
description: "TileOPs nightly run ID (leave empty for latest)"
required: false
force:
description: "Skip dedup check (force re-run even if run_id was already processed)"
type: boolean
default: false
permissions:
contents: write
concurrency:
group: nightly-report
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout report repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python dependencies
run: pip install anthropic matplotlib pyyaml numpy
# ── Find target nightly run ─────────────────────────────────────────
- name: Find target nightly run
id: run
run: |
if [ -n "${{ github.event.inputs.run_id }}" ]; then
RUN_ID="${{ github.event.inputs.run_id }}"
else
RUN_ID=$(gh run list \
--repo tile-ai/TileOPs \
--workflow nightly.yml \
--status completed \
--limit 1 \
--json databaseId \
-q '.[0].databaseId')
fi
if [ -z "${RUN_ID}" ]; then
echo "::error::No completed nightly run found."
exit 1
fi
echo "run_id=${RUN_ID}" >> "$GITHUB_OUTPUT"
echo "Using nightly run: ${RUN_ID}"
gh run view "${RUN_ID}" \
--repo tile-ai/TileOPs \
--json headSha,createdAt,conclusion \
-q '"sha=\(.headSha)\nconclusion=\(.conclusion)"' \
>> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ── Checkout gh-pages + dedup ───────────────────────────────────────
- name: Checkout existing gh-pages
run: |
git clone --depth 1 --branch gh-pages \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" \
_gh_pages 2>/dev/null \
|| { mkdir -p _gh_pages/nightly && echo "(gh-pages branch not found, starting fresh)"; }
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for duplicate run
id: dedup
run: |
if [ "${{ github.event.inputs.force }}" = "true" ]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "Force mode — skipping dedup check."
else
RUN_ID="${{ steps.run.outputs.run_id }}"
if grep -rl "^${RUN_ID}$" _gh_pages/nightly/ 2>/dev/null | grep -q "run_id.txt"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Run ${RUN_ID} already processed — skipping."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
fi
# ── Download artifacts ──────────────────────────────────────────────
- name: Download op_test artifact
if: steps.dedup.outputs.skip != 'true'
uses: dawidd6/action-download-artifact@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
repo: tile-ai/TileOPs
run_id: ${{ steps.run.outputs.run_id }}
name_is_regexp: true
name: tileops_op_test_.*
path: dl/op_test/
if_no_artifact_found: warn
- name: Download benchmark artifact
if: steps.dedup.outputs.skip != 'true'
uses: dawidd6/action-download-artifact@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
repo: tile-ai/TileOPs
run_id: ${{ steps.run.outputs.run_id }}
name_is_regexp: true
name: tileops_benchmark_.*
path: dl/benchmark/
if_no_artifact_found: warn
- name: Download perf history artifact
if: steps.dedup.outputs.skip != 'true'
uses: dawidd6/action-download-artifact@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
repo: tile-ai/TileOPs
run_id: ${{ steps.run.outputs.run_id }}
name: tileops_perf_history
path: dl/perf_history/
if_no_artifact_found: warn
# ── Prepare report directory ────────────────────────────────────────
- name: Prepare report directory
if: steps.dedup.outputs.skip != 'true'
id: prep
run: |
DATE=$(date +%Y%m%d_%H%M%S)
echo "date=${DATE}" >> "$GITHUB_OUTPUT"
SHA="${{ steps.run.outputs.sha }}"
echo "sha=${SHA}" >> "$GITHUB_OUTPUT"
REPORT_DIR="_gh_pages/nightly/${DATE}"
mkdir -p "${REPORT_DIR}"
echo "report_dir=${REPORT_DIR}" >> "$GITHUB_OUTPUT"
echo "${{ steps.run.outputs.run_id }}" > "${REPORT_DIR}/run_id.txt"
# Flatten artifact subdirectories (dawidd6 nests under artifact name)
find dl/op_test/ -mindepth 2 -type f -exec mv -t dl/op_test/ {} + 2>/dev/null || true
find dl/benchmark/ -mindepth 2 -type f -exec mv -t dl/benchmark/ {} + 2>/dev/null || true
cp dl/op_test/test_results.xml "${REPORT_DIR}/" 2>/dev/null || true
cp dl/op_test/tileops_op_test.log "${REPORT_DIR}/" 2>/dev/null || true
cp dl/op_test/nightly_report.md "${REPORT_DIR}/" 2>/dev/null || true
cp dl/benchmark/bench_results.xml "${REPORT_DIR}/" 2>/dev/null || true
cp dl/benchmark/tileops_benchmarks.log "${REPORT_DIR}/" 2>/dev/null || true
# ── Pipeline: parse → analyze → generate → email → readme → index ──
- name: Parse results
if: steps.dedup.outputs.skip != 'true'
run: |
python scripts/parse_results.py \
--test-xml "${{ steps.prep.outputs.report_dir }}/test_results.xml" \
--bench-xml "${{ steps.prep.outputs.report_dir }}/bench_results.xml" \
--perf-history dl/perf_history/perf_history.json \
--date "${{ steps.prep.outputs.date }}" \
--commit "${{ steps.prep.outputs.sha }}" \
--output "${{ steps.prep.outputs.report_dir }}/nightly_data.json"
# ── Performance history & charts (gh-data branch) ──────────────
- name: Checkout gh-data branch
if: steps.dedup.outputs.skip != 'true'
run: |
git clone --depth 1 --branch gh-data \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" \
_gh_data 2>/dev/null \
|| { mkdir -p _gh_data && echo '{"runs":[]}' > _gh_data/perf_history.json && echo "(gh-data branch not found, starting fresh)"; }
- name: Update perf history
if: steps.dedup.outputs.skip != 'true'
run: |
python scripts/update_perf_history.py \
--nightly-data "${{ steps.prep.outputs.report_dir }}/nightly_data.json" \
--perf-history _gh_data/perf_history.json
- name: Generate charts
if: steps.dedup.outputs.skip != 'true'
run: |
python scripts/generate_charts.py \
--config scripts/chart_config.yaml \
--perf-history _gh_data/perf_history.json \
--nightly-data "${{ steps.prep.outputs.report_dir }}/nightly_data.json" \
--output-dir "${{ steps.prep.outputs.report_dir }}/charts"
- name: Push perf history to gh-data
if: steps.dedup.outputs.skip != 'true'
run: |
cd _gh_data
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add perf_history.json
if git diff --staged --quiet; then
echo "perf_history.json unchanged."
else
git commit -m "data: update perf_history (${{ steps.prep.outputs.date }})"
git push origin gh-data
fi
# ── Analysis & report generation ───────────────────────────────
- name: Analyze with Claude
if: steps.dedup.outputs.skip != 'true'
continue-on-error: true
run: |
python scripts/analyze_report.py \
--nightly-data "${{ steps.prep.outputs.report_dir }}/nightly_data.json" \
--output "${{ steps.prep.outputs.report_dir }}/analysis.json"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
- name: Generate HTML report
if: steps.dedup.outputs.skip != 'true'
run: |
python scripts/generate_report.py \
--date "${{ steps.prep.outputs.date }}" \
--commit "${{ steps.prep.outputs.sha }}" \
--report-dir "${{ steps.prep.outputs.report_dir }}" \
--html-output "${{ steps.prep.outputs.report_dir }}/report.html" \
--nightly-data "${{ steps.prep.outputs.report_dir }}/nightly_data.json" \
--analysis-json "${{ steps.prep.outputs.report_dir }}/analysis.json"
- name: Send report email
if: steps.dedup.outputs.skip != 'true'
continue-on-error: true
run: |
python scripts/send_report_email.py \
--html-file "${{ steps.prep.outputs.report_dir }}/report.html" \
--subject "TileOPs Nightly Report ${{ steps.prep.outputs.date }}" \
--recipients-file scripts/email_recipients.txt
env:
MAIL_SMTP_SERVER: ${{ secrets.MAIL_SMTP_SERVER }}
MAIL_SMTP_PORT: ${{ secrets.MAIL_SMTP_PORT }}
MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }}
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
# ── Deploy to gh-pages ──────────────────────────────────────────────
- name: Copy charts to latest/
if: steps.dedup.outputs.skip != 'true'
run: |
rm -rf _gh_pages/latest/charts
if [ -d "${{ steps.prep.outputs.report_dir }}/charts" ]; then
mkdir -p _gh_pages/latest
cp -r "${{ steps.prep.outputs.report_dir }}/charts" _gh_pages/latest/charts
echo "Copied charts to _gh_pages/latest/charts/"
else
echo "No charts directory found — skipping."
fi
- name: Prune reports older than 14 days
if: steps.dedup.outputs.skip != 'true'
run: |
CUTOFF=$(date -d "14 days ago" +%Y%m%d)
for dir in _gh_pages/nightly/*/; do
dirname=$(basename "${dir}")
dir_date="${dirname:0:8}"
if [[ "${dir_date}" =~ ^[0-9]{8}$ ]] && [ "${dir_date}" -lt "${CUTOFF}" ]; then
echo "Removing old report: ${dirname}"
rm -rf "${dir}"
fi
done
- name: Generate index.html
if: steps.dedup.outputs.skip != 'true'
run: |
python scripts/gen_index.py \
--nightly-dir _gh_pages/nightly \
--output _gh_pages/nightly/index.html \
--repo-url "https://github.com/tile-ai/TileOPs"
- name: Push to gh-pages
if: steps.dedup.outputs.skip != 'true'
run: |
cd _gh_pages
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout --orphan new_gh_pages
git add -A
git commit -m "report: ${{ steps.prep.outputs.date }} (run ${{ steps.run.outputs.run_id }})"
git push --force \
"https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" \
HEAD:gh-pages