# GitHub Action Integration

> Zero-config evidence verification for AI agents. Native GitHub Security tab integration.

**GitHub Marketplace:** [assay-ai-agent-security](https://github.com/marketplace/actions/assay-ai-agent-security)

Canonical public action slug: `Rul1an/assay-action@v2`.
Monorepo-internal workflow tests may use `./assay-action`; that local path is not the recommended external reference.

---

## 30-Second Setup

```yaml
# .github/workflows/assay.yaml
name: Evidence Verification
on: [push, pull_request]

permissions:
  contents: read
  security-events: write

jobs:
  assay:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Rul1an/assay-action@v2
```

Copy, paste, done. The action auto-discovers evidence bundles and reports findings to the Security tab.

---

## Quick Start

```yaml
- uses: Rul1an/assay-action@v2
```

Zero config. Discovers evidence bundles, verifies integrity, uploads SARIF.

## What It Does

1. **Discovers** evidence bundles in your repo (`.assay/evidence/*.tar.gz`)
2. **Verifies** bundle integrity (content-addressed IDs)
3. **Lints** for security issues with optional compliance packs
4. **Reports** to GitHub Security tab + PR comments

## Full Workflow Example

```yaml
# .github/workflows/assay.yaml
name: Evidence Verification

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write
  pull-requests: write

jobs:
  assay:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tests with evidence collection
        run: |
          curl -fsSL https://getassay.dev/install.sh | sh
          echo "$HOME/.local/bin" >> $GITHUB_PATH
          assay ci --config eval.yaml --trace-file traces/golden.jsonl --strict --junit .assay/reports/junit.xml --sarif .assay/reports/sarif.json

      - name: Verify evidence
        uses: Rul1an/assay-action@v2
        with:
          fail_on: error
          baseline_key: ${{ github.event.repository.name }}
          write_baseline: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
```

## Inputs

### Core Inputs (v2.0)

| Input | Default | Description |
|-------|---------|-------------|
| `bundles` | Auto-detect | Glob pattern for evidence bundles |
| `fail_on` | `error` | Fail threshold: `error`, `warn`, `info`, `none` |
| `sarif` | `true` | Upload to GitHub Security tab |
| `comment_diff` | `true` | Post PR comment (only if findings) |
| `baseline_key` | - | Key for baseline comparison |
| `write_baseline` | `false` | Save baseline (default branch only) |
| `version` | `latest` | Assay CLI version |

### Compliance Pack Input (v2.1)

| Input | Default | Description |
|-------|---------|-------------|
| `pack` | - | Compliance pack(s): `eu-ai-act-baseline`, `soc2-baseline`, `./custom.yaml` |

### BYOS Input (v2.1)

| Input | Default | Description |
|-------|---------|-------------|
| `store` | - | BYOS URL: `s3://bucket/prefix`, `gs://bucket`, `az://container` |
| `store_provider` | `auto` | `aws`, `gcp`, `azure`, or `auto` |
| `store_role` | - | IAM role/identity for OIDC |

### Attestation Input (v2.1)

| Input | Default | Description |
|-------|---------|-------------|
| `attest` | `false` | Generate artifact attestation |

## Outputs

| Output | Description |
|--------|-------------|
| `verified` | `true` if all bundles verified |
| `findings_error` | Error count |
| `findings_warn` | Warning count |
| `reports_dir` | Reports directory path |
| `pack_applied` | Applied pack IDs (v2.1) |
| `resolved_pack_refs` | Resolved pack refs as `name@version` (v2.1) |
| `pack_score` | Compliance score 0-100 (v2.1) |
| `pack_status` | Pack status: `disabled`, `success`, `missing_pack`, `invalid_pack`, `runtime_error` (v2.1) |
| `pack_error_kind` | Pack error category when pack status is non-success (v2.1) |
| `bundle_url` | BYOS bundle URL (v2.1) |
| `attestation_id` | Attestation UUID (v2.1) |

## Permission Model

```yaml
# Minimal (lint only)
permissions:
  contents: read

# With SARIF upload
permissions:
  contents: read
  security-events: write

# With PR comments
permissions:
  contents: read
  security-events: write
  pull-requests: write

# With attestation + OIDC (v2.1)
permissions:
  contents: read
  security-events: write
  attestations: write
  id-token: write
```

## Compliance Packs

Lint evidence against regulatory requirements:

```yaml
- uses: Rul1an/assay-action@v2
  with:
    pack: eu-ai-act-baseline
```

SARIF output includes article references (`Article 12(1)`, etc.) for audit trails.

**Available packs:**

| Pack | Coverage |
|------|----------|
| `eu-ai-act-baseline` | Article 12 logging requirements |
| `soc2-baseline` | Control mapping (coming soon) |

Custom packs:

```yaml
- uses: Rul1an/assay-action@v2
  with:
    pack: ./my-org-rules.yaml
```

## BYOS Push with OIDC

Push evidence to your own storage. No static credentials.

### AWS S3

```yaml
permissions:
  id-token: write
  contents: read

jobs:
  assay:
    runs-on: ubuntu-latest
    steps:
      - uses: Rul1an/assay-action@v2
        with:
          store: s3://my-bucket/evidence
          store_provider: aws
          store_role: arn:aws:iam::123456789:role/assay-evidence-push
```

Requires IAM trust policy for `token.actions.githubusercontent.com`.

### GCP Cloud Storage

```yaml
- uses: Rul1an/assay-action@v2
  with:
    store: gs://my-bucket/evidence
    store_provider: gcp
    store_role: projects/my-project/locations/global/workloadIdentityPools/github/providers/github
```

## Artifact Attestation

Generate SLSA-aligned provenance for evidence bundles:

```yaml
permissions:
  attestations: write
  id-token: write

steps:
  - uses: Rul1an/assay-action@v2
    with:
      attest: true
```

Verify locally:

```bash
gh attestation verify bundle.tar.gz --owner Rul1an
```

Attestations only run on push to default branch (security).

## Common Patterns (Copy-Paste)

### Pattern 1: CI Gate with Baseline

Block PRs that introduce new security findings:

```yaml
name: Security Gate
on: [push, pull_request]

permissions:
  contents: read
  security-events: write
  pull-requests: write

jobs:
  assay:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        run: pytest tests/

      - uses: Rul1an/assay-action@v2
        with:
          fail_on: error
          baseline_key: ${{ github.event.repository.name }}
          write_baseline: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
```

### Pattern 2: Compliance Reporting

EU AI Act Article 12 compliance checks with SARIF output:

```yaml
- uses: Rul1an/assay-action@v2
  with:
    pack: eu-ai-act-baseline
    fail_on: warn  # Fail on warnings too
```

### Pattern 3: Enterprise Pipeline (Full v2.1)

Complete pipeline with BYOS, attestation, and compliance:

```yaml
name: Evidence Pipeline
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write
  pull-requests: write
  attestations: write
  id-token: write

jobs:
  assay:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        run: |
          curl -fsSL https://getassay.dev/install.sh | sh
          assay ci --config eval.yaml --trace-file traces/golden.jsonl --strict --junit .assay/reports/junit.xml --sarif .assay/reports/sarif.json

      - uses: Rul1an/assay-action@v2
        with:
          pack: eu-ai-act-baseline
          store: s3://my-bucket/evidence
          store_role: arn:aws:iam::123456789:role/AssayRole
          attest: true
          baseline_key: main
          write_baseline: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
```

---

## Examples

### Baseline Comparison

Detect regressions against your default branch:

```yaml
- uses: Rul1an/assay-action@v2
  with:
    baseline_key: unit-tests
    write_baseline: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
```

### Matrix Builds

Multiple test suites:

```yaml
jobs:
  test:
    strategy:
      matrix:
        suite: [unit, integration, e2e]
    steps:
      - uses: actions/checkout@v4

      - name: Run ${{ matrix.suite }} tests
        run: assay ci --config eval.yaml --trace-file traces/${{ matrix.suite }}.jsonl --strict --junit .assay/reports/${{ matrix.suite }}.junit.xml --sarif .assay/reports/${{ matrix.suite }}.sarif.json

      - uses: Rul1an/assay-action@v2
        with:
          bundles: '.assay/evidence/${{ matrix.suite }}/*.tar.gz'
```

### Full v2.1 Workflow

```yaml
name: Evidence Pipeline

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write
  pull-requests: write
  attestations: write
  id-token: write

jobs:
  assay:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        run: |
          curl -fsSL https://getassay.dev/install.sh | sh
          assay ci --config eval.yaml --trace-file traces/golden.jsonl --strict --junit .assay/reports/junit.xml --sarif .assay/reports/sarif.json

      - name: Verify with compliance pack
        uses: Rul1an/assay-action@v2
        with:
          pack: eu-ai-act-baseline
          store: s3://my-bucket/evidence
          store_provider: aws
          store_role: ${{ secrets.AWS_ROLE_ARN }}
          attest: true
          baseline_key: main
          write_baseline: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
```

## Manual CLI Workflow

Using the CLI directly:

```yaml
jobs:
  assay:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      - name: Install Assay
        run: |
          curl -fsSL https://getassay.dev/install.sh | sh
          echo "$HOME/.local/bin" >> $GITHUB_PATH

      - name: Run tests
        run: assay ci --config eval.yaml --trace-file traces/golden.jsonl --strict --junit .assay/reports/junit.xml --sarif .assay/reports/sarif.json

      - name: Export evidence
        run: assay evidence export --profile assay-profile.yaml --out evidence.tar.gz

      - name: Lint with pack
        run: assay evidence lint evidence.tar.gz --pack eu-ai-act-baseline --format sarif > results.sarif
        continue-on-error: true

      # Assay writes reports (JUnit/SARIF) as "Best Effort".
      # Failures to write reports (e.g. bad path) print a WARNING but do not fail the step.
      # The primary artifact 'run.json' is also written on a "Best Effort" basis.

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v4
        if: always()
        with:
          sarif_file: results.sarif
```

## Troubleshooting

### No evidence bundles found

The action looks for:
- `.assay/evidence/*.tar.gz`
- `evidence/*.tar.gz`

Generate with:

```bash
assay ci --config eval.yaml --trace-file traces/golden.jsonl --strict --junit .assay/reports/junit.xml --sarif .assay/reports/sarif.json
```

### SARIF upload fails

Check `security-events: write` permission.

### PR comments not appearing

Check `pull-requests: write` permission and that the action runs on a `pull_request` event.

### OIDC authentication fails

Verify IAM trust relationship includes your repo:

```json
{
  "Condition": {
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:YOUR-ORG/YOUR-REPO:*"
    }
  }
}
```

### Compliance pack step fails

Check action outputs:
- `pack_status=missing_pack`: pack ref not found (typo or missing file path)
- `pack_status=invalid_pack`: pack schema/validation error
- `pack_status=runtime_error`: unexpected lint/runtime failure

The action also exposes `pack_error_kind` for machine-readable CI triage.

Behavior note:
- pack loading/validation/runtime errors fail the action step
- policy findings continue to follow the normal `fail_on` threshold

## Security Notes

- **Write operations** (baseline, BYOS push, attestation) only run on push to default branch
- **Fork PRs** cannot trigger write operations (GitHub Actions security model)
- **BYOS push** requires OIDC trust relationship configured in cloud IAM

## Related

- [Evidence Bundles](../concepts/traces.md)
- [Compliance Packs](../architecture/SPEC-Pack-Engine-v1.md)
- [Tool Signing](../architecture/SPEC-Tool-Signing-v1.md)
- [ADR-018: Action v2.1](../architecture/ADR-018-GitHub-Action-v2.1.md)
