Repository navigation
Load env.d in cron jobs so they get GH_TOKEN - #461
Merged
Merged
Conversation
Summary Cron starts jobs from a bare `/bin/sh` environment that never loads `env.d`, so the scheduled `dot update --cron --force` ran with no `GH_TOKEN`. Shdeps then made anonymous GitHub API calls, which share one 60-per-hour quota across every host behind the same public IP. A rate-limited run reports dependencies as current instead of updating them, so most hosts silently stopped picking up new releases until someone ran `dot update` by hand. - the update cron line now reads the first line of `~/.config/gh/github-pat`, the file `env.d/60-github.sh` already exports, and passes it as `GH_TOKEN` - a `GH_TOKEN` the caller already set wins, and a missing or empty file leaves the token empty, matching the shell's fill-only behavior - it is a plain POSIX file read, so it works under dash, bash, and Termux's sh, and it never calls `gh auth token`, which once leaked keyring daemons from cron - documents the cron path in the shell README, including that root's crontab reads root's own token file Testing - new `core-cron` cases render the entry through the real merge hook with a home path containing a space, then run it under the host's sh (and dash when present) in a clean environment with a stub `dot`: missing file, token file, caller override, multi-line file, and empty file - ran the same command under dash on a Debian host: no file gives an empty token, a token file gives the token - `dot test`: 38 suites passed, 1 skipped - `checkrun lint`: pass - fresh-eyes review on side effects, security, and test quality; now tests the rendered line, and documents root's token file Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary Replaces the token file read in the update cron line with a general fix: the cron merge hook now writes `SHELL` (an absolute bash) and `BASH_ENV` (`env-noninteractive.sh`) into the managed crontab block, so every cron job loads `env.d` fill-only, like agent and script shells. `GH_TOKEN` then comes from `env.d/60-github.sh` for any job, the token file keeps a single reader, and the update line is back to plain `dot update --cron --force`. - prefers the system bash (`/bin/bash`, `/usr/bin/bash`, or Termux's `$PREFIX/bin/bash`) over a PATH lookup, so removing a Homebrew, mise, or Shdeps bash cannot leave every job, including the repairing `dot update`, with a missing shell - emits nothing when bash or the loader is missing, leaving cron on its default shell - variables precede `PATH` inside the block; hand-written crontab lines before the block are unaffected - documents cron in the shell README's Environment Ownership section Testing - `core-cron`: the block omits the variables without the loader, sets an absolute bash and the loader with it, orders them before `PATH`, and a job run with the rendered `SHELL`/`BASH_ENV` in a clean environment gets `GH_TOKEN` with no output; a job-set token wins - simulated cron jobs (crontab `PATH`, bash, `BASH_ENV`) as root and the user on three hosts, two with dash as `/bin/sh`: token present, no output, same tools found, 4-10 ms load - `dot test`: 38 suites passed, 1 skipped - `DOT_CORE_SHARD=all dot test core`: the same two pre-existing failures as `origin/main`, new checks pass - `checkrun lint`: pass - fresh-eyes review on cron implementations, side effects on existing jobs, and tests; fixed test isolation and the bash lookup order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dot updateenv.d in cron jobs so they get GH_TOKEN
Summary Most accounts have no `~/.config/gh/github-pat` (root on a fresh host, for example), so the cron env loader must leave such jobs untouched. This pins that case with a dedicated check. - run a job under the rendered `SHELL`/`BASH_ENV` with no token file and `set -eu`; it exits cleanly, prints nothing, and leaves `GH_TOKEN` unset Testing - `dot test -v core-cron`: all checks pass, including the new one - `shellcheck -x .local/lib/dotfiles/tests/core/cron.sh` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cron starts jobs from a bare
/bin/shenvironment that never loadsenv.d, so the scheduleddot update --cron --forceran with noGH_TOKEN. Shdeps then made anonymous GitHub API calls, which share one 60-per-hour quota across every host behind the same public IP. A rate-limited run reports dependencies as current instead of updating them, so hosts silently stopped picking up new releases until someone randot updateby hand.The cron merge hook now writes
SHELL(an absolute bash) andBASH_ENV(env-noninteractive.sh) into the managed crontab block, so every cron job loadsenv.dfill-only, like agent and script shells.GH_TOKENcomes fromenv.d/60-github.shfor any job, and the update line stays plaindot update --cron --force./bin/bash,/usr/bin/bash, or Termux's$PREFIX/bin/bash) over a PATH lookup, so removing a Homebrew, mise, or Shdeps bash cannot leave every job, including the repairingdot update, with a missing shell.PATHinside the block; hand-written crontab lines before the block are unaffected.env.dloaded, and its bash children inheritBASH_ENV. Each account reads its own token file, so root's crontab needs/root/.config/gh/github-pat.Testing
core-cron: the block omits the variables without the loader, sets an absolute bash and the loader with it, orders them beforePATH, and a job run with the renderedSHELL/BASH_ENVin a clean environment getsGH_TOKENwith no output; a job-set token wins.PATH, bash,BASH_ENV) as root and the user on three hosts, two with dash as/bin/sh: token present, no output, same tools found, 4-10 ms load.dot test: 38 suites passed, 1 skipped.DOT_CORE_SHARD=all dot test core: the same two pre-existing failures asorigin/main; the new checks pass.checkrun lint: pass.Review
Two fresh-eyes passes. The first (on the earlier token-file approach) covered rendering, empty-token handling, root, and security. The second covered cron implementations (cronie, Vixie/Debian, macOS, Termux, BusyBox), side effects on existing jobs, and tests; fixed test isolation under all-shard runs and the bash lookup order.
🤖 Generated with Claude Code