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: - ".github/**" - "src/**" - "build.zig" - "build.zig.zon" 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/**" - "src/**" - "build.zig" - "build.zig.zon" # 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: 20 # Don't run the CI with draft PR. if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: ./.github/actions/install - uses: ./.github/actions/v8-snapshot - name: zig build release run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64 - name: upload artifact uses: actions/upload-artifact@v7 with: name: lightpanda-build-release path: | zig-out/bin/lightpanda retention-days: 1 demo-runner: strategy: fail-fast: false matrix: proxy: [true, false] wba: [true, false] robotstxt: [true, false] name: demo-runner 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 lightpanda release uses: actions/download-artifact@v8 with: name: lightpanda-build-release - run: chmod a+x ./lightpanda - if: matrix.proxy == true name: build and start proxy run: | cd proxy go build ./proxy & - if: matrix.wba == true run: echo "${{ secrets.WBA_PRIVATE_KEY_PEM }}" > private_key.pem # Always activate cache - run: mkdir -p /tmp/lp-cache - id: args name: build LP args run: | args="--http-cache-dir /tmp/lp-cache" [ "${{ matrix.proxy }}" = "true" ] && args="$args --http-proxy http://127.0.0.1:3000" [ "${{ matrix.robotstxt }}" = "true" ] && args="$args --obey-robots" [ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-key-file private_key.pem" [ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-domain ${{ vars.WBA_DOMAIN }}" [ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-keyid ${{ vars.WBA_KEY_ID }}" echo $args echo "value=$args" >> "$GITHUB_OUTPUT" - run: | ./lightpanda serve --port 9222 ${{ steps.args.outputs.value }} & - run: | go run runner/main.go proxy-auth: strategy: fail-fast: false matrix: wba: [true, false] robotstxt: [true, false] name: proxy-auth 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 lightpanda release uses: actions/download-artifact@v8 with: name: lightpanda-build-release - run: chmod a+x ./lightpanda - name: build and start proxy run: | cd proxy go build ./proxy & - if: matrix.wba == true run: echo "${{ secrets.WBA_PRIVATE_KEY_PEM }}" > private_key.pem # Always activate cache - run: mkdir -p /tmp/lp-cache - id: args name: build LP args run: | args="--http-cache-dir /tmp/lp-cache" [ "${{ matrix.robotstxt }}" = "true" ] && args="$args --obey-robots" [ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-key-file private_key.pem" [ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-domain ${{ vars.WBA_DOMAIN }}" [ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-keyid ${{ vars.WBA_KEY_ID }}" echo $args echo "value=$args" >> "$GITHUB_OUTPUT" - name: run end to end tests through proxy run: | export PROXY_USERNAME=username PROXY_PASSWORD=password ./lightpanda serve --port 9222 --http-proxy http://127.0.0.1:3000 ${{ steps.args.outputs.value }} & echo $! > LPD.pid go run runner/main.go URL=https://demo-browser.lightpanda.io/campfire-commerce/ node puppeteer/proxy_auth.js kill `cat LPD.pid` while kill -0 `cat LPD.pid` 2>/dev/null; do sleep 1; done - name: run request interception through proxy and playwright run: | export PROXY_USERNAME=username PROXY_PASSWORD=password ./lightpanda serve --port 9222 ${{ steps.args.outputs.value }} & echo $! > LPD.pid BASE_URL=https://demo-browser.lightpanda.io/ node playwright/proxy_auth.js kill `cat LPD.pid` while kill -0 `cat LPD.pid` 2>/dev/null; do sleep 1; done wba-test: name: wba-test needs: zig-build-release runs-on: ubuntu-latest timeout-minutes: 5 # Don't execute on PR if: github.event_name != 'pull_request' 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 # force a wakup of the auth server before requesting it w/ the test itself - run: curl https://${{ vars.WBA_DOMAIN }} - name: run wba test shell: bash run: | node webbotauth/validator.js & VALIDATOR_PID=$! sleep 5 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 # 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 runner/main.go -serve & 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/ mcp-smoke: name: mcp smoke needs: zig-build-release runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: download artifact uses: actions/download-artifact@v8 with: name: lightpanda-build-release - run: chmod a+x ./lightpanda - name: jsonrpc round trip over stdio run: | set -euo pipefail jq --version timeout 30 ./lightpanda mcp > mcp.out <<'JSONRPC' {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci","version":"1.0.0"}}} {"jsonrpc":"2.0","method":"notifications/initialized"} {"jsonrpc":"2.0","id":2,"method":"tools/list"} JSONRPC cat mcp.out jq -ec 'select(.id == 1) | .result.protocolVersion == "2024-11-05"' mcp.out > /dev/null jq -ec 'select(.id == 2) | .result.tools | type == "array" and length > 0' mcp.out > /dev/null