name: js_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:
      OXCAML_OPAM_REPO_REF: d89747aa6e7b9f44d6cc844dca5b5e7c8a90716f

    strategy:
      fail-fast: false
      matrix:
        os:
          - ubuntu-latest
        os-name:
          - Ubuntu
        ocaml-name:
          - ""
        ocaml-compiler:
          - "4.13"
          - "5.0"
          - "5.1"
          - "5.2"
          - "5.3"
          - "5.5"
        skip-test:
          - true
        skip-doc:
          - true
        skip-effects:
          - true
        include:
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "4.14"
            skip-effects: true
            skip-test: false
            skip-doc: true
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-name: "4.14.2+32bit"
            ocaml-compiler: "ocaml-variants.4.14.2+options,ocaml-option-32bit"
            skip-effects: true
            skip-test: false
            skip-doc: true
          - os: macos-latest
            os-name: Macos
            ocaml-compiler: "4.14"
            skip-effects: true
            skip-test: false
            skip-doc: true
          - os: windows-latest
            os-name: Windows
            ocaml-compiler: "4.14"
            skip-effects: true
            skip-test: false
            skip-doc: true
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "5.4.0"
            skip-effects: false
            skip-test: false
            skip-doc: false
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-compiler: "5.1"
            skip-effects: false
            skip-test: false
            skip-doc: true
            # Note this OCaml compiler is bytecode only
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-name: "5.4.0+32bit"
            ocaml-compiler: "ocaml-variants.5.4.0+options,ocaml-option-32bit"
            skip-effects: true # disabled for the same reason than `skip-test`
            skip-test: true # the `time_now.0.17` package is pulled and doesn't work in 32 bits :(
            skip-doc: true
          - os: macos-latest
            os-name: MacOS
            ocaml-compiler: "5.4.0"
            skip-effects: true
            skip-test: false
            skip-doc: true
          - os: windows-latest
            os-name: Windows
            ocaml-compiler: "5.4.0"
            skip-effects: false
            skip-test: false
            skip-doc: true
          - os: ubuntu-latest
            os-name: Ubuntu
            ocaml-name: "OxCaml"
            ocaml-compiler: "ocaml-variants.5.2.0+ox"
            skip-effects: false
            skip-test: false
            skip-doc: true

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

    name:
       ${{ matrix.ocaml-name != '' && matrix.ocaml-name || matrix.ocaml-compiler}} / ${{ matrix.os-name }}${{ ! matrix.skip-test && ' / Tests' || ''}}${{ ! matrix.skip-effects && ' / Effects' || ''}}${{ ! matrix.skip-doc && ' / Docs' || ''}}


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

      - name: Checkout tree
        uses: actions/checkout@v6

        # EJGA: Note that I tried to fix this upstream as depext is
        # getting much better, but no luck yet, c.f:
        # https://github.com/ocaml/opam-repository/pull/26626
      - name: Install apt 32-bit dependencies
        if: contains( matrix.ocaml-compiler, 'ocaml-option-32bit')
        run: |
          sudo apt-get install aptitude
          sudo dpkg --add-architecture i386
          sudo aptitude -o Acquire::Retries=30 update -q
          # Note we also install the 64-bit versions here as opam will
          # try to install them anyways, so we save an apt-roundtrip.
          sudo aptitude -o Acquire::Retries=30 install gcc-multilib g++-multilib pkg-config libgmp-dev libgmp-dev:i386 libx11-dev libx11-dev:i386 libxft-dev libxft-dev:i386 -y

      - name: Set-up Node.js
        uses: actions/setup-node@v6
        with:
          node-version: latest

      - 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') }}

      # 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-g++-i686 conf-mingw-w64-g++-x86_64
        if: runner.os == 'Windows'

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

      - run: opam install . --best-effort --solver builtin-mccs+glpk
        if: ${{ matrix.skip-test }}

      - name: Pin specific packages for OxCaml
        # With OxCaml fixes
        run: |
          opam pin add opam-core -n 2.5.1+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

      - run: opam install . --deps-only --with-test
        # Install the test dependencies
        if: ${{ !matrix.skip-test }}

      - name: Pin js_of_ocaml
        # OxCaml wants a precise version of Js_of_ocaml
        run: opam pin . -n --with-version 6.0.1+ox

      - run: opam install .
        # Install the packages (without running the tests)
        if: ${{ !matrix.skip-test }}

      - run: opam exec -- make all
        if: ${{ !matrix.skip-test }}

      - run: opam exec -- make tests
        if: ${{ !matrix.skip-test }}

      - name: Run Babel downleveling test
        if: ${{ !matrix.skip-test && runner.os == 'Linux' }}
        run: |
          npm install
          opam exec -- make test-babel-downlevel

      - run: opam exec -- dune build @all @runtest @runtest-js --profile with-effects
        if: ${{ !matrix.skip-effects }}

      - run: opam exec -- dune build @all @runtest @runtest-js --profile with-effects-double-translation
        if: ${{ !matrix.skip-effects }}

      - run: opam exec -- git diff --exit-code
        if: ${{ !matrix.skip-test }}

      # Documentation is built and deployed by a dedicated workflow (doc.yml):
      # odoc (manual + API) + the interactive examples, themed with the Ocsigen
      # chrome and published to gh-pages (ocsigen.org/js_of_ocaml/). Here we only
      # check that it compiles. The old `make doc` -> wikidoc -> html_of_wiki
      # pipeline has been retired.
      - name: build doc (compile check)
        if: ${{ !matrix.skip-doc }}
        run: |
          opam install odoc lwt_log yojson ocp-indent graphics higlo
          opam exec -- make doc
