1name: CI
2on:
3  # Run CI for PRs to `main` and to release branches.
4  #
5  # Note that PRs to `main` will run a subset of tests and PRs to the
6  # `release-*` branches will run full CI.
7  pull_request:
8    branches:
9    - main
10    - 'release-*'
11
12  # Run full CI on the `main` branch once a day to prime the GitHub Actions
13  # caches used by PRs and the merge queue.
14  schedule:
15  - cron: '13 4 * * *'
16
17  # This is the CI that runs for PRs-to-merge.
18  merge_group:
19
20  push:
21    branches:
22    # Right now merge queues can't be used with wildcards in branch protections
23    # so full CI runs both on PRs to release branches as well as merges to
24    # release branches. Note that the merge to a release branch may produce a
25    # tag at the end of CI if successful and the tag will trigger the artifact
26    # uploads as well as publication to crates.io.
27    - 'release-*'
28
29defaults:
30  run:
31    shell: bash
32
33# Cancel any in-flight jobs for the same PR/branch so there's only one active
34# at a time
35concurrency:
36  group: ${{ github.workflow }}-${{ github.ref }}
37  cancel-in-progress: true
38
39jobs:
40  # Check Code style quickly by running `rustfmt` over all code
41  rustfmt:
42    name: Rustfmt
43    runs-on: ubuntu-latest
44    steps:
45    - uses: actions/checkout@v4
46      with:
47        submodules: true
48    - uses: ./.github/actions/install-rust
49    - run: rustup component add rustfmt
50    - run: cargo fmt --all -- --check
51
52    # common logic to cancel the entire run if this job fails
53    - run: gh run cancel ${{ github.run_id }}
54      if: failure() && github.event_name != 'pull_request'
55      env:
56        GH_TOKEN: ${{ github.token }}
57
58  # Quick JS formatting/linting checks for the little bits of JS we have for the
59  # `wasmtime explore` UI.
60  check_js:
61    name: Check JS
62    runs-on: ubuntu-latest
63    steps:
64    - uses: actions/checkout@v4
65    - run: npm install
66      working-directory: ./crates/explorer
67    - run: npm run lint
68      working-directory: ./crates/explorer
69    - run: npm run fmt-check
70      working-directory: ./crates/explorer
71
72    # common logic to cancel the entire run if this job fails
73    - run: gh run cancel ${{ github.run_id }}
74      if: failure() && github.event_name != 'pull_request'
75      env:
76        GH_TOKEN: ${{ github.token }}
77
78  # Check Code style quickly by running `clang-format` over all the C/C++ code
79  #
80  # Note that `wasmtime-platform.h` is excluded here as it's auto-generated.
81  clangformat:
82    name: Clang format
83    runs-on: ubuntu-latest
84    steps:
85    - uses: actions/checkout@v4
86      with:
87        submodules: true
88    - run: |
89        git ls-files '*.h' '*.c' '*.cpp' | \
90          grep -v wasmtime-platform.h | \
91          grep -v wasm.h | \
92          xargs clang-format-15 --dry-run --Werror --verbose
93
94    # common logic to cancel the entire run if this job fails
95    - run: gh run cancel ${{ github.run_id }}
96      if: failure() && github.event_name != 'pull_request'
97      env:
98        GH_TOKEN: ${{ github.token }}
99
100  # Lint dependency graph for security advisories, duplicate versions, and
101  # incompatible licences
102  cargo_deny:
103    name: Cargo deny
104    needs: determine
105    if: needs.determine.outputs.audit
106    runs-on: ubuntu-latest
107    steps:
108    - uses: actions/checkout@v4
109      with:
110        submodules: true
111    - uses: ./.github/actions/install-rust
112    - run: |
113        set -e
114        curl -L https://github.com/EmbarkStudios/cargo-deny/releases/download/0.14.5/cargo-deny-0.14.5-x86_64-unknown-linux-musl.tar.gz | tar xzf -
115        mv cargo-deny-*-x86_64-unknown-linux-musl/cargo-deny cargo-deny
116        echo `pwd` >> $GITHUB_PATH
117    - run: cargo deny check bans licenses
118
119    # common logic to cancel the entire run if this job fails
120    - run: gh run cancel ${{ github.run_id }}
121      if: failure() && github.event_name != 'pull_request'
122      env:
123        GH_TOKEN: ${{ github.token }}
124
125  # Ensure dependencies are vetted. See https://mozilla.github.io/cargo-vet/
126  #
127  # Note that this step, on PRs only, is allowed to fail. This is then followed
128  # up with the `cargo_vet_failure_for_prs` step below. The intention is to
129  # avoid causing this check to fail PRs while still enabling it to fail the
130  # merge queue checks. That way PRs can enter the merge queue when this step is
131  # failing if `main` has picked up `cargo vet` entries in the meantime for the
132  # failures.
133  cargo_vet:
134    name: Cargo vet
135    needs: determine
136    if: needs.determine.outputs.audit
137    runs-on: ubuntu-latest
138    outputs:
139      outcome: ${{ steps.vet.outcome }}
140    steps:
141    - uses: actions/checkout@v4
142      with:
143        submodules: true
144    - uses: ./.github/actions/install-rust
145    - uses: ./.github/actions/install-cargo-vet
146    - id: vet
147      run: cargo vet --locked
148      continue-on-error: ${{ github.event_name == 'pull_request' }}
149
150    # common logic to cancel the entire run if this job fails
151    - run: gh run cancel ${{ github.run_id }}
152      if: failure() && github.event_name != 'pull_request'
153      env:
154        GH_TOKEN: ${{ github.token }}
155
156  cargo_vet_failure_for_prs:
157    name: Cargo vet failed on a Pull Request
158    needs:
159    - determine
160    - cargo_vet
161    if: |
162      needs.determine.outputs.audit
163      && github.event_name == 'pull_request'
164      && needs.cargo_vet.outputs.outcome == 'failure'
165    runs-on: ubuntu-latest
166    steps:
167    # NB: this message ideally would link back to the previous step, but I'm not
168    # sure how to easily do that.
169    - run: |
170        echo 'failed to run "cargo vet", see previous `Cargo vet` step'
171        echo 'exiting with a nonzero status now to alert PR authors'
172        echo 'note, though, that this PR can still enter the merge queue'
173        echo ''
174        echo 'See https://docs.wasmtime.dev/contributing-coding-guidelines.html#cargo-vet-for-contributors'
175        echo 'for more information about the vetting process for Wasmtime'
176        exit 1
177
178  # This job is a dependency of many of the jobs below. This calculates what's
179  # actually being run for this workflow. For example:
180  #
181  # * Pushes to branches, which is currently both pushes to merge queue branches
182  #   as well as release branches, perform full CI.
183  # * PRs to release branches (not `main`) run full CI.
184  # * PRs to `main` will only run a few smoke tests above plus some elements of
185  #   the test matrix. The test matrix here is determined dynamically by the
186  #   `./ci/build-test-matrix.js` script given the commits that happened and
187  #   the files modified.
188  determine:
189    name: Determine CI jobs to run
190    runs-on: ubuntu-latest
191    outputs:
192      run-full: ${{ steps.calculate.outputs.run-full }}
193      test-matrix: ${{ steps.calculate.outputs.test-matrix }}
194      build-matrix: ${{ steps.calculate.outputs.build-matrix }}
195      test-capi: ${{ steps.calculate.outputs.test-capi }}
196      test-nightly: ${{ steps.calculate.outputs.test-nightly }}
197      audit: ${{ steps.calculate.outputs.audit }}
198      preview1-adapter: ${{ steps.calculate.outputs.preview1-adapter }}
199      run-dwarf: ${{ steps.calculate.outputs.run-dwarf }}
200    steps:
201    - uses: actions/checkout@v4
202    - id: calculate
203      env:
204        GH_TOKEN: ${{ github.token }}
205      run: |
206        touch commits.log names.log
207        # Note that CI doesn't run on pushes to `main`, only pushes to merge
208        # queue branches and release branches, so this only runs full CI in
209        # those locations.
210        if [ "${{ github.event_name }}" != "pull_request" ]; then
211          run_full=true
212        else
213          pr=${{ github.event.number }}
214          gh pr view $pr --json commits | tee commits.log
215          gh pr diff $pr --name-only | tee names.log || echo "failed to get files"
216          if [ "${{ github.base_ref }}" != "main" ]; then
217            run_full=true
218          elif grep -q 'prtest:full' commits.log; then
219            run_full=true
220          elif grep -q 'prtest:debug' commits.log; then
221            echo run-dwarf=true >> $GITHUB_OUTPUT
222          fi
223          if grep -q crates.c-api names.log; then
224            echo test-capi=true >> $GITHUB_OUTPUT
225          fi
226          if grep -q fuzz names.log; then
227            echo test-nightly=true >> $GITHUB_OUTPUT
228          fi
229          if grep -q sys.custom names.log; then
230            echo test-nightly=true >> $GITHUB_OUTPUT
231          fi
232          if grep -q Cargo.lock names.log; then
233            echo audit=true >> $GITHUB_OUTPUT
234          fi
235          if grep -q supply-chain names.log; then
236            echo audit=true >> $GITHUB_OUTPUT
237          fi
238          if grep -q component-adapter names.log; then
239            echo preview1-adapter=true >> $GITHUB_OUTPUT
240          fi
241          if grep -q debug names.log; then
242            echo run-dwarf=true >> $GITHUB_OUTPUT
243          fi
244        fi
245        matrix="$(node ./ci/build-test-matrix.js ./commits.log ./names.log $run_full)"
246        echo "test-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
247        echo "$matrix"
248
249        matrix="$(node ./ci/build-build-matrix.js)"
250        echo "build-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
251
252        if [ "$run_full" = "true" ]; then
253            echo run-full=true >> $GITHUB_OUTPUT
254            echo test-capi=true >> $GITHUB_OUTPUT
255            echo test-nightly=true >> $GITHUB_OUTPUT
256            echo audit=true >> $GITHUB_OUTPUT
257            echo preview1-adapter=true >> $GITHUB_OUTPUT
258            echo run-dwarf=true >> $GITHUB_OUTPUT
259        fi
260
261  # Build all documentation of Wasmtime, including the C API documentation,
262  # mdbook documentation, etc. This produces a `gh-pages` artifact which is what
263  # gets uploaded to the `gh-pages` branch later on.
264  doc:
265    needs: determine
266    if: needs.determine.outputs.run-full
267    name: Doc build
268    runs-on: ubuntu-latest
269    env:
270      CARGO_MDBOOK_VERSION: 0.4.37
271      RUSTDOCFLAGS: -Dbroken_intra_doc_links --cfg docsrs
272      OPENVINO_SKIP_LINKING: 1
273    steps:
274    - uses: actions/checkout@v4
275      with:
276        submodules: true
277    - uses: ./.github/actions/install-rust
278      with:
279        toolchain: wasmtime-ci-pinned-nightly
280
281    # Build C API documentation
282    - run: curl -L https://sourceforge.net/projects/doxygen/files/rel-1.9.3/doxygen-1.9.3.linux.bin.tar.gz/download | tar xzf -
283    - run: echo "`pwd`/doxygen-1.9.3/bin" >> $GITHUB_PATH
284    - run: cmake -S crates/c-api -B target/c-api
285    - run: cmake --build target/c-api --target doc
286
287    # install mdbook, build the docs, and test the docs
288    - uses: actions/cache@v4
289      with:
290        path: ${{ runner.tool_cache }}/mdbook
291        key: cargo-mdbook-bin-${{ env.CARGO_MDBOOK_VERSION }}
292    - run: |
293        echo "${{ runner.tool_cache }}/mdbook/bin" >> $GITHUB_PATH
294        cargo install --root ${{ runner.tool_cache }}/mdbook --version ${{ env.CARGO_MDBOOK_VERSION }} mdbook --locked
295    - run: (cd docs && mdbook build)
296    - run: cargo build -p wasi-common --features wasmtime/wat,wasmtime/cranelift
297    - run: (cd docs && mdbook test -L ../target/debug/deps)
298
299    # Build Rust API documentation.
300    #
301    # Enable extra features in crates as well to ensure they're documented
302    - run: |
303        cargo doc --no-deps --workspace \
304          --exclude wasmtime-cli \
305          --exclude test-programs \
306          --exclude cranelift-codegen-meta \
307          --features call-hook
308      env:
309        RUSTDOCFLAGS: --cfg=docsrs
310    - run: cargo doc --package cranelift-codegen-meta --document-private-items
311      env:
312        RUSTDOCFLAGS: --cfg=docsrs
313
314    # Assemble the documentation, and always upload it as an artifact for
315    # inspection on PRs and such.
316    - run: |
317        mv docs/book gh-pages
318        mv crates/c-api/html gh-pages/c-api
319        mv target/doc gh-pages/api
320        tar czf gh-pages.tar.gz gh-pages
321    - uses: actions/upload-artifact@v4
322      with:
323        name: gh-pages
324        path: gh-pages.tar.gz
325
326    # common logic to cancel the entire run if this job fails
327    - run: gh run cancel ${{ github.run_id }}
328      if: failure() && github.event_name != 'pull_request'
329      env:
330        GH_TOKEN: ${{ github.token }}
331
332  # Checks of various feature combinations and whether things compile. The goal
333  # here isn't to run tests, mostly just serve as a double-check that Rust code
334  # compiles and is likely to work everywhere else.
335  micro_checks:
336    needs: determine
337    name: Check ${{matrix.name}}
338    strategy:
339      fail-fast: true
340      matrix:
341        include:
342          - name: wasmtime
343            checks: |
344              -p wasmtime --no-default-features
345              -p wasmtime --no-default-features --features wat
346              -p wasmtime --no-default-features --features profiling
347              -p wasmtime --no-default-features --features cache
348              -p wasmtime --no-default-features --features async
349              -p wasmtime --no-default-features --features pooling-allocator
350              -p wasmtime --no-default-features --features cranelift
351              -p wasmtime --no-default-features --features component-model
352              -p wasmtime --no-default-features --features runtime,component-model
353              -p wasmtime --no-default-features --features cranelift,wat,async,cache
354              -p wasmtime --no-default-features --features winch
355              -p wasmtime --no-default-features --features wmemcheck
356              -p wasmtime --no-default-features --features wmemcheck,cranelift,runtime
357              -p wasmtime --no-default-features --features demangle
358              -p wasmtime --no-default-features --features addr2line
359              -p wasmtime --no-default-features --features gc
360              -p wasmtime --no-default-features --features runtime,gc
361              -p wasmtime --no-default-features --features cranelift,gc
362              -p wasmtime --no-default-features --features runtime
363              -p wasmtime --no-default-features --features threads
364              -p wasmtime --no-default-features --features runtime,threads
365              -p wasmtime --no-default-features --features cranelift,threads
366              -p wasmtime --features incremental-cache
367              -p wasmtime --all-features
368
369          - name: wasmtime-cli
370            checks: |
371              -p wasmtime-cli --no-default-features
372              -p wasmtime-cli --no-default-features --features pooling-allocator
373              -p wasmtime-cli --no-default-features --features run
374              -p wasmtime-cli --no-default-features --features run,component-model
375              -p wasmtime-cli --no-default-features --features run,pooling-allocator
376              -p wasmtime-cli --no-default-features --features compile
377              -p wasmtime-cli --no-default-features --features compile,cranelift
378              -p wasmtime-cli --no-default-features --features compile,cranelift,component-model
379              -p wasmtime-cli --all-features
380              -p wasmtime-cli --features component-model
381
382          - name: cranelift-codegen benches
383            checks: |
384              --benches -p cranelift-codegen
385
386          - name: wasmtime-bench-api
387            checks: |
388              -p wasmtime-bench-api
389
390          - name: wasmtime-c-api
391            checks: |
392              -p wasmtime-c-api --no-default-features
393              -p wasmtime-c-api --no-default-features --features wat
394              -p wasmtime-c-api --no-default-features --features wasi
395    runs-on: ubuntu-latest
396    steps:
397    - uses: actions/checkout@v4
398      with:
399        submodules: true
400    - uses: ./.github/actions/install-rust
401
402    # Run the check.
403    - run: |
404        checks=$(cat <<END
405        ${{ matrix.checks }}
406        END
407        )
408        echo "$checks" | xargs -I CHECK sh -c 'echo "=== cargo check CHECK ==="; cargo check CHECK'
409
410    # Common logic to cancel the entire run if this job fails.
411    - run: gh run cancel ${{ github.run_id }}
412      if: failure() && github.event_name != 'pull_request'
413      env:
414        GH_TOKEN: ${{ github.token }}
415
416  # Similar to `micro_checks` but where we need to install some more state
417  # (e.g. Android NDK) and we haven't factored support for those things out into
418  # a parallel jobs yet.
419  monolith_checks:
420    needs: determine
421    name: Monolith Checks
422    runs-on: ubuntu-latest
423    env:
424      CARGO_NDK_VERSION: 2.12.2
425    steps:
426    - uses: actions/checkout@v4
427      with:
428        submodules: true
429    - uses: ./.github/actions/install-rust
430
431    # Checks for no_std support, ensure that crates can build on a no_std
432    # target
433    - run: rustup target add x86_64-unknown-none
434    - run: cargo check --target x86_64-unknown-none -p wasmtime --no-default-features --features runtime,gc,component-model
435    - run: cargo check --target x86_64-unknown-none -p cranelift-control --no-default-features
436    - run: cargo check --target x86_64-unknown-none -p pulley-interpreter --features encode,decode,disas,interp
437
438    # Check that wasmtime compiles with panic=abort since there's some `#[cfg]`
439    # for specifically panic=abort there.
440    - run: cargo check -p wasmtime
441      env:
442        RUSTFLAGS: -Cpanic=abort
443
444    # Check a few builds of the cranelift backend
445    # - only x86 backend support,
446    # - only arm64 backend support,
447    # - no debug_assertions.
448    - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util --no-default-features --features=cranelift-codegen/arm64
449    - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util --no-default-features --features=cranelift-codegen/x86
450    - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util
451      env:
452        CARGO_PROFILE_DEV_DEBUG_ASSERTIONS: false
453
454    # Check whether `wasmtime` cross-compiles to x86_64-unknown-freebsd
455    # TODO: We aren't building with default features since the `ittapi` crate fails to compile on freebsd.
456    - run: rustup target add x86_64-unknown-freebsd
457    - run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache --target x86_64-unknown-freebsd
458
459    # Run clippy configuration
460    - run: rustup component add clippy
461    - run: cargo clippy --workspace
462
463    # Re-vendor all WIT files and ensure that they're all up-to-date by ensuring
464    # that there's no git changes.
465    - name: Re-vendor WIT
466      run: ./ci/vendor-wit.sh
467    - run: git diff --exit-code
468
469    # Re-vendor the C API and make sure it's up-to-date.
470    - name: Re-vendor C API
471      run: ./ci/vendor-c-api-headers.sh
472    - run: git diff --exit-code
473
474    # common logic to cancel the entire run if this job fails
475    - run: gh run cancel ${{ github.run_id }}
476      if: failure() && github.event_name != 'pull_request'
477      env:
478        GH_TOKEN: ${{ github.token }}
479
480  # Check whether `wasmtime` cross-compiles to aarch64-pc-windows-msvc
481  # We don't build nor test it because it lacks trap handling.
482  # Tracking issue: https://github.com/bytecodealliance/wasmtime/issues/4992
483  checks_winarm64:
484    needs: determine
485    if: needs.determine.outputs.run-full
486    name: Check Windows ARM64
487    runs-on: windows-latest
488    steps:
489    - uses: actions/checkout@v4
490      with:
491        submodules: true
492    - uses: ./.github/actions/install-rust
493    - run: rustup target add aarch64-pc-windows-msvc
494    - run: cargo check -p wasmtime --target aarch64-pc-windows-msvc
495
496    # common logic to cancel the entire run if this job fails
497    - run: gh run cancel ${{ github.run_id }}
498      if: failure() && github.event_name != 'pull_request'
499      env:
500        GH_TOKEN: ${{ github.token }}
501
502  # Run tests that require a nightly compiler, such as building fuzz targets.
503  test_nightly:
504    needs: determine
505    if: needs.determine.outputs.test-nightly
506    name: Nightly tests
507    runs-on: ubuntu-latest
508    steps:
509    - uses: actions/checkout@v4
510      with:
511        submodules: true
512    # Note that nightly is pinned here to insulate us from breakage that might
513    # happen upstream. This is periodically updated through a PR.
514    - uses: ./.github/actions/install-rust
515      with:
516        toolchain: wasmtime-ci-pinned-nightly
517
518    # Ensure that fuzzers still build.
519    #
520    # Install the OCaml packages necessary for fuzz targets that use the
521    # `wasm-spec-interpreter`.
522    - run: cargo install cargo-fuzz --vers "^0.11" --locked
523    - run: sudo apt-get update && sudo apt install -y ocaml-nox ocamlbuild ocaml-findlib libzarith-ocaml-dev
524    - run: cargo fetch
525      working-directory: ./fuzz
526    - run: cargo fuzz build --dev -s none
527    - run: cargo fuzz build --dev -s none --fuzz-dir ./cranelift/isle/fuzz
528    - run: cargo fuzz build --dev -s none --fuzz-dir ./crates/environ/fuzz --features component-model
529
530    # common logic to cancel the entire run if this job fails
531    - run: gh run cancel ${{ github.run_id }}
532      if: failure() && github.event_name != 'pull_request'
533      env:
534        GH_TOKEN: ${{ github.token }}
535
536  # Perform all tests of the c-api
537  test_capi:
538    needs: determine
539    name: Test C-API ${{ matrix.os }}
540    runs-on: ${{ matrix.os }}
541    if: needs.determine.outputs.test-capi
542
543    strategy:
544      fail-fast: true
545      matrix:
546        os: [ubuntu-latest, macos-latest, windows-latest]
547
548    steps:
549    - uses: actions/checkout@v4
550      with:
551        submodules: true
552    - uses: ./.github/actions/install-rust
553
554    # Build and test the C API with example C programs along with the example
555    # Rust programs. Note that this only executes if the `determine` step told
556    # us to test the capi which is off-by-default for PRs.
557    - run: cmake -Sexamples -Bexamples/build -DBUILD_SHARED_LIBS=OFF
558    - run: cmake --build examples/build --config Debug
559    - run: cmake -E env CTEST_OUTPUT_ON_FAILURE=1 cmake --build examples/build --config Debug --target RUN_TESTS
560      env:
561        RUST_BACKTRACE: 1
562      if: matrix.os == 'windows-latest'
563    - run: cmake -E env CTEST_OUTPUT_ON_FAILURE=1 cmake --build examples/build --config Debug --target test
564      env:
565        RUST_BACKTRACE: 1
566      if: matrix.os != 'windows-latest'
567
568  # Perform all tests (debug mode) for `wasmtime`.
569  #
570  # Note that the full matrix for what may run here is defined within
571  # `./ci/build-test-matrix.js` and the execution of the `determine` step will
572  # calculate whether the tests are actually run as part of PRs and such.
573  test:
574    needs: determine
575    name: ${{ matrix.name }}
576    runs-on: ${{ matrix.os }}
577    env:
578      QEMU_BUILD_VERSION: 8.1.5
579    strategy:
580      fail-fast: ${{ github.event_name != 'pull_request' }}
581      matrix: ${{ fromJson(needs.determine.outputs.test-matrix) }}
582    steps:
583    - uses: actions/checkout@v4
584      with:
585        submodules: true
586    - uses: ./.github/actions/install-rust
587      with:
588        toolchain: ${{ matrix.rust }}
589
590    # Install targets in order to build various tests throughout the repo
591    - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown ${{ matrix.target }}
592    - run: echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV
593      if: matrix.target != ''
594
595    # Fix an ICE for now in gcc when compiling zstd with debuginfo (??)
596    - run: echo CFLAGS=-g0 >> $GITHUB_ENV
597      if: matrix.target == 'x86_64-pc-windows-gnu'
598
599    # Update binutils if MinGW due to https://github.com/rust-lang/rust/issues/112368
600    - run: C:/msys64/usr/bin/pacman.exe -S --needed mingw-w64-x86_64-gcc --noconfirm
601      if: matrix.target == 'x86_64-pc-windows-gnu'
602    - shell: pwsh
603      run: echo "C:\msys64\mingw64\bin" >> $Env:GITHUB_PATH
604      if: matrix.target == 'x86_64-pc-windows-gnu'
605
606    - run: cargo fetch --locked
607
608    - name: Install cross-compilation tools
609      run: |
610        set -ex
611
612        sudo apt-get update
613        sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build
614
615        # Configure Cargo for cross compilation and tell it how it can run
616        # cross executables
617        upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
618        echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV
619      if: matrix.gcc != ''
620
621    - uses: actions/cache@v4
622      with:
623        path: ${{ runner.tool_cache }}/qemu
624        key: qemu-${{ matrix.target }}-${{ env.QEMU_BUILD_VERSION }}-patchcpuinfo
625      if: matrix.qemu != ''
626    - name: Install qemu
627      run: |
628        set -ex
629
630        upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
631        echo CARGO_TARGET_${upcase}_RUNNER=${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} >> $GITHUB_ENV
632
633        # QEMU emulation is not always the speediest, so total testing time
634        # goes down if we build the libs in release mode when running tests.
635        echo CARGO_PROFILE_DEV_OPT_LEVEL=2 >> $GITHUB_ENV
636
637        # See comments in the source for why we enable this during QEMU
638        # emulation.
639        echo WASMTIME_TEST_NO_HOG_MEMORY=1 >> $GITHUB_ENV
640
641        # See if qemu is already in the cache
642        if [ -f ${{ runner.tool_cache }}/qemu/built ]; then
643          exit 0
644        fi
645
646        # Download and build qemu from source since the most recent release is
647        # way faster at arm emulation than the current version github actions'
648        # ubuntu image uses. Disable as much as we can to get it to build
649        # quickly.
650        curl https://download.qemu.org/qemu-$QEMU_BUILD_VERSION.tar.xz | tar xJf -
651        cd qemu-$QEMU_BUILD_VERSION
652        ./configure --target-list=${{ matrix.qemu_target }} --prefix=${{ runner.tool_cache}}/qemu --disable-tools --disable-slirp --disable-fdt --disable-capstone --disable-docs
653        ninja -C build install
654        touch ${{ runner.tool_cache }}/qemu/built
655      if: matrix.qemu != ''
656
657    # Record some CPU details; this is helpful information if tests fail due
658    # to CPU-specific features.
659    - name: CPU information
660      run: lscpu
661      if: matrix.os == 'ubuntu-latest'
662    - name: CPU information
663      run: sysctl hw
664      if: contains(matrix.os, 'macos')
665    - name: CPU information
666      run: wmic cpu list /format:list
667      shell: pwsh
668      if: matrix.os == 'windows-latest'
669
670    # Since MPK (PKU) is not present on some GitHub runners, we check if it is
671    # available before force-enabling it. This occasional testing is better than
672    # none at all; ideally we would test in a system-mode QEMU VM.
673    - name: Force-run with MPK enabled, if available
674      if: ${{ contains(matrix.name, 'MPK') }}
675      run: |
676        if cargo run --example mpk-available; then
677          echo "::notice::This CI run will force-enable MPK; this ensures tests conditioned with the \`WASMTIME_TEST_FORCE_MPK\` environment variable will run with MPK-protected memory pool stripes."
678          echo WASMTIME_TEST_FORCE_MPK=1 >> $GITHUB_ENV
679        else
680          echo "::warning::This CI run will not test MPK; it has been detected as not available on this machine (\`cargo run --example mpk-available\`)."
681        fi
682
683    # Build and test all features
684    - run: ./ci/run-tests.sh --locked ${{ matrix.bucket }}
685      env:
686        RUST_BACKTRACE: 1
687
688    # NB: the test job here is explicitly lacking in cancellation of this run if
689    # something goes wrong. These take the longest anyway and otherwise if
690    # Windows fails GitHub Actions will confusingly mark the failed Windows job
691    # as cancelled instead of failed.
692
693  # Test `wasmtime-wasi-nn` in its own job, as not all of its backends are
694  # compatible with all targets, and each must be tested separately anyways.
695  test_wasi_nn:
696    strategy:
697      matrix:
698        feature: ["openvino", "onnx"]
699        os: ["ubuntu-latest", "windows-latest"]
700        include:
701          - os: windows-latest
702            feature: winml
703    name: Test wasi-nn (${{ matrix.feature }}, ${{ matrix.os }})
704    runs-on: ${{ matrix.os }}
705    needs: determine
706    if: needs.determine.outputs.run-full
707    steps:
708    - uses: actions/checkout@v4
709      with:
710        submodules: true
711    - uses: ./.github/actions/install-rust
712
713    # Install OpenVINO
714    - uses: abrown/install-openvino-action@v8
715      if: runner.arch == 'X64'
716
717    # Install WinML for testing wasi-nn WinML backend. WinML is only available
718    # on Windows clients and Windows Server with desktop experience enabled.
719    # GitHub Actions Window Server image doesn't have desktop experience
720    # enabled, so we download the standalone library from ONNX Runtime project.
721    - uses: nuget/setup-nuget@v2
722      if: (matrix.os == 'windows-latest') && (matrix.feature == 'winml')
723    - run: nuget install Microsoft.AI.MachineLearning
724      if: (matrix.os == 'windows-latest') && (matrix.feature == 'winml')
725
726    # Install Rust targets.
727    - run: rustup target add wasm32-wasip1
728
729    # Run the tests!
730    - run: cargo test -p wasmtime-wasi-nn --features ${{ matrix.feature }}
731      env:
732        RUST_BACKTRACE: 1
733
734    # common logic to cancel the entire run if this job fails
735    - run: gh run cancel ${{ github.run_id }}
736      if: failure() && github.event_name != 'pull_request'
737      env:
738        GH_TOKEN: ${{ github.token }}
739
740  # Test the `wasmtime-fuzzing` crate. Split out from the main tests because
741  # `--all-features` brings in OCaml, which is a pain to get setup for all
742  # targets.
743  test_fuzzing:
744    needs: determine
745    if: needs.determine.outputs.run-full
746    name: Test wasmtime-fuzzing
747    runs-on: 'ubuntu-latest'
748    steps:
749    - uses: actions/checkout@v4
750      with:
751        submodules: true
752    - uses: ./.github/actions/install-rust
753
754    # Run the tests
755    - run: |
756        cargo test -p wasmtime-fuzzing -p wasm-spec-interpreter
757      env:
758        RUST_BACKTRACE: 1
759
760    # common logic to cancel the entire run if this job fails
761    - run: gh run cancel ${{ github.run_id }}
762      if: failure() && github.event_name != 'pull_request'
763      env:
764        GH_TOKEN: ${{ github.token }}
765
766  # Test debug (DWARF) related functionality.
767  test_debug_dwarf:
768    needs: determine
769    if: needs.determine.outputs.run-dwarf
770    name: Test DWARF debugging
771    runs-on: 'ubuntu-latest'
772    steps:
773    - uses: actions/checkout@v4
774      with:
775        submodules: true
776    - uses: ./.github/actions/install-rust
777    - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown
778    - run: |
779        sudo apt-get update && sudo apt-get install -y gdb lldb-15 llvm
780        # workaround for https://bugs.launchpad.net/ubuntu/+source/llvm-defaults/+bug/1972855
781        sudo mkdir -p /usr/lib/local/lib/python3.10/dist-packages/lldb
782        sudo ln -s /usr/lib/llvm-15/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/
783        cargo test --test all -- --ignored --test-threads 1 debug::
784      env:
785        RUST_BACKTRACE: 1
786        LLDB: lldb-15 # override default version, 14
787
788    # common logic to cancel the entire run if this job fails
789    - run: gh run cancel ${{ github.run_id }}
790      if: failure() && github.event_name != 'pull_request'
791      env:
792        GH_TOKEN: ${{ github.token }}
793
794  build-preview1-component-adapter:
795    name: Build wasi-preview1-component-adapter
796    needs: determine
797    if: needs.determine.outputs.preview1-adapter
798    runs-on: ubuntu-latest
799    permissions:
800      deployments: write
801      contents: write
802    steps:
803    - uses: actions/checkout@v4
804      with:
805        submodules: true
806    - uses: ./.github/actions/install-rust
807    - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown
808
809    - name: Install wasm-tools
810      run: |
811        curl -L https://github.com/bytecodealliance/wasm-tools/releases/download/wasm-tools-1.0.27/wasm-tools-1.0.27-x86_64-linux.tar.gz | tar xfz -
812        echo `pwd`/wasm-tools-1.0.27-x86_64-linux >> $GITHUB_PATH
813
814    - run: ./ci/build-wasi-preview1-component-adapter.sh
815      env:
816        VERSION: ${{ github.sha }}
817
818    - uses: actions/upload-artifact@v4
819      with:
820        name: bins-wasi-preview1-component-adapter
821        path: target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.*.wasm
822
823
824    # common logic to cancel the entire run if this job fails
825    - run: gh run cancel ${{ github.run_id }}
826      if: failure() && github.event_name != 'pull_request'
827      env:
828        GH_TOKEN: ${{ github.token }}
829
830  build-preview1-component-adapter-provider:
831    name: Build wasi-preview1-component-adapter-provider
832    needs: build-preview1-component-adapter
833    runs-on: ubuntu-latest
834    steps:
835    - uses: actions/checkout@v4
836      with:
837        submodules: true
838    - uses: ./.github/actions/install-rust
839    - uses: ./.github/actions/build-adapter-provider
840      with:
841        run-id: ${{ github.run_id }}
842
843
844    # common logic to cancel the entire run if this job fails
845    - run: gh run cancel ${{ github.run_id }}
846      if: failure() && github.event_name != 'pull_request'
847      env:
848        GH_TOKEN: ${{ github.token }}
849
850  # Verify the "min platform" example still works.
851  test-min-platform-example:
852    name: Test the min-platform example
853    needs: determine
854    if: needs.determine.outputs.run-full
855    runs-on: ubuntu-latest
856    steps:
857    - uses: actions/checkout@v4
858      with:
859        submodules: true
860    - uses: ./.github/actions/install-rust
861    - run: cargo install cbindgen --vers "^0.26" --locked
862    - run: rustup target add x86_64-unknown-none
863    - run: ./build.sh x86_64-unknown-none
864      working-directory: ./examples/min-platform
865
866    # Afterwards make sure the generated header file is up to date by ensuring
867    # that the regeneration process didn't change anything in-tree.
868    - run: git diff --exit-code
869
870    # Add the `wasmtime-platform.h` file as a release artifact
871    - uses: actions/upload-artifact@v4
872      with:
873        name: wasmtime-platform-header
874        path: examples/min-platform/embedding/wasmtime-platform.h
875
876    # common logic to cancel the entire run if this job fails
877    - run: gh run cancel ${{ github.run_id }}
878      if: failure() && github.event_name != 'pull_request'
879      env:
880        GH_TOKEN: ${{ github.token }}
881
882
883  build-wasmtime-target-wasm32:
884    name: Build wasmtime-target-wasm32
885    if: needs.determine.outputs.run-full
886    needs: determine
887    runs-on: ubuntu-latest
888    steps:
889    - uses: actions/checkout@v4
890      with:
891        submodules: true
892    - uses: ./.github/actions/install-rust
893    - run: rustup target add wasm32-wasip1 wasm32-unknown-unknown
894    - run: cargo build --target wasm32-wasip1 --no-default-features --features compile,cranelift,all-arch
895      env:
896        VERSION: ${{ github.sha }}
897
898    # common logic to cancel the entire run if this job fails
899    - run: gh run cancel ${{ github.run_id }}
900      if: failure() && github.event_name != 'pull_request'
901      env:
902        GH_TOKEN: ${{ github.token }}
903
904
905  bench:
906    needs: determine
907    if: needs.determine.outputs.run-full
908    name: Run benchmarks
909    runs-on: ubuntu-latest
910    steps:
911    - uses: actions/checkout@v4
912      with:
913        submodules: true
914    - uses: ./.github/actions/install-rust
915    - run: rustup target add wasm32-wasip1
916    - run: cargo test --benches --release
917
918    # common logic to cancel the entire run if this job fails
919    - run: gh run cancel ${{ github.run_id }}
920      if: failure() && github.event_name != 'pull_request'
921      env:
922        GH_TOKEN: ${{ github.token }}
923
924  # Verify that cranelift's code generation is deterministic
925  meta_deterministic_check:
926    needs: determine
927    if: needs.determine.outputs.run-full
928    name: Meta deterministic check
929    runs-on: ubuntu-latest
930    steps:
931    - uses: actions/checkout@v4
932      with:
933        submodules: true
934    - uses: ./.github/actions/install-rust
935    - run: cd cranelift/codegen && cargo build --features all-arch
936    - run: ci/ensure_deterministic_build.sh
937
938    # common logic to cancel the entire run if this job fails
939    - run: gh run cancel ${{ github.run_id }}
940      if: failure() && github.event_name != 'pull_request'
941      env:
942        GH_TOKEN: ${{ github.token }}
943
944  verify-publish:
945    needs: determine
946    if: github.repository == 'bytecodealliance/wasmtime' && needs.determine.outputs.run-full
947    runs-on: ubuntu-latest
948    steps:
949    - uses: actions/checkout@v4
950      with:
951        submodules: true
952    - uses: ./.github/actions/install-rust
953    - run: |
954        cd ${{ runner.tool_cache }}
955        curl -L https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz | tar xzf -
956        echo "`pwd`/sccache-0.2.13-x86_64-unknown-linux-musl" >> $GITHUB_PATH
957        echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV
958    - run: rustc scripts/publish.rs
959    # Make sure the tree is publish-able as-is
960    - run: ./publish verify
961    # Make sure we can bump version numbers for the next release
962    - run: ./publish bump
963
964    # common logic to cancel the entire run if this job fails
965    - run: gh run cancel ${{ github.run_id }}
966      if: failure() && github.event_name != 'pull_request'
967      env:
968        GH_TOKEN: ${{ github.token }}
969
970  # Run a subset of tests under MIRI on CI to help check the `unsafe` code in
971  # Wasmtime to make sure it's at least not obviously incorrect for basic usage.
972  # Note that this doesn't run the full test suite since MIRI can't actually run
973  # WebAssembly itself at this time (aka it doesn't support a JIT). There are a
974  # number of annotations throughout the code which gates some tests on MIRI not
975  # being run.
976  #
977  # Note that `cargo nextest` is used here additionally to get parallel test
978  # execution by default to help cut down on the time in CI.
979  miri:
980    strategy:
981      matrix:
982        crate:
983          - "wasmtime"
984          - "wasmtime-cli"
985          - "wasmtime-environ"
986          - "pulley-interpreter --all-features"
987    needs: determine
988    if: needs.determine.outputs.run-full && github.repository == 'bytecodealliance/wasmtime'
989    name: Miri
990    runs-on: ubuntu-latest
991    env:
992      CARGO_NEXTEST_VERSION: 0.9.67
993    steps:
994    - uses: actions/checkout@v4
995      with:
996        submodules: true
997    - uses: ./.github/actions/install-rust
998      with:
999        toolchain: wasmtime-ci-pinned-nightly
1000    - run: rustup component add rust-src miri
1001    - uses: actions/cache@v4
1002      with:
1003        path: ${{ runner.tool_cache }}/cargo-nextest
1004        key: cargo-nextest-bin-${{ env.CARGO_NEXTEST_VERSION }}
1005    - run: echo "${{ runner.tool_cache }}/cargo-nextest/bin" >> $GITHUB_PATH
1006    - run: cargo install --root ${{ runner.tool_cache }}/cargo-nextest --version ${{ env.CARGO_NEXTEST_VERSION }} cargo-nextest --locked
1007    - run: |
1008        cargo miri nextest run -j4 --no-fail-fast -p ${{ matrix.crate }}
1009      env:
1010        MIRIFLAGS: -Zmiri-strict-provenance
1011
1012    # common logic to cancel the entire run if this job fails
1013    - run: gh run cancel ${{ github.run_id }}
1014      if: failure() && github.event_name != 'pull_request'
1015      env:
1016        GH_TOKEN: ${{ github.token }}
1017
1018  # Perform release builds of `wasmtime` and `libwasmtime.so`. Builds a variety
1019  # of platforms and architectures and then uploads the release artifacts to
1020  # this workflow run's list of artifacts.
1021  #
1022  # Note that the full matrix is computed by `ci/build-build-matrix.js`.
1023  build:
1024    needs: determine
1025    if: needs.determine.outputs.run-full
1026    name: Release build for ${{ matrix.build }}
1027    runs-on: ${{ matrix.os }}
1028    strategy:
1029      fail-fast: ${{ github.event_name != 'pull_request' }}
1030      matrix: ${{ fromJson(needs.determine.outputs.build-matrix) }}
1031    steps:
1032    - uses: actions/checkout@v4
1033      with:
1034        submodules: true
1035
1036    - uses: ./.github/actions/install-rust
1037      with:
1038        toolchain: ${{ matrix.rust }}
1039    - run: |
1040        rustup component add rust-src
1041        rustup target add ${{ matrix.target }}
1042
1043    # On one builder produce the source tarball since there's no need to produce
1044    # it everywhere
1045    - run: ./ci/build-src-tarball.sh
1046      if: matrix.build == 'x86_64-linux'
1047    - uses: ./.github/actions/binary-compatible-builds
1048      with:
1049        name: ${{ matrix.build }}
1050
1051    - uses: ./.github/actions/android-ndk
1052      if: contains(matrix.target, 'android')
1053      with:
1054        target: ${{ matrix.target }}
1055
1056    - run: $CENTOS ./ci/build-release-artifacts.sh "${{ matrix.build }}" "${{ matrix.target }}"
1057
1058    # Assemble release artifacts appropriate for this platform, then upload them
1059    # unconditionally to this workflow's files so we have a copy of them.
1060    - run: ./ci/build-tarballs.sh "${{ matrix.build }}" "${{ matrix.target }}"
1061
1062    - uses: actions/upload-artifact@v4
1063      with:
1064        name: bins-${{ matrix.build }}
1065        path: dist
1066
1067    # common logic to cancel the entire run if this job fails
1068    - run: gh run cancel ${{ github.run_id }}
1069      if: failure() && github.event_name != 'pull_request'
1070      env:
1071        GH_TOKEN: ${{ github.token }}
1072
1073  # This is a "join node" which depends on all prior workflows. The merge queue,
1074  # for example, gates on this to ensure that everything has executed
1075  # successfully.
1076  #
1077  # Note that this is required currently for odd reasons with github. Notably
1078  # the set of checks to enter the merge queue and leave the merge queue must
1079  # be the same which means that the "build" step for example shows as skipped
1080  # for PRs but expands to many different steps for merge-queue-based PRs. That
1081  # means that for that step there's no single name to gate on, so it's required
1082  # to have a "join" node here which joins everything.
1083  #
1084  # Note that this currently always runs to always report a status, even on
1085  # cancellation and even if dependency steps fail. Each dependency tries to
1086  # cancel the whole run if it fails, so if a test matrix entry fails, for
1087  # example, it cancels the build matrix entries too. This step then tries to
1088  # fail on cancellation to ensure that the dependency failures are propagated
1089  # correctly.
1090  ci-status:
1091    name: Record the result of testing and building steps
1092    runs-on: ubuntu-latest
1093    needs:
1094      - test
1095      - test_capi
1096      - test_debug_dwarf
1097      - test_fuzzing
1098      - test_wasi_nn
1099      - test_nightly
1100      - build
1101      - rustfmt
1102      - clangformat
1103      - cargo_deny
1104      - cargo_vet
1105      - doc
1106      - micro_checks
1107      - monolith_checks
1108      - checks_winarm64
1109      - bench
1110      - meta_deterministic_check
1111      - verify-publish
1112      - determine
1113      - miri
1114      - build-preview1-component-adapter
1115      - build-preview1-component-adapter-provider
1116      - build-wasmtime-target-wasm32
1117      - test-min-platform-example
1118      - check_js
1119    if: always()
1120    steps:
1121    - name: Successful test and build
1122      if: ${{ !(contains(needs.*.result, 'failure')) }}
1123      run: exit 0
1124    - name: Failing test and build
1125      if: ${{ contains(needs.*.result, 'failure') }}
1126      run: exit 1
1127    - name: Report failure on cancellation
1128      if: ${{ contains(needs.*.result, 'cancelled') || cancelled() }}
1129      run: exit 1
1130
1131  # The purpose of this jobs is to watch for changes on the `release-*`
1132  # branches of this repository and look for the term
1133  # "automatically-tag-and-release-this-commit" within merged PRs/commits. Once
1134  # that term is found the current version of `Cargo.toml`, the `wasmtime-cli`
1135  # Cargo.toml, is created as a tag and the tag is pushed to the repo.
1136  # Currently the tag is created through the GitHub API with an access token to
1137  # ensure that CI is further triggered for the tag itself which performs the
1138  # full release process.
1139  #
1140  # Note that this depends on the `ci-status` step above which is the "join"
1141  # point of this workflow for when everything succeeds. the purpose of that is
1142  # so that the tag is only created after the aftifacts have been uploaded for
1143  # this workflow as the `publish-artifacts.yml` workflow will download these
1144  # artifacts and then publish them to the tag.
1145  push-tag:
1146    runs-on: ubuntu-latest
1147    needs: ci-status
1148    if: |
1149      always()
1150      && needs.ci-status.result == 'success'
1151      && github.event_name == 'push'
1152      && startsWith(github.ref, 'refs/heads/release-')
1153      && github.repository == 'bytecodealliance/wasmtime'
1154    steps:
1155    - uses: actions/checkout@v4
1156      with:
1157        submodules: true
1158        fetch-depth: 0
1159    - name: Test if tag is needed
1160      run: |
1161        git log ${{ github.event.before }}...${{ github.event.after }} | tee main.log
1162        version=$(grep '^version =' Cargo.toml | head -n 1 | sed 's/.*"\(.*\)"/\1/')
1163        echo "version: $version"
1164        echo "version=$version" >> $GITHUB_OUTPUT
1165        echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
1166        if grep -q "automatically-tag-and-release-this-commit" main.log; then
1167          echo push-tag
1168          echo "push_tag=yes" >> $GITHUB_OUTPUT
1169        else
1170          echo no-push-tag
1171          echo "push_tag=no" >> $GITHUB_OUTPUT
1172        fi
1173      id: tag
1174    - name: Push the tag
1175      run: |
1176        git_refs_url=$(jq .repository.git_refs_url $GITHUB_EVENT_PATH | tr -d '"' | sed 's/{\/sha}//g')
1177        curl -iX POST $git_refs_url \
1178          -H "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \
1179          -d @- << EOF
1180        {
1181          "ref": "refs/tags/v${{ steps.tag.outputs.version }}",
1182          "sha": "${{ steps.tag.outputs.sha }}"
1183        }
1184        EOF
1185      if: steps.tag.outputs.push_tag == 'yes'
1186