Skip to content

feat(toolbarSelect): DRY up the toolbar <select>-change boilerplate #30

feat(toolbarSelect): DRY up the toolbar <select>-change boilerplate

feat(toolbarSelect): DRY up the toolbar <select>-change boilerplate #30

name: Etherpad Integration
# Boots a real Etherpad against this helper in a matrix of core versions
# and asserts the pad page renders cleanly. Catches interop regressions
# (for example the #7543 CJS/ESM settings.toolbar bug) *before* they
# propagate to every downstream plugin that depends on this helper.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
smoke:
runs-on: ubuntu-latest
strategy:
# Don't cancel sibling versions if one fails — we want the full
# compatibility picture in the check output, not just the first red.
fail-fast: false
matrix:
etherpad-ref:
# Oldest release we want to stay compatible with (v2.6.1 is the
# last tag as of 2026-04; ships the core CJS/ESM interop bug).
- v2.6.1
# Current development tip (has the #7421 compat shim).
- develop
name: smoke (etherpad@${{ matrix.etherpad-ref }})
steps:
- name: Check out this helper
uses: actions/checkout@v6
with:
path: plugin
- name: Check out Etherpad core (${{ matrix.etherpad-ref }})
uses: actions/checkout@v6
with:
repository: ether/etherpad
ref: ${{ matrix.etherpad-ref }}
path: etherpad
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10
run_install: false
- uses: actions/setup-node@v6
with:
node-version: 25
cache: pnpm
cache-dependency-path: etherpad/pnpm-lock.yaml
- name: Install Etherpad core dependencies
working-directory: ./etherpad
run: bin/installDeps.sh
- name: Install this helper into Etherpad
working-directory: ./etherpad
run: pnpm run plugins i --path ../../plugin
- name: Install a representative consumer (ep_font_color)
working-directory: ./etherpad
# Exercises the template({skip: () => settings.toolbar…}) path that
# hit #7543. Installing via the public registry against the helper
# we just linked verifies the resolver picks up our copy.
run: pnpm run plugins i ep_font_color
- name: Log which helper version Etherpad will load
working-directory: ./etherpad
# Informational only. Etherpad may resolve the helper either from the
# local path or transitively via a consumer plugin (whichever pnpm
# picks). The actual contract — "a crashing skip() does not take the
# pad down" — is verified by the later pad-load step, not by version
# equality.
run: |
pkg_path=$(find src/plugin_packages -maxdepth 5 -type f -name package.json \
-path '*ep_plugin_helpers*' 2>/dev/null | head -1)
if [ -z "$pkg_path" ]; then
echo "::warning::could not locate ep_plugin_helpers under src/plugin_packages/; listing tree"
find src/plugin_packages -maxdepth 4 -type d | head -30
else
echo "helper installed at: $pkg_path"
node -p "'helper version: ' + require('$(pwd)/$pkg_path').version"
fi
- name: Start Etherpad in the background
working-directory: ./etherpad
run: |
nohup pnpm run dev > /tmp/etherpad.log 2>&1 &
echo $! > /tmp/etherpad.pid
- name: Wait for Etherpad to listen on :9001
run: |
for i in $(seq 1 60); do
if curl -fsS http://localhost:9001/ -o /dev/null; then
echo "Etherpad up after ${i}s"
exit 0
fi
sleep 1
done
echo "::error::Etherpad did not start within 60s"
tail -200 /tmp/etherpad.log
exit 1
- name: Load a pad and assert HTTP 200
# The #7543 symptom was a 500 from eejs rendering the pad template.
# A 200 on /p/<id> is the simplest proof that the helper's template
# pipeline didn't throw when a consumer plugin is installed.
run: |
status=$(curl -s -o /tmp/pad.html -w '%{http_code}' http://localhost:9001/p/ci-smoke-$(date +%s))
echo "pad status: $status"
if [ "$status" != "200" ]; then
echo "::error::Pad load returned $status (expected 200)"
echo "--- pad body ---"
cat /tmp/pad.html
echo "--- server log (last 200 lines) ---"
tail -200 /tmp/etherpad.log
exit 1
fi
- name: Scan server log for uncaught template errors
# On the buggy path Etherpad emits the crash at [ERROR] level from the
# `settings` or `http` logger:
# [ERROR] settings - TypeError: ... Cannot read properties of
# undefined (reading 'indexOf') at Object.skip (ep_font_color)
# The helper's swallowing WARN line looks the same textually but lives
# at [WARN] level, so anchor the pattern to [ERROR] to avoid matching
# our own intentional log entry.
run: |
if grep -E "\[ERROR\].*(Cannot read properties of undefined.*indexOf|TypeError.*eejs)" \
/tmp/etherpad.log; then
echo "::error::Detected the #7543 crash pattern in server log"
tail -200 /tmp/etherpad.log
exit 1
fi
echo "no [ERROR]-level template crash pattern in log"
- name: Upload server log on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: etherpad-log-${{ matrix.etherpad-ref }}
path: /tmp/etherpad.log
retention-days: 7
- name: Stop Etherpad
if: always()
run: |
if [ -f /tmp/etherpad.pid ]; then
kill "$(cat /tmp/etherpad.pid)" 2>/dev/null || true
fi