You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e85e1e9
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: CONTRIBUTING.md
+38-29Lines changed: 38 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,15 +10,15 @@ Include a minimal failing script, the command used to run it, and the expected a
10
10
11
11
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.
12
12
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).
14
14
15
15
- New behavior comes with tests.
16
16
- Docs describe the code as it is after the change.
17
17
- Changes to the language, the CLI, or package behavior update `skill/SKILL.md`, and `cargo test -p skill` stays green.
18
18
- Significant changes run the [fuzzer](https://edgepython.com/docs/implementation/fuzzing) to check for new crashes or slowdowns.
19
19
- Changes to the engine keep `cargo run -p bench --profile cli` passing.
20
20
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.
22
22
23
23
```bash
24
24
cargo wasm
@@ -35,37 +35,49 @@ Comments are one line, at most one per block, and deleted when redundant. No fil
35
35
36
36
## Building
37
37
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.
39
39
40
40
```bash
41
41
cargo wasm # compiler.wasm, CI ships a smaller build
42
+
cargo wasm-cli # the speed build releases embed
42
43
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/
43
47
```
44
48
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
46
55
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
48
57
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.
50
59
51
-
The JS host in `js/src` is TypeScript, linted with `deno lint js/`.
60
+
### Bench
52
61
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.
54
63
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
56
65
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.
58
70
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.
60
76
61
77
```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)
64
78
cd infra && npm ci && npm run stage -- ../_cdn && npm run cdn:local -- ../_cdn # keep it running
65
79
```
66
80
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
-
69
81
```bash
70
82
export EDGE_CDN_BASE=http://127.0.0.1:8788
71
83
deno run -A npm:playwright install --with-deps chromium # once
cargo test -p skill # every executable cell of skill/SKILL.md
76
88
```
77
89
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.
79
91
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
81
93
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::`.
83
96
84
97
## Site and Docs
85
98
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.
npm test# builds the Worker and drives it in three engines
95
108
```
96
109
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.
102
113
103
114
## Infra and CI
104
115
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).
Copy file name to clipboardExpand all lines: RUNBOOK.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,4 +18,6 @@ A push to `main` promotes to dev and a `v` tag ships to production, and either o
18
18
19
19
## Migrations
20
20
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