Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
532 changes: 388 additions & 144 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions .github/workflows/full-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Full validation

on:
schedule:
- cron: '17 */4 * * *'
workflow_dispatch:
inputs:
publish:
description: 'Publish after the compact gate passes (canonical main only)'
type: boolean
default: false

permissions:
contents: read

# Keep a started snapshot; replace obsolete pending requests. This literal
# namespace is different from the reusable validation workflow's lock.
concurrency:
group: full-background-${{ github.ref }}
cancel-in-progress: false

jobs:
preflight:
if: github.event_name != 'schedule' || github.repository == 'rubys/roundhouse'
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
spinel: ${{ steps.inputs.outputs.spinel }}
publish: ${{ steps.inputs.outputs.publish }}
steps:
- name: Resolve the immutable inputs and publication boundary
id: inputs
env:
GH_TOKEN: ${{ github.token }}
EVENT: ${{ github.event_name }}
REQUEST_PUBLISH: ${{ inputs.publish || false }}
run: |
publish=false
if [ "$EVENT" = schedule ] || [ "$REQUEST_PUBLISH" = true ]; then
test "$GITHUB_REPOSITORY" = rubys/roundhouse
test "$GITHUB_REF" = refs/heads/main
publish=true
fi
spinel=$(gh api repos/matz/spinel/commits/master --jq .sha) || spinel=master
if ! [[ "$spinel" =~ ^[0-9a-f]{40}$ ]]; then spinel=master; fi
echo "spinel=$spinel" >> "$GITHUB_OUTPUT"
echo "publish=$publish" >> "$GITHUB_OUTPUT"
- name: Report fresh validation inputs
env:
SPINEL: ${{ steps.inputs.outputs.spinel }}
run: |
printf 'Source snapshot: `%s`\n\nRequested Spinel: `%s`\n\nEvery cycle executes validation freshly, including floating Rails/package/system inputs. Actual Spinel revisions are recorded in the toolchain and archive evidence.\n' "$GITHUB_SHA" "$SPINEL" >> "$GITHUB_STEP_SUMMARY"

validation:
needs: preflight
permissions:
contents: read
actions: read
uses: ./.github/workflows/ci.yml
with:
full: true
publish: ${{ needs.preflight.outputs.publish == 'true' }}
spinel_revision: ${{ needs.preflight.outputs.spinel }}

# No Pages/OIDC permissions are available to the reusable validator or any
# PR. Extra-target failures must not hide useful broken-emit repro archives.
deploy:
needs: [preflight, validation]
if: ${{ !cancelled() && needs.preflight.outputs.publish == 'true' && needs.validation.outputs.publication-ready == 'true' && github.repository == 'rubys/roundhouse' && github.ref == 'refs/heads/main' }}
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
runs-on: ubuntu-latest
timeout-minutes: 30
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Refuse to publish a superseded main snapshot
env:
GH_TOKEN: ${{ github.token }}
VALIDATED_SHA: ${{ github.sha }}
run: test "$(gh api repos/rubys/roundhouse/git/ref/heads/main --jq .object.sha)" = "$VALIDATED_SHA"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
25 changes: 16 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,14 @@ defect even if the build is green.
you changed. End commit messages with the standard `Co-Authored-By`
trailer.
- **Outside contributors: fork, and open a pull request against `main`.**
CI runs the full matrix on a PR — every toolchain lane, the DOM compare
against live Rails, the Spinel lanes — so you do not need every
toolchain locally; CI is the oracle for the lanes you cannot run.
CI runs a compact floor plus targeted lanes. For broad/risky changes,
ask a maintainer to apply `ci:full` for the complete PR matrix.
This expands validation only; PR runs never publish. Drafts retain
fixture + unit coverage even with the label. Full main validation and
publication are scheduled every four hours and execute freshly every cycle.
`CI summary` reports selected checks, not a mandatory merge gate;
maintainers decide when to merge. See [CI coverage](docs/ci-reuse.md).
You do not need every toolchain locally; CI covers the missing lanes.
Before opening one: `bin/rh fixture` (the test fixtures are generated,
not checked in — see below), `cargo test --lib` plus the targeted
integration test for what you touched, and a test that pins the fix.
Expand All @@ -110,15 +115,17 @@ defect even if the build is green.
`cargo test --all-targets` at milestones. Real-toolchain tests are
`#[ignore]`-gated (`cargo test --test <target>_toolchain -- --ignored`); CI
runs each in its own job.
- **CI is deliberately not uniformly gating.** The core `cargo test` job gates.
The ~5 `continue-on-error: true` jobs track upstream Spinel and other moving
toolchains on purpose — **red there is a signal to read, not a regression to
shim away.** Don't add workarounds just to make an advisory job green.
- **CI is deliberately not uniformly gating.** The core `cargo test` job is
non-advisory. Jobs marked `continue-on-error: true` track current Spinel
master and other moving toolchains on purpose — **red is a signal, not a
regression to shim away.** A Spinel red can come from Roundhouse runtime, RBS
or packaging, or from upstream; it is not by itself proof of an upstream
fault. Don't add workarounds just to make an advisory job green.

## The actual goal

The endpoint is not "does the fixture compile." It is a **per-target ledger of
how much of Rails transpiles** — the honest unsupported list, driven down over
time. Don't trade that real goal for a locally reachable one. The proving lanes
today are the real-blog fixture (every target, DOM-equivalent to Rails on every
push) and lobsters/Mastodon on the inference + Spinel path (see README).
today are the real-blog fixture (every target, DOM-equivalent to Rails in full
validation) and lobsters/Mastodon on the inference + Spinel path (see README).
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ oracle: the same URL fetched from Rails and from each target must
produce the same response — emitted tests, a differential compare
against live Rails (DOM node for DOM node, JSON value for value), and
browser end-to-end tests for what a static diff can't reach — and
every target on the list passes it on every push. →
the full target matrix runs in scheduled validation, with a compact
floor and targeted additions on pull requests. →
[`--target`](docs/guide/transpile.md) · [targets](docs/guide/targets.md)
· [what of Rails comes through](docs/guide/rails-coverage.md) ·
[verifying](docs/guide/verifying.md)
Expand All @@ -73,7 +74,7 @@ compiled targets it has the closest behavior to Rails by a distance,
and will for the foreseeable future, because it runs the framework
runtime itself rather than a translation of it. Basecamp's Campfire
runs this way — every page and every cable frame compared against
live Rails on every push, and a
live Rails in the full validation cycle, and a
[Docker archive](https://rubys.github.io/roundhouse/apps/campfire.html)
you can run in minutes. → [Spinel](docs/guide/spinel.md)

Expand Down Expand Up @@ -106,7 +107,7 @@ the prerequisites.
- [**Campfire**](https://rubys.github.io/roundhouse/apps/campfire.html)
— the compiled product, as a Docker archive.
- [**Browse**](https://rubys.github.io/roundhouse/browse/) — what every
emitter produces from the blog fixture, updated on each push.
emitter produces from the blog fixture, refreshed by scheduled full validation.
- [**Bench**](https://rubys.github.io/roundhouse/bench/) — throughput,
memory and latency across the live targets on a fixed box, against
Rails as it ships.
Expand Down Expand Up @@ -168,13 +169,17 @@ its predecessors; [`WHY.md`](WHY.md) is why do it at all.
## Contributing

Issues and pull requests are both welcome, and a PR does not need a
conversation first: CI runs the whole matrix on every pull request —
the unit suite, each target's toolchain, the DOM compare against live
Rails, the Spinel lanes — so a change you can only test partially on
your machine is tested fully there. The `unit` job gates; the Spinel
and Campfire jobs marked `continue-on-error` track moving toolchains,
and red there is a signal to read, not a check to make pass. Setup,
the test cycle, and what a PR should carry are in
conversation first: CI runs a compact correctness/runtime floor plus
targeted checks. Ask a maintainer to apply `ci:full` for full PR validation;
the label expands tests, never publication. The complete matrix also runs
freshly every four hours. See [CI coverage](docs/ci-reuse.md) for requesting
full/fresh validation and the separate publication controls.
`CI summary` reports selected checks without imposing a merge requirement;
maintainers decide when to merge. Spinel and Campfire jobs marked
`continue-on-error` track current Spinel master, and red there is a signal
to investigate Roundhouse runtime, RBS or packaging as well as possible
upstream drift—not proof of an upstream fault. Setup, the test cycle,
and what a PR should carry are in
[`DEVELOPMENT.md`](DEVELOPMENT.md); the invariants not to break are in
[`AGENTS.md`](AGENTS.md).

Expand Down
Loading
Loading