Metadata-Version: 2.4
Name: actdbg
Version: 0.1.0
Summary: Run GitHub Actions workflows locally with a real debugger: breakpoints, step-through, shell at the failure point, re-run one step without re-running the job.
Author-email: Sophie Nguyen <sophie.nguyenthuthuy@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/sophie-nguyenthuthuy/actdbg
Keywords: github-actions,ci,debugger,act,local-ci
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Debuggers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML>=6
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# actdbg

**Run your GitHub Actions workflow locally — with a debugger.**

`act` runs workflows locally; `actdbg` lets you *debug* them. Pause between
steps, drop into a shell at the exact failure point, and re-run one step
without re-running the whole job.

```
$ actdbg run .github/workflows/ci.yml -j test
── step 4/6: Test
  │ testing version 1.2.3
  │ FAIL: fix.marker missing
  ✗ failed (exit 1)

[actdbg] step 4 (Test) failed with exit 1
  try: `shell` to poke around, `retry` after fixing, `rerun` for a clean replay
(actdbg) shell          # bash inside the job, exact env + filesystem of the failure
(actdbg) retry          # re-run just that step, keeping your fix
(actdbg) continue       # finish the job
```

## Why not just act?

`act` replays the whole job from scratch every time. When step 7 of 9 fails,
you edit, re-run, and wait through steps 1–6 again — and you can never *look
inside* the job at the moment of failure. `actdbg` keeps the job alive as a
persistent environment and snapshots it before every step:

| | `act` | `actdbg` |
|---|---|---|
| Run workflow locally | ✅ | ✅ |
| Pause between steps / breakpoints | ❌ | ✅ `break`, `next` |
| Shell at the failure point (exact env + files) | ❌ | ✅ `shell` |
| Re-run one step without re-running the job | ❌ | ✅ `rerun` / `retry` |
| Time-travel to before any earlier step | ❌ | ✅ `goto` |
| Full `uses:` action emulation | ✅ | ⚠️ shims (see below) |

## Install

```bash
pip install actdbg          # from PyPI
pip install -e ".[dev]"     # from a checkout, for hacking
```

Requires Python ≥ 3.10. Docker is optional but recommended (Linux parity +
`docker commit` snapshots). Without Docker, steps run on the host in a
throwaway copy of your repo, with directory-copy snapshots.

## Usage

```bash
actdbg run                          # auto-discovers .github/workflows/*.yml
actdbg run ci.yml -j test           # pick a workflow + job
actdbg run -i                       # start paused, step through from the top
actdbg run -b 4 -b Test            # breakpoints by number or name substring
actdbg run --backend host           # skip Docker
actdbg run --platform linux/amd64   # x86_64 parity with GitHub runners on ARM hosts
actdbg run --matrix python=3.12     # pick a matrix combination
actdbg run --ci                     # no debugger; exit 1 on failure (for scripts)
actdbg list                         # show workflows, jobs, steps
```

On failure (or `-i`, or a breakpoint) you land in the debugger:

| Command | What it does |
|---|---|
| `list` | step list with status; `>` marks the next step, `*` a breakpoint |
| `next` / `continue` | run one step / run until breakpoint, failure, or end |
| `break N` | toggle breakpoint |
| `shell` | interactive shell **inside the job** — same env, same filesystem |
| `! CMD` / `shell CMD` | run one command inside the job (pipe-friendly for scripted sessions) |
| `rerun [N]` | replay step N from its **pre-step snapshot** (clean, deterministic) |
| `retry [N]` | re-run step N against the **current** filesystem (keeps your shell fixes) |
| `goto N` | restore filesystem + job state (env, outputs, PATH) to just before step N |
| `skip` | skip the next step |
| `logs [N]`, `env`, `outputs`, `where` | inspect captured output and job state |

`rerun` vs `retry` is the core distinction: `rerun` answers *"is this step
deterministic / did my last change fix it at source?"*, `retry` answers
*"does the job pass if I patch the state right here?"*

## How it works

- **One persistent environment per job.** Docker mode starts one container and
  `docker exec`s each step into it (your repo is *copied* in, not mounted).
  Host mode runs steps in a temp copy of your repo.
- **Snapshots before every step.** Docker: `docker commit`; host: workspace
  copy. Job-level state that lives outside the filesystem — `GITHUB_ENV`
  accumulations, step outputs, `GITHUB_PATH` additions, job status — is
  tracked in Python and snapshotted alongside. That's why `rerun`/`goto` are
  exact, not approximate.
- **Real Actions semantics** for the things that matter while debugging:
  `${{ }}` expressions (`env`, `steps.*.outputs`, `secrets`, `github`,
  `matrix`, `success()/failure()/always()`, …), `if:` conditions,
  `continue-on-error`, `GITHUB_OUTPUT`/`GITHUB_ENV` (including heredocs),
  `working-directory`, `defaults.run`, secret masking in logs, and
  post-failure behavior (later `success()`-gated steps skip, `failure()` /
  `always()` steps still run).

## `uses:` actions

actdbg focuses on debugging `run:` steps. `actions/checkout` is native (your
workspace *is* the checkout). Other actions are skipped with a warning unless
you shim them in `.actdbg.yml` at your repo root:

```yaml
# .actdbg.yml
backend: docker            # or host
image: catthehacker/ubuntu:act-latest
platform: linux/amd64      # optional: match GitHub's x86_64 runners
shims:
  actions/setup-python: |
    echo "using system python: $(python3 --version) (wanted $INPUT_PYTHON_VERSION)"
  actions/cache: "true"    # no-op
```

Shims run as bash with the action's `with:` inputs exposed as `INPUT_*`,
same as a real action would see.

Secrets load from `.secrets` or `.env` (`KEY=value` lines) or
`--secrets-file`, are available as `${{ secrets.* }}`, and are masked as
`***` in all output.

## Limitations (v0.1)

- Single job per invocation; `needs` inter-job outputs aren't wired.
- Matrix runs one combination at a time (`--matrix` to choose).
- No composite/JS/Docker action execution — shims instead.
- `shell:` supports `bash`, `sh`, `python`.
- Host mode runs with your host toolchain — full parity needs Docker.

## Development

```bash
python -m pytest
```

MIT license.
