mirror of
https://github.com/github/codeql-action
synced 2026-10-07 00:30:17 +03:00
Merge remote-tracking branch 'origin/main' into mbg/copilot/update-instructions
This commit is contained in:
Generated
+12
-4
@@ -151690,11 +151690,14 @@ async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorizat
|
||||
return { totalDurationMs };
|
||||
}
|
||||
} catch (e) {
|
||||
await cleanUpPath(dest, "CodeQL bundle", logger);
|
||||
if (asHTTPError(e)?.status === 404) {
|
||||
throw e;
|
||||
}
|
||||
core11.warning(
|
||||
`Failed to download and extract CodeQL bundle using streaming with error: ${getErrorMessage(e)}`
|
||||
);
|
||||
core11.warning(`Falling back to downloading the bundle before extracting.`);
|
||||
await cleanUpPath(dest, "CodeQL bundle", logger);
|
||||
}
|
||||
const toolsDownloadStart = import_perf_hooks2.performance.now();
|
||||
const archivedBundlePath = await toolcache2.downloadTool(
|
||||
@@ -151766,9 +151769,14 @@ async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorizatio
|
||||
});
|
||||
if (response.statusCode !== 200) {
|
||||
response.resume();
|
||||
throw new Error(
|
||||
`Failed to download CodeQL bundle from ${codeqlURL}. HTTP status code: ${response.statusCode}.`
|
||||
);
|
||||
const baseMessage = `Failed to download CodeQL bundle from ${codeqlURL}.`;
|
||||
if (response.statusCode !== void 0) {
|
||||
throw new HTTPError(
|
||||
`${baseMessage} HTTP status code: ${response.statusCode}.`,
|
||||
response.statusCode
|
||||
);
|
||||
}
|
||||
throw new Error(baseMessage);
|
||||
}
|
||||
await extractTarZst(response, dest, tarVersion, logger);
|
||||
}
|
||||
|
||||
Generated
+53
-83
@@ -43,7 +43,7 @@
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^17.0.0",
|
||||
"@octokit/types": "^18.0.0",
|
||||
"@types/archiver": "^8.0.0",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
@@ -2150,21 +2150,6 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
|
||||
"version": "29.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
|
||||
"integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
|
||||
"integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^29.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
@@ -2184,21 +2169,6 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
|
||||
"version": "29.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
|
||||
"integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
|
||||
"integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^29.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
@@ -2219,21 +2189,6 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
|
||||
"version": "29.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
|
||||
"integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
|
||||
"integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^29.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
@@ -2241,9 +2196,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "28.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
|
||||
"integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
|
||||
"version": "29.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
|
||||
"integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
@@ -2261,6 +2216,21 @@
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
|
||||
"version": "28.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
|
||||
"integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
|
||||
"integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^28.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-request-log": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
|
||||
@@ -2285,6 +2255,21 @@
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
|
||||
"version": "28.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
|
||||
"integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
|
||||
"integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^28.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-retry": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.1.1.tgz",
|
||||
@@ -2302,6 +2287,21 @@
|
||||
"@octokit/core": ">=7"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": {
|
||||
"version": "28.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-28.0.0.tgz",
|
||||
"integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-retry/node_modules/@octokit/types": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
|
||||
"integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^28.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "10.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.16.tgz",
|
||||
@@ -2331,36 +2331,6 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
|
||||
"version": "29.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
|
||||
"integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/request-error/node_modules/@octokit/types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
|
||||
"integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^29.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
|
||||
"version": "29.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz",
|
||||
"integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/@octokit/types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
|
||||
"integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^29.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request/node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
@@ -2368,12 +2338,12 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-17.0.0.tgz",
|
||||
"integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==",
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz",
|
||||
"integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^28.0.0"
|
||||
"@octokit/openapi-types": "^29.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@open-draft/deferred-promise": {
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^17.0.0",
|
||||
"@octokit/types": "^18.0.0",
|
||||
"@types/archiver": "^8.0.0",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
|
||||
@@ -1,34 +1,16 @@
|
||||
import assert from "node:assert/strict";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import { withTmpFile } from "../../src/util";
|
||||
|
||||
import {
|
||||
hasValidChangenoteCategory,
|
||||
isValidChangenoteContent,
|
||||
isValidChangenoteFile,
|
||||
isValidChangenoteFilename,
|
||||
hasValidChangenoteCategory,
|
||||
VALID_CHANGE_NOTE_CATEGORIES,
|
||||
} from "./validate.mjs";
|
||||
|
||||
async function withTmpFile<T>(
|
||||
baseFileName: string,
|
||||
contents: string,
|
||||
body: (filePath: string) => Promise<T> | T,
|
||||
): Promise<T> {
|
||||
const tmpDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "changetool-validate-test-"),
|
||||
);
|
||||
try {
|
||||
const filePath = path.join(tmpDir, baseFileName);
|
||||
fs.writeFileSync(filePath, contents);
|
||||
return await body(filePath);
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
await describe("isValidChangenoteContent", async () => {
|
||||
await it("recognizes an unordered Markdown list", () => {
|
||||
const inputs = [
|
||||
|
||||
@@ -34,7 +34,7 @@ function main(): number {
|
||||
}
|
||||
|
||||
function usage(): number {
|
||||
console.log(`Usage: validate-changelog.mts validate <path> [<path> ...]`);
|
||||
console.log(`Usage: changenotes.mts validate <path> [<path> ...]`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+9
-2
@@ -4,12 +4,19 @@ set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Run `npm ci` in CI or `npm install` otherwise.
|
||||
#
|
||||
# `pr-checks` is an npm workspace of the repository root and the two share a single hoisted
|
||||
# `node_modules` directory. Running npm from this directory puts it in workspace mode, where it
|
||||
# ignores the root project's own dependencies by default. `npm ci` would then rebuild the shared
|
||||
# `node_modules` with only this workspace's dependencies, removing the root's ones, which breaks
|
||||
# anything that imports from `src` (such as `sync.ts` itself). `--include-workspace-root` keeps the
|
||||
# root project's dependencies in the installed tree.
|
||||
if [ "$GITHUB_ACTIONS" = "true" ]; then
|
||||
echo "In Actions, running 'npm ci' for 'sync.ts'..."
|
||||
npm ci
|
||||
npm ci --include-workspace-root
|
||||
else
|
||||
echo "Running 'npm install' for 'sync.ts'..."
|
||||
npm install --no-audit --no-fund
|
||||
npm install --no-audit --no-fund --include-workspace-root
|
||||
fi
|
||||
|
||||
npx tsx sync.ts
|
||||
|
||||
+136
-5
@@ -1,8 +1,12 @@
|
||||
import { once } from "events";
|
||||
import * as fs from "fs";
|
||||
import { ClientRequest, IncomingMessage } from "http";
|
||||
import * as path from "path";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import * as toolcache from "@actions/tool-cache";
|
||||
import test from "ava";
|
||||
import { https } from "follow-redirects";
|
||||
import nock from "nock";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
@@ -10,14 +14,14 @@ import { getRunnerLogger } from "./logging";
|
||||
import * as tar from "./tar";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import { downloadAndExtract } from "./tools-download";
|
||||
import { withTmpDir } from "./util";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract reports the durations when downloading before extracting",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.gz");
|
||||
const destination = path.join(tmpDir, "codeql");
|
||||
sinon.stub(toolcache, "downloadTool").resolves(archivePath);
|
||||
@@ -43,13 +47,16 @@ test.serial(
|
||||
test.serial(
|
||||
"downloadAndExtract falls back to downloading before extracting if streaming fails",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");
|
||||
const destination = path.join(tmpDir, "codeql");
|
||||
const downloadTool = sinon
|
||||
.stub(toolcache, "downloadTool")
|
||||
.resolves(archivePath);
|
||||
.callsFake(async () => {
|
||||
t.false(fs.existsSync(destination));
|
||||
return archivePath;
|
||||
});
|
||||
const extract = sinon.stub(tar, "extract").resolves(destination);
|
||||
const extractTarZst = sinon.stub(tar, "extractTarZst").resolves();
|
||||
const request = nock("https://example.com")
|
||||
@@ -78,10 +85,134 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract rethrows a 404 rather than retrying the download",
|
||||
async (t) => {
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const destination = path.join(tmpDir, "codeql");
|
||||
const downloadTool = sinon.stub(toolcache, "downloadTool");
|
||||
const extractTarZst = sinon.stub(tar, "extractTarZst").resolves();
|
||||
const request = nock("https://example.com")
|
||||
.get("/codeql-bundle.tar.zst")
|
||||
.reply(404, "Not found");
|
||||
|
||||
const error = await t.throwsAsync(
|
||||
downloadAndExtract(
|
||||
"https://example.com/codeql-bundle.tar.zst",
|
||||
"zstd",
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
getRunnerLogger(true),
|
||||
),
|
||||
{
|
||||
instanceOf: util.HTTPError,
|
||||
message:
|
||||
"Failed to download CodeQL bundle from https://example.com/codeql-bundle.tar.zst. HTTP status code: 404.",
|
||||
},
|
||||
);
|
||||
|
||||
t.is(error?.status, 404);
|
||||
t.true(request.isDone());
|
||||
t.false(extractTarZst.called);
|
||||
t.false(downloadTool.called);
|
||||
t.false(fs.existsSync(destination));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract falls back to downloading before extracting on a server error",
|
||||
async (t) => {
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");
|
||||
const destination = path.join(tmpDir, "codeql");
|
||||
const downloadTool = sinon
|
||||
.stub(toolcache, "downloadTool")
|
||||
.callsFake(async () => {
|
||||
t.false(fs.existsSync(destination));
|
||||
return archivePath;
|
||||
});
|
||||
const extract = sinon.stub(tar, "extract").resolves(destination);
|
||||
const extractTarZst = sinon.stub(tar, "extractTarZst").resolves();
|
||||
const request = nock("https://example.com")
|
||||
.get("/codeql-bundle.tar.zst")
|
||||
.reply(500);
|
||||
|
||||
const statusReport = await downloadAndExtract(
|
||||
"https://example.com/codeql-bundle.tar.zst",
|
||||
"zstd",
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
t.assert(Number.isInteger(statusReport.downloadDurationMs));
|
||||
t.true(request.isDone());
|
||||
t.false(extractTarZst.called);
|
||||
t.true(downloadTool.calledOnce);
|
||||
t.true(extract.calledOnce);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract handles an unknown status as a non-HTTP error",
|
||||
async (t) => {
|
||||
const asHTTPError = sinon.spy(util, "asHTTPError");
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");
|
||||
const destination = path.join(tmpDir, "codeql");
|
||||
const response = sinon.createStubInstance(IncomingMessage);
|
||||
response.statusCode = undefined;
|
||||
sinon
|
||||
.stub(https, "get")
|
||||
.callsArgWith(2, response)
|
||||
.returns(sinon.createStubInstance(ClientRequest));
|
||||
const warning = sinon.stub(core, "warning");
|
||||
const downloadTool = sinon
|
||||
.stub(toolcache, "downloadTool")
|
||||
.resolves(archivePath);
|
||||
const extract = sinon.stub(tar, "extract").resolves(destination);
|
||||
const extractTarZst = sinon.stub(tar, "extractTarZst").resolves();
|
||||
|
||||
await downloadAndExtract(
|
||||
"https://example.com/codeql-bundle.tar.zst",
|
||||
"zstd",
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
t.is(
|
||||
warning.firstCall.args[0],
|
||||
"Failed to download and extract CodeQL bundle using streaming with error: Failed to download CodeQL bundle from https://example.com/codeql-bundle.tar.zst.",
|
||||
);
|
||||
t.true(response.resume.calledOnce);
|
||||
t.false(extractTarZst.called);
|
||||
t.true(downloadTool.calledOnce);
|
||||
t.true(extract.calledOnce);
|
||||
});
|
||||
|
||||
t.true(asHTTPError.calledOnce);
|
||||
t.true(asHTTPError.firstCall.args[0] instanceof Error);
|
||||
t.false(asHTTPError.firstCall.args[0] instanceof util.HTTPError);
|
||||
t.is(asHTTPError.firstCall.returnValue, undefined);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract reports only the total duration when streaming extraction",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const downloadTool = sinon.stub(toolcache, "downloadTool");
|
||||
const extractTarZst = sinon
|
||||
|
||||
+25
-8
@@ -14,7 +14,13 @@ import { ActionState } from "./action-common";
|
||||
import { ActionsEnvVars, getEnv, ReadOnlyEnv } from "./environment";
|
||||
import { formatDuration, Logger } from "./logging";
|
||||
import * as tar from "./tar";
|
||||
import { cleanUpPath, getErrorMessage, getRequiredEnvParam } from "./util";
|
||||
import {
|
||||
asHTTPError,
|
||||
cleanUpPath,
|
||||
getErrorMessage,
|
||||
getRequiredEnvParam,
|
||||
HTTPError,
|
||||
} from "./util";
|
||||
|
||||
/**
|
||||
* High watermark to use when streaming the download and extraction of the CodeQL tools.
|
||||
@@ -88,14 +94,20 @@ export async function downloadAndExtract(
|
||||
return { totalDurationMs };
|
||||
}
|
||||
} catch (e) {
|
||||
// If we failed during processing, we want to clean up the destination directory
|
||||
// before we either try again or give up.
|
||||
await cleanUpPath(dest, "CodeQL bundle", logger);
|
||||
|
||||
// Retrying a 404 is pointless: the asset does not exist, so downloading it a different way
|
||||
// will fail in the same way.
|
||||
if (asHTTPError(e)?.status === 404) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
core.warning(
|
||||
`Failed to download and extract CodeQL bundle using streaming with error: ${getErrorMessage(e)}`,
|
||||
);
|
||||
core.warning(`Falling back to downloading the bundle before extracting.`);
|
||||
|
||||
// If we failed during processing, we want to clean up the destination directory
|
||||
// before we try again.
|
||||
await cleanUpPath(dest, "CodeQL bundle", logger);
|
||||
}
|
||||
|
||||
const toolsDownloadStart = performance.now();
|
||||
@@ -191,9 +203,14 @@ async function downloadAndExtractZstdWithStreaming(
|
||||
if (response.statusCode !== 200) {
|
||||
// Discard the response body so that the connection can be released.
|
||||
response.resume();
|
||||
throw new Error(
|
||||
`Failed to download CodeQL bundle from ${codeqlURL}. HTTP status code: ${response.statusCode}.`,
|
||||
);
|
||||
const baseMessage = `Failed to download CodeQL bundle from ${codeqlURL}.`;
|
||||
if (response.statusCode !== undefined) {
|
||||
throw new HTTPError(
|
||||
`${baseMessage} HTTP status code: ${response.statusCode}.`,
|
||||
response.statusCode,
|
||||
);
|
||||
}
|
||||
throw new Error(baseMessage);
|
||||
}
|
||||
|
||||
await tar.extractTarZst(response, dest, tarVersion, logger);
|
||||
|
||||
+26
-3
@@ -84,9 +84,32 @@ export async function withTmpDir<T>(
|
||||
body: (tmpDir: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeql-action-"));
|
||||
const result = await body(tmpDir);
|
||||
await fs.promises.rm(tmpDir, { force: true, recursive: true });
|
||||
return result;
|
||||
try {
|
||||
return await body(tmpDir);
|
||||
} finally {
|
||||
await fs.promises.rm(tmpDir, { force: true, recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary file with the given contents, runs the given body, and
|
||||
* then deletes the file. Note that, to create a temporary file, we first create
|
||||
* a temporary directory via {@link withTmpDir} and then create the file within
|
||||
* that directory.
|
||||
* @param baseFileName The name to assign the temporary file.
|
||||
* @param contents The contents to write to the temporary file.
|
||||
* @param body The function to execute with the temporary file.
|
||||
*/
|
||||
export async function withTmpFile<T>(
|
||||
baseFileName: string,
|
||||
contents: string,
|
||||
body: (filePath: string) => Promise<T> | T,
|
||||
): Promise<T> {
|
||||
return withTmpDir(async (tmpDir) => {
|
||||
const filePath = path.join(tmpDir, baseFileName);
|
||||
fs.writeFileSync(filePath, contents);
|
||||
return body(filePath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user