Skip to content

Commit ec6564d

Browse files
Merge pull request #124 from dotindustries/feat/add-comprehensive-patch-maintenance-documentation-and-test-h-swcqkqgckfvuk8qlnp3a9rse
feat: add patch maintenance docs and test-libc.sh CI JUnit XML
2 parents 7b99ec5 + d8a8d07 commit ec6564d

File tree

4 files changed

+677
-0
lines changed

4 files changed

+677
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ tests/fixtures/fs-shim-guest/target/
1414
test-apps/**/target/
1515
test-apps/**/Cargo.lock
1616
__pycache__/
17+
/test-results

docs/FORK-MAINTENANCE.md

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
# Fork Maintenance Guide
2+
3+
This document describes how to maintain the WarpGrid wasi-libc patch series: why the patches exist, how to rebase them onto new upstream releases, how to add new patches, and how to troubleshoot common failures.
4+
5+
## Overview
6+
7+
WarpGrid runs user-supplied WebAssembly modules in a sandboxed environment. These modules are compiled against [wasi-libc](https://github.com/WebAssembly/wasi-libc), the standard C library for WASI targets. Stock wasi-libc does not include networking or advanced filesystem support, so WarpGrid maintains a **patch series** that adds shim functions for:
8+
9+
- **DNS resolution**`getaddrinfo`, `gethostbyname`, `getnameinfo` routed through the WarpGrid DNS shim
10+
- **Filesystem**`fopen`/`open` routed through the WarpGrid virtual filesystem, plus timezone virtualization
11+
- **Sockets**`connect`, `send`, `recv`, `read`, `write`, `close` routed through the WarpGrid database proxy
12+
13+
These patches are kept as numbered `git format-patch` files in `libc-patches/` rather than maintained as a full fork. This keeps the delta small, reviewable, and straightforward to rebase when upstream releases a new version.
14+
15+
### Repository layout
16+
17+
```
18+
libc-patches/
19+
UPSTREAM_REF # Pinned upstream tag + commit hash
20+
0001-dns-*.patch # Patch series (applied in order)
21+
0002-fs-*.patch
22+
...
23+
tests/ # C test programs that validate patch behavior
24+
test_dns_getaddrinfo.c
25+
test_fs_virtual_open.c
26+
...
27+
scripts/
28+
rebase-libc.sh # Apply, export, update, validate patches
29+
build-libc.sh # Build stock and/or patched sysroots
30+
test-libc.sh # Run test programs against sysroots
31+
```
32+
33+
## Upstream Pin Policy
34+
35+
The file `libc-patches/UPSTREAM_REF` pins the exact upstream commit that the patch series applies to:
36+
37+
```
38+
TAG=wasi-sdk-30
39+
COMMIT=ec0effd769df5f05b647216578bcf5d3b5449725
40+
```
41+
42+
**Rules:**
43+
44+
1. **Always pin to a tagged release** (e.g., `wasi-sdk-30`), never to an arbitrary commit. Tags are stable references that correspond to tested wasi-sdk releases.
45+
2. **Update the pin only when rebasing**`scripts/rebase-libc.sh --update <new-tag>` updates both `TAG` and `COMMIT` automatically.
46+
3. **Version compatibility** — The patched sysroot must be built with the matching wasi-sdk version. If `TAG=wasi-sdk-30`, use wasi-sdk 30.x to compile against it.
47+
4. **Do not edit UPSTREAM_REF by hand** — The rebase script manages it. Manual edits risk pin/reality divergence.
48+
49+
## Patch Series
50+
51+
The current series has 8 patches, applied in order. Later patches may depend on earlier ones.
52+
53+
| Patch | Purpose | Depends on |
54+
|-------|---------|------------|
55+
| `0001-dns-route-getaddrinfo-through-WarpGrid-DNS-shim` | Routes `getaddrinfo()` through the WarpGrid DNS shim ||
56+
| `0002-fs-route-fopen-open-through-WarpGrid-virtual-filesys` | Routes `fopen()`/`open()` through the WarpGrid virtual filesystem ||
57+
| `0003-socket-route-connect-through-WarpGrid-database-proxy` | Routes `connect()` through the WarpGrid database proxy | 0001 |
58+
| `0004-socket-route-send-recv-read-write-through-WarpGrid-d` | Routes `send()`/`recv()`/`read()`/`write()` through the proxy | 0003 |
59+
| `0005-warpgrid-patch-close-for-proxied-fd-cleanup-US-211` | Patches `close()` to clean up proxied file descriptors | 0004 |
60+
| `0006-dns-route-gethostbyname-through-WarpGrid-DNS-shim` | Routes `gethostbyname()` through the DNS shim | 0001 |
61+
| `0007-dns-route-getnameinfo-through-WarpGrid-DNS-reverse-r` | Routes `getnameinfo()` through the DNS shim (reverse lookups) | 0001 |
62+
| `0008-fs-timezone-virtual` | Virtualizes timezone data via the WarpGrid filesystem ||
63+
64+
**Dependency graph:**
65+
66+
```
67+
0001 (DNS getaddrinfo)
68+
├── 0003 (socket connect) → 0004 (socket send/recv) → 0005 (socket close)
69+
├── 0006 (DNS gethostbyname)
70+
└── 0007 (DNS getnameinfo)
71+
72+
0002 (FS fopen/open) [independent]
73+
0008 (FS timezone) [independent]
74+
```
75+
76+
## Rebase Workflow
77+
78+
When a new upstream wasi-libc release is available (e.g., `wasi-sdk-31`), follow these steps:
79+
80+
### 1. Validate current patch health
81+
82+
```bash
83+
scripts/rebase-libc.sh --validate
84+
```
85+
86+
This checks patch numbering, ordering, and dependency constraints. Fix any issues before proceeding.
87+
88+
### 2. Attempt the rebase
89+
90+
```bash
91+
scripts/rebase-libc.sh --update wasi-sdk-31
92+
```
93+
94+
This will:
95+
- Clone wasi-libc at the new tag
96+
- Apply each patch in order using `git am --3way`
97+
- Report which patches applied and which conflicted
98+
- On success: update `UPSTREAM_REF`, export rebased patches, move the source checkout into place
99+
100+
### 3. If the rebase succeeds
101+
102+
Build and test to confirm everything works:
103+
104+
```bash
105+
scripts/build-libc.sh --patched
106+
scripts/test-libc.sh --all
107+
```
108+
109+
If all tests pass, commit the updated patches and `UPSTREAM_REF`.
110+
111+
### 4. If the rebase has conflicts
112+
113+
The script reports which patch failed and which files conflict. To resolve:
114+
115+
1. Apply patches up to the conflict manually:
116+
```bash
117+
scripts/rebase-libc.sh --apply
118+
```
119+
2. The script stops at the first conflicting patch. Resolve the conflict in the source checkout (`build/src-patched/`).
120+
3. After resolving, commit the fix:
121+
```bash
122+
cd build/src-patched
123+
git add -A
124+
git am --continue
125+
```
126+
4. Export the updated patch series:
127+
```bash
128+
scripts/rebase-libc.sh --export
129+
```
130+
5. Build and test:
131+
```bash
132+
scripts/build-libc.sh --patched
133+
scripts/test-libc.sh --all
134+
```
135+
136+
## Adding a New Patch
137+
138+
To add a new shim or modification to the patched wasi-libc:
139+
140+
1. **Start from a clean applied state:**
141+
```bash
142+
scripts/rebase-libc.sh --apply
143+
```
144+
145+
2. **Make your changes** in `build/src-patched/`:
146+
```bash
147+
cd build/src-patched
148+
# Edit source files...
149+
```
150+
151+
3. **Commit with a descriptive message** following the existing convention (`<subsystem>: <description>`):
152+
```bash
153+
git add -A
154+
git commit -m "dns: route getXXX through WarpGrid DNS shim"
155+
```
156+
157+
4. **Export the updated patch series:**
158+
```bash
159+
scripts/rebase-libc.sh --export
160+
```
161+
This regenerates all `libc-patches/*.patch` files with correct numbering.
162+
163+
5. **Add a test program** in `libc-patches/tests/`:
164+
- Name it `test_<subsystem>_<feature>.c`
165+
- Include the `WARPGRID_SHIM_REQUIRED` marker comment if the test needs WarpGrid shims (so it gets skipped when testing the stock sysroot)
166+
- Include the `LIBPQ_REQUIRED` marker if the test depends on libpq
167+
168+
6. **Verify:**
169+
```bash
170+
scripts/build-libc.sh --patched
171+
scripts/test-libc.sh --all
172+
scripts/rebase-libc.sh --validate
173+
```
174+
175+
7. **Update the dependency table** in this document if the new patch depends on existing patches. Also update the dependency constraints in `rebase-libc.sh`'s `do_validate()` function.
176+
177+
## Troubleshooting
178+
179+
### Context conflict in header changes
180+
181+
**Symptom:** `git am --3way` fails on a patch that modifies files in `libc-bottom-half/headers/` or `libc-top-half/musl/include/`.
182+
183+
**Why it happens:** Upstream modifies the same header files that WarpGrid patches add declarations to. The 3-way merge cannot resolve the context difference automatically.
184+
185+
**Resolution:**
186+
1. Open the conflicting header file in `build/src-patched/`
187+
2. Look for `<<<<<<<` conflict markers
188+
3. Keep both the upstream changes and the WarpGrid additions (WarpGrid typically adds new function declarations or `#include` directives — these rarely conflict semantically)
189+
4. `git add` the resolved file, then `git am --continue`
190+
5. Re-export patches: `scripts/rebase-libc.sh --export`
191+
192+
### Symbol collision
193+
194+
**Symptom:** Build fails with `multiple definition of 'some_function'` or linker errors after a successful rebase.
195+
196+
**Why it happens:** Upstream added a function with the same name as a WarpGrid shim (e.g., upstream implements `getaddrinfo` natively). The patches still add WarpGrid's version, causing a duplicate symbol.
197+
198+
**Resolution:**
199+
1. Check whether upstream's implementation is equivalent to the WarpGrid shim
200+
2. If yes: remove the WarpGrid shim from the patch (the upstream version replaces it). Update the test to remove the `WARPGRID_SHIM_REQUIRED` marker
201+
3. If no: rename or guard the WarpGrid shim with a preprocessor check (e.g., `#ifndef __WASI_LIBC_HAS_GETADDRINFO`)
202+
4. Re-export patches and rebuild
203+
204+
### Build system changes
205+
206+
**Symptom:** Patches apply cleanly but `build-libc.sh` fails because a CMakeLists.txt target or Makefile variable that WarpGrid patches reference has been renamed, moved, or restructured upstream.
207+
208+
**Why it happens:** WarpGrid patches may modify build rules (e.g., adding source files to a target list). If upstream renames or reorganizes those targets, the patch content is syntactically correct but semantically wrong.
209+
210+
**Resolution:**
211+
1. Compare the upstream CMakeLists.txt/Makefile changes with the patch content
212+
2. Update the patch to use the new target names or file paths
213+
3. Re-export and rebuild:
214+
```bash
215+
cd build/src-patched
216+
# Fix the build files
217+
git add -A
218+
git commit --amend
219+
scripts/rebase-libc.sh --export
220+
scripts/build-libc.sh --patched
221+
```
222+
223+
## Testing
224+
225+
The test harness runs C test programs from `libc-patches/tests/` against built sysroots.
226+
227+
### Modes
228+
229+
```bash
230+
scripts/test-libc.sh # Test patched sysroot (default)
231+
scripts/test-libc.sh --stock # Test stock sysroot only
232+
scripts/test-libc.sh --patched # Test patched sysroot only
233+
scripts/test-libc.sh --all # Test both stock and patched sysroots
234+
scripts/test-libc.sh --ci # Test both + produce JUnit XML report
235+
```
236+
237+
### How `--stock` vs `--patched` works
238+
239+
- Tests with the `WARPGRID_SHIM_REQUIRED` marker are **skipped** when testing the stock sysroot (because the shim functions don't exist in stock wasi-libc)
240+
- Tests with the `LIBPQ_REQUIRED` marker are **skipped** if libpq has not been built
241+
- All other tests run against both sysroots, verifying that WarpGrid patches don't break baseline functionality
242+
243+
### CI mode (`--ci`)
244+
245+
When `--ci` is passed, the harness:
246+
1. Runs both stock and patched test suites (same as `--all`)
247+
2. Generates a JUnit XML report at `test-results/libc-<timestamp>.xml`
248+
3. Exits non-zero if any test fails
249+
250+
The JUnit XML contains `<testsuite>` and `<testcase>` elements with per-test timing, skip reasons, and failure output — compatible with CI systems like GitHub Actions, Jenkins, and GitLab CI.
251+
252+
### Test harness self-tests
253+
254+
The test harness itself is tested by `scripts/test-libc.test.sh`, which validates flag parsing, output format, and JUnit XML generation using mock toolchains (no wasi-sdk or wasmtime required):
255+
256+
```bash
257+
scripts/test-libc.test.sh # Run all self-tests
258+
scripts/test-libc.test.sh --quick # Skip tests that require real execution
259+
```

0 commit comments

Comments
 (0)