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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 14 additions & 2 deletions .github/actions/toolchains/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@ runs:
uses: actions/setup-node@v4
with:
node-version: '22'
# The node fixture check compiles the N-API addon against these headers.
- if: inputs.lang == 'node'
shell: bash
run: npx --yes node-gyp install
- if: inputs.lang == 'python'
uses: actions/setup-python@v5
with:
python-version: '3.12'
# The python fixture check type-checks the generated package with mypy
# against Python 3.9, the oldest version it supports; mypy 2 can't
# target 3.9.
- if: inputs.lang == 'python'
shell: bash
run: python -m pip install 'mypy==1.20.2'
- if: inputs.lang == 'ruby'
uses: ruby/setup-ruby@v1
with:
Expand All @@ -26,11 +36,13 @@ runs:
# a problem matcher relative to the calling action), so fetch the SDK.
- if: inputs.lang == 'dart'
shell: bash
env:
DART_VERSION: 3.13.5
run: |
os=linux; [ "$RUNNER_OS" = macOS ] && os=macos
arch=x64; case "$(uname -m)" in arm64|aarch64) arch=arm64 ;; esac
curl -fsSL -o "$RUNNER_TEMP/dart.zip" \
"https://storage.googleapis.com/dart-archive/channels/stable/release/latest/sdk/dartsdk-$os-$arch-release.zip"
"https://storage.googleapis.com/dart-archive/channels/stable/release/$DART_VERSION/sdk/dartsdk-$os-$arch-release.zip"
unzip -q -o "$RUNNER_TEMP/dart.zip" -d "$RUNNER_TEMP"
echo "$RUNNER_TEMP/dart-sdk/bin" >> "$GITHUB_PATH"
- if: inputs.lang == 'go'
Expand All @@ -51,7 +63,7 @@ runs:
distribution: temurin
java-version: '17'
- if: inputs.lang == 'kotlin'
uses: fwilhe2/setup-kotlin@main
uses: fwilhe2/setup-kotlin@v2.1
with:
version: '2.0.21'
- if: (inputs.lang == 'c' || inputs.lang == 'cpp') && runner.os == 'Linux'
Expand Down
37 changes: 12 additions & 25 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
name: Bench

# Weekly (and on demand) sampled benchmark run. The uploaded artifact is the
# historical record of the numbers. Pull requests get a cheaper smoke run in
# ci.yml, which executes every benchmark body once.
on:
workflow_dispatch:
schedule:
- cron: '0 9 * * 1'

concurrency:
group: bench-${{ github.ref }}
cancel-in-progress: true

jobs:
bench:
name: cargo bench
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -21,39 +29,18 @@ jobs:
with:
cache-targets: true

# PR smoke run: criterion's `--test` mode executes every benchmark
# body exactly once, with no warmup or sampling. This catches
# panics and stale fixtures (clippy already compile-checks the
# harnesses on PRs, but only execution catches runtime failures)
# without spending minutes collecting numbers that are too noisy
# to compare on shared runners.
- name: Run cargo bench (smoke)
if: github.event_name == 'pull_request'
run: |
set -euo pipefail
cargo bench -p weaveffi-gen --bench codegen_bench -- --test
cargo bench -p weaveffi-cli --bench generate_bench -- --test

# Full sampled run on main; the uploaded artifact is the historical
# record of the numbers.
- name: Run cargo bench
if: github.event_name == 'push'
# The two criterion harnesses (`weaveffi-gen` and `weaveffi-cli`)
# accept `--noplot`; the workspace `cargo bench --workspace` flag
# The criterion harness accepts `--noplot`; `cargo bench --workspace`
# also fans out to the default `libtest` harness in every other
# crate, which rejects `--noplot`, so we invoke each criterion
# bench explicitly. `--noplot` skips the gnuplot/plotters HTML
# crate, which rejects `--noplot`, so we invoke the criterion bench
# explicitly. `--noplot` skips the gnuplot/plotters HTML
# reports so the artifact stays small (a few KB of text instead
# of MBs of SVG plots).
run: |
set -euo pipefail
{
cargo bench -p weaveffi-gen --bench codegen_bench -- --noplot
cargo bench -p weaveffi-cli --bench generate_bench -- --noplot
} 2>&1 | tee bench.txt
cargo bench -p weaveffi-cli --bench codegen_bench -- --noplot 2>&1 | tee bench.txt

- name: Upload bench results
if: github.event_name == 'push'
uses: actions/upload-artifact@v4
with:
name: bench-results
Expand Down
75 changes: 66 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
lint:
name: Format, clippy, and rustdoc
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand All @@ -35,6 +36,7 @@ jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
Expand All @@ -48,13 +50,19 @@ jobs:
tool: cargo-insta
# One pass runs every test and fails on any snapshot drift.
- run: cargo insta test --workspace --check
- name: Benchmarks compile
# Bench smoke run: every benchmark compiles, and criterion's `--test`
# mode executes each body once, with no warmup or sampling, which
# catches panics and stale inputs. bench.yml collects the numbers.
- name: Benchmarks build and run once
if: runner.os == 'Linux'
run: cargo bench --workspace --no-run
run: |
cargo bench --workspace --no-run
cargo bench -p weaveffi-cli --bench codegen_bench -- --test

generated-drift:
name: Samples, schema, and wasm build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand All @@ -69,12 +77,12 @@ jobs:
run: |
set -euo pipefail
BIN=target/debug/weaveffi
for sample in calculator contacts events kvstore async-demo codec; do
"$BIN" generate "samples/$sample/src/lib.rs" -o "diffcheck/$sample" --quiet
"$BIN" diff "samples/$sample/src/lib.rs" -o "diffcheck/$sample" --check --quiet
for sample in calculator codec kvstore; do
"$BIN" generate "samples/$sample" -o "diffcheck/$sample" --quiet
"$BIN" diff "samples/$sample" -o "diffcheck/$sample" --check --quiet
done
echo "stale" >> diffcheck/calculator/c/calculator.h
if "$BIN" diff samples/calculator/src/lib.rs -o diffcheck/calculator --check --quiet; then
if "$BIN" diff samples/calculator -o diffcheck/calculator --check --quiet; then
echo "diff --check did not detect a modified file" >&2
exit 1
fi
Expand All @@ -83,14 +91,27 @@ jobs:
target/debug/weaveffi schema > weaveffi.schema.json.regenerated
diff -u weaveffi.schema.json weaveffi.schema.json.regenerated
- name: Every sample builds for wasm32
run: cargo build --target wasm32-unknown-unknown -p calculator -p contacts -p events -p kvstore -p async-demo -p codec
run: cargo build --target wasm32-unknown-unknown -p calculator -p codec -p kvstore
# A `.wasm` module carries its metadata in a custom section rather
# than in symbols; it must describe the same API as the native build.
- name: The wasm32 metadata matches the native metadata
run: |
set -euo pipefail
for sample in calculator codec kvstore; do
target/debug/weaveffi extract -q "samples/$sample" > "native-$sample.yml"
target/debug/weaveffi extract -q "samples/$sample" \
--library "target/wasm32-unknown-unknown/debug/$sample.wasm" > "wasm-$sample.yml"
diff -u "native-$sample.yml" "wasm-$sample.yml"
done

fixtures:
# Snapshot tests pin the generated text; this compiles or type-checks it
# with each language's own toolchain, for every fixture, so invalid code
# in shapes no sample exercises still fails CI.
# in shapes no sample exercises still fails CI. GitHub sets CI=true, so a
# missing tool fails the job instead of skipping a check.
name: Fixture compile check (${{ matrix.target }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -144,6 +165,7 @@ jobs:
# producer built for Android.
name: Android (NDK link)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand All @@ -163,9 +185,44 @@ jobs:
NDK: ${{ steps.ndk.outputs.ndk-path }}
run: |
set -euxo pipefail
cargo run -q -p weaveffi-cli -- generate samples/kvstore/src/lib.rs -o generated --target c,kotlin
cargo run -q -p weaveffi-cli -- generate samples/kvstore -o generated --target c,kotlin \
--library target/aarch64-linux-android/release/libkvstore.so
CLANG="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang"
shim=$(ls generated/kotlin/src/main/cpp/*.c | head -1)
"$CLANG" -shared -fPIC -Werror -I generated/kotlin/src/main/cpp -I generated/c "$shim" \
-L target/aarch64-linux-android/release -lkvstore -o libkvstore_jni.so
"$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readelf" -d libkvstore_jni.so | grep 'libkvstore.so'
- name: weaveffi build cross-compiles the producer and prebuilds the shim
env:
ANDROID_NDK_HOME: ${{ steps.ndk.outputs.ndk-path }}
run: |
set -euxo pipefail
cargo run -q -p weaveffi-cli -- build samples/kvstore --platforms android-arm64 --target kotlin
readelf="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readelf"
"$readelf" -d target/weaveffi/android-arm64/libkvstore.so | grep 'SONAME.*libkvstore.so'
"$readelf" -d target/weaveffi/android-arm64/libkvstore_jni.so | grep 'NEEDED.*libkvstore.so'

package-smoke:
# `weaveffi package` must produce artifacts each ecosystem's own tool
# installs with no compiler: the wheel into a venv, the npm tarballs into
# a throwaway project (using the prebuilt addon), and on macOS the
# SwiftPM package over its XCFramework. GitHub sets CI=true, so a missing
# tool fails the job instead of skipping a target.
name: Package install smoke (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: ./.github/actions/toolchains
with:
lang: python
- uses: ./.github/actions/toolchains
with:
lang: node
- run: bash scripts/package-smoke.sh
8 changes: 5 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/configure-pages@v5
Expand All @@ -29,9 +30,9 @@ jobs:

- name: Install mdbook + mdbook-linkcheck2
# Keep this in sync with the docs linkcheck job in quality.yml.
run: |
cargo install mdbook --locked
cargo install mdbook-linkcheck2 --locked
uses: taiki-e/install-action@v2
with:
tool: mdbook,mdbook-linkcheck2

- name: Build mdBook
run: mdbook build docs
Expand Down Expand Up @@ -60,6 +61,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- id: deployment
uses: actions/deploy-pages@v4
29 changes: 23 additions & 6 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
name: Fuzz

# Daily (and on demand), each target runs for 60 seconds from a corpus seeded
# with its committed seeds plus inputs derived from the snapshot fixtures.
# Extend a run by bumping `-max_total_time` below.
on:
workflow_dispatch:
schedule:
# 08:00 UTC daily. Extend a run by bumping `-max_total_time` below.
# 08:00 UTC daily.
- cron: '0 8 * * *'

concurrency:
group: fuzz-${{ github.ref }}
cancel-in-progress: true

jobs:
fuzz:
name: cargo-fuzz (${{ matrix.target }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand All @@ -19,6 +27,7 @@ jobs:
- fuzz_parse_toml
- fuzz_parse_type_ref
- fuzz_validate
- fuzz_value_buffer
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -35,23 +44,31 @@ jobs:
with:
tool: cargo-fuzz

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Seed the corpus
run: |
python -m pip install pyyaml tomli-w
python crates/weaveffi-fuzz/seed_corpus.py ${{ matrix.target }} \
crates/weaveffi-fuzz/corpus/${{ matrix.target }}

- name: Run ${{ matrix.target }} for 60s
# Each target is fuzzed for 60s in PRs (≈5 min total). The cron run
# uses the same budget today; bump `-max_total_time` to extend it.
# Any crash is written to fuzz/artifacts/ and fails the job.
# Any crash is written to artifacts/ and fails the job.
run: |
cargo +nightly fuzz run \
--fuzz-dir crates/weaveffi-fuzz \
--features fuzzing \
${{ matrix.target }} \
crates/weaveffi-fuzz/fuzz/seeds/${{ matrix.target }} \
crates/weaveffi-fuzz/corpus/${{ matrix.target }} \
-- -max_total_time=60 -seed=1

- name: Upload crash artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-artifacts-${{ matrix.target }}
path: crates/weaveffi-fuzz/fuzz/artifacts/
path: crates/weaveffi-fuzz/artifacts/
if-no-files-found: ignore
retention-days: 14
6 changes: 6 additions & 0 deletions .github/workflows/pr-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ on:
permissions:
pull-requests: read

concurrency:
group: pr-lint-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
pr-title:
name: PR title (Conventional Commits)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Validate PR title
uses: amannn/action-semantic-pull-request@v5
Expand Down Expand Up @@ -39,6 +44,7 @@ jobs:
commits:
name: Commit messages
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
Expand Down
Loading
Loading