mirror of
https://github.com/github/codeql-action.git
synced 2026-10-03 17:41:28 +00:00
Use per-language CodeQL bundles
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2
.github/workflows/__bundle-toolcache.yml
generated
vendored
2
.github/workflows/__bundle-toolcache.yml
generated
vendored
@@ -80,7 +80,7 @@ jobs:
|
|||||||
- id: init
|
- id: init
|
||||||
uses: ./../action/init
|
uses: ./../action/init
|
||||||
with:
|
with:
|
||||||
languages: javascript
|
languages: javascript,python
|
||||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
- uses: ./../action/analyze
|
- uses: ./../action/analyze
|
||||||
with:
|
with:
|
||||||
|
|||||||
164
.github/workflows/__per-language-bundle-validation.yml
generated
vendored
Normal file
164
.github/workflows/__per-language-bundle-validation.yml
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
# Warning: This file is generated automatically, and should not be modified.
|
||||||
|
# Instead, please modify the template in the pr-checks directory and run:
|
||||||
|
# pr-checks/sync.sh
|
||||||
|
# to regenerate this file.
|
||||||
|
|
||||||
|
name: PR Check - Per-language bundles
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GO111MODULE: auto
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/v*
|
||||||
|
pull_request: {}
|
||||||
|
merge_group:
|
||||||
|
types:
|
||||||
|
- checks_requested
|
||||||
|
schedule:
|
||||||
|
- cron: '0 5 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
workflow_call:
|
||||||
|
inputs: {}
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
concurrency:
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
|
||||||
|
group: per-language-bundle-validation-${{github.ref}}
|
||||||
|
jobs:
|
||||||
|
per-language-bundle-validation:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- language: actions
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
expected-extractors: actions javascript
|
||||||
|
- language: cpp
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: manual
|
||||||
|
build-command: gcc -o main main.c
|
||||||
|
- language: csharp
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: none
|
||||||
|
- language: go
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: autobuild
|
||||||
|
- language: java
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: none
|
||||||
|
- language: javascript
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: python
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: ruby
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: rust
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: swift
|
||||||
|
os: macos-latest-xlarge
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: autobuild
|
||||||
|
name: Per-language bundles
|
||||||
|
if: github.triggering_actor != 'dependabot[bot]'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
security-events: read
|
||||||
|
timeout-minutes: 45
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
- name: Prepare test
|
||||||
|
id: prepare-test
|
||||||
|
uses: ./.github/actions/prepare-test
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.version }}
|
||||||
|
use-all-platform-bundle: 'false'
|
||||||
|
setup-kotlin: 'true'
|
||||||
|
- uses: ./../action/init
|
||||||
|
id: init
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
build-mode: ${{ matrix['build-mode'] }}
|
||||||
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
|
- name: Check that the bundle contains only the expected extractors
|
||||||
|
env:
|
||||||
|
CODEQL_PATH: ${{ steps.init.outputs.codeql-path }}
|
||||||
|
LANGUAGE: ${{ matrix.language }}
|
||||||
|
EXPECTED_EXTRACTORS: ${{ matrix['expected-extractors'] || matrix.language }}
|
||||||
|
run: |
|
||||||
|
extractors="$("$CODEQL_PATH" resolve languages --format=json | jq -r 'keys[]')"
|
||||||
|
echo "Extractors in the bundle:"
|
||||||
|
echo "$extractors"
|
||||||
|
echo "Expected: $EXPECTED_EXTRACTORS"
|
||||||
|
|
||||||
|
for expected in $EXPECTED_EXTRACTORS; do
|
||||||
|
if ! echo "$extractors" | grep -qx "$expected"; then
|
||||||
|
echo "::error::The ${LANGUAGE} bundle does not contain the ${expected} extractor."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If the bundle contained extractors beyond those the language needs, then it would not
|
||||||
|
# have been trimmed, and this job would be silently validating the combined bundle.
|
||||||
|
for other in actions cpp csharp go java javascript python ruby rust swift; do
|
||||||
|
if echo "$EXPECTED_EXTRACTORS" | grep -qw "$other"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if echo "$extractors" | grep -qx "$other"; then
|
||||||
|
echo "::error::The ${LANGUAGE} bundle also contains the ${other} extractor, so it is not trimmed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
- name: Check that the bundle was not added to the toolcache
|
||||||
|
env:
|
||||||
|
CODEQL_PATH: ${{ steps.init.outputs.codeql-path }}
|
||||||
|
run: |
|
||||||
|
# A bundle that is missing most of its extractors must never be left in the toolcache,
|
||||||
|
# where a later job analyzing a different language could pick it up. The runner image
|
||||||
|
# ships with its own CodeQL in the toolcache, so check where this bundle was extracted to
|
||||||
|
# rather than whether the toolcache contains CodeQL at all.
|
||||||
|
echo "CodeQL is at $CODEQL_PATH"
|
||||||
|
if [[ "$CODEQL_PATH" == "$RUNNER_TOOL_CACHE"/* ]]; then
|
||||||
|
echo "::error::The per-language bundle was added to the toolcache at $CODEQL_PATH."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$CODEQL_PATH" != "$RUNNER_TEMP"/* ]]; then
|
||||||
|
echo "::error::Expected the per-language bundle to be extracted under $RUNNER_TEMP, but found it at $CODEQL_PATH."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Build code
|
||||||
|
if: matrix['build-command']
|
||||||
|
run: ${{ matrix['build-command'] }}
|
||||||
|
- uses: ./../action/analyze
|
||||||
|
id: analysis
|
||||||
|
with:
|
||||||
|
upload-database: false
|
||||||
|
- name: Check that a database was created for the language
|
||||||
|
env:
|
||||||
|
DB_LOCATIONS: ${{ steps.analysis.outputs.db-locations }}
|
||||||
|
LANGUAGE: ${{ matrix.language }}
|
||||||
|
run: |
|
||||||
|
database="$(echo "$DB_LOCATIONS" | jq -r --arg lang "$LANGUAGE" '.[$lang] // empty')"
|
||||||
|
if [ -z "$database" ] || [ ! -d "$database" ]; then
|
||||||
|
echo "::error::No CodeQL database was created for ${LANGUAGE}."
|
||||||
|
echo "Databases: $DB_LOCATIONS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Created a ${LANGUAGE} database at ${database}."
|
||||||
|
env:
|
||||||
|
CODEQL_ACTION_PER_LANGUAGE_BUNDLES: true
|
||||||
|
CODEQL_ACTION_TEST_MODE: true
|
||||||
@@ -75,7 +75,8 @@ jobs:
|
|||||||
uses: ./../action/.github/actions/check-codescanning-config
|
uses: ./../action/.github/actions/check-codescanning-config
|
||||||
with:
|
with:
|
||||||
expected-config-file-contents: "{}"
|
expected-config-file-contents: "{}"
|
||||||
languages: javascript
|
# Request multiple languages so later checks can reuse the combined bundle.
|
||||||
|
languages: javascript,python
|
||||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
|
|
||||||
- name: Packs from input
|
- name: Packs from input
|
||||||
|
|||||||
357
lib/entry-points.js
generated
357
lib/entry-points.js
generated
@@ -27216,8 +27216,8 @@ var require_gte = __commonJS({
|
|||||||
"node_modules/semver/functions/gte.js"(exports2, module2) {
|
"node_modules/semver/functions/gte.js"(exports2, module2) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var compare3 = require_compare();
|
var compare3 = require_compare();
|
||||||
var gte7 = (a, b, loose) => compare3(a, b, loose) >= 0;
|
var gte8 = (a, b, loose) => compare3(a, b, loose) >= 0;
|
||||||
module2.exports = gte7;
|
module2.exports = gte8;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27238,7 +27238,7 @@ var require_cmp = __commonJS({
|
|||||||
var eq = require_eq();
|
var eq = require_eq();
|
||||||
var neq = require_neq();
|
var neq = require_neq();
|
||||||
var gt = require_gt();
|
var gt = require_gt();
|
||||||
var gte7 = require_gte();
|
var gte8 = require_gte();
|
||||||
var lt2 = require_lt();
|
var lt2 = require_lt();
|
||||||
var lte2 = require_lte();
|
var lte2 = require_lte();
|
||||||
var cmp = (a, op, b, loose) => {
|
var cmp = (a, op, b, loose) => {
|
||||||
@@ -27268,7 +27268,7 @@ var require_cmp = __commonJS({
|
|||||||
case ">":
|
case ">":
|
||||||
return gt(a, b, loose);
|
return gt(a, b, loose);
|
||||||
case ">=":
|
case ">=":
|
||||||
return gte7(a, b, loose);
|
return gte8(a, b, loose);
|
||||||
case "<":
|
case "<":
|
||||||
return lt2(a, b, loose);
|
return lt2(a, b, loose);
|
||||||
case "<=":
|
case "<=":
|
||||||
@@ -28076,7 +28076,7 @@ var require_outside = __commonJS({
|
|||||||
var gt = require_gt();
|
var gt = require_gt();
|
||||||
var lt2 = require_lt();
|
var lt2 = require_lt();
|
||||||
var lte2 = require_lte();
|
var lte2 = require_lte();
|
||||||
var gte7 = require_gte();
|
var gte8 = require_gte();
|
||||||
var outside = (version, range2, hilo, options) => {
|
var outside = (version, range2, hilo, options) => {
|
||||||
version = new SemVer(version, options);
|
version = new SemVer(version, options);
|
||||||
range2 = new Range2(range2, options);
|
range2 = new Range2(range2, options);
|
||||||
@@ -28091,7 +28091,7 @@ var require_outside = __commonJS({
|
|||||||
break;
|
break;
|
||||||
case "<":
|
case "<":
|
||||||
gtfn = lt2;
|
gtfn = lt2;
|
||||||
ltefn = gte7;
|
ltefn = gte8;
|
||||||
ltfn = gt;
|
ltfn = gt;
|
||||||
comp = "<";
|
comp = "<";
|
||||||
ecomp = "<=";
|
ecomp = "<=";
|
||||||
@@ -28406,7 +28406,7 @@ var require_semver2 = __commonJS({
|
|||||||
var lt2 = require_lt();
|
var lt2 = require_lt();
|
||||||
var eq = require_eq();
|
var eq = require_eq();
|
||||||
var neq = require_neq();
|
var neq = require_neq();
|
||||||
var gte7 = require_gte();
|
var gte8 = require_gte();
|
||||||
var lte2 = require_lte();
|
var lte2 = require_lte();
|
||||||
var cmp = require_cmp();
|
var cmp = require_cmp();
|
||||||
var coerce3 = require_coerce();
|
var coerce3 = require_coerce();
|
||||||
@@ -28445,7 +28445,7 @@ var require_semver2 = __commonJS({
|
|||||||
lt: lt2,
|
lt: lt2,
|
||||||
eq,
|
eq,
|
||||||
neq,
|
neq,
|
||||||
gte: gte7,
|
gte: gte8,
|
||||||
lte: lte2,
|
lte: lte2,
|
||||||
cmp,
|
cmp,
|
||||||
coerce: coerce3,
|
coerce: coerce3,
|
||||||
@@ -31721,7 +31721,7 @@ var require_brace_expansion = __commonJS({
|
|||||||
function lte2(i, y) {
|
function lte2(i, y) {
|
||||||
return i <= y;
|
return i <= y;
|
||||||
}
|
}
|
||||||
function gte7(i, y) {
|
function gte8(i, y) {
|
||||||
return i >= y;
|
return i >= y;
|
||||||
}
|
}
|
||||||
function combine2(acc, base, pre, values, max, maxLength, dropEmpties, outBase) {
|
function combine2(acc, base, pre, values, max, maxLength, dropEmpties, outBase) {
|
||||||
@@ -31754,7 +31754,7 @@ var require_brace_expansion = __commonJS({
|
|||||||
var reverse = y < x;
|
var reverse = y < x;
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
incr *= -1;
|
incr *= -1;
|
||||||
test = gte7;
|
test = gte8;
|
||||||
}
|
}
|
||||||
var pad = n.some(isPadded2);
|
var pad = n.some(isPadded2);
|
||||||
var length = 0;
|
var length = 0;
|
||||||
@@ -33901,8 +33901,8 @@ var require_semver3 = __commonJS({
|
|||||||
function neq(a, b, loose) {
|
function neq(a, b, loose) {
|
||||||
return compare3(a, b, loose) !== 0;
|
return compare3(a, b, loose) !== 0;
|
||||||
}
|
}
|
||||||
exports2.gte = gte7;
|
exports2.gte = gte8;
|
||||||
function gte7(a, b, loose) {
|
function gte8(a, b, loose) {
|
||||||
return compare3(a, b, loose) >= 0;
|
return compare3(a, b, loose) >= 0;
|
||||||
}
|
}
|
||||||
exports2.lte = lte2;
|
exports2.lte = lte2;
|
||||||
@@ -33933,7 +33933,7 @@ var require_semver3 = __commonJS({
|
|||||||
case ">":
|
case ">":
|
||||||
return gt(a, b, loose);
|
return gt(a, b, loose);
|
||||||
case ">=":
|
case ">=":
|
||||||
return gte7(a, b, loose);
|
return gte8(a, b, loose);
|
||||||
case "<":
|
case "<":
|
||||||
return lt2(a, b, loose);
|
return lt2(a, b, loose);
|
||||||
case "<=":
|
case "<=":
|
||||||
@@ -34478,7 +34478,7 @@ var require_semver3 = __commonJS({
|
|||||||
break;
|
break;
|
||||||
case "<":
|
case "<":
|
||||||
gtfn = lt2;
|
gtfn = lt2;
|
||||||
ltefn = gte7;
|
ltefn = gte8;
|
||||||
ltfn = gt;
|
ltfn = gt;
|
||||||
comp = "<";
|
comp = "<";
|
||||||
ecomp = "<=";
|
ecomp = "<=";
|
||||||
@@ -34699,7 +34699,7 @@ var require_cacheUtils = __commonJS({
|
|||||||
var crypto3 = __importStar2(require("crypto"));
|
var crypto3 = __importStar2(require("crypto"));
|
||||||
var fs32 = __importStar2(require("fs"));
|
var fs32 = __importStar2(require("fs"));
|
||||||
var path30 = __importStar2(require("path"));
|
var path30 = __importStar2(require("path"));
|
||||||
var semver11 = __importStar2(require_semver3());
|
var semver12 = __importStar2(require_semver3());
|
||||||
var util3 = __importStar2(require("util"));
|
var util3 = __importStar2(require("util"));
|
||||||
var constants_1 = require_constants7();
|
var constants_1 = require_constants7();
|
||||||
var versionSalt = "1.0";
|
var versionSalt = "1.0";
|
||||||
@@ -34792,7 +34792,7 @@ var require_cacheUtils = __commonJS({
|
|||||||
function getCompressionMethod() {
|
function getCompressionMethod() {
|
||||||
return __awaiter2(this, void 0, void 0, function* () {
|
return __awaiter2(this, void 0, void 0, function* () {
|
||||||
const versionOutput = yield getVersion("zstd", ["--quiet"]);
|
const versionOutput = yield getVersion("zstd", ["--quiet"]);
|
||||||
const version = semver11.clean(versionOutput);
|
const version = semver12.clean(versionOutput);
|
||||||
core32.debug(`zstd version: ${version}`);
|
core32.debug(`zstd version: ${version}`);
|
||||||
if (versionOutput === "") {
|
if (versionOutput === "") {
|
||||||
return constants_1.CompressionMethod.Gzip;
|
return constants_1.CompressionMethod.Gzip;
|
||||||
@@ -82401,7 +82401,7 @@ var require_manifest = __commonJS({
|
|||||||
exports2._findMatch = _findMatch;
|
exports2._findMatch = _findMatch;
|
||||||
exports2._getOsVersion = _getOsVersion;
|
exports2._getOsVersion = _getOsVersion;
|
||||||
exports2._readLinuxVersionFile = _readLinuxVersionFile;
|
exports2._readLinuxVersionFile = _readLinuxVersionFile;
|
||||||
var semver11 = __importStar2(require_semver2());
|
var semver12 = __importStar2(require_semver2());
|
||||||
var core_1 = require_core();
|
var core_1 = require_core();
|
||||||
var os7 = require("os");
|
var os7 = require("os");
|
||||||
var cp = require("child_process");
|
var cp = require("child_process");
|
||||||
@@ -82415,7 +82415,7 @@ var require_manifest = __commonJS({
|
|||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
const version = candidate.version;
|
const version = candidate.version;
|
||||||
(0, core_1.debug)(`check ${version} satisfies ${versionSpec}`);
|
(0, core_1.debug)(`check ${version} satisfies ${versionSpec}`);
|
||||||
if (semver11.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) {
|
if (semver12.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) {
|
||||||
file = candidate.files.find((item) => {
|
file = candidate.files.find((item) => {
|
||||||
(0, core_1.debug)(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`);
|
(0, core_1.debug)(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`);
|
||||||
let chk = item.arch === archFilter && item.platform === platFilter;
|
let chk = item.arch === archFilter && item.platform === platFilter;
|
||||||
@@ -82424,7 +82424,7 @@ var require_manifest = __commonJS({
|
|||||||
if (osVersion === item.platform_version) {
|
if (osVersion === item.platform_version) {
|
||||||
chk = true;
|
chk = true;
|
||||||
} else {
|
} else {
|
||||||
chk = semver11.satisfies(osVersion, item.platform_version);
|
chk = semver12.satisfies(osVersion, item.platform_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chk;
|
return chk;
|
||||||
@@ -82684,7 +82684,7 @@ var require_tool_cache = __commonJS({
|
|||||||
var os7 = __importStar2(require("os"));
|
var os7 = __importStar2(require("os"));
|
||||||
var path30 = __importStar2(require("path"));
|
var path30 = __importStar2(require("path"));
|
||||||
var httpm = __importStar2(require_lib());
|
var httpm = __importStar2(require_lib());
|
||||||
var semver11 = __importStar2(require_semver2());
|
var semver12 = __importStar2(require_semver2());
|
||||||
var stream2 = __importStar2(require("stream"));
|
var stream2 = __importStar2(require("stream"));
|
||||||
var util3 = __importStar2(require("util"));
|
var util3 = __importStar2(require("util"));
|
||||||
var assert_1 = require("assert");
|
var assert_1 = require("assert");
|
||||||
@@ -82957,7 +82957,7 @@ var require_tool_cache = __commonJS({
|
|||||||
}
|
}
|
||||||
function cacheDir2(sourceDir, tool, version, arch2) {
|
function cacheDir2(sourceDir, tool, version, arch2) {
|
||||||
return __awaiter2(this, void 0, void 0, function* () {
|
return __awaiter2(this, void 0, void 0, function* () {
|
||||||
version = semver11.clean(version) || version;
|
version = semver12.clean(version) || version;
|
||||||
arch2 = arch2 || os7.arch();
|
arch2 = arch2 || os7.arch();
|
||||||
core32.debug(`Caching tool ${tool} ${version} ${arch2}`);
|
core32.debug(`Caching tool ${tool} ${version} ${arch2}`);
|
||||||
core32.debug(`source dir: ${sourceDir}`);
|
core32.debug(`source dir: ${sourceDir}`);
|
||||||
@@ -82975,7 +82975,7 @@ var require_tool_cache = __commonJS({
|
|||||||
}
|
}
|
||||||
function cacheFile(sourceFile, targetFile, tool, version, arch2) {
|
function cacheFile(sourceFile, targetFile, tool, version, arch2) {
|
||||||
return __awaiter2(this, void 0, void 0, function* () {
|
return __awaiter2(this, void 0, void 0, function* () {
|
||||||
version = semver11.clean(version) || version;
|
version = semver12.clean(version) || version;
|
||||||
arch2 = arch2 || os7.arch();
|
arch2 = arch2 || os7.arch();
|
||||||
core32.debug(`Caching tool ${tool} ${version} ${arch2}`);
|
core32.debug(`Caching tool ${tool} ${version} ${arch2}`);
|
||||||
core32.debug(`source file: ${sourceFile}`);
|
core32.debug(`source file: ${sourceFile}`);
|
||||||
@@ -83005,7 +83005,7 @@ var require_tool_cache = __commonJS({
|
|||||||
}
|
}
|
||||||
let toolPath = "";
|
let toolPath = "";
|
||||||
if (versionSpec) {
|
if (versionSpec) {
|
||||||
versionSpec = semver11.clean(versionSpec) || "";
|
versionSpec = semver12.clean(versionSpec) || "";
|
||||||
const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2);
|
const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2);
|
||||||
core32.debug(`checking cache: ${cachePath}`);
|
core32.debug(`checking cache: ${cachePath}`);
|
||||||
if (fs32.existsSync(cachePath) && fs32.existsSync(`${cachePath}.complete`)) {
|
if (fs32.existsSync(cachePath) && fs32.existsSync(`${cachePath}.complete`)) {
|
||||||
@@ -83085,7 +83085,7 @@ var require_tool_cache = __commonJS({
|
|||||||
}
|
}
|
||||||
function _createToolPath(tool, version, arch2) {
|
function _createToolPath(tool, version, arch2) {
|
||||||
return __awaiter2(this, void 0, void 0, function* () {
|
return __awaiter2(this, void 0, void 0, function* () {
|
||||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || "");
|
const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || "");
|
||||||
core32.debug(`destination ${folderPath}`);
|
core32.debug(`destination ${folderPath}`);
|
||||||
const markerPath = `${folderPath}.complete`;
|
const markerPath = `${folderPath}.complete`;
|
||||||
yield io9.rmRF(folderPath);
|
yield io9.rmRF(folderPath);
|
||||||
@@ -83095,15 +83095,15 @@ var require_tool_cache = __commonJS({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
function _completeToolPath(tool, version, arch2) {
|
function _completeToolPath(tool, version, arch2) {
|
||||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || "");
|
const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || "");
|
||||||
const markerPath = `${folderPath}.complete`;
|
const markerPath = `${folderPath}.complete`;
|
||||||
fs32.writeFileSync(markerPath, "");
|
fs32.writeFileSync(markerPath, "");
|
||||||
core32.debug("finished caching tool");
|
core32.debug("finished caching tool");
|
||||||
}
|
}
|
||||||
function isExplicitVersion(versionSpec) {
|
function isExplicitVersion(versionSpec) {
|
||||||
const c = semver11.clean(versionSpec) || "";
|
const c = semver12.clean(versionSpec) || "";
|
||||||
core32.debug(`isExplicit: ${c}`);
|
core32.debug(`isExplicit: ${c}`);
|
||||||
const valid4 = semver11.valid(c) != null;
|
const valid4 = semver12.valid(c) != null;
|
||||||
core32.debug(`explicit? ${valid4}`);
|
core32.debug(`explicit? ${valid4}`);
|
||||||
return valid4;
|
return valid4;
|
||||||
}
|
}
|
||||||
@@ -83111,14 +83111,14 @@ var require_tool_cache = __commonJS({
|
|||||||
let version = "";
|
let version = "";
|
||||||
core32.debug(`evaluating ${versions.length} versions`);
|
core32.debug(`evaluating ${versions.length} versions`);
|
||||||
versions = versions.sort((a, b) => {
|
versions = versions.sort((a, b) => {
|
||||||
if (semver11.gt(a, b)) {
|
if (semver12.gt(a, b)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
});
|
});
|
||||||
for (let i = versions.length - 1; i >= 0; i--) {
|
for (let i = versions.length - 1; i >= 0; i--) {
|
||||||
const potential = versions[i];
|
const potential = versions[i];
|
||||||
const satisfied = semver11.satisfies(potential, versionSpec);
|
const satisfied = semver12.satisfies(potential, versionSpec);
|
||||||
if (satisfied) {
|
if (satisfied) {
|
||||||
version = potential;
|
version = potential;
|
||||||
break;
|
break;
|
||||||
@@ -89595,7 +89595,7 @@ var require_brace_expansion2 = __commonJS({
|
|||||||
function lte2(i, y) {
|
function lte2(i, y) {
|
||||||
return i <= y;
|
return i <= y;
|
||||||
}
|
}
|
||||||
function gte7(i, y) {
|
function gte8(i, y) {
|
||||||
return i >= y;
|
return i >= y;
|
||||||
}
|
}
|
||||||
function combine2(acc, pre, values, max, maxLength, dropEmpties) {
|
function combine2(acc, pre, values, max, maxLength, dropEmpties) {
|
||||||
@@ -89627,7 +89627,7 @@ var require_brace_expansion2 = __commonJS({
|
|||||||
var reverse = y < x;
|
var reverse = y < x;
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
incr *= -1;
|
incr *= -1;
|
||||||
test = gte7;
|
test = gte8;
|
||||||
}
|
}
|
||||||
var pad = n.some(isPadded2);
|
var pad = n.some(isPadded2);
|
||||||
var length = 0;
|
var length = 0;
|
||||||
@@ -148091,6 +148091,11 @@ var featureConfig = {
|
|||||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS",
|
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS",
|
||||||
minimumVersion: void 0
|
minimumVersion: void 0
|
||||||
},
|
},
|
||||||
|
["per_language_bundles" /* PerLanguageBundles */]: {
|
||||||
|
defaultValue: false,
|
||||||
|
envVar: "CODEQL_ACTION_PER_LANGUAGE_BUNDLES",
|
||||||
|
minimumVersion: void 0
|
||||||
|
},
|
||||||
["qa_telemetry_enabled" /* QaTelemetryEnabled */]: {
|
["qa_telemetry_enabled" /* QaTelemetryEnabled */]: {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
envVar: "CODEQL_ACTION_QA_TELEMETRY",
|
envVar: "CODEQL_ACTION_QA_TELEMETRY",
|
||||||
@@ -151192,7 +151197,7 @@ var path13 = __toESM(require("path"));
|
|||||||
var core12 = __toESM(require_core());
|
var core12 = __toESM(require_core());
|
||||||
var toolcache3 = __toESM(require_tool_cache());
|
var toolcache3 = __toESM(require_tool_cache());
|
||||||
var import_fast_deep_equal = __toESM(require_fast_deep_equal());
|
var import_fast_deep_equal = __toESM(require_fast_deep_equal());
|
||||||
var semver9 = __toESM(require_semver2());
|
var semver10 = __toESM(require_semver2());
|
||||||
|
|
||||||
// src/overlay/caching.ts
|
// src/overlay/caching.ts
|
||||||
var fs11 = __toESM(require("fs"));
|
var fs11 = __toESM(require("fs"));
|
||||||
@@ -151492,6 +151497,89 @@ async function getCodeQlVersionsForOverlayBaseDatabases(rawLanguages, logger) {
|
|||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/per-language-bundles.ts
|
||||||
|
var semver7 = __toESM(require_semver2());
|
||||||
|
var MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION = "2.27.1";
|
||||||
|
var PER_LANGUAGE_BUNDLE_NAME = /^codeql-bundle-(.+)-(?:linux64|osx64|win64)\.tar\.(?:gz|zst)$/;
|
||||||
|
function tryGetBundleLanguageFromUrl(url2) {
|
||||||
|
let assetName;
|
||||||
|
try {
|
||||||
|
const pathname = new URL(url2).pathname;
|
||||||
|
assetName = decodeURIComponent(pathname.split("/").pop() ?? "");
|
||||||
|
} catch {
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
const match2 = assetName.match(PER_LANGUAGE_BUNDLE_NAME);
|
||||||
|
return match2 ? parseBuiltInLanguage(match2[1]) : void 0;
|
||||||
|
}
|
||||||
|
var PER_LANGUAGE_BUNDLE_PLATFORMS = {
|
||||||
|
["actions" /* actions */]: "linux64",
|
||||||
|
["cpp" /* cpp */]: "linux64",
|
||||||
|
["csharp" /* csharp */]: "linux64",
|
||||||
|
["go" /* go */]: "linux64",
|
||||||
|
["java" /* java */]: "linux64",
|
||||||
|
["javascript" /* javascript */]: "linux64",
|
||||||
|
["python" /* python */]: "linux64",
|
||||||
|
["ruby" /* ruby */]: "linux64",
|
||||||
|
["rust" /* rust */]: "linux64",
|
||||||
|
["swift" /* swift */]: "osx64"
|
||||||
|
};
|
||||||
|
async function getPerLanguageBundleLanguage(options, features, logger) {
|
||||||
|
const {
|
||||||
|
rawLanguages,
|
||||||
|
cliVersion: cliVersion2,
|
||||||
|
compressionMethod,
|
||||||
|
platform: platform2,
|
||||||
|
variant,
|
||||||
|
isNightly
|
||||||
|
} = options;
|
||||||
|
const explain = (reason) => {
|
||||||
|
logger.debug(`Not using a per-language CodeQL bundle since ${reason}.`);
|
||||||
|
return void 0;
|
||||||
|
};
|
||||||
|
if (rawLanguages?.length !== 1) {
|
||||||
|
return explain(
|
||||||
|
`exactly one language must be requested via the 'languages' input, but ${rawLanguages?.length ?? 0} were`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const language = parseBuiltInLanguage(rawLanguages[0]);
|
||||||
|
if (language === void 0) {
|
||||||
|
return explain(`'${rawLanguages[0]}' is not a known CodeQL language`);
|
||||||
|
}
|
||||||
|
if (compressionMethod !== "zstd") {
|
||||||
|
return explain(`the bundle would be downloaded as ${compressionMethod}`);
|
||||||
|
}
|
||||||
|
if (variant !== "GitHub.com" /* DOTCOM */) {
|
||||||
|
return explain(`we are running against ${variant}`);
|
||||||
|
}
|
||||||
|
if (!isGitHubHostedRunner()) {
|
||||||
|
return explain("the job is not running on a GitHub-hosted runner");
|
||||||
|
}
|
||||||
|
if (!isNightly) {
|
||||||
|
if (cliVersion2 === void 0) {
|
||||||
|
return explain("the CLI version of the bundle is unknown");
|
||||||
|
}
|
||||||
|
if (!semver7.gte(cliVersion2, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION)) {
|
||||||
|
return explain(
|
||||||
|
`CodeQL ${cliVersion2} is older than ${MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION}, which is the first version that publishes per-language bundles`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const supportedPlatform = PER_LANGUAGE_BUNDLE_PLATFORMS[language];
|
||||||
|
if (supportedPlatform === void 0) {
|
||||||
|
return explain(`no per-language bundle is published for ${language}`);
|
||||||
|
}
|
||||||
|
if (supportedPlatform !== platform2) {
|
||||||
|
return explain(
|
||||||
|
`the ${language} bundle is only published for ${supportedPlatform}, but this job is running on ${platform2 ?? "an unknown platform"}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!await features.getValue("per_language_bundles" /* PerLanguageBundles */)) {
|
||||||
|
return explain(`the ${"per_language_bundles" /* PerLanguageBundles */} feature is disabled`);
|
||||||
|
}
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
// src/tar.ts
|
// src/tar.ts
|
||||||
var import_child_process = require("child_process");
|
var import_child_process = require("child_process");
|
||||||
var fs12 = __toESM(require("fs"));
|
var fs12 = __toESM(require("fs"));
|
||||||
@@ -151499,7 +151587,7 @@ var stream = __toESM(require("stream"));
|
|||||||
var import_toolrunner = __toESM(require_toolrunner());
|
var import_toolrunner = __toESM(require_toolrunner());
|
||||||
var io4 = __toESM(require_io());
|
var io4 = __toESM(require_io());
|
||||||
var toolcache = __toESM(require_tool_cache());
|
var toolcache = __toESM(require_tool_cache());
|
||||||
var semver7 = __toESM(require_semver2());
|
var semver8 = __toESM(require_semver2());
|
||||||
var MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
|
var MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
|
||||||
var MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
var MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
||||||
async function getTarVersion() {
|
async function getTarVersion() {
|
||||||
@@ -151541,9 +151629,9 @@ async function isZstdAvailable(logger) {
|
|||||||
case "gnu":
|
case "gnu":
|
||||||
return {
|
return {
|
||||||
available: foundZstdBinary && // GNU tar only uses major and minor version numbers
|
available: foundZstdBinary && // GNU tar only uses major and minor version numbers
|
||||||
semver7.gte(
|
semver8.gte(
|
||||||
semver7.coerce(version),
|
semver8.coerce(version),
|
||||||
semver7.coerce(MIN_REQUIRED_GNU_TAR_VERSION)
|
semver8.coerce(MIN_REQUIRED_GNU_TAR_VERSION)
|
||||||
),
|
),
|
||||||
foundZstdBinary,
|
foundZstdBinary,
|
||||||
version: tarVersion
|
version: tarVersion
|
||||||
@@ -151552,7 +151640,7 @@ async function isZstdAvailable(logger) {
|
|||||||
return {
|
return {
|
||||||
available: foundZstdBinary && // Do a loose comparison since these version numbers don't contain
|
available: foundZstdBinary && // Do a loose comparison since these version numbers don't contain
|
||||||
// a patch version number.
|
// a patch version number.
|
||||||
semver7.gte(version, MIN_REQUIRED_BSD_TAR_VERSION),
|
semver8.gte(version, MIN_REQUIRED_BSD_TAR_VERSION),
|
||||||
foundZstdBinary,
|
foundZstdBinary,
|
||||||
version: tarVersion
|
version: tarVersion
|
||||||
};
|
};
|
||||||
@@ -151661,7 +151749,7 @@ var core11 = __toESM(require_core());
|
|||||||
var import_http_client = __toESM(require_lib());
|
var import_http_client = __toESM(require_lib());
|
||||||
var toolcache2 = __toESM(require_tool_cache());
|
var toolcache2 = __toESM(require_tool_cache());
|
||||||
var import_follow_redirects = __toESM(require_follow_redirects());
|
var import_follow_redirects = __toESM(require_follow_redirects());
|
||||||
var semver8 = __toESM(require_semver2());
|
var semver9 = __toESM(require_semver2());
|
||||||
var STREAMING_HIGH_WATERMARK_BYTES = 4 * 1024 * 1024;
|
var STREAMING_HIGH_WATERMARK_BYTES = 4 * 1024 * 1024;
|
||||||
var STREAMING_STALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
var STREAMING_STALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
||||||
var TOOLCACHE_TOOL_NAME = "CodeQL";
|
var TOOLCACHE_TOOL_NAME = "CodeQL";
|
||||||
@@ -151787,7 +151875,7 @@ function getToolcacheToolDirectory(env) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
function getToolcacheVersionDirectoryName(version) {
|
function getToolcacheVersionDirectoryName(version) {
|
||||||
return semver8.clean(version) || version;
|
return semver9.clean(version) || version;
|
||||||
}
|
}
|
||||||
function getToolcacheDirectory(version) {
|
function getToolcacheDirectory(version) {
|
||||||
return path12.join(
|
return path12.join(
|
||||||
@@ -151899,18 +151987,27 @@ function getCodeQLBundleExtension(compressionMethod) {
|
|||||||
assertNever(compressionMethod);
|
assertNever(compressionMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getCodeQLBundleName(compressionMethod) {
|
function getBundlePlatform() {
|
||||||
|
switch (process.platform) {
|
||||||
|
case "win32":
|
||||||
|
return "win64";
|
||||||
|
case "linux":
|
||||||
|
return process.arch === "arm64" ? "linux-arm64" : "linux64";
|
||||||
|
case "darwin":
|
||||||
|
return "osx64";
|
||||||
|
default:
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getCodeQLBundleName(compressionMethod, language) {
|
||||||
const extension = getCodeQLBundleExtension(compressionMethod);
|
const extension = getCodeQLBundleExtension(compressionMethod);
|
||||||
let platform2;
|
const platform2 = getBundlePlatform();
|
||||||
if (process.platform === "win32") {
|
if (platform2 === void 0) {
|
||||||
platform2 = "win64";
|
|
||||||
} else if (process.platform === "linux") {
|
|
||||||
platform2 = process.arch === "arm64" ? "linux-arm64" : "linux64";
|
|
||||||
} else if (process.platform === "darwin") {
|
|
||||||
platform2 = "osx64";
|
|
||||||
} else {
|
|
||||||
return `codeql-bundle${extension}`;
|
return `codeql-bundle${extension}`;
|
||||||
}
|
}
|
||||||
|
if (language !== void 0) {
|
||||||
|
return `codeql-bundle-${language}-${platform2}${extension}`;
|
||||||
|
}
|
||||||
return `codeql-bundle-${platform2}${extension}`;
|
return `codeql-bundle-${platform2}${extension}`;
|
||||||
}
|
}
|
||||||
function getCodeQLActionRepository(logger) {
|
function getCodeQLActionRepository(logger) {
|
||||||
@@ -151922,7 +152019,7 @@ function getCodeQLActionRepository(logger) {
|
|||||||
}
|
}
|
||||||
return getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
|
return getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
|
||||||
}
|
}
|
||||||
async function getCodeQLBundleDownloadURL(tagName, apiDetails, compressionMethod, logger) {
|
async function getCodeQLBundleDownloadURL(tagName, apiDetails, codeQLBundleName, logger) {
|
||||||
const codeQLActionRepository = getCodeQLActionRepository(logger);
|
const codeQLActionRepository = getCodeQLActionRepository(logger);
|
||||||
const potentialDownloadSources = [
|
const potentialDownloadSources = [
|
||||||
// This GitHub instance, and this Action.
|
// This GitHub instance, and this Action.
|
||||||
@@ -151937,7 +152034,6 @@ async function getCodeQLBundleDownloadURL(tagName, apiDetails, compressionMethod
|
|||||||
return !self2.slice(0, index2).some((other) => (0, import_fast_deep_equal.default)(source, other));
|
return !self2.slice(0, index2).some((other) => (0, import_fast_deep_equal.default)(source, other));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const codeQLBundleName = getCodeQLBundleName(compressionMethod);
|
|
||||||
for (const downloadSource of uniqueDownloadSources) {
|
for (const downloadSource of uniqueDownloadSources) {
|
||||||
const [apiURL, repository] = downloadSource;
|
const [apiURL, repository] = downloadSource;
|
||||||
if (apiURL === GITHUB_DOTCOM_URL && repository === CODEQL_DEFAULT_ACTION_REPOSITORY) {
|
if (apiURL === GITHUB_DOTCOM_URL && repository === CODEQL_DEFAULT_ACTION_REPOSITORY) {
|
||||||
@@ -151992,13 +152088,13 @@ function tryGetTagNameFromUrl(url2, logger) {
|
|||||||
return match2[1];
|
return match2[1];
|
||||||
}
|
}
|
||||||
function convertToSemVer(version, logger) {
|
function convertToSemVer(version, logger) {
|
||||||
if (!semver9.valid(version)) {
|
if (!semver10.valid(version)) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Bundle version ${version} is not in SemVer format. Will treat it as pre-release 0.0.0-${version}.`
|
`Bundle version ${version} is not in SemVer format. Will treat it as pre-release 0.0.0-${version}.`
|
||||||
);
|
);
|
||||||
version = `0.0.0-${version}`;
|
version = `0.0.0-${version}`;
|
||||||
}
|
}
|
||||||
const s = semver9.clean(version);
|
const s = semver10.clean(version);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
throw new Error(`Bundle version ${version} is not in SemVer format.`);
|
throw new Error(`Bundle version ${version} is not in SemVer format.`);
|
||||||
}
|
}
|
||||||
@@ -152126,6 +152222,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
|
|||||||
let cliVersion2;
|
let cliVersion2;
|
||||||
let tagName;
|
let tagName;
|
||||||
let url2;
|
let url2;
|
||||||
|
let bundle;
|
||||||
const canForceNightlyWithFF = isDynamicWorkflow() || isInTestMode();
|
const canForceNightlyWithFF = isDynamicWorkflow() || isInTestMode();
|
||||||
const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */);
|
const forceNightlyValueFF = await features.getValue("force_nightly" /* ForceNightly */);
|
||||||
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
|
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
|
||||||
@@ -152156,7 +152253,8 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
|
|||||||
`Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`
|
`Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
toolsInput = await getNightlyToolsUrl(logger);
|
bundle = await getNightlyBundle(rawLanguages, variant, features, logger);
|
||||||
|
toolsInput = bundle.url;
|
||||||
}
|
}
|
||||||
const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput);
|
const forceShippedTools = toolsInput && CODEQL_BUNDLE_VERSION_ALIAS.includes(toolsInput);
|
||||||
if (forceShippedTools) {
|
if (forceShippedTools) {
|
||||||
@@ -152207,7 +152305,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
|
|||||||
url2 = toolsInput;
|
url2 = toolsInput;
|
||||||
if (tagName) {
|
if (tagName) {
|
||||||
const bundleVersion3 = tryGetBundleVersionFromTagName(tagName, logger);
|
const bundleVersion3 = tryGetBundleVersionFromTagName(tagName, logger);
|
||||||
if (bundleVersion3 !== void 0 && semver9.valid(bundleVersion3)) {
|
if (bundleVersion3 !== void 0 && semver10.valid(bundleVersion3)) {
|
||||||
cliVersion2 = convertToSemVer(bundleVersion3, logger);
|
cliVersion2 = convertToSemVer(bundleVersion3, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152310,12 +152408,38 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
|
|||||||
let compressionMethod;
|
let compressionMethod;
|
||||||
if (!url2) {
|
if (!url2) {
|
||||||
compressionMethod = cliVersion2 !== void 0 && await useZstdBundle(cliVersion2, tarSupportsZstd) ? "zstd" : "gzip";
|
compressionMethod = cliVersion2 !== void 0 && await useZstdBundle(cliVersion2, tarSupportsZstd) ? "zstd" : "gzip";
|
||||||
url2 = await getCodeQLBundleDownloadURL(
|
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
|
||||||
tagName,
|
{
|
||||||
apiDetails,
|
rawLanguages,
|
||||||
compressionMethod,
|
cliVersion: cliVersion2,
|
||||||
|
compressionMethod,
|
||||||
|
platform: getBundlePlatform(),
|
||||||
|
variant
|
||||||
|
},
|
||||||
|
features,
|
||||||
logger
|
logger
|
||||||
);
|
);
|
||||||
|
const resolveBundleURL = (language) => getCodeQLBundleDownloadURL(
|
||||||
|
tagName,
|
||||||
|
apiDetails,
|
||||||
|
getCodeQLBundleName(compressionMethod, language),
|
||||||
|
logger
|
||||||
|
);
|
||||||
|
if (perLanguageBundleLanguage !== void 0) {
|
||||||
|
logger.info(
|
||||||
|
`Downloading the ${perLanguageBundleLanguage} CodeQL bundle, since ${perLanguageBundleLanguage} is the only language being analyzed.`
|
||||||
|
);
|
||||||
|
url2 = await resolveBundleURL(perLanguageBundleLanguage);
|
||||||
|
bundle = {
|
||||||
|
kind: "per-language",
|
||||||
|
url: url2,
|
||||||
|
language: perLanguageBundleLanguage,
|
||||||
|
combinedBundleURL: await resolveBundleURL()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
url2 = await resolveBundleURL();
|
||||||
|
bundle = { kind: "combined", url: url2 };
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const method = inferCompressionMethod(url2);
|
const method = inferCompressionMethod(url2);
|
||||||
if (method === void 0) {
|
if (method === void 0) {
|
||||||
@@ -152324,6 +152448,15 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
compressionMethod = method;
|
compressionMethod = method;
|
||||||
|
if (bundle === void 0) {
|
||||||
|
const language = tryGetBundleLanguageFromUrl(url2);
|
||||||
|
bundle = language === void 0 ? { kind: "combined", url: url2 } : { kind: "per-language", url: url2, language };
|
||||||
|
}
|
||||||
|
if (bundle.kind === "per-language") {
|
||||||
|
logger.info(
|
||||||
|
`${url2} appears to be a CodeQL bundle that contains only ${bundle.language}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cliVersion2) {
|
if (cliVersion2) {
|
||||||
logger.info(`Using CodeQL CLI version ${cliVersion2} sourced from ${url2} .`);
|
logger.info(`Using CodeQL CLI version ${cliVersion2} sourced from ${url2} .`);
|
||||||
@@ -152331,7 +152464,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO
|
|||||||
logger.info(`Using CodeQL CLI sourced from ${url2} .`);
|
logger.info(`Using CodeQL CLI sourced from ${url2} .`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
bundle: { kind: "combined", url: url2 },
|
bundle,
|
||||||
bundleVersion: bundleVersion2,
|
bundleVersion: bundleVersion2,
|
||||||
cliVersion: cliVersion2,
|
cliVersion: cliVersion2,
|
||||||
compressionMethod,
|
compressionMethod,
|
||||||
@@ -152383,7 +152516,7 @@ var downloadCodeQL = async function(source, apiDetails, tarVersion, tempDir, log
|
|||||||
writeToolcacheMarkerFile(toolcacheDestination, logger);
|
writeToolcacheMarkerFile(toolcacheDestination, logger);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Could not cache CodeQL tools because we could not determine the bundle version from the URL ${codeqlURL}.`
|
bundle.kind === "per-language" ? "Not caching the CodeQL tools because they came from a bundle that contains only a single language." : `Could not cache CodeQL tools because we could not determine the bundle version from the URL ${codeqlURL}.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -152392,7 +152525,7 @@ var downloadCodeQL = async function(source, apiDetails, tarVersion, tempDir, log
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
function getToolcacheDestination(source, logger) {
|
function getToolcacheDestination(source, logger) {
|
||||||
if (!source.bundleVersion) {
|
if (source.bundle.kind !== "combined" || !source.bundleVersion) {
|
||||||
return void 0;
|
return void 0;
|
||||||
}
|
}
|
||||||
return getToolcacheDirectory(
|
return getToolcacheDirectory(
|
||||||
@@ -152496,30 +152629,77 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
async function downloadCodeQLBundle(action, source, apiDetails, tarVersion, tempDir) {
|
async function downloadCodeQLBundle(action, source, apiDetails, tarVersion, tempDir) {
|
||||||
|
const { bundle } = source;
|
||||||
|
const { logger } = action;
|
||||||
await tryDeleteToolcacheBundles(action);
|
await tryDeleteToolcacheBundles(action);
|
||||||
return await downloadCodeQL(
|
try {
|
||||||
source,
|
const result = await downloadCodeQL(
|
||||||
apiDetails,
|
source,
|
||||||
tarVersion,
|
apiDetails,
|
||||||
tempDir,
|
tarVersion,
|
||||||
action.logger
|
tempDir,
|
||||||
);
|
logger
|
||||||
|
);
|
||||||
|
return bundle.kind === "combined" ? result : {
|
||||||
|
...result,
|
||||||
|
statusReport: {
|
||||||
|
...result.statusReport,
|
||||||
|
bundleLanguage: bundle.language
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (bundle.kind !== "per-language" || bundle.combinedBundleURL === void 0 || asHTTPError(e)?.status !== 404) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
logger.warning(
|
||||||
|
`No ${bundle.language} CodeQL bundle was found at ${bundle.url}, so falling back to the bundle that contains all languages. This analysis will still produce correct results, but will take longer to set up.`
|
||||||
|
);
|
||||||
|
const result = await downloadCodeQL(
|
||||||
|
{
|
||||||
|
...source,
|
||||||
|
bundle: { kind: "combined", url: bundle.combinedBundleURL }
|
||||||
|
},
|
||||||
|
apiDetails,
|
||||||
|
tarVersion,
|
||||||
|
tempDir,
|
||||||
|
logger
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
statusReport: {
|
||||||
|
...result.statusReport,
|
||||||
|
perLanguageBundleFallback: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function useZstdBundle(cliVersion2, tarSupportsZstd) {
|
async function useZstdBundle(cliVersion2, tarSupportsZstd) {
|
||||||
return (
|
return (
|
||||||
// In testing, gzip performs better than zstd on Windows.
|
// In testing, gzip performs better than zstd on Windows.
|
||||||
process.platform !== "win32" && tarSupportsZstd && semver9.gte(cliVersion2, CODEQL_VERSION_ZSTD_BUNDLE)
|
process.platform !== "win32" && tarSupportsZstd && semver10.gte(cliVersion2, CODEQL_VERSION_ZSTD_BUNDLE)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function getTempExtractionDir(tempDir) {
|
function getTempExtractionDir(tempDir) {
|
||||||
return path13.join(tempDir, v4_default());
|
return path13.join(tempDir, v4_default());
|
||||||
}
|
}
|
||||||
async function getNightlyToolsUrl(logger) {
|
async function getNightlyBundle(rawLanguages, variant, features, logger) {
|
||||||
const zstdAvailability = await isZstdAvailable(logger);
|
const zstdAvailability = await isZstdAvailable(logger);
|
||||||
const compressionMethod = await useZstdBundle(
|
const compressionMethod = await useZstdBundle(
|
||||||
CODEQL_VERSION_ZSTD_BUNDLE,
|
CODEQL_VERSION_ZSTD_BUNDLE,
|
||||||
zstdAvailability.available
|
zstdAvailability.available
|
||||||
) ? "zstd" : "gzip";
|
) ? "zstd" : "gzip";
|
||||||
|
const language = await getPerLanguageBundleLanguage(
|
||||||
|
{
|
||||||
|
rawLanguages,
|
||||||
|
cliVersion: void 0,
|
||||||
|
compressionMethod,
|
||||||
|
platform: getBundlePlatform(),
|
||||||
|
variant,
|
||||||
|
isNightly: true
|
||||||
|
},
|
||||||
|
features,
|
||||||
|
logger
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
const release2 = await getApiClient().rest.repos.listReleases({
|
const release2 = await getApiClient().rest.repos.listReleases({
|
||||||
owner: CODEQL_NIGHTLIES_REPOSITORY_OWNER,
|
owner: CODEQL_NIGHTLIES_REPOSITORY_OWNER,
|
||||||
@@ -152532,7 +152712,14 @@ async function getNightlyToolsUrl(logger) {
|
|||||||
if (!latestRelease) {
|
if (!latestRelease) {
|
||||||
throw new Error("Could not find the latest nightly release.");
|
throw new Error("Could not find the latest nightly release.");
|
||||||
}
|
}
|
||||||
return `https://github.com/${CODEQL_NIGHTLIES_REPOSITORY_OWNER}/${CODEQL_NIGHTLIES_REPOSITORY_NAME}/releases/download/${latestRelease.tag_name}/${getCodeQLBundleName(compressionMethod)}`;
|
const assetUrl = (name) => `https://github.com/${CODEQL_NIGHTLIES_REPOSITORY_OWNER}/${CODEQL_NIGHTLIES_REPOSITORY_NAME}/releases/download/${latestRelease.tag_name}/${name}`;
|
||||||
|
const url2 = assetUrl(getCodeQLBundleName(compressionMethod, language));
|
||||||
|
return language === void 0 ? { kind: "combined", url: url2 } : {
|
||||||
|
kind: "per-language",
|
||||||
|
url: url2,
|
||||||
|
language,
|
||||||
|
combinedBundleURL: assetUrl(getCodeQLBundleName(compressionMethod))
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to retrieve the latest nightly release: ${wrapError(e)}`
|
`Failed to retrieve the latest nightly release: ${wrapError(e)}`
|
||||||
@@ -152540,7 +152727,7 @@ async function getNightlyToolsUrl(logger) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getLatestToolcacheVersion(logger) {
|
function getLatestToolcacheVersion(logger) {
|
||||||
const allVersions = toolcache3.findAllVersions("CodeQL").sort((a, b) => semver9.compare(b, a));
|
const allVersions = toolcache3.findAllVersions("CodeQL").sort((a, b) => semver10.compare(b, a));
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Found the following versions of the CodeQL tools in the toolcache: ${JSON.stringify(
|
`Found the following versions of the CodeQL tools in the toolcache: ${JSON.stringify(
|
||||||
allVersions
|
allVersions
|
||||||
@@ -156724,7 +156911,7 @@ function isPadded(el) {
|
|||||||
function lte(i, y) {
|
function lte(i, y) {
|
||||||
return i <= y;
|
return i <= y;
|
||||||
}
|
}
|
||||||
function gte6(i, y) {
|
function gte7(i, y) {
|
||||||
return i >= y;
|
return i >= y;
|
||||||
}
|
}
|
||||||
function combine(acc, pre, values, max, maxLength, dropEmpties) {
|
function combine(acc, pre, values, max, maxLength, dropEmpties) {
|
||||||
@@ -156759,7 +156946,7 @@ function expandSequence(body, isAlphaSequence, max, maxLength) {
|
|||||||
const reverse = y < x;
|
const reverse = y < x;
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
incr *= -1;
|
incr *= -1;
|
||||||
test = gte6;
|
test = gte7;
|
||||||
}
|
}
|
||||||
const pad = n.some(isPadded);
|
const pad = n.some(isPadded);
|
||||||
let length = 0;
|
let length = 0;
|
||||||
@@ -158656,7 +158843,7 @@ var import_async = __toESM(require_async(), 1);
|
|||||||
var import_path7 = require("path");
|
var import_path7 = require("path");
|
||||||
|
|
||||||
// node_modules/archiver/lib/error.js
|
// node_modules/archiver/lib/error.js
|
||||||
var import_util34 = __toESM(require("util"), 1);
|
var import_util35 = __toESM(require("util"), 1);
|
||||||
var ERROR_CODES = {
|
var ERROR_CODES = {
|
||||||
ABORTED: "archive was aborted",
|
ABORTED: "archive was aborted",
|
||||||
DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value",
|
DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value",
|
||||||
@@ -158681,7 +158868,7 @@ function ArchiverError(code, data) {
|
|||||||
this.code = code;
|
this.code = code;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
import_util34.default.inherits(ArchiverError, Error);
|
import_util35.default.inherits(ArchiverError, Error);
|
||||||
|
|
||||||
// node_modules/archiver/lib/core.js
|
// node_modules/archiver/lib/core.js
|
||||||
var import_readable_stream2 = __toESM(require_ours(), 1);
|
var import_readable_stream2 = __toESM(require_ours(), 1);
|
||||||
@@ -161613,7 +161800,7 @@ var fs29 = __toESM(require("fs"));
|
|||||||
var path25 = __toESM(require("path"));
|
var path25 = __toESM(require("path"));
|
||||||
var core22 = __toESM(require_core());
|
var core22 = __toESM(require_core());
|
||||||
var io7 = __toESM(require_io());
|
var io7 = __toESM(require_io());
|
||||||
var semver10 = __toESM(require_semver2());
|
var semver11 = __toESM(require_semver2());
|
||||||
|
|
||||||
// src/config/inputs.ts
|
// src/config/inputs.ts
|
||||||
async function getToolsInput(action, repositoryProperties) {
|
async function getToolsInput(action, repositoryProperties) {
|
||||||
@@ -161974,6 +162161,12 @@ async function sendCompletedStatusReport2(startedAt, config, configFile, toolsIn
|
|||||||
if (toolsDownloadStatusReport?.totalDurationMs !== void 0) {
|
if (toolsDownloadStatusReport?.totalDurationMs !== void 0) {
|
||||||
initToolsDownloadFields.tools_total_duration_ms = toolsDownloadStatusReport.totalDurationMs;
|
initToolsDownloadFields.tools_total_duration_ms = toolsDownloadStatusReport.totalDurationMs;
|
||||||
}
|
}
|
||||||
|
if (toolsDownloadStatusReport?.bundleLanguage !== void 0) {
|
||||||
|
initToolsDownloadFields.tools_bundle_language = toolsDownloadStatusReport.bundleLanguage;
|
||||||
|
}
|
||||||
|
if (toolsDownloadStatusReport?.perLanguageBundleFallback !== void 0) {
|
||||||
|
initToolsDownloadFields.tools_per_language_bundle_fallback = toolsDownloadStatusReport.perLanguageBundleFallback;
|
||||||
|
}
|
||||||
if (toolsFeatureFlagsValid !== void 0) {
|
if (toolsFeatureFlagsValid !== void 0) {
|
||||||
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
||||||
}
|
}
|
||||||
@@ -162093,12 +162286,12 @@ async function run3(actionState) {
|
|||||||
const experimental = "2.19.3";
|
const experimental = "2.19.3";
|
||||||
const publicPreview = "2.22.1";
|
const publicPreview = "2.22.1";
|
||||||
const actualVer = (await codeql.getVersion()).version;
|
const actualVer = (await codeql.getVersion()).version;
|
||||||
if (semver10.lt(actualVer, experimental)) {
|
if (semver11.lt(actualVer, experimental)) {
|
||||||
throw new ConfigurationError(
|
throw new ConfigurationError(
|
||||||
`Rust analysis is supported by CodeQL CLI version ${experimental} or higher, but found version ${actualVer}`
|
`Rust analysis is supported by CodeQL CLI version ${experimental} or higher, but found version ${actualVer}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (semver10.lt(actualVer, publicPreview)) {
|
if (semver11.lt(actualVer, publicPreview)) {
|
||||||
core22.exportVariable("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true");
|
core22.exportVariable("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true");
|
||||||
logger.info("Experimental Rust analysis enabled");
|
logger.info("Experimental Rust analysis enabled");
|
||||||
}
|
}
|
||||||
@@ -163022,6 +163215,12 @@ async function sendCompletedStatusReport3(startedAt, toolsInput, toolsDownloadSt
|
|||||||
if (toolsDownloadStatusReport?.totalDurationMs !== void 0) {
|
if (toolsDownloadStatusReport?.totalDurationMs !== void 0) {
|
||||||
initToolsDownloadFields.tools_total_duration_ms = toolsDownloadStatusReport.totalDurationMs;
|
initToolsDownloadFields.tools_total_duration_ms = toolsDownloadStatusReport.totalDurationMs;
|
||||||
}
|
}
|
||||||
|
if (toolsDownloadStatusReport?.bundleLanguage !== void 0) {
|
||||||
|
initToolsDownloadFields.tools_bundle_language = toolsDownloadStatusReport.bundleLanguage;
|
||||||
|
}
|
||||||
|
if (toolsDownloadStatusReport?.perLanguageBundleFallback !== void 0) {
|
||||||
|
initToolsDownloadFields.tools_per_language_bundle_fallback = toolsDownloadStatusReport.perLanguageBundleFallback;
|
||||||
|
}
|
||||||
if (toolsFeatureFlagsValid !== void 0) {
|
if (toolsFeatureFlagsValid !== void 0) {
|
||||||
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ steps:
|
|||||||
- id: init
|
- id: init
|
||||||
uses: ./../action/init
|
uses: ./../action/init
|
||||||
with:
|
with:
|
||||||
languages: javascript
|
languages: javascript,python
|
||||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
- uses: ./../action/analyze
|
- uses: ./../action/analyze
|
||||||
with:
|
with:
|
||||||
|
|||||||
117
pr-checks/checks/per-language-bundle-validation.yml
Normal file
117
pr-checks/checks/per-language-bundle-validation.yml
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
name: Per-language bundles
|
||||||
|
description: Validates extraction and analysis using each per-language CodeQL bundle.
|
||||||
|
# TODO: Use a released bundle once releases include per-language bundles.
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- language: actions
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
# Actions also needs the JavaScript extractor.
|
||||||
|
expected-extractors: actions javascript
|
||||||
|
- language: cpp
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: manual
|
||||||
|
build-command: gcc -o main main.c
|
||||||
|
- language: csharp
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: none
|
||||||
|
- language: go
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: autobuild
|
||||||
|
- language: java
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: none
|
||||||
|
- language: javascript
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: python
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: ruby
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: rust
|
||||||
|
os: ubuntu-latest
|
||||||
|
version: nightly-latest
|
||||||
|
- language: swift
|
||||||
|
os: macos-latest-xlarge
|
||||||
|
version: nightly-latest
|
||||||
|
build-mode: autobuild
|
||||||
|
env:
|
||||||
|
CODEQL_ACTION_PER_LANGUAGE_BUNDLES: true
|
||||||
|
steps:
|
||||||
|
- uses: ./../action/init
|
||||||
|
id: init
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
build-mode: ${{ matrix['build-mode'] }}
|
||||||
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
|
- name: Check that the bundle contains only the expected extractors
|
||||||
|
env:
|
||||||
|
CODEQL_PATH: ${{ steps.init.outputs.codeql-path }}
|
||||||
|
LANGUAGE: ${{ matrix.language }}
|
||||||
|
EXPECTED_EXTRACTORS: ${{ matrix['expected-extractors'] || matrix.language }}
|
||||||
|
run: |
|
||||||
|
extractors="$("$CODEQL_PATH" resolve languages --format=json | jq -r 'keys[]')"
|
||||||
|
echo "Extractors in the bundle:"
|
||||||
|
echo "$extractors"
|
||||||
|
echo "Expected: $EXPECTED_EXTRACTORS"
|
||||||
|
|
||||||
|
for expected in $EXPECTED_EXTRACTORS; do
|
||||||
|
if ! echo "$extractors" | grep -qx "$expected"; then
|
||||||
|
echo "::error::The ${LANGUAGE} bundle does not contain the ${expected} extractor."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If the bundle contained extractors beyond those the language needs, then it would not
|
||||||
|
# have been trimmed, and this job would be silently validating the combined bundle.
|
||||||
|
for other in actions cpp csharp go java javascript python ruby rust swift; do
|
||||||
|
if echo "$EXPECTED_EXTRACTORS" | grep -qw "$other"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if echo "$extractors" | grep -qx "$other"; then
|
||||||
|
echo "::error::The ${LANGUAGE} bundle also contains the ${other} extractor, so it is not trimmed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
- name: Check that the bundle was not added to the toolcache
|
||||||
|
env:
|
||||||
|
CODEQL_PATH: ${{ steps.init.outputs.codeql-path }}
|
||||||
|
run: |
|
||||||
|
# A bundle that is missing most of its extractors must never be left in the toolcache,
|
||||||
|
# where a later job analyzing a different language could pick it up. The runner image
|
||||||
|
# ships with its own CodeQL in the toolcache, so check where this bundle was extracted to
|
||||||
|
# rather than whether the toolcache contains CodeQL at all.
|
||||||
|
echo "CodeQL is at $CODEQL_PATH"
|
||||||
|
if [[ "$CODEQL_PATH" == "$RUNNER_TOOL_CACHE"/* ]]; then
|
||||||
|
echo "::error::The per-language bundle was added to the toolcache at $CODEQL_PATH."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$CODEQL_PATH" != "$RUNNER_TEMP"/* ]]; then
|
||||||
|
echo "::error::Expected the per-language bundle to be extracted under $RUNNER_TEMP, but found it at $CODEQL_PATH."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Build code
|
||||||
|
if: matrix['build-command']
|
||||||
|
run: ${{ matrix['build-command'] }}
|
||||||
|
- uses: ./../action/analyze
|
||||||
|
id: analysis
|
||||||
|
with:
|
||||||
|
upload-database: false
|
||||||
|
- name: Check that a database was created for the language
|
||||||
|
env:
|
||||||
|
DB_LOCATIONS: ${{ steps.analysis.outputs.db-locations }}
|
||||||
|
LANGUAGE: ${{ matrix.language }}
|
||||||
|
run: |
|
||||||
|
database="$(echo "$DB_LOCATIONS" | jq -r --arg lang "$LANGUAGE" '.[$lang] // empty')"
|
||||||
|
if [ -z "$database" ] || [ ! -d "$database" ]; then
|
||||||
|
echo "::error::No CodeQL database was created for ${LANGUAGE}."
|
||||||
|
echo "Databases: $DB_LOCATIONS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Created a ${LANGUAGE} database at ${database}."
|
||||||
@@ -79,6 +79,8 @@ interface Specification extends JobSpecification {
|
|||||||
useAllPlatformBundle?: string;
|
useAllPlatformBundle?: string;
|
||||||
/** Values for the `analysis-kinds` matrix dimension. */
|
/** Values for the `analysis-kinds` matrix dimension. */
|
||||||
analysisKinds?: string[];
|
analysisKinds?: string[];
|
||||||
|
/** Overrides the generated job matrix using GitHub Actions matrix syntax. */
|
||||||
|
matrix?: Record<string, unknown>;
|
||||||
|
|
||||||
/** Container image configuration for the job. */
|
/** Container image configuration for the job. */
|
||||||
container?: any;
|
container?: any;
|
||||||
@@ -512,9 +514,6 @@ function generateJob(
|
|||||||
specDocument: yaml.Document,
|
specDocument: yaml.Document,
|
||||||
checkSpecification: Specification,
|
checkSpecification: Specification,
|
||||||
) {
|
) {
|
||||||
const matrix: Array<Record<string, any>> =
|
|
||||||
generateJobMatrix(checkSpecification);
|
|
||||||
|
|
||||||
const useAllPlatformBundle = checkSpecification.useAllPlatformBundle
|
const useAllPlatformBundle = checkSpecification.useAllPlatformBundle
|
||||||
? checkSpecification.useAllPlatformBundle
|
? checkSpecification.useAllPlatformBundle
|
||||||
: "false";
|
: "false";
|
||||||
@@ -567,8 +566,8 @@ function generateJob(
|
|||||||
const checkJob: Record<string, any> = {
|
const checkJob: Record<string, any> = {
|
||||||
strategy: {
|
strategy: {
|
||||||
"fail-fast": false,
|
"fail-fast": false,
|
||||||
matrix: {
|
matrix: checkSpecification.matrix ?? {
|
||||||
include: matrix,
|
include: generateJobMatrix(checkSpecification),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
name: checkSpecification.name,
|
name: checkSpecification.name,
|
||||||
|
|||||||
@@ -164,6 +164,11 @@ export enum Feature {
|
|||||||
OverlayAnalysisStatusCheck = "overlay_analysis_status_check",
|
OverlayAnalysisStatusCheck = "overlay_analysis_status_check",
|
||||||
/** Controls whether overlay build failures on the default branch are stored in the Actions cache. */
|
/** Controls whether overlay build failures on the default branch are stored in the Actions cache. */
|
||||||
OverlayAnalysisStatusSave = "overlay_analysis_status_save",
|
OverlayAnalysisStatusSave = "overlay_analysis_status_save",
|
||||||
|
/**
|
||||||
|
* Controls whether we may download a bundle containing only the single language being analysed,
|
||||||
|
* rather than the combined bundle that contains every language.
|
||||||
|
*/
|
||||||
|
PerLanguageBundles = "per_language_bundles",
|
||||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||||
/** Routes (some) API requests through the registry proxy. */
|
/** Routes (some) API requests through the registry proxy. */
|
||||||
ProxyApiRequests = "proxy_api_requests",
|
ProxyApiRequests = "proxy_api_requests",
|
||||||
@@ -434,6 +439,11 @@ export const featureConfig = {
|
|||||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS",
|
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS",
|
||||||
minimumVersion: undefined,
|
minimumVersion: undefined,
|
||||||
},
|
},
|
||||||
|
[Feature.PerLanguageBundles]: {
|
||||||
|
defaultValue: false,
|
||||||
|
envVar: "CODEQL_ACTION_PER_LANGUAGE_BUNDLES",
|
||||||
|
minimumVersion: undefined,
|
||||||
|
},
|
||||||
[Feature.QaTelemetryEnabled]: {
|
[Feature.QaTelemetryEnabled]: {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
envVar: "CODEQL_ACTION_QA_TELEMETRY",
|
envVar: "CODEQL_ACTION_QA_TELEMETRY",
|
||||||
|
|||||||
@@ -182,6 +182,14 @@ async function sendCompletedStatusReport(
|
|||||||
initToolsDownloadFields.tools_total_duration_ms =
|
initToolsDownloadFields.tools_total_duration_ms =
|
||||||
toolsDownloadStatusReport.totalDurationMs;
|
toolsDownloadStatusReport.totalDurationMs;
|
||||||
}
|
}
|
||||||
|
if (toolsDownloadStatusReport?.bundleLanguage !== undefined) {
|
||||||
|
initToolsDownloadFields.tools_bundle_language =
|
||||||
|
toolsDownloadStatusReport.bundleLanguage;
|
||||||
|
}
|
||||||
|
if (toolsDownloadStatusReport?.perLanguageBundleFallback !== undefined) {
|
||||||
|
initToolsDownloadFields.tools_per_language_bundle_fallback =
|
||||||
|
toolsDownloadStatusReport.perLanguageBundleFallback;
|
||||||
|
}
|
||||||
if (toolsFeatureFlagsValid !== undefined) {
|
if (toolsFeatureFlagsValid !== undefined) {
|
||||||
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
||||||
}
|
}
|
||||||
|
|||||||
189
src/per-language-bundles.test.ts
Normal file
189
src/per-language-bundles.test.ts
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import test from "ava";
|
||||||
|
|
||||||
|
import { ActionsEnvVars } from "./environment";
|
||||||
|
import { Feature } from "./feature-flags";
|
||||||
|
import { BuiltInLanguage } from "./languages";
|
||||||
|
import { getRunnerLogger } from "./logging";
|
||||||
|
import {
|
||||||
|
getPerLanguageBundleLanguage,
|
||||||
|
MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
|
||||||
|
PerLanguageBundleOptions,
|
||||||
|
tryGetBundleLanguageFromUrl,
|
||||||
|
} from "./per-language-bundles";
|
||||||
|
import { createFeatures, setupTests } from "./testing-utils";
|
||||||
|
import { GitHubVariant } from "./util";
|
||||||
|
|
||||||
|
setupTests(test);
|
||||||
|
|
||||||
|
/** Options for which we would use a per-language bundle. */
|
||||||
|
const ELIGIBLE_OPTIONS: PerLanguageBundleOptions = {
|
||||||
|
rawLanguages: ["java"],
|
||||||
|
// Any version at least as new as the minimum will do.
|
||||||
|
cliVersion: MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
|
||||||
|
compressionMethod: "zstd",
|
||||||
|
platform: "linux64",
|
||||||
|
variant: GitHubVariant.DOTCOM,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkEligibility(
|
||||||
|
overrides: Partial<PerLanguageBundleOptions>,
|
||||||
|
enabledFeatures: Feature[] = [Feature.PerLanguageBundles],
|
||||||
|
) {
|
||||||
|
return getPerLanguageBundleLanguage(
|
||||||
|
{ ...ELIGIBLE_OPTIONS, ...overrides },
|
||||||
|
createFeatures(enabledFeatures),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test.beforeEach(() => {
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("uses Linux bundles for non-Swift languages", async (t) => {
|
||||||
|
for (const language of Object.values(BuiltInLanguage)) {
|
||||||
|
if (language === BuiltInLanguage.swift) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
t.is(await checkEligibility({ rawLanguages: [language] }), language);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("normalizes an alias before selecting a bundle", async (t) => {
|
||||||
|
t.is(
|
||||||
|
await checkEligibility({ rawLanguages: ["java-kotlin"] }),
|
||||||
|
BuiltInLanguage.java,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("uses the macOS bundle for Swift", async (t) => {
|
||||||
|
t.is(
|
||||||
|
await checkEligibility({ rawLanguages: ["swift"], platform: "osx64" }),
|
||||||
|
BuiltInLanguage.swift,
|
||||||
|
);
|
||||||
|
// Swift is only published for macOS.
|
||||||
|
t.is(
|
||||||
|
await checkEligibility({ rawLanguages: ["swift"], platform: "linux64" }),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("only publishes non-Swift languages for Linux", async (t) => {
|
||||||
|
t.is(await checkEligibility({ platform: "osx64" }), undefined);
|
||||||
|
t.is(await checkEligibility({ platform: "win64" }), undefined);
|
||||||
|
// We do not publish per-language bundles for Linux Arm64 either.
|
||||||
|
t.is(await checkEligibility({ platform: "linux-arm64" }), undefined);
|
||||||
|
t.is(await checkEligibility({ platform: undefined }), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires exactly one language", async (t) => {
|
||||||
|
t.is(await checkEligibility({ rawLanguages: undefined }), undefined);
|
||||||
|
t.is(await checkEligibility({ rawLanguages: [] }), undefined);
|
||||||
|
t.is(await checkEligibility({ rawLanguages: ["java", "python"] }), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires a language that CodeQL knows about", async (t) => {
|
||||||
|
t.is(await checkEligibility({ rawLanguages: ["cobol"] }), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires a zstd bundle", async (t) => {
|
||||||
|
t.is(await checkEligibility({ compressionMethod: "gzip" }), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires GitHub.com", async (t) => {
|
||||||
|
// Other products resolve the combined bundle against their own instance, so asking for a
|
||||||
|
// per-language bundle they do not mirror would move the download off that instance.
|
||||||
|
for (const variant of [GitHubVariant.GHES, GitHubVariant.GHEC_DR]) {
|
||||||
|
t.is(await checkEligibility({ variant }), undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires a GitHub-hosted runner", async (t) => {
|
||||||
|
// A self-hosted runner may have a toolcache that persists between jobs, which is worth more than
|
||||||
|
// a smaller download.
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "self-hosted";
|
||||||
|
t.is(await checkEligibility({}), undefined);
|
||||||
|
|
||||||
|
// Self-hosted runners are routinely configured to look like hosted ones, for example by mounting
|
||||||
|
// a persistent volume at `/opt/hostedtoolcache`, so we require the service to tell us explicitly.
|
||||||
|
delete process.env[ActionsEnvVars.RUNNER_ENVIRONMENT];
|
||||||
|
process.env["RUNNER_TOOL_CACHE"] = "/opt/hostedtoolcache";
|
||||||
|
t.is(await checkEligibility({}), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires a new enough CLI version", async (t) => {
|
||||||
|
t.is(await checkEligibility({ cliVersion: undefined }), undefined);
|
||||||
|
t.is(await checkEligibility({ cliVersion: "2.27.0" }), undefined);
|
||||||
|
t.is(await checkEligibility({ cliVersion: "2.27.1" }), BuiltInLanguage.java);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("requires the feature flag", async (t) => {
|
||||||
|
t.is(await checkEligibility({}, []), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("nightlies skip only the release version check", async (t) => {
|
||||||
|
const nightly = { isNightly: true, cliVersion: undefined };
|
||||||
|
t.is(await checkEligibility(nightly), BuiltInLanguage.java);
|
||||||
|
|
||||||
|
for (const overrides of [
|
||||||
|
{ rawLanguages: undefined },
|
||||||
|
{ rawLanguages: ["java", "python"] },
|
||||||
|
{ compressionMethod: "gzip" as const },
|
||||||
|
{ platform: "osx64" },
|
||||||
|
{ variant: GitHubVariant.GHES },
|
||||||
|
{ variant: GitHubVariant.GHEC_DR },
|
||||||
|
]) {
|
||||||
|
t.is(await checkEligibility({ ...nightly, ...overrides }), undefined);
|
||||||
|
}
|
||||||
|
t.is(await checkEligibility(nightly, []), undefined);
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "self-hosted";
|
||||||
|
t.is(await checkEligibility(nightly), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("recognizes a per-language bundle from its URL", (t) => {
|
||||||
|
const url = (name: string) =>
|
||||||
|
`https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;
|
||||||
|
|
||||||
|
t.is(
|
||||||
|
tryGetBundleLanguageFromUrl(url("codeql-bundle-java-linux64.tar.zst")),
|
||||||
|
BuiltInLanguage.java,
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
tryGetBundleLanguageFromUrl(url("codeql-bundle-swift-osx64.tar.zst")),
|
||||||
|
BuiltInLanguage.swift,
|
||||||
|
);
|
||||||
|
// We do not publish these, but should still recognize them if we ever do.
|
||||||
|
t.is(
|
||||||
|
tryGetBundleLanguageFromUrl(url("codeql-bundle-csharp-win64.tar.gz")),
|
||||||
|
BuiltInLanguage.csharp,
|
||||||
|
);
|
||||||
|
// A percent-encoded name resolves to the same asset, so it must not let a bundle that contains a
|
||||||
|
// single language pass for one that contains them all and end up in the toolcache.
|
||||||
|
t.is(
|
||||||
|
tryGetBundleLanguageFromUrl(url("codeql-bundle-%70ython-linux64.tar.zst")),
|
||||||
|
BuiltInLanguage.python,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("does not mistake other bundles for per-language ones", (t) => {
|
||||||
|
const url = (name: string) =>
|
||||||
|
`https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;
|
||||||
|
|
||||||
|
for (const name of [
|
||||||
|
"codeql-bundle-linux64.tar.zst",
|
||||||
|
"codeql-bundle-osx64.tar.gz",
|
||||||
|
"codeql-bundle-win64.tar.zst",
|
||||||
|
// The all-platform bundle.
|
||||||
|
"codeql-bundle.tar.gz",
|
||||||
|
// A platform we do not publish per-language bundles for, whose name also contains a hyphen.
|
||||||
|
"codeql-bundle-linux-arm64.tar.zst",
|
||||||
|
// Not a language we know about.
|
||||||
|
"codeql-bundle-cobol-linux64.tar.zst",
|
||||||
|
// A name we cannot decode must not be mistaken for a language either.
|
||||||
|
"codeql-bundle-%zz-linux64.tar.zst",
|
||||||
|
]) {
|
||||||
|
t.is(tryGetBundleLanguageFromUrl(url(name)), undefined, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
t.is(tryGetBundleLanguageFromUrl("not a url"), undefined);
|
||||||
|
});
|
||||||
142
src/per-language-bundles.ts
Normal file
142
src/per-language-bundles.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import * as semver from "semver";
|
||||||
|
|
||||||
|
import { isGitHubHostedRunner } from "./actions-util";
|
||||||
|
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||||
|
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";
|
||||||
|
import { Logger } from "./logging";
|
||||||
|
import * as tar from "./tar";
|
||||||
|
import { GitHubVariant } from "./util";
|
||||||
|
|
||||||
|
/** Minimum CLI version for selecting a per-language release bundle. */
|
||||||
|
export const MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION = "2.27.1";
|
||||||
|
|
||||||
|
const PER_LANGUAGE_BUNDLE_NAME =
|
||||||
|
/^codeql-bundle-(.+)-(?:linux64|osx64|win64)\.tar\.(?:gz|zst)$/;
|
||||||
|
|
||||||
|
/** Identifies per-language tools URLs that must not populate the toolcache. */
|
||||||
|
export function tryGetBundleLanguageFromUrl(
|
||||||
|
url: string,
|
||||||
|
): BuiltInLanguage | undefined {
|
||||||
|
let assetName: string;
|
||||||
|
try {
|
||||||
|
const pathname = new URL(url).pathname;
|
||||||
|
// URL-encoded names must not bypass the toolcache safeguard.
|
||||||
|
assetName = decodeURIComponent(pathname.split("/").pop() ?? "");
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = assetName.match(PER_LANGUAGE_BUNDLE_NAME);
|
||||||
|
return match ? parseBuiltInLanguage(match[1]) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Published platform for each language; absent entries are ineligible. */
|
||||||
|
const PER_LANGUAGE_BUNDLE_PLATFORMS: Readonly<
|
||||||
|
Partial<Record<BuiltInLanguage, string>>
|
||||||
|
> = {
|
||||||
|
[BuiltInLanguage.actions]: "linux64",
|
||||||
|
[BuiltInLanguage.cpp]: "linux64",
|
||||||
|
[BuiltInLanguage.csharp]: "linux64",
|
||||||
|
[BuiltInLanguage.go]: "linux64",
|
||||||
|
[BuiltInLanguage.java]: "linux64",
|
||||||
|
[BuiltInLanguage.javascript]: "linux64",
|
||||||
|
[BuiltInLanguage.python]: "linux64",
|
||||||
|
[BuiltInLanguage.ruby]: "linux64",
|
||||||
|
[BuiltInLanguage.rust]: "linux64",
|
||||||
|
[BuiltInLanguage.swift]: "osx64",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Inputs that determine whether we may download a per-language bundle. */
|
||||||
|
export interface PerLanguageBundleOptions {
|
||||||
|
/** Explicit input only: autodetection needs a CLI instance. */
|
||||||
|
rawLanguages: string[] | undefined;
|
||||||
|
/** CLI version, if known. Ignored for nightly bundles. */
|
||||||
|
cliVersion: string | undefined;
|
||||||
|
compressionMethod: tar.CompressionMethod;
|
||||||
|
/** Bundle platform identifier, such as linux64. */
|
||||||
|
platform: string | undefined;
|
||||||
|
variant: GitHubVariant;
|
||||||
|
isNightly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the eligible bundle language, or undefined for the combined bundle. */
|
||||||
|
export async function getPerLanguageBundleLanguage(
|
||||||
|
options: PerLanguageBundleOptions,
|
||||||
|
features: FeatureEnablement,
|
||||||
|
logger: Logger,
|
||||||
|
): Promise<BuiltInLanguage | undefined> {
|
||||||
|
const {
|
||||||
|
rawLanguages,
|
||||||
|
cliVersion,
|
||||||
|
compressionMethod,
|
||||||
|
platform,
|
||||||
|
variant,
|
||||||
|
isNightly,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const explain = (reason: string) => {
|
||||||
|
logger.debug(`Not using a per-language CodeQL bundle since ${reason}.`);
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rawLanguages?.length !== 1) {
|
||||||
|
return explain(
|
||||||
|
`exactly one language must be requested via the 'languages' input, but ${
|
||||||
|
rawLanguages?.length ?? 0
|
||||||
|
} were`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const language = parseBuiltInLanguage(rawLanguages[0]);
|
||||||
|
if (language === undefined) {
|
||||||
|
return explain(`'${rawLanguages[0]}' is not a known CodeQL language`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compressionMethod !== "zstd") {
|
||||||
|
// Per-language bundles are only published as zstd archives.
|
||||||
|
return explain(`the bundle would be downloaded as ${compressionMethod}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant !== GitHubVariant.DOTCOM) {
|
||||||
|
// Tenant mirrors may lack these assets, and an unreachable github.com fails with a
|
||||||
|
// connection error rather than a recoverable 404.
|
||||||
|
return explain(`we are running against ${variant}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isGitHubHostedRunner()) {
|
||||||
|
// Per-language installs stay out of the toolcache; self-hosted runners should retain
|
||||||
|
// the reusable combined bundle instead.
|
||||||
|
return explain("the job is not running on a GitHub-hosted runner");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nightly tags contain dates rather than comparable CLI versions.
|
||||||
|
if (!isNightly) {
|
||||||
|
if (cliVersion === undefined) {
|
||||||
|
return explain("the CLI version of the bundle is unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!semver.gte(cliVersion, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION)) {
|
||||||
|
return explain(
|
||||||
|
`CodeQL ${cliVersion} is older than ${MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION}, which is the ` +
|
||||||
|
"first version that publishes per-language bundles",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const supportedPlatform = PER_LANGUAGE_BUNDLE_PLATFORMS[language];
|
||||||
|
if (supportedPlatform === undefined) {
|
||||||
|
return explain(`no per-language bundle is published for ${language}`);
|
||||||
|
}
|
||||||
|
if (supportedPlatform !== platform) {
|
||||||
|
return explain(
|
||||||
|
`the ${language} bundle is only published for ${supportedPlatform}, but this job is ` +
|
||||||
|
`running on ${platform ?? "an unknown platform"}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await features.getValue(Feature.PerLanguageBundles))) {
|
||||||
|
return explain(`the ${Feature.PerLanguageBundles} feature is disabled`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return language;
|
||||||
|
}
|
||||||
@@ -93,6 +93,14 @@ async function sendCompletedStatusReport(
|
|||||||
initToolsDownloadFields.tools_total_duration_ms =
|
initToolsDownloadFields.tools_total_duration_ms =
|
||||||
toolsDownloadStatusReport.totalDurationMs;
|
toolsDownloadStatusReport.totalDurationMs;
|
||||||
}
|
}
|
||||||
|
if (toolsDownloadStatusReport?.bundleLanguage !== undefined) {
|
||||||
|
initToolsDownloadFields.tools_bundle_language =
|
||||||
|
toolsDownloadStatusReport.bundleLanguage;
|
||||||
|
}
|
||||||
|
if (toolsDownloadStatusReport?.perLanguageBundleFallback !== undefined) {
|
||||||
|
initToolsDownloadFields.tools_per_language_bundle_fallback =
|
||||||
|
toolsDownloadStatusReport.perLanguageBundleFallback;
|
||||||
|
}
|
||||||
if (toolsFeatureFlagsValid !== undefined) {
|
if (toolsFeatureFlagsValid !== undefined) {
|
||||||
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import * as api from "./api-client";
|
|||||||
import * as diagnostics from "./diagnostics";
|
import * as diagnostics from "./diagnostics";
|
||||||
import { ActionsEnvVars, EnvVar, getEnv, ReadOnlyEnv } from "./environment";
|
import { ActionsEnvVars, EnvVar, getEnv, ReadOnlyEnv } from "./environment";
|
||||||
import { Feature } from "./feature-flags";
|
import { Feature } from "./feature-flags";
|
||||||
|
import { BuiltInLanguage } from "./languages";
|
||||||
import { getRunnerLogger } from "./logging";
|
import { getRunnerLogger } from "./logging";
|
||||||
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
|
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
|
||||||
|
import { MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION } from "./per-language-bundles";
|
||||||
import * as setupCodeql from "./setup-codeql";
|
import * as setupCodeql from "./setup-codeql";
|
||||||
import * as tar from "./tar";
|
import * as tar from "./tar";
|
||||||
import {
|
import {
|
||||||
@@ -55,6 +57,25 @@ function stubDownloadAndExtract() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stubHostedNightly(tagName: string) {
|
||||||
|
sinon.stub(process, "platform").value("linux");
|
||||||
|
sinon.stub(process, "arch").value("x64");
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||||
|
sinon.stub(tar, "isZstdAvailable").resolves({
|
||||||
|
available: true,
|
||||||
|
foundZstdBinary: true,
|
||||||
|
});
|
||||||
|
const client = github.getOctokit("123", {
|
||||||
|
request: {
|
||||||
|
fetch: async () =>
|
||||||
|
new Response(JSON.stringify([{ tag_name: tagName }]), {
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
sinon.stub(api, "getApiClient").value(() => client);
|
||||||
|
}
|
||||||
|
|
||||||
test.serial("parse codeql bundle url version", (t) => {
|
test.serial("parse codeql bundle url version", (t) => {
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
setupCodeql.getCodeQLURLVersion(
|
setupCodeql.getCodeQLURLVersion(
|
||||||
@@ -374,20 +395,7 @@ test.serial(
|
|||||||
const expectedDate = "30260213";
|
const expectedDate = "30260213";
|
||||||
const expectedTag = `codeql-bundle-${expectedDate}`;
|
const expectedTag = `codeql-bundle-${expectedDate}`;
|
||||||
|
|
||||||
// Ensure that we consistently select "zstd" for the test.
|
stubHostedNightly(expectedTag);
|
||||||
sinon.stub(process, "platform").value("linux");
|
|
||||||
sinon.stub(tar, "isZstdAvailable").resolves({
|
|
||||||
available: true,
|
|
||||||
foundZstdBinary: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const client = github.getOctokit("123");
|
|
||||||
const listReleases = sinon.stub(client.rest.repos, "listReleases");
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
||||||
listReleases.resolves({
|
|
||||||
data: [{ tag_name: expectedTag }],
|
|
||||||
} as any);
|
|
||||||
sinon.stub(api, "getApiClient").value(() => client);
|
|
||||||
|
|
||||||
await withTmpDir(async (tmpDir) => {
|
await withTmpDir(async (tmpDir) => {
|
||||||
setupActionsVars(tmpDir, tmpDir);
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
@@ -456,20 +464,7 @@ test.serial(
|
|||||||
const expectedDate = "30260213";
|
const expectedDate = "30260213";
|
||||||
const expectedTag = `codeql-bundle-${expectedDate}`;
|
const expectedTag = `codeql-bundle-${expectedDate}`;
|
||||||
|
|
||||||
// Ensure that we consistently select "zstd" for the test.
|
stubHostedNightly(expectedTag);
|
||||||
sinon.stub(process, "platform").value("linux");
|
|
||||||
sinon.stub(tar, "isZstdAvailable").resolves({
|
|
||||||
available: true,
|
|
||||||
foundZstdBinary: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const client = github.getOctokit("123");
|
|
||||||
const listReleases = sinon.stub(client.rest.repos, "listReleases");
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
||||||
listReleases.resolves({
|
|
||||||
data: [{ tag_name: expectedTag }],
|
|
||||||
} as any);
|
|
||||||
sinon.stub(api, "getApiClient").value(() => client);
|
|
||||||
|
|
||||||
await withTmpDir(async (tmpDir) => {
|
await withTmpDir(async (tmpDir) => {
|
||||||
setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic" });
|
setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic" });
|
||||||
@@ -513,6 +508,8 @@ for (const bundlePath of [
|
|||||||
"codeql-bundle.tar.gz",
|
"codeql-bundle.tar.gz",
|
||||||
"codeql-bundle.tar.zst",
|
"codeql-bundle.tar.zst",
|
||||||
"codeql-bundle-/codeql-bundle.tar.gz",
|
"codeql-bundle-/codeql-bundle.tar.gz",
|
||||||
|
"codeql-bundle-linux64.tar.zst",
|
||||||
|
"codeql-bundle-ruby-linux64.tar.zst",
|
||||||
]) {
|
]) {
|
||||||
test.serial(
|
test.serial(
|
||||||
`setupCodeQLBundle reports an unknown version for ${bundlePath}`,
|
`setupCodeQLBundle reports an unknown version for ${bundlePath}`,
|
||||||
@@ -542,6 +539,12 @@ for (const bundlePath of [
|
|||||||
t.is(downloadSpy.firstCall.args[0].toolsVersion, "unknown");
|
t.is(downloadSpy.firstCall.args[0].toolsVersion, "unknown");
|
||||||
t.is(result.toolsVersion, "unknown");
|
t.is(result.toolsVersion, "unknown");
|
||||||
t.is(result.toolsSource, setupCodeql.ToolsSource.Download);
|
t.is(result.toolsSource, setupCodeql.ToolsSource.Download);
|
||||||
|
t.is(
|
||||||
|
result.toolsDownloadStatusReport?.bundleLanguage,
|
||||||
|
bundlePath === "codeql-bundle-ruby-linux64.tar.zst"
|
||||||
|
? BuiltInLanguage.ruby
|
||||||
|
: undefined,
|
||||||
|
);
|
||||||
t.is(path.dirname(result.codeqlFolder), tmpDir);
|
t.is(path.dirname(result.codeqlFolder), tmpDir);
|
||||||
t.true(fs.existsSync(result.codeqlFolder));
|
t.true(fs.existsSync(result.codeqlFolder));
|
||||||
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
|
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
|
||||||
@@ -594,6 +597,131 @@ test.serial(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for (const toolsInput of ["nightly", "nightly-latest"]) {
|
||||||
|
test.serial(
|
||||||
|
`getCodeQLSource selects a per-language bundle for tools == ${toolsInput}`,
|
||||||
|
async (t) => {
|
||||||
|
const expectedTag = "codeql-bundle-30260213";
|
||||||
|
const baseURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}`;
|
||||||
|
stubHostedNightly(expectedTag);
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
const source = await setupCodeql.getCodeQLSource(
|
||||||
|
toolsInput,
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
["java"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
true, // tarSupportsZstd
|
||||||
|
createFeatures([Feature.PerLanguageBundles]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.deepEqual(source, {
|
||||||
|
sourceType: "download",
|
||||||
|
bundle: {
|
||||||
|
kind: "per-language",
|
||||||
|
language: BuiltInLanguage.java,
|
||||||
|
url: `${baseURL}/codeql-bundle-java-linux64.tar.zst`,
|
||||||
|
combinedBundleURL: `${baseURL}/codeql-bundle-linux64.tar.zst`,
|
||||||
|
},
|
||||||
|
bundleVersion: "30260213",
|
||||||
|
cliVersion: undefined,
|
||||||
|
compressionMethod: "zstd",
|
||||||
|
toolsVersion: "0.0.0-30260213",
|
||||||
|
} satisfies setupCodeql.CodeQLDownloadSource);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test.serial(
|
||||||
|
"getCodeQLSource downloads the combined nightly bundle when not eligible",
|
||||||
|
async (t) => {
|
||||||
|
const expectedTag = "codeql-bundle-30260213";
|
||||||
|
stubHostedNightly(expectedTag);
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
for (const { languages, features } of [
|
||||||
|
{ languages: ["java"], features: createFeatures([]) },
|
||||||
|
{
|
||||||
|
languages: ["java", "python"],
|
||||||
|
features: createFeatures([Feature.PerLanguageBundles]),
|
||||||
|
},
|
||||||
|
]) {
|
||||||
|
const source = await setupCodeql.getCodeQLSource(
|
||||||
|
"nightly",
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
languages,
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
true, // tarSupportsZstd
|
||||||
|
features,
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.is(source.sourceType, "download");
|
||||||
|
if (source.sourceType === "download") {
|
||||||
|
t.deepEqual(source.bundle, {
|
||||||
|
kind: "combined",
|
||||||
|
url: `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}/codeql-bundle-linux64.tar.zst`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const perLanguageBundles of [false, true]) {
|
||||||
|
test.serial(
|
||||||
|
`getCodeQLSource uses a ${perLanguageBundles ? "per-language" : "combined"} bundle for a forced nightly`,
|
||||||
|
async (t) => {
|
||||||
|
const expectedTag = "codeql-bundle-30260213";
|
||||||
|
const baseURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}`;
|
||||||
|
stubHostedNightly(expectedTag);
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir, { GITHUB_EVENT_NAME: "dynamic" });
|
||||||
|
const source = await setupCodeql.getCodeQLSource(
|
||||||
|
undefined, // toolsInput: the nightly is selected by ForceNightly
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
["java"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
true, // tarSupportsZstd
|
||||||
|
createFeatures(
|
||||||
|
perLanguageBundles
|
||||||
|
? [Feature.ForceNightly, Feature.PerLanguageBundles]
|
||||||
|
: [Feature.ForceNightly],
|
||||||
|
),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.is(source.sourceType, "download");
|
||||||
|
if (source.sourceType === "download") {
|
||||||
|
const combinedURL = `${baseURL}/codeql-bundle-linux64.tar.zst`;
|
||||||
|
t.deepEqual(
|
||||||
|
source.bundle,
|
||||||
|
perLanguageBundles
|
||||||
|
? {
|
||||||
|
kind: "per-language",
|
||||||
|
language: BuiltInLanguage.java,
|
||||||
|
url: `${baseURL}/codeql-bundle-java-linux64.tar.zst`,
|
||||||
|
combinedBundleURL: combinedURL,
|
||||||
|
}
|
||||||
|
: { kind: "combined", url: combinedURL },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test.serial(
|
test.serial(
|
||||||
"getCodeQLSource correctly returns latest version from toolcache when tools == toolcache",
|
"getCodeQLSource correctly returns latest version from toolcache when tools == toolcache",
|
||||||
async (t) => {
|
async (t) => {
|
||||||
@@ -878,6 +1006,439 @@ test.serial(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const PER_LANGUAGE_CLI_VERSION = {
|
||||||
|
enabledVersions: [
|
||||||
|
{
|
||||||
|
cliVersion: MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
|
||||||
|
tagName: `codeql-bundle-v${MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
test.serial("getCodeQLBundleName names the per-language bundle", (t) => {
|
||||||
|
sinon.stub(process, "platform").value("linux");
|
||||||
|
sinon.stub(process, "arch").value("x64");
|
||||||
|
t.is(
|
||||||
|
setupCodeql.getCodeQLBundleName("zstd", BuiltInLanguage.java),
|
||||||
|
"codeql-bundle-java-linux64.tar.zst",
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
setupCodeql.getCodeQLBundleName("zstd"),
|
||||||
|
"codeql-bundle-linux64.tar.zst",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial("getCodeQLBundleName names the Swift bundle for macOS", (t) => {
|
||||||
|
sinon.stub(process, "platform").value("darwin");
|
||||||
|
t.is(
|
||||||
|
setupCodeql.getCodeQLBundleName("zstd", BuiltInLanguage.swift),
|
||||||
|
"codeql-bundle-swift-osx64.tar.zst",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.serial(
|
||||||
|
"getCodeQLSource downloads the per-language bundle for a single explicit language",
|
||||||
|
async (t) => {
|
||||||
|
sinon.stub(process, "platform").value("linux");
|
||||||
|
sinon.stub(process, "arch").value("x64");
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
const source = await setupCodeql.getCodeQLSource(
|
||||||
|
undefined,
|
||||||
|
PER_LANGUAGE_CLI_VERSION,
|
||||||
|
["java-kotlin"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
true, // tarSupportsZstd
|
||||||
|
createFeatures([Feature.PerLanguageBundles]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.is(source.sourceType, "download");
|
||||||
|
if (source.sourceType === "download") {
|
||||||
|
t.true(
|
||||||
|
source.bundle.url.endsWith("/codeql-bundle-java-linux64.tar.zst"),
|
||||||
|
`Unexpected URL ${source.bundle.url}`,
|
||||||
|
);
|
||||||
|
t.is(source.bundle.kind, "per-language");
|
||||||
|
if (source.bundle.kind === "per-language") {
|
||||||
|
t.is(source.bundle.language, BuiltInLanguage.java);
|
||||||
|
t.true(
|
||||||
|
source.bundle.combinedBundleURL?.endsWith(
|
||||||
|
"/codeql-bundle-linux64.tar.zst",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.serial(
|
||||||
|
"getCodeQLSource downloads the combined bundle when the feature is disabled",
|
||||||
|
async (t) => {
|
||||||
|
sinon.stub(process, "platform").value("linux");
|
||||||
|
sinon.stub(process, "arch").value("x64");
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
const source = await setupCodeql.getCodeQLSource(
|
||||||
|
undefined,
|
||||||
|
PER_LANGUAGE_CLI_VERSION,
|
||||||
|
["java"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
true, // tarSupportsZstd
|
||||||
|
createFeatures([]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.is(source.sourceType, "download");
|
||||||
|
if (source.sourceType === "download") {
|
||||||
|
t.true(source.bundle.url.endsWith("/codeql-bundle-linux64.tar.zst"));
|
||||||
|
t.is(source.bundle.kind, "combined");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const fallback of [false, true]) {
|
||||||
|
test.serial(
|
||||||
|
`setupCodeQLBundle retains the selected release identity for an opaque asset URL${fallback ? " with fallback" : ""}`,
|
||||||
|
async (t) => {
|
||||||
|
sinon.stub(process, "platform").value("linux");
|
||||||
|
sinon.stub(process, "arch").value("x64");
|
||||||
|
sinon.stub(actionsUtil, "isRunningLocalAction").returns(false);
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||||
|
sinon.stub(tar, "isZstdAvailable").resolves({
|
||||||
|
available: true,
|
||||||
|
foundZstdBinary: true,
|
||||||
|
});
|
||||||
|
const tag = PER_LANGUAGE_CLI_VERSION.enabledVersions[0].tagName;
|
||||||
|
const assetURL =
|
||||||
|
"https://api.github.com/repos/codeql-testing/action-fork/releases/assets/123";
|
||||||
|
const combinedURL = `${assetURL}4`;
|
||||||
|
const fetchRelease = sinon
|
||||||
|
.stub<Parameters<typeof fetch>, ReturnType<typeof fetch>>()
|
||||||
|
.callsFake(
|
||||||
|
async () =>
|
||||||
|
new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
assets: [
|
||||||
|
{ name: "codeql-bundle-java-linux64.tar.zst", url: assetURL },
|
||||||
|
{
|
||||||
|
name: "codeql-bundle-linux64.tar.zst",
|
||||||
|
url: combinedURL,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
{ headers: { "content-type": "application/json" } },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const client = github.getOctokit("123", {
|
||||||
|
request: { fetch: fetchRelease },
|
||||||
|
});
|
||||||
|
sinon.stub(api, "getApiClient").value(() => client);
|
||||||
|
const authorizationSpy = sinon.spy(api, "getAuthorizationHeaderFor");
|
||||||
|
const extractStub = stubDownloadAndExtract();
|
||||||
|
if (fallback) {
|
||||||
|
extractStub.onFirstCall().rejects(new HTTPError("Not Found", 404));
|
||||||
|
}
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir, {
|
||||||
|
GITHUB_ACTION_REPOSITORY: "codeql-testing/action-fork",
|
||||||
|
});
|
||||||
|
const result = await setupCodeql.setupCodeQLBundle(
|
||||||
|
undefined,
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
tmpDir,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
PER_LANGUAGE_CLI_VERSION,
|
||||||
|
["java"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
createFeatures([Feature.PerLanguageBundles]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.true(fetchRelease.calledTwice);
|
||||||
|
t.is(
|
||||||
|
fetchRelease.firstCall.args[0],
|
||||||
|
`https://api.github.com/repos/codeql-testing/action-fork/releases/tags/${tag}`,
|
||||||
|
);
|
||||||
|
t.is(extractStub.callCount, fallback ? 2 : 1);
|
||||||
|
t.is(extractStub.firstCall.args[0], assetURL);
|
||||||
|
t.is(extractStub.lastCall.args[0], fallback ? combinedURL : assetURL);
|
||||||
|
t.is(authorizationSpy.callCount, extractStub.callCount);
|
||||||
|
t.is(authorizationSpy.firstCall.args[2], assetURL);
|
||||||
|
t.is(
|
||||||
|
authorizationSpy.lastCall.args[2],
|
||||||
|
fallback ? combinedURL : assetURL,
|
||||||
|
);
|
||||||
|
t.is(extractStub.lastCall.args[3], "token token");
|
||||||
|
t.is(result.toolsVersion, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION);
|
||||||
|
t.is(
|
||||||
|
result.toolsDownloadStatusReport?.bundleLanguage,
|
||||||
|
fallback ? undefined : BuiltInLanguage.java,
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
result.toolsDownloadStatusReport?.perLanguageBundleFallback,
|
||||||
|
fallback ? true : undefined,
|
||||||
|
);
|
||||||
|
if (fallback) {
|
||||||
|
t.is(
|
||||||
|
result.codeqlFolder,
|
||||||
|
toolcache.find("CodeQL", MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION),
|
||||||
|
);
|
||||||
|
t.true(fs.existsSync(`${result.codeqlFolder}.complete`));
|
||||||
|
} else {
|
||||||
|
t.is(path.dirname(result.codeqlFolder), tmpDir);
|
||||||
|
t.deepEqual(toolcache.findAllVersions("CodeQL"), []);
|
||||||
|
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const bundle of ["per-language", "combined", "fallback"] as const) {
|
||||||
|
test.serial(
|
||||||
|
`setupCodeQLBundle preserves the nightly version for a ${bundle} download`,
|
||||||
|
async (t) => {
|
||||||
|
const expectedDate = "30260213";
|
||||||
|
const expectedTag = `codeql-bundle-${expectedDate}`;
|
||||||
|
const expectedVersion = `0.0.0-${expectedDate}`;
|
||||||
|
const baseURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}`;
|
||||||
|
const combinedURL = `${baseURL}/codeql-bundle-linux64.tar.zst`;
|
||||||
|
const perLanguageURL = `${baseURL}/codeql-bundle-javascript-linux64.tar.zst`;
|
||||||
|
const loggedMessages: LoggedMessage[] = [];
|
||||||
|
const logger = getRecordingLogger(loggedMessages);
|
||||||
|
|
||||||
|
stubHostedNightly(expectedTag);
|
||||||
|
delete process.env[EnvVar.HAS_SET_UP_CODEQL];
|
||||||
|
|
||||||
|
const downloadSpy = sinon.spy(setupCodeql, "downloadCodeQL");
|
||||||
|
const extractStub = stubDownloadAndExtract();
|
||||||
|
if (bundle === "fallback") {
|
||||||
|
extractStub.onFirstCall().rejects(new HTTPError("Not Found", 404));
|
||||||
|
}
|
||||||
|
const addDiagnostic = sinon.stub(diagnostics, "addNoLanguageDiagnostic");
|
||||||
|
const features = createFeatures([
|
||||||
|
Feature.PerLanguageBundles,
|
||||||
|
Feature.CleanupToolcacheBundles,
|
||||||
|
]);
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
const result = await setupCodeql.setupCodeQLBundle(
|
||||||
|
"nightly",
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
tmpDir,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
bundle === "combined" ? ["javascript", "python"] : ["javascript"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
const source = downloadSpy.firstCall.args[0];
|
||||||
|
t.is(result.toolsVersion, expectedVersion);
|
||||||
|
t.is(result.toolsVersion, source.toolsVersion);
|
||||||
|
t.is(
|
||||||
|
source.bundle.kind,
|
||||||
|
bundle === "combined" ? "combined" : "per-language",
|
||||||
|
);
|
||||||
|
t.is(result.codeqlFolder, extractStub.lastCall.args[2]);
|
||||||
|
t.is(extractStub.callCount, bundle === "fallback" ? 2 : 1);
|
||||||
|
t.is(downloadSpy.callCount, extractStub.callCount);
|
||||||
|
t.is(
|
||||||
|
extractStub.firstCall.args[0],
|
||||||
|
bundle === "combined" ? combinedURL : perLanguageURL,
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
extractStub.lastCall.args[0],
|
||||||
|
bundle === "per-language" ? perLanguageURL : combinedURL,
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
result.toolsDownloadStatusReport?.bundleLanguage,
|
||||||
|
bundle === "per-language" ? BuiltInLanguage.javascript : undefined,
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
result.toolsDownloadStatusReport?.perLanguageBundleFallback,
|
||||||
|
bundle === "fallback" ? true : undefined,
|
||||||
|
);
|
||||||
|
t.is(
|
||||||
|
addDiagnostic
|
||||||
|
.getCalls()
|
||||||
|
.filter(
|
||||||
|
(call) =>
|
||||||
|
call.args[1].source?.id ===
|
||||||
|
"codeql-action/toolcache-bundle-cleanup",
|
||||||
|
).length,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
if (bundle === "fallback") {
|
||||||
|
t.deepEqual(downloadSpy.secondCall.args[0], {
|
||||||
|
...source,
|
||||||
|
bundle: { kind: "combined", url: combinedURL },
|
||||||
|
});
|
||||||
|
checkExpectedLogMessages(t, loggedMessages, [
|
||||||
|
`No javascript CodeQL bundle was found at ${perLanguageURL}`,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (bundle === "per-language") {
|
||||||
|
t.is(path.dirname(result.codeqlFolder), tmpDir);
|
||||||
|
t.deepEqual(toolcache.findAllVersions("CodeQL"), []);
|
||||||
|
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
|
||||||
|
} else {
|
||||||
|
t.is(
|
||||||
|
result.codeqlFolder,
|
||||||
|
toolsDownload.getToolcacheDirectory(expectedVersion),
|
||||||
|
);
|
||||||
|
t.true(fs.existsSync(`${result.codeqlFolder}.complete`));
|
||||||
|
|
||||||
|
const cachedResult = await setupCodeql.setupCodeQLBundle(
|
||||||
|
"nightly",
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
tmpDir,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
["javascript"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
t.is(cachedResult.toolsSource, setupCodeql.ToolsSource.Toolcache);
|
||||||
|
t.is(cachedResult.toolsVersion, expectedVersion);
|
||||||
|
t.is(cachedResult.codeqlFolder, result.codeqlFolder);
|
||||||
|
t.is(extractStub.callCount, bundle === "fallback" ? 2 : 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const asset of [
|
||||||
|
"codeql-bundle-ruby-linux64.tar.zst",
|
||||||
|
"codeql-bundle-%72uby-linux64.tar.zst",
|
||||||
|
]) {
|
||||||
|
test.serial(
|
||||||
|
`setupCodeQLBundle keeps explicitly requested ${asset} out of the toolcache`,
|
||||||
|
async (t) => {
|
||||||
|
const extractStub = stubDownloadAndExtract();
|
||||||
|
const url = `https://github.com/github/codeql-action/releases/download/codeql-bundle-v9.9.9/${asset}`;
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "self-hosted";
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
const result = await setupCodeql.setupCodeQLBundle(
|
||||||
|
url,
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
tmpDir,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
undefined, // rawLanguages
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
createFeatures([]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.true(extractStub.calledOnce);
|
||||||
|
t.is(extractStub.firstCall.args[0], url);
|
||||||
|
t.is(result.toolsVersion, "9.9.9");
|
||||||
|
t.is(
|
||||||
|
result.toolsDownloadStatusReport?.bundleLanguage,
|
||||||
|
BuiltInLanguage.ruby,
|
||||||
|
);
|
||||||
|
t.is(path.dirname(result.codeqlFolder), tmpDir);
|
||||||
|
t.deepEqual(toolcache.findAllVersions("CodeQL"), []);
|
||||||
|
t.false(fs.existsSync(`${result.codeqlFolder}.complete`));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const error of [
|
||||||
|
new HTTPError("Internal Server Error", 500),
|
||||||
|
new Error("Connection reset"),
|
||||||
|
]) {
|
||||||
|
test.serial(
|
||||||
|
`setupCodeQLBundle does not fall back after ${error.message}`,
|
||||||
|
async (t) => {
|
||||||
|
sinon.stub(process, "platform").value("linux");
|
||||||
|
sinon.stub(process, "arch").value("x64");
|
||||||
|
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||||
|
sinon.stub(tar, "isZstdAvailable").resolves({
|
||||||
|
available: true,
|
||||||
|
foundZstdBinary: true,
|
||||||
|
});
|
||||||
|
const extractStub = sinon
|
||||||
|
.stub(toolsDownload, "downloadAndExtract")
|
||||||
|
.rejects(error);
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
await t.throwsAsync(
|
||||||
|
setupCodeql.setupCodeQLBundle(
|
||||||
|
undefined,
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
tmpDir,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
PER_LANGUAGE_CLI_VERSION,
|
||||||
|
["java"],
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
createFeatures([Feature.PerLanguageBundles]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
),
|
||||||
|
{ is: error },
|
||||||
|
);
|
||||||
|
t.true(extractStub.calledOnce);
|
||||||
|
t.true(
|
||||||
|
extractStub.firstCall.args[0].endsWith(
|
||||||
|
"/codeql-bundle-java-linux64.tar.zst",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test.serial(
|
||||||
|
"setupCodeQLBundle does not substitute a bundle for an explicitly requested one that is missing",
|
||||||
|
async (t) => {
|
||||||
|
const error = new HTTPError("Not Found", 404);
|
||||||
|
const extractStub = sinon
|
||||||
|
.stub(toolsDownload, "downloadAndExtract")
|
||||||
|
.rejects(error);
|
||||||
|
|
||||||
|
await withTmpDir(async (tmpDir) => {
|
||||||
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
await t.throwsAsync(
|
||||||
|
setupCodeql.setupCodeQLBundle(
|
||||||
|
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v9.9.9/codeql-bundle-ruby-linux64.tar.zst",
|
||||||
|
SAMPLE_DOTCOM_API_DETAILS,
|
||||||
|
tmpDir,
|
||||||
|
GitHubVariant.DOTCOM,
|
||||||
|
SAMPLE_DEFAULT_CLI_VERSION,
|
||||||
|
undefined, // rawLanguages
|
||||||
|
false, // useOverlayAwareDefaultCliVersion
|
||||||
|
createFeatures([]),
|
||||||
|
getRunnerLogger(true),
|
||||||
|
),
|
||||||
|
{ is: error },
|
||||||
|
);
|
||||||
|
|
||||||
|
t.true(extractStub.calledOnce);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test.serial(
|
test.serial(
|
||||||
"getEnabledVersionsWithOverlayBaseDatabases returns flag-enabled versions present in cache, sorted desc",
|
"getEnabledVersionsWithOverlayBaseDatabases returns flag-enabled versions present in cache, sorted desc",
|
||||||
async (t) => {
|
async (t) => {
|
||||||
|
|||||||
@@ -30,8 +30,13 @@ import {
|
|||||||
Feature,
|
Feature,
|
||||||
FeatureEnablement,
|
FeatureEnablement,
|
||||||
} from "./feature-flags";
|
} from "./feature-flags";
|
||||||
|
import { BuiltInLanguage } from "./languages";
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
|
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
|
||||||
|
import {
|
||||||
|
getPerLanguageBundleLanguage,
|
||||||
|
tryGetBundleLanguageFromUrl,
|
||||||
|
} from "./per-language-bundles";
|
||||||
import * as tar from "./tar";
|
import * as tar from "./tar";
|
||||||
import {
|
import {
|
||||||
deleteToolcacheBundles,
|
deleteToolcacheBundles,
|
||||||
@@ -72,21 +77,40 @@ function getCodeQLBundleExtension(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the platform component of the CodeQL bundle name for the current platform. */
|
||||||
|
export function getBundlePlatform(): string | undefined {
|
||||||
|
switch (process.platform) {
|
||||||
|
case "win32":
|
||||||
|
return "win64";
|
||||||
|
case "linux":
|
||||||
|
return process.arch === "arm64" ? "linux-arm64" : "linux64";
|
||||||
|
case "darwin":
|
||||||
|
return "osx64";
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the CodeQL bundle asset to download.
|
||||||
|
*
|
||||||
|
* @param compressionMethod The compression method of the bundle.
|
||||||
|
* @param language If provided, the name of the bundle that contains only this language, rather than
|
||||||
|
* the name of the combined bundle that contains every language.
|
||||||
|
*/
|
||||||
export function getCodeQLBundleName(
|
export function getCodeQLBundleName(
|
||||||
compressionMethod: tar.CompressionMethod,
|
compressionMethod: tar.CompressionMethod,
|
||||||
|
language?: BuiltInLanguage,
|
||||||
): string {
|
): string {
|
||||||
const extension = getCodeQLBundleExtension(compressionMethod);
|
const extension = getCodeQLBundleExtension(compressionMethod);
|
||||||
|
const platform = getBundlePlatform();
|
||||||
|
|
||||||
let platform: string;
|
if (platform === undefined) {
|
||||||
if (process.platform === "win32") {
|
|
||||||
platform = "win64";
|
|
||||||
} else if (process.platform === "linux") {
|
|
||||||
platform = process.arch === "arm64" ? "linux-arm64" : "linux64";
|
|
||||||
} else if (process.platform === "darwin") {
|
|
||||||
platform = "osx64";
|
|
||||||
} else {
|
|
||||||
return `codeql-bundle${extension}`;
|
return `codeql-bundle${extension}`;
|
||||||
}
|
}
|
||||||
|
if (language !== undefined) {
|
||||||
|
return `codeql-bundle-${language}-${platform}${extension}`;
|
||||||
|
}
|
||||||
return `codeql-bundle-${platform}${extension}`;
|
return `codeql-bundle-${platform}${extension}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +131,7 @@ export function getCodeQLActionRepository(logger: Logger): string {
|
|||||||
async function getCodeQLBundleDownloadURL(
|
async function getCodeQLBundleDownloadURL(
|
||||||
tagName: string,
|
tagName: string,
|
||||||
apiDetails: api.GitHubApiDetails,
|
apiDetails: api.GitHubApiDetails,
|
||||||
compressionMethod: tar.CompressionMethod,
|
codeQLBundleName: string,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const codeQLActionRepository = getCodeQLActionRepository(logger);
|
const codeQLActionRepository = getCodeQLActionRepository(logger);
|
||||||
@@ -126,7 +150,6 @@ async function getCodeQLBundleDownloadURL(
|
|||||||
return !self.slice(0, index).some((other) => deepEqual(source, other));
|
return !self.slice(0, index).some((other) => deepEqual(source, other));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const codeQLBundleName = getCodeQLBundleName(compressionMethod);
|
|
||||||
for (const downloadSource of uniqueDownloadSources) {
|
for (const downloadSource of uniqueDownloadSources) {
|
||||||
const [apiURL, repository] = downloadSource;
|
const [apiURL, repository] = downloadSource;
|
||||||
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
|
// If we've reached the final case, short-circuit the API check since we know the bundle exists and is public.
|
||||||
@@ -216,7 +239,15 @@ export function convertToSemVer(version: string, logger: Logger): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Describes the contents and location of a downloadable CodeQL bundle. */
|
/** Describes the contents and location of a downloadable CodeQL bundle. */
|
||||||
type CodeQLBundle = { kind: "combined"; url: string };
|
type CodeQLBundle =
|
||||||
|
| { kind: "combined"; url: string }
|
||||||
|
| {
|
||||||
|
kind: "per-language";
|
||||||
|
url: string;
|
||||||
|
language: BuiltInLanguage;
|
||||||
|
/** Only set when the Action selected the bundle, allowing a same-version fallback. */
|
||||||
|
combinedBundleURL?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/** A resolved download, including its bundle identity and version. */
|
/** A resolved download, including its bundle identity and version. */
|
||||||
export interface CodeQLDownloadSource {
|
export interface CodeQLDownloadSource {
|
||||||
@@ -467,6 +498,7 @@ export async function getCodeQLSource(
|
|||||||
* This does not always include a tag name.
|
* This does not always include a tag name.
|
||||||
*/
|
*/
|
||||||
let url: string | undefined;
|
let url: string | undefined;
|
||||||
|
let bundle: CodeQLBundle | undefined;
|
||||||
|
|
||||||
// We allow forcing the nightly CLI via the FF for `dynamic` events (or in test mode) where the
|
// We allow forcing the nightly CLI via the FF for `dynamic` events (or in test mode) where the
|
||||||
// `tools` input cannot be adjusted to explicitly request it.
|
// `tools` input cannot be adjusted to explicitly request it.
|
||||||
@@ -475,7 +507,8 @@ export async function getCodeQLSource(
|
|||||||
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
|
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
|
||||||
|
|
||||||
// For advanced workflows, a value from `CODEQL_NIGHTLY_TOOLS_INPUTS` can be specified explicitly
|
// For advanced workflows, a value from `CODEQL_NIGHTLY_TOOLS_INPUTS` can be specified explicitly
|
||||||
// for the `tools` input in the workflow file.
|
// for the `tools` input. This is the computed input, so it may come from the repository property
|
||||||
|
// rather than the workflow file.
|
||||||
const nightlyRequestedByToolsInput =
|
const nightlyRequestedByToolsInput =
|
||||||
toolsInput !== undefined &&
|
toolsInput !== undefined &&
|
||||||
CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput);
|
CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput);
|
||||||
@@ -509,7 +542,8 @@ export async function getCodeQLSource(
|
|||||||
`Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`,
|
`Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
toolsInput = await getNightlyToolsUrl(logger);
|
bundle = await getNightlyBundle(rawLanguages, variant, features, logger);
|
||||||
|
toolsInput = bundle.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -738,12 +772,42 @@ export async function getCodeQLSource(
|
|||||||
? "zstd"
|
? "zstd"
|
||||||
: "gzip";
|
: "gzip";
|
||||||
|
|
||||||
url = await getCodeQLBundleDownloadURL(
|
const perLanguageBundleLanguage = await getPerLanguageBundleLanguage(
|
||||||
tagName!,
|
{
|
||||||
apiDetails,
|
rawLanguages,
|
||||||
compressionMethod,
|
cliVersion,
|
||||||
|
compressionMethod,
|
||||||
|
platform: getBundlePlatform(),
|
||||||
|
variant,
|
||||||
|
},
|
||||||
|
features,
|
||||||
logger,
|
logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const resolveBundleURL = (language?: BuiltInLanguage) =>
|
||||||
|
getCodeQLBundleDownloadURL(
|
||||||
|
tagName!,
|
||||||
|
apiDetails,
|
||||||
|
getCodeQLBundleName(compressionMethod, language),
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (perLanguageBundleLanguage !== undefined) {
|
||||||
|
logger.info(
|
||||||
|
`Downloading the ${perLanguageBundleLanguage} CodeQL bundle, since ${perLanguageBundleLanguage} ` +
|
||||||
|
"is the only language being analyzed.",
|
||||||
|
);
|
||||||
|
url = await resolveBundleURL(perLanguageBundleLanguage);
|
||||||
|
bundle = {
|
||||||
|
kind: "per-language",
|
||||||
|
url,
|
||||||
|
language: perLanguageBundleLanguage,
|
||||||
|
combinedBundleURL: await resolveBundleURL(),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
url = await resolveBundleURL();
|
||||||
|
bundle = { kind: "combined", url };
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const method = tar.inferCompressionMethod(url);
|
const method = tar.inferCompressionMethod(url);
|
||||||
if (method === undefined) {
|
if (method === undefined) {
|
||||||
@@ -753,6 +817,20 @@ export async function getCodeQLSource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
compressionMethod = method;
|
compressionMethod = method;
|
||||||
|
|
||||||
|
if (bundle === undefined) {
|
||||||
|
// Explicit per-language URLs must also stay out of the toolcache, but have no fallback.
|
||||||
|
const language = tryGetBundleLanguageFromUrl(url);
|
||||||
|
bundle =
|
||||||
|
language === undefined
|
||||||
|
? { kind: "combined", url }
|
||||||
|
: { kind: "per-language", url, language };
|
||||||
|
}
|
||||||
|
if (bundle.kind === "per-language") {
|
||||||
|
logger.info(
|
||||||
|
`${url} appears to be a CodeQL bundle that contains only ${bundle.language}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cliVersion) {
|
if (cliVersion) {
|
||||||
@@ -761,7 +839,7 @@ export async function getCodeQLSource(
|
|||||||
logger.info(`Using CodeQL CLI sourced from ${url} .`);
|
logger.info(`Using CodeQL CLI sourced from ${url} .`);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
bundle: { kind: "combined", url },
|
bundle,
|
||||||
bundleVersion,
|
bundleVersion,
|
||||||
cliVersion,
|
cliVersion,
|
||||||
compressionMethod,
|
compressionMethod,
|
||||||
@@ -841,8 +919,11 @@ export const downloadCodeQL = async function (
|
|||||||
writeToolcacheMarkerFile(toolcacheDestination, logger);
|
writeToolcacheMarkerFile(toolcacheDestination, logger);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Could not cache CodeQL tools because we could not determine the bundle version from the " +
|
bundle.kind === "per-language"
|
||||||
`URL ${codeqlURL}.`,
|
? "Not caching the CodeQL tools because they came from a bundle that contains only a " +
|
||||||
|
"single language."
|
||||||
|
: "Could not cache CodeQL tools because we could not determine the bundle version from the " +
|
||||||
|
`URL ${codeqlURL}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,7 +941,8 @@ function getToolcacheDestination(
|
|||||||
source: CodeQLDownloadSource,
|
source: CodeQLDownloadSource,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
if (!source.bundleVersion) {
|
// Per-language bundles must not be stored in the toolcache.
|
||||||
|
if (source.bundle.kind !== "combined" || !source.bundleVersion) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,6 +1128,9 @@ export async function setupCodeQLBundle(
|
|||||||
/**
|
/**
|
||||||
* Performs eligible toolcache cleanup once, then downloads and extracts the resolved bundle.
|
* Performs eligible toolcache cleanup once, then downloads and extracts the resolved bundle.
|
||||||
*
|
*
|
||||||
|
* If `source` refers to a bundle for a single language and that bundle turns out not to exist, this
|
||||||
|
* falls back to downloading the combined bundle.
|
||||||
|
*
|
||||||
* @returns The extraction directory and download timings.
|
* @returns The extraction directory and download timings.
|
||||||
*/
|
*/
|
||||||
export async function downloadCodeQLBundle(
|
export async function downloadCodeQLBundle(
|
||||||
@@ -1058,14 +1143,60 @@ export async function downloadCodeQLBundle(
|
|||||||
codeqlFolder: string;
|
codeqlFolder: string;
|
||||||
statusReport: ToolsDownloadStatusReport;
|
statusReport: ToolsDownloadStatusReport;
|
||||||
}> {
|
}> {
|
||||||
|
const { bundle } = source;
|
||||||
|
const { logger } = action;
|
||||||
|
|
||||||
await tryDeleteToolcacheBundles(action);
|
await tryDeleteToolcacheBundles(action);
|
||||||
return await downloadCodeQL(
|
|
||||||
source,
|
try {
|
||||||
apiDetails,
|
const result = await downloadCodeQL(
|
||||||
tarVersion,
|
source,
|
||||||
tempDir,
|
apiDetails,
|
||||||
action.logger,
|
tarVersion,
|
||||||
);
|
tempDir,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
return bundle.kind === "combined"
|
||||||
|
? result
|
||||||
|
: {
|
||||||
|
...result,
|
||||||
|
statusReport: {
|
||||||
|
...result.statusReport,
|
||||||
|
bundleLanguage: bundle.language,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (
|
||||||
|
bundle.kind !== "per-language" ||
|
||||||
|
bundle.combinedBundleURL === undefined ||
|
||||||
|
util.asHTTPError(e)?.status !== 404
|
||||||
|
) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
logger.warning(
|
||||||
|
`No ${bundle.language} CodeQL bundle was found at ${bundle.url}, so ` +
|
||||||
|
"falling back to the bundle that contains all languages. This analysis will still " +
|
||||||
|
"produce correct results, but will take longer to set up.",
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await downloadCodeQL(
|
||||||
|
{
|
||||||
|
...source,
|
||||||
|
bundle: { kind: "combined", url: bundle.combinedBundleURL },
|
||||||
|
},
|
||||||
|
apiDetails,
|
||||||
|
tarVersion,
|
||||||
|
tempDir,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
statusReport: {
|
||||||
|
...result.statusReport,
|
||||||
|
perLanguageBundleFallback: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function useZstdBundle(
|
async function useZstdBundle(
|
||||||
@@ -1084,10 +1215,13 @@ function getTempExtractionDir(tempDir: string) {
|
|||||||
return path.join(tempDir, uuidV4());
|
return path.join(tempDir, uuidV4());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Selects a bundle from the latest nightly, with a same-release fallback when applicable. */
|
||||||
* Get the URL of the latest nightly CodeQL bundle.
|
async function getNightlyBundle(
|
||||||
*/
|
rawLanguages: string[] | undefined,
|
||||||
async function getNightlyToolsUrl(logger: Logger) {
|
variant: util.GitHubVariant,
|
||||||
|
features: FeatureEnablement,
|
||||||
|
logger: Logger,
|
||||||
|
): Promise<CodeQLBundle> {
|
||||||
const zstdAvailability = await tar.isZstdAvailable(logger);
|
const zstdAvailability = await tar.isZstdAvailable(logger);
|
||||||
// The nightly is guaranteed to have a zstd bundle
|
// The nightly is guaranteed to have a zstd bundle
|
||||||
const compressionMethod = (await useZstdBundle(
|
const compressionMethod = (await useZstdBundle(
|
||||||
@@ -1097,6 +1231,19 @@ async function getNightlyToolsUrl(logger: Logger) {
|
|||||||
? "zstd"
|
? "zstd"
|
||||||
: "gzip";
|
: "gzip";
|
||||||
|
|
||||||
|
const language = await getPerLanguageBundleLanguage(
|
||||||
|
{
|
||||||
|
rawLanguages,
|
||||||
|
cliVersion: undefined,
|
||||||
|
compressionMethod,
|
||||||
|
platform: getBundlePlatform(),
|
||||||
|
variant,
|
||||||
|
isNightly: true,
|
||||||
|
},
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Since nightlies are prereleases, we can't just download the latest release
|
// Since nightlies are prereleases, we can't just download the latest release
|
||||||
// on the repository. So instead we need to find the latest pre-release
|
// on the repository. So instead we need to find the latest pre-release
|
||||||
@@ -1112,7 +1259,17 @@ async function getNightlyToolsUrl(logger: Logger) {
|
|||||||
if (!latestRelease) {
|
if (!latestRelease) {
|
||||||
throw new Error("Could not find the latest nightly release.");
|
throw new Error("Could not find the latest nightly release.");
|
||||||
}
|
}
|
||||||
return `https://github.com/${CODEQL_NIGHTLIES_REPOSITORY_OWNER}/${CODEQL_NIGHTLIES_REPOSITORY_NAME}/releases/download/${latestRelease.tag_name}/${getCodeQLBundleName(compressionMethod)}`;
|
const assetUrl = (name: string) =>
|
||||||
|
`https://github.com/${CODEQL_NIGHTLIES_REPOSITORY_OWNER}/${CODEQL_NIGHTLIES_REPOSITORY_NAME}/releases/download/${latestRelease.tag_name}/${name}`;
|
||||||
|
const url = assetUrl(getCodeQLBundleName(compressionMethod, language));
|
||||||
|
return language === undefined
|
||||||
|
? { kind: "combined", url }
|
||||||
|
: {
|
||||||
|
kind: "per-language",
|
||||||
|
url,
|
||||||
|
language,
|
||||||
|
combinedBundleURL: assetUrl(getCodeQLBundleName(compressionMethod)),
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to retrieve the latest nightly release: ${util.wrapError(e)}`,
|
`Failed to retrieve the latest nightly release: ${util.wrapError(e)}`,
|
||||||
|
|||||||
@@ -645,6 +645,13 @@ export interface InitToolsDownloadFields {
|
|||||||
* Whether the relevant tools dotcom feature flags have been misconfigured.
|
* Whether the relevant tools dotcom feature flags have been misconfigured.
|
||||||
* Only populated if we attempt to determine the default version based on the dotcom feature flags. */
|
* Only populated if we attempt to determine the default version based on the dotcom feature flags. */
|
||||||
tools_feature_flags_valid?: boolean;
|
tools_feature_flags_valid?: boolean;
|
||||||
|
/** The language of the single-language bundle that was downloaded, if any. */
|
||||||
|
tools_bundle_language?: string;
|
||||||
|
/**
|
||||||
|
* Whether we tried to download a single-language bundle, but it did not exist and we fell back to
|
||||||
|
* the combined bundle.
|
||||||
|
*/
|
||||||
|
tools_per_language_bundle_fallback?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ export type ToolsDownloadStatusReport = {
|
|||||||
* spent on a streaming attempt that failed and fell back to downloading before extracting.
|
* spent on a streaming attempt that failed and fell back to downloading before extracting.
|
||||||
*/
|
*/
|
||||||
totalDurationMs: number;
|
totalDurationMs: number;
|
||||||
|
/** The language of the single-language bundle that was downloaded, if any. */
|
||||||
|
bundleLanguage?: string;
|
||||||
|
/**
|
||||||
|
* Whether we tried to download a single-language bundle, but it did not exist and we fell back to
|
||||||
|
* the combined bundle.
|
||||||
|
*/
|
||||||
|
perLanguageBundleFallback?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function downloadAndExtract(
|
export async function downloadAndExtract(
|
||||||
|
|||||||
Reference in New Issue
Block a user