You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Monthly Climate Data Update workflow (.github/workflows/climate-update.yml) has failed on every scheduled run (2026-05-01, 06-01, 07-01, 08-01). The only green runs were manual workflow_dispatch back in April. So the automated producer pipeline has effectively never worked on schedule, and the S3 climate data has not been auto-updated since April.
This is not the benign "no new data this month" case — the job dies at package load, before it ever checks for new data.
Reconciled 2026-08-26 (at merge of #80). This body originally named two bugs and proposed granting contents: write. The work found four independent defects, and contents: write turned out to be the wrong fix — see "What was actually found" below. The original Bug 1 / Bug 2 descriptions are kept intact for the record; the proposed-fix and acceptance sections have been corrected to describe what shipped.
Bug 1 — devtools is not installed on the runner
scripts/pipeline_update_edh.R:31:
if (requireNamespace("cd", quietly=TRUE)) library(cd) elsedevtools::load_all()
CI installs cd's dependencies but not cd itself, so the else branch runs → devtools::load_all() →
Error in loadNamespace(x) : there is no package called 'devtools'
Execution halted
The pipeline halts immediately; no EDH fetch, no catalog read, nothing.
Bug 2 — the workflow token is read-only
The Commit log step (if: always()) then runs and cannot push:
remote: Permission to NewGraphEnvironment/cd.git denied to github-actions[bot].
fatal: ... The requested URL returned error: 403 (exit code 128)
There is no permissions: block, so the default GITHUB_TOKEN is read-only.
What was actually found
Four defects, stacked. Bugs 3 and 4 were discovered during the work and are the reason the fix does not match the original proposal.
Bug 3 — the Commit log step was dead code, so contents: write was the wrong fix..gitignore carries logs/*.log, and the pipeline writes logs/update_$(date +%Y%m%d).log. git add logs/ therefore staged nothing, the git diff --cached --quiet guard short-circuited the commit, and only the doomed git push remained — and git push 403s during git-receive-pack ref advertisement even with nothing to push. Granting contents: write would have "repaired" a step that could never commit anything. The step was deleted and replaced with actions/upload-artifact@v4; the token stays contents: read (plus issues: write for the failure-alarm step only).
Bug 4 — the EDH_TOKEN repo secret was stale. Found by the new dry-run credential probe: the secret had not been updated since 2026-04-14T17:04:21Z, exactly the date of the last green run, and returned HTTP 403 (authenticates, but forbidden). Even with Bugs 1–3 fixed, a live run would still have died — six hours in at the EDH fetch rather than four seconds in at the probe. Rotated by the repo owner on 2026-08-26; the next dispatch went green.
Related gotcha worth keeping: EDH's HTTP Basic auth needs httpauth = 1L on the curl handle, because libcurl otherwise waits for a WWW-Authenticate challenge EDH never sends. Credentials go on the handle, not in the URL — the token is not URL-encoded, and this keeps it out of anything loggable.
Fix as shipped
Bug 1:setup-r-dependencies@v2 gets extra-packages: local::. so cd itself installs and the script takes the library(cd) branch. Chosen over any::devtools — it exercises the installed-package path CI should be testing and avoids pulling the whole dev-tooling tree. The devtools::load_all() fallback now fails with a readable message instead of erroring inside loadNamespace.
Bug 2 + 3:permissions: { contents: write } — rejected. The log-commit step was removed in favour of actions/upload-artifact@v4, and the job declares contents: read + issues: write. Nothing in this workflow legitimately needs write access to the repo; it publishes to S3.
Bug 4:EDH_TOKEN rotated. The weekly heartbeat is what catches this next time.
Two complementary mechanisms: a dry-run mode to actively confirm the plumbing (now and on a weekly heartbeat), and an auto-filed GitHub issue on failure as the durable, team-visible alarm.
dry_run path in pipeline_update_edh.R (--dry-run flag or CD_DRY_RUN env): probes credentials, reads the STAC catalog, computes the target year, logs what a live run would fetch, then exits 0 before any EDH pull / COG rebuild / S3 push.
Deliberately not a no-write mode: it round-trips a sentinel object under s3://stac-era5-land/_healthcheck/<run-id>. aws sts get-caller-identity proves the keys parse and says nothing about whether the bucket is writable — which is the exact class of failure that has taken this pipeline down.
The probe block runs before Step 1, so it executes on the live path too, not just in dry-run.
workflow_dispatch input dry_run (default true) — manual triggers are cheap and safe by default.
Weekly cron'0 6 * * 1' in dry-run mode alongside the monthly '0 6 1 * *' live run, distinguished via github.event.schedule. Gives a regular "plumbing still works" signal and keeps the scheduled workflow active so GitHub does not auto-disable it after 60 days of repo inactivity.
Auto-file a GitHub issue on failure: final if: failure() step searches for an open issue labelled climate-update-failure and comments on it if one exists, otherwise creates one. Body carries run URL, event name, resolved dry-run mode, and the last 50 log lines; the full log is attached as a workflow artifact (30d retention).
Confirming the fix (acceptance)
workflow_dispatch with dry_run=true completes green: package loads, secrets verified, EDH probe HTTP 200, AWS identity + bucket write both proven, STAC read, target year computed, exits before the EDH fetch. Run 33006696386 — the first non-failing run since April.
The push path is exercised without a 403 — moot. There is no push path any more (Bug 3); the log is uploaded as an artifact and the token is contents: read.
Auto-file-on-failure verified organically rather than by deliberate breakage: run 31204565836 opened Monthly Climate Data Update failed (2026-08-07) #79, and run 31204944259 commented on it instead of opening a duplicate — the dedup path, exercised end to end. The alarm's first real customer was Bug 4 itself.
Both run URLs + outcomes recorded in planning/archive/2026-08-issue-78-climate-update-workflow/findings.md.
Problem
The Monthly Climate Data Update workflow (
.github/workflows/climate-update.yml) has failed on every scheduled run (2026-05-01, 06-01, 07-01, 08-01). The only green runs were manualworkflow_dispatchback in April. So the automated producer pipeline has effectively never worked on schedule, and the S3 climate data has not been auto-updated since April.This is not the benign "no new data this month" case — the job dies at package load, before it ever checks for new data.
Bug 1 —
devtoolsis not installed on the runnerscripts/pipeline_update_edh.R:31:CI installs cd's dependencies but not cd itself, so the
elsebranch runs →devtools::load_all()→The pipeline halts immediately; no EDH fetch, no catalog read, nothing.
Bug 2 — the workflow token is read-only
The
Commit logstep (if: always()) then runs and cannot push:There is no
permissions:block, so the defaultGITHUB_TOKENis read-only.What was actually found
Four defects, stacked. Bugs 3 and 4 were discovered during the work and are the reason the fix does not match the original proposal.
Bug 3 — the
Commit logstep was dead code, socontents: writewas the wrong fix..gitignorecarrieslogs/*.log, and the pipeline writeslogs/update_$(date +%Y%m%d).log.git add logs/therefore staged nothing, thegit diff --cached --quietguard short-circuited the commit, and only the doomedgit pushremained — andgit push403s duringgit-receive-packref advertisement even with nothing to push. Grantingcontents: writewould have "repaired" a step that could never commit anything. The step was deleted and replaced withactions/upload-artifact@v4; the token stayscontents: read(plusissues: writefor the failure-alarm step only).Bug 4 — the
EDH_TOKENrepo secret was stale. Found by the new dry-run credential probe: the secret had not been updated since2026-04-14T17:04:21Z, exactly the date of the last green run, and returned HTTP 403 (authenticates, but forbidden). Even with Bugs 1–3 fixed, a live run would still have died — six hours in at the EDH fetch rather than four seconds in at the probe. Rotated by the repo owner on 2026-08-26; the next dispatch went green.Related gotcha worth keeping: EDH's HTTP Basic auth needs
httpauth = 1Lon the curl handle, because libcurl otherwise waits for aWWW-Authenticatechallenge EDH never sends. Credentials go on the handle, not in the URL — the token is not URL-encoded, and this keeps it out of anything loggable.Fix as shipped
setup-r-dependencies@v2getsextra-packages: local::.so cd itself installs and the script takes thelibrary(cd)branch. Chosen overany::devtools— it exercises the installed-package path CI should be testing and avoids pulling the whole dev-tooling tree. Thedevtools::load_all()fallback now fails with a readable message instead of erroring insideloadNamespace.— rejected. The log-commit step was removed in favour ofpermissions: { contents: write }actions/upload-artifact@v4, and the job declarescontents: read+issues: write. Nothing in this workflow legitimately needs write access to the repo; it publishes to S3.EDH_TOKENrotated. The weekly heartbeat is what catches this next time.QA / monitoring — dry-run + auto-file-issue-on-failure
Two complementary mechanisms: a dry-run mode to actively confirm the plumbing (now and on a weekly heartbeat), and an auto-filed GitHub issue on failure as the durable, team-visible alarm.
dry_runpath inpipeline_update_edh.R(--dry-runflag orCD_DRY_RUNenv): probes credentials, reads the STAC catalog, computes the target year, logs what a live run would fetch, then exits 0 before any EDH pull / COG rebuild / S3 push.s3://stac-era5-land/_healthcheck/<run-id>.aws sts get-caller-identityproves the keys parse and says nothing about whether the bucket is writable — which is the exact class of failure that has taken this pipeline down.workflow_dispatchinputdry_run(defaulttrue) — manual triggers are cheap and safe by default.'0 6 * * 1'in dry-run mode alongside the monthly'0 6 1 * *'live run, distinguished viagithub.event.schedule. Gives a regular "plumbing still works" signal and keeps the scheduled workflow active so GitHub does not auto-disable it after 60 days of repo inactivity.if: failure()step searches for an open issue labelledclimate-update-failureand comments on it if one exists, otherwise creates one. Body carries run URL, event name, resolved dry-run mode, and the last 50 log lines; the full log is attached as a workflow artifact (30d retention).Confirming the fix (acceptance)
workflow_dispatchwithdry_run=truecompletes green: package loads, secrets verified, EDH probeHTTP 200, AWS identity + bucket write both proven, STAC read, target year computed, exits before the EDH fetch. Run 33006696386 — the first non-failing run since April.The push path is exercised without a 403— moot. There is no push path any more (Bug 3); the log is uploaded as an artifact and the token iscontents: read.planning/archive/2026-08-issue-78-climate-update-workflow/findings.md.References
.github/workflows/climate-update.ymlscripts/pipeline_update_edh.R