name: e2e-test

env:
  AWS_ACCESS_KEY_ID: ${{ vars.LPD_PERF_AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.LPD_PERF_AWS_SECRET_ACCESS_KEY }}
  AWS_BUCKET: ${{ vars.LPD_PERF_AWS_BUCKET }}
  AWS_REGION: ${{ vars.LPD_PERF_AWS_REGION }}
  LIGHTPANDA_DISABLE_TELEMETRY: true

on:
  push:
    branches:
      - main
    paths:
      - "build.zig"
      - "src/**/*.zig"
      - "src/*.zig"
      - "vendor/zig-js-runtime"
      - ".github/**"
      - "vendor/**"
  pull_request:

    # By default GH trigger on types opened, synchronize and reopened.
    # see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
    # Since we skip the job when the PR is in draft state, we want to force CI
    # running when the PR is marked ready_for_review w/o other change.
    # see https://github.com/orgs/community/discussions/25722#discussioncomment-3248917
    types: [opened, synchronize, reopened, ready_for_review]

    paths:
      - ".github/**"
      - "build.zig"
      - "src/**/*.zig"
      - "src/*.zig"
      - "vendor/**"
      - ".github/**"
      - "vendor/**"
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  zig-build-release:
    name: zig build release

    runs-on: ubuntu-latest
    timeout-minutes: 15

    # Don't run the CI with draft PR.
    if: github.event.pull_request.draft == false

    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
          # fetch submodules recusively, to get zig-js-runtime submodules also.
          submodules: recursive

      - uses: ./.github/actions/install

      - name: zig build release
        run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64 -Dgit_commit=$(git rev-parse --short ${{ github.sha }})

      - name: upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: lightpanda-build-release
          path: |
            zig-out/bin/lightpanda
          retention-days: 1

  demo-scripts:
    name: demo-scripts
    needs: zig-build-release

    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6
        with:
          repository: 'lightpanda-io/demo'
          fetch-depth: 0

      - run: npm install

      - name: download artifact
        uses: actions/download-artifact@v8
        with:
          name: lightpanda-build-release

      - run: chmod a+x ./lightpanda

      - name: run end to end tests
        run: |
          ./lightpanda serve & echo $! > LPD.pid
          go run runner/main.go
          kill `cat LPD.pid`

      - name: build proxy
        run: |
          cd proxy
          go build

      - name: run end to end tests through proxy
        run: |
          ./proxy/proxy & echo $! > PROXY.id
          ./lightpanda serve --http_proxy 'http://127.0.0.1:3000' & echo $! > LPD.pid
          go run runner/main.go
          kill `cat LPD.pid` `cat PROXY.id`

      - name: run request interception through proxy
        run: |
          export PROXY_USERNAME=username PROXY_PASSWORD=password
          ./proxy/proxy & echo $! > PROXY.id
          ./lightpanda serve & echo $! > LPD.pid
          URL=https://demo-browser.lightpanda.io/campfire-commerce/ node puppeteer/proxy_auth.js
          BASE_URL=https://demo-browser.lightpanda.io/ node playwright/proxy_auth.js
          kill `cat LPD.pid` `cat PROXY.id`

  # e2e tests w/ web-bot-auth configuration on.
  wba-demo-scripts:
    name: wba-demo-scripts
    needs: zig-build-release

    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6
        with:
          repository: 'lightpanda-io/demo'
          fetch-depth: 0

      - run: npm install

      - name: download artifact
        uses: actions/download-artifact@v8
        with:
          name: lightpanda-build-release

      - run: chmod a+x ./lightpanda

      - run: echo "${{ secrets.WBA_PRIVATE_KEY_PEM }}" > private_key.pem

      - name: run end to end tests
        run: |
          ./lightpanda serve \
            --web_bot_auth_key_file private_key.pem \
            --web_bot_auth_keyid ${{ vars.WBA_KEY_ID }} \
            --web_bot_auth_domain ${{ vars.WBA_DOMAIN }} \
            & echo $! > LPD.pid
          go run runner/main.go
          kill `cat LPD.pid`

      - name: build proxy
        run: |
          cd proxy
          go build

      - name: run end to end tests through proxy
        run: |
          ./proxy/proxy & echo $! > PROXY.id
          ./lightpanda serve \
            --web_bot_auth_key_file private_key.pem \
            --web_bot_auth_keyid ${{ vars.WBA_KEY_ID }} \
            --web_bot_auth_domain ${{ vars.WBA_DOMAIN }} \
            --http_proxy 'http://127.0.0.1:3000' \
            & echo $! > LPD.pid
          go run runner/main.go
          kill `cat LPD.pid` `cat PROXY.id`

      - name: run request interception through proxy
        run: |
          export PROXY_USERNAME=username PROXY_PASSWORD=password
          ./proxy/proxy & echo $! > PROXY.id
          ./lightpanda serve & echo $! > LPD.pid
          URL=https://demo-browser.lightpanda.io/campfire-commerce/ node puppeteer/proxy_auth.js
          BASE_URL=https://demo-browser.lightpanda.io/ node playwright/proxy_auth.js
          kill `cat LPD.pid` `cat PROXY.id`

  wba-test:
    name: wba-test
    needs: zig-build-release

    env:
      LIGHTPANDA_DISABLE_TELEMETRY: true

    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - uses: actions/checkout@v6
        with:
          repository: 'lightpanda-io/demo'
          fetch-depth: 0

      - name: download artifact
        uses: actions/download-artifact@v8
        with:
          name: lightpanda-build-release

      - run: chmod a+x ./lightpanda

      - name: run wba test
        shell: bash
        run: |

          node webbotauth/validator.js &
          VALIDATOR_PID=$!
          sleep 2

          exec 3<<< "${{ secrets.WBA_PRIVATE_KEY_PEM }}"

          ./lightpanda fetch --dump http://127.0.0.1:8989/ \
            --web_bot_auth_key_file /proc/self/fd/3 \
            --web_bot_auth_keyid ${{ vars.WBA_KEY_ID }} \
            --web_bot_auth_domain ${{ vars.WBA_DOMAIN }}

          wait $VALIDATOR_PID
          exec 3>&-

  cdp-and-hyperfine-bench:
    name: cdp-and-hyperfine-bench
    needs: zig-build-release

    env:
      MAX_VmHWM: 28000 # 28MB (KB)
      MAX_CG_PEAK: 8000 # 8MB (KB)
      MAX_AVG_DURATION: 17
      LIGHTPANDA_DISABLE_TELEMETRY: true

      # How to give cgroups access to the user actions-runner on the host:
      # $ sudo apt install cgroup-tools
      # $ sudo chmod o+w /sys/fs/cgroup/cgroup.procs
      # $ sudo mkdir -p /sys/fs/cgroup/actions-runner
      # $ sudo chown -R actions-runner:actions-runner /sys/fs/cgroup/actions-runner
      CG_ROOT: /sys/fs/cgroup
      CG: actions-runner/lpd_${{ github.run_id }}_${{ github.run_attempt }}

    # use a self host runner.
    runs-on: lpd-bench-hetzner
    timeout-minutes: 15

    steps:
      - uses: actions/checkout@v6
        with:
          repository: 'lightpanda-io/demo'
          fetch-depth: 0

      - run: npm install

      - name: download artifact
        uses: actions/download-artifact@v8
        with:
          name: lightpanda-build-release

      - run: chmod a+x ./lightpanda

      - name: start http
        run: |
          go run ws/main.go & echo $! > WS.pid
          sleep 2

      - name: run lightpanda in cgroup
        run: |
          if [ ! -f /sys/fs/cgroup/cgroup.controllers ]; then
            echo "cgroup v2 not available: /sys/fs/cgroup/cgroup.controllers missing"
            exit 1
          fi

          mkdir -p $CG_ROOT/$CG
          cgexec -g memory:$CG ./lightpanda serve & echo $! > LPD.pid

          sleep 2

      - name: run puppeteer
        run: |
          RUNS=100 npm run bench-puppeteer-cdp > puppeteer.out || exit 1
          cat /proc/`cat LPD.pid`/status |grep VmHWM|grep -oP '\d+' > LPD.VmHWM
          kill `cat LPD.pid`

          PID=$(cat LPD.pid)
          while kill -0 $PID 2>/dev/null; do
            sleep 1
          done
          if [ ! -f $CG_ROOT/$CG/memory.peak ]; then
            echo "memory.peak not available in $CG"
            exit 1
          fi
          cat $CG_ROOT/$CG/memory.peak > LPD.cg_mem_peak

      - name: puppeteer result
        run: cat puppeteer.out

      - name: cgroup memory regression
        run: |
          PEAK_BYTES=$(cat LPD.cg_mem_peak)
          PEAK_KB=$((PEAK_BYTES / 1024))
          echo "memory.peak_bytes=$PEAK_BYTES"
          echo "memory.peak_kb=$PEAK_KB"
          test "$PEAK_KB" -le "$MAX_CG_PEAK"

      - name: virtual memory regression
        run: |
          export LPD_VmHWM=`cat LPD.VmHWM`
          echo "Peak resident set size: $LPD_VmHWM"
          test "$LPD_VmHWM" -le "$MAX_VmHWM"

      - name: cleanup cgroup
        run: rmdir $CG_ROOT/$CG

      - name: duration regression
        run: |
          export PUPPETEER_AVG_DURATION=`cat puppeteer.out|grep 'avg run'|sed 's/avg run duration (ms) //'`
          echo "puppeteer avg duration: $PUPPETEER_AVG_DURATION"
          test "$PUPPETEER_AVG_DURATION" -le "$MAX_AVG_DURATION"

      - name: json output
        run: |
          export AVG_DURATION=`cat puppeteer.out|grep 'avg run'|sed 's/avg run duration (ms) //'`
          export TOTAL_DURATION=`cat puppeteer.out|grep 'total duration'|sed 's/total duration (ms) //'`
          export LPD_VmHWM=`cat LPD.VmHWM`
          export LPD_CG_PEAK_KB=$(( $(cat LPD.cg_mem_peak) / 1024 ))
          echo "{\"duration_total\":${TOTAL_DURATION},\"duration_avg\":${AVG_DURATION},\"mem_peak\":${LPD_VmHWM},\"cg_mem_peak\":${LPD_CG_PEAK_KB}}" > bench.json
          cat bench.json

      - name: run hyperfine
        run: |
          hyperfine --export-json=hyperfine.json --warmup 3 --runs 20 --shell=none "./lightpanda --dump http://127.0.0.1:1234/campfire-commerce/"

      - name: stop http
        run: kill `cat WS.pid`

      - name: write commit
        run: |
          echo "${{github.sha}}" > commit.txt

      - name: upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: bench-results
          path: |
            bench.json
            hyperfine.json
            commit.txt
          retention-days: 10


  perf-fmt:
    name: perf-fmt
    needs: cdp-and-hyperfine-bench

    # Don't execute on PR
    if: github.event_name != 'pull_request'

    runs-on: ubuntu-latest
    timeout-minutes: 15

    container:
      image: ghcr.io/lightpanda-io/perf-fmt:latest
      credentials:
       username: ${{ github.actor }}
       password: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - name: download artifact
        uses: actions/download-artifact@v8
        with:
          name: bench-results

      - name: format and send json result
        run: /perf-fmt cdp ${{ github.sha }} bench.json

      - name: format and send json result
        run: /perf-fmt hyperfine ${{ github.sha }} hyperfine.json

  browser-fetch:
    name: browser fetch
    needs: zig-build-release

    runs-on: ubuntu-latest

    steps:
      - name: download artifact
        uses: actions/download-artifact@v8
        with:
          name: lightpanda-build-release

      - run: chmod a+x ./lightpanda

      - run: ./lightpanda fetch https://demo-browser.lightpanda.io/campfire-commerce/
