Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/actions/cache/restore/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Restore cache

description: Restore a cache from GitHub storage or the self-hosted internal cache service

inputs:
key:
description: An explicit key for restoring the cache
required: true
path:
description: Files, directories, and wildcard patterns to restore
required: true
restore-keys:
description: Ordered prefixes used to restore stale caches
required: false

outputs:
cache-hit:
description: Whether the primary cache key was restored
value: ${{ steps.github-cache.outputs.cache-hit == 'true' || steps.internal-cache.outputs.cache-hit == 'true' }}

runs:
using: composite
steps:
- name: Restore cache from GitHub storage
id: github-cache
if: ${{ runner.environment == 'github-hosted' }}
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6
with:
key: ${{ inputs.key }}
path: ${{ inputs.path }}
restore-keys: ${{ inputs.restore-keys }}

- name: Restore cache from internal storage
id: internal-cache
if: ${{ runner.environment == 'self-hosted' }}
uses: lynx-infra/cache/restore@d6bd70bf2182b228ff5cfa9539e5f61b0929a1ba # main
with:
key: ${{ inputs.key }}
path: ${{ inputs.path }}
restore-keys: ${{ inputs.restore-keys }}
28 changes: 28 additions & 0 deletions .github/actions/cache/save/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Save cache

description: Save a cache to GitHub storage or the self-hosted internal cache service

inputs:
key:
description: An explicit key for saving the cache
required: true
path:
description: Files, directories, and wildcard patterns to save
required: true

runs:
using: composite
steps:
- name: Save cache to GitHub storage
if: ${{ runner.environment == 'github-hosted' }}
uses: actions/cache/save@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6
with:
key: ${{ inputs.key }}
path: ${{ inputs.path }}

- name: Save cache to internal storage
if: ${{ runner.environment == 'self-hosted' }}
uses: lynx-infra/cache/save@d6bd70bf2182b228ff5cfa9539e5f61b0929a1ba # main
with:
key: ${{ inputs.key }}
path: ${{ inputs.path }}
27 changes: 27 additions & 0 deletions .github/actions/pnpm-cache/restore/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Restore pnpm store

description: Restore the pnpm content-addressable store from the runner-appropriate backend

outputs:
cache-hit:
description: Whether the primary pnpm cache key was restored
value: ${{ steps.cache.outputs.cache-hit }}

runs:
using: composite
steps:
- name: Resolve pnpm store
id: pnpm-store
shell: bash
run: |
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
echo "version=$(pnpm --version)" >> "$GITHUB_OUTPUT"

- name: Restore pnpm store
id: cache
uses: ./.github/actions/cache/restore
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: midscene-pnpm-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.pnpm-store.outputs.version }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
midscene-pnpm-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.pnpm-store.outputs.version }}-
19 changes: 19 additions & 0 deletions .github/actions/pnpm-cache/save/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Save pnpm store

description: Save the pnpm content-addressable store to the runner-appropriate backend

runs:
using: composite
steps:
- name: Resolve pnpm store
id: pnpm-store
shell: bash
run: |
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
echo "version=$(pnpm --version)" >> "$GITHUB_OUTPUT"

- name: Save pnpm store
uses: ./.github/actions/cache/save
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: midscene-pnpm-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.pnpm-store.outputs.version }}-${{ hashFiles('pnpm-lock.yaml') }}
80 changes: 80 additions & 0 deletions .github/actions/setup-headless-browser/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Setup headless browser

description: Install missing Linux desktop libraries and restore a Puppeteer Chrome binary

inputs:
package-directory:
description: Workspace package that provides the Puppeteer CLI
required: false
default: packages/web-integration

outputs:
browser-path:
description: Absolute path to the Chrome for Testing executable
value: ${{ steps.browser.outputs.browser-path }}
browser-cache-hit:
description: Whether the Puppeteer browser cache was restored
value: ${{ steps.browser.outputs.browser-cache-hit }}

runs:
using: composite
steps:
- name: Install missing system dependencies
shell: bash
run: |
set -euo pipefail

packages=(
xvfb
x11-xserver-utils
imagemagick
fonts-liberation
libasound2
libatk-bridge2.0-0
libdrm2
libgbm1
libgtk-3-0
libnss3
libpango-1.0-0
libvulkan1
libx11-6
libx11-xcb1
libxcomposite1
libxdamage1
libxfixes3
libxinerama1
libxkbcommon-x11-0
libxkbcommon0
libxrandr2
libxshmfence1
libxss1
libxtst6
xdg-utils
)
missing=()

for package in "${packages[@]}"; do
status="$(dpkg-query -W -f='${Status}' "$package" 2>/dev/null || true)"
if [ "$status" != "install ok installed" ]; then
missing+=("$package")
fi
done

if [ "${#missing[@]}" -eq 0 ]; then
echo "All headless browser system dependencies are preinstalled"
exit 0
fi

echo "Installing missing packages: ${missing[*]}"
sudo apt-get update
sudo env DEBIAN_FRONTEND=noninteractive apt-get install \
--yes \
--no-install-recommends \
"${missing[@]}"

- name: Setup Puppeteer browser
id: browser
uses: ./.github/actions/setup-puppeteer-browser
with:
package-directory: ${{ inputs.package-directory }}
expose-as-chromium: "true"
57 changes: 57 additions & 0 deletions .github/actions/setup-node-pnpm/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Setup Node.js and pnpm

description: Restore the Node.js toolcache on self-hosted runners, then set up Node.js and pnpm

inputs:
node-version:
description: Node.js version to install
required: false
default: 24.13.0
pnpm-version:
description: pnpm version to install
required: false
default: 9.3.0

outputs:
node-cache-hit:
description: Whether the self-hosted Node.js toolcache was restored
value: ${{ steps.node-cache.outputs.cache-hit }}

runs:
using: composite
steps:
- name: Resolve Node.js toolcache
id: node-toolcache
if: ${{ runner.environment == 'self-hosted' }}
shell: bash
run: |
if [ -z "${RUNNER_TOOL_CACHE:-}" ]; then
echo "::error::RUNNER_TOOL_CACHE is not configured"
exit 1
fi
echo "path=$RUNNER_TOOL_CACHE/node/${{ inputs.node-version }}" >> "$GITHUB_OUTPUT"

- name: Restore Node.js toolcache
id: node-cache
if: ${{ runner.environment == 'self-hosted' }}
uses: ./.github/actions/cache/restore
with:
path: ${{ steps.node-toolcache.outputs.path }}
key: midscene-node-toolcache-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ inputs.node-version }}

- name: Save Node.js toolcache
if: ${{ runner.environment == 'self-hosted' && steps.node-cache.outputs.cache-hit != 'true' }}
uses: ./.github/actions/cache/save
with:
path: ${{ steps.node-toolcache.outputs.path }}
key: midscene-node-toolcache-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}

- name: Setup pnpm
uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2.4.1
with:
version: ${{ inputs.pnpm-version }}
71 changes: 71 additions & 0 deletions .github/actions/setup-puppeteer-browser/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Setup Puppeteer browser

description: Restore and install the Puppeteer Chrome for Testing binary

inputs:
package-directory:
description: Workspace package that provides the Puppeteer CLI
required: false
default: packages/web-integration
expose-as-chromium:
description: Add a chromium alias for tests that discover browsers from PATH
required: false
default: "false"

outputs:
browser-path:
description: Absolute path to the Chrome for Testing executable
value: ${{ steps.browser.outputs.path }}
browser-cache-hit:
description: Whether the Puppeteer browser cache was restored
value: ${{ steps.browser-cache.outputs.cache-hit }}

runs:
using: composite
steps:
- name: Restore Puppeteer browser
id: browser-cache
uses: ./.github/actions/cache/restore
with:
path: ~/.cache/puppeteer
key: midscene-puppeteer-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
midscene-puppeteer-v1-${{ runner.os }}-${{ runner.arch }}-

- name: Install Chrome for Testing
shell: bash
run: pnpm --dir "${{ inputs.package-directory }}" exec puppeteer browsers install chrome

- name: Save Puppeteer browser
if: ${{ steps.browser-cache.outputs.cache-hit != 'true' }}
uses: ./.github/actions/cache/save
with:
path: ~/.cache/puppeteer
key: midscene-puppeteer-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}

- name: Resolve Chrome for Testing
id: browser
shell: bash
run: |
set -euo pipefail

browser_path="$(
pnpm --dir "${{ inputs.package-directory }}" exec node -e \
'process.stdout.write(require("puppeteer").executablePath())'
)"
if [ ! -x "$browser_path" ]; then
echo "::error::Puppeteer resolved a missing or non-executable browser: $browser_path"
exit 1
fi

echo "PUPPETEER_EXECUTABLE_PATH=$browser_path" >> "$GITHUB_ENV"

if [ "${{ inputs.expose-as-chromium }}" = "true" ]; then
bin_dir="$RUNNER_TEMP/midscene-browser-bin"
mkdir -p "$bin_dir"
ln -sfn "$browser_path" "$bin_dir/chromium"
echo "$bin_dir" >> "$GITHUB_PATH"
fi

echo "path=$browser_path" >> "$GITHUB_OUTPUT"
"$browser_path" --version
Loading
Loading