Skip to content
Merged
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
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ jobs:
uses: actions/cache@v4
with:
path: .vscode-test
key: vscode-test-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'tests/integration/runTest.js') }}
key: vscode-test-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
vscode-test-${{ runner.os }}-

- name: Install dependencies
env:
Expand All @@ -47,4 +49,7 @@ jobs:
- name: Verify
env:
NPM_CONFIG_CAFILE: ""
TCTIDIER_VSCODE_DOWNLOAD_ATTEMPTS: "5"
TCTIDIER_VSCODE_DOWNLOAD_DELAY_MS: "5000"
TCTIDIER_VSCODE_DOWNLOAD_TIMEOUT_MS: "300000"
run: npm run verify
14 changes: 13 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,27 @@ jobs:
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json

- name: Cache VS Code test runtime
uses: actions/cache@v4
with:
path: .vscode-test
key: vscode-test-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
vscode-test-${{ runner.os }}-

- name: Install dependencies
env:
NPM_CONFIG_CAFILE: ""
run: npm ci
run: npm ci --prefer-offline --no-audit --fund=false

- name: Release check
env:
NPM_CONFIG_CAFILE: ""
TCTIDIER_VSCODE_DOWNLOAD_ATTEMPTS: "5"
TCTIDIER_VSCODE_DOWNLOAD_DELAY_MS: "5000"
TCTIDIER_VSCODE_DOWNLOAD_TIMEOUT_MS: "300000"
run: npm run release:check

- name: Validate release tag and package version
Expand Down
138 changes: 126 additions & 12 deletions tests/integration/runTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,150 @@ const {
runTests
} = require("@vscode/test-electron");

function resolveVsCodeExecutablePath() {
const candidates = [
process.env.VSCODE_EXECUTABLE_PATH,
"C:\\Users\\TwinCAT\\AppData\\Local\\Programs\\Microsoft VS Code\\bin\\code.cmd",
"C:\\Program Files\\Microsoft VS Code\\bin\\code.cmd",
"C:\\Program Files (x86)\\Microsoft VS Code\\bin\\code.cmd",
"C:\\Users\\TwinCAT\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe",
"C:\\Program Files\\Microsoft VS Code\\Code.exe",
"C:\\Program Files (x86)\\Microsoft VS Code\\Code.exe"
].filter(Boolean);
const RETRYABLE_DOWNLOAD_CODES = new Set([
"ECONNABORTED",
"ECONNRESET",
"EAI_AGAIN",
"ENETDOWN",
"ENETRESET",
"ENETUNREACH",
"ENOTFOUND",
"ETIMEDOUT"
]);

function sleep(delayMs) {
return new Promise((resolve) => setTimeout(resolve, delayMs));
}

function uniqueExistingPaths(candidates) {
const seen = new Set();
const existing = [];

for (const candidate of candidates) {
if (!candidate || seen.has(candidate)) {
continue;
}

seen.add(candidate);

if (fs.existsSync(candidate)) {
return candidate;
existing.push(candidate);
}
}

return existing;
}

function resolveExecutablesFromPath() {
const executableNames =
process.platform === "win32"
? ["code.cmd", "code.exe", "code-insiders.cmd", "code-insiders.exe"]
: ["code", "code-insiders"];
const pathEntries = (process.env.PATH || "")
.split(path.delimiter)
.map((entry) => entry.trim().replace(/^"(.*)"$/, "$1"))
.filter(Boolean);

return pathEntries.flatMap((entry) =>
executableNames.map((name) => path.join(entry, name))
);
}

function resolveVsCodeExecutablePath() {
const localAppData = process.env.LOCALAPPDATA;
const programFiles = process.env.ProgramFiles;
const programFilesX86 = process.env["ProgramFiles(x86)"];
const installRoots = [
localAppData && path.join(localAppData, "Programs", "Microsoft VS Code"),
localAppData &&
path.join(localAppData, "Programs", "Microsoft VS Code Insiders"),
programFiles && path.join(programFiles, "Microsoft VS Code"),
programFiles && path.join(programFiles, "Microsoft VS Code Insiders"),
programFilesX86 && path.join(programFilesX86, "Microsoft VS Code"),
programFilesX86 &&
path.join(programFilesX86, "Microsoft VS Code Insiders")
].filter(Boolean);
const candidates = [
process.env.VSCODE_EXECUTABLE_PATH,
...installRoots.flatMap((root) => [
path.join(root, "bin", "code.cmd"),
path.join(root, "bin", "code-insiders.cmd"),
path.join(root, "Code.exe"),
path.join(root, "Code - Insiders.exe")
]),
...resolveExecutablesFromPath()
];

for (const candidate of uniqueExistingPaths(candidates)) {
return candidate;
}

return undefined;
}

function isRetryableDownloadError(error) {
if (!error || typeof error !== "object") {
return false;
}

const code = typeof error.code === "string" ? error.code : "";
if (RETRYABLE_DOWNLOAD_CODES.has(code)) {
return true;
}

const message = typeof error.message === "string" ? error.message.toLowerCase() : "";
return message.includes("aborted") || message.includes("socket hang up");
}

async function downloadVsCodeWithRetries() {
const attempts = Number.parseInt(
process.env.TCTIDIER_VSCODE_DOWNLOAD_ATTEMPTS || "3",
10
);
const delayMs = Number.parseInt(
process.env.TCTIDIER_VSCODE_DOWNLOAD_DELAY_MS || "2000",
10
);
const timeoutMs = Number.parseInt(
process.env.TCTIDIER_VSCODE_DOWNLOAD_TIMEOUT_MS || "120000",
10
);

for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
return await downloadAndUnzipVSCode({
cachePath: path.resolve(process.cwd(), ".vscode-test"),
timeout: timeoutMs
});
} catch (error) {
if (!isRetryableDownloadError(error) || attempt === attempts) {
throw error;
}

console.warn(
`Retrying VS Code test runtime download (${attempt}/${attempts}) after error: ${
error instanceof Error ? error.message : String(error)
}`
);
await sleep(delayMs * attempt);
}
}

throw new Error("Failed to download the VS Code test runtime.");
}

async function resolveVsCodeCliPath() {
const installedPath = resolveVsCodeExecutablePath();
if (installedPath) {
console.log(`Using local VS Code executable: ${installedPath}`);
if (installedPath.toLowerCase().endsWith(".exe")) {
return resolveCliPathFromVSCodeExecutablePath(installedPath);
}
return installedPath;
}

const downloadedPath = await downloadAndUnzipVSCode();
console.log("No local VS Code executable found. Downloading test runtime.");
const downloadedPath = await downloadVsCodeWithRetries();
return resolveCliPathFromVSCodeExecutablePath(downloadedPath);
}

Expand Down
Loading