job-hunter: build-triggers-deploy model; CronJob :latest + docs

CI now drives the Deployment rollout (kubectl set image to the build SHA in
.woodpecker.yml), so the stack moves to image_tag = "latest": the Deployment
runs whatever CI last set (image ignore_changes keeps TF from fighting it),
and the CronJob uses :latest + imagePullPolicy=Always (fresh pod each weekly
run). Keel stays enrolled in parallel as a redundant net.

Docs: rewrite the runbook "Deploying" section for build-triggers-deploy;
record the reversal of decision #12 in the auto-upgrade design doc (owned
apps drive their own rollout, Keel parallel — upstream stays Keel-only); add
the owned-app deploy model to infra/.claude/CLAUDE.md CI/CD section.

[ci skip] — applied locally (stack-scoped); avoids a broad CI auto-apply.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-02 20:24:50 +00:00
parent 052c776eba
commit fe8db19aaf
5 changed files with 82 additions and 13 deletions

View file

@ -5,8 +5,10 @@
#
# The alembic-migrate init container mirrors the Deployment so the CronJob can
# never run a refresh against an un-migrated DB (snapshot inserts would fail).
# Image is :latest (Keel-managed for the Deployment); the CronJob pulls the
# current latest at each run, so it always executes the newest code.
# Image is local.image (:latest via image_tag) with imagePullPolicy=Always: a
# CronJob spawns a fresh pod each run, so Always pull = it always executes the
# newest built code. The Deployment is rolled by CI (kubectl set image to the
# build SHA); the CronJob needs no rollout Always pull covers it.
resource "kubernetes_cron_job_v1" "job_hunter_refresh" {
metadata {
name = "job-hunter-refresh"
@ -40,9 +42,10 @@ resource "kubernetes_cron_job_v1" "job_hunter_refresh" {
}
init_container {
name = "alembic-migrate"
image = local.image
command = ["python", "-m", "job_hunter", "migrate"]
name = "alembic-migrate"
image = local.image
image_pull_policy = "Always"
command = ["python", "-m", "job_hunter", "migrate"]
env_from {
secret_ref {
name = "job-hunter-secrets"
@ -65,8 +68,9 @@ resource "kubernetes_cron_job_v1" "job_hunter_refresh" {
}
container {
name = "refresh"
image = local.image
name = "refresh"
image = local.image
image_pull_policy = "Always"
command = ["python", "-m", "job_hunter", "refresh",
"--source", "ats", "--source", "hn", "--source", "levels_fyi"]

View file

@ -18,9 +18,12 @@ dependency "external-secrets" {
}
inputs = {
# 92afc38d = master HEAD with levels.fyi scraper + comp_table COALESCE
# fix + Frankfurter FX backend (exchangerate.host free tier deprecated
# in 2026). Built + pushed locally 2026-04-19 while the Woodpecker
# Forgejo webhook remains broken.
image_tag = "92afc38d"
# :latest CI drives the rollout. On every master push the pipeline builds
# latest + :<sha> and runs `kubectl set image deployment/job-hunter ...:<sha>`
# so the Deployment rolls to the just-built code immediately (no wait for
# Keel's poll). Keel stays enrolled in parallel as a redundant net. The
# CronJob uses :latest + Always pull (fresh pod each run). Project version
# lives in pyproject.toml + git tag vX.Y.Z (semver), independent of the
# deploy tag. CI OOM that had blocked all builds since 2026-04 is fixed.
image_tag = "latest"
}