{"id":49608671,"url":"https://github.com/nathanjordan/ghagen","last_synced_at":"2026-05-04T14:06:38.644Z","repository":{"id":349879635,"uuid":"1203414876","full_name":"nathanjordan/ghagen","owner":"nathanjordan","description":"Python/Typescript libraries for generating GitHub Actions YAML","archived":false,"fork":false,"pushed_at":"2026-05-01T08:52:43.000Z","size":1685,"stargazers_count":0,"open_issues_count":12,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-01T10:27:48.545Z","etag":null,"topics":["action","actions","generator","gha","github","pinning","version","workflow"],"latest_commit_sha":null,"homepage":"https://nathanjordan.github.io/ghagen/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nathanjordan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-07T02:46:13.000Z","updated_at":"2026-04-23T16:24:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nathanjordan/ghagen","commit_stats":null,"previous_names":["nathanjordan/ghagen"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/nathanjordan/ghagen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nathanjordan%2Fghagen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nathanjordan%2Fghagen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nathanjordan%2Fghagen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nathanjordan%2Fghagen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nathanjordan","download_url":"https://codeload.github.com/nathanjordan/ghagen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nathanjordan%2Fghagen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32610313,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"ssl_error","status_checked_at":"2026-05-04T10:08:02.005Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["action","actions","generator","gha","github","pinning","version","workflow"],"created_at":"2026-05-04T14:06:36.524Z","updated_at":"2026-05-04T14:06:38.636Z","avatar_url":"https://github.com/nathanjordan.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ghagen\n\nGenerate GitHub Actions workflows from Python or TypeScript code.\n\n[![CI](https://github.com/nathanjordan/ghagen/actions/workflows/ci.yml/badge.svg)](https://github.com/nathanjordan/ghagen/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/ghagen)](https://pypi.org/project/ghagen/)\n[![Python](https://img.shields.io/pypi/pyversions/ghagen)](https://pypi.org/project/ghagen/)\n[![License](https://img.shields.io/github/license/nathanjordan/ghagen)](LICENSE)\n\n## Features\n\n- **Dual language support** - The tool comes in two flavors depending on your\n  constraints/preferences: Python and Javascript/Typescript.\n- **Typed models** — type checking and IDE autocomplete which prevents typos or unsupported values.\n- **YAML comments** — Add comments to the generated yaml for additional documentation/clarity\n- **Helpers** — expression builder (`expr`) ensures you are using supported template variables\n- **Escape hatches** — Break out of the type system when you want to. You're not stuck with the\n  schema if new features come out or you need to override something.\n- **Freshness checking** — ensure your generated yaml files are in sync with your defined ghagen\n  models\n- **Version pinning** — Prevent surprises and security risks by ensuring the same actions run every\n  time.\n\n\u003e [!NOTE] **You might not need this** if your GitHub Actions setup is relatively simple, ghagen\n\u003e might not be worth the added complexity — [actionlint][actionlint]\n\u003e [renovate][renovate]/[dependabot][dependabot] and [ratchet][ratchet] can cover a lot of common\n\u003e issues. Reach for ghagen when keeping track of workflows by hand becomes painful, or when you want\n\u003e the extra assurances a real programming language provides (types, tests, refactoring tools).\n\n## Quickstart\n\n### Python\n\n```bash\npip install ghagen        # or: uv tool install ghagen\n```\n\n```python\nfrom ghagen import App, Job, On, PushTrigger, Step, Workflow\n\nci = Workflow(\n    name=\"CI\",\n    on=On(push=PushTrigger(branches=[\"main\"])),\n    jobs={\n        \"test\": Job(\n            runs_on=\"ubuntu-latest\",\n            steps=[Step(uses=\"actions/checkout@v4\"), Step(run=\"pytest\")],\n        ),\n    },\n)\n\napp = App()\napp.add_workflow(ci, \"ci.yml\")\napp.synth()\n```\n\n```bash\nghagen synth\n```\n\n### TypeScript\n\n```bash\nnpm install --save-dev @ghagen/ghagen\n```\n\n```typescript\nimport { App, workflow, job, step, on, pushTrigger } from \"@ghagen/ghagen\";\n\nconst ci = workflow({\n  name: \"CI\",\n  on: on({ push: pushTrigger({ branches: [\"main\"] }) }),\n  jobs: {\n    test: job({\n      runsOn: \"ubuntu-latest\",\n      steps: [step({ uses: \"actions/checkout@v4\" }), step({ run: \"pytest\" })],\n    }),\n  },\n});\n\nconst app = new App();\napp.addWorkflow(ci, \"ci.yml\");\nawait app.synth();\n```\n\n```bash\nnpx ghagen synth\n```\n\n### GitHub Action\n\nRun `ghagen check-synced` in CI so a PR fails if the generated YAML drifts from the Python config:\n\n```yaml\njobs:\n  check-workflows:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: nathanjordan/ghagen/check-synth@v0\n        with:\n          config: .github/ghagen_workflows.py # optional; default shown\n          python-version: \"3.13\" # optional; default shown\n          ghagen-version: \"\" # optional; empty = latest\n```\n\n`v0` is a rolling major tag. The Action is a drift check for the Python path; TypeScript users can\nrun `npx ghagen check-synced` instead.\n\n## Example output\n\nBoth snippets above generate:\n\n```yaml\nname: CI\non:\n  push:\n    branches:\n      - main\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - run: pytest\n```\n\n## FAQ\n\n**Python or TypeScript — which should I pick?** Pick whatever you're comfortable with or fits with\nyour project. Both Python and Typescript/Javscript implementations have feature parity and are\ninterchangeable.\n\n**Can I mix ghagen-generated workflows with hand-written YAML?** Yes. ghagen only touches files you\nexplicitly register. Any other file in `.github/workflows/` is left alone — drop a hand-written\n`weekly-report.yml` next to a ghagen-generated `ci.yml` and nothing breaks.\n\n**What does the GitHub Action do?** It runs `ghagen check-synced` against your Python config and\nfails the build if the generated YAML doesn't match what the current definitions would produce. It\nprevents changes made to the Python/JS code from not making it into the YAML spec.\n\n**How do I handle something ghagen's models don't cover?** Use `extras` on any model for arbitrary\nkeys, or `Raw` / `raw()` to drop an expression into a field that expects a literal. Both leave the\nrest of the model fully typed.\n\n**How do I pin actions to commit SHAs?** Run `ghagen deps pin` to populate\n`.github/ghagen.lock.toml`; subsequent `ghagen synth` calls rewrite every `uses:` to its pinned SHA.\nWire `ghagen deps check-synced` into CI to catch unpinned additions.\n\n_More questions? See the [full FAQ](https://nathanjordan.github.io/ghagen/faq/)._\n\n## Documentation\n\nFull documentation: [nathanjordan.github.io/ghagen](https://nathanjordan.github.io/ghagen/)\n\n## License\n\nMIT\n\n[actionlint]: https://github.com/rhysd/actionlint\n[renovate]: https://docs.renovatebot.com/\n[dependabot]: https://docs.github.com/en/code-security/dependabot\n[ratchet]: https://github.com/sethvargo/ratchet\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnathanjordan%2Fghagen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnathanjordan%2Fghagen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnathanjordan%2Fghagen/lists"}