Skip to content

ci: fix Playwright demo DBus env + make act artifact debug manual #170

ci: fix Playwright demo DBus env + make act artifact debug manual

ci: fix Playwright demo DBus env + make act artifact debug manual #170

Workflow file for this run

name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: {}
# CI trigger: 2026-01-20
jobs:
playwright-demo:
name: Playwright Demo Test
if: github.actor == 'nektos/act' || github.event_name != 'push' || (github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message || '', '[skip ci]'))
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.57.0-jammy
steps:
- name: Checkout repository (CI only)
if: github.actor != 'nektos/act'
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use act-provided workspace (act only)
if: github.actor == 'nektos/act'
run: |
# Under act we want to use local sources, and avoid actions/checkout which would fetch from
# GitHub and lose local changes.
#
# Some act configurations can produce an empty workspace volume. When that happens, we seed
# the workspace volume from a read-only bind mount at /repo (provided via .actrc).
echo "GITHUB_WORKSPACE=$GITHUB_WORKSPACE"
echo "pwd=$(pwd)"
if [ ! -f "$GITHUB_WORKSPACE/package.json" ]; then
echo "Workspace volume appears empty (no package.json)."
if [ -f "/repo/package.json" ]; then
echo "Seeding workspace from /repo (read-only bind mount)."
# Copy everything we need, but avoid copying host-installed node_modules into the
# container workspace (it would be huge and contain the wrong platform binaries).
tar -C /repo \
--exclude='./node_modules' \
--exclude='./.act-cache' \
--exclude='./.act-artifacts' \
-cf - . \
| tar -C "$GITHUB_WORKSPACE" -xf -
else
echo "::error::act workspace is empty and /repo is not mounted. Ensure .actrc includes: --container-options \"-v .:/repo:ro\""
exit 1
fi
fi
echo "--- ls (GITHUB_WORKSPACE) ---"
ls -la "$GITHUB_WORKSPACE" || true
echo "Using act local sources; skipping actions/checkout."
- name: Mark workspace as safe for git
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config --global --add safe.directory "$GITHUB_WORKSPACE/external/vscode-test-playwright"
- name: Init required submodules (CI only)
if: github.actor != 'nektos/act'
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Verify required submodules are present (act only)
if: github.actor == 'nektos/act'
run: |
test -f external/vscode-test-playwright/index.ts
test -f external/vscode-test-playwright/tsconfig.json
# Local act runs also want an MP4 output for quick sharing/debugging.
# The existing `demo:pw:videos` script converts the recorded .webm to docs/pw-videos/demo.mp4.
- name: Init demo video submodule (act only)
if: github.actor == 'nektos/act'
run: |
git submodule update --init --depth 1 external/playwright-test-videos
# NOTE: Under act (Docker Desktop), actions/setup-node's caching can hang or behave
# unexpectedly because it relies on GitHub cache backends.
- name: Setup Node.js (CI)
if: github.actor != 'nektos/act'
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: npm
- name: Setup Node.js (act)
if: github.actor == 'nektos/act'
timeout-minutes: 5
uses: actions/setup-node@v4
with:
node-version: 22.x
- name: Install dependencies
run: npm ci
- name: Build extension bundle
run: npm run build:dist
- name: Install system dependencies for Playwright demo
run: |
apt-get update
apt-get install -y \
dbus \
dbus-x11 \
xvfb
- name: Run Playwright demo test
shell: bash
env:
# Matches the demo test timeout and gives the VS Code test server more
# time to emit its readiness line on slower/colder CI hosts.
PW_VSCODE_WAIT_FOR_LINE_TIMEOUT_MS: '15000'
# The extension host may take significantly longer to start inside act/Docker Desktop.
# This controls how long the harness waits for the extension host to connect back.
PW_VSCODE_TEST_WS_CONNECT_TIMEOUT_MS: ${{ github.actor == 'nektos/act' && '180000' || '30000' }}
# Under act (Docker Desktop mounts / containerized Electron), video finalization can be slow.
# This controls how long vscode-test-playwright waits for the recorded .webm to appear.
PW_VSCODE_VIDEO_FINALIZE_TIMEOUT_MS: ${{ github.actor == 'nektos/act' && '180000' || '30000' }}
# Extra diagnostics for local act runs only.
PW_VSCODE_DEBUG: ${{ github.actor == 'nektos/act' && '1' || '0' }}
run: |
set -euo pipefail
# VS Code/Electron may try to talk to the DBus system bus. GitHub-hosted runners have
# one by default, but act containers often don't. If the system bus socket is missing,
# start a minimal system bus so Electron doesn't exit early.
printenv
echo "DBUS_SESSION_BUS_ADDRESS: ${DBUS_SESSION_BUS_ADDRESS:-<unset>}"
if [ ! -S /run/dbus/system_bus_socket ]; then
mkdir -p /run/dbus
echo "Starting minimal system bus..."
dbus-daemon --system --fork --nopidfile
else
echo "System bus already running."
fi
# VS Code/Electron expects a DBus session bus. In containerized runs (e.g. act) the
# inherited DBUS_SESSION_BUS_ADDRESS can be invalid (e.g. macOS 'launchd:'), causing
# VS Code to exit before emitting the test server readiness line.
xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' npm run demo:pw
# When running locally under act, also convert the recorded .webm to an .mp4
# so the developer has a single shareable artifact.
#
# Note: If the Playwright demo fails, this step will already have failed (as intended),
# and the act-only upload step (with if: always()) will still export trace/logs/videos.
if [ "${GITHUB_ACTOR:-}" = "nektos/act" ]; then
echo "Video files under test-results (if any):"
find test-results -type f \( -name '*.webm' -o -name '*.mp4' \) -maxdepth 6 -print 2>/dev/null || true
npm run demo:pw:videos
fi
- name: Upload Playwright video artifacts (act only)
if: always() && github.actor == 'nektos/act'
timeout-minutes: 10
run: |
set -euo pipefail
# act runs the artifact server on the *host* and advertises an
# unreachable URL (127.0.0.1) to containers on Docker Desktop.
# Run upload-artifact's Node entrypoint ourselves and override the URLs.
rm -rf /tmp/actions-upload-artifact
git clone --depth 1 --branch v4 https://github.com/actions/upload-artifact /tmp/actions-upload-artifact
ACTIONS_RUNTIME_URL='http://host.docker.internal:34567/' \
ACTIONS_RESULTS_URL='http://host.docker.internal:34567/' \
node -e "
process.env['INPUT_NAME'] = 'playwright-demo-videos';
process.env['INPUT_IF-NO-FILES-FOUND'] = 'warn';
process.env['INPUT_RETENTION-DAYS'] = '3';
process.env['INPUT_OVERWRITE'] = 'false';
process.env['INPUT_INCLUDE-HIDDEN-FILES'] = 'false';
process.env['INPUT_PATH'] = [
'docs/pw-videos/demo.mp4',
'test-results/**/videos/**/*.webm',
'test-results/**/trace.zip',
'test-results/**/vscode-logs/**'
].join('\n');
require('/tmp/actions-upload-artifact/dist/upload/index.js');
"
release-notes:
name: Release Notes
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message || '', '[skip ci]') && github.actor != 'nektos/act'
runs-on: ubuntu-latest
needs: [playwright-demo]
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Determine next version
id: version
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({ owner: context.repo.owner, repo: context.repo.repo }).catch(()=>null);
let nextVersion;
let prevTagRaw = '';
if (latestRelease) {
prevTagRaw = latestRelease.data.tag_name || '';
const prev = prevTagRaw.replace(/^v/, '');
const parts = prev.split('.');
if (parts.length !== 3 || parts.some(p => isNaN(Number(p)))) {
core.setFailed(`Latest release tag '${prevTagRaw}' is not semver x.y.z`);
return;
}
parts[2] = String(Number(parts[2]) + 1);
nextVersion = parts.join('.');
} else {
nextVersion = '0.0.1';
}
core.info(`Computed next version: ${nextVersion}`);
core.setOutput('version', nextVersion);
core.setOutput('previous_tag', prevTagRaw);
- name: Generate Context
id: context
run: |
PREV_TAG="${{ steps.version.outputs.previous_tag }}"
if [ -n "$PREV_TAG" ]; then
RANGE="$PREV_TAG..HEAD"
echo "Generating context for range: $RANGE"
else
RANGE=""
echo "Generating context for initial release (all history)"
fi
{
echo 'commits<<EOF'
git log --pretty=format:"- %s (%an) [%h]" $RANGE
echo ''
echo 'EOF'
} >> $GITHUB_OUTPUT
{
echo 'stats<<EOF'
if [ -n "$RANGE" ]; then
git diff --stat $RANGE
else
git show --stat
fi
echo 'EOF'
} >> $GITHUB_OUTPUT
- name: Generate Release Notes
run: npm run release:notes
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
VERSION: ${{ steps.version.outputs.version }}
PREV_TAG: ${{ steps.version.outputs.previous_tag }}
COMMITS: ${{ steps.context.outputs.commits }}
STATS: ${{ steps.context.outputs.stats }}
- name: Verify RELEASE_NOTES.md
run: |
test -s RELEASE_NOTES.md
echo "--- RELEASE_NOTES.md (first 40 lines) ---"
head -n 40 RELEASE_NOTES.md
- name: Upload release notes artifact
uses: actions/upload-artifact@v4
with:
name: release-notes
path: |
RELEASE_NOTES.md
build-and-test:
name: Build and Test
runs-on: ${{ matrix.os }}
needs: [playwright-demo]
strategy:
matrix:
os: [windows-latest, macos-latest]
node-version: [20.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Compile TypeScript
run: npm run compile
- name: Run tests
shell: bash
run: |
npm test
env:
CI: true
build-and-test-linux:
name: Build and Test (Linux)
runs-on: ubuntu-latest
needs: [playwright-demo]
container:
image: mcr.microsoft.com/playwright:v1.57.0-jammy
strategy:
matrix:
node-version: [20.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Mark workspace as safe for git
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config --global --add safe.directory "$GITHUB_WORKSPACE/external/vscode-test-playwright"
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Compile TypeScript
run: npm run compile
- name: Ensure virtual display tools
run: |
apt-get update
apt-get install -y xvfb
- name: Run tests (Xvfb)
shell: bash
run: |
xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' npm test
env:
CI: true
auto-release:
name: Auto Release
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message || '', '[skip ci]') && github.actor != 'nektos/act'
runs-on: ubuntu-latest
needs: [build-and-test, build-and-test-linux, playwright-demo, release-notes]
container:
image: mcr.microsoft.com/playwright:v1.57.0-jammy
permissions:
contents: write
outputs:
version: ${{ steps.version.outputs.version }}
env:
USE_CODEX: 'false' # Set to 'true' to use OpenAI Codex instead of Claude
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Mark workspace as safe for git
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config --global --add safe.directory "$GITHUB_WORKSPACE/external/vscode-test-playwright"
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install system dependencies for Playwright demo
run: |
apt-get update
apt-get install -y \
dbus-x11 \
xvfb \
ffmpeg \
fontconfig \
fonts-liberation \
libasound2t64 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libcups2 \
libdrm2 \
libgbm1 \
libgtk-3-0 \
libnss3 \
libsecret-1-0 \
libx11-xcb1 \
libxcomposite1 \
libxdamage1 \
libxfixes3 \
libxkbcommon0 \
libxkbfile1 \
libxrandr2 \
libxshmfence1 \
libxss1
- name: Determine next version
id: version
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({ owner: context.repo.owner, repo: context.repo.repo }).catch(()=>null);
let nextVersion;
let prevTagRaw = '';
if (latestRelease) {
prevTagRaw = latestRelease.data.tag_name || '';
const prev = prevTagRaw.replace(/^v/, '');
const parts = prev.split('.');
if (parts.length !== 3 || parts.some(p => isNaN(Number(p)))) {
core.setFailed(`Latest release tag '${prevTagRaw}' is not semver x.y.z`);
return;
}
parts[2] = String(Number(parts[2]) + 1);
nextVersion = parts.join('.');
} else {
nextVersion = '0.0.1';
}
core.info(`Computed next version: ${nextVersion}`);
core.setOutput('version', nextVersion);
core.setOutput('previous_tag', prevTagRaw);
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Generate Context
id: context
run: |
PREV_TAG="${{ steps.version.outputs.previous_tag }}"
if [ -n "$PREV_TAG" ]; then
RANGE="$PREV_TAG..HEAD"
echo "Generating context for range: $RANGE"
else
RANGE=""
echo "Generating context for initial release (all history)"
fi
{
echo 'commits<<EOF'
git log --pretty=format:"- %s (%an) [%h]" $RANGE
echo ''
echo 'EOF'
} >> $GITHUB_OUTPUT
{
echo 'stats<<EOF'
if [ -n "$RANGE" ]; then
git diff --stat $RANGE
else
git show --stat
fi
echo 'EOF'
} >> $GITHUB_OUTPUT
- name: Init demo video submodule
run: |
git submodule update --init --depth 1 external/playwright-test-videos
- name: Update README demo video
shell: bash
env:
PW_VSCODE_WAIT_FOR_LINE_TIMEOUT_MS: '10000'
run: |
dbus-run-session -- xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' npm run demo:pw:update-readme
- name: Generate Release Notes
run: npm run release:notes
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
VERSION: ${{ steps.version.outputs.version }}
PREV_TAG: ${{ steps.version.outputs.previous_tag }}
COMMITS: ${{ steps.context.outputs.commits }}
STATS: ${{ steps.context.outputs.stats }}
- name: Commit and Push Changes
run: |
git add CHANGELOG.md README.md package.json docs/pw-videos/demo.mp4
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "docs: update for release ${{ steps.version.outputs.version }} [skip ci]"
git push origin main
fi
- name: Create Tag
run: |
git tag ${{ steps.version.outputs.version }}
git push origin ${{ steps.version.outputs.version }}
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.version }}
body_path: RELEASE_NOTES.md
token: ${{ secrets.GITHUB_TOKEN }}
package:
name: Package Extension
runs-on: ubuntu-latest
needs: build-and-test
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install vsce
run: npm install -g @vscode/vsce
- name: Package extension
run: vsce package
- name: Upload VSIX artifact
uses: actions/upload-artifact@v4
with:
name: vsix-package
path: '*.vsix'
retention-days: 30
publish:
name: Publish to VS Code Marketplace
runs-on: ubuntu-latest
needs: [package, auto-release]
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message || '', '[skip ci]') && github.actor != 'nektos/act'
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Init required submodules
run: |
git submodule update --init --depth 1 external/vscode-test-playwright
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install vsce
run: npm install -g @vscode/vsce
- name: Sync package.json version to tag (no commit)
env:
TAG_VERSION: ${{ needs.auto-release.outputs.version }}
run: |
node -e "const fs=require('fs'); const tag=process.env.TAG_VERSION; const pkg=JSON.parse(fs.readFileSync('package.json','utf8')); if(pkg.version!==tag){ pkg.version=tag; fs.writeFileSync('package.json', JSON.stringify(pkg,null,2)+'\n'); console.log('Synced package.json version to tag', tag); } else { console.log('package.json version already matches tag', tag); }"
- name: Publish to VS Code Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: vsce publish -p $VSCE_PAT --allow-proposed-apis terminalDataWriteEvent
- name: Download VSIX artifact
uses: actions/download-artifact@v4
with:
name: vsix-package
- name: Upload VSIX to GitHub Release
uses: softprops/action-gh-release@v1
with:
files: '*.vsix'
tag_name: ${{ needs.auto-release.outputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}