Skip to main content

gheasy

Install

pip install gheasy

Quickstart

One call generates a complete Python CI workflow:

from gheasy.workflow import uv_ci
print(uv_ci("ci", lint_cmd=None).to_yaml())
name: ci
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Install dependencies
        run: uv sync --frozen
      - name: Test
        run: uv run pytest

Library CI

gheasy, dockeasy, and every *easy package uses this pattern. Lint on push → test (needs lint) → publish to PyPI (needs test):

from gheasy.workflow import Workflow

wfb = Workflow("ci")
wfb.on.push(branches=["main"]).pull_request()
wfb.uv_lint_job()
wfb.uv_test_job(needs="lint")
wfb.uv_pypi_job(needs="test")
print(wfb.build().to_yaml())
name: ci
on:
  push:
    branches:
      - main
  pull_request:
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Install dependencies
        run: uv sync --frozen
      - name: Lint
        run: uv run ruff check . && uv run ruff format --check .
  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Install dependencies
        run: uv sync --frozen
      - name: Test
        run: uv run pytest
  publish:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'release'
    permissions:
      id-token: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Build
        run: uv build
      - name: Publish
        uses: pypa/gh-action-pypi-publish@release/v1

App Pipeline

For web apps (FastHTML, Django, etc.) — test on every push, deploy to Fly.io on main. Uses the DSL directly for custom step logic:

wfb = Workflow("deploy")
wfb.on.push(branches=["main"])

wfb.uv_test_job()

wfb.job("deploy").needs("test").runs_on("ubuntu-latest")\
    .checkout().end_step()\
    .setup_uv().end_step()\
    .step("Deploy to Fly.io").run("fly deploy --remote-only").end_job()

print(wfb.build().to_yaml())
name: deploy
on:
  push:
    branches:
      - main
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Install dependencies
        run: uv sync --frozen
      - name: Test
        run: uv run pytest
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Deploy to Fly.io
        run: fly deploy --remote-only

Project setup: LFS & secrets

Not every project needs CI. Sometimes you just need git-lfs tracking and GitHub secrets synced from your local .env. For an app like vedicreader with media files and OAuth secrets:

from gheasy.core import gh_lfs, gh_secrets_from_file, gh_push_env, GheasyConfig

# 1. Track binary/media files in git-lfs
gh_lfs(['*.mp3', '*.ogg', '*.wav', '*.png', '*.jpg', '*.webp', '*.xml', '*.db'])

# 2. Push .env values to GitHub — None-default keys become secrets, string-default become variables
cfg = GheasyConfig(app='vedicreader', env_schema={
    # variables (have sane defaults)
    'MODE': 'dev', 'PORT': '5001', 'DOMAIN': 'http://localhost:5001',
    'WANT_GOOGLE': 'true', 'WANT_GIT': 'false',
    'NEED_BACKUP': 'false', 'RC_TYPE': 's3', 'RC_PROVIDER': 'Cloudflare',
    # secrets (no default — must be set)
    'JWT_SCRT': None, 'RESEND_API_KEY': None,
    'GOOGLE_CLI': None, 'GOOGLE_SCRT': None,
    'CF_ACCESS_KEY_ID': None, 'CF_SCRT_ACCESS_KEY': None, 'CF_ENDPOINT': None,
})
cfg.save()

# reads local .env, routes each key: None-schema → gh secret set, string-schema → gh variable set
import os
gh_push_env(dict(os.environ))

Or push everything from as secrets (no schema needed):

# Push all KEY=VALUE pairs from .env as GitHub secrets
gh_secrets_from_file('.env')

# With dry-run to preview what would be pushed
gh_secrets_from_file('.env', dry_run=True)

Cross-package pipeline

gheasy generates workflows that call your own Python code. Here: a workflow that uses dockeasy to generate a Dockerfile, then builds and pushes to GHCR — triggered only when the relevant source files change:

wfb = Workflow("Build caddy-sqlite image")
wfb.on.push(branches=["main"], paths=["dockeasy/proxy.py", "nbs/01_proxy.ipynb"])
wfb.on.workflow_dispatch()

wfb.job("build-and-push").runs_on("ubuntu-latest")\
    .permissions(contents="read", packages="write")\
    .checkout().end_step()\
    .setup_uv().end_step()\
    .step("Install deps").run("uv sync").end_step()\
    .step("Generate Dockerfile").run(
        'uv run python -c "\n'
        'from dockeasy.proxy import caddy_sqlite_dockerfile\n'
        'caddy_sqlite_dockerfile().save(\'Dockerfile\')\n'
        '"'
    ).end_step()\
    .step("Log in to GHCR").uses("docker/login-action@v3")\
        .with_(registry="ghcr.io",
               username="${{ github.actor }}",
               password="${{ secrets.GITHUB_TOKEN }}").end_step()\
    .step("Build and push").uses("docker/build-push-action@v5")\
        .with_(context=".", push=True,
               tags="ghcr.io/vedicreader/caddy-sqlite:latest").end_job()

print(wfb.build().to_yaml())
name: Build caddy-sqlite image
on:
  push:
    branches:
      - main
    paths:
      - dockeasy/proxy.py
      - nbs/01_proxy.ipynb
  workflow_dispatch:
jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup uv
        uses: astral-sh/setup-uv@v5
      - name: Install deps
        run: uv sync
      - name: Generate Dockerfile
        run: "uv run python -c \"\nfrom dockeasy.proxy import caddy_sqlite_dockerfile\ncaddy_sqlite_dockerfile().save('Dockerfile')\n\
          \""
      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/vedicreader/caddy-sqlite:latest

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gheasy-0.0.5.tar.gz (28.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gheasy-0.0.5-py3-none-any.whl (29.5 kB view details)

Uploaded Python 3

File details

Details for the file gheasy-0.0.5.tar.gz.

File metadata

  • Download URL: gheasy-0.0.5.tar.gz
  • Upload date:
  • Size: 28.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for gheasy-0.0.5.tar.gz
Algorithm Hash digest
SHA256 8a32a6f48fa1e135ba48517440e81d432fecab5a63bd3aa835597f3120eaee8a
MD5 51006edd83e4b4ce906870541428a3db
BLAKE2b-256 56952b69310b1263f79e720b701c9f30ca4965fff44530da284604d1feec534d

See more details on using hashes here.

File details

Details for the file gheasy-0.0.5-py3-none-any.whl.

File metadata

  • Download URL: gheasy-0.0.5-py3-none-any.whl
  • Upload date:
  • Size: 29.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for gheasy-0.0.5-py3-none-any.whl
Algorithm Hash digest
SHA256 706afa7b3d544443a38b7975b4dc9468e66f70ea9565be219e3c6a2ba4b0af23
MD5 10bf79f5e368ed02817bd668cb8da0b5
BLAKE2b-256 dc38773709a8b38b8ef9386bb3ab004248898478420c8bbcbb066ca2bcd4d659

See more details on using hashes here.

Release history Release notifications | RSS feed

0.0.10

2 files

0.0.9

2 files

0.0.7

2 files

0.0.6

2 files

This release

0.0.5 This release

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page