Skip to content

Commit 6ce5f72

Browse files
authored
ci: validate wit definitions (#827)
Signed-off-by: Bailey Hayes <bailey@cosmonic.com>
1 parent a844d3b commit 6ce5f72

File tree

5 files changed

+249
-0
lines changed

5 files changed

+249
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: 'Install WIT Tools'
2+
description: 'Installs wit-deps and wasm-tools for WIT validation'
3+
4+
inputs:
5+
wit-deps-version:
6+
description: 'Version of wit-deps to install'
7+
required: false
8+
default: '0.5.0'
9+
wasm-tools-version:
10+
description: 'Version of wasm-tools to install'
11+
required: false
12+
default: '1.242.0'
13+
14+
runs:
15+
using: 'composite'
16+
steps:
17+
- name: Setup wasm-tools
18+
uses: bytecodealliance/actions/wasm-tools/setup@v1
19+
with:
20+
version: ${{ inputs.wasm-tools-version }}
21+
22+
- name: Cache wit-deps
23+
id: cache-wit-deps
24+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
25+
with:
26+
path: ~/.local/bin/wit-deps
27+
key: wit-deps-${{ inputs.wit-deps-version }}
28+
29+
- name: Install wit-deps
30+
if: steps.cache-wit-deps.outputs.cache-hit != 'true'
31+
shell: bash
32+
run: |
33+
mkdir -p ~/.local/bin
34+
curl -Lo ~/.local/bin/wit-deps \
35+
"https://github.com/bytecodealliance/wit-deps/releases/download/v${{ inputs.wit-deps-version }}/wit-deps-x86_64-unknown-linux-musl"
36+
chmod +x ~/.local/bin/wit-deps
37+
38+
- name: Add wit-deps to PATH
39+
shell: bash
40+
run: echo "$HOME/.local/bin" >> $GITHUB_PATH

.github/scripts/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# CI Scripts
2+
3+
## validate-proposals.js
4+
5+
Validates WIT definitions for changed proposals. Used by the CI workflow.
6+
7+
### Prerequisites
8+
9+
- Node.js
10+
- [Wasm-tools](https://github.com/bytecodealliance/Wasm-tools)
11+
- [wit-deps](https://github.com/bytecodealliance/wit-deps)
12+
13+
### Running Locally
14+
15+
```bash
16+
# Validate specific proposals by simulating changed files
17+
WIT_02_FILES='["proposals/cli/wit/cli.wit"]' node .github/scripts/validate-proposals.js
18+
19+
# Validate 0.3 proposals
20+
WIT_03_FILES='["proposals/http/wit-0.3.0-draft/handler.wit"]' node .github/scripts/validate-proposals.js
21+
22+
# Validate multiple proposals
23+
WIT_02_FILES='["proposals/cli/wit/cli.wit", "proposals/http/wit/proxy.wit"]' node .github/scripts/validate-proposals.js
24+
```
25+
26+
### Environment Variables
27+
28+
| Variable | Description |
29+
|----------|-------------|
30+
| `WIT_02_FILES` | JSON array of changed files in `proposals/*/wit/**` |
31+
| `WIT_03_FILES` | JSON array of changed files in `proposals/*/wit-0.3.0-draft/**` |
32+
33+
### What it validates
34+
35+
1. **wit-deps lock** - If `deps.toml` exists, checks that `deps.lock` is up to date
36+
2. **WIT syntax** - Parses WIT files with `Wasm-tools component wit`
37+
3. **Wasm encoding** - Validates Wasm binary encoding with `--Wasm` flag
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/usr/bin/env node
2+
3+
const { execSync } = require('child_process');
4+
const fs = require('fs');
5+
6+
const witPath = (proposal, version) => {
7+
if (version === '0.2') return `proposals/${proposal}/wit`;
8+
if (version === '0.3') return `proposals/${proposal}/wit-0.3.0-draft`;
9+
throw new Error(`Unknown version: ${version}`);
10+
};
11+
12+
const parseFiles = (filesJson) => {
13+
if (!filesJson || filesJson === 'null') return [];
14+
try {
15+
return JSON.parse(filesJson);
16+
} catch {
17+
return [];
18+
}
19+
};
20+
21+
const extractProposals = (files) => {
22+
const proposals = new Set();
23+
for (const f of files) {
24+
const match = f.match(/^proposals\/([^/]+)\//);
25+
if (match) proposals.add(match[1]);
26+
}
27+
return [...proposals].sort();
28+
};
29+
30+
const run = (cmd) => {
31+
console.log(` $ ${cmd}`);
32+
try {
33+
const output = execSync(cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
34+
if (output.trim()) {
35+
console.log(output);
36+
}
37+
return true;
38+
} catch (err) {
39+
if (err.stdout) console.log(err.stdout);
40+
if (err.stderr) console.error(err.stderr);
41+
console.error(` Exit code: ${err.status}`);
42+
return false;
43+
}
44+
};
45+
46+
// Collect proposals to validate from changed files
47+
const toValidate = [];
48+
const filesByVersion = [
49+
[process.env.WIT_02_FILES, '0.2'],
50+
[process.env.WIT_03_FILES, '0.3'],
51+
];
52+
53+
for (const [filesJson, version] of filesByVersion) {
54+
for (const proposal of extractProposals(parseFiles(filesJson))) {
55+
toValidate.push({ proposal, version });
56+
}
57+
}
58+
59+
if (toValidate.length === 0) {
60+
console.log('No proposals to validate');
61+
process.exit(0);
62+
}
63+
64+
let failed = false;
65+
66+
for (const { proposal, version } of toValidate) {
67+
const witDir = witPath(proposal, version);
68+
console.log(`::group::Validating ${proposal} v${version}`);
69+
70+
try {
71+
console.log(` Path: ${witDir}`);
72+
73+
// Check wit-deps lock if deps.toml exists
74+
if (fs.existsSync(`${witDir}/deps.toml`)) {
75+
console.log(' Checking dependencies...');
76+
if (!run(`wit-deps -m "${witDir}"/deps.toml -l "${witDir}"/deps.lock -d "${witDir}"/deps lock --check`)) {
77+
console.log(`::error::wit-deps lock check failed for ${proposal} v${version}`);
78+
failed = true;
79+
}
80+
}
81+
82+
// Validate WIT syntax
83+
console.log(' Validating WIT...');
84+
if (!run(`wasm-tools component wit "${witDir}" -o /dev/null`)) {
85+
console.log(`::error::WIT validation failed for ${proposal} v${version}`);
86+
failed = true;
87+
}
88+
89+
// Validate wasm encoding
90+
console.log(' Validating wasm encoding...');
91+
if (!run(`wasm-tools component wit "${witDir}" --wasm -o /dev/null`)) {
92+
console.log(`::error::wasm encoding failed for ${proposal} v${version}`);
93+
failed = true;
94+
}
95+
} finally {
96+
console.log('::endgroup::');
97+
}
98+
}
99+
100+
if (failed) {
101+
console.log('\n❌ Validation failed');
102+
process.exit(1);
103+
} else {
104+
console.log('\n✅ All proposals validated successfully');
105+
process.exit(0);
106+
}

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'proposals/**/wit/**'
8+
- 'proposals/**/wit-0.3.0-draft/**'
9+
- '.github/scripts/**'
10+
- '.github/workflows/ci.yml'
11+
pull_request:
12+
branches: [main]
13+
paths:
14+
- 'proposals/**/wit/**'
15+
- 'proposals/**/wit-0.3.0-draft/**'
16+
- '.github/scripts/**'
17+
- '.github/workflows/ci.yml'
18+
19+
jobs:
20+
validate:
21+
name: Validate WIT
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
25+
26+
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
27+
id: changes
28+
with:
29+
list-files: json
30+
filters: |
31+
wit-02:
32+
- 'proposals/*/wit/**'
33+
wit-03:
34+
- 'proposals/*/wit-0.3.0-draft/**'
35+
36+
- name: Install tools
37+
if: steps.changes.outputs.wit-02 == 'true' || steps.changes.outputs.wit-03 == 'true'
38+
uses: ./.github/actions/install-tools
39+
40+
- name: Validate changed proposals
41+
if: steps.changes.outputs.wit-02 == 'true' || steps.changes.outputs.wit-03 == 'true'
42+
run: node .github/scripts/validate-proposals.js
43+
env:
44+
WIT_02_FILES: ${{ steps.changes.outputs.wit-02_files }}
45+
WIT_03_FILES: ${{ steps.changes.outputs.wit-03_files }}

.github/workflows/lint-gh.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Lint GitHub Actions and Workflows
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- '.github/workflows/**'
8+
- '.github/actions/**'
9+
pull_request:
10+
branches: [main]
11+
paths:
12+
- '.github/workflows/**'
13+
- '.github/actions/**'
14+
15+
jobs:
16+
actionlint:
17+
name: Lint GitHub Actions
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
21+
- uses: rhysd/actionlint@a443f344ff32813837fa49f7aa6cbc478d770e62 # v1.7.9

0 commit comments

Comments
 (0)