name: wasm_of_ocaml

on:
  pull_request:
  push:
    branches:
      - master
  schedule:
    # Prime the caches every Monday
    - cron: 0 1 * * MON

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  build:
    env:
      WASM_OF_OCAML: true
      OXCAML_OPAM_REPO_REF: d89747aa6e7b9f44d6cc844dca5b5e7c8a90716f

    strategy:
      fail-fast: false
      matrix:
        os:
          - ubuntu-latest
        os-name:
           - Ubuntu
        ocaml-compiler:
          - "4.14"
          - "5.0"
          - "5.1"
          - "5.2"
          - "5.4"
          - "5.5"
        separate_compilation:
          - true
        jane_street_tests:
          - false
        all_jane_street_tests:
          - false
        wasi:
          - false
        include:
          - os: macos-latest
            os-name: MacOS
            ocaml-compiler: "5.3"
            separate_compilation: true
            jane_street_tests: false
            all_jane_street_tests: false
            wasi: false
          - os: windows-latest
            os-name: Windows
            ocaml-compiler: "5.3"
            separate_compilation: true
            # Jane Street tests disabled for now (basement only works on Linux)
            jane_street_tests: false
            all_jane_street_tests: false
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "5.3"
            separate_compilation: true
            jane_street_tests: true
            all_jane_street_tests: true
            wasi: false
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "5.3"
            separate_compilation: false
            jane_street_tests: true
            all_jane_street_tests: false
            wasi: false
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "ocaml-variants.5.2.0+ox"
            separate_compilation: true
            jane_street_tests: true
            all_jane_street_tests: true
            wasi: false
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "5.3"
            separate_compilation: true
            jane_street_tests: false
            all_jane_street_tests: false
            wasi: true

    runs-on: ${{ matrix.os }}

    name:
       ${{ matrix.wasi && 'WASI / ' || '' }}${{ (! matrix.separate_compilation) && 'Whole program / ' || ''}}${{ matrix.ocaml-compiler }} / ${{ matrix.os-name }}${{ matrix.all_jane_street_tests && ' / Jane Street tests' || ''}}

    steps:
      - name: Update apt cache
        if: ${{ matrix.os == 'ubuntu-latest' }}
        run: sudo apt-get update

      - name: Checkout tree
        uses: actions/checkout@v6
        with:
          path: wasm_of_ocaml

      - name: Checkout Jane Street opam repository
        if: ${{ matrix.jane_street_tests && ! endsWith(matrix.ocaml-compiler, '+ox') }}
        uses: actions/checkout@v6
        with:
          repository: janestreet/opam-repository
          ref: 2819773f29b6f6c14b918eae3cb40c8ff6b22d0e
          path: janestreet/opam-repository

      - name: Checkout OxCaml opam repository
        if: ${{ matrix.jane_street_tests && endsWith(matrix.ocaml-compiler, '+ox') }}
        uses: actions/checkout@v6
        with:
          repository: oxcaml/opam-repository
          ref: ${{ env.OXCAML_OPAM_REPO_REF }}
          path: janestreet/opam-repository

      - name: Set-up Node.js
        uses: actions/setup-node@v6
        with:
          node-version: ${{ matrix.os == 'windows-latest' && 'latest' || 'v26.0.0-v8-canary20260216631fb6e5ef' }}

      - name: Set-up Wasmtime
        if: matrix.wasi
        uses: bytecodealliance/actions/wasmtime/setup@v1
        with:
          version: "dev"

      - name: Set-up Wizard engine
        if: matrix.wasi
        uses: ./wasm_of_ocaml/.github/actions/setup-wizard

      - name: Set-up OCaml ${{ matrix.ocaml-compiler }}
        uses: ocaml/setup-ocaml@v3
        with:
          ocaml-compiler: ${{ matrix.ocaml-compiler }}
        if: ${{ ! endsWith(matrix.ocaml-compiler, '+ox') }}

      - name: Set-up OxCaml ${{ matrix.ocaml-compiler }}
        uses: ocaml/setup-ocaml@v3
        with:
          ocaml-compiler: ${{ matrix.ocaml-compiler }}
          opam-repositories: |
            default: https://github.com/ocaml/opam-repository.git
            ox: https://github.com/oxcaml/opam-repository.git#${{ env.OXCAML_OPAM_REPO_REF }}
        if: ${{ endsWith(matrix.ocaml-compiler, '+ox') }}

      - name: Set-up Binaryen
        uses: Aandreba/setup-binaryen@v1.0.0
        with:
          token: ${{ github.token }}

      # Work-around a race between reinstalling mingw-w64-shims
      # (because of conf-pkg-config optional dep) and installing other
      # packages that implicitly depend on mingw-w64-shims.
      - run: opam install conf-pkg-config conf-mingw-w64-gcc-i686 conf-mingw-w64-g++-x86_64
        if: runner.os == 'Windows'

      - name: Pin wasm_of_ocaml
        working-directory: ./wasm_of_ocaml
        run: opam pin . -n --with-version 6.0.1+ox

      - name: Pin specific packages for OxCaml
        run: |
          opam pin add opam-core -n 2.5.1+ox
          opam pin add gen_js_api -n 1.1.2+ox
          opam pin add zarith -n 1.14+ox
          opam pin add sedlex -n 3.6+ox
        if: ${{ endsWith(matrix.ocaml-compiler, '+ox') }}

      # oxcaml-dune-patches pins dune <= 3.21, but the project requires
      # dune >= 3.23. Re-pin the package with the dune version constraint
      # stripped — opam re-checks dependency constraints on every install,
      # so a one-shot --ignore-constraints-on flag isn't enough.
      - name: Drop dune version constraint from oxcaml-dune-patches (OxCaml)
        if: ${{ endsWith(matrix.ocaml-compiler, '+ox') }}
        run: |
          DIR=$(mktemp -d)
          opam show --raw oxcaml-dune-patches \
            | perl -0777 -pe 's/"dune"\s+\{build & \([^)]+\)\}/"dune" {build}/' \
            > "$DIR/oxcaml-dune-patches.opam"
          opam pin add oxcaml-dune-patches "$DIR" -k path -n -y

      # Add some packaged to the opam invariant;
      # opam makes wrong choices otherwise
      - name: Hold some packages
        if: ${{ matrix.jane_street_tests && endsWith(matrix.ocaml-compiler, '+ox') }}
        run: |
          opam install gen_js_api zarith opam-format
          opam switch set-invariant --formula '["ocaml-variants" {= "5.2.0+ox"} "zarith" "gen_js_api" "opam-format"]'

      - name: Checkout Jane Street packages
        if: matrix.jane_street_tests
        run: |
          opam repo add js janestreet/opam-repository
          opam install opam-format
          opam exec -- dune exec --root wasm_of_ocaml tools/ci_setup.exe

      - name: Pin Jane Street packages (v0.16)
        if: contains(fromJson('["4.14","5.0","5.1","5.2"]'), matrix.ocaml-compiler)
        run: |
          opam pin add -n base.v0.16.1 https://github.com/ocaml-wasm/base.git#wasm
          opam pin add -n time_now.v0.16.1 https://github.com/ocaml-wasm/time_now.git#wasm
          opam pin add -n ppx_inline_test.v0.16.1 https://github.com/ocaml-wasm/ppx_inline_test.git#wasm
          opam pin add -n ppx_expect.v0.16.1 https://github.com/ocaml-wasm/ppx_expect.git#wasm

      - name: Pin Jane Street packages (v0.17)
        if: ${{ ! (matrix.jane_street_tests || contains(fromJson('["4.14","5.0","5.1","5.2"]'), matrix.ocaml-compiler)) }}
        run: |
          opam pin add -n base.v0.17.3 https://github.com/ocaml-wasm/base.git#wasm-v0.17
          opam pin add -n time_now.v0.17.0 https://github.com/ocaml-wasm/time_now.git#wasm-v0.17
          opam pin add -n ppx_inline_test.v0.17.1 https://github.com/ocaml-wasm/ppx_inline_test.git#wasm-v0.17
          opam pin add -n ppx_expect.v0.17.3 https://github.com/ocaml-wasm/ppx_expect.git#wasm-v0.17

      - name: Install wasm_of_ocaml and its test dependencies
        working-directory: ./wasm_of_ocaml
        run: |
          opam install . -t

      - name: Run tests
        if: ${{ matrix.separate_compilation && ! matrix.wasi }}
        working-directory: ./wasm_of_ocaml
        run: opam exec -- dune build @runtest-wasm

      # we continue-on-error on windows because we seem to often hit
      # an internal assert inside libuv.
      # See https://github.com/libuv/libuv/issues/3622

      - name: Run tests with CPS effects
        if: ${{ matrix.ocaml-compiler >= '5.' && matrix.separate_compilation && ! matrix.wasi }}
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./wasm_of_ocaml
        run: opam exec -- dune build @runtest-wasm --profile with-effects

      - name: Run tests with native effects
        if: ${{ matrix.ocaml-compiler >= '5.' && matrix.separate_compilation && matrix.os != 'windows-latest' }}
        working-directory: ./wasm_of_ocaml
        run: opam exec -- dune build @runtest-wasm --profile with-native-effects

      - name: Run tests (WASI runtime - node)
        if: ${{ matrix.wasi }}
        working-directory: ./wasm_of_ocaml
        run: opam exec -- dune build @runtest-wasm --profile wasi-with-native-effects

      - name: Run tests (WASI runtime - Wizard engine)
        if: ${{ matrix.wasi }}
        working-directory: ./wasm_of_ocaml
        env:
          WASM_ENGINE: wizard-fast
        run: opam exec -- dune build @runtest-wasm --profile wasi-with-native-effects

      - name: Run tests (WASI runtime - Wizard engine - SPC)
        if: ${{ matrix.wasi }}
        working-directory: ./wasm_of_ocaml
        env:
          WASM_ENGINE: wizard-spc
        run: opam exec -- dune build @runtest-wasm --profile wasi-with-native-effects

      - name: Run tests (WASI runtime - wasmtime)
        if: ${{ matrix.wasi }}
        working-directory: ./wasm_of_ocaml
        env:
          WASM_ENGINE: wasmtime
        run: opam exec -- dune build @runtest-wasm --profile wasi

      - name: Run Base tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/base
        run: opam exec -- dune runtest

      - name: Run Base bigstring tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/base_bigstring
        run: opam exec -- dune runtest

      - name: Run Core tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/core
        run: opam exec -- dune runtest

      - name: Run Bignum tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/bignum
        run: opam exec -- dune runtest

      - name: Run Bin_prot tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/bin_prot
        run: opam exec -- dune runtest

      - name: Run String_dict tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/string_dict
        run: opam exec -- dune runtest

      - name: Run Zarith tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/zarith_stubs_js
        run: opam exec -- dune runtest

      - name: Run Virtual_dom tests
        if: matrix.all_jane_street_tests
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/virtual_dom
        run: opam exec -- dune runtest

      - name: Run Bonsai tests
        if: ${{ matrix.all_jane_street_tests && matrix.os != 'windows-latest' }}
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/bonsai_test
        run: opam exec -- dune runtest

      - name: Run Bonsai web tests
        if: ${{ matrix.all_jane_street_tests && matrix.os != 'windows-latest' }}
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/bonsai_web_test
        run: opam exec -- dune runtest

      - name: Run Bonsai web components' tests
        if: ${{ matrix.all_jane_street_tests && matrix.os != 'windows-latest' }}
        continue-on-error: ${{ matrix.os == 'windows-latest' }}
        working-directory: ./janestreet/lib/bonsai_web_components
        run: opam exec -- dune runtest

      - name: Run Bonsai tests (release profile)
        if: ${{ ! matrix.separate_compilation }}
        working-directory: ./janestreet/lib/bonsai_test
        run: opam exec -- dune runtest --profile release

      - name: Run Bonsai web tests (release profile)
        if: ${{ ! matrix.separate_compilation }}
        working-directory: ./janestreet/lib/bonsai_web_test
        run: opam exec -- dune runtest --profile release

      - name: Run Bonsai web components' tests (release profile)
        if: ${{ ! matrix.separate_compilation }}
        working-directory: ./janestreet/lib/bonsai_web_components
        run: opam exec -- dune runtest --profile release
