Skip to content

Merge pull request #8 from pip-install-python/sync/1.6.22-1.6.33 #12

Merge pull request #8 from pip-install-python/sync/1.6.22-1.6.33

Merge pull request #8 from pip-install-python/sync/1.6.22-1.6.33 #12

Workflow file for this run

name: CD
# Deploys llms.2plot.dev, then checks the live site.
#
# ======================= THE LIVE HALF IS DORMANT =========================
# Everything that touches a running host — the build-match wait and the whole
# `verify` job — is gated on the repository variable `SITE_URL`. While it is
# unset those steps skip with a notice and CD is effectively "CI plus an
# optional deploy trigger".
#
# This is not caution for its own sake. The template shipped
# SITE_URL=https://boilerplate.2plot.dev hard-coded, so an unguarded run here
# would poll ANOTHER SITE's /healthz waiting for a commit it will never
# serve, then battery it and report red — a fork failing CD on day one over a
# host it does not own. (Same class as the excalidraw finding: inherited
# workflows reference things that do not exist in the fork and deploy hosts
# that are not yours.) The guard is a recorded divergence — see
# DIVERGENCES.md — and it stays.
#
# STATUS 2026-08-26: the cutover HAPPENED. https://llms.2plot.dev serves this
# app (healthz `app: "llms"`, build == HEAD, verified by hand every round),
# so the remaining condition is met and the variable is simply not set yet.
#
# TO UNGUARD — one repository setting, no workflow edit:
# Settings → Secrets and variables → Actions → Variables →
# SITE_URL = https://llms.2plot.dev
# The build-match wait and the whole verify job come alive on the next push,
# and hand-verification stops being the only proof this repo has.
# ==========================================================================
#
# The deploy step POSTs to a Render deploy hook held in the
# RENDER_DEPLOY_HOOK_URL secret. Without that secret the step is skipped and
# the workflow goes straight to verification — useful when Render is already
# auto-deploying from GitHub on its own, and it means this repo doesn't fail
# CD on day one for a secret it was never given.
on:
push:
branches: [main]
workflow_dispatch:
inputs:
target_url:
description: Site to verify (skips the deploy when set to another host)
required: false
type: string
permissions:
contents: read
concurrency:
group: cd-production
cancel-in-progress: false
env:
PIP_DISABLE_PIP_VERSION_CHECK: "1"
# No hard-coded fallback, deliberately: an empty SITE_URL is the signal
# that no service exists yet, and every live step keys off it. A default
# here would silently re-point this workflow at somebody else's host.
SITE_URL: ${{ inputs.target_url || vars.SITE_URL }}
jobs:
test:
name: ci
uses: ./.github/workflows/ci.yml
deploy:
name: deploy to render
needs: [test]
runs-on: ubuntu-latest
# Long enough for the build-match wait below (up to 100 × 15s = 25
# minutes) and no longer. Without it the job inherits GitHub's six-hour
# default, which is how a platform that never comes back healthy holds
# the `cd-production` concurrency group all day. Sized for the WORST
# build, not the median: a floor bump busts the Docker dependency cache
# by design, so this pipeline's most important deploy is also Render's
# slowest — dash-email's wait timed out on exactly that build class
# (2026-08-23) with the old 60 × 15s window.
timeout-minutes: 30
environment:
name: production
# Restore `url: ${{ inputs.target_url || vars.SITE_URL }}` when
# SITE_URL is set. Omitted while dormant rather than evaluated to an
# empty string — the deployment panel simply shows no link rather than
# this repo's runs hinging on how GitHub treats a blank environment
# URL.
outputs:
deployed: ${{ steps.hook.outputs.deployed }}
steps:
- name: Say plainly that the live half is dormant
if: env.SITE_URL == ''
run: |
echo "::warning::The SITE_URL repo variable is unset, so the build-match wait and the live batteries are SKIPPED — this run certifies nothing about production. The site is live at https://llms.2plot.dev; set SITE_URL to it (Settings → Variables) to bring the live half up. See this workflow's header."
- name: Trigger the Render deploy hook
id: hook
env:
HOOK: ${{ secrets.RENDER_DEPLOY_HOOK_URL }}
run: |
if [ -z "$HOOK" ]; then
# ::warning, not ::notice — dash-email's finding (2026-08-23):
# with no hook AND a push Render's autoDeploy happened to skip,
# nothing deployed at all and the build-match wait below timed
# out on a deploy nobody had started. The wait refusing was
# honest; the quiet notice was why the cause took a run to see.
echo "::warning::RENDER_DEPLOY_HOOK_URL is not set — this push deploys only if render.yaml autoDeploy fires. The build-match wait below still holds for THIS commit either way."
echo "deployed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
curl -fsS -X POST "$HOOK" > /dev/null
echo "deployed=true" >> "$GITHUB_OUTPUT"
- name: Wait for THIS build to serve traffic
# Skips while SITE_URL is unset — see the header. Without this guard
# the loop below polls a host this repo does not own for twenty-five
# minutes and then fails the run.
if: env.SITE_URL != ''
# NOT gated on the hook step: with RENDER_DEPLOY_HOOK_URL unset,
# Render's own autoDeploy ships the commit minutes after the push —
# and the old shape skipped this wait and ran the battery three
# seconds later against the PREVIOUS release. That defect was
# invisible for the workflow's whole life (the old build always
# already passed the old battery) until a new surface made the race
# lose — found on muicharts, 2026-08-21, fleet-wide class.
run: |
# A bare 200 proves nothing about WHICH build answered: /healthz
# now reports the running instance's commit (RENDER_GIT_COMMIT),
# and this loop holds until it equals the SHA that triggered this
# run. Fallback: a build that predates the field gets the old
# sustained-health wait, with a warning naming what it can't tell
# — that fallback fires exactly once, on the deploy carrying the
# field for the first time.
want="${GITHUB_SHA}"
matched=0
ok=0
for _ in $(seq 1 100); do
body="$(curl -fsS "$SITE_URL/healthz" 2>/dev/null || true)"
if [ -n "$body" ]; then
build="$(printf '%s' "$body" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("build",""))' 2>/dev/null || true)"
if [ "$build" = "$want" ]; then
matched=$((matched + 1))
[ "$matched" -ge 3 ] && break
elif [ -z "$build" ]; then
ok=$((ok + 1))
fi
fi
sleep 15
done
if [ "$matched" -ge 3 ]; then
echo "::notice::live /healthz reports build $want — verifying the artifact this run shipped."
elif [ "$ok" -ge 5 ]; then
echo "::warning::live /healthz predates the build field — cannot prove WHICH build is serving; verified sustained health only. This warning should appear exactly once."
else
echo "::error::$SITE_URL never served this run's build ($want) and never became reliably healthy"
exit 1
fi
verify:
name: verify the live site
needs: [deploy]
# The whole job is dormant until SITE_URL names a real service.
# `vars.SITE_URL` rather than `env.SITE_URL` because job-level `if` is
# evaluated before the job's env context exists.
# 'skipped' as well as 'cancelled' (muicharts' guard, 2026-08-23): a
# deploy job that never ran leaves no build-match wait behind it, so a
# verify that fires anyway certifies whatever build happens to be
# serving and turns one cause into two red jobs.
if: >-
always() && needs.deploy.result != 'cancelled'
&& needs.deploy.result != 'skipped'
&& (inputs.target_url || vars.SITE_URL) != ''
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
# The fleet Python — tests/test_python_version.py pins this literal
# against the Dockerfile's FROM tag.
python-version: "3.14"
# The network battery first: it is the same script, with the same check
# names, that CI ran against the container this deploy shipped. A name
# that passed in CI and fails here isolates the fault to the deploy.
- name: Network smoke battery
run: python scripts/network_smoke.py --base-url "$SITE_URL"
# Then the satellite-specific checks the battery does not make: every
# canonical, every crawler body, and every peer llms.txt in the
# directory actually resolving.
- name: Smoke-test the deployment
run: python scripts/smoke_live.py "$SITE_URL"
- name: Report
if: failure()
run: |
echo "::error::Live verification failed for $SITE_URL. Every failure these check for is silent in production: a site identity that fell back to a framework default, a stale dash-improve-my-llms artifact, a canonical on the wrong host, a page serving the JavaScript stub, a missing network directory, and dead peer llms.txt links."