Generate GitHub Actions workflow YAML from Python code
Project description
ghagen
Generate GitHub Actions workflows from Python or TypeScript code.
Features
- Dual language support - The tool comes in two flavors depending on your constraints/preferences: Python and Javascript/Typescript.
- Typed models — type checking and IDE autocomplete which prevents typos or unsupported values.
- YAML comments — Add comments to the generated yaml for additional documentation/clarity
- Helpers — expression builder (
expr) ensures you are using supported template variables - Escape hatches — Break out of the type system when you want to. You're not stuck with the schema if new features come out or you need to override something.
- Linting — catch gotchas like invalid permissions and more.
timeout-minutes, and duplicate step ids with source-line precision - Freshness checking — ensure your generated yaml files are in sync with your defined ghagen models
- Version pinning — Prevent surprises and security risks by ensuring the same actions run every time.
[!NOTE] You might not need this if your GitHub Actions setup is relatively simple, ghagen might not be worth the added complexity — actionlint renovate/dependabot and ratchet can cover a lot of common issues. Reach for ghagen when keeping track of workflows by hand becomes painful, or when you want the extra assurances a real programming language provides (types, tests, refactoring tools).
Quickstart
Python
pip install ghagen # or: uv tool install ghagen
from ghagen import App, Job, On, PushTrigger, Step, Workflow
ci = Workflow(
name="CI",
on=On(push=PushTrigger(branches=["main"])),
jobs={
"test": Job(
runs_on="ubuntu-latest",
steps=[Step(uses="actions/checkout@v4"), Step(run="pytest")],
),
},
)
app = App()
app.add_workflow(ci, "ci.yml")
app.synth()
ghagen synth
TypeScript
npm install --save-dev @ghagen/ghagen
import { App, workflow, job, step, on, pushTrigger } from "@ghagen/ghagen";
const ci = workflow({
name: "CI",
on: on({ push: pushTrigger({ branches: ["main"] }) }),
jobs: {
test: job({
runsOn: "ubuntu-latest",
steps: [step({ uses: "actions/checkout@v4" }), step({ run: "pytest" })],
}),
},
});
const app = new App();
app.addWorkflow(ci, "ci.yml");
await app.synth();
npx ghagen synth
GitHub Action
Run ghagen check-synced in CI so a PR fails if the generated YAML drifts from the Python config:
jobs:
check-workflows:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: nathanjordan/ghagen/check-synth@v0
with:
config: .github/ghagen_workflows.py # optional; default shown
python-version: "3.13" # optional; default shown
ghagen-version: "" # optional; empty = latest
v0 is a rolling major tag. The Action is a drift check for the Python path; TypeScript users can
run npx ghagen check-synced instead.
Example output
Both snippets above generate:
name: CI
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pytest
FAQ
Python or TypeScript — which should I pick? Pick whatever you're comfortable with or fits with your project. Both Python and Typescript/Javscript implementations have feature parity and are interchangeable.
Can I mix ghagen-generated workflows with hand-written YAML? Yes. ghagen only touches files you
explicitly register. Any other file in .github/workflows/ is left alone — drop a hand-written
weekly-report.yml next to a ghagen-generated ci.yml and nothing breaks.
What does the GitHub Action do? It runs ghagen check-synced against your Python config and
fails the build if the generated YAML doesn't match what the current definitions would produce. It
prevents changes made to the Python/JS code from not making it into the YAML spec.
How do I handle something ghagen's models don't cover? Use extras on any model for arbitrary
keys, or Raw / raw() to drop an expression into a field that expects a literal. Both leave the
rest of the model fully typed.
How do I pin actions to commit SHAs? Run ghagen deps pin to populate
.github/ghagen.lock.toml; subsequent ghagen synth calls rewrite every uses: to its pinned SHA.
Wire ghagen deps check-synced into CI to catch unpinned additions.
More questions? See the full FAQ.
Documentation
Full documentation: nathanjordan.github.io/ghagen
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ghagen-0.5.0.tar.gz.
File metadata
- Download URL: ghagen-0.5.0.tar.gz
- Upload date:
- Size: 375.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
936e39158d7625aa28b68f86337bdd9a5e0fc3d8b788d4038384e076c4e67bab
|
|
| MD5 |
28fe8ba5a771a529718e8e4233e280b5
|
|
| BLAKE2b-256 |
4051edb92799972b4665b7844f14b4192c8a0498462793da44a1b7f8e675d2c8
|
Provenance
The following attestation bundles were made for ghagen-0.5.0.tar.gz:
Publisher:
release.yml on nathanjordan/ghagen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghagen-0.5.0.tar.gz -
Subject digest:
936e39158d7625aa28b68f86337bdd9a5e0fc3d8b788d4038384e076c4e67bab - Sigstore transparency entry: 1352273160
- Sigstore integration time:
-
Permalink:
nathanjordan/ghagen@bf594a1244e5b11dac445a6df3bda1ef02a13800 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/nathanjordan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bf594a1244e5b11dac445a6df3bda1ef02a13800 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ghagen-0.5.0-py3-none-any.whl.
File metadata
- Download URL: ghagen-0.5.0-py3-none-any.whl
- Upload date:
- Size: 108.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b1ad353da4533d5d9a19eecb37bdc5d4fdd2fa80fa84611a0b74573d2f371c3
|
|
| MD5 |
f032610648d9298480ffa71fad3c7325
|
|
| BLAKE2b-256 |
276c1bb2faa75ab327a92e755b92456a6a04b34c8c1d716f3106993a31e24d41
|
Provenance
The following attestation bundles were made for ghagen-0.5.0-py3-none-any.whl:
Publisher:
release.yml on nathanjordan/ghagen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghagen-0.5.0-py3-none-any.whl -
Subject digest:
0b1ad353da4533d5d9a19eecb37bdc5d4fdd2fa80fa84611a0b74573d2f371c3 - Sigstore transparency entry: 1352273244
- Sigstore integration time:
-
Permalink:
nathanjordan/ghagen@bf594a1244e5b11dac445a6df3bda1ef02a13800 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/nathanjordan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bf594a1244e5b11dac445a6df3bda1ef02a13800 -
Trigger Event:
push
-
Statement type: