Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
63db958
ci: cache test/e2e node_modules on Windows e2e shards
midleman May 18, 2026
4ad0011
fix(build): patch fs with graceful-fs in Win32 gulp tasks to prevent …
midleman May 19, 2026
cfa1b0d
spike: add Windows packaged-build workflow to test graceful-fs EMFILE…
midleman May 19, 2026
b50ee30
spike: trigger packaged-build workflow on PR to test graceful-fs fix
midleman May 19, 2026
9bbe6b6
fix: prevent EMFILE in fromLocalWebpack by opening files sequentially
midleman May 19, 2026
b17ba8d
ci: add build/lib/extensions.ts to spike workflow paths filter
midleman May 19, 2026
60cee6e
ci: remove spike workflow and graceful-fs patch
midleman May 19, 2026
5f0c28b
ci: implement Windows build-once for e2e shards
midleman May 19, 2026
5f5425d
spike: test zstd availability and performance on Windows runner
midleman May 19, 2026
bb511a8
spike: trigger zstd test on pull_request
midleman May 19, 2026
4c7ba52
spike: fix missing mkdir for gzip dest in comparison step
midleman May 19, 2026
8c1c031
ci: use zstd compression for Windows build artifact
midleman May 19, 2026
dca2c71
ci: bump Linux PR workers to 3 with per-worker Xvfb isolation
midleman May 20, 2026
e7164c6
ci: bump Linux PR workers from 3 to 4
midleman May 20, 2026
237fc46
ci: remove zstd validation spike workflow
midleman May 20, 2026
e4ec09e
ci: validate xvfb WORKERS input and guard zstd extraction
midleman May 20, 2026
64c5187
ci: revert Linux PR workers to 2; keep per-worker Xvfb infrastructure
midleman May 27, 2026
54bd5ad
ci: bump test-merge shards for Linux electron (2 to 3) and Windows (3…
midleman May 27, 2026
14696a5
ci: temporarily trigger test-merge on this PR to validate shard counts
midleman May 27, 2026
c02396e
ci: speed up Windows e2e shards via Defender exclusion + drop Quarto …
midleman May 27, 2026
4275a43
ci: bump test-merge to 6 shards across Linux electron, Windows, and c…
midleman May 27, 2026
26731e7
ci: extend Defender exclusion to build-windows job
midleman May 27, 2026
76a2dfa
ci: lower test-merge chromium shards from 6 to 5
midleman May 27, 2026
c548bb5
ci: fix Defender exclusion paths and add verification logging
midleman May 27, 2026
749b8a3
ci: restore Quarto setup on Windows e2e shards
midleman May 27, 2026
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
6 changes: 6 additions & 0 deletions .github/actions/setup-test-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ inputs:
aws-region:
description: "The AWS region for S3"
required: true
workers:
description: "Number of Playwright workers (used to size per-worker Xvfb displays)"
required: false
default: "1"

runs:
using: "composite"
Expand All @@ -25,3 +29,5 @@ runs:

- name: Setup Xvfb
uses: ./.github/actions/setup-xvfb
with:
workers: ${{ inputs.workers }}
42 changes: 33 additions & 9 deletions .github/actions/setup-xvfb/action.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
name: "Setup Xvfb"
description: "Configure and start Xvfb virtual framebuffer"
description: "Configure and start Xvfb virtual framebuffer, one display per Playwright worker"

inputs:
workers:
description: "Number of Xvfb displays to start (one per Playwright worker)"
required: false
default: "1"

runs:
using: "composite"
steps:
- name: Setup Xvfb
shell: bash
env:
WORKERS: ${{ inputs.workers }}
run: |
# Start Xvfb in the background
/usr/bin/Xvfb :10 -ac -screen 0 2560x1440x24 > /tmp/Xvfb.out 2>&1 &
# Fail fast on invalid input - otherwise `seq 0 -1` silently starts zero
# Xvfb instances and the ready-check reports success with no displays.
[[ "$WORKERS" =~ ^[1-9][0-9]*$ ]] || { echo "WORKERS must be a positive integer, got: '$WORKERS'"; exit 1; }

# Start one Xvfb instance per worker starting at :10, so Playwright workers
# don't race on a shared X server. test/e2e/infra/electron.ts maps each
# worker's parallelIndex to DISPLAY=:(10+parallelIndex).
for i in $(seq 0 $((WORKERS - 1))); do
display=":$((10 + i))"
/usr/bin/Xvfb "$display" -ac -screen 0 2560x1440x24 > "/tmp/Xvfb.$i.out" 2>&1 &
done

# Wait until Xvfb is ready
# Wait until every display is ready (default DISPLAY=:10 used by non-Playwright steps).
export DISPLAY=:10
for i in {1..10}; do
if xdpyinfo > /dev/null 2>&1; then
echo "Xvfb is ready"
for attempt in {1..15}; do
ready=true
for i in $(seq 0 $((WORKERS - 1))); do
if ! DISPLAY=":$((10 + i))" xdpyinfo > /dev/null 2>&1; then
ready=false
break
fi
done
if [ "$ready" = true ]; then
echo "All $WORKERS Xvfb display(s) ready"
break
fi
echo "Waiting for Xvfb to start..."
echo "Waiting for Xvfb displays to start..."
sleep 1
done

# Export the DISPLAY variable for subsequent steps
# Export the default DISPLAY for any subsequent step that doesn't go through Playwright.
echo "DISPLAY=:10" >> $GITHUB_ENV

# Start a proper dbus session bus for Electron/Chromium
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-e2e-ubuntu-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ jobs:
with:
aws-role-to-assume: ${{ secrets.QA_AWS_RO_ROLE }}
aws-region: ${{ secrets.QA_AWS_REGION }}
workers: ${{ inputs.workers }}

# - name: Log Shard Distribution Info
# run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-e2e-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ jobs:
with:
aws-role-to-assume: ${{ secrets.QA_AWS_RO_ROLE }}
aws-region: ${{ secrets.QA_AWS_REGION }}
workers: ${{ inputs.workers }}

# Preloading ensures the Node.js binary is fully built and ready before
# any parallel processes start, preventing runtime conflicts
Expand Down
201 changes: 201 additions & 0 deletions .github/workflows/test-e2e-windows-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
name: "E2E: Build Positron (Windows)"

on:
workflow_call:
inputs:
commit:
description: "Commit SHA to test"
required: false
default: ""
type: string
skip_cache:
required: false
description: "Skip restoring caches (use when testing a specific commit)"
type: boolean
default: false
save_cache:
required: false
description: "Whether to save caches after the build (only one workflow per run should save to avoid race conditions)"
type: boolean
default: false

permissions:
id-token: write
contents: read

jobs:
build-windows:
name: build-windows
timeout-minutes: 60
runs-on: windows-latest-8x
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
POSITRON_BUILD_NUMBER: 0

steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive

- name: Checkout specific commit
if: ${{ inputs.commit != '' }}
run: git checkout ${{ inputs.commit }}

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc

# Defender's real-time scanning hits every file created during cache
# restore, npm install (100k+ files), compile, and Playwright install.
# Cover workspace, parent, and both possible drive roots since the
# GitHub Actions Windows runner uses D:\a (and sometimes C:\a).
- name: Disable Defender scanning for build paths
shell: pwsh
run: |
$workspace = "${{ github.workspace }}"
$parent = Split-Path -Parent $workspace
Add-MpPreference -ExclusionPath "${{ runner.temp }}" -ErrorAction SilentlyContinue
Add-MpPreference -ExclusionPath "$workspace" -ErrorAction SilentlyContinue
Add-MpPreference -ExclusionPath "$parent" -ErrorAction SilentlyContinue
Add-MpPreference -ExclusionPath "D:\a" -ErrorAction SilentlyContinue
Add-MpPreference -ExclusionPath "C:\a" -ErrorAction SilentlyContinue
Write-Host "Defender exclusions applied:"
Write-Host " - ${{ runner.temp }}"
Write-Host " - $workspace"
Write-Host " - $parent"
Write-Host " - D:\a, C:\a"
(Get-MpPreference).ExclusionPath | ForEach-Object { Write-Host " [verified] $_" }

- name: 📦 Restore caches
if: ${{ !inputs.skip_cache }}
id: restore-caches
uses: ./.github/actions/restore-build-caches

- name: Install node dependencies
if: steps.restore-caches.outputs.cache-npm-core-hit != 'true' ||
steps.restore-caches.outputs.cache-npm-extensions-volatile-hit != 'true' ||
steps.restore-caches.outputs.cache-npm-extensions-stable-hit != 'true'
uses: nick-fields/retry@v4
env:
npm_config_arch: x64
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
POSITRON_GITHUB_RO_PAT: ${{ github.token }}
POSITRON_PARALLEL_INSTALL: '0' # Disabled on Windows due to native module file locking races
POSITRON_NPM_CONCURRENCY: '30' # Unused when parallel install is disabled
NPM_CONFIG_AUDIT: 'false' # Skip security audit during install (speeds up install)
NPM_CONFIG_FUND: 'false' # Skip funding messages
NPM_CONFIG_UPDATE_NOTIFIER: 'false' # Skip update notifications
POSITRON_EXTENSIONS_FILTER: ${{ (steps.restore-caches.outputs.cache-npm-extensions-volatile-hit == 'true' && steps.restore-caches.outputs.cache-npm-extensions-stable-hit != 'true' && 'stable') || (steps.restore-caches.outputs.cache-npm-extensions-volatile-hit != 'true' && steps.restore-caches.outputs.cache-npm-extensions-stable-hit == 'true' && 'volatile') || '' }}
with:
timeout_minutes: 90
max_attempts: 3
retry_wait_seconds: 5
shell: bash
command: |
set -e
npm --prefix build ci --no-audit --no-fund --fetch-timeout 120000
npm ci --no-audit --no-fund --fetch-timeout 120000

- name: Download binaries (Ark, Kallichore) with retry
uses: nick-fields/retry@v4
env:
POSITRON_GITHUB_RO_PAT: ${{ github.token }}
with:
timeout_minutes: 20
max_attempts: 3
retry_wait_seconds: 10
shell: bash
command: |
set -e
cd extensions/positron-r && npm rebuild --foreground-scripts
cd ../positron-supervisor && npm rebuild --foreground-scripts

- name: Compile Positron and Download Electron
run: npm exec -- npm-run-all --max-old-space-size=8192 -p compile "electron x64"

- name: Install Playwright browsers
run: npm run playwright-install

- name: Restore E2E test dependencies cache
id: cache-e2e
uses: actions/cache/restore@v5
with:
path: test/e2e/node_modules
key: ${{ runner.os }}-e2e-v1-${{ hashFiles('test/e2e/package-lock.json') }}

- name: Install E2E test dependencies
if: steps.cache-e2e.outputs.cache-hit != 'true'
run: npm --prefix test/e2e ci --no-audit --no-fund

- name: Save E2E test dependencies cache
if: ${{ steps.cache-e2e.outputs.cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@v5
with:
path: test/e2e/node_modules
key: ${{ runner.os }}-e2e-v1-${{ hashFiles('test/e2e/package-lock.json') }}

- name: Compile E2E Tests
run: npm --prefix test/e2e run compile

# Downloads Builtin Extensions (needed for integration & e2e testing)
- name: Download Builtin Extensions
shell: bash
run: npm run prelaunch

- name: 💾 Save caches
if: ${{ inputs.save_cache }}
uses: ./.github/actions/save-build-caches
with:
cache-npm-core-hit: ${{ steps.restore-caches.outputs.cache-npm-core-hit }}
cache-npm-extensions-volatile-hit: ${{ steps.restore-caches.outputs.cache-npm-extensions-volatile-hit }}
cache-npm-extensions-stable-hit: ${{ steps.restore-caches.outputs.cache-npm-extensions-stable-hit }}
cache-builtins-hit: ${{ steps.restore-caches.outputs.cache-builtins-hit }}
cache-playwright-hit: ${{ steps.restore-caches.outputs.cache-playwright-hit }}
extensions-volatile-hash: ${{ steps.restore-caches.outputs.extensions-volatile-hash }}
extensions-stable-hash: ${{ steps.restore-caches.outputs.extensions-stable-hash }}
package-locks-hash: ${{ steps.restore-caches.outputs.package-locks-hash }}
node-version: ${{ steps.restore-caches.outputs.node-version }}
playwright-version: ${{ steps.restore-caches.outputs.playwright-version }}

- name: Create Build Artifact
shell: bash
run: |
WORKSPACE=$(cygpath -u "${GITHUB_WORKSPACE}")
WORKSPACE_PARENT="$(dirname "${WORKSPACE}")"
WORKSPACE_NAME="$(basename "${WORKSPACE}")"
ARTIFACTS_DIR="$(cygpath -u "${RUNNER_TEMP}")/artifacts"
mkdir -p "${ARTIFACTS_DIR}"
echo "Creating build artifact from ${WORKSPACE}..."
# Tar the full workspace so shards skip npm ci and compile entirely.
# Exclude .git (shards restore their own via checkout) and npm/pip caches.
# Prefer zstd (faster compress+decompress, smaller output) with gzip fallback.
if command -v zstd >/dev/null 2>&1; then
echo "Using zstd (multithreaded) for compression"
tar -C "${WORKSPACE_PARENT}" \
--exclude="${WORKSPACE_NAME}/.git" \
--exclude="${WORKSPACE_NAME}/.npm-cache" \
--use-compress-program='zstd -T0 -1' \
-cf "${ARTIFACTS_DIR}/positron-build.tar.zst" \
"${WORKSPACE_NAME}"
echo "Artifact size: $(du -sh "${ARTIFACTS_DIR}/positron-build.tar.zst")"
else
echo "zstd not available, falling back to gzip"
tar -C "${WORKSPACE_PARENT}" \
--exclude="${WORKSPACE_NAME}/.git" \
--exclude="${WORKSPACE_NAME}/.npm-cache" \
-czf "${ARTIFACTS_DIR}/positron-build.tar.gz" \
"${WORKSPACE_NAME}"
echo "Artifact size: $(du -sh "${ARTIFACTS_DIR}/positron-build.tar.gz")"
fi

- name: Upload Build Artifact
uses: actions/upload-artifact@v7
with:
name: positron-build-windows
path: ${{ runner.temp }}/artifacts
retention-days: 1
compression-level: 0 # already compressed
Loading
Loading