name: "Ruby: Build"

on:
  push:
    paths:
      - "ruby/**"
      - .github/workflows/ruby-build.yml
      - .github/actions/fetch-codeql/action.yml
      - codeql-workspace.yml
    branches:
      - main
      - "rc/*"
  pull_request:
    paths:
      - "ruby/**"
      - .github/workflows/ruby-build.yml
      - .github/actions/fetch-codeql/action.yml
      - codeql-workspace.yml
    branches:
      - main
      - "rc/*"
  workflow_dispatch:
    inputs:
      tag:
        description: "Version tag to create"
        required: false

env:
  CARGO_TERM_COLOR: always

defaults:
  run:
    working-directory: ruby

permissions:
  contents: read

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]

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

    steps:
      - uses: actions/checkout@v4
      - name: Install GNU tar
        if: runner.os == 'macOS'
        run: |
          brew install gnu-tar
          echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
      - name: Prepare Windows
        if: runner.os == 'Windows'
        shell: powershell
        run: |
          git config --global core.longpaths true
      - uses: ./.github/actions/os-version
        id: os_version
      - name: Cache entire extractor
        uses: actions/cache@v3
        id: cache-extractor
        with:
          path: |
            ruby/extractor/target/release/codeql-extractor-ruby
            ruby/extractor/target/release/codeql-extractor-ruby.exe
            ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
          key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
      - uses: actions/cache@v3
        if: steps.cache-extractor.outputs.cache-hit != 'true'
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            ruby/target
          key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/**/Cargo.lock') }}
      - name: Check formatting
        if: steps.cache-extractor.outputs.cache-hit != 'true'
        run: cd extractor && cargo fmt --all -- --check
      - name: Build
        if: steps.cache-extractor.outputs.cache-hit != 'true'
        run: cd extractor && cargo build --verbose
      - name: Run tests
        if: steps.cache-extractor.outputs.cache-hit != 'true'
        run: cd extractor && cargo test --verbose
      - name: Release build
        if: steps.cache-extractor.outputs.cache-hit != 'true'
        run: cd extractor && cargo build --release
      - name: Generate dbscheme
        if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
        run: extractor/target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
      - uses: actions/upload-artifact@v3
        if: ${{ matrix.os == 'ubuntu-latest' }}
        with:
          name: ruby.dbscheme
          path: ruby/ql/lib/ruby.dbscheme
      - uses: actions/upload-artifact@v3
        if: ${{ matrix.os == 'ubuntu-latest' }}
        with:
          name: TreeSitter.qll
          path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
      - uses: actions/upload-artifact@v3
        with:
          name: extractor-${{ matrix.os }}
          path: |
            ruby/extractor/target/release/codeql-extractor-ruby
            ruby/extractor/target/release/codeql-extractor-ruby.exe
          retention-days: 1
  compile-queries:
    if: github.repository_owner == 'github'
    runs-on: ubuntu-latest-xl
    steps:
      - uses: actions/checkout@v4
      - name: Fetch CodeQL
        uses: ./.github/actions/fetch-codeql
      - name: Cache compilation cache
        id: query-cache
        uses: ./.github/actions/cache-query-compilation
        with:
          key: ruby-build
      - name: Build Query Pack
        run: |
          PACKS=${{ runner.temp }}/query-packs
          rm -rf $PACKS
          codeql pack create ../misc/suite-helpers --output "$PACKS"
          codeql pack create ../shared/regex --output "$PACKS"
          codeql pack create ../shared/ssa --output "$PACKS"
          codeql pack create ../shared/tutorial --output "$PACKS"
          codeql pack create ql/lib --output "$PACKS"
          codeql pack create -j0 ql/src --output "$PACKS" --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
          PACK_FOLDER=$(readlink -f "$PACKS"/codeql/ruby-queries/*)
          codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
          (cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
      - uses: actions/upload-artifact@v3
        with:
          name: codeql-ruby-queries
          path: |
            ${{ runner.temp }}/query-packs/*
          retention-days: 1

  package:
    runs-on: ubuntu-latest
    needs: [build, compile-queries]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v3
        with:
          name: ruby.dbscheme
          path: ruby/ruby
      - uses: actions/download-artifact@v3
        with:
          name: extractor-ubuntu-latest
          path: ruby/linux64
      - uses: actions/download-artifact@v3
        with:
          name: extractor-windows-latest
          path: ruby/win64
      - uses: actions/download-artifact@v3
        with:
          name: extractor-macos-latest
          path: ruby/osx64
      - run: |
          mkdir -p ruby
          cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
          mkdir -p ruby/tools/{linux64,osx64,win64}
          cp linux64/codeql-extractor-ruby ruby/tools/linux64/extractor
          cp osx64/codeql-extractor-ruby ruby/tools/osx64/extractor
          cp win64/codeql-extractor-ruby.exe ruby/tools/win64/extractor.exe
          chmod +x ruby/tools/{linux64,osx64}/extractor
          zip -rq codeql-ruby.zip ruby
      - uses: actions/upload-artifact@v3
        with:
          name: codeql-ruby-pack
          path: ruby/codeql-ruby.zip
          retention-days: 1
      - uses: actions/download-artifact@v3
        with:
          name: codeql-ruby-queries
          path: ruby/qlpacks
      - run: |
          echo '{
            "provide": [
            "ruby/codeql-extractor.yml",
            "qlpacks/*/*/*/qlpack.yml"
            ]
          }' > .codeqlmanifest.json
          zip -rq codeql-ruby-bundle.zip .codeqlmanifest.json ruby qlpacks
      - uses: actions/upload-artifact@v3
        with:
          name: codeql-ruby-bundle
          path: ruby/codeql-ruby-bundle.zip
          retention-days: 1

  test:
    defaults:
      run:
        working-directory: ${{ github.workspace }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]

    runs-on: ${{ matrix.os }}
    needs: [package]
    steps:
      - uses: actions/checkout@v4
      - name: Fetch CodeQL
        uses: ./.github/actions/fetch-codeql

      - name: Download Ruby bundle
        uses: actions/download-artifact@v3
        with:
          name: codeql-ruby-bundle
          path: ${{ runner.temp }}
      - name: Unzip Ruby bundle
        shell: bash
        run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"

      - name: Run QL test
        shell: bash
        run: |
          codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
      - name: Create database
        shell: bash
        run: |
          codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
      - name: Analyze database
        shell: bash
        run: |
          codeql database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls
