# Advanced Workflow

## Affected Runs

Like turborepo and nx, actrun can skip workflows when no relevant files have changed since the last successful run. This is useful in monorepos or projects where different workflows cover different parts of the codebase.

### Configuration

Define file patterns per workflow in `actrun.toml`:

```toml
[affected."ci.yml"]
patterns = ["src/**", "*.toml", "Cargo.lock"]

[affected."lint.yml"]
patterns = ["src/**", "*.config.js"]

[affected."docs.yml"]
patterns = ["docs/**", "README.md"]
```

The key after `affected.` matches the workflow filename (basename, not full path).

### Usage

```bash
# Compare against last successful run
actrun workflow run .github/workflows/ci.yml --affected

# Compare against a specific revision
actrun workflow run .github/workflows/ci.yml --affected HEAD~3
actrun workflow run .github/workflows/ci.yml --affected abc123
```

### Pattern Fallback

If no `[affected."<workflow>"]` section is defined in `actrun.toml`, actrun falls back to the `on.push.paths` patterns from the workflow file itself. This means many workflows work with `--affected` out of the box.

### How It Works

1. Find the base revision: use the explicit rev argument (e.g. `HEAD~3`), or find the latest **successful** run's `after_sha`
2. Compute changed files: `git diff --name-only <base_rev> HEAD` + uncommitted/untracked files
3. Resolve patterns: use `actrun.toml` `[affected."<workflow>"]` patterns, or fall back to `on.push.paths` from the workflow
4. If **any** changed file matches the patterns, run the workflow
5. If **no** files match, skip with a message

```
# No changes in src/ since last success
$ actrun ci.yml --affected
affected: no changes match patterns for ci.yml (since run-1)

# After editing src/main.rs
$ actrun ci.yml --affected
affected: changes detected for ci.yml (since run-1)
run_id=run-2
...

# First run (no history) always executes
$ actrun ci.yml --affected
affected: no previous successful run found, running
```

### Glob Patterns

| Pattern | Matches |
|---------|---------|
| `src/**` | All files under `src/` recursively |
| `src/*` | Direct children of `src/` only |
| `*.rs` | Files ending with `.rs` in the root |
| `**/*.rs` | Files ending with `.rs` at any depth |
| `README.md` | Exact match |
| `docs/**` | All files under `docs/` recursively |

## Retry Failed Runs

When a workflow partially fails, `--retry` re-runs only the failed jobs, skipping jobs that already succeeded. This saves time by not re-executing expensive build steps.

### Usage

```bash
# First run: build succeeds, test fails
$ actrun workflow run ci.yml
run_id=run-1
state=partial_failed
build/step_1: success
test/step_1: failed

# Retry: only test job runs, build is skipped
$ actrun workflow run ci.yml --retry
retry: re-running failed jobs from run-1:
  - test
retry: skipping successful jobs:
  - build
run_id=run-2
state=completed
test/step_1: success

# No more failures to retry
$ actrun workflow run ci.yml --retry
retry: last run run-2 was successful, nothing to retry
```

### How It Works

1. Find the latest run for this workflow (success or failure)
2. If the latest run was successful, print "nothing to retry" and exit
3. Extract failed job IDs from the run record
4. Filter out succeeded jobs from the workflow
5. Execute only the remaining (failed) jobs

### Notes

- `--retry` looks at the **latest** run regardless of success/failure status
- Jobs are the unit of retry. If a job has 5 steps and the 3rd fails, the entire job is re-run (all 5 steps)
- `--retry` can be combined with `--skip-action` and other flags
- Skipped jobs and re-running jobs are logged before execution

## Combining Affected and Retry

These features work independently and can be used in sequence:

```bash
# Normal development loop
actrun ci.yml --affected          # Skip if nothing changed
actrun ci.yml --retry             # Re-run only failed jobs after fixing

# CI-like workflow
actrun ci.yml --affected --local  # Run only if relevant files changed
```

## Local Skip Actions

`local_skip_actions` in `actrun.toml` specifies actions to skip when running locally. This is the primary mechanism for using host-installed toolchains instead of GitHub Actions setup actions.

```toml
# Generated by: actrun init (auto-detects installed tools)
local_skip_actions = [
  "actions/checkout",
  "actions/setup-node",
  "actions/setup-python",
]
```

When actions are skipped, actrun logs each one before execution:

```
skip  build/actions/checkout@v4 (actions/checkout@v4)
skip  build/Setup Node (actions/setup-node@v4)
run_id=run-1
...
```

### Validation with doctor

`actrun doctor` validates that skipped setup actions have corresponding local tools:

```
$ actrun doctor
...
=== actrun.toml ===
  local_skip_actions:
    - actions/checkout
    - actions/setup-node
    - actions/setup-python
ok  skip actions/setup-node: local node found (v24.12.0)
ok  skip actions/setup-python: local python3 found (Python 3.14.2)
```

### With Nix

When combined with nix integration, `local_skip_actions` skips the GitHub setup actions while nix provides the toolchains:

```toml
local_skip_actions = ["actions/checkout", "actions/setup-node"]
nix_mode = ""   # auto-detect flake.nix
```

The workflow's `actions/setup-node@v4` is skipped, and the `run:` steps execute inside `nix develop` where `node` is provided by the flake.

## Local GitHub Context

When `--event` is omitted, actrun first tries to infer common GitHub context values from the local git repository. If you need stable local values, add `[local_context]` to `actrun.toml`:

```toml
[local_context]
repository = "owner/repo"
ref_name = "feature/local-demo"
before_rev = "HEAD^"
after_rev = "HEAD"
actor = "your-name"
```

This fills missing values for `github.repository`, `github.ref_name`, `github.sha`, `github.actor`, and the matching `GITHUB_*` variables. For precedence details and a runnable example, see [Local GitHub Context](local-context.md).

## Full actrun.toml Example

```toml
# Workspace
workspace_mode = "local"

# Skip setup actions — use local (or nix) toolchain
local_skip_actions = [
  "actions/checkout",
  "actions/setup-node",
  "actions/setup-python",
]

# Trust third-party actions
trust_actions = true

# Optional local GitHub context override
# [local_context]
# repository = "owner/repo"
# ref_name = "main"
# before_rev = "HEAD^"
# after_rev = "HEAD"
# actor = "your-name"

# Nix (auto-detected from flake.nix)
# nix_mode = ""
# nix_packages = ["python312", "jq"]

# Container runtime
# container_runtime = "docker"

# Affected file patterns per workflow
[affected."ci.yml"]
patterns = ["src/**", "package.json", "pnpm-lock.yaml"]

[affected."lint.yml"]
patterns = ["src/**", "*.config.*"]

[affected."docs.yml"]
patterns = ["docs/**", "*.md"]
```

## Cloud-Only Features

Some GitHub Actions features are designed for cloud execution and have no local equivalent. actrun handles these gracefully:

### OIDC (OpenID Connect)

Workflows using OIDC for cloud authentication (`permissions: id-token: write`) work without errors — `permissions` blocks are silently ignored. However, `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` are not set, so steps calling `getIDToken()` will fail.

**Recommended approach**: skip or override OIDC-dependent steps:

```toml
# actrun.toml
[override."aws-actions/configure-aws-credentials"]
run = "echo 'using local AWS credentials'"
```

Or use `if: ${{ !env.ACTRUN_LOCAL }}` in the workflow:

```yaml
- uses: aws-actions/configure-aws-credentials@v4
  if: ${{ !env.ACTRUN_LOCAL }}
  with:
    role-to-assume: arn:aws:iam::123456:role/deploy
```

### Permissions and Concurrency

- `permissions`: Silently ignored. Local execution uses host user permissions.
- `concurrency`: Silently ignored. Local execution is sequential.
