Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ jobs:
# ``-m ''`` overrides the project default ``-m 'not slow'`` so the
# integration test still gates merges in CI. Coverage threshold only
# makes sense once, so attach it to the 3.13 leg.
- name: requirements-prod.lock drift check
# kayak-deploy installs the release venv from this hash-pinned export
# (PR #190: dependency resolution must not happen at deploy time), so
# it must stay in lockstep with uv.lock. Regenerate with:
# uv export --no-dev --no-emit-project --format requirements-txt > requirements-prod.lock
run: |
uv export --no-dev --no-emit-project --format requirements-txt -q > /tmp/req-prod.lock
diff -u requirements-prod.lock /tmp/req-prod.lock || {
echo "::error::requirements-prod.lock is out of date — regenerate with the uv export command above"
exit 1
}

- name: requirements-build.lock drift check
# kayak-deploy builds the wheel with --no-build-isolation from this
# hash-pinned PEP 517 backend (PR #190: the build backend must not be
# network-resolved or unpinned at deploy time), so it must track
# pyproject.toml's [build-system].requires. Regenerate with:
# python3 -c "import tomllib; print(chr(10).join(tomllib.load(open('pyproject.toml','rb'))['build-system']['requires']))" \
# | uv pip compile --generate-hashes - > requirements-build.lock
run: |
python3 -c "import tomllib; print(chr(10).join(tomllib.load(open('pyproject.toml','rb'))['build-system']['requires']))" \
| uv pip compile --generate-hashes -q - > /tmp/req-build.lock
diff -u requirements-build.lock /tmp/req-build.lock || {
echo "::error::requirements-build.lock is out of date vs pyproject.toml [build-system].requires — regenerate with the command above"
exit 1
}

- name: Run tests (with slow marker enabled)
run: uv run pytest -m '' --cov=kayak --cov-report=term-missing --cov-fail-under=75 -q

Expand Down
53 changes: 53 additions & 0 deletions deploy/deploy.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# /etc/kayak/deploy.env — kayak-deploy configuration (S7, Batch 4B).
# Copy to /etc/kayak/deploy.env, root-owned 0644 (no secrets here; the
# read-only repo credentials are SSH deploy keys in root's ~/.ssh).
#
# Both refs given to kayak-deploy must be FULL 40-hex commit SHAs reachable
# from these protected branches — that reachability plus required CI is the
# trust anchor (decision D2: digests, not signatures).

ENGINE_REPO=git@github.com:mousebrains/kayak_python.git
DATASET_REPO=git@github.com:mousebrains/kayak_data.git
#ENGINE_BRANCH=main
#DATASET_BRANCH=main

# REQUIRED when kayak-deploy runs as root (the normal mode — systemctl + the
# root config installer): the service account that owns the DB/docroot. Every
# DB/build step runs as this user via runuser so root never creates
# app-owned-resource WAL sidecars. (Omit only when running the deployer
# unprivileged, e.g. --stage-only.)
KAYAK_APP_USER=pat

# Units stopped before activation and restarted after (space-separated). MUST
# cover every DB/release-touching consumer — the cutover gate only inspects this
# list, so anything omitted can run the old checkout against the migrated DB
# undetected. The 4C runbook owns verifying the list is complete for the host
# (recap/heartbeat/config-drift also run engine code).
#KAYAK_UNITS="kayak-pipeline.timer kayak-backup-hourly.timer kayak-backup-weekly.timer kayak-decimate.timer kayak-status.timer kayak-fetch-osmb.timer kayak-editor-retention.timer kayak-audit-gauges.timer kayak-healthcheck.timer"

# Optional post-activation health probe; non-2xx rolls the release back.
#HEALTH_URL=https://levels.wkcc.org/status.json

# Release root (default /opt/kayak). The first run on a new host should be
# `kayak-deploy --engine-ref … --dataset-ref … --stage-only` to inspect the
# staged release before any activation.
#KAYAK_DEPLOY_ROOT=/opt/kayak

# Scratch base for staging — MUST be on real disk, never a tmpfs. /tmp on the
# production host is a ~1 GB tmpfs; the wheel/venv build plus the ~650 MB
# pre-activation DB backup overflow it (ENOSPC mid-deploy). Defaults to
# $KAYAK_DEPLOY_ROOT/.staging (real disk); only override if /opt is itself
# space-constrained and you have a larger real-disk volume elsewhere.
#KAYAK_DEPLOY_TMPDIR=/opt/kayak/.staging

# NOTE: any value with spaces MUST be quoted — this file is sourced by the
# shell, so e.g. KAYAK_HOST_UNITS="a.service b.service" (unquoted, the shell
# tries to RUN the 2nd word as a command and the deploy aborts at config load).

# Activation is REFUSED until the host is fully cut over to the paired-release
# layout — BOTH the serving config (nginx root, FPM open_basedir) AND the
# systemd CONSUMER units point at $KAYAK_DEPLOY_ROOT/current (kayak-deploy
# verifies the levels-running units' ExecStart before activating). The Batch
# 4C cutover runbook re-points all of them and sets this flag. Until then,
# stage-only works and scripts/deploy.sh remains the deploy path.
#SERVING_CUTOVER=yes
Loading