Skip to content
Draft
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
25 changes: 17 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,19 @@ jobs:
- name: Unit Test
run: pnpm run test:unit

# ======== E2E: Windows + macOS, no Linux ========
# These are the platforms the extension is actually used on, and the only
# ones upstream rstest runs its ported VS Code suites on. Linux is excluded
# deliberately: the Extension Host needs xvfb there, and inotify reports a
# non-atomic file rewrite as separate truncate/write events, so fixture edits
# in the watch-mode suites race the watcher in a way no user hits. Do not add
# a Linux E2E job back without also making every fixture edit atomic.
# ======== E2E: Windows + macOS + Linux ========
# Windows and macOS are the platforms users run the extension on.
# Linux is here because rstack-ecosystem-ci runs every suite on
# ubuntu-latest (rstackjs/rstack-editor#40). The Extension Host needs a
# display there; `e2e/run.mjs` re-execs under `xvfb-run` when DISPLAY is unset.
e2e:
name: E2E (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]
os: [windows-latest, macos-latest, ubuntu-latest]

steps:
- name: Checkout
Expand All @@ -94,6 +92,17 @@ jobs:
- name: Build
run: pnpm run build

# The Electron runtime libraries plus xvfb. ubuntu-latest carries all
# five today, so this is a no-op there; naming them keeps the job working
# on images that lack them (`t64` is the Ubuntu 24.04 spelling).
- name: Install Extension Host Dependencies
if: runner.os == 'Linux'
run: |
dpkg -s libasound2t64 libgbm1 libgtk-3-0t64 libnss3 xvfb >/dev/null 2>&1 || {
sudo apt-get update
sudo apt-get install -y --no-install-recommends libasound2t64 libgbm1 libgtk-3-0t64 libnss3 xvfb
}

# `@vscode/test-electron` downloads a full VS Code into
# `packages/vscode/.vscode-test`. Cache only the immutable distribution
# directories, keyed on the extension manifest (which carries
Expand Down
1 change: 1 addition & 0 deletions packages/vscode/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten
- E2E fixtures install exact versions of published npm packages (not workspace links): the extension must work against what users actually install. Renovate updates the exact toolchain pins; generated fixture lockfiles and `node_modules` remain disposable and uncommitted.
- Prefer running the E2E slice that covers the change over the full chain: `pnpm test:e2e <slice ...>` (or the `test:e2e:<slice>` aliases). Slices are declared in the `SLICES` table in `e2e/run.mjs` (name, fixtures, entry) — the package.json scripts are thin forwards and carry no slice knowledge. `RSTACK_LINT_E2E_SUITES=<name,...>` filters lint suites.
- Run E2E locally as `VSCODE_CLI=1 pnpm test:e2e <slice ...>`. Without it, the launched VS Code overwrites the extension host's `PATH` with a login-shell snapshot; on a machine whose login-shell `node` is below the runtime floor, the User Node preflight (correctly) refuses and every fmt test times out waiting for a server. CI is unaffected — its PATH `node` is new enough either way. The heavier alternative, `--force-disable-user-env` in `e2e/runTest.ts`, was deliberately not taken: it would change env fidelity for every slice.
- On Linux, `e2e/run.mjs` re-execs itself under `xvfb-run -a` when `DISPLAY` is unset, so the whole chain runs against one X server and `pnpm test:e2e` is the same command everywhere. That makes `xvfb` a prerequisite on Linux; CI installs it with the Electron runtime libraries on ubuntu-latest.
27 changes: 27 additions & 0 deletions packages/vscode/e2e/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
// up-to-date fixture, and unknown names throw there), then the entries run
// sequentially — each `compile`d entry launches its own VS Code via
// `@vscode/test-electron`.
//
// On Linux that VS Code needs an X display, so this file re-execs itself under
// `xvfb-run` when there is none — running the whole chain, not just the
// entries, under one server.
import { spawnSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
Expand Down Expand Up @@ -59,6 +63,29 @@ const packageRoot = path.resolve(
'..',
);

// The Extension Host needs an X display. `xvfb-run` exports DISPLAY into the
// child, so the re-exec'd process fails this check — it cannot recurse.
if (process.platform === 'linux' && !process.env.DISPLAY) {
const result = spawnSync(
'xvfb-run',
[
'-a',
process.execPath,
fileURLToPath(import.meta.url),
...process.argv.slice(2),
],
{ cwd: packageRoot, stdio: 'inherit' },
);
if (result.error) {
if (/** @type {NodeJS.ErrnoException} */ (result.error).code === 'ENOENT') {
console.error('[e2e] xvfb-run not found; install the xvfb package');
process.exit(1);
}
throw result.error;
}
process.exit(result.status ?? 1);
}

/**
* @param {string} command
* @param {string[]} args
Expand Down
Loading