Working patterns for running scheduled, unattended AI automations on GitHub Actions instead of local cron jobs — extracted (and scrubbed) from a personal AI operating system that runs ~20 scheduled pipelines in production.
This repo is built to be pointed at by an LLM. Clone it, open it in Claude Code / Cursor / your tool of choice, and ask: "Which of these patterns fit my setup?" Every workflow file is heavily commented and every pattern is explained in docs/.
If your data is reachable through an API or a connector (email, HubSpot, a database, a CRM), your automations should run in the cloud on GitHub Actions — not on your laptop via cron. One cloud runner replaces every "which computer is this running on?" problem:
- No machine has to be awake. No two machines can double-fire the same job.
- Runs are logged, retryable, and manually triggerable (
workflow_dispatch). - A failed run sends you a free email alert (GitHub's built-in failed-workflow notification).
- Secrets live in GitHub Actions secrets, not scattered across laptops.
Local cron earns its place only when the job needs something that only exists on one machine — local files, OS-level integrations (e.g., Apple Reminders), or tools that can't run headless. Full decision framework: docs/gha-vs-cron.md.
The one gotcha that surprises everyone: MCP connectors that work in your interactive AI chat/IDE session are not available in headless or CI environments. Scheduled pipelines must call APIs directly (Google API client, HubSpot API, Supabase client, SMTP). See docs/gha-vs-cron.md.
| Workflow | Pattern it demonstrates |
|---|---|
daily-briefing.yml |
Scheduled AI pipeline + commit results back via auto-merged PR |
email-ingest.yml |
Silent ingestion job (reads email via API, writes to a database, no human-facing output) |
learning-update.yml |
The minimal scheduled job — the template to start from |
weekly-synthesis.yml |
Kill switch + email pause via repo variables |
competitive-watch.yml |
Research pipeline that explicitly dispatches a downstream workflow |
competitive-actioner.yml |
Headless Claude Code (claude -p) with a hard budget cap, output gated behind a human-review PR |
pipeline-health-check.yml |
No-silent-failure guard: exit non-zero → free email alert |
eval-gate.yml |
Deterministic eval that blocks contract drift on push + weekly |
one-shot-reminder-TEMPLATE.yml |
Date-guarded one-shot reminder (cron has no year field) |
secrets-presence-check.yml |
Verify all secrets are configured without printing any values |
Pattern-by-pattern explanations: docs/patterns.md. Secrets setup: docs/secrets.md.
Prerequisites: Python 3.10+.
git clone https://github.com/c-b-g-m/gha-automation-patterns.git
cd gha-automation-patterns
pip install -r automation/requirements.txt
python3 scripts/validate_workflows.pyvalidate_workflows.py parses every workflow and checks the invariants this repo teaches (every job has a timeout, explicit permissions, and a manual trigger). If it prints OK, you have a working copy.
The Python files in automation/ are documented stubs — they print what a real implementation would do and exit cleanly, so a forked copy of this repo runs green out of the box. Replace each stub's body with your own pipeline logic and add the secrets listed in docs/secrets.md.
- Fork the repo (or copy the workflow you need).
- Start from
learning-update.yml— the minimal shape. - Add secrets in Settings → Secrets and variables → Actions.
- Run
secrets-presence-checkfrom the Actions tab to confirm they're all set. - Layer in patterns as you need them: commit-back, health checks, kill switches, eval gates.
MIT — see LICENSE.