pygha 0.3.1
A Python-native CI/CD framework for defining, testing, and transpiling pipelines.
License: MIT
Requires Python: >=3.11

<div align="center">
  <a href="https://github.com/parneetsingh022/pygha"><img alt="logo_pygha_dark" width="300px" src="https://github.com/user-attachments/assets/0b6bdf53-d405-4d55-92d9-0e01bb00ae94" /></a>
</div>



<p align="center">
  <em>A Python-native CI/CD framework for defining, testing, and transpiling pipelines to GitHub Actions.</em>
</p>
<p align="center">
  <strong><a href="https://pygha.readthedocs.io/">Read the Full Documentation</a></strong>
</p>

---

<p align="center">
  <a href="https://pypi.org/project/pygha/">
    <img src="https://img.shields.io/pypi/v/pygha?color=blue" alt="PyPI">
  </a>
  <a href="https://anaconda.org/psidhu22/pygha">
    <img src="https://img.shields.io/conda/vn/psidhu22/pygha?color=green&label=anaconda" alt="Conda Version">
  </a>
  <a href="https://pypi.org/project/pygha/">
    <img src="https://img.shields.io/pypi/pyversions/pygha" alt="Python Versions">
  </a>
  <a href="https://github.com/parneetsingh022/pygha/actions/workflows/ci.yml">
    <img src="https://github.com/parneetsingh022/pygha/actions/workflows/ci.yml/badge.svg" alt="CI Status">
  </a>
  <a href="https://pygha.readthedocs.io/">
    <img src="https://img.shields.io/readthedocs/pygha" alt="Documentation Status">
  </a>
  <a href="https://codecov.io/gh/parneetsingh022/pygha">
    <img src="https://codecov.io/gh/parneetsingh022/pygha/branch/main/graph/badge.svg" alt="Coverage">
  </a>
  <img src="https://img.shields.io/badge/lint-Ruff-blue" alt="Lint (Ruff)">
  <img src="https://img.shields.io/badge/type--check-mypy-blue" alt="Type Check (mypy)">
  <img src="https://img.shields.io/badge/security-Bandit-green" alt="Security (Bandit)">
  <a href="LICENSE">
    <img src="https://img.shields.io/github/license/parneetsingh022/pygha.svg" alt="License">
  </a>
</p>

---

## Installation

### PyPI
You can install `pygha` via pip:
```bash
pip install pygha
```


## Example: Define a CI Pipeline with `pygha`

Below is an example of a **Python-defined pipeline** that mirrors what most teams use in production —
build, lint, test, coverage, and deploy — all orchestrated through `pygha`.

```python
from pygha import job, default_pipeline
from pygha.steps import run, checkout, uses

# Configure the default pipeline to run on main push and PRs
default_pipeline(on_push=["main"], on_pull_request=True)

@job(
    name="test",
    matrix={"python": ["3.11", "3.12", "3.13"]},
)
def test_matrix():
    """Run tests across multiple Python versions."""
    checkout()

    # Use the matrix variable in your step arguments
    setup_python("${{ matrix.python }}", cache="pip")

    run("pip install .[dev]")
    run("pytest")

@job(name="deploy", depends_on=["test"])
def deploy():
    """Build and publish if tests pass."""
    checkout()
    setup_python("3.11", cache="pip")

    run("pip install build twine")
    run("python -m build")
    run("twine check dist/*")
```

## Generating Workflows (CLI)

Once you have defined your pipelines (by default, `pygha` looks for files matching `pipeline_*.py` or `*_pipeline.py` in a `.pipe` directory), use the CLI to generate the GitHub Actions YAML files.

```bash
# Default behavior: Scans .pipe/ and outputs to .github/workflows/
pygha build
```

### Options
* `--src-dir`: Source directory containing your Python pipeline definitions (default: .pipe).

* `--out-dir`: Output directory where the generated YAML files will be saved (default: .github/workflows).

* `--clean`: Automatically deletes YAML files in the output directory that are no longer registered in your pipelines. This is useful when you rename or remove pipelines.

  > If you have a manually created workflow file in your output directory that you want to preserve (e.g., `manual-deploy.yml`), add `# pygha: keep` to the first 10 lines of that file. The CLI will skip deleting it.

## Advanced: Conditional Logic
`pygha` allows you to write conditional workflows using Python syntax instead of raw YAML strings.

### Job-Level Conditions
Use the `@run_if` decorator to skip entire jobs based on context.
```python
from pygha.decorators import run_if
from pygha.expr import github

@job(name="nightly-scan")
@run_if(github.event_name == "schedule")
def security_scan():
    """Only runs on scheduled events."""
    ...
```

### Step-Level Conditions
Use the `when` context manager to group steps that should only run under certain conditions. Nested conditions are automatically `AND`-ed together.

```python
from pygha.steps import when
from pygha.expr import runner, always, failure

@job
def conditional_steps():
    # Simple check
    with when(runner.os == 'Linux'):
        run("sudo apt-get update")

    # Status check helper (runs even if previous steps failed)
    with when(always()):
        run("echo 'Cleanup...'")

    # Nested check: (failure()) AND (runner.os == 'Linux')
    with when(failure()):
        with when(runner.os == 'Linux'):
            run("echo 'Linux build failed!'")
```