Skip to content

Nightly

Nightly #30

Workflow file for this run

name: Nightly
# Catches drift that PR-time CI misses:
# - link-check job: always runs, no secrets needed. HEAD-checks every CTA
# URL in every recipe. Catches marketing-site changes (e.g. the /signup
# 404 surfaced during Phase 0.5 hardening).
# - live-smoke job: runs only when FLASHALPHA_API_KEY is configured as a
# repo secret. Re-executes every recipe against the live API. Catches
# API drift, SDK regressions, and stale recipe code.
#
# To enable live-smoke: add FLASHALPHA_API_KEY as a repo secret at
# https://github.com/FlashAlpha-lab/flashalpha-examples/settings/secrets/actions
on:
schedule:
- cron: '17 3 * * *' # 03:17 UTC daily
workflow_dispatch:
jobs:
link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install dev deps
run: python -m pip install -e ".[dev]"
- name: Layer 1 link liveness (no auth required)
run: |
# Force-skip the 7-day cache so this nightly always hits the network.
rm -rf .cache
pytest tests/test_links_alive.py -v
live-smoke:
runs-on: ubuntu-latest
# `vars.` IS allowed in job-level `if:` (unlike `secrets.`). Setup:
# gh secret set FLASHALPHA_API_KEY < <(printf '%s' "$KEY")
# gh variable set LIVE_SMOKE_ENABLED --body true
# The variable acts as an explicit opt-in marker — the secret alone
# doesn't activate the job, preventing fingerprinting of secret presence.
if: ${{ vars.LIVE_SMOKE_ENABLED == 'true' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install dev deps + ipykernel
run: |
python -m pip install -e ".[dev]"
python -m pip install ipykernel
python -m ipykernel install --user --name python3
- name: Re-execute every recipe against live API (in-place)
env:
FLASHALPHA_API_KEY: ${{ secrets.FLASHALPHA_API_KEY }}
run: |
set -e
for nb in notebooks/tier-*/[0-9]*.ipynb; do
if [ -f "$nb" ]; then
echo "::group::papermill $nb"
python -m papermill "$nb" "$nb" --kernel python3
echo "::endgroup::"
fi
done
- name: Scrub auth headers (defensive)
run: |
for nb in notebooks/tier-*/[0-9]*.ipynb; do
if [ -f "$nb" ]; then
python -m scripts.scrub_outputs "$nb" || true
fi
done
- name: Report drift (don't fail — live-data recipes drift every run)
# Live-market recipes (anything calling fa.gex/fa.exposure_levels/etc
# without a locked `at=` timestamp) produce different outputs on every
# nightly run by design. Phase 6 will add deterministic backtest
# recipes that SHOULD fail on drift — handled then via a separate
# check that reads frontmatter.expected_artifacts.
#
# For now: log the diff stats and pass. The nightly run's purpose is
# "did papermill succeed against live API" — output drift is signal
# but not failure.
run: |
set +e
if git diff --quiet -- notebooks/tier-*/[0-9]*.ipynb; then
echo "No drift — recipe outputs match committed state (rare for live-data recipes)."
else
echo "::notice::Recipe output drift detected against live API."
echo "Files changed:"
git diff --stat -- notebooks/tier-*/[0-9]*.ipynb
echo ""
echo "This is expected for live-data recipes. The recipes ran successfully."
echo "If you want to refresh committed outputs, run locally:"
echo " python -m papermill notebooks/tier-a-hooks/01-gex-dashboard.ipynb \\"
echo " notebooks/tier-a-hooks/01-gex-dashboard.ipynb --kernel python3"
echo " python -m scripts.scrub_outputs notebooks/tier-a-hooks/01-gex-dashboard.ipynb"
echo " git commit"
fi