name: Test & Release

env:
  DEBUG: 'napi:*'
  RUST_BACKTRACE: 1
  CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-gnu-gcc

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

permissions:
  contents: write
  id-token: write

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  lint:
    name: Lint SourceCode
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'

      - name: Install
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: stable
          components: rustfmt, clippy

      - name: Cache cargo
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
          key: lint-cargo-cache

      - name: 'Install dependencies'
        run: yarn install --immutable --mode=skip-build

      - name: 'Lint JS/TS'
        run: yarn lint

      - name: Cargo fmt
        run: cargo fmt -- --check

      - name: Clippy
        run: cargo clippy

  build_and_test:
    strategy:
      fail-fast: false
      matrix:
        node: ['16', '18', '20']
        settings:
          - host: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            build: yarn build:test
            test: |
              yarn test:cli
              yarn test --verbose
              yarn tsc -p examples/napi/tsconfig.json --noEmit
              yarn test:macro
            toolchain: stable
          - host: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            build: yarn build:test
            test: |
              yarn test:cli
              yarn test --verbose
              yarn tsc -p examples/napi/tsconfig.json --noEmit
              yarn test:macro
            toolchain: 1.63.0
          - host: macos-latest
            target: x86_64-apple-darwin
            build: yarn build:test
            test: |
              yarn test:cli
              yarn test --verbose
              yarn tsc -p examples/napi/tsconfig.json --noEmit
              yarn test:macro
            toolchain: stable
          - host: windows-latest
            target: x86_64-pc-windows-msvc
            build: yarn build:test
            test: |
              yarn test:cli
              yarn test --verbose
              yarn tsc -p examples/napi/tsconfig.json --noEmit
              yarn test:macro
            toolchain: stable
          - host: windows-latest
            target: i686-pc-windows-msvc
            build: |
              yarn workspace @examples/napi build --target i686-pc-windows-msvc --release
              yarn workspace @examples/compat-mode build --target i686-pc-windows-msvc --release
            test: |
              yarn test --verbose
              node ./node_modules/electron/install.js
              yarn test:electron
            toolchain: stable
        exclude:
          - settings:
              toolchain: 1.63.0
            node: 18
          - settings:
              toolchain: 1.63.0
            node: 20
          - settings:
              target: i686-pc-windows-msvc
            node: 16
          - settings:
              target: i686-pc-windows-msvc
            node: 18
    name: ${{ matrix.settings.host }} - node@${{ matrix.node }} - toolchain@ ${{ matrix.settings.toolchain }}
    runs-on: ${{ matrix.settings.host }}

    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node }}
          cache: 'yarn'

      - name: Install
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: ${{ matrix.settings.toolchain }}
          targets: ${{ matrix.settings.target }}

      - name: Cache cargo
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ matrix.settings.host }}-${{ matrix.settings.toolchain }}-${{ matrix.settings.target }}-cargo-cache

      - name: 'Install dependencies'
        shell: bash
        run: yarn install --mode=skip-build --immutable

      - name: Check build
        run: cargo check --target ${{ matrix.settings.target }} --all --bins --examples --tests -vvv

      - name: Build tests
        if: matrix.settings.build
        run: ${{ matrix.settings.build }}

      - name: Setup node
        uses: actions/setup-node@v3
        if: matrix.settings.target == 'i686-pc-windows-msvc'
        with:
          node-version: 18
          architecture: 'x86'

      - name: Unit tests
        if: matrix.settings.test
        run: ${{ matrix.settings.test }}

      - name: Electron tests
        if: matrix.settings.target == 'x86_64-apple-darwin' || matrix.settings.target == 'x86_64-pc-windows-msvc'
        run: |
          node ./node_modules/electron/install.js
          yarn test:electron

      - name: Electron tests
        if: matrix.settings.target == 'x86_64-unknown-linux-gnu'
        run: |
          node ./node_modules/electron/install.js
          xvfb-run --auto-servernum yarn test:electron

      - name: Test build with profile
        run: yarn workspace @examples/napi build --profile napi-rs-custom

  build_only:
    name: Build only test - ${{ matrix.settings.target }}
    runs-on: ${{ matrix.settings.host }}
    strategy:
      fail-fast: false
      matrix:
        settings:
          - host: ubuntu-latest
            target: aarch64-linux-android
          - host: ubuntu-latest
            target: armv7-linux-androideabi
          - host: ubuntu-latest
            target: riscv64gc-unknown-linux-gnu
            setup: |
              sudo apt-get update
              sudo apt-get install -y gcc-riscv64-linux-gnu
          - host: windows-latest
            target: aarch64-pc-windows-msvc
    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'

      - name: Install
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: stable
          targets: ${{ matrix.settings.target }}

      - name: Cache cargo
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: stable-${{ matrix.settings.host }}-${{ matrix.settings.target }}-cargo-cache

      - name: Setup toolchain
        if: matrix.settings.setup
        run: ${{ matrix.settings.setup }}

      - name: Install dependencies
        run: yarn install --immutable --mode=skip-build

      - name: Cross build native tests
        run: yarn build:test -- --target ${{ matrix.settings.target }} --release
        shell: bash

  build_in_docker:
    name: build - ${{ matrix.settings.target }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        settings:
          - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
            target: x86_64-unknown-linux-musl
            libc: 'musl'
            arch: 'x64'
          - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
            target: aarch64-unknown-linux-musl
          - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
            target: aarch64-unknown-linux-gnu
            arch: 'arm64'
            libc: 'gnu'
          - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
            target: x86_64-unknown-linux-gnu

    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'

      - name: Install dependencies
        run: |
          yarn config set supportedArchitectures.cpu --json '["x64", "arm64"]'
          yarn config set supportedArchitectures.libc --json '["musl", "glibc"]'
          yarn install --immutable --mode=skip-build

      - name: Cache cargo
        uses: actions/cache@v3
        with:
          path: |
            .cargo-cache/registry
            .cargo-cache/git
            target
          key: stable-${{ matrix.settings.target }}-cargo-cache

      - name: Cross build native tests
        uses: addnab/docker-run-action@v3
        with:
          image: ${{ matrix.settings.image }}
          options: -v ${{ github.workspace }}/.cargo-cache/registry:/usr/local/cargo/registry -v ${{ github.workspace }}/.cargo-cache/git:/usr/local/cargo/git -v ${{ github.workspace }}:/napi-rs -w /napi-rs
          run: |
            yarn build:test -- --target ${{ matrix.settings.target }}
            chmod 777 -R .cargo-cache
            chmod 777 -R target

      - uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.settings.target }}-example
          path: examples/napi/index.node
      - uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.settings.target }}-example-compat
          path: examples/napi-compat-mode/index.node

  test_in_docker:
    name: Test - ${{ matrix.settings.target }} - ${{ matrix.node }}
    runs-on: ubuntu-latest
    needs: build_in_docker
    strategy:
      fail-fast: false
      matrix:
        node: [16, 18, 20]
        settings:
          - image: 'node:{:version}-slim'
            target: x86_64-unknown-linux-gnu
            args: ''
            arch: 'x64'
            libc: 'gnu'
          - image: 'node:{:version}-slim'
            target: aarch64-unknown-linux-gnu
            args: '--platform linux/arm64'
            arch: 'arm64'
            libc: 'gnu'
          - image: 'node:{:version}-alpine'
            target: x86_64-unknown-linux-musl
            args: ''
            arch: 'x64'
            libc: 'musl'
          - image: 'node:{:version}-alpine'
            target: aarch64-unknown-linux-musl
            args: '--platform linux/arm64'
            arch: 'arm64'
            libc: 'musl'
    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'
      - uses: actions/download-artifact@v3
        with:
          name: ${{ matrix.settings.target }}-example
          path: examples/napi/index.node
      - uses: actions/download-artifact@v3
        with:
          name: ${{ matrix.settings.target }}-example-compat
          path: examples/napi-compat-mode/index.node
      - name: Install dependencies
        run: |
          yarn config set --json supportedArchitectures.cpu '["current", "${{ matrix.settings.arch }}"]'
          yarn config set --json supportedArchitectures.libc '["current", "${{ matrix.settings.libc }}"]'
          yarn install --immutable --mode=skip-build
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2
        with:
          platforms: all
      - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
      - name: Setup image name
        id: image-name
        run: |
          node -e "console.info('docker-image=${{ matrix.settings.image }}'.replace('{:version}', ${{ matrix.node }}))" >> "$GITHUB_OUTPUT"
      - name: Setup and run tests
        uses: addnab/docker-run-action@v3
        with:
          image: ${{ steps.image-name.outputs.docker-image }}
          options: ${{ matrix.settings.args }} -v ${{ github.workspace }}/cores:/cores -v ${{ github.workspace }}:/build -w /build
          run: >-
            ulimit -c &&
            ulimit -c unlimited &&
            ulimit -c &&
            yarn test
      - name: List files
        run: |
          ls -la .
          ls -la ./cores

  build-and-test-linux-armv7:
    name: stable - armv7-unknown-linux-gnu - node@18
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'

      - name: Install
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: stable
          targets: armv7-unknown-linux-gnueabihf

      - name: Install ziglang
        uses: goto-bus-stop/setup-zig@v2
        with:
          version: 0.10.1

      - name: Cache cargo
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: stable-linux-armv7-gnu-node@18-cargo-cache

      - name: Install dependencies
        run: |
          yarn config set --json supportedArchitectures.cpu '["arm", "current"]'
          yarn install --immutable --mode=skip-build

      - name: Cross build native tests
        run: yarn build:test -- --target armv7-unknown-linux-gnueabihf --cross-compile

      - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

      - name: Setup and run tests
        uses: addnab/docker-run-action@v3
        with:
          image: node:lts-bullseye-slim
          options: --platform linux/arm/v7 -v ${{ github.workspace }}:/build -w /build
          run: yarn test

  build_binary_crate:
    runs-on: ubuntu-latest
    name: Test cli build binary
    steps:
      - uses: actions/checkout@v4

      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'

      - name: Install
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: stable

      - name: Cache cargo
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: stable-cargo-cache-build-binary

      - name: 'Install dependencies'
        run: yarn install --mode=skip-build --immutable

      - name: Build and run binary
        run: |
          yarn workspace binary build
          ./examples/binary/napi-examples-binary
          yarn workspace binary build --profile napi-rs-custom

  check-all-features:
    strategy:
      fail-fast: false
      matrix:
        settings:
          - features: 'napi1,napi2,napi3,napi4,napi5,napi6,napi7,napi8,experimental,async,chrono_date,latin1,full'
            package: 'napi'
          - features: 'compat-mode,strict,type-def,noop,full,default'
            package: 'napi-derive'
    name: stable - ubuntu-latest
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: stable

      - name: Check build
        run: cargo check -p ${{ matrix.settings.package }} -F ${{ matrix.settings.features }}

  release-npm:
    runs-on: ubuntu-latest
    needs:
      - lint
      - build_binary_crate
    if: "startsWith(github.event.head_commit.message, 'chore(release): publish')"
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'yarn'
      - name: Publish
        run: |
          yarn install --mode=skip-build
          yarn build
          npm config set provenance true
          echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
          yarn lerna publish from-package --pre-dist-tag canary --yes
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}