Skip to content

Commit 40b14ad

Browse files
committed
Pre-download VS Code + Java pack in setup-steps; refine repro guidance
Add .github/scripts/prewarm-vscode.js and a copilot-setup-steps step that warms AutoTest's <repo>/.vscode-test cache (VS Code stable + vscjava.vscode-java-pack) before the agent firewall engages, so firewalled UI reproductions launch offline. Refine repro/uitest guidance: separate reproduction from fix-proof (UI test's key value is red->green screenshots), require verifiers only on the decisive assertion step, and make PRs state repro method + execution status.
1 parent 67c6598 commit 40b14ad

5 files changed

Lines changed: 126 additions & 9 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
- When asked to add, update, run, or debug UI/E2E coverage, prefer the AutoTest YAML workflow under `test/e2e-plans/`.
1414
- Use the `uitest` skill for UI test work. It should create or update `test/e2e-plans/*.yaml`, validate the plan, build the OSGi bundle and package the extension when needed, run AutoTest, and inspect `test-results/`.
1515
- Do not create legacy VS Code extension tests (`test/maven-suite`, `test/gui`) for UI coverage unless the user explicitly asks for that format.
16-
- Prefer deterministic AutoTest verifiers (`verifyTreeItem`, `verifyFile`, `verifyEditorTab`, `verifyClipboard`) over screenshot-only checks.
16+
- Prefer deterministic AutoTest verifiers (`verifyTreeItem`, `verifyFile`, `verifyEditorTab`, `verifyClipboard`) on the decisive assertion step; you do not need a verifier on every step. Use AutoTest screenshots to prove a fix (a red run before, a green run after) — but never as the sole pass/fail authority for the decisive assertion.

.github/instructions/uitest-plan.instructions.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ action: 'clickViewTitleAction "Java Projects" "Unlink with Editor"'
3131
3232
## Verification rules
3333
34-
- Add deterministic verification to every meaningful step. The natural-language `verify` field is context for humans and failure analysis; it is not pass/fail authority by itself, and it is auto-passed when a plan runs with `--no-llm`.
34+
- You do **not** need a verifier on every step. Author the *actions* step-by-step, but gate pass/fail with a deterministic verifier only on the **decisive assertion step(s)** — the step that captures the reported bug — plus any step prone to a silent no-op (see the `expandTreeItem` / free-form action caveat above). Intermediate action steps can rely on AutoTest screenshots instead of their own verifier.
35+
- The natural-language `verify` field is context for humans and failure analysis; it is not pass/fail authority by itself, and it is auto-passed when a plan runs with `--no-llm`. So the decisive step **must** carry a deterministic verifier, or a `--no-llm` run is a false green.
3536
- Use `verifyTreeItem` (with `name:`, optional `exact: true`, and `visible: false` for absence) as the authoritative check for Java Projects tree state.
3637
- Use `verifyFile` after operations that create, modify, or delete files on disk (new type, export jar, permanent delete). VS Code can open duplicate editor tabs with stale buffers, so prefer file-content checks over editor checks after such operations.
3738
- Use `verifyEditorTab` to assert which file an action opened, and `verifyClipboard` for copy-path commands.
3839
- On state-check steps whose only assertion is a deterministic verifier, omit the `verify:` field to avoid false LLM failures.
39-
- Use screenshots only as diagnostics produced by AutoTest; do not make screenshots the only evidence of pass/fail.
40+
- Screenshots are AutoTest's evidence that an action ran and are the primary artifact for **proving a fix** (a red run before, a green run after). Do not make a screenshot the sole pass/fail authority for the decisive assertion — pair it with a deterministic verifier.
4041

4142
## Local validation commands
4243

.github/scripts/prewarm-vscode.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env node
2+
/*
3+
* Pre-download VS Code + the Java extensions into AutoTest's cache BEFORE the
4+
* Copilot coding agent's firewall is enabled.
5+
*
6+
* AutoTest (`@vscjava/vscode-autotest`) launches VS Code via `@vscode/test-electron`:
7+
* 1. downloadAndUnzipVSCode(version) -> <cwd>/.vscode-test/vscode-<...>
8+
* 2. resolveCliArgsFromVSCodeExecutablePath() -> --extensions-dir=<cwd>/.vscode-test/extensions
9+
* 3. code --install-extension <id> --force -> pulls Marketplace bits into that extensions dir
10+
*
11+
* The VS Code CDN (update.code.visualstudio.com) and the Marketplace are NOT on the
12+
* Copilot agent's default firewall allowlist, so those network calls fail at run time.
13+
* This script performs the exact same three operations during `copilot-setup-steps`
14+
* (which runs before the firewall), so the caches are warm and the firewalled UI run
15+
* hits them offline.
16+
*
17+
* Because `@vscode/test-electron` derives its cache from `process.cwd()`, this MUST run
18+
* from the repository root — the same directory AutoTest runs from at agent time.
19+
*
20+
* Env overrides:
21+
* VSCODE_VERSION VS Code channel/version to warm (default: "stable")
22+
* PREWARM_EXTENSIONS comma-separated extension ids (default: "vscjava.vscode-java-pack")
23+
*/
24+
"use strict";
25+
26+
const path = require("path");
27+
const cp = require("child_process");
28+
29+
function resolveTestElectron() {
30+
// Prefer the exact copy that the globally installed AutoTest uses, so the
31+
// version and default-cache-path logic match the agent run byte-for-byte.
32+
const candidates = [];
33+
try {
34+
const globalRoot = cp.execSync("npm root -g", { encoding: "utf-8" }).trim();
35+
candidates.push(path.join(globalRoot, "@vscjava", "vscode-autotest"));
36+
candidates.push(globalRoot);
37+
} catch {
38+
/* npm not on PATH — fall back to local resolution below */
39+
}
40+
candidates.push(process.cwd());
41+
try {
42+
const entry = require.resolve("@vscode/test-electron", { paths: candidates });
43+
return require(entry);
44+
} catch {
45+
// Last resort: a plain require (works if it is a local dependency).
46+
return require("@vscode/test-electron");
47+
}
48+
}
49+
50+
async function main() {
51+
const version = process.env.VSCODE_VERSION || "stable";
52+
const extensions = (process.env.PREWARM_EXTENSIONS || "vscjava.vscode-java-pack")
53+
.split(",")
54+
.map((s) => s.trim())
55+
.filter(Boolean);
56+
57+
const { downloadAndUnzipVSCode, resolveCliArgsFromVSCodeExecutablePath } = resolveTestElectron();
58+
59+
console.log(`⬇️ Pre-downloading VS Code "${version}" into ${path.join(process.cwd(), ".vscode-test")} ...`);
60+
const vscodePath = await downloadAndUnzipVSCode(version);
61+
console.log(`✅ VS Code ready: ${vscodePath}`);
62+
63+
const [cli, ...baseArgs] = resolveCliArgsFromVSCodeExecutablePath(vscodePath);
64+
const extensionsDir = baseArgs.find((a) => a.startsWith("--extensions-dir="))?.split("=")[1];
65+
console.log(`📁 Extensions dir: ${extensionsDir ?? "(default)"}`);
66+
67+
let failures = 0;
68+
for (const ext of extensions) {
69+
console.log(`📦 Installing ${ext} (+ Extension Pack members) ...`);
70+
try {
71+
cp.execFileSync(cli, [...baseArgs, "--install-extension", ext, "--force"], {
72+
stdio: "inherit",
73+
timeout: 300_000,
74+
env: { ...process.env },
75+
shell: process.platform === "win32",
76+
});
77+
console.log(`✅ Installed ${ext}`);
78+
} catch (e) {
79+
failures++;
80+
console.warn(`⚠️ Failed to install ${ext}: ${e.message}`);
81+
}
82+
}
83+
84+
if (failures > 0) {
85+
// Non-fatal: a missing extension only degrades UI reproduction, and the agent
86+
// can still fall back to the non-UI path. Surface it without aborting setup.
87+
console.warn(`⚠️ ${failures} extension(s) failed to pre-install; UI reproduction may be degraded.`);
88+
}
89+
console.log("🎉 VS Code + Java extensions pre-warmed for AutoTest.");
90+
}
91+
92+
main().catch((err) => {
93+
console.error("❌ Pre-warm failed:", err);
94+
process.exit(1);
95+
});

.github/skills/repro/SKILL.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ From the issue body (and the `bug_report` template fields) collect:
1919

2020
## 2. Decide: does this need a UI/E2E test?
2121

22+
The reproduction and the fix-proof are two different questions — decide each:
23+
24+
- **Reproduction** can often be non-UI or even a code read, especially for simple, obvious bugs. Prefer the cheapest reproduction that captures the report.
25+
- **Fix-proof** is where a UI/E2E test earns its cost: a red run before the fix and a green run after, with screenshots, is the strongest evidence for a user-facing bug. If the bug is user-facing, favour leaving a committed UI plan even when you first reproduced it another way.
26+
2227
**Use a UI/E2E AutoTest plan (`uitest` skill) when the bug is in the user-facing surface**, e.g.:
2328

2429
- Java Projects tree rendering, ordering, labels, icons, or node presence/absence.
@@ -57,7 +62,7 @@ npx @vscode/vsce package -o vscode-java-dependency.vsix
5762
npx -y @vscjava/vscode-autotest run test\e2e-plans\repro-issue-<n>.yaml --vsix vscode-java-dependency.vsix --no-llm --output test-results\repro-issue-<n>
5863
```
5964

60-
Author the plan so its deterministic verifier (`verifyTreeItem` / `verifyFile` / `verifyEditorTab` / `verifyClipboard`) asserts the **expected** behavior it therefore **fails on the current (buggy) build**, capturing the bug. Inspect `test-results/repro-issue-<n>/results.json` and screenshots to confirm the failure matches the report.
65+
Author the plan step-by-step for the **actions**, but you do not need a verifier on every step — put a deterministic verifier (`verifyTreeItem` / `verifyFile` / `verifyEditorTab` / `verifyClipboard`) on the **decisive assertion step** (the one that captures the bug) and on any step prone to a silent no-op. That decisive verifier must assert the **expected** behavior, so it **fails on the current (buggy) build**. Inspect `test-results/repro-issue-<n>/results.json` and the screenshots to confirm the failure matches the report, and keep the red-run screenshot as before-fix evidence.
6166

6267
**Non-UI path** — add the failing `test/maven-suite` or `jdtls.ext` test and run the existing suite (`npm test`, or the `jdtls.ext` Maven test) to confirm it fails.
6368

@@ -66,16 +71,21 @@ Author the plan so its deterministic verifier (`verifyTreeItem` / `verifyFile` /
6671
1. Fix the product code (`src/**` for TS, `jdtls.ext/**` for the OSGi backend).
6772
2. **Rebuild and repackage the VSIX** (`npm run build-server` + `vsce package`) before rerunning any UI plan — never rerun against a stale VSIX.
6873
3. Rerun the reproduction; the same plan/test must now pass (red → green).
69-
4. Leave the reproduction committed as a permanent regression test. `.github/workflows/e2eUI.yml` discovers `test/e2e-plans/*.yaml` automatically, so `repro-issue-<n>.yaml` becomes its own CI check with no workflow edits.
74+
4. Keep both runs' evidence: the **before** (red) and **after** (green) screenshots plus the `results.json` reason. The green screenshot is the primary proof that the fix works — attach it (and the before/after pair) to the PR.
75+
5. Leave the reproduction committed as a permanent regression test. `.github/workflows/e2eUI.yml` discovers `test/e2e-plans/*.yaml` automatically, so `repro-issue-<n>.yaml` becomes its own CI check with no workflow edits.
7076

7177
## 6. Report back
7278

73-
- **Reproduced + fixed**: open a PR citing the failing step / screenshot / `results.json` reason as evidence, and note that the committed reproduction now passes. Reference the issue.
79+
Every PR or comment must state **how you reproduced** (UI plan vs unit test vs code read) and the **execution status** (ran red→green with screenshots attached, or could not execute — e.g. the UI run was blocked — and why).
80+
81+
- **Reproduced + fixed**: open a PR that attaches the before (red) and after (green) screenshots as the fix-proof, cites the failing step / `results.json` reason, and notes the committed reproduction now passes. Reference the issue.
7482
- **Reproduced, report only**: comment with the reproduction (plan or test), the observed vs expected behavior, and the exact failing step.
83+
- **Reproduced but could not run the UI test** (e.g. VS Code download / Marketplace blocked): commit the plan, explain what fails and why it could not execute, and either fall back to a non-UI proof or ask a maintainer to unblock — do not claim a green run you did not observe.
7584
- **Could not reproduce**: comment with what you tried and precisely what is missing; label `needs-more-info`. Do not fabricate a fix for an unreproduced bug.
7685

7786
## Environment notes
7887

7988
- The Copilot coding agent environment is prepared by `.github/workflows/copilot-setup-steps.yml` (JDK 21, Node 20, AutoTest, Xvfb, a baseline VSIX). Assume these are present.
80-
- AutoTest downloads VS Code and installs `vscjava.vscode-java-pack` at run time. If those network hosts are blocked by the agent firewall, UI reproduction cannot launch — fall back to the non-UI path and note the limitation, or ask a maintainer to allow the VS Code download + Marketplace hosts.
89+
- That setup runs **before the agent firewall**, and its final step pre-downloads VS Code (stable) and the `vscjava.vscode-java-pack` extensions into AutoTest's `<repo>/.vscode-test` cache (via `.github/scripts/prewarm-vscode.js`). So the firewalled UI run should launch offline from that warm cache — you normally do **not** need to fetch VS Code or Marketplace bits yourself.
90+
- If the pre-warm did not run (e.g. an older branch) or the cache is cold, AutoTest will try to download VS Code + install `vscjava.vscode-java-pack` at run time. Those hosts (VS Code CDN + Marketplace) are firewall-blocked by default — if that happens, fall back to the non-UI path and note the limitation, or ask a maintainer to allow those hosts.
8191
- Always run AutoTest with `--no-llm` in the agent so pass/fail comes only from deterministic verifiers.

.github/workflows/copilot-setup-steps.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ name: "Copilot Setup Steps"
1010
# Xvfb / GTK libraries required to launch VS Code headless.
1111
#
1212
# NOTE: This workflow only takes effect once it is on the default branch.
13-
# Reproducing UI tests additionally needs the Copilot firewall to allow the
14-
# VS Code download + Marketplace hosts — see .github/skills/repro/SKILL.md.
13+
#
14+
# Setup steps run BEFORE the Copilot agent firewall is enabled, so the final
15+
# step pre-downloads VS Code (stable) and the Java Extension Pack into AutoTest's
16+
# `<repo>/.vscode-test` cache. That warms the exact files AutoTest fetches from
17+
# the VS Code CDN + Marketplace at run time — hosts the firewall blocks — so the
18+
# firewalled UI reproduction launches offline. See .github/skills/repro/SKILL.md.
1519

1620
on:
1721
workflow_dispatch:
@@ -67,3 +71,10 @@ jobs:
6771
# pay the full build cost. Copilot must repackage after editing src/** or
6872
# jdtls.ext/** before rerunning a plan against a stale VSIX.
6973
run: vsce package -o vscode-java-dependency.vsix
74+
75+
- name: Pre-download VS Code and Java extensions (before firewall)
76+
# Warms <repo>/.vscode-test so the firewalled agent run does not need the
77+
# VS Code CDN or Marketplace. Best-effort: a failure here only degrades UI
78+
# reproduction, so it must not block Copilot from starting work.
79+
continue-on-error: true
80+
run: node .github/scripts/prewarm-vscode.js

0 commit comments

Comments
 (0)