Skip to content

Commit 3e8d7bc

Browse files
committed
fix(action): enforce planner contract compatibility and simplify README
1 parent e3726d4 commit 3e8d7bc

12 files changed

Lines changed: 250 additions & 229 deletions

CONTRIBUTING.md

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
# Contributing to cargo-rail-action
22

3-
Thanks for contributing.
3+
## Local Setup
44

5-
## Development Setup
5+
Required tools:
66

7-
Prerequisites:
7+
- `bash`
8+
- `python3`
9+
- `ruby`
10+
- `jq`
811

9-
- `python3` (summary renderer tests)
10-
- `ruby` (YAML parse validation)
11-
- `jq` (used by workflow verification steps)
12-
- `actionlint` (recommended)
12+
Recommended:
1313

14-
Run fixture tests locally:
14+
- `actionlint`
15+
16+
Run the local test set:
1517

1618
```bash
17-
./tests/test_summary.sh
19+
bash tests/test_summary.sh
20+
bash tests/test_contracts.sh
21+
bash tests/test_ensure_history.sh
1822
```
1923

2024
Validate `action.yaml` structure:
@@ -23,25 +27,19 @@ Validate `action.yaml` structure:
2327
ruby -ryaml -e 'YAML.load_file("action.yaml")'
2428
```
2529

26-
Recommended semantic lint:
27-
28-
```bash
29-
actionlint
30-
```
31-
32-
## Change Requirements
30+
## Expectations
3331

3432
- Keep changes focused and deterministic.
35-
- Update README and tests when output contracts change.
33+
- Update README and tests when documented behavior changes.
3634
- Preserve compatibility with documented outputs.
37-
- For changes that touch both repos, validate cargo-rail with `just check-all && just test` before opening a PR.
35+
- If a change spans both repos, validate `cargo-rail` with `just check && just test`.
3836

3937
## Pull Requests
4038

4139
- Use a clear title and summary.
42-
- Include behavior changes and migration notes if outputs changed.
40+
- Call out output or compatibility changes.
4341
- Link related issues when applicable.
4442

4543
## Security
4644

47-
Please do not open public issues for vulnerabilities. See [SECURITY.md](SECURITY.md).
45+
Do not open public issues for vulnerabilities. See [SECURITY.md](SECURITY.md).

README.md

Lines changed: 39 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
# cargo-rail-action
22

3-
> GitHub Action wrapper for planner gates and `scope-json`.
3+
> GitHub Action for `cargo rail plan` gates and execution scope.
44
5-
[![Test](https://github.com/loadingalias/cargo-rail-action/actions/workflows/test.yaml/badge.svg)](https://github.com/loadingalias/cargo-rail-action/actions/workflows/test.yaml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6-
7-
## What It Does
8-
9-
- Runs `cargo rail plan` in planner-owned GitHub transport mode.
10-
- Publishes planner gates and `scope-json` for job gating.
11-
- Keeps CI behavior aligned with local `plan` + `run` workflows.
12-
13-
Minimum planner contract: `cargo-rail >= 0.11.0`.
5+
[![Test](https://github.com/loadingalias/cargo-rail-action/actions/workflows/test.yaml/badge.svg)](https://github.com/loadingalias/cargo-rail-action/actions/workflows/test.yaml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
146

157
## Quick Start
168

@@ -23,213 +15,71 @@ jobs:
2315
runs-on: ubuntu-latest
2416
steps:
2517
- uses: actions/checkout@v4
26-
with:
27-
fetch-depth: 1
2818

2919
- uses: loadingalias/cargo-rail-action@v4
3020
id: rail
31-
with:
32-
version: "0.11.0"
33-
34-
- name: Run targeted tests
35-
if: steps.rail.outputs.test == 'true'
36-
env:
37-
SCOPE_JSON: ${{ steps.rail.outputs.scope-json }}
38-
run: |
39-
MODE=$(echo "$SCOPE_JSON" | jq -r '.mode')
40-
if [ "$MODE" = "workspace" ]; then
41-
cargo nextest run --workspace
42-
elif [ "$MODE" = "crates" ]; then
43-
mapfile -t CRATES < <(echo "$SCOPE_JSON" | jq -r '.crates[]')
44-
ARGS=()
45-
for crate in "${CRATES[@]}"; do
46-
ARGS+=(-p "$crate")
47-
done
48-
cargo nextest run "${ARGS[@]}"
49-
else
50-
echo "planner enabled test surface, but no package-scoped work was selected"
51-
fi
21+
22+
- name: Run selected CI profile
23+
if: steps.rail.outputs.build == 'true' || steps.rail.outputs.test == 'true'
24+
run: cargo rail run --since "${{ steps.rail.outputs.base-ref }}" --profile ci
5225

5326
- name: Run docs pipeline
5427
if: steps.rail.outputs.docs == 'true'
5528
run: cargo rail run --since "${{ steps.rail.outputs.base-ref }}" --surface docs
5629
```
5730
58-
Use the stable major tag `@v4`, or pin a commit SHA for maximum reproducibility. Pin `version` for deterministic
59-
`cargo-rail` installs.
31+
The default `version` tracks the latest published stable `cargo-rail` release this action is tested against. Set `version` only when you need a different published release.
32+
33+
## What It Publishes
34+
35+
Minimal mode publishes:
36+
37+
- `build`
38+
- `test`
39+
- `bench`
40+
- `docs`
41+
- `infra`
42+
- `scope-json`
43+
- `base-ref`
44+
- `custom_<name>` for custom surfaces
45+
46+
Debug mode adds:
6047

61-
The action handles shallow checkouts intentionally: raw commit SHAs are fetched directly when possible, and broader
62-
history fetch only happens when a branch-based comparison still needs merge-base history.
48+
- `plan-json`
49+
50+
`scope-json` is the execution handoff. `plan-json` is for debugging.
6351

6452
## Inputs
6553

6654
| Input | Default | Description |
6755
|---|---|---|
68-
| `version` | `0.11.0` | `cargo-rail` version to install (use `latest` only if you intentionally want floating upgrades) |
56+
| `version` | `0.12.0` | Published `cargo-rail` release to install |
6957
| `checksum` | `required` | `required`, `if-available`, or `off` |
7058
| `since` | auto | Git ref for planner comparison |
71-
| `args` | `""` | Extra planner args except `-f`/`--format`, `--json`, and `-o`/`--output` |
59+
| `args` | `""` | Extra planner args except format/output flags |
7260
| `working-directory` | `.` | Workspace directory |
7361
| `token` | `${{ github.token }}` | Token for release download API |
74-
| `mode` | `minimal` | `minimal` (recommended) or `debug` |
75-
76-
**Optional `args` examples:**
77-
78-
```yaml
79-
# Explain plan decisions in job logs
80-
- uses: loadingalias/cargo-rail-action@v4
81-
with:
82-
args: '--explain'
83-
84-
# Use different comparison ref
85-
- uses: loadingalias/cargo-rail-action@v4
86-
with:
87-
since: 'origin/develop'
88-
```
89-
90-
## Outputs
91-
92-
### Modes and gates
62+
| `mode` | `minimal` | `minimal` or `debug` |
9363

94-
**Gates** are boolean outputs (`true`/`false`) used in job conditions. They answer: "should this CI surface run?"
64+
## Compatibility
9565

96-
**How gates work:**
97-
1. The planner (`cargo rail plan`) analyzes changed files against your `.config/rail.toml` rules
98-
2. It classifies changes into **surfaces**: `build`, `test`, `bench`, `docs`, `infra`
99-
3. The action publishes these as GitHub Action outputs: `build=true`, `test=false`, etc.
100-
4. You use these in job `if:` conditions to skip unnecessary work
66+
The action validates both planner contracts before publishing outputs.
10167

102-
**Why gates exist:**
103-
- Deterministic: same plan locally and in CI (run `cargo rail plan --merge-base` to preview)
104-
- Configurable: you define infrastructure files, doc-only paths, etc. in `rail.toml`
105-
- Composable: combine gates (`if: steps.rail.outputs.build == 'true' || steps.rail.outputs.infra == 'true'`)
68+
- `plan_contract_version` covers the full diagnostic planner payload
69+
- `scope_contract_version` covers the execution handoff payload
10670

107-
**Modes** control what outputs are published:
108-
- `minimal` (default): gates, `scope-json`, `base-ref`, and per-custom-surface booleans
109-
- `debug`: minimal outputs plus `plan-json`
71+
They are separate on purpose. `scope-json` should be able to stay stable even when `plan-json` grows.
11072

111-
### Minimal mode (default)
73+
## Notes
11274

113-
| Output | Type | Use |
114-
|---|---|---|
115-
| `build` | `true`/`false` | Gate build jobs — set when code changes affect compilation |
116-
| `test` | `true`/`false` | Gate test jobs — set when changes affect test outcomes |
117-
| `bench` | `true`/`false` | Gate benchmark jobs — set when changes affect performance tests |
118-
| `docs` | `true`/`false` | Gate docs jobs — set for doc comments, README, markdown changes |
119-
| `infra` | `true`/`false` | Gate infra jobs — set for CI config, scripts, toolchain changes |
120-
| `scope-json` | JSON | Compact execution-scope payload emitted by `cargo-rail` |
121-
| `base-ref` | string | Git ref for downstream `run` calls (`--since "${{ steps.rail.outputs.base-ref }}"`) |
122-
| `custom_<name>` | `true`/`false` | Custom surface gates, with punctuation normalized to `_` |
123-
124-
### Debug mode (`mode: debug`)
125-
126-
Includes all minimal outputs plus the full planner contract.
127-
128-
| Output | Type | Description |
129-
|---|---|---|
130-
| `plan-json` | JSON | Full deterministic planner contract for debugging and deep inspection |
131-
132-
**Note:** Most workflows should use `minimal` mode. `scope-json` is the execution handoff; use it for package selection in scripts and jobs. `plan-json` is forensic/debug output only.
133-
134-
Example:
135-
136-
```yaml
137-
- name: Show targeted crates
138-
run: echo '${{ steps.rail.outputs.scope-json }}' | jq -r '.crates[]'
139-
```
140-
141-
Custom surfaces are exported as `custom_<name>` outputs. Example:
142-
143-
```yaml
144-
- name: Run benchmark suite
145-
if: steps.rail.outputs.custom_benchmarks == 'true'
146-
run: cargo bench --workspace
147-
```
148-
149-
## Security and Runtime
150-
151-
### Installation order
152-
153-
The action tries installation methods in this order:
154-
155-
1. **Cached binary** (fastest) — uses GitHub Actions cache (`actions/cache`)
156-
- Why: Subsequent runs on same runner are instant (no download)
157-
- Cache reuse requires exact version match; when `version: latest`, the action resolves latest first, then compares
158-
159-
2. **Release binary** (fast) — downloads from GitHub Releases
160-
- Why: Pre-built binaries for all supported platforms, verified against `SHA256SUMS`
161-
- Supports all platforms except macOS Intel (see below)
162-
163-
3. **`cargo-binstall`** (fallback) — installs via binstall if available
164-
- Why: Faster than `cargo install` (downloads binary instead of compiling)
165-
- Used when platform is unsupported for release binaries
166-
167-
4. **`cargo install`** (slowest, last resort) — compiles from source
168-
- Why: Guaranteed to work on any platform with Rust toolchain
169-
- Adds ~2-5 minutes to workflow run time
170-
171-
### Checksum verification
172-
173-
Default is `required`: download `SHA256SUMS` and verify binary integrity.
174-
175-
**Why:** Supply chain security. Detects corrupted downloads and tampering.
176-
177-
**Options:**
178-
- `required` (default): fail if checksum missing or mismatched
179-
- `if-available`: verify if `SHA256SUMS` exists, skip if not (for pre-release testing)
180-
- `off`: skip verification (not recommended for production)
181-
182-
### macOS Intel unsupported
183-
184-
macOS Intel (`x86_64-apple-darwin`) binaries are **not** published in releases.
185-
186-
**Why:** GitHub Actions deprecated `macos-latest` Intel runners in 2024. All macOS runners are now ARM (`macos-14`, `macos-15`). Publishing Intel binaries wastes release bandwidth for a platform no longer used in CI.
187-
188-
**Workaround:** The action falls back to `cargo-binstall` or `cargo install` on Intel Macs (primarily for local testing, not CI).
189-
190-
## Proven On Large Repos
191-
192-
Action + planner flow validated on production repos with real merge history:
193-
194-
| Repository | Crates | Fork |
195-
|---|---|---|
196-
| [tokio-rs/tokio](https://github.com/tokio-rs/tokio) | 10 | [Config + Guide](https://github.com/loadingalias/cargo-rail-testing/tree/change-detection/tokio) |
197-
| [helix-editor/helix](https://github.com/helix-editor/helix) | 14 | [Config + Guide](https://github.com/loadingalias/cargo-rail-testing/tree/change-detection/helix) |
198-
| [meilisearch/meilisearch](https://github.com/meilisearch/meilisearch) | 23 | [Config + Guide](https://github.com/loadingalias/cargo-rail-testing/tree/change-detection/meilisearch) |
199-
| [helixdb/helix-db](https://github.com/helixdb/helix-db) | 6 | [Config + Guide](https://github.com/loadingalias/cargo-rail-testing/tree/change-detection/helix-db) |
200-
201-
**Validation forks**: [cargo-rail-testing](https://github.com/loadingalias/cargo-rail-testing) — full configs, integration guides, and reproducible artifacts.
202-
203-
**Real-world benefits:**
204-
- Docs-only PRs skip build/test entirely (run only docs checks)
205-
- Infrastructure changes (CI config, scripts) trigger full rebuild (no false-negatives)
206-
- Isolated crate changes run targeted tests (not entire workspace)
207-
- Plan matches local behavior (`cargo rail plan --merge-base` previews CI gates)
208-
209-
**Measured impact (last 20 commits per repo):**
210-
211-
| Repository | Could Skip Build | Could Skip Tests | Targeted (Not Full Run) |
212-
|---|---:|---:|---:|
213-
| tokio | 10% | 0% | 95% |
214-
| meilisearch | 35% | 35% | 60% |
215-
| helix | 30% | 30% | 40% |
216-
| helix-db | 10% | 10% | 75% |
217-
| **Aggregate (80 commits)** | **21%** | **19%** | **68%** |
218-
219-
**Each fork includes:**
220-
- `.config/rail.toml` — production-ready config with custom surfaces
221-
- `docs/cargo-rail-integration-guide.md` — step-by-step CI integration with workflow examples
222-
- `docs/CHANGE_DETECTION_METRICS.md` — measured impact analysis on recent commits
223-
224-
Reproducible command matrix and examples live in `cargo-rail`:
225-
226-
- https://github.com/loadingalias/cargo-rail/blob/main/examples/validation-protocol.md
227-
- https://github.com/loadingalias/cargo-rail/tree/main/examples/change_detection
75+
- Checksum verification is on by default.
76+
- Shallow checkouts are handled automatically when more history is needed.
77+
- Use `@v4` for the stable action major, or pin a commit SHA if you want maximum reproducibility.
22878

22979
## Getting Help
23080

231-
- Action issues: [GitHub Issues](https://github.com/loadingalias/cargo-rail-action/issues)
232-
- Core tool: [loadingalias/cargo-rail](https://github.com/loadingalias/cargo-rail)
81+
- Action Issues: [GitHub Issues](https://github.com/loadingalias/cargo-rail-action/issues)
82+
- Core Issues: [loadingalias/cargo-rail](https://github.com/loadingalias/cargo-rail/issues)
23383

23484
## Contributing
23585

@@ -239,10 +89,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
23989

24090
See [SECURITY.md](SECURITY.md).
24191

242-
## Code of Conduct
243-
244-
See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
245-
24692
## License
24793

24894
Licensed under [MIT](LICENSE).

SECURITY.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
Security fixes are applied to the latest release series.
66

7-
| Version | Supported |
7+
| Release line | Status |
88
|---|---|
9-
| Latest | :white_check_mark: |
10-
| Older | :x: |
9+
| Latest | supported |
10+
| Older | unsupported |
1111

1212
## Reporting a Vulnerability
1313

0 commit comments

Comments
 (0)