Skip to content

Commit e85e1e9

Browse files
docs: CONTRIBUTING reads in short sections, and migrations live in RUNBOOK
1 parent 3bc76ee commit e85e1e9

2 files changed

Lines changed: 41 additions & 30 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ Include a minimal failing script, the command used to run it, and the expected a
1010

1111
For a large change, open an issue or email [c.sutton.dylan@gmail.com](mailto:c.sutton.dylan@gmail.com) first so it can be accepted once ready.
1212

13-
Pull requests are welcome everywhere the Apache 2.0 License applies, which is every part except `lang/`, `site/` and `infra/`. `lang/` is under PolyForm Noncommercial and the other two carry no license, and all three stay free of anyone else's copyright, so a patch to them is closed with thanks and written again by the maintainer. Issues about them are welcome all the same, since a report or a suggestion costs you nothing and carries no copyright.
13+
Pull requests are welcome in every part under the Apache 2.0 License, which is all but `lang/`, `site/` and `infra/`. Those three stay free of anyone else's copyright, so a patch to them is closed with thanks and written again by the maintainer, while an issue about them is always welcome. The licenses are in [README.md](README.md#license).
1414

1515
- New behavior comes with tests.
1616
- Docs describe the code as it is after the change.
1717
- Changes to the language, the CLI, or package behavior update `skill/SKILL.md`, and `cargo test -p skill` stays green.
1818
- Significant changes run the [fuzzer](https://edgepython.com/docs/implementation/fuzzing) to check for new crashes or slowdowns.
1919
- Changes to the engine keep `cargo run -p bench --profile cli` passing.
2020

21-
Run these from the repo root before sending. The maintainer runs CI once the PR is open.
21+
Run these from the repo root before sending. The maintainer runs CI once the pull request is open.
2222

2323
```bash
2424
cargo wasm
@@ -35,37 +35,49 @@ Comments are one line, at most one per block, and deleted when redundant. No fil
3535

3636
## Building
3737

38-
The root Cargo workspace holds the engine, `abi`, `pdk`, `skill`, `lang` and `bench`. `cli/` and `fuzz/` are separate workspaces. `rust-toolchain.toml` pins every build to one Rust release, so the `compiler.wasm` the bench counts is the same everywhere.
38+
The root Cargo workspace holds the engine, `abi`, `pdk`, `skill`, `lang` and `bench`, and `cli/` and `fuzz/` are workspaces of their own. `rust-toolchain.toml` pins every build to one Rust release, so the bench counts the same `compiler.wasm` everywhere.
3939

4040
```bash
4141
cargo wasm # compiler.wasm, CI ships a smaller build
42+
cargo wasm-cli # the speed build releases embed
4243
cargo build --release # the .rlib Rust embedders link
44+
(cd js && deno run -A npm:typescript@5.9.3/tsc -p tsconfig.json && deno run -A npm:typescript@5.9.3/tsc -p tsconfig.worker.json && deno bundle --platform browser --format iife src/worker/worker.ts -o dist/worker/bundle.js)
45+
(cd cli && cargo build --release)
46+
deno lint js/
4347
```
4448

45-
`cargo wasm` turns on the `runtime` feature, the wrapper that makes `compiler.wasm` with its exports, host imports, allocator and panic handler. Without it the crate is the engine alone, for a wasm that links it as a library, such as a plugin that only parses.
49+
- `cargo wasm` turns on the `runtime` feature, the wrapper that makes `compiler.wasm` with its exports, host imports, allocator and panic handler. Without it the crate is the engine alone, for a wasm that links it as a library.
50+
- `cli/` embeds `compiler.wasm` and `js/dist` at build time, so build them first or point `EDGE_COMPILER_WASM` and `EDGE_JS_DIST` at copies.
51+
- The CLI runs the system calls of `js/src/system` in SpiderMonkey through `mozjs`, which compiles from source on the first build and wants clang, python3 and `llvm-objdump` on the path. On macOS a symlink named `llvm-objdump` to `/usr/bin/objdump` serves, and CI sets `MOZJS_FROM_SOURCE=1` on every target.
52+
- The static Linux binary builds inside `rust:alpine` through [`musl.sh`](.github/actions/cli/musl.sh), since musl-gcc has no C++.
53+
54+
## Testing
4655

47-
`cli/` embeds `compiler.wasm` and the JS host from `js/dist` at build time. Build them first, or point `EDGE_COMPILER_WASM` and `EDGE_JS_DIST` at copies. Releases embed the speed build from `cargo wasm-cli`.
56+
### Engine
4857

49-
The system calls in `js/src/system` run in SpiderMonkey inside the CLI, through `mozjs`, which compiles SpiderMonkey from source on the first build and wants clang, python3 and `llvm-objdump` on the path. On macOS a symlink named `llvm-objdump` to `/usr/bin/objdump` serves, and CI sets `MOZJS_FROM_SOURCE=1` on every target. The static Linux binary builds inside `rust:alpine` through [`.github/actions/cli/musl.sh`](.github/actions/cli/musl.sh), since musl-gcc has no C++.
58+
`cargo test --release` runs `tests/cases/vm.json` under `Limits::sandbox()`, so a budget, heap, or call-depth regression fails as a `MemoryError` or `RecursionError` instead of hanging. Every case must fit that budget, and two cases equal in every field fail the suite.
5059

51-
The JS host in `js/src` is TypeScript, linted with `deno lint js/`.
60+
### Bench
5261

53-
## Testing
62+
`cargo run -p bench --profile cli` runs every case of `vm.json` on the `cargo wasm-cli` build, and stops when that build is older than `src/`. It counts the WebAssembly instructions each case executes and prices each at 0.82 ns, the 822756 gas that `wasm_regular_op_cost` sets in `core/parameters/res/runtime_configs/parameters.yaml` of nearcore, at the 1 ms per Tgas its gas estimator budgets, so `bench/.snapshot` keeps reference seconds that come out the same on every machine.
5463

55-
`cargo test --release` runs `tests/cases/vm.json` under `Limits::sandbox()`, so a budget, heap, or call-depth regression fails as a `MemoryError` or `RecursionError` instead of hanging. Every fixture must fit that budget.
64+
The Bench job fails a pull request when
5665

57-
`cargo run -p bench --profile cli` runs every case of `tests/cases/vm.json` on the `compiler.wasm` that `cargo wasm-cli` builds, and stops when that build is older than `src/`. It counts the WebAssembly instructions each case executes and prices each at 0.82 ns, the 822756 gas that `wasm_regular_op_cost` sets in `core/parameters/res/runtime_configs/parameters.yaml` of nearcore, taken at the 1 ms per Tgas that its gas estimator budgets, so `bench/.snapshot` keeps reference seconds that come out the same on every machine and every run, and a change shows case by case in its diff. The Bench job holds every pull request to it and fails when the snapshot was taken with another Rust, when a case is missing from it or an entry has no case, when the geometric mean moves past its threshold slower or faster, or when a single case moves past its own. Every run prints how the cases already in the snapshot moved, even when another rule fails. `--update` reports the change and then takes the snapshot again, and a faster engine takes it too so the next change is measured from where the code stands. `tests/vm.rs` also fails on two cases equal in every field.
66+
- the snapshot was taken with another Rust,
67+
- a case is missing from it, or an entry has no case,
68+
- the geometric mean moves past its threshold, slower or faster,
69+
- a single case moves past its own threshold.
5870

59-
The other suites read the builds from a CDN, the way CI does. Build what you changed, stage it, and serve it locally with no credentials.
71+
Every run prints how the cases already in the snapshot moved, even when another rule fails. `--update` reports the change and takes the snapshot again, and a faster engine takes it too, so the next change is measured from where the code stands.
72+
73+
### Hosts
74+
75+
The JS, CLI and skill suites read the builds from a CDN, the way CI does. Stage what you built and serve it locally with no credentials, then point the suites at it. A suite without `EDGE_CDN_BASE` fails before it starts, so no test reaches the production CDN.
6076

6177
```bash
62-
cargo wasm
63-
(cd js && deno run -A npm:typescript@5.9.3/tsc -p tsconfig.json && deno run -A npm:typescript@5.9.3/tsc -p tsconfig.worker.json && deno bundle --platform browser --format iife src/worker/worker.ts -o dist/worker/bundle.js)
6478
cd infra && npm ci && npm run stage -- ../_cdn && npm run cdn:local -- ../_cdn # keep it running
6579
```
6680

67-
Then point the suites at it. A suite without `EDGE_CDN_BASE` fails before it starts, so no test reaches the production CDN.
68-
6981
```bash
7082
export EDGE_CDN_BASE=http://127.0.0.1:8788
7183
deno run -A npm:playwright install --with-deps chromium # once
@@ -75,15 +87,16 @@ cargo build --release --target wasm32-unknown-unknown -p slugify-mod
7587
cargo test -p skill # every executable cell of skill/SKILL.md
7688
```
7789

78-
The CLI's lock cases serve their own registry on loopback through `EDGE_SITE_BASE`, and only `edge add` of an unknown name and `edge publish` with a bad token still reach the production one.
90+
The lock cases of the CLI serve their own registry on loopback through `EDGE_SITE_BASE`. Only `edge add` of an unknown name and `edge publish` with a bad token still reach the production one.
7991

80-
`fuzz/` runs coverage-guided fuzzing of the lexer, parser, and VM on [cargo-afl](https://github.com/rust-fuzz/afl.rs). Campaigns and crash triage are in [Fuzzing](https://edgepython.com/docs/implementation/fuzzing).
92+
### Fuzzing and Miri
8193

82-
[`.github/workflows/miri.yml`](.github/workflows/miri.yml) interprets the same corpora under [Miri](https://github.com/rust-lang/miri) once a day, on the nightly it keeps to itself, for undefined behavior the native build runs past. Run one module with `cargo +nightly miri test -p edge-python --test tests vm::`.
94+
- `fuzz/` runs coverage-guided fuzzing of the lexer, parser, and VM on [cargo-afl](https://github.com/rust-fuzz/afl.rs). Campaigns and crash triage are in [Fuzzing](https://edgepython.com/docs/implementation/fuzzing).
95+
- [`miri.yml`](.github/workflows/miri.yml) interprets the same corpora under [Miri](https://github.com/rust-lang/miri) once a day, on a nightly of its own, for undefined behavior the native build runs past. Run one module with `cargo +nightly miri test -p edge-python --test tests vm::`.
8396

8497
## Site and Docs
8598

86-
`docs/` holds MDX pages ordered by numeric prefix. `site/` is Astro on Cloudflare Workers with a D1 database, and renders them under `/docs`. It runs locally on miniflare with no credentials, and the sign-in code prints to the terminal.
99+
`docs/` holds MDX pages ordered by numeric prefix, and `site/` renders them under `/docs`. The site is Astro on Cloudflare Workers with a D1 database, and runs locally on miniflare with no credentials, printing the sign-in code to the terminal.
87100

88101
```bash
89102
cd site && npm ci
@@ -94,16 +107,12 @@ npx playwright install --with-deps chromium firefox webkit # once
94107
npm test # builds the Worker and drives it in three engines
95108
```
96109

97-
[`site/src/draft.ts`](site/src/draft.ts) names what is still being built, a path for a page and a fragment for a surface inside one. Under `EDGE_ENV=prod` a page answers 404 and the rest is rewritten out of the html.
98-
99-
An `edge-python` code block followed by an `output` block becomes a playground on the real engine, so every example and its output stay a verifiable pair.
100-
101-
A page nests one folder deep at most, carries a numeric prefix on every path segment, opens with a closed frontmatter block holding a `title` and a `description`, and has exactly one top-level heading. `npm run build` refuses a page that breaks any of it, and `edge build` holds a package's own `docs` directory to the same rules. Both read [`site/src/lib/docs/convention.ts`](site/src/lib/docs/convention.ts) and [`cli/src/docs.rs`](cli/src/docs.rs), kept in step by [`tests/cases/docs.json`](tests/cases/docs.json), so a rule changed on one side fails on the other.
110+
- [`site/src/draft.ts`](site/src/draft.ts) names what is still being built, a path for a page and a fragment for a surface inside one. Under `EDGE_ENV=prod` a page answers 404 and the rest is rewritten out of the html.
111+
- An `edge-python` code block followed by an `output` block becomes a playground on the real engine, so every example and its output stay a verifiable pair.
112+
- A page nests one folder deep at most, carries a numeric prefix on every path segment, opens with a closed frontmatter block holding a `title` and a `description`, and has exactly one top-level heading. `npm run build` refuses a page that breaks any of it, and `edge build` holds the `docs` directory of a package to the same rules. Both read [`convention.ts`](site/src/lib/docs/convention.ts) and [`docs.rs`](cli/src/docs.rs), kept in step by [`docs.json`](tests/cases/docs.json), so a rule changed on one side fails on the other.
102113

103114
## Infra and CI
104115

105-
`infra/` declares every Cloudflare resource in code and is checked with `npm run check` and `npm test`. `stage` and `cdn:local` run locally. The other scripts deploy and read `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`.
106-
107-
[`.github/workflows/main.yml`](.github/workflows/main.yml) runs CI and CD, with each part in a composite action under [`.github/actions/`](.github/actions). Build jobs stage their outputs on `cdn.tmp.edgepython.com`, test jobs run every suite against them, and pushes to `main` promote the tested tree to `dev.edgepython.com`.
108-
109-
[`site/db/schema.sql`](site/db/schema.sql) is the whole database as it stands, and every local, test and dev database is built from it. Production keeps its rows, so a schema change also adds a file to `site/db/migrations/` that a `v` tag applies before the Worker ships, and the file is deleted once production has run it. `npm run schema` in `infra/` reads production and checks that it plus the pending migrations matches `schema.sql`, which the Database job warns about on `main` and enforces on a tag.
116+
- `infra/` declares every Cloudflare resource in code and is checked with `npm run check` and `npm test`. `stage` and `cdn:local` run locally, and the other scripts deploy with `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`.
117+
- [`main.yml`](.github/workflows/main.yml) runs CI and CD, each part a composite action under [`.github/actions/`](.github/actions). Build jobs stage their outputs on `cdn.tmp.edgepython.com`, test jobs run every suite against them, and a push to `main` promotes the tested tree to `dev.edgepython.com`.
118+
- [`site/db/schema.sql`](site/db/schema.sql) is the whole database as it stands, and every local, test and dev database is built from it. How production takes a schema change is in [RUNBOOK.md](RUNBOOK.md#migrations).

‎RUNBOOK.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ A push to `main` promotes to dev and a `v` tag ships to production, and either o
1818

1919
## Migrations
2020

21-
A schema change edits `site/db/schema.sql` and adds its step to `site/db/migrations/` in the same commit. Production runs the step on the next `v` tag, and after that release you delete the file by hand, which the Database job warns about until you do.
21+
A schema change edits `site/db/schema.sql` and adds its step to `site/db/migrations/` in the same commit, since production keeps its rows. The next `v` tag runs the step before the Worker ships, and after that release you delete the file by hand, which the Database job warns about until you do.
22+
23+
`npm run schema` in `infra/` reads production and checks that it plus the pending migrations matches `schema.sql`. The Database job warns about a mismatch on `main` and enforces it on a tag.

0 commit comments

Comments
 (0)