diff --git a/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml b/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml index e0ff968bd..bad4921eb 100644 --- a/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml +++ b/.github/workflows/__autobuild-direct-tracing-with-working-dir.yml @@ -63,7 +63,7 @@ jobs: - name: Check out repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: java-version: ${{ inputs.java-version || '17' }} distribution: temurin diff --git a/.github/workflows/__build-mode-autobuild.yml b/.github/workflows/__build-mode-autobuild.yml index edc1d505d..76e3f348a 100644 --- a/.github/workflows/__build-mode-autobuild.yml +++ b/.github/workflows/__build-mode-autobuild.yml @@ -63,7 +63,7 @@ jobs: - name: Check out repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: java-version: ${{ inputs.java-version || '17' }} distribution: temurin diff --git a/.github/workflows/__bundle-from-toolcache.yml b/.github/workflows/__bundle-from-toolcache.yml index a1c1fade0..1a307ba2e 100644 --- a/.github/workflows/__bundle-from-toolcache.yml +++ b/.github/workflows/__bundle-from-toolcache.yml @@ -46,6 +46,15 @@ jobs: steps: - name: Check out repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Node.js + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 20.x + cache: npm + - name: Install newer npm + run: npm install -g npm@11.19.1 + - name: Install dependencies + run: npm ci - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -53,31 +62,20 @@ jobs: version: ${{ matrix.version }} use-all-platform-bundle: 'false' setup-kotlin: 'true' - - name: Install @actions/tool-cache - run: npm install @actions/tool-cache@3 - name: Check toolcache contains CodeQL continue-on-error: true - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - if (allCodeqlVersions.length === 0) { - throw new Error(`CodeQL could not be found in the toolcache`); - } + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts installed + - id: setup-codeql uses: ./../action/setup-codeql with: tools: ${{ steps.prepare-test.outputs.tools-url }} + - name: Check CodeQL is installed within the toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - console.log(`Found CodeQL versions: ${allCodeqlVersions}`); - if (allCodeqlVersions.length === 0) { - throw new Error('CodeQL not found in toolcache'); - } + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts installed env: CODEQL_ACTION_TEST_MODE: true diff --git a/.github/workflows/__bundle-toolcache.yml b/.github/workflows/__bundle-toolcache.yml index 0055f9470..96a901cf3 100644 --- a/.github/workflows/__bundle-toolcache.yml +++ b/.github/workflows/__bundle-toolcache.yml @@ -50,6 +50,15 @@ jobs: steps: - name: Check out repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Node.js + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 20.x + cache: npm + - name: Install newer npm + run: npm install -g npm@11.19.1 + - name: Install dependencies + run: npm ci - name: Prepare test id: prepare-test uses: ./.github/actions/prepare-test @@ -58,47 +67,30 @@ jobs: use-all-platform-bundle: 'false' setup-kotlin: 'true' - name: Remove CodeQL from toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const fs = require('fs'); - const path = require('path'); - const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL'); - fs.rmdirSync(codeqlPath, { recursive: true }); - - name: Install @actions/tool-cache - run: npm install @actions/tool-cache@3 + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts clear + - name: Check toolcache does not contain CodeQL - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - if (allCodeqlVersions.length !== 0) { - throw new Error(`CodeQL should not be found in the toolcache, but found ${allCodeqlVersions}`); - } - console.log('No versions of CodeQL found in the toolcache'); + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts absent + - id: init uses: ./../action/init with: # Request multiple languages so this check uses the combined bundle. languages: javascript,python tools: ${{ steps.prepare-test.outputs.tools-url }} + - uses: ./../action/analyze with: output: ${{ runner.temp }}/results upload-database: false + - name: Check CodeQL is installed within the toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - console.log(`Found CodeQL versions: ${allCodeqlVersions}`); - if (allCodeqlVersions.length === 0) { - throw new Error('CodeQL not found in toolcache'); - } - if (allCodeqlVersions.length > 1) { - throw new Error('Multiple CodeQL versions found in toolcache'); - } + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts installed -n 1 env: CODEQL_ACTION_TEST_MODE: true diff --git a/.github/workflows/__diagnostics-without-db.yml b/.github/workflows/__diagnostics-without-db.yml new file mode 100644 index 000000000..28472c95a --- /dev/null +++ b/.github/workflows/__diagnostics-without-db.yml @@ -0,0 +1,143 @@ +# 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 - Diagnostic export without database +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: diagnostics-without-db-${{github.ref}} +jobs: + diagnostics-without-db: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + version: linked + name: Diagnostic export without database + if: github.triggering_actor != 'dependabot[bot]' + permissions: + contents: read + security-events: read + timeout-minutes: 45 + runs-on: ${{ matrix.os }} + steps: + # We deliberately need to make this step fail before the database is initialised, but after + # some diagnostic has been produced. The simplest way to do that right now is to: + # + # - Produce a diagnostic before the db is initialised by forcing the use of a nightly release + # with the `CODEQL_ACTION_FORCE_NIGHTLY` feature flag. + # - Cause `init` to abort because we are attempting to analyse Swift on a non-macOS runner. + # + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Node.js + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 20.x + cache: npm + - name: Install newer npm + run: npm install -g npm@11.19.1 + - name: Install dependencies + run: npm ci + - name: Prepare test + id: prepare-test + uses: ./.github/actions/prepare-test + with: + version: ${{ matrix.version }} + use-all-platform-bundle: 'false' + setup-kotlin: 'true' + - name: Initialise failing analysis + id: init + uses: ./../action/init + continue-on-error: true + env: + CODEQL_ACTION_FORCE_NIGHTLY: true + with: + languages: swift + tools: ${{ steps.prepare-test.outputs.tools-url }} + + - name: Analyze + uses: ./analyze + # In a real workflow, this step wouldn't run. Since we used `continue-on-error` + # above, we manually disable it with an `if` condition. Having this step is required + # for the init post-action because it checks the workflow definition for e.g. the + # `category` input. Without this step, that part of the init post-action would fail. + if: false + with: + category: '/test-codeql-version:${{ matrix.version }}' + + - name: Check diagnostics appear on disk + env: + DIAGNOSTICS_DIR: '${{ runner.temp }}/codeql-action-temp-diagnostics/' + run: | + cd ../action + npx tsx ./pr-checks/diagnostics-without-db.ts + env: + CODEQL_ACTION_SKIP_GENERATE_FAILED_SARIF: false + CODEQL_ACTION_SKIP_SARIF_UPLOAD: true + CODEQL_ACTION_TESTING_ENVIRONMENT: codeql-action-pr-checks + CODEQL_ACTION_TEST_MODE: true + diagnostics-without-db-validation: + if: github.triggering_actor != 'dependabot[bot]' + needs: + - diagnostics-without-db + permissions: + contents: read + security-events: read + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Node.js + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 20.x + cache: npm + - name: Install newer npm + run: npm install -g npm@11.19.1 + - name: Install dependencies + run: npm ci + - name: Download workflow artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: test-mode-sarif-* + path: sarif + + - name: List artifact contents + run: ls -laR ./sarif + + - name: Check diagnostics appear in SARIF + env: + SARIF_PATH: "./sarif/codeql-failed-run.sarif" + DIAGNOSTIC_ID: "codeql-action/forced-nightly-cli" + DIAGNOSTIC_NAME: "A nightly release of CodeQL was used" + EXPECTED_LOCATIONS: "0" + EXPECTED_MESSAGE: | + GitHub configured this analysis to use a nightly release of CodeQL to allow you to preview changes from an upcoming release. + run: npx tsx ./pr-checks/check-diagnostics.ts + env: + CODEQL_ACTION_TEST_MODE: true diff --git a/.github/workflows/__rubocop-multi-language.yml b/.github/workflows/__rubocop-multi-language.yml index 7bda6ce9f..c3348db6c 100644 --- a/.github/workflows/__rubocop-multi-language.yml +++ b/.github/workflows/__rubocop-multi-language.yml @@ -54,7 +54,7 @@ jobs: use-all-platform-bundle: 'false' setup-kotlin: 'true' - name: Set up Ruby - uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 + uses: ruby/setup-ruby@651ff0d62c5c79bed6d53ec518387aae20e72c1a # v1.329.0 with: ruby-version: 2.6 - name: Install Code Scanning integration diff --git a/.github/workflows/__submit-sarif-failure.yml b/.github/workflows/__submit-sarif-failure.yml index 099e93001..36f7be4eb 100644 --- a/.github/workflows/__submit-sarif-failure.yml +++ b/.github/workflows/__submit-sarif-failure.yml @@ -71,7 +71,9 @@ jobs: run: exit 1 - uses: ./analyze # In a real workflow, this step wouldn't run. Since we used `continue-on-error` - # above, we manually disable it with an `if` condition. + # above, we manually disable it with an `if` condition. Having this step is required + # for the init post-action because it checks the workflow definition for e.g. the + # `category` input. Without this step, that part of the init post-action would fail. if: false with: category: '/test-codeql-version:${{ matrix.version }}' diff --git a/.github/workflows/post-release-mergeback.yml b/.github/workflows/post-release-mergeback.yml index 354da0afd..76ef6e555 100644 --- a/.github/workflows/post-release-mergeback.yml +++ b/.github/workflows/post-release-mergeback.yml @@ -134,7 +134,7 @@ jobs: echo "::endgroup::" - name: Generate token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0 id: app-token with: app-id: ${{ vars.AUTOMATION_APP_ID }} diff --git a/.github/workflows/rollback-release.yml b/.github/workflows/rollback-release.yml index 01651796e..14a8490b8 100644 --- a/.github/workflows/rollback-release.yml +++ b/.github/workflows/rollback-release.yml @@ -139,7 +139,7 @@ jobs: - name: Generate token if: github.event_name == 'workflow_dispatch' - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0 id: app-token with: app-id: ${{ vars.AUTOMATION_APP_ID }} diff --git a/.github/workflows/update-release-branch.yml b/.github/workflows/update-release-branch.yml index a506b87ce..981a38eac 100644 --- a/.github/workflows/update-release-branch.yml +++ b/.github/workflows/update-release-branch.yml @@ -95,7 +95,7 @@ jobs: pull-requests: write # needed to create pull request steps: - name: Generate token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0 id: app-token with: app-id: ${{ vars.AUTOMATION_APP_ID }} diff --git a/CHANGELOG.md b/CHANGELOG.md index b88dff6aa..8a96e0a9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th ## [UNRELEASED] +No user facing changes. + +## 4.39.1 - 21 Oct 2026 + - Update default CodeQL bundle version to [2.27.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.3). [#4256](https://github.com/github/codeql-action/pull/4256) ## 4.39.0 - 15 Oct 2026 diff --git a/lib/entry-points.js b/lib/entry-points.js index da54da44e..2f5cc50fc 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -213,7 +213,7 @@ var require_file_command = __commonJS({ exports2.issueFileCommand = issueFileCommand; exports2.prepareKeyValueMessage = prepareKeyValueMessage; var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var os7 = __importStar2(require("os")); var utils_1 = require_utils(); function issueFileCommand(command, message) { @@ -221,10 +221,10 @@ var require_file_command = __commonJS({ if (!filePath) { throw new Error(`Unable to find environment variable for file command ${command}`); } - if (!fs32.existsSync(filePath)) { + if (!fs33.existsSync(filePath)) { throw new Error(`Missing file at path: ${filePath}`); } - fs32.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { + fs33.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { encoding: "utf8" }); } @@ -20443,13 +20443,13 @@ var require_io_util = __commonJS({ exports2.isRooted = isRooted; exports2.tryGetExecutablePath = tryGetExecutablePath; exports2.getCmdPath = getCmdPath; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); - _a6 = fs32.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; + _a6 = fs33.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; exports2.IS_WINDOWS = process.platform === "win32"; function readlink(fsPath) { return __awaiter2(this, void 0, void 0, function* () { - const result = yield fs32.promises.readlink(fsPath); + const result = yield fs33.promises.readlink(fsPath); if (exports2.IS_WINDOWS && !result.endsWith("\\")) { return `${result}\\`; } @@ -20457,7 +20457,7 @@ var require_io_util = __commonJS({ }); } exports2.UV_FS_O_EXLOCK = 268435456; - exports2.READONLY = fs32.constants.O_RDONLY; + exports2.READONLY = fs33.constants.O_RDONLY; function exists(fsPath) { return __awaiter2(this, void 0, void 0, function* () { try { @@ -33065,7 +33065,7 @@ var require_internal_globber = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DefaultGlobber = void 0; var core25 = __importStar2(require_core()); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var globOptionsHelper = __importStar2(require_internal_glob_options_helper()); var path30 = __importStar2(require("path")); var patternHelper = __importStar2(require_internal_pattern_helper()); @@ -33119,7 +33119,7 @@ var require_internal_globber = __commonJS({ for (const searchPath of patternHelper.getSearchPaths(patterns)) { core25.debug(`Search path '${searchPath}'`); try { - yield __await2(fs32.promises.lstat(searchPath)); + yield __await2(fs33.promises.lstat(searchPath)); } catch (err) { if (err.code === "ENOENT") { continue; @@ -33153,7 +33153,7 @@ var require_internal_globber = __commonJS({ continue; } const childLevel = item.level + 1; - const childItems = (yield __await2(fs32.promises.readdir(item.path))).map((x) => new internal_search_state_1.SearchState(path30.join(item.path, x), childLevel)); + const childItems = (yield __await2(fs33.promises.readdir(item.path))).map((x) => new internal_search_state_1.SearchState(path30.join(item.path, x), childLevel)); stack.push(...childItems.reverse()); } else if (match2 & internal_match_kind_1.MatchKind.File) { yield yield __await2(item.path); @@ -33188,7 +33188,7 @@ var require_internal_globber = __commonJS({ let stats; if (options.followSymbolicLinks) { try { - stats = yield fs32.promises.stat(item.path); + stats = yield fs33.promises.stat(item.path); } catch (err) { if (err.code === "ENOENT") { if (options.omitBrokenSymbolicLinks) { @@ -33200,10 +33200,10 @@ var require_internal_globber = __commonJS({ throw err; } } else { - stats = yield fs32.promises.lstat(item.path); + stats = yield fs33.promises.lstat(item.path); } if (stats.isDirectory() && options.followSymbolicLinks) { - const realPath = yield fs32.promises.realpath(item.path); + const realPath = yield fs33.promises.realpath(item.path); while (traversalChain.length >= item.level) { traversalChain.pop(); } @@ -33312,7 +33312,7 @@ var require_internal_hash_files = __commonJS({ exports2.hashFiles = hashFiles2; var crypto3 = __importStar2(require("crypto")); var core25 = __importStar2(require_core()); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var stream2 = __importStar2(require("stream")); var util4 = __importStar2(require("util")); var path30 = __importStar2(require("path")); @@ -33335,13 +33335,13 @@ var require_internal_hash_files = __commonJS({ writeDelegate(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`); continue; } - if (fs32.statSync(file).isDirectory()) { + if (fs33.statSync(file).isDirectory()) { writeDelegate(`Skip directory '${file}'.`); continue; } const hash2 = crypto3.createHash("sha256"); const pipeline2 = util4.promisify(stream2.pipeline); - yield pipeline2(fs32.createReadStream(file), hash2); + yield pipeline2(fs33.createReadStream(file), hash2); result.write(hash2.digest()); count++; if (!hasMatch) { @@ -34717,7 +34717,7 @@ var require_cacheUtils = __commonJS({ var glob2 = __importStar2(require_glob()); var io9 = __importStar2(require_io()); var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); var semver11 = __importStar2(require_semver3()); var util4 = __importStar2(require("util")); @@ -34746,7 +34746,7 @@ var require_cacheUtils = __commonJS({ }); } function getArchiveFileSizeInBytes(filePath) { - return fs32.statSync(filePath).size; + return fs33.statSync(filePath).size; } function resolvePaths(patterns) { return __awaiter2(this, void 0, void 0, function* () { @@ -34784,7 +34784,7 @@ var require_cacheUtils = __commonJS({ } function unlinkFile(filePath) { return __awaiter2(this, void 0, void 0, function* () { - return util4.promisify(fs32.unlink)(filePath); + return util4.promisify(fs33.unlink)(filePath); }); } function getVersion(app_1) { @@ -34826,7 +34826,7 @@ var require_cacheUtils = __commonJS({ } function getGnuTarPathOnWindows() { return __awaiter2(this, void 0, void 0, function* () { - if (fs32.existsSync(constants_1.GnuTarPathOnWindows)) { + if (fs33.existsSync(constants_1.GnuTarPathOnWindows)) { return constants_1.GnuTarPathOnWindows; } const versionOutput = yield getVersion("tar"); @@ -99550,7 +99550,7 @@ var require_downloadUtils = __commonJS({ var http_client_1 = require_lib(); var storage_blob_1 = require_commonjs15(); var buffer = __importStar2(require("buffer")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var stream2 = __importStar2(require("stream")); var util4 = __importStar2(require("util")); var utils = __importStar2(require_cacheUtils()); @@ -99661,7 +99661,7 @@ var require_downloadUtils = __commonJS({ exports2.DownloadProgress = DownloadProgress; function downloadCacheHttpClient(archiveLocation, archivePath) { return __awaiter2(this, void 0, void 0, function* () { - const writeStream = fs32.createWriteStream(archivePath); + const writeStream = fs33.createWriteStream(archivePath); const httpClient = new http_client_1.HttpClient("actions/cache"); const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)("downloadCache", () => __awaiter2(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); @@ -99686,7 +99686,7 @@ var require_downloadUtils = __commonJS({ function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options) { return __awaiter2(this, void 0, void 0, function* () { var _a6; - const archiveDescriptor = yield fs32.promises.open(archivePath, "w"); + const archiveDescriptor = yield fs33.promises.open(archivePath, "w"); const httpClient = new http_client_1.HttpClient("actions/cache", void 0, { socketTimeout: options.timeoutInMs, keepAlive: true @@ -99802,7 +99802,7 @@ var require_downloadUtils = __commonJS({ } else { const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); const downloadProgress = new DownloadProgress(contentLength); - const fd = fs32.openSync(archivePath, "w"); + const fd = fs33.openSync(archivePath, "w"); try { downloadProgress.startDisplayTimer(); const controller = new abort_controller_1.AbortController(); @@ -99820,12 +99820,12 @@ var require_downloadUtils = __commonJS({ controller.abort(); throw new Error("Aborting cache download as the download time exceeded the timeout."); } else if (Buffer.isBuffer(result)) { - fs32.writeFileSync(fd, result); + fs33.writeFileSync(fd, result); } } } finally { downloadProgress.stopDisplayTimer(); - fs32.closeSync(fd); + fs33.closeSync(fd); } } }); @@ -100164,7 +100164,7 @@ var require_cacheHttpClient = __commonJS({ var core25 = __importStar2(require_core()); var http_client_1 = require_lib(); var auth_1 = require_auth(); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var url_1 = require("url"); var utils = __importStar2(require_cacheUtils()); var uploadUtils_1 = require_uploadUtils(); @@ -100305,7 +100305,7 @@ Other caches with similar key:`); return __awaiter2(this, void 0, void 0, function* () { const fileSize = utils.getArchiveFileSizeInBytes(archivePath); const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs32.openSync(archivePath, "r"); + const fd = fs33.openSync(archivePath, "r"); const uploadOptions = (0, options_1.getUploadOptions)(options); const concurrency = utils.assertDefined("uploadConcurrency", uploadOptions.uploadConcurrency); const maxChunkSize = utils.assertDefined("uploadChunkSize", uploadOptions.uploadChunkSize); @@ -100319,7 +100319,7 @@ Other caches with similar key:`); const start = offset; const end = offset + chunkSize - 1; offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs32.createReadStream(archivePath, { + yield uploadChunk(httpClient, resourceUrl, () => fs33.createReadStream(archivePath, { fd, start, end, @@ -100330,7 +100330,7 @@ Other caches with similar key:`); } }))); } finally { - fs32.closeSync(fd); + fs33.closeSync(fd); } return; }); @@ -106365,7 +106365,7 @@ var require_manifest = __commonJS({ var core_1 = require_core(); var os7 = require("os"); var cp = require("child_process"); - var fs32 = require("fs"); + var fs33 = require("fs"); function _findMatch(versionSpec, stable, candidates, archFilter) { return __awaiter2(this, void 0, void 0, function* () { const platFilter = os7.platform(); @@ -106427,10 +106427,10 @@ var require_manifest = __commonJS({ const lsbReleaseFile = "/etc/lsb-release"; const osReleaseFile = "/etc/os-release"; let contents = ""; - if (fs32.existsSync(lsbReleaseFile)) { - contents = fs32.readFileSync(lsbReleaseFile).toString(); - } else if (fs32.existsSync(osReleaseFile)) { - contents = fs32.readFileSync(osReleaseFile).toString(); + if (fs33.existsSync(lsbReleaseFile)) { + contents = fs33.readFileSync(lsbReleaseFile).toString(); + } else if (fs33.existsSync(osReleaseFile)) { + contents = fs33.readFileSync(osReleaseFile).toString(); } return contents; } @@ -106639,7 +106639,7 @@ var require_tool_cache = __commonJS({ var core25 = __importStar2(require_core()); var io9 = __importStar2(require_io()); var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var mm = __importStar2(require_manifest()); var os7 = __importStar2(require("os")); var path30 = __importStar2(require("path")); @@ -106685,7 +106685,7 @@ var require_tool_cache = __commonJS({ } function downloadToolAttempt(url2, dest, auth2, headers) { return __awaiter2(this, void 0, void 0, function* () { - if (fs32.existsSync(dest)) { + if (fs33.existsSync(dest)) { throw new Error(`Destination file path ${dest} already exists`); } const http = new httpm.HttpClient(userAgent2, [], { @@ -106709,7 +106709,7 @@ var require_tool_cache = __commonJS({ const readStream = responseMessageFactory(); let succeeded = false; try { - yield pipeline2(readStream, fs32.createWriteStream(dest)); + yield pipeline2(readStream, fs33.createWriteStream(dest)); core25.debug("download complete"); succeeded = true; return dest; @@ -106921,11 +106921,11 @@ var require_tool_cache = __commonJS({ arch2 = arch2 || os7.arch(); core25.debug(`Caching tool ${tool} ${version} ${arch2}`); core25.debug(`source dir: ${sourceDir}`); - if (!fs32.statSync(sourceDir).isDirectory()) { + if (!fs33.statSync(sourceDir).isDirectory()) { throw new Error("sourceDir is not a directory"); } const destPath = yield _createToolPath(tool, version, arch2); - for (const itemName of fs32.readdirSync(sourceDir)) { + for (const itemName of fs33.readdirSync(sourceDir)) { const s = path30.join(sourceDir, itemName); yield io9.cp(s, destPath, { recursive: true }); } @@ -106939,7 +106939,7 @@ var require_tool_cache = __commonJS({ arch2 = arch2 || os7.arch(); core25.debug(`Caching tool ${tool} ${version} ${arch2}`); core25.debug(`source file: ${sourceFile}`); - if (!fs32.statSync(sourceFile).isFile()) { + if (!fs33.statSync(sourceFile).isFile()) { throw new Error("sourceFile is not a file"); } const destFolder = yield _createToolPath(tool, version, arch2); @@ -106968,7 +106968,7 @@ var require_tool_cache = __commonJS({ versionSpec = semver11.clean(versionSpec) || ""; const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2); core25.debug(`checking cache: ${cachePath}`); - if (fs32.existsSync(cachePath) && fs32.existsSync(`${cachePath}.complete`)) { + if (fs33.existsSync(cachePath) && fs33.existsSync(`${cachePath}.complete`)) { core25.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch2}`); toolPath = cachePath; } else { @@ -106981,12 +106981,12 @@ var require_tool_cache = __commonJS({ const versions = []; arch2 = arch2 || os7.arch(); const toolPath = path30.join(_getCacheDirectory(), toolName); - if (fs32.existsSync(toolPath)) { - const children = fs32.readdirSync(toolPath); + if (fs33.existsSync(toolPath)) { + const children = fs33.readdirSync(toolPath); for (const child of children) { if (isExplicitVersion(child)) { const fullPath = path30.join(toolPath, child, arch2 || ""); - if (fs32.existsSync(fullPath) && fs32.existsSync(`${fullPath}.complete`)) { + if (fs33.existsSync(fullPath) && fs33.existsSync(`${fullPath}.complete`)) { versions.push(child); } } @@ -107057,7 +107057,7 @@ var require_tool_cache = __commonJS({ function _completeToolPath(tool, version, arch2) { const folderPath = path30.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || ""); const markerPath = `${folderPath}.complete`; - fs32.writeFileSync(markerPath, ""); + fs33.writeFileSync(markerPath, ""); core25.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { @@ -113270,15 +113270,15 @@ var require_upload_zip_specification = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getUploadZipSpecification = exports2.validateRootDirectory = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core_1 = require_core(); var path_1 = require("path"); var path_and_artifact_name_validation_1 = require_path_and_artifact_name_validation(); function validateRootDirectory(rootDirectory) { - if (!fs32.existsSync(rootDirectory)) { + if (!fs33.existsSync(rootDirectory)) { throw new Error(`The provided rootDirectory ${rootDirectory} does not exist`); } - if (!fs32.statSync(rootDirectory).isDirectory()) { + if (!fs33.statSync(rootDirectory).isDirectory()) { throw new Error(`The provided rootDirectory ${rootDirectory} is not a valid directory`); } (0, core_1.info)(`Root directory input is valid!`); @@ -113289,7 +113289,7 @@ var require_upload_zip_specification = __commonJS({ rootDirectory = (0, path_1.normalize)(rootDirectory); rootDirectory = (0, path_1.resolve)(rootDirectory); for (let file of filesToZip) { - const stats = fs32.lstatSync(file, { throwIfNoEntry: false }); + const stats = fs33.lstatSync(file, { throwIfNoEntry: false }); if (!stats) { throw new Error(`File ${file} does not exist`); } @@ -114364,13 +114364,13 @@ var require_minimatch2 = __commonJS({ var require_readdir_glob = __commonJS({ "node_modules/@actions/artifact/node_modules/readdir-glob/index.js"(exports2, module2) { module2.exports = readdirGlob2; - var fs32 = require("fs"); + var fs33 = require("fs"); var { EventEmitter: EventEmitter2 } = require("events"); var { Minimatch: Minimatch2 } = require_minimatch2(); var { resolve: resolve15 } = require("path"); function readdir3(dir, strict) { return new Promise((resolve16, reject) => { - fs32.readdir(dir, { withFileTypes: true }, (err, files) => { + fs33.readdir(dir, { withFileTypes: true }, (err, files) => { if (err) { switch (err.code) { case "ENOTDIR": @@ -114403,7 +114403,7 @@ var require_readdir_glob = __commonJS({ } function stat2(file, followSymlinks) { return new Promise((resolve16, reject) => { - const statFunc = followSymlinks ? fs32.stat : fs32.lstat; + const statFunc = followSymlinks ? fs33.stat : fs33.lstat; statFunc(file, (err, stats) => { if (err) { switch (err.code) { @@ -116468,54 +116468,54 @@ var require_polyfills = __commonJS({ } var chdir; module2.exports = patch; - function patch(fs32) { + function patch(fs33) { if (constants.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { - patchLchmod(fs32); + patchLchmod(fs33); } - if (!fs32.lutimes) { - patchLutimes(fs32); + if (!fs33.lutimes) { + patchLutimes(fs33); } - fs32.chown = chownFix(fs32.chown); - fs32.fchown = chownFix(fs32.fchown); - fs32.lchown = chownFix(fs32.lchown); - fs32.chmod = chmodFix(fs32.chmod); - fs32.fchmod = chmodFix(fs32.fchmod); - fs32.lchmod = chmodFix(fs32.lchmod); - fs32.chownSync = chownFixSync(fs32.chownSync); - fs32.fchownSync = chownFixSync(fs32.fchownSync); - fs32.lchownSync = chownFixSync(fs32.lchownSync); - fs32.chmodSync = chmodFixSync(fs32.chmodSync); - fs32.fchmodSync = chmodFixSync(fs32.fchmodSync); - fs32.lchmodSync = chmodFixSync(fs32.lchmodSync); - fs32.stat = statFix(fs32.stat); - fs32.fstat = statFix(fs32.fstat); - fs32.lstat = statFix(fs32.lstat); - fs32.statSync = statFixSync(fs32.statSync); - fs32.fstatSync = statFixSync(fs32.fstatSync); - fs32.lstatSync = statFixSync(fs32.lstatSync); - if (fs32.chmod && !fs32.lchmod) { - fs32.lchmod = function(path30, mode, cb) { + fs33.chown = chownFix(fs33.chown); + fs33.fchown = chownFix(fs33.fchown); + fs33.lchown = chownFix(fs33.lchown); + fs33.chmod = chmodFix(fs33.chmod); + fs33.fchmod = chmodFix(fs33.fchmod); + fs33.lchmod = chmodFix(fs33.lchmod); + fs33.chownSync = chownFixSync(fs33.chownSync); + fs33.fchownSync = chownFixSync(fs33.fchownSync); + fs33.lchownSync = chownFixSync(fs33.lchownSync); + fs33.chmodSync = chmodFixSync(fs33.chmodSync); + fs33.fchmodSync = chmodFixSync(fs33.fchmodSync); + fs33.lchmodSync = chmodFixSync(fs33.lchmodSync); + fs33.stat = statFix(fs33.stat); + fs33.fstat = statFix(fs33.fstat); + fs33.lstat = statFix(fs33.lstat); + fs33.statSync = statFixSync(fs33.statSync); + fs33.fstatSync = statFixSync(fs33.fstatSync); + fs33.lstatSync = statFixSync(fs33.lstatSync); + if (fs33.chmod && !fs33.lchmod) { + fs33.lchmod = function(path30, mode, cb) { if (cb) process.nextTick(cb); }; - fs32.lchmodSync = function() { + fs33.lchmodSync = function() { }; } - if (fs32.chown && !fs32.lchown) { - fs32.lchown = function(path30, uid, gid, cb) { + if (fs33.chown && !fs33.lchown) { + fs33.lchown = function(path30, uid, gid, cb) { if (cb) process.nextTick(cb); }; - fs32.lchownSync = function() { + fs33.lchownSync = function() { }; } if (platform2 === "win32") { - fs32.rename = typeof fs32.rename !== "function" ? fs32.rename : (function(fs$rename) { + fs33.rename = typeof fs33.rename !== "function" ? fs33.rename : (function(fs$rename) { function rename(from, to, cb) { var start = Date.now(); var backoff = 0; fs$rename(from, to, function CB(er) { if (er && (er.code === "EACCES" || er.code === "EPERM") && Date.now() - start < 6e4) { setTimeout(function() { - fs32.stat(to, function(stater, st) { + fs33.stat(to, function(stater, st) { if (stater && stater.code === "ENOENT") fs$rename(from, to, CB); else @@ -116531,9 +116531,9 @@ var require_polyfills = __commonJS({ } if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename); return rename; - })(fs32.rename); + })(fs33.rename); } - fs32.read = typeof fs32.read !== "function" ? fs32.read : (function(fs$read) { + fs33.read = typeof fs33.read !== "function" ? fs33.read : (function(fs$read) { function read(fd, buffer, offset, length, position, callback_) { var callback; if (callback_ && typeof callback_ === "function") { @@ -116541,22 +116541,22 @@ var require_polyfills = __commonJS({ callback = function(er, _2, __) { if (er && er.code === "EAGAIN" && eagCounter < 10) { eagCounter++; - return fs$read.call(fs32, fd, buffer, offset, length, position, callback); + return fs$read.call(fs33, fd, buffer, offset, length, position, callback); } callback_.apply(this, arguments); }; } - return fs$read.call(fs32, fd, buffer, offset, length, position, callback); + return fs$read.call(fs33, fd, buffer, offset, length, position, callback); } if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read); return read; - })(fs32.read); - fs32.readSync = typeof fs32.readSync !== "function" ? fs32.readSync : /* @__PURE__ */ (function(fs$readSync) { + })(fs33.read); + fs33.readSync = typeof fs33.readSync !== "function" ? fs33.readSync : /* @__PURE__ */ (function(fs$readSync) { return function(fd, buffer, offset, length, position) { var eagCounter = 0; while (true) { try { - return fs$readSync.call(fs32, fd, buffer, offset, length, position); + return fs$readSync.call(fs33, fd, buffer, offset, length, position); } catch (er) { if (er.code === "EAGAIN" && eagCounter < 10) { eagCounter++; @@ -116566,10 +116566,10 @@ var require_polyfills = __commonJS({ } } }; - })(fs32.readSync); - function patchLchmod(fs33) { - fs33.lchmod = function(path30, mode, callback) { - fs33.open( + })(fs33.readSync); + function patchLchmod(fs34) { + fs34.lchmod = function(path30, mode, callback) { + fs34.open( path30, constants.O_WRONLY | constants.O_SYMLINK, mode, @@ -116578,80 +116578,80 @@ var require_polyfills = __commonJS({ if (callback) callback(err); return; } - fs33.fchmod(fd, mode, function(err2) { - fs33.close(fd, function(err22) { + fs34.fchmod(fd, mode, function(err2) { + fs34.close(fd, function(err22) { if (callback) callback(err2 || err22); }); }); } ); }; - fs33.lchmodSync = function(path30, mode) { - var fd = fs33.openSync(path30, constants.O_WRONLY | constants.O_SYMLINK, mode); + fs34.lchmodSync = function(path30, mode) { + var fd = fs34.openSync(path30, constants.O_WRONLY | constants.O_SYMLINK, mode); var threw = true; var ret; try { - ret = fs33.fchmodSync(fd, mode); + ret = fs34.fchmodSync(fd, mode); threw = false; } finally { if (threw) { try { - fs33.closeSync(fd); + fs34.closeSync(fd); } catch (er) { } } else { - fs33.closeSync(fd); + fs34.closeSync(fd); } } return ret; }; } - function patchLutimes(fs33) { - if (constants.hasOwnProperty("O_SYMLINK") && fs33.futimes) { - fs33.lutimes = function(path30, at, mt, cb) { - fs33.open(path30, constants.O_SYMLINK, function(er, fd) { + function patchLutimes(fs34) { + if (constants.hasOwnProperty("O_SYMLINK") && fs34.futimes) { + fs34.lutimes = function(path30, at, mt, cb) { + fs34.open(path30, constants.O_SYMLINK, function(er, fd) { if (er) { if (cb) cb(er); return; } - fs33.futimes(fd, at, mt, function(er2) { - fs33.close(fd, function(er22) { + fs34.futimes(fd, at, mt, function(er2) { + fs34.close(fd, function(er22) { if (cb) cb(er2 || er22); }); }); }); }; - fs33.lutimesSync = function(path30, at, mt) { - var fd = fs33.openSync(path30, constants.O_SYMLINK); + fs34.lutimesSync = function(path30, at, mt) { + var fd = fs34.openSync(path30, constants.O_SYMLINK); var ret; var threw = true; try { - ret = fs33.futimesSync(fd, at, mt); + ret = fs34.futimesSync(fd, at, mt); threw = false; } finally { if (threw) { try { - fs33.closeSync(fd); + fs34.closeSync(fd); } catch (er) { } } else { - fs33.closeSync(fd); + fs34.closeSync(fd); } } return ret; }; - } else if (fs33.futimes) { - fs33.lutimes = function(_a6, _b2, _c2, cb) { + } else if (fs34.futimes) { + fs34.lutimes = function(_a6, _b2, _c2, cb) { if (cb) process.nextTick(cb); }; - fs33.lutimesSync = function() { + fs34.lutimesSync = function() { }; } } function chmodFix(orig) { if (!orig) return orig; return function(target, mode, cb) { - return orig.call(fs32, target, mode, function(er) { + return orig.call(fs33, target, mode, function(er) { if (chownErOk(er)) er = null; if (cb) cb.apply(this, arguments); }); @@ -116661,7 +116661,7 @@ var require_polyfills = __commonJS({ if (!orig) return orig; return function(target, mode) { try { - return orig.call(fs32, target, mode); + return orig.call(fs33, target, mode); } catch (er) { if (!chownErOk(er)) throw er; } @@ -116670,7 +116670,7 @@ var require_polyfills = __commonJS({ function chownFix(orig) { if (!orig) return orig; return function(target, uid, gid, cb) { - return orig.call(fs32, target, uid, gid, function(er) { + return orig.call(fs33, target, uid, gid, function(er) { if (chownErOk(er)) er = null; if (cb) cb.apply(this, arguments); }); @@ -116680,7 +116680,7 @@ var require_polyfills = __commonJS({ if (!orig) return orig; return function(target, uid, gid) { try { - return orig.call(fs32, target, uid, gid); + return orig.call(fs33, target, uid, gid); } catch (er) { if (!chownErOk(er)) throw er; } @@ -116700,13 +116700,13 @@ var require_polyfills = __commonJS({ } if (cb) cb.apply(this, arguments); } - return options ? orig.call(fs32, target, options, callback) : orig.call(fs32, target, callback); + return options ? orig.call(fs33, target, options, callback) : orig.call(fs33, target, callback); }; } function statFixSync(orig) { if (!orig) return orig; return function(target, options) { - var stats = options ? orig.call(fs32, target, options) : orig.call(fs32, target); + var stats = options ? orig.call(fs33, target, options) : orig.call(fs33, target); if (stats) { if (stats.uid < 0) stats.uid += 4294967296; if (stats.gid < 0) stats.gid += 4294967296; @@ -116735,7 +116735,7 @@ var require_legacy_streams = __commonJS({ "node_modules/graceful-fs/legacy-streams.js"(exports2, module2) { var Stream = require("stream").Stream; module2.exports = legacy; - function legacy(fs32) { + function legacy(fs33) { return { ReadStream, WriteStream @@ -116778,7 +116778,7 @@ var require_legacy_streams = __commonJS({ }); return; } - fs32.open(this.path, this.flags, this.mode, function(err, fd) { + fs33.open(this.path, this.flags, this.mode, function(err, fd) { if (err) { self2.emit("error", err); self2.readable = false; @@ -116817,7 +116817,7 @@ var require_legacy_streams = __commonJS({ this.busy = false; this._queue = []; if (this.fd === null) { - this._open = fs32.open; + this._open = fs33.open; this._queue.push([this._open, this.path, this.flags, this.mode, void 0]); this.flush(); } @@ -116852,7 +116852,7 @@ var require_clone = __commonJS({ // node_modules/graceful-fs/graceful-fs.js var require_graceful_fs = __commonJS({ "node_modules/graceful-fs/graceful-fs.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var polyfills = require_polyfills(); var legacy = require_legacy_streams(); var clone = require_clone(); @@ -116884,12 +116884,12 @@ var require_graceful_fs = __commonJS({ m = "GFS4: " + m.split(/\n/).join("\nGFS4: "); console.error(m); }; - if (!fs32[gracefulQueue]) { + if (!fs33[gracefulQueue]) { queue2 = global[gracefulQueue] || []; - publishQueue(fs32, queue2); - fs32.close = (function(fs$close) { + publishQueue(fs33, queue2); + fs33.close = (function(fs$close) { function close(fd, cb) { - return fs$close.call(fs32, fd, function(err) { + return fs$close.call(fs33, fd, function(err) { if (!err) { resetQueue(); } @@ -116901,40 +116901,40 @@ var require_graceful_fs = __commonJS({ value: fs$close }); return close; - })(fs32.close); - fs32.closeSync = (function(fs$closeSync) { + })(fs33.close); + fs33.closeSync = (function(fs$closeSync) { function closeSync(fd) { - fs$closeSync.apply(fs32, arguments); + fs$closeSync.apply(fs33, arguments); resetQueue(); } Object.defineProperty(closeSync, previousSymbol, { value: fs$closeSync }); return closeSync; - })(fs32.closeSync); + })(fs33.closeSync); if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) { process.on("exit", function() { - debug6(fs32[gracefulQueue]); - require("assert").equal(fs32[gracefulQueue].length, 0); + debug6(fs33[gracefulQueue]); + require("assert").equal(fs33[gracefulQueue].length, 0); }); } } var queue2; if (!global[gracefulQueue]) { - publishQueue(global, fs32[gracefulQueue]); + publishQueue(global, fs33[gracefulQueue]); } - module2.exports = patch(clone(fs32)); - if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs32.__patched) { - module2.exports = patch(fs32); - fs32.__patched = true; + module2.exports = patch(clone(fs33)); + if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs33.__patched) { + module2.exports = patch(fs33); + fs33.__patched = true; } - function patch(fs33) { - polyfills(fs33); - fs33.gracefulify = patch; - fs33.createReadStream = createReadStream4; - fs33.createWriteStream = createWriteStream3; - var fs$readFile = fs33.readFile; - fs33.readFile = readFile; + function patch(fs34) { + polyfills(fs34); + fs34.gracefulify = patch; + fs34.createReadStream = createReadStream4; + fs34.createWriteStream = createWriteStream3; + var fs$readFile = fs34.readFile; + fs34.readFile = readFile; function readFile(path30, options, cb) { if (typeof options === "function") cb = options, options = null; @@ -116950,8 +116950,8 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$writeFile = fs33.writeFile; - fs33.writeFile = writeFile; + var fs$writeFile = fs34.writeFile; + fs34.writeFile = writeFile; function writeFile(path30, data, options, cb) { if (typeof options === "function") cb = options, options = null; @@ -116967,9 +116967,9 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$appendFile = fs33.appendFile; + var fs$appendFile = fs34.appendFile; if (fs$appendFile) - fs33.appendFile = appendFile; + fs34.appendFile = appendFile; function appendFile(path30, data, options, cb) { if (typeof options === "function") cb = options, options = null; @@ -116985,9 +116985,9 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$copyFile = fs33.copyFile; + var fs$copyFile = fs34.copyFile; if (fs$copyFile) - fs33.copyFile = copyFile2; + fs34.copyFile = copyFile2; function copyFile2(src, dest, flags, cb) { if (typeof flags === "function") { cb = flags; @@ -117005,8 +117005,8 @@ var require_graceful_fs = __commonJS({ }); } } - var fs$readdir = fs33.readdir; - fs33.readdir = readdir3; + var fs$readdir = fs34.readdir; + fs34.readdir = readdir3; var noReaddirOptionVersions = /^v[0-5]\./; function readdir3(path30, options, cb) { if (typeof options === "function") @@ -117047,21 +117047,21 @@ var require_graceful_fs = __commonJS({ } } if (process.version.substr(0, 4) === "v0.8") { - var legStreams = legacy(fs33); + var legStreams = legacy(fs34); ReadStream = legStreams.ReadStream; WriteStream = legStreams.WriteStream; } - var fs$ReadStream = fs33.ReadStream; + var fs$ReadStream = fs34.ReadStream; if (fs$ReadStream) { ReadStream.prototype = Object.create(fs$ReadStream.prototype); ReadStream.prototype.open = ReadStream$open; } - var fs$WriteStream = fs33.WriteStream; + var fs$WriteStream = fs34.WriteStream; if (fs$WriteStream) { WriteStream.prototype = Object.create(fs$WriteStream.prototype); WriteStream.prototype.open = WriteStream$open; } - Object.defineProperty(fs33, "ReadStream", { + Object.defineProperty(fs34, "ReadStream", { get: function() { return ReadStream; }, @@ -117071,7 +117071,7 @@ var require_graceful_fs = __commonJS({ enumerable: true, configurable: true }); - Object.defineProperty(fs33, "WriteStream", { + Object.defineProperty(fs34, "WriteStream", { get: function() { return WriteStream; }, @@ -117082,7 +117082,7 @@ var require_graceful_fs = __commonJS({ configurable: true }); var FileReadStream = ReadStream; - Object.defineProperty(fs33, "FileReadStream", { + Object.defineProperty(fs34, "FileReadStream", { get: function() { return FileReadStream; }, @@ -117093,7 +117093,7 @@ var require_graceful_fs = __commonJS({ configurable: true }); var FileWriteStream = WriteStream; - Object.defineProperty(fs33, "FileWriteStream", { + Object.defineProperty(fs34, "FileWriteStream", { get: function() { return FileWriteStream; }, @@ -117142,13 +117142,13 @@ var require_graceful_fs = __commonJS({ }); } function createReadStream4(path30, options) { - return new fs33.ReadStream(path30, options); + return new fs34.ReadStream(path30, options); } function createWriteStream3(path30, options) { - return new fs33.WriteStream(path30, options); + return new fs34.WriteStream(path30, options); } - var fs$open = fs33.open; - fs33.open = open; + var fs$open = fs34.open; + fs34.open = open; function open(path30, flags, mode, cb) { if (typeof mode === "function") cb = mode, mode = null; @@ -117164,20 +117164,20 @@ var require_graceful_fs = __commonJS({ }); } } - return fs33; + return fs34; } function enqueue(elem) { debug6("ENQUEUE", elem[0].name, elem[1]); - fs32[gracefulQueue].push(elem); + fs33[gracefulQueue].push(elem); retry2(); } var retryTimer; function resetQueue() { var now = Date.now(); - for (var i = 0; i < fs32[gracefulQueue].length; ++i) { - if (fs32[gracefulQueue][i].length > 2) { - fs32[gracefulQueue][i][3] = now; - fs32[gracefulQueue][i][4] = now; + for (var i = 0; i < fs33[gracefulQueue].length; ++i) { + if (fs33[gracefulQueue][i].length > 2) { + fs33[gracefulQueue][i][3] = now; + fs33[gracefulQueue][i][4] = now; } } retry2(); @@ -117185,9 +117185,9 @@ var require_graceful_fs = __commonJS({ function retry2() { clearTimeout(retryTimer); retryTimer = void 0; - if (fs32[gracefulQueue].length === 0) + if (fs33[gracefulQueue].length === 0) return; - var elem = fs32[gracefulQueue].shift(); + var elem = fs33[gracefulQueue].shift(); var fn = elem[0]; var args = elem[1]; var err = elem[2]; @@ -117209,7 +117209,7 @@ var require_graceful_fs = __commonJS({ debug6("RETRY", fn.name, args); fn.apply(null, args.concat([startTime])); } else { - fs32[gracefulQueue].push(elem); + fs33[gracefulQueue].push(elem); } } if (retryTimer === void 0) { @@ -128032,7 +128032,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s)) retur g.minimatch.escape = vi.escape; g.minimatch.unescape = Ei.unescape; }); - var fs32 = R((Wt) => { + var fs33 = R((Wt) => { "use strict"; Object.defineProperty(Wt, "__esModule", { value: true }); Wt.LRUCache = void 0; @@ -128901,7 +128901,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s)) retur }; Object.defineProperty(_2, "__esModule", { value: true }); _2.PathScurry = _2.Path = _2.PathScurryDarwin = _2.PathScurryPosix = _2.PathScurryWin32 = _2.PathScurryBase = _2.PathPosix = _2.PathWin32 = _2.PathBase = _2.ChildrenCache = _2.ResolveCache = void 0; - var Qt = fs32(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s2 = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r2 = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => { + var Qt = fs33(), Yt = require("node:path"), yr = require("node:url"), pt = require("fs"), Sr = br(require("node:fs")), vr = pt.realpathSync.native, Ht = require("node:fs/promises"), bs = Oe(), mt = { lstatSync: pt.lstatSync, readdir: pt.readdir, readdirSync: pt.readdirSync, readlinkSync: pt.readlinkSync, realpathSync: vr, promises: { lstat: Ht.lstat, readdir: Ht.readdir, readlink: Ht.readlink, realpath: Ht.realpath } }, _s2 = (n) => !n || n === mt || n === Sr ? mt : { ...mt, ...n, promises: { ...mt.promises, ...n.promises || {} } }, Os = /^\\\\\?\\([a-z]:)\\?$/i, Er = (n) => n.replace(/\//g, "\\").replace(Os, "$1\\"), _r2 = /[\\\/]/, N = 0, xs = 1, Ts = 2, G = 4, Cs = 6, Rs = 8, Q = 10, As = 12, j = 15, dt = ~j, xe = 16, ys = 32, gt = 64, W = 128, Vt = 256, Xt = 512, Ss = gt | W | Xt, Or = 1023, Te = (n) => n.isFile() ? Rs : n.isDirectory() ? G : n.isSymbolicLink() ? Q : n.isCharacterDevice() ? Ts : n.isBlockDevice() ? Cs : n.isSocket() ? As : n.isFIFO() ? xs : N, vs = new Qt.LRUCache({ max: 2 ** 12 }), wt = (n) => { let t = vs.get(n); if (t) return t; let e = n.normalize("NFKD"); @@ -130279,7 +130279,7 @@ globstar while`, t, d, e, u, m), this.matchOne(t.slice(d), e.slice(u), s)) retur // node_modules/archiver-utils/file.js var require_file3 = __commonJS({ "node_modules/archiver-utils/file.js"(exports2, module2) { - var fs32 = require_graceful_fs(); + var fs33 = require_graceful_fs(); var path30 = require("path"); var flatten = require_flatten(); var difference = require_difference(); @@ -130306,7 +130306,7 @@ var require_file3 = __commonJS({ }; file.exists = function() { var filepath = path30.join.apply(path30, arguments); - return fs32.existsSync(filepath); + return fs33.existsSync(filepath); }; file.expand = function(...args) { var options = isPlainObject4(args[0]) ? args.shift() : {}; @@ -130324,7 +130324,7 @@ var require_file3 = __commonJS({ if (typeof options.filter === "function") { return options.filter(filepath); } else { - return fs32.statSync(filepath)[options.filter](); + return fs33.statSync(filepath)[options.filter](); } } catch (e) { return false; @@ -130432,7 +130432,7 @@ var require_file3 = __commonJS({ // node_modules/archiver-utils/index.js var require_archiver_utils = __commonJS({ "node_modules/archiver-utils/index.js"(exports2, module2) { - var fs32 = require_graceful_fs(); + var fs33 = require_graceful_fs(); var path30 = require("path"); var isStream2 = require_is_stream(); var lazystream = require_lazystream(); @@ -130481,7 +130481,7 @@ var require_archiver_utils = __commonJS({ }; utils.lazyReadStream = function(filepath) { return new lazystream.Readable(function() { - return fs32.createReadStream(filepath); + return fs33.createReadStream(filepath); }); }; utils.normalizeInputSource = function(source) { @@ -130509,7 +130509,7 @@ var require_archiver_utils = __commonJS({ callback = base; base = dirpath; } - fs32.readdir(dirpath, function(err, list) { + fs33.readdir(dirpath, function(err, list) { var i = 0; var file; var filepath; @@ -130522,7 +130522,7 @@ var require_archiver_utils = __commonJS({ return callback(null, results); } filepath = path30.join(dirpath, file); - fs32.stat(filepath, function(err2, stats) { + fs33.stat(filepath, function(err2, stats) { results.push({ path: filepath, relative: path30.relative(base, filepath).replace(/\\/g, "/"), @@ -130584,7 +130584,7 @@ var require_error3 = __commonJS({ // node_modules/@actions/artifact/node_modules/archiver/lib/core.js var require_core2 = __commonJS({ "node_modules/@actions/artifact/node_modules/archiver/lib/core.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var glob2 = require_readdir_glob(); var async = require_async(); var path30 = require("path"); @@ -130648,7 +130648,7 @@ var require_core2 = __commonJS({ data.sourcePath = filepath; task.data = data; this._entriesCount++; - if (data.stats && data.stats instanceof fs32.Stats) { + if (data.stats && data.stats instanceof fs33.Stats) { task = this._updateQueueTaskWithStats(task, data.stats); if (task) { if (data.stats.size) { @@ -130819,7 +130819,7 @@ var require_core2 = __commonJS({ callback(); return; } - fs32.lstat(task.filepath, function(err, stats) { + fs33.lstat(task.filepath, function(err, stats) { if (this._state.aborted) { setImmediate(callback); return; @@ -130862,7 +130862,7 @@ var require_core2 = __commonJS({ task.data.sourceType = "buffer"; task.source = Buffer.concat([]); } else if (stats.isSymbolicLink() && this._moduleSupports("symlink")) { - var linkPath = fs32.readlinkSync(task.filepath); + var linkPath = fs33.readlinkSync(task.filepath); var dirName = path30.dirname(task.filepath); task.data.type = "symlink"; task.data.linkname = path30.relative(dirName, path30.resolve(dirName, linkPath)); @@ -141764,7 +141764,7 @@ var require_parser_stream = __commonJS({ var require_mkdirp = __commonJS({ "node_modules/mkdirp/index.js"(exports2, module2) { var path30 = require("path"); - var fs32 = require("fs"); + var fs33 = require("fs"); var _0777 = parseInt("0777", 8); module2.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; function mkdirP(p, opts, f, made) { @@ -141775,7 +141775,7 @@ var require_mkdirp = __commonJS({ opts = { mode: opts }; } var mode = opts.mode; - var xfs = opts.fs || fs32; + var xfs = opts.fs || fs33; if (mode === void 0) { mode = _0777; } @@ -141814,7 +141814,7 @@ var require_mkdirp = __commonJS({ opts = { mode: opts }; } var mode = opts.mode; - var xfs = opts.fs || fs32; + var xfs = opts.fs || fs33; if (mode === void 0) { mode = _0777; } @@ -141851,7 +141851,7 @@ var require_mkdirp = __commonJS({ // node_modules/unzip-stream/lib/extract.js var require_extract2 = __commonJS({ "node_modules/unzip-stream/lib/extract.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var path30 = require("path"); var util4 = require("util"); var mkdirp = require_mkdirp(); @@ -141898,7 +141898,7 @@ var require_extract2 = __commonJS({ var directory = entry.isDirectory ? destPath : path30.dirname(destPath); this.unfinishedEntries++; var writeFileFn = function() { - var pipedStream = fs32.createWriteStream(destPath); + var pipedStream = fs33.createWriteStream(destPath); pipedStream.on("close", function() { self2.unfinishedEntries--; self2._notifyAwaiter(); @@ -143096,7 +143096,7 @@ var require_file_command2 = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.prepareKeyValueMessage = exports2.issueFileCommand = void 0; var crypto3 = __importStar2(require("crypto")); - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var os7 = __importStar2(require("os")); var utils_1 = require_utils11(); function issueFileCommand(command, message) { @@ -143104,10 +143104,10 @@ var require_file_command2 = __commonJS({ if (!filePath) { throw new Error(`Unable to find environment variable for file command ${command}`); } - if (!fs32.existsSync(filePath)) { + if (!fs33.existsSync(filePath)) { throw new Error(`Missing file at path: ${filePath}`); } - fs32.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { + fs33.appendFileSync(filePath, `${(0, utils_1.toCommandValue)(message)}${os7.EOL}`, { encoding: "utf8" }); } @@ -144430,12 +144430,12 @@ var require_io_util2 = __commonJS({ var _a6; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getCmdPath = exports2.tryGetExecutablePath = exports2.isRooted = exports2.isDirectory = exports2.exists = exports2.READONLY = exports2.UV_FS_O_EXLOCK = exports2.IS_WINDOWS = exports2.unlink = exports2.symlink = exports2.stat = exports2.rmdir = exports2.rm = exports2.rename = exports2.readlink = exports2.readdir = exports2.open = exports2.mkdir = exports2.lstat = exports2.copyFile = exports2.chmod = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); - _a6 = fs32.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.readlink = _a6.readlink, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; + _a6 = fs33.promises, exports2.chmod = _a6.chmod, exports2.copyFile = _a6.copyFile, exports2.lstat = _a6.lstat, exports2.mkdir = _a6.mkdir, exports2.open = _a6.open, exports2.readdir = _a6.readdir, exports2.readlink = _a6.readlink, exports2.rename = _a6.rename, exports2.rm = _a6.rm, exports2.rmdir = _a6.rmdir, exports2.stat = _a6.stat, exports2.symlink = _a6.symlink, exports2.unlink = _a6.unlink; exports2.IS_WINDOWS = process.platform === "win32"; exports2.UV_FS_O_EXLOCK = 268435456; - exports2.READONLY = fs32.constants.O_RDONLY; + exports2.READONLY = fs33.constants.O_RDONLY; function exists(fsPath) { return __awaiter2(this, void 0, void 0, function* () { try { @@ -145824,25 +145824,25 @@ var require_upload_specification = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getUploadSpecification = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core_1 = require_core3(); var path_1 = require("path"); var path_and_artifact_name_validation_1 = require_path_and_artifact_name_validation2(); function getUploadSpecification(artifactName, rootDirectory, artifactFiles) { const specifications = []; - if (!fs32.existsSync(rootDirectory)) { + if (!fs33.existsSync(rootDirectory)) { throw new Error(`Provided rootDirectory ${rootDirectory} does not exist`); } - if (!fs32.statSync(rootDirectory).isDirectory()) { + if (!fs33.statSync(rootDirectory).isDirectory()) { throw new Error(`Provided rootDirectory ${rootDirectory} is not a valid directory`); } rootDirectory = (0, path_1.normalize)(rootDirectory); rootDirectory = (0, path_1.resolve)(rootDirectory); for (let file of artifactFiles) { - if (!fs32.existsSync(file)) { + if (!fs33.existsSync(file)) { throw new Error(`File ${file} does not exist`); } - if (!fs32.statSync(file).isDirectory()) { + if (!fs33.statSync(file).isDirectory()) { file = (0, path_1.normalize)(file); file = (0, path_1.resolve)(file); if (!file.startsWith(rootDirectory)) { @@ -145867,11 +145867,11 @@ var require_upload_specification = __commonJS({ // node_modules/tmp/lib/tmp.js var require_tmp = __commonJS({ "node_modules/tmp/lib/tmp.js"(exports2, module2) { - var fs32 = require("fs"); + var fs33 = require("fs"); var os7 = require("os"); var path30 = require("path"); var crypto3 = require("crypto"); - var _c2 = { fs: fs32.constants, os: os7.constants }; + var _c2 = { fs: fs33.constants, os: os7.constants }; var RANDOM_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var TEMPLATE_PATTERN = /XXXXXX/; var DEFAULT_TRIES = 3; @@ -145883,13 +145883,13 @@ var require_tmp = __commonJS({ var FILE_MODE = 384; var EXIT = "exit"; var _removeObjects = []; - var FN_RMDIR_SYNC = fs32.rmdirSync.bind(fs32); + var FN_RMDIR_SYNC = fs33.rmdirSync.bind(fs33); var _gracefulCleanup = false; function rimraf(dirPath, callback) { - return fs32.rm(dirPath, { recursive: true }, callback); + return fs33.rm(dirPath, { recursive: true }, callback); } function FN_RIMRAF_SYNC(dirPath) { - return fs32.rmSync(dirPath, { recursive: true }); + return fs33.rmSync(dirPath, { recursive: true }); } function tmpName(options, callback) { const args = _parseArguments(options, callback), opts = args[0], cb = args[1]; @@ -145899,7 +145899,7 @@ var require_tmp = __commonJS({ (function _getUniqueName() { try { const name = _generateTmpName(sanitizedOptions); - fs32.stat(name, function(err2) { + fs33.stat(name, function(err2) { if (!err2) { if (tries-- > 0) return _getUniqueName(); return cb(new Error("Could not get a unique tmp filename, max tries reached " + name)); @@ -145919,7 +145919,7 @@ var require_tmp = __commonJS({ do { const name = _generateTmpName(sanitizedOptions); try { - fs32.statSync(name); + fs33.statSync(name); } catch (e) { return name; } @@ -145930,10 +145930,10 @@ var require_tmp = __commonJS({ const args = _parseArguments(options, callback), opts = args[0], cb = args[1]; tmpName(opts, function _tmpNameCreated(err, name) { if (err) return cb(err); - fs32.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) { + fs33.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) { if (err2) return cb(err2); if (opts.discardDescriptor) { - return fs32.close(fd, function _discardCallback(possibleErr) { + return fs33.close(fd, function _discardCallback(possibleErr) { return cb(possibleErr, name, void 0, _prepareTmpFileRemoveCallback(name, -1, opts, false)); }); } else { @@ -145947,9 +145947,9 @@ var require_tmp = __commonJS({ const args = _parseArguments(options), opts = args[0]; const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor; const name = tmpNameSync(opts); - let fd = fs32.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE); + let fd = fs33.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE); if (opts.discardDescriptor) { - fs32.closeSync(fd); + fs33.closeSync(fd); fd = void 0; } return { @@ -145962,7 +145962,7 @@ var require_tmp = __commonJS({ const args = _parseArguments(options, callback), opts = args[0], cb = args[1]; tmpName(opts, function _tmpNameCreated(err, name) { if (err) return cb(err); - fs32.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) { + fs33.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) { if (err2) return cb(err2); cb(null, name, _prepareTmpDirRemoveCallback(name, opts, false)); }); @@ -145971,7 +145971,7 @@ var require_tmp = __commonJS({ function dirSync(options) { const args = _parseArguments(options), opts = args[0]; const name = tmpNameSync(opts); - fs32.mkdirSync(name, opts.mode || DIR_MODE); + fs33.mkdirSync(name, opts.mode || DIR_MODE); return { name, removeCallback: _prepareTmpDirRemoveCallback(name, opts, true) @@ -145985,20 +145985,20 @@ var require_tmp = __commonJS({ next(); }; if (0 <= fdPath[0]) - fs32.close(fdPath[0], function() { - fs32.unlink(fdPath[1], _handler); + fs33.close(fdPath[0], function() { + fs33.unlink(fdPath[1], _handler); }); - else fs32.unlink(fdPath[1], _handler); + else fs33.unlink(fdPath[1], _handler); } function _removeFileSync(fdPath) { let rethrownException = null; try { - if (0 <= fdPath[0]) fs32.closeSync(fdPath[0]); + if (0 <= fdPath[0]) fs33.closeSync(fdPath[0]); } catch (e) { if (!_isEBADF(e) && !_isENOENT(e)) throw e; } finally { try { - fs32.unlinkSync(fdPath[1]); + fs33.unlinkSync(fdPath[1]); } catch (e) { if (!_isENOENT(e)) rethrownException = e; } @@ -146014,7 +146014,7 @@ var require_tmp = __commonJS({ return sync ? removeCallbackSync : removeCallback; } function _prepareTmpDirRemoveCallback(name, opts, sync) { - const removeFunction = opts.unsafeCleanup ? rimraf : fs32.rmdir.bind(fs32); + const removeFunction = opts.unsafeCleanup ? rimraf : fs33.rmdir.bind(fs33); const removeFunctionSync = opts.unsafeCleanup ? FN_RIMRAF_SYNC : FN_RMDIR_SYNC; const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name, sync); const removeCallback = _prepareRemoveCallback(removeFunction, name, sync, removeCallbackSync); @@ -146077,24 +146077,24 @@ var require_tmp = __commonJS({ } function _resolvePath(name, tmpDir, cb) { const pathToResolve = path30.isAbsolute(name) ? name : path30.join(tmpDir, name); - fs32.stat(pathToResolve, function(err) { + fs33.stat(pathToResolve, function(err) { if (err) { - fs32.realpath(path30.dirname(pathToResolve), function(err2, parentDir) { + fs33.realpath(path30.dirname(pathToResolve), function(err2, parentDir) { if (err2) return cb(err2); cb(null, path30.join(parentDir, path30.basename(pathToResolve))); }); } else { - fs32.realpath(pathToResolve, cb); + fs33.realpath(pathToResolve, cb); } }); } function _resolvePathSync(name, tmpDir) { const pathToResolve = path30.isAbsolute(name) ? name : path30.join(tmpDir, name); try { - fs32.statSync(pathToResolve); - return fs32.realpathSync(pathToResolve); + fs33.statSync(pathToResolve); + return fs33.realpathSync(pathToResolve); } catch (_err) { - const parentDir = fs32.realpathSync(path30.dirname(pathToResolve)); + const parentDir = fs33.realpathSync(path30.dirname(pathToResolve)); return path30.join(parentDir, path30.basename(pathToResolve)); } } @@ -146215,10 +146215,10 @@ var require_tmp = __commonJS({ _gracefulCleanup = true; } function _getTmpDir(options, cb) { - return fs32.realpath(options && options.tmpdir || os7.tmpdir(), cb); + return fs33.realpath(options && options.tmpdir || os7.tmpdir(), cb); } function _getTmpDirSync(options) { - return fs32.realpathSync(options && options.tmpdir || os7.tmpdir()); + return fs33.realpathSync(options && options.tmpdir || os7.tmpdir()); } process.addListener(EXIT, _garbageCollector); Object.defineProperty(module2.exports, "tmpdir", { @@ -147072,10 +147072,10 @@ var require_upload_gzip = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.createGZipFileInBuffer = exports2.createGZipFileOnDisk = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var zlib3 = __importStar2(require("zlib")); var util_1 = require("util"); - var stat2 = (0, util_1.promisify)(fs32.stat); + var stat2 = (0, util_1.promisify)(fs33.stat); var gzipExemptFileExtensions = [ ".gz", ".gzip", @@ -147108,9 +147108,9 @@ var require_upload_gzip = __commonJS({ } } return new Promise((resolve15, reject) => { - const inputStream = fs32.createReadStream(originalFilePath); + const inputStream = fs33.createReadStream(originalFilePath); const gzip = zlib3.createGzip(); - const outputStream = fs32.createWriteStream(tempFilePath); + const outputStream = fs33.createWriteStream(tempFilePath); inputStream.pipe(gzip).pipe(outputStream); outputStream.on("finish", () => __awaiter2(this, void 0, void 0, function* () { const size = (yield stat2(tempFilePath)).size; @@ -147128,7 +147128,7 @@ var require_upload_gzip = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { return new Promise((resolve15) => __awaiter2(this, void 0, void 0, function* () { var _a6, e_1, _b2, _c2; - const inputStream = fs32.createReadStream(originalFilePath); + const inputStream = fs33.createReadStream(originalFilePath); const gzip = zlib3.createGzip(); inputStream.pipe(gzip); const chunks = []; @@ -147337,7 +147337,7 @@ var require_upload_http_client = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.UploadHttpClient = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core25 = __importStar2(require_core3()); var tmp2 = __importStar2(require_tmp_promise()); var stream2 = __importStar2(require("stream")); @@ -147351,7 +147351,7 @@ var require_upload_http_client = __commonJS({ var http_manager_1 = require_http_manager(); var upload_gzip_1 = require_upload_gzip(); var requestUtils_1 = require_requestUtils2(); - var stat2 = (0, util_1.promisify)(fs32.stat); + var stat2 = (0, util_1.promisify)(fs33.stat); var UploadHttpClient = class { constructor() { this.uploadHttpManager = new http_manager_1.HttpManager((0, config_variables_1.getUploadFileConcurrency)(), "@actions/artifact-upload"); @@ -147488,7 +147488,7 @@ var require_upload_http_client = __commonJS({ let openUploadStream; if (totalFileSize < buffer.byteLength) { core25.debug(`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`); - openUploadStream = () => fs32.createReadStream(parameters.file); + openUploadStream = () => fs33.createReadStream(parameters.file); isGzip = false; uploadFileSize = totalFileSize; } else { @@ -147534,7 +147534,7 @@ var require_upload_http_client = __commonJS({ failedChunkSizes += chunkSize; continue; } - const result = yield this.uploadChunk(httpClientIndex, parameters.resourceUrl, () => fs32.createReadStream(uploadFilePath, { + const result = yield this.uploadChunk(httpClientIndex, parameters.resourceUrl, () => fs33.createReadStream(uploadFilePath, { start: startChunkIndex, end: endChunkIndex, autoClose: false @@ -147729,7 +147729,7 @@ var require_download_http_client = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DownloadHttpClient = void 0; - var fs32 = __importStar2(require("fs")); + var fs33 = __importStar2(require("fs")); var core25 = __importStar2(require_core3()); var zlib3 = __importStar2(require("zlib")); var utils_1 = require_utils12(); @@ -147820,7 +147820,7 @@ var require_download_http_client = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { let retryCount = 0; const retryLimit = (0, config_variables_1.getRetryLimit)(); - let destinationStream = fs32.createWriteStream(downloadPath); + let destinationStream = fs33.createWriteStream(downloadPath); const headers = (0, utils_1.getDownloadHeaders)("application/json", true, true); const makeDownloadRequest = () => __awaiter2(this, void 0, void 0, function* () { const client = this.downloadHttpManager.getClient(httpClientIndex); @@ -147862,7 +147862,7 @@ var require_download_http_client = __commonJS({ } }); yield (0, utils_1.rmFile)(fileDownloadPath); - destinationStream = fs32.createWriteStream(fileDownloadPath); + destinationStream = fs33.createWriteStream(fileDownloadPath); }); while (retryCount <= retryLimit) { let response; @@ -166068,7 +166068,7 @@ __export(entry_points_exports, { module.exports = __toCommonJS(entry_points_exports); // src/analyze-action.ts -var fs23 = __toESM(require("fs")); +var fs24 = __toESM(require("fs")); var import_path5 = __toESM(require("path")); var import_perf_hooks6 = require("perf_hooks"); var core14 = __toESM(require_core()); @@ -166219,21 +166219,21 @@ async function getFolderSize(itemPath, options) { getFolderSize.loose = async (itemPath, options) => await core2(itemPath, options); getFolderSize.strict = async (itemPath, options) => await core2(itemPath, options, { strict: true }); async function core2(rootItemPath, options = {}, returnType = {}) { - const fs32 = options.fs || await import("node:fs/promises"); + const fs33 = options.fs || await import("node:fs/promises"); let folderSize = 0n; const foundInos = /* @__PURE__ */ new Set(); const errors = []; await processItem(rootItemPath); async function processItem(itemPath) { if (options.ignore?.test(itemPath)) return; - const stats = returnType.strict ? await fs32.lstat(itemPath, { bigint: true }) : await fs32.lstat(itemPath, { bigint: true }).catch((error3) => errors.push(error3)); + const stats = returnType.strict ? await fs33.lstat(itemPath, { bigint: true }) : await fs33.lstat(itemPath, { bigint: true }).catch((error3) => errors.push(error3)); if (typeof stats !== "object") return; if (!foundInos.has(stats.ino)) { foundInos.add(stats.ino); folderSize += stats.size; } if (stats.isDirectory()) { - const directoryItems = returnType.strict ? await fs32.readdir(itemPath) : await fs32.readdir(itemPath).catch((error3) => errors.push(error3)); + const directoryItems = returnType.strict ? await fs33.readdir(itemPath) : await fs33.readdir(itemPath).catch((error3) => errors.push(error3)); if (typeof directoryItems !== "object") return; await Promise.all( directoryItems.map( @@ -169994,6 +169994,9 @@ function wrapError(error3) { function getErrorMessage(error3) { return error3 instanceof Error ? error3.message : String(error3); } +function isErrnoException(error3) { + return typeof error3 === "object" && error3 !== null && "code" in error3 && typeof error3.code === "string"; +} function prettyPrintPack(pack) { return `${pack.name}${pack.version ? `@${pack.version}` : ""}${pack.path ? `:${pack.path}` : ""}`; } @@ -170163,7 +170166,7 @@ function getDiffRangesJsonFilePath(env = getEnv()) { return path2.join(getTemporaryDirectory(env), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.39.1"; + return "4.39.2"; } function getWorkflowEventName(env = getEnv()) { return env.getRequired("GITHUB_EVENT_NAME" /* GITHUB_EVENT_NAME */); @@ -170466,12 +170469,24 @@ function fixCodeQualityCategory(logger2, category, env = getEnv()) { // src/logging.ts var core5 = __toESM(require_core()); +var joinMessageStrings = (message) => { + if (Array.isArray(message)) { + return message.join(" "); + } else { + return message; + } +}; +var autoJoinMessageStrings = (fn) => { + return (message) => { + return fn(joinMessageStrings(message)); + }; +}; function getActionsLogger() { return { - debug: core5.debug, - info: core5.info, - warning: core5.warning, - error: core5.error, + debug: autoJoinMessageStrings(core5.debug), + info: autoJoinMessageStrings(core5.info), + warning: autoJoinMessageStrings(core5.warning), + error: autoJoinMessageStrings(core5.error), isDebug: core5.isDebug, startGroup: core5.startGroup, endGroup: core5.endGroup @@ -170881,14 +170896,6 @@ async function listActionsCaches(keyPrefix, ref) { } ); } -async function deleteActionsCache(id) { - const repositoryNwo = getRepositoryNwo(); - await getApiClient().rest.actions.deleteActionsCacheById({ - owner: repositoryNwo.owner, - repo: repositoryNwo.repo, - cache_id: id - }); -} async function getRepositoryProperties(repositoryNwo) { return getApiClient().request("GET /repos/:owner/:repo/properties/values", { owner: repositoryNwo.owner, @@ -171953,11 +171960,6 @@ var featureConfig = { envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", minimumVersion: void 0 }, - ["cleanup_trap_caches" /* CleanupTrapCaches */]: { - defaultValue: false, - envVar: "CODEQL_ACTION_CLEANUP_TRAP_CACHES", - minimumVersion: void 0 - }, ["config_file_repository_property" /* ConfigFileRepositoryProperty */]: { defaultValue: false, envVar: "CODEQL_ACTION_CONFIG_FILE_REPOSITORY_PROPERTY", @@ -172106,16 +172108,6 @@ var featureConfig = { minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_RUBY }, // Other overlay-related feature flags - ["overlay_analysis_match_codeql_version" /* OverlayAnalysisMatchCodeqlVersion */]: { - defaultValue: false, - envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION", - minimumVersion: void 0 - }, - ["overlay_analysis_match_codeql_version_dry_run" /* OverlayAnalysisMatchCodeqlVersionDryRun */]: { - defaultValue: false, - envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN", - minimumVersion: void 0 - }, ["overlay_analysis_min_disk_8_gb" /* OverlayAnalysisMinDisk8Gb */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MIN_DISK_8_GB", @@ -172679,13 +172671,13 @@ var SarifScanOrder = [ ]; // src/analyze.ts -var fs17 = __toESM(require("fs")); +var fs18 = __toESM(require("fs")); var path16 = __toESM(require("path")); var import_perf_hooks5 = require("perf_hooks"); var io5 = __toESM(require_io()); // src/codeql.ts -var fs16 = __toESM(require("fs")); +var fs17 = __toESM(require("fs")); var path15 = __toESM(require("path")); var core12 = __toESM(require_core()); var toolrunner3 = __toESM(require_toolrunner()); @@ -172940,7 +172932,7 @@ function wrapCliConfigurationError(cliError) { } // src/config-utils.ts -var fs10 = __toESM(require("fs")); +var fs11 = __toESM(require("fs")); var path11 = __toESM(require("path")); var import_perf_hooks2 = require("perf_hooks"); @@ -173000,10 +172992,14 @@ var jsonschema = __toESM(require_lib2()); var semver4 = __toESM(require_semver2()); // src/diagnostics.ts -var import_fs = require("fs"); +var fs7 = __toESM(require("fs")); var import_path2 = __toESM(require("path")); -var unwrittenDiagnostics = []; -var unwrittenDefaultLanguageDiagnostics = []; +var TEMP_DIAGNOSTIC_DIR_NAME = "codeql-action-temp-diagnostics"; +var CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS = [ + "diagnostic", + "codeql-action" +]; +var temporaryDiagnostics = []; var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { @@ -173012,19 +173008,38 @@ function makeDiagnostic(id, name, data = void 0) { source: { ...data?.source, id, name } }; } -function addDiagnostic(config, language, diagnostic) { - const logger2 = getActionsLogger(); +function getLanguageDiagnosticsPath(basePath, language) { + return import_path2.default.resolve( + basePath, + language, + ...CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS + ); +} +function getTempDiagnosticsRoot(tmpDir) { + return import_path2.default.resolve(tmpDir, TEMP_DIAGNOSTIC_DIR_NAME); +} +function getTempLanguageDiagnosticsPath(tmpDir, language) { + const root = getTempDiagnosticsRoot(tmpDir); + if (language !== void 0) { + return getLanguageDiagnosticsPath(root, language); + } + return import_path2.default.resolve(root, ...CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS); +} +function getDatabaseDiagnosticsPath(config, language) { + return getLanguageDiagnosticsPath(config.dbLocation, language); +} +function addDiagnostic(config, language, diagnostic, logger2 = getActionsLogger()) { const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - if ((0, import_fs.existsSync)(databasePath)) { - writeDiagnostic(config, language, diagnostic); + if (fs7.existsSync(databasePath)) { + writeDiagnostic({ logger: logger2 }, config, language, diagnostic); } else { logger2.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.` ); - unwrittenDiagnostics.push({ diagnostic, language }); + writeTemporaryDiagnosticFile(logger2, config.tempDir, language, diagnostic); } } -function addNoLanguageDiagnostic(config, diagnostic) { +function addNoLanguageDiagnostic(config, diagnostic, logger2 = getActionsLogger()) { if (config !== void 0) { addDiagnostic( config, @@ -173034,58 +173049,106 @@ function addNoLanguageDiagnostic(config, diagnostic) { diagnostic ); } else { - unwrittenDefaultLanguageDiagnostics.push(diagnostic); + logger2.debug( + `Writing a diagnostic not associated with any language, but we have no configuration to tell us where the database is yet.` + ); + writeTemporaryDiagnosticFile( + logger2, + getTemporaryDirectory(), + void 0, + diagnostic + ); } } -function writeDiagnostic(config, language, diagnostic) { - const logger2 = getActionsLogger(); - const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; - const diagnosticsPath = import_path2.default.resolve( - databasePath, - "diagnostic", - "codeql-action" - ); +function writeTemporaryDiagnosticFile(logger2, tempDir, language, diagnostic) { + const tempDiagnosticsPath = getTempLanguageDiagnosticsPath(tempDir, language); try { - (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); - const uniqueSuffix = (diagnosticCounter++).toString(); - const sanitizedTimestamp = diagnostic.timestamp.replace( - /[^a-zA-Z0-9.-]/g, - "" - ); - const jsonPath = import_path2.default.resolve( - diagnosticsPath, - `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` - ); - (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + temporaryDiagnostics.push({ path: diagnosticPath, language }); } catch (err) { - logger2.warning(`Unable to write diagnostic message to database: ${err}`); - logger2.debug(JSON.stringify(diagnostic)); + logger2.warning( + `Unable to write temporary diagnostic to '${tempDiagnosticsPath}': ${getErrorMessage(err)} + +${JSON.stringify(diagnostic)}` + ); } } -function logUnwrittenDiagnostics() { - const logger2 = getActionsLogger(); - const num = unwrittenDiagnostics.length; - if (num > 0) { - logger2.warning( - `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.` +function writeDiagnosticFile(diagnosticsPath, diagnostic) { + fs7.mkdirSync(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); + const jsonPath = import_path2.default.resolve( + diagnosticsPath, + `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json` + ); + fs7.writeFileSync(jsonPath, JSON.stringify(diagnostic)); + return jsonPath; +} +function writeDiagnostic(action, config, language, diagnostic) { + const diagnosticsPath = getDatabaseDiagnosticsPath(config, language); + try { + writeDiagnosticFile(diagnosticsPath, diagnostic); + } catch (err) { + action.logger.warning( + `Unable to write diagnostic message to database: ${err}` ); - for (const unwritten of unwrittenDiagnostics) { - logger2.debug(JSON.stringify(unwritten.diagnostic)); + action.logger.debug(JSON.stringify(diagnostic)); + } +} +function logTemporaryDiagnostics(action) { + const num = temporaryDiagnostics.length; + if (num > 0 && action.logger.isDebug()) { + action.logger.startGroup("Diagnostics in temporary location"); + action.logger.debug( + `${num} diagnostic(s) were not moved to the database and remain in the temporary directory.` + ); + for (const temporary of temporaryDiagnostics) { + try { + action.logger.debug(fs7.readFileSync(temporary.path, "utf-8")); + } catch (err) { + action.logger.debug( + `Failed to read diagnostics from temporary location at '${temporary.path}': ${getErrorMessage(err)}` + ); + } + } + action.logger.endGroup(); + } +} +function flushDiagnostics(action, config) { + const diagnosticsCount = temporaryDiagnostics.length; + action.logger.debug( + `Moving ${diagnosticsCount} diagnostic(s) to their respective databases.` + ); + const failedFlushDiagnostics = []; + for (const temporary of temporaryDiagnostics) { + const directory = getDatabaseDiagnosticsPath( + config, + temporary.language ?? config.languages[0] + ); + const filename = import_path2.default.basename(temporary.path); + const destination = import_path2.default.join(directory, filename); + try { + fs7.mkdirSync(directory, { recursive: true }); + try { + fs7.renameSync(temporary.path, destination); + } catch (renameErr) { + if (!isErrnoException(renameErr) || renameErr.code !== "EXDEV") { + throw renameErr; + } + fs7.copyFileSync(temporary.path, destination); + fs7.unlinkSync(temporary.path); + } + } catch (err) { + action.logger.warning( + `Failed to relocate '${temporary.path}' to '${destination}': ${getErrorMessage(err)}` + ); + failedFlushDiagnostics.push(temporary); } } -} -function flushDiagnostics(config) { - const logger2 = getActionsLogger(); - const diagnosticsCount = unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; - logger2.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); - for (const unwritten of unwrittenDiagnostics) { - writeDiagnostic(config, unwritten.language, unwritten.diagnostic); - } - for (const unwritten of unwrittenDefaultLanguageDiagnostics) { - addNoLanguageDiagnostic(config, unwritten); - } - unwrittenDiagnostics = []; - unwrittenDefaultLanguageDiagnostics = []; + temporaryDiagnostics = failedFlushDiagnostics; } function makeTelemetryDiagnostic(id, name, attributes, tags) { return makeDiagnostic(id, name, { @@ -173747,7 +173810,7 @@ async function getRemoteConfig(actionState, configFile, apiDetails) { } // src/diff-informed-analysis-utils.ts -var fs7 = __toESM(require("fs")); +var fs8 = __toESM(require("fs")); async function getDiffInformedAnalysisBranches(codeql, features, logger2) { if (!await features.getValue("diff_informed_queries" /* DiffInformedQueries */, codeql)) { return void 0; @@ -173789,7 +173852,7 @@ async function prepareDiffInformedAnalysis(codeql, features, logger2) { function writeDiffRangesJsonFile(logger2, ranges) { const jsonContents = JSON.stringify(ranges, null, 2); const jsonFilePath = getDiffRangesJsonFilePath(); - fs7.writeFileSync(jsonFilePath, jsonContents); + fs8.writeFileSync(jsonFilePath, jsonContents); logger2.debug( `Wrote pr-diff-range JSON file to ${jsonFilePath}: ${jsonContents}` @@ -173797,11 +173860,11 @@ ${jsonContents}` } function readDiffRangesJsonFile(logger2) { const jsonFilePath = getDiffRangesJsonFilePath(); - if (!fs7.existsSync(jsonFilePath)) { + if (!fs8.existsSync(jsonFilePath)) { logger2.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`); return void 0; } - const jsonContents = fs7.readFileSync(jsonFilePath, "utf8"); + const jsonContents = fs8.readFileSync(jsonFilePath, "utf8"); logger2.debug( `Read pr-diff-range JSON file from ${jsonFilePath}: ${jsonContents}` @@ -174052,7 +174115,7 @@ Improved incremental analysis will be automatically retried when the next versio } // src/overlay/status.ts -var fs8 = __toESM(require("fs")); +var fs9 = __toESM(require("fs")); var path9 = __toESM(require("path")); var actionsCache = __toESM(require_cache4()); var MAX_CACHE_OPERATION_MS = 3e4; @@ -174097,7 +174160,7 @@ async function getOverlayStatus(codeql, languages, diskUsage, logger2) { const cacheKey3 = await getCacheKey(codeql, languages, diskUsage); const statusFile = getStatusFilePath(languages); try { - await fs8.promises.mkdir(path9.dirname(statusFile), { recursive: true }); + await fs9.promises.mkdir(path9.dirname(statusFile), { recursive: true }); const foundKey = await waitForResultWithTimeLimit( MAX_CACHE_OPERATION_MS, actionsCache.restoreCache([statusFile], cacheKey3, [cacheKey3]), @@ -174109,13 +174172,13 @@ async function getOverlayStatus(codeql, languages, diskUsage, logger2) { logger2.debug("No overlay status found in Actions cache."); return void 0; } - if (!fs8.existsSync(statusFile)) { + if (!fs9.existsSync(statusFile)) { logger2.debug( "Overlay status cache entry found but status file is missing." ); return void 0; } - const contents = await fs8.promises.readFile(statusFile, "utf-8"); + const contents = await fs9.promises.readFile(statusFile, "utf-8"); const parsed = JSON.parse(contents); if (!isObject2(parsed) || typeof parsed["attemptedToBuildOverlayBaseDatabase"] !== "boolean" || typeof parsed["builtOverlayBaseDatabase"] !== "boolean") { logger2.debug( @@ -174135,8 +174198,8 @@ async function saveOverlayStatus(codeql, languages, diskUsage, status, logger2) const cacheKey3 = await getCacheKey(codeql, languages, diskUsage); const statusFile = getStatusFilePath(languages); try { - await fs8.promises.mkdir(path9.dirname(statusFile), { recursive: true }); - await fs8.promises.writeFile(statusFile, JSON.stringify(status)); + await fs9.promises.mkdir(path9.dirname(statusFile), { recursive: true }); + await fs9.promises.writeFile(statusFile, JSON.stringify(status)); const cacheId = await waitForResultWithTimeLimit( MAX_CACHE_OPERATION_MS, actionsCache.saveCache([statusFile], cacheKey3), @@ -174162,7 +174225,7 @@ async function getCacheKey(codeql, languages, diskUsage) { } // src/trap-caching.ts -var fs9 = __toESM(require("fs")); +var fs10 = __toESM(require("fs")); var path10 = __toESM(require("path")); var actionsCache2 = __toESM(require_cache4()); var CACHE_VERSION = 1; @@ -174186,7 +174249,7 @@ async function downloadTrapCaches(codeql, languages, logger2, repositoryRoot) { ); for (const language of languagesSupportingCaching) { const cacheDir2 = path10.join(cachesDir, language); - fs9.mkdirSync(cacheDir2, { recursive: true }); + fs10.mkdirSync(cacheDir2, { recursive: true }); result[language] = cacheDir2; } if (await isAnalyzingDefaultBranch(getEnv(), repositoryRoot)) { @@ -174198,7 +174261,7 @@ async function downloadTrapCaches(codeql, languages, logger2, repositoryRoot) { let baseSha = "unknown"; const eventPath = process.env.GITHUB_EVENT_PATH; if (getWorkflowEventName() === "pull_request" && eventPath !== void 0) { - const event = JSON.parse(fs9.readFileSync(path10.resolve(eventPath), "utf-8")); + const event = JSON.parse(fs10.readFileSync(path10.resolve(eventPath), "utf-8")); baseSha = event.pull_request?.base?.sha || baseSha; } for (const language of languages) { @@ -174266,83 +174329,16 @@ async function uploadTrapCaches(codeql, config, logger2) { } return true; } -async function cleanupTrapCaches(config, features, logger2) { - const env = getEnv(); - if (!await features.getValue("cleanup_trap_caches" /* CleanupTrapCaches */)) { - return { - trap_cache_cleanup_skipped_because: "feature disabled" - }; - } - logger2.warning( - "TRAP cache cleanup is deprecated and will be removed in May 2026. We recommend instead disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action." - ); - if (!await isAnalyzingDefaultBranch(env, config.repositoryRoot)) { - return { - trap_cache_cleanup_skipped_because: "not analyzing default branch" - }; - } - try { - let totalBytesCleanedUp = 0; - const allCaches = await listActionsCaches( - CODEQL_TRAP_CACHE_PREFIX, - await getRef(env, config.repositoryRoot) +function warnIfTrapCacheCleanupEnvVarIsSet({ + env, + logger: logger2 +}) { + if (env.getOptional("CODEQL_ACTION_CLEANUP_TRAP_CACHES") !== void 0) { + logger2.warning( + "TRAP cache cleanup has been removed, so the CODEQL_ACTION_CLEANUP_TRAP_CACHES environment variable no longer has any effect and can be removed from your workflow. If you used TRAP cache cleanup to limit Actions cache usage, we recommend disabling TRAP caching instead by passing the `trap-caching: false` input to the `init` Action." ); - for (const language of config.languages) { - if (config.trapCaches[language]) { - const cachesToRemove = await getTrapCachesForLanguage( - allCaches, - language, - logger2 - ); - cachesToRemove.sort((a, b) => a.created_at.localeCompare(b.created_at)); - const mostRecentCache = cachesToRemove.pop(); - logger2.debug( - `Keeping most recent TRAP cache (${JSON.stringify(mostRecentCache)})` - ); - if (cachesToRemove.length === 0) { - logger2.info(`No TRAP caches to clean up for ${language}.`); - continue; - } - for (const cache of cachesToRemove) { - logger2.debug(`Cleaning up TRAP cache (${JSON.stringify(cache)})`); - await deleteActionsCache(cache.id); - } - const bytesCleanedUp = cachesToRemove.reduce( - (acc, item) => acc + item.size_in_bytes, - 0 - ); - totalBytesCleanedUp += bytesCleanedUp; - const megabytesCleanedUp = (bytesCleanedUp / (1024 * 1024)).toFixed(2); - logger2.info( - `Cleaned up ${megabytesCleanedUp} MiB of old TRAP caches for ${language}.` - ); - } - } - return { trap_cache_cleanup_size_bytes: totalBytesCleanedUp }; - } catch (e) { - if (asHTTPError(e)?.status === 403) { - logger2.warning( - `Could not cleanup TRAP caches as the token did not have the required permissions. To clean up TRAP caches, ensure the token has the "actions:write" permission. See ${"https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs" /* ASSIGNING_PERMISSIONS_TO_JOBS */} for more information.` - ); - } else { - logger2.info(`Failed to cleanup TRAP caches, continuing. Details: ${e}`); - } - return { trap_cache_cleanup_error: getErrorMessage(e) }; } } -async function getTrapCachesForLanguage(allCaches, language, logger2) { - logger2.debug(`Listing TRAP caches for ${language}`); - for (const cache of allCaches) { - if (!cache.created_at || !cache.id || !cache.key || !cache.size_in_bytes) { - throw new Error( - `An unexpected cache item was returned from the API that was missing one or more required fields: ${JSON.stringify(cache)}` - ); - } - } - return allCaches.filter((cache) => { - return cache.key?.includes(`-${language}-`); - }); -} async function getLanguagesSupportingCaching(codeql, languages, logger2) { const result = []; const resolveResult = await codeql.resolveLanguages(); @@ -174427,8 +174423,8 @@ async function getSupportedLanguageMap(codeql, logger2) { var baseWorkflowsPath = ".github/workflows"; function hasActionsWorkflows(sourceRoot) { const workflowsPath = path11.resolve(sourceRoot, baseWorkflowsPath); - const stats = fs10.lstatSync(workflowsPath, { throwIfNoEntry: false }); - return stats !== void 0 && stats.isDirectory() && fs10.readdirSync(workflowsPath).length > 0; + const stats = fs11.lstatSync(workflowsPath, { throwIfNoEntry: false }); + return stats !== void 0 && stats.isDirectory() && fs11.readdirSync(workflowsPath).length > 0; } async function getRawLanguagesInRepo(repository, sourceRoot, logger2) { logger2.debug( @@ -174574,7 +174570,9 @@ async function initActionState({ useOverlayDatabaseCaching: false, overlayModeSetExplicitly: false, repositoryProperties, - enableFileCoverageInformation + enableFileCoverageInformation, + // We don't set this to `true` until `init` is successful. + successfullyInitialised: false }; } async function downloadCacheWithTime(codeQL, languages, logger2, repositoryRoot) { @@ -174920,7 +174918,7 @@ async function determineUserConfig(action, tempDir, inputs) { inputs.configInput, fromConfigFile ); - fs10.writeFileSync(computedConfigPath, dump(mergedConfig)); + fs11.writeFileSync(computedConfigPath, dump(mergedConfig)); action.logger.debug( `Using merged configurations from 'config' input with configuration from '${inputs.configFile}': ${computedConfigPath}` ); @@ -174932,7 +174930,7 @@ async function determineUserConfig(action, tempDir, inputs) { `Both a config file and config input were provided. Ignoring config file.` ); } - fs10.writeFileSync(computedConfigPath, dump(inputs.configInput)); + fs11.writeFileSync(computedConfigPath, dump(inputs.configInput)); inputs.configFile = computedConfigPath; action.logger.debug( `Using config from action input: ${inputs.configFile}` @@ -175089,7 +175087,7 @@ function isLocal(configPath) { return !containsAtRef(configPath); } function getLocalConfig(logger2, configFile, validateConfig) { - if (!fs10.existsSync(configFile)) { + if (!fs11.existsSync(configFile)) { throw new ConfigurationError( getConfigFileDoesNotExistErrorMessage(configFile) ); @@ -175097,7 +175095,7 @@ function getLocalConfig(logger2, configFile, validateConfig) { return parseUserConfig( logger2, configFile, - fs10.readFileSync(configFile, "utf-8"), + fs11.readFileSync(configFile, "utf-8"), validateConfig ); } @@ -175109,8 +175107,8 @@ function getPathToFailedConfigFile(tempDir) { } function saveConfigFile(logger2, configFilePath, config) { const configString = JSON.stringify(config); - fs10.mkdirSync(path11.dirname(configFilePath), { recursive: true }); - fs10.writeFileSync(configFilePath, configString, "utf8"); + fs11.mkdirSync(path11.dirname(configFilePath), { recursive: true }); + fs11.writeFileSync(configFilePath, configString, "utf8"); logger2.debug("Saved config:"); logger2.debug(configString); } @@ -175141,10 +175139,10 @@ function saveAbortedInitConfig(action, config) { ); } function readConfigFile(logger2, configFilePath) { - if (!fs10.existsSync(configFilePath)) { + if (!fs11.existsSync(configFilePath)) { return void 0; } - const configString = fs10.readFileSync(configFilePath, "utf8"); + const configString = fs11.readFileSync(configFilePath, "utf8"); logger2.debug("Loaded config:"); logger2.debug(configString); const config = JSON.parse(configString); @@ -175185,7 +175183,7 @@ async function generateRegistries(registriesInput, tempDir, logger2) { const qlconfig = createRegistriesBlock(registries); qlconfigFile = path11.join(tempDir, "qlconfig.yml"); const qlconfigContents = dump(qlconfig); - fs10.writeFileSync(qlconfigFile, qlconfigContents, "utf8"); + fs11.writeFileSync(qlconfigFile, qlconfigContents, "utf8"); logger2.debug("Generated qlconfig.yml:"); logger2.debug(qlconfigContents); registriesAuthTokens = registries.map((registry) => `${registry.url}=${registry.token}`).join(","); @@ -175314,7 +175312,7 @@ async function logGeneratedFilesTelemetry(config, duration, generatedFilesCount) } // src/setup-codeql.ts -var fs14 = __toESM(require("fs")); +var fs15 = __toESM(require("fs")); var path13 = __toESM(require("path")); var import_perf_hooks4 = require("perf_hooks"); var toolcache3 = __toESM(require_tool_cache()); @@ -175350,7 +175348,7 @@ function getCodeQLBundleFromUrl(url2) { } // src/overlay/caching.ts -var fs11 = __toESM(require("fs")); +var fs12 = __toESM(require("fs")); var actionsCache3 = __toESM(require_cache4()); var semver5 = __toESM(require_semver2()); var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 7500; @@ -175360,7 +175358,7 @@ var CACHE_PREFIX = "codeql-overlay-base-database"; var MAX_CACHE_OPERATION_MS3 = 6e5; async function checkOverlayBaseDatabase(codeql, config, logger2, warningPrefix) { const baseDatabaseOidsFilePath = getBaseDatabaseOidsFilePath(config); - if (!fs11.existsSync(baseDatabaseOidsFilePath)) { + if (!fs12.existsSync(baseDatabaseOidsFilePath)) { logger2.warning( `${warningPrefix}: ${baseDatabaseOidsFilePath} does not exist` ); @@ -175847,7 +175845,7 @@ async function getCodeQLAssetDownloadURL(action, apiDetails, tagName, assetName, // src/tar.ts var import_child_process = require("child_process"); -var fs12 = __toESM(require("fs")); +var fs13 = __toESM(require("fs")); var stream = __toESM(require("stream")); var import_toolrunner = __toESM(require_toolrunner()); var io4 = __toESM(require_io()); @@ -175920,7 +175918,7 @@ async function isZstdAvailable(logger2) { } } async function extract(tarPath, dest, compressionMethod, tarVersion, logger2) { - fs12.mkdirSync(dest, { recursive: true }); + fs13.mkdirSync(dest, { recursive: true }); switch (compressionMethod) { case "gzip": return await toolcache.extractTar(tarPath, dest); @@ -176006,7 +176004,7 @@ function inferCompressionMethod(tarPath) { } // src/tools-download.ts -var fs13 = __toESM(require("fs")); +var fs14 = __toESM(require("fs")); var os4 = __toESM(require("os")); var path12 = __toESM(require("path")); var import_perf_hooks3 = require("perf_hooks"); @@ -176092,7 +176090,7 @@ async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorizat }; } async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorization, headers, tarVersion, logger2) { - fs13.mkdirSync(dest, { recursive: true }); + fs14.mkdirSync(dest, { recursive: true }); const agent = new import_http_client.HttpClient().getAgent(codeqlURL); headers = Object.assign( { "User-Agent": "CodeQL Action" }, @@ -176151,7 +176149,7 @@ function getToolcacheDirectory(version) { } function isToolcacheOnWorkspaceFilesystem(logger2) { try { - return fs13.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev === fs13.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev; + return fs14.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev === fs14.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev; } catch (e) { logger2.debug( `Could not determine whether the toolcache is on the same filesystem as the workspace: ${getErrorMessage(e)}` @@ -176173,7 +176171,7 @@ async function deleteToolcacheBundles({ return { deletedVersions: [], failed: true }; } try { - if ((await fs13.promises.lstat(toolDirectory)).isSymbolicLink()) { + if ((await fs14.promises.lstat(toolDirectory)).isSymbolicLink()) { logger2.info( `Not deleting the CodeQL tools from the toolcache since '${toolDirectory}' is a symlink.` ); @@ -176192,7 +176190,7 @@ async function deleteToolcacheBundles({ return { deletedVersions: [], failed: true }; } try { - const entries = await fs13.promises.readdir(toolDirectory, { + const entries = await fs14.promises.readdir(toolDirectory, { withFileTypes: true }); const deletedVersions = []; @@ -176206,7 +176204,7 @@ async function deleteToolcacheBundles({ } const versionDirectory = path12.join(toolDirectory, entry.name); try { - await fs13.promises.rm(versionDirectory, { + await fs14.promises.rm(versionDirectory, { force: true, recursive: true }); @@ -176231,7 +176229,7 @@ async function deleteToolcacheBundles({ } function writeToolcacheMarkerFile(extractedPath, logger2) { const markerFilePath = `${extractedPath}.complete`; - fs13.writeFileSync(markerFilePath, ""); + fs14.writeFileSync(markerFilePath, ""); logger2.info(`Created toolcache marker file ${markerFilePath}`); } @@ -176283,7 +176281,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger2) { const candidates = toolcache3.findAllVersions("CodeQL").filter(isGoodVersion).map((version) => ({ folder: toolcache3.find("CodeQL", version), version - })).filter(({ folder }) => fs14.existsSync(path13.join(folder, "pinned-version"))); + })).filter(({ folder }) => fs15.existsSync(path13.join(folder, "pinned-version"))); if (candidates.length === 1) { const candidate = candidates[0]; logger2.debug( @@ -176305,17 +176303,10 @@ async function findOverridingToolsInCache(humanReadableVersion, logger2) { } return void 0; } -async function getEnabledVersionsWithOverlayBaseDatabases(defaultCliVersion, rawLanguages, features, logger2) { +async function getEnabledVersionsWithOverlayBaseDatabases(defaultCliVersion, rawLanguages, logger2) { if (rawLanguages === void 0 || rawLanguages.length === 0) { return []; } - const isEnabled = await features.getValue( - "overlay_analysis_match_codeql_version" /* OverlayAnalysisMatchCodeqlVersion */ - ); - const isDryRun = !isEnabled && await features.getValue("overlay_analysis_match_codeql_version_dry_run" /* OverlayAnalysisMatchCodeqlVersionDryRun */); - if (!isEnabled && !isDryRun) { - return []; - } let cachedVersions; try { cachedVersions = await getCodeQlVersionsForOverlayBaseDatabases( @@ -176332,46 +176323,18 @@ async function getEnabledVersionsWithOverlayBaseDatabases(defaultCliVersion, raw return []; } const cachedVersionsSet = new Set(cachedVersions); - const overlayVersions = defaultCliVersion.enabledVersions.filter( + return defaultCliVersion.enabledVersions.filter( (v) => cachedVersionsSet.has(v.cliVersion) ); - if (overlayVersions.length === 0) { - return []; - } - const isCachedVersionDifferent = overlayVersions[0].cliVersion !== defaultCliVersion.enabledVersions[0].cliVersion; - if (isCachedVersionDifferent) { - addNoLanguageDiagnostic( - void 0, - makeTelemetryDiagnostic( - "codeql-action/overlay-aware-default-codeql-version", - "Overlay-aware default CodeQL version selection", - { - cachedVersions, - enabledVersions: defaultCliVersion.enabledVersions.map( - (v) => v.cliVersion - ), - isDryRun, - overlayAwareVersion: overlayVersions[0].cliVersion - } - ) - ); - } - if (isDryRun) { - logger2.debug( - `Overlay-aware default CodeQL version selection is running in dry-run mode. Would have used version ${overlayVersions[0].cliVersion}.` - ); - return []; - } - return overlayVersions; } -async function resolveDefaultCliVersion(defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger2) { - if (!useOverlayAwareDefaultCliVersion || !isAnalyzingPullRequest()) { +async function resolveDefaultCliVersion(defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, logger2) { + if (!useOverlayAwareDefaultCliVersion || // Listing the Actions cache can't change which version we use if only one version is enabled. + defaultCliVersion.enabledVersions.length === 1 || !isAnalyzingPullRequest()) { return defaultCliVersion.enabledVersions[0]; } const overlayVersions = await getEnabledVersionsWithOverlayBaseDatabases( defaultCliVersion, rawLanguages, - features, logger2 ); if (overlayVersions.length > 0) { @@ -176478,7 +176441,6 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, othe defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, - features, logger2 ); cliVersion2 = version.cliVersion; @@ -176498,7 +176460,6 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, othe defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, - features, logger2 ); cliVersion2 = version.cliVersion; @@ -176945,7 +176906,7 @@ function isReservedToolsValue(tools) { } // src/tracer-config.ts -var fs15 = __toESM(require("fs")); +var fs16 = __toESM(require("fs")); var path14 = __toESM(require("path")); async function shouldEnableIndirectTracing(codeql, config) { if (config.buildMode === "none" /* None */) { @@ -176965,14 +176926,14 @@ async function endTracingForCluster(codeql, config, logger2) { config.dbLocation, "temp/tracingEnvironment/end-tracing.json" ); - if (!fs15.existsSync(envVariablesFile)) { + if (!fs16.existsSync(envVariablesFile)) { throw new Error( `Environment file for ending tracing not found: ${envVariablesFile}` ); } try { const endTracingEnvVariables = JSON.parse( - fs15.readFileSync(envVariablesFile, "utf8") + fs16.readFileSync(envVariablesFile, "utf8") ); for (const [key, value] of Object.entries(endTracingEnvVariables)) { if (value !== null) { @@ -176993,7 +176954,7 @@ async function getTracerConfigForCluster(logger2, config) { "temp/tracingEnvironment/start-tracing.json" ); try { - const tracingEnvVariables = JSON.parse(fs15.readFileSync(filePath, "utf8")); + const tracingEnvVariables = JSON.parse(fs16.readFileSync(filePath, "utf8")); return { env: tracingEnvVariables }; @@ -177115,7 +177076,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion) { "tools", "tracing-config.lua" ); - return fs16.existsSync(tracingConfigPath); + return fs17.existsSync(tracingConfigPath); }, async isScannedLanguage(language) { return !await this.isTracedLanguage(language); @@ -177397,15 +177358,26 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion) { } await new toolrunner3.ToolRunner(cmd, args).exec(); }, - async diagnosticsExport(sarifFile, automationDetailsId, config) { + async diagnosticsExport(sarifFile, automationDetailsId, config, diagnosticDir) { + let configPath = getGeneratedCodeScanningConfigPath(config); + if (!fs17.existsSync(configPath)) { + if (config.successfullyInitialised) { + logger2.warning( + [ + `Expected a configuration file at '${configPath}', which does not exist.`, + "Continuing with diagnostics export anyway, without a configuration file." + ].join(" ") + ); + } + configPath = void 0; + } const args = [ "diagnostics", "export", "--format=sarif-latest", `--output=${sarifFile}`, - `--sarif-codescanning-config=${getGeneratedCodeScanningConfigPath( - config - )}`, + ...configPath !== void 0 ? [`--sarif-codescanning-config=${configPath}`] : [], + ...diagnosticDir !== void 0 ? [`--diagnostic-dir=${diagnosticDir}`] : [], ...getExtraOptionsFromEnv(["diagnostics", "export"]) ]; if (automationDetailsId !== void 0) { @@ -177573,7 +177545,7 @@ async function writeCodeScanningConfigFile(config, logger2) { logger2.startGroup("Augmented user configuration file contents"); logger2.info(dump(augmentedConfig)); logger2.endGroup(); - fs16.writeFileSync(codeScanningConfigFile, dump(augmentedConfig)); + fs17.writeFileSync(codeScanningConfigFile, dump(augmentedConfig)); return codeScanningConfigFile; } var TRAP_CACHE_SIZE_MB = 1024; @@ -178066,7 +178038,7 @@ function dbIsFinalized(config, language, logger2) { const dbPath = getCodeQLDatabasePath(config, language); try { const dbInfo = load( - fs17.readFileSync(path16.resolve(dbPath, "codeql-database.yml"), "utf8") + fs18.readFileSync(path16.resolve(dbPath, "codeql-database.yml"), "utf8") ); return !("inProgress" in dbInfo); } catch { @@ -178150,8 +178122,8 @@ function writeDiffRangeDataExtensionPack(logger2, ranges, checkoutPath) { ranges = [{ path: "", startLine: 0, endLine: 0 }]; } const diffRangeDir = path16.join(getTemporaryDirectory(), "pr-diff-range"); - fs17.mkdirSync(diffRangeDir, { recursive: true }); - fs17.writeFileSync( + fs18.mkdirSync(diffRangeDir, { recursive: true }); + fs18.writeFileSync( path16.join(diffRangeDir, "qlpack.yml"), ` name: codeql-action/pr-diff-range @@ -178168,7 +178140,7 @@ dataExtensions: checkoutPath ); const extensionFilePath = path16.join(diffRangeDir, "pr-diff-range.yml"); - fs17.writeFileSync(extensionFilePath, extensionContents); + fs18.writeFileSync(extensionFilePath, extensionContents); logger2.debug( `Wrote pr-diff-range extension pack to ${extensionFilePath}: ${extensionContents}` @@ -178313,7 +178285,7 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir } function getPerQueryAlertCounts(sarifPath) { const sarifObject = JSON.parse( - fs17.readFileSync(sarifPath, "utf8") + fs18.readFileSync(sarifPath, "utf8") ); const perQueryAlertCounts = {}; for (const sarifRun of sarifObject.runs) { @@ -178331,13 +178303,13 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir } async function runFinalize(features, outputDir, threadsFlag, memoryFlag, codeql, config, logger2) { try { - await fs17.promises.rm(outputDir, { force: true, recursive: true }); + await fs18.promises.rm(outputDir, { force: true, recursive: true }); } catch (error3) { if (error3?.code !== "ENOENT") { throw error3; } } - await fs17.promises.mkdir(outputDir, { recursive: true }); + await fs18.promises.mkdir(outputDir, { recursive: true }); const timings = await finalizeDatabaseCreation( codeql, features, @@ -178381,7 +178353,7 @@ async function warnIfGoInstalledAfterInit(config, logger2) { } // src/database-upload.ts -var fs18 = __toESM(require("fs")); +var fs19 = __toESM(require("fs")); async function cleanupAndUploadDatabases(action, repositoryNwo, codeql, config, apiDetails, checkoutPath) { const logger2 = action.logger; if (getRequiredInput("upload-database") !== "true") { @@ -178418,7 +178390,7 @@ async function cleanupAndUploadDatabases(action, repositoryNwo, codeql, config, const bundledDb = await bundleDb(config, language, codeql, language, { includeDiagnostics: false }); - bundledDbSize = fs18.statSync(bundledDb).size; + bundledDbSize = fs19.statSync(bundledDb).size; const commitOid = await getCommitOid(action.env, checkoutPath); const maxAttempts = 4; let uploadDurationMs; @@ -178496,7 +178468,7 @@ async function recordClearCleanupSizes(codeql, config, reports, logger2) { const bundledDb = await bundleDb(config, language, codeql, language, { includeDiagnostics: false }); - report.clear_cleanup_zipped_size_bytes = fs18.statSync(bundledDb).size; + report.clear_cleanup_zipped_size_bytes = fs19.statSync(bundledDb).size; logger2.debug( `Database for ${language} is ${report.clear_cleanup_zipped_size_bytes} bytes zipped at the '${"clear" /* Clear */}' cleanup level (vs. ${report.zipped_upload_size_bytes ?? "unknown"} bytes at the '${"overlay" /* Overlay */}' level).` ); @@ -178519,7 +178491,7 @@ async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundled if (uploadsBaseUrl.endsWith("/")) { uploadsBaseUrl = uploadsBaseUrl.slice(0, -1); } - const bundledDbReadStream = fs18.createReadStream(bundledDb); + const bundledDbReadStream = fs19.createReadStream(bundledDb); try { const startTime = performance.now(); await client.request( @@ -178571,7 +178543,7 @@ __export(upload_lib_exports, { waitForProcessing: () => waitForProcessing, writePostProcessedFiles: () => writePostProcessedFiles }); -var fs22 = __toESM(require("fs")); +var fs23 = __toESM(require("fs")); var path19 = __toESM(require("path")); var url = __toESM(require("url")); var import_zlib = __toESM(require("zlib")); @@ -178579,7 +178551,7 @@ var core13 = __toESM(require_core()); var jsonschema2 = __toESM(require_lib2()); // src/fingerprints.ts -var fs19 = __toESM(require("fs")); +var fs20 = __toESM(require("fs")); var import_path4 = __toESM(require("path")); // node_modules/long/index.js @@ -179567,7 +179539,7 @@ async function hash(callback, filepath) { } updateHash(current); }; - const readStream = fs19.createReadStream(filepath, "utf8"); + const readStream = fs20.createReadStream(filepath, "utf8"); for await (const data of readStream) { for (let i = 0; i < data.length; ++i) { processCharacter(data.charCodeAt(i)); @@ -179642,11 +179614,11 @@ function resolveUriToFile(location, artifacts, sourceRoot, logger2) { if (!import_path4.default.isAbsolute(uri)) { uri = srcRootPrefix + uri; } - if (!fs19.existsSync(uri)) { + if (!fs20.existsSync(uri)) { logger2.debug(`Unable to compute fingerprint for non-existent file: ${uri}`); return void 0; } - if (fs19.statSync(uri).isDirectory()) { + if (fs20.statSync(uri).isDirectory()) { logger2.debug(`Unable to compute fingerprint for directory: ${uri}`); return void 0; } @@ -179701,7 +179673,7 @@ async function addFingerprints(sarifLog, sourceRoot, logger2) { } // src/init.ts -var fs20 = __toESM(require("fs")); +var fs21 = __toESM(require("fs")); var path18 = __toESM(require("path")); var toolrunner4 = __toESM(require_toolrunner()); var github3 = __toESM(require_github()); @@ -179736,7 +179708,7 @@ async function initConfig2(actionState, inputs) { }); } async function runDatabaseInitCluster(databaseInitEnvironment, codeql, config, sourceRoot, processName, qlconfigFile) { - fs20.mkdirSync(config.dbLocation, { recursive: true }); + fs21.mkdirSync(config.dbLocation, { recursive: true }); await wrapEnvironment( databaseInitEnvironment, async () => await codeql.databaseInitCluster( @@ -179771,24 +179743,24 @@ async function checkPacksForOverlayCompatibility(codeql, config, logger2) { function checkPackForOverlayCompatibility(packDir, codeQlOverlayVersion, logger2) { try { let qlpackPath = path18.join(packDir, "qlpack.yml"); - if (!fs20.existsSync(qlpackPath)) { + if (!fs21.existsSync(qlpackPath)) { qlpackPath = path18.join(packDir, "codeql-pack.yml"); } const qlpackContents = load( - fs20.readFileSync(qlpackPath, "utf8") + fs21.readFileSync(qlpackPath, "utf8") ); if (!qlpackContents.buildMetadata) { return true; } const packInfoPath = path18.join(packDir, ".packinfo"); - if (!fs20.existsSync(packInfoPath)) { + if (!fs21.existsSync(packInfoPath)) { logger2.warning( `The query pack at ${packDir} does not have a .packinfo file, so it cannot support overlay analysis. Recompiling the query pack with the latest CodeQL CLI should solve this problem.` ); return false; } const packInfoFileContents = JSON.parse( - fs20.readFileSync(packInfoPath, "utf8") + fs21.readFileSync(packInfoPath, "utf8") ); const packOverlayVersion = packInfoFileContents.overlayVersion; if (typeof packOverlayVersion !== "number") { @@ -179823,8 +179795,8 @@ async function checkInstallPython311(languages, codeql) { ]).exec(); } } -function cleanupDatabaseClusterDirectory(config, logger2, options = {}, rmSync5 = fs20.rmSync) { - if (fs20.existsSync(config.dbLocation) && (fs20.statSync(config.dbLocation).isFile() || fs20.readdirSync(config.dbLocation).length > 0)) { +function cleanupDatabaseClusterDirectory(config, logger2, options = {}, rmSync5 = fs21.rmSync) { + if (fs21.existsSync(config.dbLocation) && (fs21.statSync(config.dbLocation).isFile() || fs21.readdirSync(config.dbLocation).length > 0)) { if (!options.disableExistingDirectoryWarning) { logger2.warning( `The database cluster directory ${config.dbLocation} must be empty. Attempting to clean it up.` @@ -179929,7 +179901,7 @@ To opt out of this change, ${envVarOptOut}`; } // src/sarif/index.ts -var fs21 = __toESM(require("fs")); +var fs22 = __toESM(require("fs")); var InvalidSarifUploadError = class extends Error { }; function getToolNames(sarifFile) { @@ -179945,7 +179917,7 @@ function getToolNames(sarifFile) { } function readSarifFile(sarifFilePath) { try { - return JSON.parse(fs21.readFileSync(sarifFilePath, "utf8")); + return JSON.parse(fs22.readFileSync(sarifFilePath, "utf8")); } catch (err) { throw new Error( `Parsing SARIF file at '${sarifFilePath}' failed: ${getErrorMessage(err)}` @@ -180086,8 +180058,8 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo codeQL = initCodeQLResult.codeql; } const baseTempDir = path19.resolve(tempDir, "combined-sarif"); - fs22.mkdirSync(baseTempDir, { recursive: true }); - const outputDirectory = fs22.mkdtempSync(path19.resolve(baseTempDir, "output-")); + fs23.mkdirSync(baseTempDir, { recursive: true }); + const outputDirectory = fs23.mkdtempSync(path19.resolve(baseTempDir, "output-")); const outputFile = path19.resolve(outputDirectory, "combined-sarif.sarif"); await codeQL.mergeResults(sarifFiles, outputFile, { mergeRunsFromEqualCategory: true @@ -180132,7 +180104,7 @@ async function uploadPayload(payload, repositoryNwo, logger2, analysis) { `SARIF upload disabled by an environment variable. Saving to ${payloadSaveFile}` ); logger2.info(`Payload: ${JSON.stringify(payload, null, 2)}`); - fs22.writeFileSync(payloadSaveFile, JSON.stringify(payload, null, 2)); + fs23.writeFileSync(payloadSaveFile, JSON.stringify(payload, null, 2)); return "dummy-sarif-id"; } const client = getApiClient(); @@ -180166,7 +180138,7 @@ async function uploadPayload(payload, repositoryNwo, logger2, analysis) { function findSarifFilesInDir(sarifPath, isSarif) { const sarifFiles = []; const walkSarifFiles = (dir) => { - const entries = fs22.readdirSync(dir, { withFileTypes: true }); + const entries = fs23.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && isSarif(entry.name)) { sarifFiles.push(path19.resolve(dir, entry.name)); @@ -180179,11 +180151,11 @@ function findSarifFilesInDir(sarifPath, isSarif) { return sarifFiles; } function getSarifFilePaths(sarifPath, isSarif) { - if (!fs22.existsSync(sarifPath)) { + if (!fs23.existsSync(sarifPath)) { throw new ConfigurationError(`Path does not exist: ${sarifPath}`); } let sarifFiles; - if (fs22.lstatSync(sarifPath).isDirectory()) { + if (fs23.lstatSync(sarifPath).isDirectory()) { sarifFiles = findSarifFilesInDir(sarifPath, isSarif); if (sarifFiles.length === 0) { throw new ConfigurationError( @@ -180196,7 +180168,7 @@ function getSarifFilePaths(sarifPath, isSarif) { return sarifFiles; } async function getGroupedSarifFilePaths(logger2, sarifPath) { - const stats = fs22.statSync(sarifPath, { throwIfNoEntry: false }); + const stats = fs23.statSync(sarifPath, { throwIfNoEntry: false }); if (stats === void 0) { throw new ConfigurationError(`Path does not exist: ${sarifPath}`); } @@ -180331,7 +180303,7 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo payloadObj.base_sha = mergeBaseCommitOid; } else if (process.env.GITHUB_EVENT_PATH) { const githubEvent = JSON.parse( - fs22.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") + fs23.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") ); payloadObj.base_ref = `refs/heads/${githubEvent.pull_request.base.ref}`; payloadObj.base_sha = githubEvent.pull_request.base.sha; @@ -180469,9 +180441,9 @@ async function uploadPostProcessedFiles(logger2, checkoutPath, uploadTarget, pos }; } function dumpSarifFile(sarifPayload, outputDir, logger2, uploadTarget) { - if (!fs22.existsSync(outputDir)) { - fs22.mkdirSync(outputDir, { recursive: true }); - } else if (!fs22.lstatSync(outputDir).isDirectory()) { + if (!fs23.existsSync(outputDir)) { + fs23.mkdirSync(outputDir, { recursive: true }); + } else if (!fs23.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( `The path that processed SARIF files should be written to exists, but is not a directory: ${outputDir}` ); @@ -180481,7 +180453,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger2, uploadTarget) { `upload${uploadTarget.sarifExtension}` ); logger2.info(`Writing processed SARIF file to ${outputFile}`); - fs22.writeFileSync(outputFile, sarifPayload); + fs23.writeFileSync(outputFile, sarifPayload); } var STATUS_CHECK_INITIAL_BACKOFF_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_BACKOFF_MULTIPLIER = 2; @@ -180680,7 +180652,7 @@ async function postProcessAndUploadSarif(logger2, features, config, uploadKind, } // src/analyze-action.ts -async function sendStatusReport2(startedAt, config, stats, error3, trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, trapCacheCleanup, dependencyCacheResults, databaseUploadResults, logger2) { +async function sendStatusReport2(startedAt, config, stats, error3, trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, dependencyCacheResults, databaseUploadResults, logger2) { const status = getActionsStatus(error3, stats?.analyze_failure_language); const statusReportBase = await createStatusReportBase( "finish" /* Analyze */, @@ -180697,7 +180669,6 @@ async function sendStatusReport2(startedAt, config, stats, error3, trapCacheUplo ...statusReportBase, ...stats || {}, ...dbCreationTimings || {}, - ...trapCacheCleanup || {}, dependency_caching_upload_results: dependencyCacheResults, database_upload_results: databaseUploadResults }; @@ -180728,7 +180699,7 @@ function doesGoExtractionOutputExist(config) { "trap", "go" /* go */ ); - return fs23.existsSync(trapDirectory) && fs23.readdirSync(trapDirectory).some( + return fs24.existsSync(trapDirectory) && fs24.readdirSync(trapDirectory).some( (fileName) => [ ".trap", ".trap.gz", @@ -180781,7 +180752,6 @@ async function run(action) { let uploadResults = void 0; let runStats = void 0; let config = void 0; - let trapCacheCleanupTelemetry = void 0; let trapCacheUploadTime = void 0; let dbCreationTimings = void 0; let didUploadTrapCaches = false; @@ -180818,6 +180788,7 @@ async function run(action) { "The 'cleanup-level' input is ignored since the CodeQL Action now automatically manages database cleanup. This input can safely be removed from your workflow." ); } + warnIfTrapCacheCleanupEnvVarIsSet(action); const apiDetails = getApiDetails(); const outputDir = getRequiredInput("output"); action.env.export("CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR" /* SARIF_RESULTS_OUTPUT_DIR */, outputDir); @@ -180926,11 +180897,6 @@ async function run(action) { const trapCacheUploadStartTime = import_perf_hooks6.performance.now(); didUploadTrapCaches = await uploadTrapCaches(codeql, config, logger2); trapCacheUploadTime = import_perf_hooks6.performance.now() - trapCacheUploadStartTime; - trapCacheCleanupTelemetry = await cleanupTrapCaches( - config, - features, - logger2 - ); if (shouldStoreCache(config.dependencyCachingEnabled)) { dependencyCacheResults = await uploadDependencyCaches( codeql, @@ -180969,7 +180935,6 @@ async function run(action) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger2 @@ -180988,7 +180953,6 @@ async function run(action) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger2 @@ -181002,7 +180966,6 @@ async function run(action) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger2 @@ -181016,7 +180979,6 @@ async function run(action) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger2 @@ -181033,17 +180995,17 @@ async function runWrapper() { } // src/analyze-action-post.ts -var fs27 = __toESM(require("fs")); +var fs28 = __toESM(require("fs")); // src/debug-artifacts.ts -var fs26 = __toESM(require("fs")); +var fs27 = __toESM(require("fs")); var path23 = __toESM(require("path")); var artifact = __toESM(require_artifact2()); var artifactLegacy = __toESM(require_artifact_client2()); var core15 = __toESM(require_core()); // node_modules/archiver/lib/core.js -var import_fs2 = require("fs"); +var import_fs = require("fs"); // node_modules/is-stream/index.js function isStream(stream2, { checkOpen = true } = {}) { @@ -181051,7 +181013,7 @@ function isStream(stream2, { checkOpen = true } = {}) { } // node_modules/readdir-glob/dist/index.mjs -var fs24 = __toESM(require("fs"), 1); +var fs25 = __toESM(require("fs"), 1); var import_events = require("events"); // node_modules/readdir-glob/node_modules/balanced-match/dist/esm/index.js @@ -182928,7 +182890,7 @@ minimatch.unescape = unescape2; var import_path6 = require("path"); function readdir2(dir, strict) { return new Promise((resolve$1, reject) => { - fs24.readdir(dir, { withFileTypes: true }, (err, files) => { + fs25.readdir(dir, { withFileTypes: true }, (err, files) => { if (err) switch (err.code) { case "ENOTDIR": if (strict) reject(err); @@ -182951,7 +182913,7 @@ function readdir2(dir, strict) { } function getStat(file, followSymlinks) { return new Promise((resolve$1) => { - const statFunc = followSymlinks ? fs24.stat : fs24.lstat; + const statFunc = followSymlinks ? fs25.stat : fs25.lstat; statFunc(file, (err, stats) => { if (err) switch (err.code) { case "ENOENT": @@ -183259,7 +183221,7 @@ var Archiver = class extends import_readable_stream2.Transform { data.sourcePath = filepath; task.data = data; this._entriesCount++; - if (data.stats && data.stats instanceof import_fs2.Stats) { + if (data.stats && data.stats instanceof import_fs.Stats) { task = this._updateQueueTaskWithStats(task, data.stats); if (task) { if (data.stats.size) { @@ -183507,7 +183469,7 @@ var Archiver = class extends import_readable_stream2.Transform { callback(); return; } - (0, import_fs2.lstat)( + (0, import_fs.lstat)( task.filepath, function(err, stats) { if (this._state.aborted) { @@ -183569,7 +183531,7 @@ var Archiver = class extends import_readable_stream2.Transform { task.data.type = "file"; task.data.sourceType = "stream"; task.source = new import_lazystream.Readable(function() { - return (0, import_fs2.createReadStream)(task.filepath); + return (0, import_fs.createReadStream)(task.filepath); }); } else if (stats.isDirectory() && this._supportsDirectory) { task.data.name = trailingSlashIt(task.data.name); @@ -183578,7 +183540,7 @@ var Archiver = class extends import_readable_stream2.Transform { task.data.sourceType = "buffer"; task.source = Buffer.concat([]); } else if (stats.isSymbolicLink() && this._supportsSymlink) { - const linkPath = (0, import_fs2.readlinkSync)(task.filepath); + const linkPath = (0, import_fs.readlinkSync)(task.filepath); const dirName = (0, import_path7.dirname)(task.filepath); task.data.type = "symlink"; task.data.linkname = (0, import_path7.relative)( @@ -185421,7 +185383,7 @@ var ZipArchive = class extends Archiver { }; // src/artifact-scanner.ts -var fs25 = __toESM(require("fs")); +var fs26 = __toESM(require("fs")); var os6 = __toESM(require("os")); var path22 = __toESM(require("path")); var exec = __toESM(require_exec()); @@ -185468,7 +185430,7 @@ function isAuthToken(value, patterns = GITHUB_TOKEN_PATTERNS) { function scanFileForTokens(filePath, relativePath2, logger2) { const findings = []; try { - const content = fs25.readFileSync(filePath, "utf8"); + const content = fs26.readFileSync(filePath, "utf8"); for (const { type, pattern } of GITHUB_TOKEN_PATTERNS) { const matches = content.match(pattern); if (matches) { @@ -185501,7 +185463,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log findings: [] }; try { - const tempExtractDir = fs25.mkdtempSync( + const tempExtractDir = fs26.mkdtempSync( path22.join(extractDir, `extract-${depth}-`) ); const fileName = path22.basename(archivePath).toLowerCase(); @@ -185535,7 +185497,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log path22.basename(archivePath, ".gz") ); await exec.exec("gunzip", ["-c", archivePath], { - outStream: fs25.createWriteStream(outputFile), + outStream: fs26.createWriteStream(outputFile), silent: true }); } else if (fileName.endsWith(".zip")) { @@ -185556,7 +185518,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log ); result.scannedFiles += scanResult.scannedFiles; result.findings.push(...scanResult.findings); - fs25.rmSync(tempExtractDir, { recursive: true, force: true }); + fs26.rmSync(tempExtractDir, { recursive: true, force: true }); } catch (e) { logger2.debug( `Could not extract or scan archive file ${archivePath}: ${getErrorMessage(e)}` @@ -185591,7 +185553,7 @@ async function scanDirectory(dirPath, baseRelativePath, logger2, depth = 0) { scannedFiles: 0, findings: [] }; - const entries = fs25.readdirSync(dirPath, { withFileTypes: true }); + const entries = fs26.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path22.join(dirPath, entry.name); const relativePath2 = path22.join(baseRelativePath, entry.name); @@ -185626,10 +185588,10 @@ async function scanArtifactsForTokens(filesToScan, logger2) { scannedFiles: 0, findings: [] }; - const tempScanDir = fs25.mkdtempSync(path22.join(os6.tmpdir(), "artifact-scan-")); + const tempScanDir = fs26.mkdtempSync(path22.join(os6.tmpdir(), "artifact-scan-")); try { for (const filePath of filesToScan) { - const stats = fs25.statSync(filePath); + const stats = fs26.statSync(filePath); const fileName = path22.basename(filePath); if (stats.isDirectory()) { const dirResult = await scanDirectory(filePath, fileName, logger2); @@ -185667,7 +185629,7 @@ async function scanArtifactsForTokens(filesToScan, logger2) { } } finally { try { - fs25.rmSync(tempScanDir, { recursive: true, force: true }); + fs26.rmSync(tempScanDir, { recursive: true, force: true }); } catch (e) { logger2.debug( `Could not clean up temporary scan directory: ${getErrorMessage(e)}` @@ -185689,10 +185651,10 @@ async function uploadCombinedSarifArtifacts(logger2, gitHubVariant) { ); const baseTempDir = path23.resolve(tempDir, "combined-sarif"); const toUpload = []; - if (fs26.existsSync(baseTempDir)) { - const outputDirs = fs26.readdirSync(baseTempDir); + if (fs27.existsSync(baseTempDir)) { + const outputDirs = fs27.readdirSync(baseTempDir); for (const outputDir of outputDirs) { - const sarifFiles = fs26.readdirSync(path23.resolve(baseTempDir, outputDir)).filter((f) => path23.extname(f) === ".sarif"); + const sarifFiles = fs27.readdirSync(path23.resolve(baseTempDir, outputDir)).filter((f) => path23.extname(f) === ".sarif"); for (const sarifFile of sarifFiles) { toUpload.push(path23.resolve(baseTempDir, outputDir, sarifFile)); } @@ -185719,17 +185681,17 @@ async function uploadCombinedSarifArtifacts(logger2, gitHubVariant) { function tryPrepareSarifDebugArtifact(config, language, logger2) { try { const analyzeActionOutputDir = process.env["CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR" /* SARIF_RESULTS_OUTPUT_DIR */]; - if (analyzeActionOutputDir !== void 0 && fs26.existsSync(analyzeActionOutputDir) && fs26.lstatSync(analyzeActionOutputDir).isDirectory()) { + if (analyzeActionOutputDir !== void 0 && fs27.existsSync(analyzeActionOutputDir) && fs27.lstatSync(analyzeActionOutputDir).isDirectory()) { const sarifFile = path23.resolve( analyzeActionOutputDir, `${language}.sarif` ); - if (fs26.existsSync(sarifFile)) { + if (fs27.existsSync(sarifFile)) { const sarifInDbLocation = path23.resolve( config.dbLocation, `${language}.sarif` ); - fs26.copyFileSync(sarifFile, sarifInDbLocation); + fs27.copyFileSync(sarifFile, sarifInDbLocation); return sarifInDbLocation; } } @@ -185897,10 +185859,10 @@ async function createPartialDatabaseBundle(config, language) { core15.info( `${config.debugDatabaseName}-${language} is not finalized. Uploading partial database bundle at ${databaseBundlePath}...` ); - if (fs26.existsSync(databaseBundlePath)) { - await fs26.promises.rm(databaseBundlePath, { force: true }); + if (fs27.existsSync(databaseBundlePath)) { + await fs27.promises.rm(databaseBundlePath, { force: true }); } - const output = fs26.createWriteStream(databaseBundlePath); + const output = fs27.createWriteStream(databaseBundlePath); const zip = new ZipArchive(); zip.on("error", (err) => { throw err; @@ -185949,9 +185911,9 @@ async function run2(action) { getCsharpTempDependencyDir() ]; for (const tempDependencyDir of tempDependencyDirs) { - if (fs27.existsSync(tempDependencyDir)) { + if (fs28.existsSync(tempDependencyDir)) { try { - fs27.rmSync(tempDependencyDir, { recursive: true }); + fs28.rmSync(tempDependencyDir, { recursive: true }); } catch (error3) { logger2.info( `Failed to remove temporary dependencies directory: ${getErrorMessage(error3)}` @@ -186069,7 +186031,7 @@ async function runWrapper3() { } // src/init-action.ts -var fs29 = __toESM(require("fs")); +var fs30 = __toESM(require("fs")); var path25 = __toESM(require("path")); var core18 = __toESM(require_core()); var io7 = __toESM(require_io()); @@ -186128,7 +186090,7 @@ function createInitToolsDownloadFields(report, toolsFeatureFlagsValid) { } // src/workflow.ts -var fs28 = __toESM(require("fs")); +var fs29 = __toESM(require("fs")); var path24 = __toESM(require("path")); var import_zlib3 = __toESM(require("zlib")); var core17 = __toESM(require_core()); @@ -186280,7 +186242,7 @@ async function getWorkflow(logger2) { ); } const workflowPath = await getWorkflowAbsolutePath(logger2); - return load(fs28.readFileSync(workflowPath, "utf-8")); + return load(fs29.readFileSync(workflowPath, "utf-8")); } async function getWorkflowAbsolutePath(logger2) { const relativePath2 = await getWorkflowRelativePath(); @@ -186288,7 +186250,7 @@ async function getWorkflowAbsolutePath(logger2) { getRequiredEnvParam("GITHUB_WORKSPACE"), relativePath2 ); - if (fs28.existsSync(absolutePath)) { + if (fs29.existsSync(absolutePath)) { logger2.debug( `Derived the following absolute path for the currently executing workflow: ${absolutePath}.` ); @@ -186684,6 +186646,7 @@ async function run4(actionState) { if (statusReportBase !== void 0) { await sendStatusReport(statusReportBase); } + logTemporaryDiagnostics(actionState); return; } let overlayBaseDatabaseStats; @@ -186731,16 +186694,16 @@ async function run4(actionState) { "codeql-action-go-tracing", "bin" ); - fs29.mkdirSync(tempBinPath, { recursive: true }); + fs30.mkdirSync(tempBinPath, { recursive: true }); core18.addPath(tempBinPath); const goWrapperPath = path25.resolve(tempBinPath, "go"); - fs29.writeFileSync( + fs30.writeFileSync( goWrapperPath, `#!/bin/bash exec ${goBinaryPath} "$@"` ); - fs29.chmodSync(goWrapperPath, "755"); + fs30.chmodSync(goWrapperPath, "755"); actionState.env.export("CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */, goWrapperPath); } catch (e) { logger2.warning( @@ -186869,7 +186832,8 @@ exec ${goBinaryPath} "$@"` `${existingJavaToolOptions} -Djavax.net.debug=all` ); } - flushDiagnostics(config); + flushDiagnostics(actionState, config); + config.successfullyInitialised = true; await saveConfig(config, logger2); core18.setOutput("codeql-path", config.codeQLCmd); core18.setOutput("codeql-version", (await codeql.getVersion()).version); @@ -186893,7 +186857,7 @@ exec ${goBinaryPath} "$@"` ); return; } finally { - logUnwrittenDiagnostics(); + logTemporaryDiagnostics(actionState); } await sendCompletedStatusReport2( startedAt, @@ -186919,7 +186883,7 @@ async function runWrapper4() { } // src/init-action-post-helper.ts -var fs30 = __toESM(require("fs")); +var fs31 = __toESM(require("fs")); var import_path8 = __toESM(require("path")); var github4 = __toESM(require_github()); function createFailedUploadFailedSarifResult(error3) { @@ -186993,8 +186957,18 @@ async function generateFailedSarif(logger2, features, config, category, checkout if (sarifFile === void 0) { sarifFile = "../codeql-failed-run.sarif"; } - if (databasePath === void 0 || !await features.getValue("export_diagnostics_enabled" /* ExportDiagnosticsEnabled */, codeql)) { - await codeql.diagnosticsExport(sarifFile, category, config); + const exportDiagnosticsEnabled = await features.getValue( + "export_diagnostics_enabled" /* ExportDiagnosticsEnabled */, + codeql + ); + if (databasePath === void 0 || !exportDiagnosticsEnabled) { + let diagnosticDir = getTempDiagnosticsRoot( + config.tempDir + ); + if (!exportDiagnosticsEnabled || !fs31.existsSync(diagnosticDir)) { + diagnosticDir = void 0; + } + await codeql.diagnosticsExport(sarifFile, category, config, diagnosticDir); } else { await codeql.databaseExportDiagnostics(databasePath, sarifFile, category); } @@ -187118,7 +187092,7 @@ async function uploadFailureInfo(uploadAllAvailableDebugArtifacts, printDebugLog } if (isSelfHostedRunner() && config.dbLocation) { try { - fs30.rmSync(config.dbLocation, { + fs31.rmSync(config.dbLocation, { recursive: true, force: true, maxRetries: 3 @@ -188045,7 +188019,7 @@ function generateCertificateAuthority() { } // src/start-proxy/environment.ts -var fs31 = __toESM(require("fs")); +var fs32 = __toESM(require("fs")); var path28 = __toESM(require("path")); var toolrunner5 = __toESM(require_toolrunner()); var io8 = __toESM(require_io()); @@ -188118,9 +188092,9 @@ function checkJdkSettings(logger2, jdkHome) { for (const fileToCheck of filesToCheck) { const file = path28.join(jdkHome, fileToCheck); try { - if (fs31.existsSync(file)) { + if (fs32.existsSync(file)) { logger2.debug(`Found '${file}'.`); - const lines = String(fs31.readFileSync(file)).split("\n"); + const lines = String(fs32.readFileSync(file)).split("\n"); for (const line of lines) { for (const property of javaProperties) { if (line.startsWith(`${property}=`)) { diff --git a/package-lock.json b/package-lock.json index d702b0f6f..c06678b60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codeql", - "version": "4.39.1", + "version": "4.39.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codeql", - "version": "4.39.1", + "version": "4.39.2", "license": "MIT", "workspaces": [ "pr-checks" @@ -36,7 +36,7 @@ "node-forge": "^1.4.0", "semver": "^7.8.5", "undici": "^6.28.0", - "uuid": "^14.0.2" + "uuid": "^14.0.3" }, "devDependencies": { "@ava/typescript": "6.0.0", @@ -57,14 +57,14 @@ "eslint-import-resolver-typescript": "^4.4.5", "eslint-plugin-github": "^6.1.2", "eslint-plugin-import-x": "^4.17.1", - "eslint-plugin-jsdoc": "^65.2.0", + "eslint-plugin-jsdoc": "^65.3.4", "eslint-plugin-no-async-foreach": "^0.1.1", "glob": "^13.0.6", "globals": "^17.13.0", "nock": "^14.0.17", "sinon": "^22.1.0", "typescript": "^6.0.3", - "typescript-eslint": "^8.71.1" + "typescript-eslint": "^8.71.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2702,17 +2702,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.1.tgz", - "integrity": "sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.2.tgz", + "integrity": "sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.71.1", - "@typescript-eslint/type-utils": "8.71.1", - "@typescript-eslint/utils": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1", + "@typescript-eslint/scope-manager": "8.71.2", + "@typescript-eslint/type-utils": "8.71.2", + "@typescript-eslint/utils": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2725,7 +2725,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.71.1", + "@typescript-eslint/parser": "^8.71.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -2741,16 +2741,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.1.tgz", - "integrity": "sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.2.tgz", + "integrity": "sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.71.1", - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1", + "@typescript-eslint/scope-manager": "8.71.2", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2", "debug": "^4.4.3" }, "engines": { @@ -2784,14 +2784,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.1.tgz", - "integrity": "sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.2.tgz", + "integrity": "sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.71.1", - "@typescript-eslint/types": "^8.71.1", + "@typescript-eslint/tsconfig-utils": "^8.71.2", + "@typescript-eslint/types": "^8.71.2", "debug": "^4.4.3" }, "engines": { @@ -2824,14 +2824,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.1.tgz", - "integrity": "sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.2.tgz", + "integrity": "sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1" + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2842,9 +2842,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.1.tgz", - "integrity": "sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.2.tgz", + "integrity": "sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==", "dev": true, "license": "MIT", "engines": { @@ -2859,15 +2859,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.1.tgz", - "integrity": "sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.2.tgz", + "integrity": "sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1", - "@typescript-eslint/utils": "8.71.1", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2", + "@typescript-eslint/utils": "8.71.2", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2902,9 +2902,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.1.tgz", - "integrity": "sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.2.tgz", + "integrity": "sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==", "dev": true, "license": "MIT", "engines": { @@ -2916,16 +2916,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.1.tgz", - "integrity": "sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.2.tgz", + "integrity": "sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.71.1", - "@typescript-eslint/tsconfig-utils": "8.71.1", - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1", + "@typescript-eslint/project-service": "8.71.2", + "@typescript-eslint/tsconfig-utils": "8.71.2", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/visitor-keys": "8.71.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3001,16 +3001,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.1.tgz", - "integrity": "sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.2.tgz", + "integrity": "sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.71.1", - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1" + "@typescript-eslint/scope-manager": "8.71.2", + "@typescript-eslint/types": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3025,13 +3025,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.1.tgz", - "integrity": "sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.2.tgz", + "integrity": "sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/types": "8.71.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5365,9 +5365,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "65.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-65.2.0.tgz", - "integrity": "sha512-IMZaNwdDa5otUQYGWN1pzywZLsGKswitvFFNhcO8IOsBfaWpWIDXYuZh2sYhe9MNyKN9hz0YryOFaKdpbTP5Bw==", + "version": "65.3.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-65.3.4.tgz", + "integrity": "sha512-Z3/ro8sZPqnXKhyAzCNa3F7KCZ3js5xVx/q9BEvTjUGzN6OIQfdb+q3Sc9lJ3zCfyPnN0tI524ulZjYRxAwnAw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9981,16 +9981,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.1.tgz", - "integrity": "sha512-oiNVPC3/NbV1FpaHU07l8O6ynqG9v4PAiW2WvTrAmZdR0f2HI/gE668ElMLCK7Xbsu5k2fK5C4fXNiq05j7/nQ==", + "version": "8.71.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.2.tgz", + "integrity": "sha512-Rz4effbMy2g9wnvOJZG6SbVavX313Lap1vNEbk1RUochzickvzP8P4HB+bkNg5e50eVz1hQGjh9K1omI1xB4Bw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.71.1", - "@typescript-eslint/parser": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1", - "@typescript-eslint/utils": "8.71.1" + "@typescript-eslint/eslint-plugin": "8.71.2", + "@typescript-eslint/parser": "8.71.2", + "@typescript-eslint/typescript-estree": "8.71.2", + "@typescript-eslint/utils": "8.71.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10167,9 +10167,9 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz", - "integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", + "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -10484,6 +10484,7 @@ "dependencies": { "@actions/core": "^2.0.3", "@actions/github": "^8.0.1", + "@actions/tool-cache": "^3.0.1", "@octokit/core": "^7.0.8", "@octokit/plugin-paginate-rest": ">=16.0.0", "@octokit/plugin-rest-endpoint-methods": "^18.0.0", diff --git a/package.json b/package.json index 209ded679..76ac14cf2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql", - "version": "4.39.1", + "version": "4.39.2", "private": true, "description": "CodeQL action", "scripts": { @@ -44,7 +44,7 @@ "node-forge": "^1.4.0", "semver": "^7.8.5", "undici": "^6.28.0", - "uuid": "^14.0.2" + "uuid": "^14.0.3" }, "devDependencies": { "@ava/typescript": "6.0.0", @@ -65,14 +65,14 @@ "eslint-import-resolver-typescript": "^4.4.5", "eslint-plugin-github": "^6.1.2", "eslint-plugin-import-x": "^4.17.1", - "eslint-plugin-jsdoc": "^65.2.0", + "eslint-plugin-jsdoc": "^65.3.4", "eslint-plugin-no-async-foreach": "^0.1.1", "glob": "^13.0.6", "globals": "^17.13.0", "nock": "^14.0.17", "sinon": "^22.1.0", "typescript": "^6.0.3", - "typescript-eslint": "^8.71.1" + "typescript-eslint": "^8.71.2" }, "overrides": { "@actions/tool-cache": { diff --git a/pr-checks/check-diagnostics.ts b/pr-checks/check-diagnostics.ts index 0a35ac28b..6e9d0ba82 100644 --- a/pr-checks/check-diagnostics.ts +++ b/pr-checks/check-diagnostics.ts @@ -4,16 +4,32 @@ import * as core from "@actions/core"; import * as sarif from "../src/sarif"; -function checkStatusPageNotification(n) { - const expectedMessage = "Plaintext message"; - if (n.message.text !== expectedMessage) { - core.setFailed( - `Expected the status page diagnostic to have the message '${expectedMessage}', but found '${n.message.text}'.`, - ); +const DIAGNOSTIC_ID = + process.env["DIAGNOSTIC_ID"] ?? "lang/diagnostics/example"; +const DIAGNOSTIC_NAME = process.env["DIAGNOSTIC_NAME"] ?? "Diagnostic name"; +const EXPECTED_MESSAGE = process.env["EXPECTED_MESSAGE"] ?? "Plaintext message"; +const EXPECTED_LOCATIONS = Number.parseInt( + process.env["EXPECTED_LOCATIONS"] ?? "1", +); + +function checkStatusPageNotification(n: sarif.Notification) { + const expectedMessage = EXPECTED_MESSAGE.trim(); + if (!n.message.text?.includes(expectedMessage)) { + if (n.message.text !== undefined) { + core.setFailed( + `Expected the status page diagnostic to contain the message '${expectedMessage}', but found '${n.message.text.replaceAll("\n", "\\n")}'.`, + ); + } else { + core.setFailed( + `Expected the status page diagnostic to contain the message '${expectedMessage}', but found nothing.`, + ); + } } - if (n.locations.length !== 1) { + + const locationCount = n.locations?.length ?? 0; + if (locationCount !== EXPECTED_LOCATIONS) { core.setFailed( - `Expected the status page diagnostic to have exactly 1 location, but found ${n.locations.length}.`, + `Expected the status page diagnostic to have exactly ${EXPECTED_LOCATIONS} location(s), but found ${locationCount}.`, ); } } @@ -40,8 +56,7 @@ if (toolExecutionNotifications === undefined) { const statusPageNotifications = toolExecutionNotifications.filter( (n) => - n.descriptor?.id === "lang/diagnostics/example" && - n.properties?.visibility?.statusPage, + n.descriptor?.id === DIAGNOSTIC_ID && n.properties?.visibility?.statusPage, ); if (statusPageNotifications.length !== 1) { core.setFailed( @@ -61,9 +76,9 @@ if (notifications === undefined) { const diagnosticNotification = notifications.filter( (n) => - n.id === "lang/diagnostics/example" && - n.name === "lang/diagnostics/example" && - n.fullDescription?.text === "Diagnostic name", + n.id === DIAGNOSTIC_ID && + n.name === DIAGNOSTIC_ID && + n.fullDescription?.text === DIAGNOSTIC_NAME, ); if (diagnosticNotification.length !== 1) { core.setFailed( diff --git a/pr-checks/checks/bundle-from-toolcache.yml b/pr-checks/checks/bundle-from-toolcache.yml index 0e51e2e12..947b27390 100644 --- a/pr-checks/checks/bundle-from-toolcache.yml +++ b/pr-checks/checks/bundle-from-toolcache.yml @@ -2,30 +2,20 @@ name: "Bundle: From toolcache" description: "The CodeQL bundle should be cached within the toolcache" versions: - toolcache +installNode: true steps: - - name: Install @actions/tool-cache - run: npm install @actions/tool-cache@3 - name: Check toolcache contains CodeQL continue-on-error: true - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - if (allCodeqlVersions.length === 0) { - throw new Error(`CodeQL could not be found in the toolcache`); - } + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts installed + - id: setup-codeql uses: ./../action/setup-codeql with: tools: ${{ steps.prepare-test.outputs.tools-url }} + - name: Check CodeQL is installed within the toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - console.log(`Found CodeQL versions: ${allCodeqlVersions}`); - if (allCodeqlVersions.length === 0) { - throw new Error('CodeQL not found in toolcache'); - } + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts installed diff --git a/pr-checks/checks/bundle-toolcache.yml b/pr-checks/checks/bundle-toolcache.yml index f74c6af75..3317a45f4 100644 --- a/pr-checks/checks/bundle-toolcache.yml +++ b/pr-checks/checks/bundle-toolcache.yml @@ -6,47 +6,31 @@ operatingSystems: - ubuntu - macos - windows +installNode: true steps: - name: Remove CodeQL from toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const fs = require('fs'); - const path = require('path'); - const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL'); - fs.rmdirSync(codeqlPath, { recursive: true }); - - name: Install @actions/tool-cache - run: npm install @actions/tool-cache@3 + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts clear + - name: Check toolcache does not contain CodeQL - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - if (allCodeqlVersions.length !== 0) { - throw new Error(`CodeQL should not be found in the toolcache, but found ${allCodeqlVersions}`); - } - console.log('No versions of CodeQL found in the toolcache'); + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts absent + - id: init uses: ./../action/init with: # Request multiple languages so this check uses the combined bundle. languages: javascript,python tools: ${{ steps.prepare-test.outputs.tools-url }} + - uses: ./../action/analyze with: output: ${{ runner.temp }}/results upload-database: false + - name: Check CodeQL is installed within the toolcache - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const toolcache = require('@actions/tool-cache'); - const allCodeqlVersions = toolcache.findAllVersions('CodeQL'); - console.log(`Found CodeQL versions: ${allCodeqlVersions}`); - if (allCodeqlVersions.length === 0) { - throw new Error('CodeQL not found in toolcache'); - } - if (allCodeqlVersions.length > 1) { - throw new Error('Multiple CodeQL versions found in toolcache'); - } + run: | + cd ./../action + npx tsx ./pr-checks/toolcache.ts installed -n 1 diff --git a/pr-checks/checks/diagnostics-without-db.yml b/pr-checks/checks/diagnostics-without-db.yml new file mode 100644 index 000000000..0d43f1c62 --- /dev/null +++ b/pr-checks/checks/diagnostics-without-db.yml @@ -0,0 +1,72 @@ +name: "Diagnostic export without database" +description: "Tests that temporary diagnostics are correctly included in the failed SARIF." +versions: + # Overridden by the FF in the `init` step below. + - linked +installNode: true + +env: + # Allow the failed SARIF file to be generated. + CODEQL_ACTION_SKIP_GENERATE_FAILED_SARIF: false + # But don't actually upload it. + CODEQL_ACTION_SKIP_SARIF_UPLOAD: true + # Mark telemetry for this workflow so it can be treated separately. + CODEQL_ACTION_TESTING_ENVIRONMENT: codeql-action-pr-checks + +steps: + # We deliberately need to make this step fail before the database is initialised, but after + # some diagnostic has been produced. The simplest way to do that right now is to: + # + # - Produce a diagnostic before the db is initialised by forcing the use of a nightly release + # with the `CODEQL_ACTION_FORCE_NIGHTLY` feature flag. + # - Cause `init` to abort because we are attempting to analyse Swift on a non-macOS runner. + # + - name: Initialise failing analysis + id: init + uses: ./../action/init + continue-on-error: true + env: + CODEQL_ACTION_FORCE_NIGHTLY: true + with: + languages: swift + tools: ${{ steps.prepare-test.outputs.tools-url }} + + - name: Analyze + uses: ./analyze + # In a real workflow, this step wouldn't run. Since we used `continue-on-error` + # above, we manually disable it with an `if` condition. Having this step is required + # for the init post-action because it checks the workflow definition for e.g. the + # `category` input. Without this step, that part of the init post-action would fail. + if: false + with: + category: "/test-codeql-version:${{ matrix.version }}" + + - name: Check diagnostics appear on disk + env: + DIAGNOSTICS_DIR: "${{ runner.temp }}/codeql-action-temp-diagnostics/" + run: | + cd ../action + npx tsx ./pr-checks/diagnostics-without-db.ts +validationJobs: + diagnostics-without-db-validation: + checkout: true + installNode: true + steps: + - name: Download workflow artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: test-mode-sarif-* + path: sarif + + - name: List artifact contents + run: ls -laR ./sarif + + - name: Check diagnostics appear in SARIF + env: + SARIF_PATH: "./sarif/codeql-failed-run.sarif" + DIAGNOSTIC_ID: "codeql-action/forced-nightly-cli" + DIAGNOSTIC_NAME: "A nightly release of CodeQL was used" + EXPECTED_LOCATIONS: "0" + EXPECTED_MESSAGE: | + GitHub configured this analysis to use a nightly release of CodeQL to allow you to preview changes from an upcoming release. + run: npx tsx ./pr-checks/check-diagnostics.ts diff --git a/pr-checks/checks/rubocop-multi-language.yml b/pr-checks/checks/rubocop-multi-language.yml index 633c2a379..eec3aa833 100644 --- a/pr-checks/checks/rubocop-multi-language.yml +++ b/pr-checks/checks/rubocop-multi-language.yml @@ -5,7 +5,7 @@ versions: - default steps: - name: Set up Ruby - uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 + uses: ruby/setup-ruby@651ff0d62c5c79bed6d53ec518387aae20e72c1a # v1.329.0 with: ruby-version: 2.6 - name: Install Code Scanning integration diff --git a/pr-checks/checks/submit-sarif-failure.yml b/pr-checks/checks/submit-sarif-failure.yml index c33e1322f..ea156ca93 100644 --- a/pr-checks/checks/submit-sarif-failure.yml +++ b/pr-checks/checks/submit-sarif-failure.yml @@ -35,7 +35,9 @@ steps: run: exit 1 - uses: ./analyze # In a real workflow, this step wouldn't run. Since we used `continue-on-error` - # above, we manually disable it with an `if` condition. + # above, we manually disable it with an `if` condition. Having this step is required + # for the init post-action because it checks the workflow definition for e.g. the + # `category` input. Without this step, that part of the init post-action would fail. if: false with: category: "/test-codeql-version:${{ matrix.version }}" diff --git a/pr-checks/diagnostics-without-db.ts b/pr-checks/diagnostics-without-db.ts new file mode 100644 index 000000000..b276260f9 --- /dev/null +++ b/pr-checks/diagnostics-without-db.ts @@ -0,0 +1,48 @@ +import * as fs from "fs"; +import * as path from "path"; + +import * as core from "@actions/core"; + +import type * as diagnostics from "../src/diagnostics"; + +const DIAGNOSTIC_ID = "codeql-action/forced-nightly-cli"; +const diagnosticDirectory = process.env["DIAGNOSTICS_DIR"]; + +if (diagnosticDirectory === undefined) { + throw new Error("DIAGNOSTICS_DIR is undefined."); +} + +const paths = fs + .readdirSync(diagnosticDirectory, { + recursive: true, + encoding: "utf-8", + }) + .filter((p) => p.endsWith(".json")); + +core.info(`Found ${paths.length} paths in '${diagnosticDirectory}'.`); +core.info(`Searching for a diagnostic with id '${DIAGNOSTIC_ID}'...`); + +let found = false; +for (const diagnosticPath of paths) { + if (path.extname(diagnosticPath) !== ".json") { + core.info(`Skipping ${diagnosticPath}.`); + continue; + } + core.info(`Found ${diagnosticPath}.`); + + const contents = JSON.parse( + fs.readFileSync(path.resolve(diagnosticDirectory, diagnosticPath), "utf-8"), + ) as diagnostics.DiagnosticMessage; + + if (contents.source.id === DIAGNOSTIC_ID) { + core.info( + `Found '${DIAGNOSTIC_ID}' in '${diagnosticPath}'. Skipping remaining files.`, + ); + found = true; + break; + } +} + +if (!found) { + throw new Error(`Didn't find '${DIAGNOSTIC_ID}' diagnostic.`); +} diff --git a/pr-checks/package.json b/pr-checks/package.json index 3f20d119d..343f94381 100644 --- a/pr-checks/package.json +++ b/pr-checks/package.json @@ -5,6 +5,7 @@ "dependencies": { "@actions/core": "^2.0.3", "@actions/github": "^8.0.1", + "@actions/tool-cache": "^3.0.1", "@octokit/core": "^7.0.8", "@octokit/plugin-paginate-rest": ">=16.0.0", "@octokit/plugin-rest-endpoint-methods": "^18.0.0", diff --git a/pr-checks/sync.ts b/pr-checks/sync.ts index d6382786c..1729f5409 100755 --- a/pr-checks/sync.ts +++ b/pr-checks/sync.ts @@ -90,7 +90,7 @@ interface Specification extends JobSpecification { services?: any; /** Additional jobs to run after the main PR check job. */ - validationJobs?: Record; + validationJobs?: Record; /** If set, this check is part of a named collection that gets its own caller workflow. */ collection?: string; @@ -122,6 +122,10 @@ interface JobSpecification { installYq?: boolean; } +type ValidationJobSpecification = JobSpecification & { + checkout?: boolean; +}; + /** Describes language/framework-specific steps and inputs. */ interface LanguageSetup { specProperty: keyof JobSpecification; @@ -263,8 +267,8 @@ const languageSetups: LanguageSetups = { name: "Install Java", uses: pinnedUses( "actions/setup-java", - "de7274f081f381c8f8158605e0321c36c376e2e6", - "v6.0.1", + "b843ebf9b913146b38da2eaa7aeddf76074ae1ad", + "v6.1.0", ), with: { "java-version": `\${{ inputs.java-version || '${defaultLanguageVersions.java}' }}`, @@ -462,6 +466,20 @@ function generateJobMatrix( return matrix; } +/** + * Gets the `actions/checkout` step. + */ +function getCheckoutStep(): Step { + return { + name: "Check out repository", + uses: pinnedUses( + "actions/checkout", + "3d3c42e5aac5ba805825da76410c181273ba90b1", + "v7.0.1", + ), + }; +} + /** * Retrieves setup steps and additional input definitions based on specific languages or frameworks * that are requested by the `checkSpecification`. @@ -531,14 +549,7 @@ function generateJob( // Construct the workflow steps needed for this check. const steps: Step[] = [ - { - name: "Check out repository", - uses: pinnedUses( - "actions/checkout", - "3d3c42e5aac5ba805825da76410c181273ba90b1", - "v7.0.1", - ), - }, + getCheckoutStep(), ...setupInfo.steps, { name: "Prepare test", @@ -609,13 +620,16 @@ function generateJob( /** Generates a validation job. */ function generateValidationJob( specDocument: yaml.Document, - jobSpecification: JobSpecification, + jobSpecification: ValidationJobSpecification, checkName: string, name: string, ) { // Determine which languages or frameworks have to be installed. const { inputs, steps } = getSetupSteps(jobSpecification); + // Construct a checkout step if requested. + const checkoutStep = jobSpecification.checkout ? [getCheckoutStep()] : []; + // Extract the sequence of steps from the YAML document to persist as much formatting as possible. const specSteps = specDocument.getIn([ "validationJobs", @@ -624,7 +638,7 @@ function generateValidationJob( ]) as yaml.YAMLSeq; // Add the generated steps in front of the ones from the specification. - specSteps.items.unshift(...steps); + specSteps.items.unshift(...checkoutStep, ...steps); const validationJob: Record = { name: jobSpecification.name, @@ -635,7 +649,7 @@ function generateValidationJob( "security-events": "read", }, "timeout-minutes": 5, - "runs-on": "ubuntu-slim", + "runs-on": "ubuntu-latest", steps: specSteps, }; diff --git a/pr-checks/toolcache.ts b/pr-checks/toolcache.ts new file mode 100755 index 000000000..aa7d9afc1 --- /dev/null +++ b/pr-checks/toolcache.ts @@ -0,0 +1,94 @@ +#!/usr/bin/env npx tsx + +/** + * Provides utility commands for working with the Actions toolcache in PR checks. + */ + +import fs from "fs"; +import { parseArgs } from "node:util"; +import path from "path"; + +import * as core from "@actions/core"; +import * as toolcache from "@actions/tool-cache"; + +function removeCodeQLFromToolcache() { + const toolcachePath = process.env["RUNNER_TOOL_CACHE"]; + + if (toolcachePath === undefined) { + throw new Error("Expected RUNNER_TOOL_CACHE to be defined"); + } + + const codeqlPath = path.join(toolcachePath, "CodeQL"); + fs.rmSync(codeqlPath, { recursive: true }); + + core.info(`Removed '${codeqlPath}' from the toolcache.`); +} + +function findCodeQL() { + return toolcache.findAllVersions("CodeQL"); +} + +function checkNoCodeQLInToolcache() { + const allCodeqlVersions = findCodeQL(); + if (allCodeqlVersions.length !== 0) { + throw new Error( + `CodeQL should not be found in the toolcache, but found ${allCodeqlVersions}`, + ); + } + core.info("No versions of CodeQL found in the toolcache"); +} + +function hasCodeQLInToolcache(expectedNumber?: string) { + const expected = + expectedNumber !== undefined + ? Number.parseInt(expectedNumber, 10) + : undefined; + + const allCodeqlVersions = findCodeQL(); + if (allCodeqlVersions.length === 0) { + throw new Error("CodeQL not found in toolcache"); + } + + core.info(`Found CodeQL versions: ${allCodeqlVersions.join(", ")}`); + if (expected !== undefined && allCodeqlVersions.length !== expected) { + throw new Error( + `Expected ${expected} versions of CodeQL in the toolcache, but found ${allCodeqlVersions.length}`, + ); + } +} + +function main() { + const { positionals, values } = parseArgs({ + options: { + // The number of expected versions in the toolcache. + "expected-version-count": { type: "string", short: "n" }, + }, + allowPositionals: true, + }); + + if (positionals.length < 1) { + throw new Error("Command not specified."); + } + + const cmd = positionals[0]; + + switch (cmd.trim().toLowerCase()) { + case "clear": + removeCodeQLFromToolcache(); + return; + case "absent": + checkNoCodeQLInToolcache(); + return; + case "installed": + hasCodeQLInToolcache(values["expected-version-count"]); + return; + default: + throw new Error(`Unknown command '${cmd}'.`); + } +} + +try { + main(); +} catch (err) { + core.setFailed(err instanceof Error ? err.message : String(err)); +} diff --git a/src/actions-util.test.ts b/src/actions-util.test.ts index 17cf96a4a..175fdea96 100644 --- a/src/actions-util.test.ts +++ b/src/actions-util.test.ts @@ -11,7 +11,6 @@ import { } from "./actions-util"; import { computeAutomationID } from "./api-client"; import { EnvVar } from "./environment"; -import { getRunnerLogger } from "./logging"; import { RecordingLogger, setupTests } from "./testing-utils"; import { initializeEnvironment, withTmpDir } from "./util"; @@ -238,7 +237,7 @@ test.serial("fixCodeQualityCategory", (t) => { GITHUB_EVENT_NAME: "dynamic", }, () => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); // Categories that should get adjusted. t.is(fixCodeQualityCategory(logger, "/language:c#"), "/language:csharp"); diff --git a/src/analyses.test.ts b/src/analyses.test.ts index 9d0a9eb3e..b9d182618 100644 --- a/src/analyses.test.ts +++ b/src/analyses.test.ts @@ -15,7 +15,6 @@ import { supportedAnalysisKinds, } from "./analyses"; import { EnvVar } from "./environment"; -import { getRunnerLogger } from "./logging"; import { createFeatures, RecordingLogger, @@ -65,7 +64,7 @@ test.serial( .withArgs("analysis-kinds") .returns("code-scanning,code-quality"); const result = await getAnalysisKinds( - getRunnerLogger(true), + new RecordingLogger(), features, true, ); @@ -139,7 +138,7 @@ test.serial( const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput"); optionalInputStub.withArgs("quality-queries").returns("code-quality"); const result = await getAnalysisKinds( - getRunnerLogger(true), + new RecordingLogger(), features, true, ); @@ -155,7 +154,7 @@ test.serial( const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput"); requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing"); await t.throwsAsync( - getAnalysisKinds(getRunnerLogger(true), features, true), + getAnalysisKinds(new RecordingLogger(), features, true), ); }, ); @@ -181,7 +180,7 @@ for (let i = 0; i < analysisKinds.length; i++) { .withArgs("analysis-kinds") .returns([analysisKind, otherAnalysis].join(",")); const result = await getAnalysisKinds( - getRunnerLogger(true), + new RecordingLogger(), features, true, ); @@ -199,7 +198,7 @@ for (let i = 0; i < analysisKinds.length; i++) { .withArgs("analysis-kinds") .returns([analysisKind, otherAnalysis].join(",")); await t.throwsAsync( - getAnalysisKinds(getRunnerLogger(true), features, true), + getAnalysisKinds(new RecordingLogger(), features, true), { instanceOf: ConfigurationError, message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`, diff --git a/src/analyze-action.ts b/src/analyze-action.ts index 0bed377be..23ce54d30 100644 --- a/src/analyze-action.ts +++ b/src/analyze-action.ts @@ -50,9 +50,8 @@ import { StatusReportBase, } from "./status-report"; import { - cleanupTrapCaches, - TrapCacheCleanupStatusReport, uploadTrapCaches, + warnIfTrapCacheCleanupEnvVarIsSet, } from "./trap-caching"; import * as uploadLib from "./upload-lib"; import { UploadResult } from "./upload-lib"; @@ -86,7 +85,6 @@ async function sendStatusReport( trapCacheUploadTime: number | undefined, dbCreationTimings: DatabaseCreationTimings | undefined, didUploadTrapCaches: boolean, - trapCacheCleanup: TrapCacheCleanupStatusReport | undefined, dependencyCacheResults: DependencyCacheUploadStatusReport | undefined, databaseUploadResults: DatabaseUploadResult[], logger: Logger, @@ -107,7 +105,6 @@ async function sendStatusReport( ...statusReportBase, ...(stats || {}), ...(dbCreationTimings || {}), - ...(trapCacheCleanup || {}), dependency_caching_upload_results: dependencyCacheResults, database_upload_results: databaseUploadResults, }; @@ -229,8 +226,6 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { | undefined = undefined; let runStats: QueriesStatusReport | undefined = undefined; let config: Config | undefined = undefined; - let trapCacheCleanupTelemetry: TrapCacheCleanupStatusReport | undefined = - undefined; let trapCacheUploadTime: number | undefined = undefined; let dbCreationTimings: DatabaseCreationTimings | undefined = undefined; let didUploadTrapCaches = false; @@ -278,6 +273,8 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { ); } + warnIfTrapCacheCleanupEnvVarIsSet(action); + const apiDetails = getApiDetails(); const outputDir = actionsUtil.getRequiredInput("output"); action.env.export(EnvVar.SARIF_RESULTS_OUTPUT_DIR, outputDir); @@ -416,13 +413,6 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { didUploadTrapCaches = await uploadTrapCaches(codeql, config, logger); trapCacheUploadTime = performance.now() - trapCacheUploadStartTime; - // Clean up TRAP caches - trapCacheCleanupTelemetry = await cleanupTrapCaches( - config, - features, - logger, - ); - // Store dependency cache(s) if dependency caching is enabled. if (shouldStoreCache(config.dependencyCachingEnabled)) { dependencyCacheResults = await uploadDependencyCaches( @@ -474,7 +464,6 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger, @@ -497,7 +486,6 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger, @@ -511,7 +499,6 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger, @@ -525,7 +512,6 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) { trapCacheUploadTime, dbCreationTimings, didUploadTrapCaches, - trapCacheCleanupTelemetry, dependencyCacheResults, databaseUploadResults, logger, diff --git a/src/analyze.test.ts b/src/analyze.test.ts index e14d87d10..c9f060881 100644 --- a/src/analyze.test.ts +++ b/src/analyze.test.ts @@ -17,12 +17,12 @@ import { defaultSuites } from "./config/db-config"; import { Feature } from "./feature-flags"; import * as gitUtils from "./git-utils"; import { BuiltInLanguage } from "./languages"; -import { getRunnerLogger } from "./logging"; import { setupTests, setupActionsVars, createFeatures, createTestConfig, + RecordingLogger, callee, } from "./testing-utils"; import * as uploadLib from "./upload-lib"; @@ -195,7 +195,7 @@ test.serial("status report fields", async (t) => { undefined, codeql, config, - getRunnerLogger(true), + new RecordingLogger(), createFeatures([Feature.QaTelemetryEnabled]), ); t.deepEqual(Object.keys(statusReport).sort(), [ diff --git a/src/api-client.ts b/src/api-client.ts index 7aa4d61d7..8a456c169 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -401,21 +401,6 @@ export async function listActionsCaches( ); } -/** - * Delete an Actions cache item by its ID. - * - * See https://docs.github.com/en/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id. - */ -export async function deleteActionsCache(id: number) { - const repositoryNwo = getRepositoryNwo(); - - await getApiClient().rest.actions.deleteActionsCacheById({ - owner: repositoryNwo.owner, - repo: repositoryNwo.repo, - cache_id: id, - }); -} - /** * Retrieve all custom repository properties. * diff --git a/src/artifact-scanner.test.ts b/src/artifact-scanner.test.ts index 56f99e113..50c0d3fd2 100644 --- a/src/artifact-scanner.test.ts +++ b/src/artifact-scanner.test.ts @@ -10,12 +10,12 @@ import { scanArtifactsForTokens, TokenType, } from "./artifact-scanner"; -import { getRunnerLogger } from "./logging"; import { checkExpectedLogMessages, getRecordingLogger, LoggedMessage, makeTestToken, + RecordingLogger, } from "./testing-utils"; test("makeTestToken", (t) => { @@ -121,7 +121,7 @@ for (const { type, value, checkPattern } of testTokens) { } test("scanArtifactsForTokens handles files without tokens", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "scanner-test-")); try { diff --git a/src/cli/output-cache.test.ts b/src/cli/output-cache.test.ts index 656cfd820..ed49dab09 100644 --- a/src/cli/output-cache.test.ts +++ b/src/cli/output-cache.test.ts @@ -3,15 +3,14 @@ import path from "path"; import test from "ava"; -import { getRunnerLogger } from "../logging"; -import { setupTests } from "../testing-utils"; +import { RecordingLogger, setupTests } from "../testing-utils"; import * as util from "../util"; import { getCachedCodeQlVersion } from "./output-cache"; setupTests(test); -const logger = getRunnerLogger(true); +const logger = new RecordingLogger(); test.serial( "getCachedCodeQlVersion reuses a version persisted by an earlier step", diff --git a/src/codeql.test.ts b/src/codeql.test.ts index e70dcd99f..d702fe438 100644 --- a/src/codeql.test.ts +++ b/src/codeql.test.ts @@ -22,7 +22,6 @@ import type { Config } from "./config-utils"; import * as defaults from "./defaults.json"; import { DocUrl } from "./doc-url"; import { BuiltInLanguage } from "./languages"; -import { getRunnerLogger } from "./logging"; import { ToolsSource } from "./setup-codeql"; import { setupTests, @@ -34,6 +33,8 @@ import { makeVersionInfo, createTestConfig, makeMacro, + getCodeQLForTesting, + RecordingLogger, } from "./testing-utils"; import { ToolsDownloadStatusReport } from "./tools-download"; import * as util from "./util"; @@ -102,7 +103,7 @@ async function installIntoToolcache({ undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), - getRunnerLogger(true), + new RecordingLogger(), false, ); } @@ -140,7 +141,7 @@ function mockApiDetails(apiDetails: Required) { } async function stubCodeql(): Promise { - const codeqlObject = await codeql.getCodeQLForTesting(); + const codeqlObject = await getCodeQLForTesting(); sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6")); sinon .stub(codeqlObject, "isTracedLanguage") @@ -176,7 +177,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); @@ -212,7 +213,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); @@ -252,7 +253,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); t.assert(toolcache.find("CodeQL", "0.0.0-20200610")); @@ -362,7 +363,7 @@ for (const { undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); t.assert(toolcache.find("CodeQL", expectedToolcacheVersion)); @@ -405,7 +406,7 @@ for (const toolcacheVersion of [ undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); t.is( @@ -450,7 +451,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); t.deepEqual(result.toolsVersion, "0.0.0-20200601"); @@ -497,7 +498,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); t.deepEqual(result.toolsVersion, defaults.cliVersion); @@ -537,7 +538,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); t.deepEqual(result.toolsVersion, defaults.cliVersion); @@ -579,7 +580,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, - getRunnerLogger(true), + new RecordingLogger(), false, ); @@ -664,7 +665,7 @@ const injectedConfigMacro = makeMacro({ tempDir, }; thisStubConfig.computedConfig = generateCodeScanningConfig( - getRunnerLogger(true), + new RecordingLogger(), thisStubConfig.originalUserInput, augmentationProperties, ); @@ -1039,7 +1040,7 @@ test.serial("runTool summarizes autobuilder errors", async (t) => { [2019-09-18 12:00:01] [autobuild] A non-error message `; stubToolRunnerConstructor(1, stderr); - const codeqlObject = await codeql.getCodeQLForTesting(); + const codeqlObject = await getCodeQLForTesting(); sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6")); sinon.stub(codeqlObject, "resolveExtractor").resolves("/path/to/extractor"); // io throws because of the test CodeQL object. @@ -1095,7 +1096,7 @@ test.serial("runTool recognizes fatal internal errors", async (t) => { com.semmle.util.exception.CatastrophicError: An error occurred while evaluating ControlFlowGraph::ControlFlow::Root.isRootOf/1#dispred#f610e6ed/2@86282cc8 Severe disk cache trouble (corruption or out of space) at /home/runner/work/_temp/codeql_databases/go/db-go/default/cache/pages/28/33.pack: Failed to write item to disk`; stubToolRunnerConstructor(1, stderr); - const codeqlObject = await codeql.getCodeQLForTesting(); + const codeqlObject = await getCodeQLForTesting(); sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6")); sinon.stub(codeqlObject, "resolveExtractor").resolves("/path/to/extractor"); // io throws because of the test CodeQL object. diff --git a/src/codeql.ts b/src/codeql.ts index a76e29727..28ba2243e 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -26,7 +26,7 @@ import { } from "./feature-flags"; import { isAnalyzingDefaultBranch } from "./git-utils"; import { Language } from "./languages"; -import { getRunnerLogger, Logger } from "./logging"; +import type { Logger } from "./logging"; import { writeBaseDatabaseOidsFile, writeOverlayChangesFile } from "./overlay"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; import * as setupCodeql from "./setup-codeql"; @@ -199,6 +199,7 @@ export interface CodeQL { sarifFile: string, automationDetailsId: string | undefined, config: ActionConfig<"tempDir">, + diagnosticDir: string | undefined, ): Promise; /** Get the location of an extractor for the specified language. */ resolveExtractor(language: Language): Promise; @@ -486,18 +487,6 @@ export function createStubCodeQL(partialCodeql: Partial): CodeQL { }; } -/** - * Get a real, newly created CodeQL instance for testing. The instance refers to - * a non-existent placeholder codeql command, so tests that use this function - * should also stub the toolrunner.ToolRunner constructor. - */ -export async function getCodeQLForTesting( - cmd = "codeql-for-testing", - logger: Logger = getRunnerLogger(true), -): Promise { - return getCodeQLForCmd(logger, cmd, false); -} - /** * Return a CodeQL object for CodeQL CLI access. * @@ -506,7 +495,7 @@ export async function getCodeQLForTesting( * version requirement. Must be set to true outside tests. * @returns A new CodeQL object */ -async function getCodeQLForCmd( +export async function getCodeQLForCmd( logger: Logger, cmd: string, checkVersion: boolean, @@ -915,15 +904,43 @@ async function getCodeQLForCmd( sarifFile: string, automationDetailsId: string | undefined, config: ActionConfig<"tempDir">, + diagnosticDir: string | undefined, ): Promise { + // We may be exporting diagnostics for a failed SARIF, in which case the configuration + // file may not have been written to disk and there may not be a database. The CLI + // will produce a SARIF for diagnostics from `diagnosticDir` without it. Therefore, + // we check whether `configPath` exists and only provide it to the CLI if it does. + // Although not needed, the CLI will produce an error if it + // is provided with an argument to `--sarif-codescanning-config` that does not exist. + let configPath: string | undefined = + getGeneratedCodeScanningConfigPath(config); + + if (!fs.existsSync(configPath)) { + // Warn if there is no configuration file, even though `config` claims that `init` + // was successful. This shouldn't be happening. + if (config.successfullyInitialised) { + logger.warning( + [ + `Expected a configuration file at '${configPath}', which does not exist.`, + "Continuing with diagnostics export anyway, without a configuration file.", + ].join(" "), + ); + } + + configPath = undefined; + } + const args = [ "diagnostics", "export", "--format=sarif-latest", `--output=${sarifFile}`, - `--sarif-codescanning-config=${getGeneratedCodeScanningConfigPath( - config, - )}`, + ...(configPath !== undefined + ? [`--sarif-codescanning-config=${configPath}`] + : []), + ...(diagnosticDir !== undefined + ? [`--diagnostic-dir=${diagnosticDir}`] + : []), ...getExtraOptionsFromEnv(["diagnostics", "export"]), ]; if (automationDetailsId !== undefined) { diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index f31cf0206..b66d4a797 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -21,7 +21,6 @@ import { RepositoryProperties } from "./feature-flags/properties"; import * as gitUtils from "./git-utils"; import { GitVersionInfo } from "./git-utils"; import { BuiltInLanguage, Language } from "./languages"; -import { getRunnerLogger } from "./logging"; import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay"; import * as overlayDiagnostics from "./overlay/diagnostics"; import { OverlayDisabledReason } from "./overlay/diagnostics"; @@ -42,6 +41,7 @@ import { callee, SAMPLE_DOTCOM_API_DETAILS, AssertableTarget, + RecordingLogger, } from "./testing-utils"; import { GitHubVariant, @@ -102,7 +102,7 @@ function createTestInitConfigInputs( features: createFeatures([]), repositoryProperties: {}, enableFileCoverageInformation: true, - logger: getRunnerLogger(true), + logger: new RecordingLogger(), } satisfies configUtils.InitConfigInputs, overrides, ); @@ -150,7 +150,7 @@ function mockListLanguages(languages: string[]) { test.serial("load empty config", async (t) => { return await withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const languages = "javascript,python"; setupActionsVars(tempDir, tempDir); @@ -196,7 +196,7 @@ test.serial("load empty config", async (t) => { test.serial("load code quality config", async (t) => { return await withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const languages = "actions"; setupActionsVars(tempDir, tempDir); @@ -252,7 +252,7 @@ test.serial( "initActionState doesn't throw if there are queries configured in the repository properties", async (t) => { return await withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const languages = "javascript"; setupActionsVars(tempDir, tempDir); @@ -315,7 +315,7 @@ test.serial( test.serial("loading a saved config produces the same config", async (t) => { return await withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = createStubCodeQL({ async resolveLanguages() { @@ -363,7 +363,7 @@ test.serial("loading a saved config produces the same config", async (t) => { test.serial("loading config with version mismatch throws", async (t) => { return await withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = createStubCodeQL({ async resolveLanguages() { @@ -788,12 +788,12 @@ test.serial("Unknown languages", async (t) => { }); }); -const mockLogger = getRunnerLogger(true); +const mockLogger = new RecordingLogger(); test.serial("no generateRegistries when registries is undefined", async (t) => { return await withTmpDir(async (tmpDir) => { const registriesInput = undefined; - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const { registriesAuthTokens, qlconfigFile } = await configUtils.generateRegistries(registriesInput, tmpDir, logger); @@ -814,7 +814,7 @@ test.serial( token: "not-a-token", }, ]); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const { registriesAuthTokens, qlconfigFile } = await configUtils.generateRegistries(registriesInput, tmpDir, logger); @@ -2225,7 +2225,7 @@ test("applyIncrementalAnalysisSettings: no-op when mode is not Overlay and diff const config = createTestConfig({}); config.overlayDatabaseMode = OverlayDatabaseMode.None; const codeql = createStubCodeQL({}); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); await configUtils.applyIncrementalAnalysisSettings( config, @@ -2243,7 +2243,7 @@ test("applyIncrementalAnalysisSettings: keeps overlay mode and adds exclusions w overlayDatabaseMode: OverlayDatabaseMode.Overlay, }); const codeql = createStubCodeQL({}); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); await configUtils.applyIncrementalAnalysisSettings( config, @@ -2266,7 +2266,7 @@ test.serial( }); config.useOverlayDatabaseCaching = true; const codeql = createStubCodeQL({}); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const addDiagnosticsStub = sinon .stub(overlayDiagnostics, "addOverlayDisablementDiagnostics") .resolves(); @@ -2298,7 +2298,7 @@ test.serial( config.useOverlayDatabaseCaching = false; config.overlayModeSetExplicitly = true; const codeql = createStubCodeQL({}); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const addDiagnosticsStub = sinon .stub(overlayDiagnostics, "addOverlayDisablementDiagnostics") .resolves(); @@ -2321,7 +2321,7 @@ test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only r const config = createTestConfig({}); config.overlayDatabaseMode = OverlayDatabaseMode.None; const codeql = createStubCodeQL({}); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); await configUtils.applyIncrementalAnalysisSettings( config, @@ -2535,7 +2535,7 @@ test("determineUserConfig - merges configs if FF is enabled in Default Setup", a // also check whether loading the configuration from disk that was written // by `determineUserConfig` matches our expectations. const loadedFromDisk = configUtils.getLocalConfig( - getRunnerLogger(true), + new RecordingLogger(), expectedConfigPath, false, ); diff --git a/src/config-utils.ts b/src/config-utils.ts index 7bb7a37c0..12c1024f8 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -465,6 +465,8 @@ export async function initActionState( overlayModeSetExplicitly: false, repositoryProperties, enableFileCoverageInformation, + // We don't set this to `true` until `init` is successful. + successfullyInitialised: false, }; } diff --git a/src/config/action-config.ts b/src/config/action-config.ts index 4990ba675..9daf966ee 100644 --- a/src/config/action-config.ts +++ b/src/config/action-config.ts @@ -130,6 +130,9 @@ export interface Config { * Whether to enable file coverage information. */ enableFileCoverageInformation: boolean; + + /** Whether the analysis was successfully initialised using the configuration. */ + successfullyInitialised: boolean; } /** Properties available in `Config`. */ diff --git a/src/config/db-config.test.ts b/src/config/db-config.test.ts index 76fa91801..ac1df16a0 100644 --- a/src/config/db-config.test.ts +++ b/src/config/db-config.test.ts @@ -2,15 +2,15 @@ import test, { ExecutionContext } from "ava"; import { RepositoryProperties } from "../feature-flags/properties"; import { BuiltInLanguage, Language } from "../languages"; -import { getRunnerLogger } from "../logging"; import { checkExpectedLogMessages, getRecordingLogger, LoggedMessage, makeMacro, RecordingLogger, + setupActionsVars, } from "../testing-utils"; -import { ConfigurationError, prettyPrintPack } from "../util"; +import { ConfigurationError, prettyPrintPack, withTmpDir } from "../util"; import * as dbConfig from "./db-config"; @@ -384,7 +384,7 @@ calculateAugmentationErrorMacro( test("parseUserConfig - successfully parses valid YAML", (t) => { const result = dbConfig.parseUserConfig( - getRunnerLogger(true), + new RecordingLogger(), "test", ` paths-ignore: @@ -410,7 +410,7 @@ test("parseUserConfig - throws a ConfigurationError if the file is not valid YAM t.throws( () => dbConfig.parseUserConfig( - getRunnerLogger(true), + new RecordingLogger(), "test", ` paths-ignore: @@ -562,74 +562,89 @@ test("mergeDefaultSetupAndUserConfigs - keeps other properties from user-supplie t.deepEqual(result, configFile); }); -test("mergeDefaultSetupAndUserConfigs - ignores, but warns about, unknown keys from Default Setup", async (t) => { - const logger = new RecordingLogger(); - const configFile: dbConfig.UserConfig = { - "query-filters": [{ exclude: { a: "b" } }], - "paths-ignore": ["path"], - }; +test.serial( + "mergeDefaultSetupAndUserConfigs - ignores, but warns about, unknown keys from Default Setup", + async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = { + "query-filters": [{ exclude: { a: "b" } }], + "paths-ignore": ["path"], + }; - const result = dbConfig.mergeDefaultSetupAndUserConfigs( - logger, - { - "default-setup": { - borg: [], - org: { - unknown: "foo", - "model-packs": [], + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + borg: [], + org: { + unknown: "foo", + "model-packs": [], + }, + } as unknown as dbConfig.DefaultSetupConfig, + "paths-ignore": ["other-path"], }, - } as unknown as dbConfig.DefaultSetupConfig, - "paths-ignore": ["other-path"], - }, - configFile, - ); + configFile, + ); - t.deepEqual(result, { - ...configFile, - "default-setup": { org: { "model-packs": [] } }, - }); + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [] } }, + }); - const expectedUnrecognisedKeys = [ - ".default-setup.org.unknown", - ".default-setup.borg", - ".paths-ignore", - ].join(", "); - checkExpectedLogMessages(t, logger.messages, [ - `Unrecognised keys in Default Setup configuration: ${expectedUnrecognisedKeys}`, - ]); -}); + const expectedUnrecognisedKeys = [ + ".default-setup.org.unknown", + ".default-setup.borg", + ".paths-ignore", + ].join(", "); + checkExpectedLogMessages(t, logger.messages, [ + `Unrecognised keys in Default Setup configuration: ${expectedUnrecognisedKeys}`, + ]); + }); + }, +); -test("mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Setup", async (t) => { - const logger = new RecordingLogger(); - const configFile: dbConfig.UserConfig = {}; +test.serial( + "mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Setup", + async (t) => { + const logger = new RecordingLogger(); + const configFile: dbConfig.UserConfig = {}; - const result = dbConfig.mergeDefaultSetupAndUserConfigs( - logger, - { - "default-setup": { - org: { - "model-packs": [123], + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + + const result = dbConfig.mergeDefaultSetupAndUserConfigs( + logger, + { + "default-setup": { + org: { + "model-packs": [123], + }, + } as unknown as dbConfig.DefaultSetupConfig, }, - } as unknown as dbConfig.DefaultSetupConfig, - }, - configFile, - ); + configFile, + ); - t.deepEqual(result, { - ...configFile, - "default-setup": { org: { "model-packs": [123] } }, - }); + t.deepEqual(result, { + ...configFile, + "default-setup": { org: { "model-packs": [123] } }, + }); - const expectedInvalidKeys = [".default-setup.org.model-packs[0]"].join(", "); - checkExpectedLogMessages(t, logger.messages, [ - `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, - ]); -}); + const expectedInvalidKeys = [".default-setup.org.model-packs[0]"].join( + ", ", + ); + checkExpectedLogMessages(t, logger.messages, [ + `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, + ]); + }); + }, +); /** Parses `contents` as a configuration without validating it. */ function parseUnvalidatedConfig(contents: string): dbConfig.UserConfig { return dbConfig.parseUserConfig( - getRunnerLogger(true), + new RecordingLogger(), "test", contents, false, diff --git a/src/diagnostics.test.ts b/src/diagnostics.test.ts new file mode 100644 index 000000000..39ed95b20 --- /dev/null +++ b/src/diagnostics.test.ts @@ -0,0 +1,165 @@ +import * as fs from "fs"; +import path from "path"; + +import test, { type ExecutionContext } from "ava"; + +import * as diagnostics from "./diagnostics"; +import { BuiltInLanguage } from "./languages"; +import { + checkExpectedLogMessages, + createTestConfig, + initAllState, + RecordingLogger, + setupTests, +} from "./testing-utils"; +import { withTmpDir } from "./util"; + +setupTests(test); + +const id = "codeql-action/test-diagnostic"; +const name = "Test title"; + +test("makeDiagnostic - adds expected properties", (t) => { + const diagnostic = diagnostics.makeDiagnostic(id, name, {}); + + t.truthy(diagnostic.timestamp); + t.is(diagnostic.source.id, id); + t.is(diagnostic.source.name, name); +}); + +test("getLanguageDiagnosticsPath gets language-specific path", async (t) => + withTmpDir(async (tmpDir) => { + for (const language of Object.values(BuiltInLanguage)) { + t.is( + diagnostics.getLanguageDiagnosticsPath(tmpDir, language), + path.resolve( + tmpDir, + language, + ...diagnostics.CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS, + ), + ); + } + })); + +test("getTempLanguageDiagnosticsPath returns path without language", async (t) => + withTmpDir(async (tmpDir) => { + t.is( + diagnostics.getTempLanguageDiagnosticsPath(tmpDir), + path.resolve( + tmpDir, + diagnostics.TEMP_DIAGNOSTIC_DIR_NAME, + ...diagnostics.CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS, + ), + ); + })); + +test("getTempLanguageDiagnosticsPath returns language-specific path if a language is provided", async (t) => + withTmpDir(async (tmpDir) => { + t.is( + diagnostics.getTempLanguageDiagnosticsPath(tmpDir, BuiltInLanguage.swift), + path.resolve( + tmpDir, + diagnostics.TEMP_DIAGNOSTIC_DIR_NAME, + BuiltInLanguage.swift, + ...diagnostics.CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS, + ), + ); + })); + +test("getDatabaseDiagnosticsPath returns language-specific path", async (t) => + withTmpDir(async (tmpDir) => { + t.is( + diagnostics.getDatabaseDiagnosticsPath( + createTestConfig({ dbLocation: tmpDir }), + BuiltInLanguage.swift, + ), + path.resolve( + tmpDir, + BuiltInLanguage.swift, + ...diagnostics.CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS, + ), + ); + })); + +/** + * Diagnostic files that are written to disk don't have deterministic names. + * This function recursively finds all `.json` files at {@link rootPath}, + * asserts that there is exactly one, and that it matches {@link expected}. + * + * @param rootPath The path to start searching from. + */ +function assertSingleDiagnosticIn( + t: ExecutionContext, + rootPath: string, + expected: diagnostics.DiagnosticMessage, +) { + // Find all `.json` files in `rootPath` and check that there is exactly one. + const filePaths = fs + .readdirSync(rootPath, { + recursive: true, + encoding: "utf-8", + }) + .filter((p) => p.endsWith(".json")); + + t.is(filePaths.length, 1); + + // Check that the written diagnostic matches the one we added. + const contents = fs.readFileSync( + path.resolve(rootPath, filePaths[0]), + "utf-8", + ); + + const readDiagnostic = JSON.parse(contents) as diagnostics.DiagnosticMessage; + t.deepEqual(readDiagnostic, expected); +} + +test("addDiagnostic writes temporary diagnostics and flushDiagnostics moves them to the database", async (t) => + withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + + const tempDiagDir = path.resolve(tmpDir, "temp"); + const testDbDir = path.resolve(tmpDir, "test", "database"); + const diagnostic = diagnostics.makeDiagnostic(id, name, {}); + + // Write the diagnostic. The path provided for `dbLocation` doesn't exist, + // so the diagnostic will be written to a temporary location. + t.notThrows(() => { + diagnostics.addDiagnostic( + createTestConfig({ tempDir: tempDiagDir, dbLocation: testDbDir }), + BuiltInLanguage.actions, + diagnostic, + logger, + ); + }); + + // Check that `tempDiagDir` was created. + t.true(fs.existsSync(tempDiagDir)); + + // Check that the diagnostic exists under `tempDiagDir`. + assertSingleDiagnosticIn(t, tempDiagDir, diagnostic); + + // Flush the diagnostics. This will cause the diagnostic we wrote previously to + // be moved to a location under `dbLocation`. + t.notThrows(() => { + diagnostics.flushDiagnostics( + initAllState({ logger }), + createTestConfig({ + tempDir: tempDiagDir, + dbLocation: testDbDir, + }), + ); + }); + + // Check that `testDbDir` was created. + t.true(fs.existsSync(testDbDir)); + + // Check that the diagnostic exists under `testDbDir`. + assertSingleDiagnosticIn(t, testDbDir, diagnostic); + + // Check that we logged that the database directory doesn't exist yet + // and that we moved one diagnostic. + checkExpectedLogMessages(t, logger.messages, [ + `but the database at ${path.join(testDbDir, "actions")} does not exist yet`, + "Moving 1 diagnostic(s) to their respective databases.", + ]); + })); diff --git a/src/diagnostics.ts b/src/diagnostics.ts index fa0e87c04..bec1d28ab 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -1,10 +1,24 @@ -import { existsSync, mkdirSync, writeFileSync } from "fs"; +import * as fs from "fs"; import path from "path"; +import type { ActionState } from "./action-common"; +import { getTemporaryDirectory } from "./actions-util"; import type { Config } from "./config-utils"; import { Language } from "./languages"; -import { getActionsLogger } from "./logging"; -import { getCodeQLDatabasePath } from "./util"; +import { getActionsLogger, type Logger } from "./logging"; +import { + getCodeQLDatabasePath, + getErrorMessage, + isErrnoException, +} from "./util"; + +/** The name of the directory we use to temporarily store diagnostics. */ +export const TEMP_DIAGNOSTIC_DIR_NAME = "codeql-action-temp-diagnostics"; + +export const CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS = [ + "diagnostic", + "codeql-action", +]; /** * Known tags for diagnostics. There is currently only "internal-error", @@ -84,22 +98,16 @@ export type DiagnosticMessage = DiagnosticMessageOptions & { source: DiagnosticSource; }; -/** Represents a diagnostic message that has not yet been written to the database. */ -interface UnwrittenDiagnostic { - /** The diagnostic message that has not yet been written. */ - diagnostic: DiagnosticMessage; - /** The language the diagnostic is for. */ - language: Language; +/** Represents a diagnostic message that has not yet been saved to the database. */ +interface TemporaryDiagnostic { + /** The path to which the diagnostic has temporarily been written to. */ + path: string; + /** The language the diagnostic is for, if any. */ + language?: Language; } -/** A list of diagnostics which have not yet been written to disk. */ -let unwrittenDiagnostics: UnwrittenDiagnostic[] = []; - -/** - * A list of diagnostics which have not yet been written to disk, - * and where the language does not matter. - */ -let unwrittenDefaultLanguageDiagnostics: DiagnosticMessage[] = []; +/** A list of diagnostics which have not yet been saved to the database. */ +let temporaryDiagnostics: TemporaryDiagnostic[] = []; /** * Counter used to generate a unique suffix for each diagnostic filename, so that @@ -128,6 +136,51 @@ export function makeDiagnostic( }; } +/** + * Gets a path relative to {@link basePath} where diagnostics for {@link language} should be stored at. + */ +export function getLanguageDiagnosticsPath( + basePath: string, + language: Language, +) { + return path.resolve( + basePath, + language, + ...CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS, + ); +} + +/** + * Gets a path relative to {@link tmpDir} where diagnostics should temporarily be stored + * until they can be moved to the database. + */ +export function getTempDiagnosticsRoot(tmpDir: string): string { + return path.resolve(tmpDir, TEMP_DIAGNOSTIC_DIR_NAME); +} + +/** + * Gets a path relative to {@link tmpDir} where diagnostics for {@link language} + * can temporarily be stored at before the database is initialised. If {@link language} + * is `undefined`, then a non-language-specific path is returned. + */ +export function getTempLanguageDiagnosticsPath( + tmpDir: string, + language?: Language, +) { + const root = getTempDiagnosticsRoot(tmpDir); + + if (language !== undefined) { + return getLanguageDiagnosticsPath(root, language); + } + + return path.resolve(root, ...CODEQL_ACTION_DIAGNOSTIC_SUBDIR_PARTS); +} + +/** Gets the path where diagnostics for {@link language} should be stored in the database. */ +export function getDatabaseDiagnosticsPath(config: Config, language: Language) { + return getLanguageDiagnosticsPath(config.dbLocation, language); +} + /** * Adds the given diagnostic to the database. If the database does not yet exist, * the diagnostic will be written to it once it has been created. @@ -140,22 +193,23 @@ export function addDiagnostic( config: Config, language: Language, diagnostic: DiagnosticMessage, + logger: Logger = getActionsLogger(), ) { - const logger = getActionsLogger(); const databasePath = language ? getCodeQLDatabasePath(config, language) : config.dbLocation; // Check that the database exists before writing to it. If the database does not yet exist, - // store the diagnostic in memory and write it later. - if (existsSync(databasePath)) { - writeDiagnostic(config, language, diagnostic); + // store the diagnostic in a temporary location and move it to the database later. + if (fs.existsSync(databasePath)) { + writeDiagnostic({ logger }, config, language, diagnostic); } else { logger.debug( `Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.`, ); - unwrittenDiagnostics.push({ diagnostic, language }); + // Write the diagnostic to a temporary location. + writeTemporaryDiagnosticFile(logger, config.tempDir, language, diagnostic); } } @@ -163,6 +217,7 @@ export function addDiagnostic( export function addNoLanguageDiagnostic( config: Config | undefined, diagnostic: DiagnosticMessage, + logger: Logger = getActionsLogger(), ) { if (config !== undefined) { addDiagnostic( @@ -173,10 +228,74 @@ export function addNoLanguageDiagnostic( diagnostic, ); } else { - unwrittenDefaultLanguageDiagnostics.push(diagnostic); + logger.debug( + `Writing a diagnostic not associated with any language, but we have no configuration to tell us where the database is yet.`, + ); + + // Write the diagnostic to a temporary location. + writeTemporaryDiagnosticFile( + logger, + getTemporaryDirectory(), + undefined, + diagnostic, + ); } } +/** + * Writes {@link diagnostic} for an optional {@link language} to a subdirectory of {@link tempDir} + * and tracks it as a diagnostic that was written to a temporary location. + */ +function writeTemporaryDiagnosticFile( + logger: Logger, + tempDir: string, + language: Language | undefined, + diagnostic: DiagnosticMessage, +) { + const tempDiagnosticsPath = getTempLanguageDiagnosticsPath(tempDir, language); + + try { + // Write the diagnostic to a temporary location. + const diagnosticPath = writeDiagnosticFile(tempDiagnosticsPath, diagnostic); + + // Track the temporary file. + temporaryDiagnostics.push({ path: diagnosticPath, language }); + } catch (err) { + logger.warning( + `Unable to write temporary diagnostic to '${tempDiagnosticsPath}': ${getErrorMessage(err)}\n\n${JSON.stringify(diagnostic)}`, + ); + } +} + +/** + * Writes {@link diagnostic} to a file in {@link diagnosticsPath}. + */ +function writeDiagnosticFile( + diagnosticsPath: string, + diagnostic: DiagnosticMessage, +): string { + // Create the directory if it doesn't exist yet. + fs.mkdirSync(diagnosticsPath, { recursive: true }); + + // Include a monotonically increasing suffix to avoid filename collisions + // between diagnostics produced within the same millisecond. + const uniqueSuffix = (diagnosticCounter++).toString(); + // We should only need to remove colons, but to be defensive, only allow a restricted set of + // characters. + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "", + ); + const jsonPath = path.resolve( + diagnosticsPath, + `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`, + ); + + fs.writeFileSync(jsonPath, JSON.stringify(diagnostic)); + + return jsonPath; +} + /** * Writes the given diagnostic to the database. * @@ -185,78 +304,94 @@ export function addNoLanguageDiagnostic( * @param diagnostic The diagnostic message to add to the database. */ function writeDiagnostic( + action: ActionState<["Logger"]>, config: Config, - language: Language | undefined, + language: Language, diagnostic: DiagnosticMessage, ) { - const logger = getActionsLogger(); - const databasePath = language - ? getCodeQLDatabasePath(config, language) - : config.dbLocation; - const diagnosticsPath = path.resolve( - databasePath, - "diagnostic", - "codeql-action", - ); + const diagnosticsPath = getDatabaseDiagnosticsPath(config, language); try { - // Create the directory if it doesn't exist yet. - mkdirSync(diagnosticsPath, { recursive: true }); - - // Include a monotonically increasing suffix to avoid filename collisions - // between diagnostics produced within the same millisecond. - const uniqueSuffix = (diagnosticCounter++).toString(); - // We should only need to remove colons, but to be defensive, only allow a restricted set of - // characters. - const sanitizedTimestamp = diagnostic.timestamp.replace( - /[^a-zA-Z0-9.-]/g, - "", - ); - const jsonPath = path.resolve( - diagnosticsPath, - `codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`, - ); - - writeFileSync(jsonPath, JSON.stringify(diagnostic)); + writeDiagnosticFile(diagnosticsPath, diagnostic); } catch (err) { - logger.warning(`Unable to write diagnostic message to database: ${err}`); - logger.debug(JSON.stringify(diagnostic)); + action.logger.warning( + `Unable to write diagnostic message to database: ${err}`, + ); + action.logger.debug(JSON.stringify(diagnostic)); } } -/** Report if there are unwritten diagnostics and write them to the log. */ -export function logUnwrittenDiagnostics() { - const logger = getActionsLogger(); - const num = unwrittenDiagnostics.length; - if (num > 0) { - logger.warning( - `${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.`, +/** Report if there are temporary diagnostics and write them to the log. */ +export function logTemporaryDiagnostics(action: ActionState<["Logger"]>) { + const num = temporaryDiagnostics.length; + + if (num > 0 && action.logger.isDebug()) { + action.logger.startGroup("Diagnostics in temporary location"); + action.logger.debug( + `${num} diagnostic(s) were not moved to the database and remain in the temporary directory.`, ); - for (const unwritten of unwrittenDiagnostics) { - logger.debug(JSON.stringify(unwritten.diagnostic)); + for (const temporary of temporaryDiagnostics) { + try { + action.logger.debug(fs.readFileSync(temporary.path, "utf-8")); + } catch (err) { + action.logger.debug( + `Failed to read diagnostics from temporary location at '${temporary.path}': ${getErrorMessage(err)}`, + ); + } + } + + action.logger.endGroup(); + } +} + +/** Relocates all temporary diagnostics to the respective databases. */ +export function flushDiagnostics( + action: ActionState<["Logger"]>, + config: Config, +) { + const diagnosticsCount = temporaryDiagnostics.length; + action.logger.debug( + `Moving ${diagnosticsCount} diagnostic(s) to their respective databases.`, + ); + + const failedFlushDiagnostics: TemporaryDiagnostic[] = []; + for (const temporary of temporaryDiagnostics) { + // If `temporary.language` is `undefined`, then we didn't have a `config` at the time that + // `addNoLanguageDiagnostic` was called. In that case, we arbitrarily choose the first + // configured language here like we would have done in `addNoLanguageDiagnostic`. + const directory = getDatabaseDiagnosticsPath( + config, + temporary.language ?? config.languages[0], + ); + const filename = path.basename(temporary.path); + const destination = path.join(directory, filename); + + try { + fs.mkdirSync(directory, { recursive: true }); + try { + fs.renameSync(temporary.path, destination); + } catch (renameErr: unknown) { + // Rethrow the error if it isn't an EXDEV one. + if (!isErrnoException(renameErr) || renameErr.code !== "EXDEV") { + throw renameErr; + } + + // The error was an EXDEV one: try to copy the file and delete the temporary one. + fs.copyFileSync(temporary.path, destination); + fs.unlinkSync(temporary.path); + } + } catch (err) { + action.logger.warning( + `Failed to relocate '${temporary.path}' to '${destination}': ${getErrorMessage(err)}`, + ); + + failedFlushDiagnostics.push(temporary); } } -} -/** Writes all unwritten diagnostics to disk. */ -export function flushDiagnostics(config: Config) { - const logger = getActionsLogger(); - - const diagnosticsCount = - unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length; - logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`); - - for (const unwritten of unwrittenDiagnostics) { - writeDiagnostic(config, unwritten.language, unwritten.diagnostic); - } - for (const unwritten of unwrittenDefaultLanguageDiagnostics) { - addNoLanguageDiagnostic(config, unwritten); - } - - // Reset the unwritten diagnostics arrays. - unwrittenDiagnostics = []; - unwrittenDefaultLanguageDiagnostics = []; + // Reset the temporary diagnostics arrays. + temporaryDiagnostics = failedFlushDiagnostics; } /** diff --git a/src/diff-informed-analysis-utils.test.ts b/src/diff-informed-analysis-utils.test.ts index e07240e32..76e5f33b4 100644 --- a/src/diff-informed-analysis-utils.test.ts +++ b/src/diff-informed-analysis-utils.test.ts @@ -10,7 +10,6 @@ import { exportedForTesting, } from "./diff-informed-analysis-utils"; import { Feature, FeatureEnablement, initFeatures } from "./feature-flags"; -import { getRunnerLogger } from "./logging"; import { parseRepositoryNwo } from "./repository"; import { setupTests, @@ -19,6 +18,7 @@ import { mockFeatureFlagApiEndpoint, setupActionsVars, makeMacro, + RecordingLogger, } from "./testing-utils"; import { GitHubVariant, withTmpDir } from "./util"; import type { GitHubVersion } from "./util"; @@ -55,7 +55,7 @@ const testShouldPerformDiffInformedAnalysis = makeMacro({ setupActionsVars(tmpDir, tmpDir); const testCase = { ...defaultTestCase, ...partialTestCase }; - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = mockCodeQLVersion(testCase.codeQLVersion); if (testCase.diffInformedQueriesEnvVar !== undefined) { @@ -182,7 +182,7 @@ test.serial( async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = mockCodeQLVersion("2.21.0"); const features = createFeatures([Feature.DiffInformedQueries]); @@ -207,7 +207,7 @@ test.serial( async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = mockCodeQLVersion("2.21.0"); // A features implementation whose getValue rejects, simulating an // unexpected failure when determining whether diff-informed analysis @@ -237,7 +237,7 @@ test.serial( async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = mockCodeQLVersion("2.21.0"); const features = createFeatures([Feature.DiffInformedQueries]); @@ -274,7 +274,7 @@ test.serial( async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const codeql = mockCodeQLVersion("2.21.0"); const features = createFeatures([Feature.DiffInformedQueries]); @@ -313,7 +313,7 @@ function runGetDiffRanges(changes: number, patch: string[] | undefined): any { changes, patch: patch?.join("\n"), }, - getRunnerLogger(true), + new RecordingLogger(), ); } diff --git a/src/doc-url.ts b/src/doc-url.ts index c624817e9..97f5451d8 100644 --- a/src/doc-url.ts +++ b/src/doc-url.ts @@ -3,7 +3,6 @@ */ export enum DocUrl { - ASSIGNING_PERMISSIONS_TO_JOBS = "https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs", AUTOMATIC_BUILD_FAILED = "https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/automatic-build-failed", CODEQL_BUILD_MODES = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#codeql-build-modes", DEFINE_ENV_VARIABLES = "https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow", diff --git a/src/feature-flags.ts b/src/feature-flags.ts index a380c1a63..a4c119ad3 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -74,7 +74,6 @@ export enum Feature { AllowMergeConfigFiles = "allow_merge_config_files", /** Controls whether we allow multiple values for the `analysis-kinds` input. */ AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds", - CleanupTrapCaches = "cleanup_trap_caches", /** Whether to allow the `config-file` input to be specified via a repository property. */ ConfigFileRepositoryProperty = "config_file_repository_property", CppDependencyInstallation = "cpp_dependency_installation_enabled", @@ -106,19 +105,6 @@ export enum Feature { OverlayAnalysisGo = "overlay_analysis_go", OverlayAnalysisJava = "overlay_analysis_java", OverlayAnalysisJavascript = "overlay_analysis_javascript", - /** - * When set, chooses the default CodeQL CLI version as the highest version that is both enabled by - * feature flags and present as an overlay-base database in the Actions cache for the configured - * languages. Falls back to the highest feature flagged version if no intersecting overlay-base - * database exists in the cache. - */ - OverlayAnalysisMatchCodeqlVersion = "overlay_analysis_match_codeql_version", - /** - * Like `OverlayAnalysisMatchCodeqlVersion`, but only logs a diagnostic with the version that - * would have been chosen instead of actually changing the default CodeQL CLI version. - * `OverlayAnalysisMatchCodeqlVersion` overrides this flag. - */ - OverlayAnalysisMatchCodeqlVersionDryRun = "overlay_analysis_match_codeql_version_dry_run", /** * Lowers the overlay minimum available disk space to 8 GB. The lowest enabled limit wins; if * none are enabled, the default applies. @@ -214,11 +200,6 @@ export const featureConfig = { envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS", minimumVersion: undefined, }, - [Feature.CleanupTrapCaches]: { - defaultValue: false, - envVar: "CODEQL_ACTION_CLEANUP_TRAP_CACHES", - minimumVersion: undefined, - }, [Feature.ConfigFileRepositoryProperty]: { defaultValue: false, envVar: "CODEQL_ACTION_CONFIG_FILE_REPOSITORY_PROPERTY", @@ -367,16 +348,6 @@ export const featureConfig = { minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_RUBY, }, // Other overlay-related feature flags - [Feature.OverlayAnalysisMatchCodeqlVersion]: { - defaultValue: false, - envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION", - minimumVersion: undefined, - }, - [Feature.OverlayAnalysisMatchCodeqlVersionDryRun]: { - defaultValue: false, - envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN", - minimumVersion: undefined, - }, [Feature.OverlayAnalysisMinDisk8Gb]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MIN_DISK_8_GB", diff --git a/src/feature-flags/properties.test.ts b/src/feature-flags/properties.test.ts index d3094a8d1..6544d15d3 100644 --- a/src/feature-flags/properties.test.ts +++ b/src/feature-flags/properties.test.ts @@ -2,7 +2,6 @@ import test from "ava"; import * as sinon from "sinon"; import * as api from "../api-client"; -import { getRunnerLogger } from "../logging"; import { parseRepositoryNwo } from "../repository"; import { RecordingLogger, setupTests } from "../testing-utils"; @@ -19,7 +18,7 @@ test.serial( url: "", data: {}, }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); await t.throwsAsync( properties.loadPropertiesFromApi(logger, mockRepositoryNwo), @@ -39,7 +38,7 @@ test.serial( url: "", data: [{}], }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); await t.throwsAsync( properties.loadPropertiesFromApi(logger, mockRepositoryNwo), @@ -63,7 +62,7 @@ test.serial( { property_name: "also-not-used-by-us", value: ["A", "B", "C"] }, ], }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); await t.notThrowsAsync( properties.loadPropertiesFromApi(logger, mockRepositoryNwo), @@ -86,7 +85,7 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => { { property_name: "unknown-property", value: "something" }, ] satisfies properties.GitHubPropertiesResponse, }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); const response = await properties.loadPropertiesFromApi( logger, @@ -113,7 +112,7 @@ test.serial("loadPropertiesFromApi parses true boolean property", async (t) => { { property_name: "github-codeql-extra-queries", value: "+queries" }, ] satisfies properties.GitHubPropertiesResponse, }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const warningSpy = sinon.spy(logger, "warning"); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); const response = await properties.loadPropertiesFromApi( @@ -141,7 +140,7 @@ test.serial( }, ] satisfies properties.GitHubPropertiesResponse, }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const warningSpy = sinon.spy(logger, "warning"); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); const response = await properties.loadPropertiesFromApi( @@ -164,7 +163,7 @@ test.serial( url: "", data: [{ property_name: "github-codeql-extra-queries", value: 123 }], }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); await t.throwsAsync( properties.loadPropertiesFromApi(logger, mockRepositoryNwo), @@ -190,7 +189,7 @@ test.serial( }, ] satisfies properties.GitHubPropertiesResponse, }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const warningSpy = sinon.spy(logger, "warning"); const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); const response = await properties.loadPropertiesFromApi( diff --git a/src/feature-flags/testing-util.ts b/src/feature-flags/testing-util.ts index 202f2c97b..46795bdf0 100644 --- a/src/feature-flags/testing-util.ts +++ b/src/feature-flags/testing-util.ts @@ -8,11 +8,12 @@ import { FeatureWithoutCLI, initFeatures, } from "../feature-flags"; -import { getRunnerLogger } from "../logging"; +import type { Logger } from "../logging"; import { parseRepositoryNwo } from "../repository"; import { LoggedMessage, mockCodeQLVersion, + RecordingLogger, setupActionsVars, } from "../testing-utils"; import { ToolsFeature } from "../tools-features"; @@ -51,7 +52,7 @@ export function assertAllFeaturesUndefinedInApi( export function setUpFeatureFlagTests( tmpDir: string, - logger = getRunnerLogger(true), + logger: Logger = new RecordingLogger(), gitHubVersion = { type: GitHubVariant.DOTCOM } as util.GitHubVersion, ): FeatureEnablement { setupActionsVars(tmpDir, tmpDir); diff --git a/src/fingerprints.test.ts b/src/fingerprints.test.ts index 6cb9b6861..6b480a777 100644 --- a/src/fingerprints.test.ts +++ b/src/fingerprints.test.ts @@ -5,9 +5,8 @@ import * as ava from "ava"; import test from "ava"; import * as fingerprints from "./fingerprints"; -import { getRunnerLogger } from "./logging"; import * as sarif from "./sarif"; -import { setupTests } from "./testing-utils"; +import { RecordingLogger, setupTests } from "./testing-utils"; import * as util from "./util"; setupTests(test); @@ -131,7 +130,7 @@ function testResolveUriToFile(uri: any, index: any, artifactsURIs: any[]) { location, artifacts, process.cwd(), - getRunnerLogger(true), + new RecordingLogger(), ); } @@ -218,7 +217,7 @@ test("addFingerprints", async (t) => { await fingerprints.addFingerprints( input, sourceRoot, - getRunnerLogger(true), + new RecordingLogger(), ), expected, ); @@ -246,7 +245,7 @@ test("missingRegions", async (t) => { await fingerprints.addFingerprints( input, sourceRoot, - getRunnerLogger(true), + new RecordingLogger(), ), expected, ); diff --git a/src/init-action-post-helper.test.ts b/src/init-action-post-helper.test.ts index 554add71f..c0c898391 100644 --- a/src/init-action-post-helper.test.ts +++ b/src/init-action-post-helper.test.ts @@ -1,3 +1,6 @@ +import * as fs from "fs"; +import * as path from "path"; + import * as core from "@actions/core"; import test, { ExecutionContext } from "ava"; import * as sinon from "sinon"; @@ -8,10 +11,10 @@ import * as apiClient from "./api-client"; import * as codeql from "./codeql"; import * as configUtils from "./config-utils"; import * as debugArtifacts from "./debug-artifacts"; +import { getTempDiagnosticsRoot } from "./diagnostics"; import { EnvVar } from "./environment"; import { Feature } from "./feature-flags"; import * as initActionPostHelper from "./init-action-post-helper"; -import { getRunnerLogger } from "./logging"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; import * as overlayStatus from "./overlay/status"; import { parseRepositoryNwo } from "./repository"; @@ -20,6 +23,7 @@ import { createFeatures, createTestConfig, DEFAULT_ACTIONS_VARS, + getCodeQLForTesting, getTestEnv, makeMacro, makeVersionInfo, @@ -35,6 +39,47 @@ const NUM_BYTES_PER_GIB = 1024 * 1024 * 1024; setupTests(test); +test.serial("generateFailedSarif picks up unwritten diagnostics", async (t) => + util.withTmpDir(async (tmpDir) => { + const logger = new RecordingLogger(); + + const tempDiagDir = getTempDiagnosticsRoot(tmpDir); + fs.mkdirSync(tempDiagDir, { recursive: true }); + + const codeqlObject = await getCodeQLForTesting(); + const diagnosticsExport = sinon + .stub(codeqlObject, "diagnosticsExport") + .resolves(); + + sinon.stub(codeql, "getCodeQL").resolves(codeqlObject); + + const sarifFile = path.join(tmpDir, "failed.sarif"); + const result = await initActionPostHelper.generateFailedSarif( + logger, + createFeatures([Feature.ExportDiagnosticsEnabled]), + createTestConfig({ + tempDir: tmpDir, + // Explicitly undefined to ensure we get the "no database" path. + dbLocation: undefined, + }), + "category", + "", + sarifFile, + ); + + t.truthy(result); + t.is(diagnosticsExport.callCount, 1); + t.true( + diagnosticsExport.calledOnceWith( + sarifFile, + "category", + sinon.match.any, + tempDiagDir, + ), + ); + }), +); + test.serial("init-post action with debug mode off", async (t) => { return await util.withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); @@ -62,7 +107,7 @@ test.serial("init-post action with debug mode off", async (t) => { createFeatures([]), "success", getTestEnv(), - getRunnerLogger(true), + new RecordingLogger(), ); t.assert(uploadAllAvailableDebugArtifactsSpy.notCalled); @@ -86,7 +131,7 @@ test.serial("init-post action with debug mode on", async (t) => { createFeatures([]), "success", getTestEnv(), - getRunnerLogger(true), + new RecordingLogger(), ); t.assert(uploadAllAvailableDebugArtifactsSpy.called); @@ -383,7 +428,7 @@ test.serial( createFeatures([Feature.OverlayAnalysisStatusSave]), "success", getTestEnv(), - getRunnerLogger(true), + new RecordingLogger(), ); t.true( @@ -453,7 +498,7 @@ test.serial( createFeatures([]), "success", getTestEnv(), - getRunnerLogger(true), + new RecordingLogger(), ); t.true( @@ -497,7 +542,7 @@ test.serial("does not save overlay status when build successful", async (t) => { createFeatures([Feature.OverlayAnalysisStatusSave]), "success", env, - getRunnerLogger(true), + new RecordingLogger(), ); t.true( @@ -536,7 +581,7 @@ test.serial( createFeatures([]), "success", getTestEnv(), - getRunnerLogger(true), + new RecordingLogger(), ); t.true( @@ -589,7 +634,7 @@ async function runOverlayPostStep({ createFeatures([Feature.OverlayAnalysisStatusSave]), jobStatus, env, - getRunnerLogger(true), + new RecordingLogger(), ); return { saveOverlayStatusStub }; @@ -737,7 +782,7 @@ async function testFailedSarifUpload( .withArgs("matrix") .returns(JSON.stringify(matrix)); - const codeqlObject = await codeql.getCodeQLForTesting(); + const codeqlObject = await getCodeQLForTesting(); sinon.stub(codeql, "getCodeQL").resolves(codeqlObject); sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6")); const databaseExportDiagnosticsStub = sinon.stub( @@ -764,7 +809,7 @@ async function testFailedSarifUpload( config, parseRepositoryNwo("github/codeql-action"), createFeatures(features), - getRunnerLogger(true), + new RecordingLogger(), ); if (expectUpload) { t.deepEqual(result, { @@ -833,7 +878,7 @@ async function mockRiskAssessmentEnv(matrix: string) { .stub(apiClient, "getGitHubVersion") .resolves({ type: util.GitHubVariant.GHES, version: "3.0.0" }); - const codeqlObject = await codeql.getCodeQLForTesting(); + const codeqlObject = await getCodeQLForTesting(); const databaseExportDiagnostics = sinon .stub(codeqlObject, "databaseExportDiagnostics") .resolves(); diff --git a/src/init-action-post-helper.ts b/src/init-action-post-helper.ts index cfaab312f..060592cac 100644 --- a/src/init-action-post-helper.ts +++ b/src/init-action-post-helper.ts @@ -24,6 +24,7 @@ import { type UploadDebugArtifactsConfig, } from "./debug-artifacts"; import * as dependencyCaching from "./dependency-caching"; +import { getTempDiagnosticsRoot } from "./diagnostics"; import { EnvVar, ReadOnlyEnv } from "./environment"; import { Feature, FeatureEnablement } from "./feature-flags"; import { Logger } from "./logging"; @@ -177,7 +178,16 @@ async function prepareFailedSarif( } } -async function generateFailedSarif( +/** + * Generates a SARIF file for a failed run, i.e. an analysis where no SARIF was produced as a result of the analysis. + * + * Not intended to be called directly, other than by `prepareFailedSarif`. + * + * @param category The analysis category to include in the SARIF. + * @param checkoutPath The path where the repository was checked out at. + * @param sarifFile The SARIF file to produce. + */ +export async function generateFailedSarif( logger: Logger, features: FeatureEnablement, config: ActionConfig<"codeQLCmd" | "tempDir">, @@ -194,11 +204,26 @@ async function generateFailedSarif( } // If there is no database or the feature flag is off, we run 'export diagnostics' - if ( - databasePath === undefined || - !(await features.getValue(Feature.ExportDiagnosticsEnabled, codeql)) - ) { - await codeql.diagnosticsExport(sarifFile, category, config); + const exportDiagnosticsEnabled = await features.getValue( + Feature.ExportDiagnosticsEnabled, + codeql, + ); + if (databasePath === undefined || !exportDiagnosticsEnabled) { + // If we don't have a database path, then we may have diagnostics that were written to a + // temporary location. Get the path that they would have been written to and check whether + // it exists. If so, pass it on to the CLI. The CLI will not complain if we give it a + // path that doesn't exist, so we are just being extra defensive here. + // We also don't pass the path to the CLI if `exportDiagnosticsEnabled` is not enabled, to preserve + // its intended behaviour of controlling whether diagnostics are included in the SARIF or not. + let diagnosticDir: string | undefined = getTempDiagnosticsRoot( + config.tempDir, + ); + + if (!exportDiagnosticsEnabled || !fs.existsSync(diagnosticDir)) { + diagnosticDir = undefined; + } + + await codeql.diagnosticsExport(sarifFile, category, config, diagnosticDir); } else { // We call 'database export-diagnostics' to find any per-database diagnostics. await codeql.databaseExportDiagnostics(databasePath, sarifFile, category); diff --git a/src/init-action.ts b/src/init-action.ts index f58d4ccca..ceffe9d6e 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -40,7 +40,7 @@ import { addDiagnostic, addNoLanguageDiagnostic, flushDiagnostics, - logUnwrittenDiagnostics, + logTemporaryDiagnostics, makeDiagnostic, makeTelemetryDiagnostic, } from "./diagnostics"; @@ -484,6 +484,7 @@ async function run( if (statusReportBase !== undefined) { await sendStatusReport(statusReportBase); } + logTemporaryDiagnostics(actionState); return; } @@ -766,14 +767,15 @@ async function run( ); } - // Write diagnostics to the database that we previously stored in memory because the database - // did not exist until now. - flushDiagnostics(config); + // Move diagnostics to the database that we previously kept in a temporary location + // because the database did not exist until now + flushDiagnostics(actionState, config); // We save the config here instead of at the end of `initConfig` because we // may have updated the config returned from `initConfig`, e.g. to revert to // `OverlayDatabaseMode.None` if we failed to download an overlay-base // database. + config.successfullyInitialised = true; await configUtils.saveConfig(config, logger); core.setOutput("codeql-path", config.codeQLCmd); @@ -797,7 +799,7 @@ async function run( ); return; } finally { - logUnwrittenDiagnostics(); + logTemporaryDiagnostics(actionState); } await sendCompletedStatusReport( startedAt, diff --git a/src/logging.test.ts b/src/logging.test.ts new file mode 100644 index 000000000..33f6f8c16 --- /dev/null +++ b/src/logging.test.ts @@ -0,0 +1,17 @@ +import test from "ava"; + +import { joinMessageStrings } from "./logging"; +import { setupTests } from "./testing-utils"; + +setupTests(test); + +test("joinMessageStrings", async (t) => { + // For strings and errors, it is the identity function. + t.deepEqual(joinMessageStrings("Hello"), "Hello"); + + const error = new Error("Some error"); + t.deepEqual(joinMessageStrings(error), error); + + // For arrays of strings, we get a join-ed string. + t.deepEqual(joinMessageStrings(["foo", "bar"]), "foo bar"); +}); diff --git a/src/logging.ts b/src/logging.ts index 2c34cb54d..da6716872 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -1,45 +1,92 @@ import * as core from "@actions/core"; -export interface Logger { - debug: (message: string) => void; - info: (message: string) => void; - warning: (message: string | Error) => void; - error: (message: string | Error) => void; +/** Types that all of our logging functions accept. */ +export type Loggable = string | string[]; +/** Some logging functions also accept errors directly. */ +export type LoggableError = Loggable | Error; + +export interface Logger { + /** + * Logs {@link message} at debug-level. + * In Actions, this will only be output if workflow debugging is enabled. + * + * @param message + * A string or an array of strings. If an array of strings is provided, they are joined using + * space as the delimiter and printed as a single line. + */ + debug: (message: Loggable) => void; + /** + * Logs {@link message} at info-level. + * In Actions, this will always be output. + * + * @param message + * A string or an array of strings. If an array of strings is provided, they are joined using + * space as the delimiter and printed as a single line. + */ + info: (message: Loggable) => void; + /** + * Logs {@link message} at warning-level. + * In Actions, this will always be output and highlighted as a warning. It will additionally + * result in a warning-level workflow annotation. + * + * @param message + * A string or an array of strings. If an array of strings is provided, they are joined using + * space as the delimiter and printed as a single line. + */ + warning: (message: LoggableError) => void; + /** + * Logs {@link message} at error-level. + * In Actions, this will always be output and highlighted as an error. It will additionally + * result in an error-level workflow annotation. + * + * @param message + * A string or an array of strings. If an array of strings is provided, they are joined using + * space as the delimiter and printed as a single line. + */ + error: (message: LoggableError) => void; + /** + * Gets a value indicating whether debugging is enabled. + */ isDebug: () => boolean; startGroup: (name: string) => void; endGroup: () => void; } +/** If `message` is an array of strings, the array is `join`-ed into one string separated by spaces. */ +export const joinMessageStrings = ( + message: string[] | string | T, +): string | T => { + if (Array.isArray(message)) { + return message.join(" "); + } else { + return message; + } +}; + +/** + * Wraps a logging function so that `joinMessageStrings` is applied to the input before + * calling the wrapped logging function. + */ +const autoJoinMessageStrings = (fn: (msg: string | T) => void) => { + return (message: string[] | string | T) => { + return fn(joinMessageStrings(message)); + }; +}; + export function getActionsLogger(): Logger { return { - debug: core.debug, - info: core.info, - warning: core.warning, - error: core.error, + debug: autoJoinMessageStrings(core.debug), + info: autoJoinMessageStrings(core.info), + warning: autoJoinMessageStrings(core.warning), + error: autoJoinMessageStrings(core.error), isDebug: core.isDebug, startGroup: core.startGroup, endGroup: core.endGroup, }; } -export function getRunnerLogger(debugMode: boolean): Logger { - return { - // eslint-disable-next-line no-console - debug: debugMode ? console.debug : () => undefined, - // eslint-disable-next-line no-console - info: console.info, - // eslint-disable-next-line no-console - warning: console.warn, - // eslint-disable-next-line no-console - error: console.error, - isDebug: () => debugMode, - startGroup: () => undefined, - endGroup: () => undefined, - }; -} - export function withGroup(groupName: string, f: () => T): T { core.startGroup(groupName); try { diff --git a/src/overlay/caching.test.ts b/src/overlay/caching.test.ts index 364eeff9c..e965b06a7 100644 --- a/src/overlay/caching.test.ts +++ b/src/overlay/caching.test.ts @@ -11,11 +11,11 @@ import type { ResolveDatabaseOutput } from "../codeql"; import * as environment from "../environment"; import * as gitUtils from "../git-utils"; import { BuiltInLanguage } from "../languages"; -import { getRunnerLogger } from "../logging"; import { createTestConfig, makeMacro, mockCodeQLVersion, + RecordingLogger, setupTests, } from "../testing-utils"; import * as utils from "../util"; @@ -63,7 +63,7 @@ const testDownloadOverlayBaseDatabaseFromCache = makeMacro({ const dbLocation = path.join(tmpDir, "db"); await fs.promises.mkdir(dbLocation, { recursive: true }); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const testCase = { ...defaultDownloadTestCase, ...partialTestCase }; const config = createTestConfig({ dbLocation, @@ -214,7 +214,7 @@ testDownloadOverlayBaseDatabaseFromCache.serial( ); test.serial("overlay-base database cache keys remain stable", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const config = createTestConfig({ languages: ["python", "javascript"] }); const codeQlVersion = "2.23.0"; const commitOid = "abc123def456"; @@ -261,7 +261,7 @@ test.serial("overlay-base database cache keys remain stable", async (t) => { test.serial( "getCodeQlVersionsForOverlayBaseDatabases returns unique versions sorted latest first", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); sinon.stub(apiClient, "listActionsCaches").resolves([ @@ -287,7 +287,7 @@ test.serial( test.serial( "getCodeQlVersionsForOverlayBaseDatabases returns empty list when no caches exist", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); sinon.stub(apiClient, "listActionsCaches").resolves([]); @@ -303,7 +303,7 @@ test.serial( test.serial( "getCodeQlVersionsForOverlayBaseDatabases returns empty list when cache keys are unparseable", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); sinon.stub(apiClient, "listActionsCaches").resolves([ @@ -324,7 +324,7 @@ test.serial( test.serial( "getCodeQlVersionsForOverlayBaseDatabases returns the single version when only one cache exists", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); sinon.stub(apiClient, "listActionsCaches").resolves([ @@ -344,7 +344,7 @@ test.serial( test.serial( "getCodeQlVersionsForOverlayBaseDatabases resolves language aliases", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); // The alias `c++` should be resolved to "cpp" and match cache entries keyed with "cpp" sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); @@ -365,7 +365,7 @@ test.serial( test.serial( "getCodeQlVersionsForOverlayBaseDatabases de-duplicates resolved language aliases", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); const listActionsCachesStub = sinon @@ -391,7 +391,7 @@ test.serial( test.serial( "getCodeQlVersionsForOverlayBaseDatabases ignores nightly versions with build metadata", async (t) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); sinon.stub(apiClient, "listActionsCaches").resolves([ diff --git a/src/overlay/index.test.ts b/src/overlay/index.test.ts index 967ccf3a7..4534006f8 100644 --- a/src/overlay/index.test.ts +++ b/src/overlay/index.test.ts @@ -6,8 +6,11 @@ import * as sinon from "sinon"; import * as actionsUtil from "../actions-util"; import * as gitUtils from "../git-utils"; -import { getRunnerLogger } from "../logging"; -import { createTestConfig, setupTests } from "../testing-utils"; +import { + createTestConfig, + RecordingLogger, + setupTests, +} from "../testing-utils"; import { withTmpDir } from "../util"; import { writeBaseDatabaseOidsFile, writeOverlayChangesFile } from "."; @@ -27,7 +30,7 @@ test.serial( ), ); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const config = createTestConfig({ dbLocation }); // Mock the getFileOidsUnderPath function to return base OIDs @@ -91,7 +94,7 @@ test.serial( ), ); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const config = createTestConfig({ dbLocation }); // Mock the getFileOidsUnderPath function to return base OIDs @@ -169,7 +172,7 @@ test.serial( ), ); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const config = createTestConfig({ dbLocation }); // Mock the getFileOidsUnderPath function to return base OIDs @@ -232,7 +235,7 @@ test.serial( ), ); - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const config = createTestConfig({ dbLocation }); // Base OIDs (sourceRoot-relative paths) diff --git a/src/setup-codeql.test.ts b/src/setup-codeql.test.ts index 7dfac1914..8da398523 100644 --- a/src/setup-codeql.test.ts +++ b/src/setup-codeql.test.ts @@ -14,7 +14,6 @@ import * as diagnostics from "./diagnostics"; import { ActionsEnvVars, Env, EnvVar, ReadOnlyEnv } from "./environment"; import { Feature } from "./feature-flags"; import { BuiltInLanguage } from "./languages"; -import { getRunnerLogger } from "./logging"; import { getCacheRestoreKeyPrefix } from "./overlay/caching"; import { MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION } from "./per-language-bundles"; import * as setupCodeql from "./setup-codeql"; @@ -22,6 +21,7 @@ import * as tar from "./tar"; import { LINKED_CLI_VERSION, LoggedMessage, + RecordingLogger, SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DOTCOM_API_DETAILS, checkExpectedLogMessages, @@ -117,7 +117,7 @@ test.serial("convert to semver", (t) => { try { const parsedVersion = setupCodeql.convertToSemVer( version, - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(parsedVersion, expectedVersion); } catch (e) { @@ -145,7 +145,7 @@ test.serial( GitHubVariant.DOTCOM, false, features, - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(source, { @@ -231,7 +231,7 @@ for (const { GitHubVariant.DOTCOM, tarSupportsZstd, features, - getRunnerLogger(true), + new RecordingLogger(), ); t.is(source.toolsVersion, LINKED_CLI_VERSION.cliVersion); @@ -597,7 +597,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), - getRunnerLogger(true), + new RecordingLogger(), ); t.is(result.toolsVersion, "local"); @@ -634,7 +634,7 @@ for (const toolsInput of ["nightly", "nightly-latest"]) { GitHubVariant.DOTCOM, true, // tarSupportsZstd createFeatures([Feature.PerLanguageBundles]), - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(source, { @@ -694,7 +694,7 @@ test.serial( GitHubVariant.DOTCOM, true, // tarSupportsZstd features, - getRunnerLogger(true), + new RecordingLogger(), ); t.is(source.sourceType, "download"); @@ -733,7 +733,7 @@ for (const perLanguageBundles of [false, true]) { ? [Feature.ForceNightly, Feature.PerLanguageBundles] : [Feature.ForceNightly], ), - getRunnerLogger(true), + new RecordingLogger(), ); t.is(source.sourceType, "download"); @@ -848,7 +848,7 @@ test.serial( GitHubVariant.DOTCOM, true, createFeatures([]), - getRunnerLogger(true), + new RecordingLogger(), ), { message: @@ -1020,7 +1020,7 @@ test.serial( t.is( setupCodeql.tryGetTagNameFromUrl( "https://github.com/org/codeql-bundle-testing/releases/download/codeql-bundle-v2.19.0/codeql-bundle-linux64.tar.zst", - getRunnerLogger(true), + new RecordingLogger(), ), "codeql-bundle-v2.19.0", ); @@ -1032,7 +1032,7 @@ test.serial( (t) => { sinon.stub(toolcache, "findAllVersions").returns([]); t.is( - setupCodeql.getLatestToolcacheVersion(getRunnerLogger(true)), + setupCodeql.getLatestToolcacheVersion(new RecordingLogger()), undefined, ); }, @@ -1044,7 +1044,7 @@ test.serial( const testVersions = ["2.3.1", "3.2.1", "1.2.3"]; sinon.stub(toolcache, "findAllVersions").returns(testVersions); - t.is(setupCodeql.getLatestToolcacheVersion(getRunnerLogger(true)), "3.2.1"); + t.is(setupCodeql.getLatestToolcacheVersion(new RecordingLogger()), "3.2.1"); }, ); @@ -1070,7 +1070,7 @@ async function fakeOverlayBaseCacheKey( } test.serial( - "getCodeQLSource uses overlay-aware default version when requested for a PR", + "getCodeQLSource uses overlay-aware default version for a Code Scanning PR", async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); @@ -1097,8 +1097,8 @@ test.serial( SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, false, - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), + createFeatures([]), + new RecordingLogger(), ); t.assert(listStub.calledOnce); @@ -1109,7 +1109,95 @@ test.serial( ); test.serial( - "getCodeQLSource skips overlay-aware default version when not requested", + "getCodeQLSource skips the Actions cache when only one default version is enabled", + async (t) => { + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + process.env[EnvVar.CODE_SCANNING_REF] = "refs/heads/feature-branch"; + process.env[EnvVar.CODE_SCANNING_BASE_BRANCH] = "main"; + + sinon.stub(api, "getAutomationID").resolves("test/"); + const listStub = sinon.stub(api, "listActionsCaches").resolves([ + { + key: await fakeOverlayBaseCacheKey( + "javascript", + SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion, + "abc-1-1", + ), + }, + ]); + sinon + .stub(toolcache, "find") + .withArgs( + "CodeQL", + SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion, + ) + .returns("/path/to/codeql"); + + const source = await setupCodeql.getCodeQLSource( + undefined, + SAMPLE_DEFAULT_CLI_VERSION, + ["javascript"], + undefined, // otherLanguagePacksReason + true, + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + false, + createFeatures([]), + new RecordingLogger(), + ); + + t.assert(listStub.notCalled); + t.is(source.sourceType, "toolcache"); + t.is( + source.toolsVersion, + SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion, + ); + }); + }, +); + +test.serial( + "getCodeQLSource skips overlay-aware default version outside pull request analysis", + async (t) => { + sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(false); + + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + + sinon.stub(api, "getAutomationID").resolves("test/"); + const listStub = sinon.stub(api, "listActionsCaches").resolves([ + { + key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"), + }, + ]); + sinon + .stub(toolcache, "find") + .withArgs("CodeQL", "2.20.2") + .returns("/path/to/codeql-2.20.2"); + + const source = await setupCodeql.getCodeQLSource( + undefined, + overlayMatchEnabledVersions, + ["javascript"], + undefined, // otherLanguagePacksReason + true, + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + false, + createFeatures([]), + new RecordingLogger(), + ); + + t.assert(listStub.notCalled); + t.is(source.sourceType, "toolcache"); + t.is(source.toolsVersion, "2.20.2"); + }); + }, +); + +test.serial( + "getCodeQLSource skips overlay-aware default version when not requested for Code Scanning", async (t) => { await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); @@ -1136,8 +1224,8 @@ test.serial( SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, false, - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), + createFeatures([]), + new RecordingLogger(), ); t.assert(listStub.notCalled); @@ -1175,7 +1263,7 @@ test.serial( GitHubVariant.DOTCOM, true, // tarSupportsZstd createFeatures([Feature.PerLanguageBundles]), - getRunnerLogger(true), + new RecordingLogger(), ); t.is(source.sourceType, "download"); @@ -1226,7 +1314,7 @@ test.serial( GitHubVariant.DOTCOM, true, // tarSupportsZstd features, - getRunnerLogger(true), + new RecordingLogger(), ); t.is(source.sourceType, "download"); @@ -1296,7 +1384,7 @@ for (const fallback of [false, true]) { undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([Feature.PerLanguageBundles]), - getRunnerLogger(true), + new RecordingLogger(), ); t.true(fetchRelease.calledTwice); @@ -1561,7 +1649,7 @@ for (const error of [ undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([Feature.PerLanguageBundles]), - getRunnerLogger(true), + new RecordingLogger(), ), { is: error }, ); @@ -1597,7 +1685,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), - getRunnerLogger(true), + new RecordingLogger(), ), { is: error }, ); @@ -1608,7 +1696,7 @@ test.serial( ); test.serial( - "getEnabledVersionsWithOverlayBaseDatabases returns flag-enabled versions present in cache, sorted desc", + "getEnabledVersionsWithOverlayBaseDatabases returns enabled versions present in cache, sorted desc", async (t) => { sinon.stub(api, "getAutomationID").resolves("test/"); sinon.stub(api, "listActionsCaches").resolves([ @@ -1626,21 +1714,24 @@ test.serial( }, ]); - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), - ); - t.deepEqual(result, [ - { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, - { cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" }, - ]); + await withTmpDir(async (tmpDir) => { + setupActionsVars(tmpDir, tmpDir); + const result = + await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( + overlayMatchEnabledVersions, + ["javascript"], + new RecordingLogger(), + ); + t.deepEqual(result, [ + { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, + { cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" }, + ]); + }); }, ); test.serial( - "getEnabledVersionsWithOverlayBaseDatabases returns empty when no cached version is flag-enabled", + "getEnabledVersionsWithOverlayBaseDatabases returns empty when no cached version is enabled", async (t) => { sinon.stub(api, "getAutomationID").resolves("test/"); sinon.stub(api, "listActionsCaches").resolves([ @@ -1652,8 +1743,7 @@ test.serial( const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( overlayMatchEnabledVersions, ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(result, []); }, @@ -1669,8 +1759,7 @@ const noLanguagesMacro = makeMacro({ const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( overlayMatchEnabledVersions, rawLanguages, - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(result, []); t.assert( @@ -1694,8 +1783,7 @@ test.serial( const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( overlayMatchEnabledVersions, ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(result, []); }, @@ -1714,8 +1802,7 @@ test.serial( const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( overlayMatchEnabledVersions, ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]), - getRunnerLogger(true), + new RecordingLogger(), ); t.deepEqual(result, [ { cliVersion: "2.20.2", tagName: "codeql-bundle-v2.20.2" }, @@ -1723,78 +1810,6 @@ test.serial( }, ); -test.serial( - "getEnabledVersionsWithOverlayBaseDatabases does not list caches when both gates are off", - async (t) => { - const listStub = sinon.stub(api, "listActionsCaches").resolves([]); - - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([]), - getRunnerLogger(true), - ); - t.deepEqual(result, []); - t.assert( - listStub.notCalled, - "Should not list Actions caches when both gating feature flags are off.", - ); - }, -); - -test.serial( - "getEnabledVersionsWithOverlayBaseDatabases dry-run returns empty but lists caches", - async (t) => { - sinon.stub(api, "getAutomationID").resolves("test/"); - const listStub = sinon.stub(api, "listActionsCaches").resolves([ - { - key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"), - }, - ]); - - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([Feature.OverlayAnalysisMatchCodeqlVersionDryRun]), - getRunnerLogger(true), - ); - t.deepEqual( - result, - [], - "Dry-run should return an empty list so the caller falls back.", - ); - t.assert( - listStub.calledOnce, - "Dry-run should still list Actions caches to populate the diagnostic.", - ); - }, -); - -test.serial( - "getEnabledVersionsWithOverlayBaseDatabases match flag wins over dry-run", - async (t) => { - sinon.stub(api, "getAutomationID").resolves("test/"); - sinon.stub(api, "listActionsCaches").resolves([ - { - key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"), - }, - ]); - - const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases( - overlayMatchEnabledVersions, - ["javascript"], - createFeatures([ - Feature.OverlayAnalysisMatchCodeqlVersion, - Feature.OverlayAnalysisMatchCodeqlVersionDryRun, - ]), - getRunnerLogger(true), - ); - t.deepEqual(result, [ - { cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" }, - ]); - }, -); - /** The CLI version that the toolcache cleanup tests download. */ const CLEANUP_CLI_VERSION = "2.21.0"; /** The bundle version that the toolcache cleanup tests download. */ @@ -2093,7 +2108,7 @@ test.serial( env: new ReadOnlyEnv({ [ActionsEnvVars.RUNNER_TOOL_CACHE]: injectedRoot, }), - logger: getRunnerLogger(true), + logger: new RecordingLogger(), }); t.deepEqual(result, { @@ -2248,7 +2263,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), - getRunnerLogger(true), + new RecordingLogger(), ); t.is( @@ -2288,7 +2303,7 @@ test.serial( undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), - getRunnerLogger(true), + new RecordingLogger(), ), { is: error }, ); @@ -2403,7 +2418,7 @@ test.serial( "isToolcacheOnWorkspaceFilesystem assumes a different filesystem when it cannot tell", async (t) => { await withTmpDir(async (tmpDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); setupActionsVars(tmpDir, tmpDir); t.true(toolsDownload.isToolcacheOnWorkspaceFilesystem(logger)); diff --git a/src/setup-codeql.ts b/src/setup-codeql.ts index b4d7416a1..c895f57f7 100644 --- a/src/setup-codeql.ts +++ b/src/setup-codeql.ts @@ -183,29 +183,16 @@ async function findOverridingToolsInCache( /** * Returns the sorted set of enabled versions that have cached overlay-base databases for the - * given languages, or an empty list if neither the `OverlayAnalysisMatchCodeqlVersion` nor the - * `OverlayAnalysisMatchCodeqlVersionDryRun` feature flag is enabled. When only the dry-run flag - * is enabled, this performs the lookup and emits a telemetry diagnostic with the version that - * would have been chosen, but still returns an empty list so the caller falls back. + * given languages. */ export async function getEnabledVersionsWithOverlayBaseDatabases( defaultCliVersion: CodeQLDefaultVersionInfo, rawLanguages: string[] | undefined, - features: FeatureEnablement, logger: Logger, ): Promise { if (rawLanguages === undefined || rawLanguages.length === 0) { return []; } - const isEnabled = await features.getValue( - Feature.OverlayAnalysisMatchCodeqlVersion, - ); - const isDryRun = - !isEnabled && - (await features.getValue(Feature.OverlayAnalysisMatchCodeqlVersionDryRun)); - if (!isEnabled && !isDryRun) { - return []; - } let cachedVersions: string[] | undefined; try { @@ -226,66 +213,35 @@ export async function getEnabledVersionsWithOverlayBaseDatabases( } const cachedVersionsSet = new Set(cachedVersions); - const overlayVersions = defaultCliVersion.enabledVersions.filter((v) => + return defaultCliVersion.enabledVersions.filter((v) => cachedVersionsSet.has(v.cliVersion), ); - - if (overlayVersions.length === 0) { - return []; - } - - const isCachedVersionDifferent = - overlayVersions[0].cliVersion !== - defaultCliVersion.enabledVersions[0].cliVersion; - - if (isCachedVersionDifferent) { - addNoLanguageDiagnostic( - undefined, - makeTelemetryDiagnostic( - "codeql-action/overlay-aware-default-codeql-version", - "Overlay-aware default CodeQL version selection", - { - cachedVersions, - enabledVersions: defaultCliVersion.enabledVersions.map( - (v) => v.cliVersion, - ), - isDryRun, - overlayAwareVersion: overlayVersions[0].cliVersion, - }, - ), - ); - } - - if (isDryRun) { - logger.debug( - `Overlay-aware default CodeQL version selection is running in dry-run mode. Would have used version ${overlayVersions[0].cliVersion}.`, - ); - return []; - } - - return overlayVersions; } /** * Resolves the newest enabled default CLI version that has a cached overlay-base database for the - * relevant languages, if running a Code Scanning analysis for a pull request and one exists. - * Otherwise, falls back to the newest enabled default CLI version. + * relevant languages, if this is a Code Scanning analysis of a pull request, multiple default + * versions are enabled, and such a version exists. Otherwise, falls back to the newest enabled + * default CLI version. */ async function resolveDefaultCliVersion( defaultCliVersion: CodeQLDefaultVersionInfo, rawLanguages: string[] | undefined, useOverlayAwareDefaultCliVersion: boolean, - features: FeatureEnablement, logger: Logger, ): Promise { - if (!useOverlayAwareDefaultCliVersion || !isAnalyzingPullRequest()) { + if ( + !useOverlayAwareDefaultCliVersion || + // Listing the Actions cache can't change which version we use if only one version is enabled. + defaultCliVersion.enabledVersions.length === 1 || + !isAnalyzingPullRequest() + ) { return defaultCliVersion.enabledVersions[0]; } const overlayVersions = await getEnabledVersionsWithOverlayBaseDatabases( defaultCliVersion, rawLanguages, - features, logger, ); if (overlayVersions.length > 0) { @@ -482,7 +438,6 @@ export async function getCodeQLSource( defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, - features, logger, ); cliVersion = version.cliVersion; @@ -505,7 +460,6 @@ export async function getCodeQLSource( defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, - features, logger, ); cliVersion = version.cliVersion; diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index 78704a38a..2446c994c 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -13,7 +13,7 @@ import { setUpFeatureFlagTests } from "./feature-flags/testing-util"; import { UnvalidatedObject, validateSchema } from "./json"; import { makeFromSchema } from "./json/testing-util"; import { BuiltInLanguage } from "./languages"; -import { getRunnerLogger, Logger } from "./logging"; +import { Logger } from "./logging"; import { BundlePlatform, getBundlePlatform } from "./platform"; import * as repository from "./setup/repository"; import * as startProxyExports from "./start-proxy"; @@ -67,7 +67,7 @@ test("getCredentials prefers registriesCredentials over registrySecrets", async ]); const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), registrySecrets, registryCredentials, undefined, @@ -84,7 +84,7 @@ test("getCredentials throws an error when configurations are not an array", asyn t.throws( () => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, registryCredentials, undefined, @@ -103,7 +103,7 @@ test("getCredentials throws error when credential is not an object", async (t) = t.throws( () => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, testCredential, undefined, @@ -124,7 +124,7 @@ test("getCredentials throws error when credential is missing type", async (t) => t.throws( () => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, testCredential, undefined, @@ -147,7 +147,7 @@ test("getCredentials throws error when credential missing host and url", async ( t.throws( () => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, testCredential, undefined, @@ -161,7 +161,7 @@ test("getCredentials throws error when credential missing host and url", async ( test("getCredentials filters by language when specified", async (t) => { const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON(mixedCredentials), BuiltInLanguage.java, @@ -172,7 +172,7 @@ test("getCredentials filters by language when specified", async (t) => { test("getCredentials returns all for a language when specified", async (t) => { const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON([...mixedCredentials, gitSourceCredential]), BuiltInLanguage.go, @@ -192,7 +192,7 @@ test("getCredentials returns all goproxy_servers for Go when specified", async ( ]; const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON(multipleGoproxyServers), BuiltInLanguage.go, @@ -221,7 +221,7 @@ test("getCredentials returns all maven_repositories for Java when specified", as ]; const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON(multipleMavenRepositories), BuiltInLanguage.java, @@ -239,7 +239,7 @@ test("getCredentials returns all credentials when no language specified", async const credentialsInput = toEncodedJSON(mixedCredentials); const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, credentialsInput, undefined, @@ -269,7 +269,7 @@ test("getCredentials throws an error when non-printable characters are used", as t.throws( () => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, credentialsInput, undefined, @@ -300,7 +300,7 @@ for (const oidcSchemaInfo of startProxyExports.oidcSchemas) { t.throws( () => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, credentialsInput, undefined, @@ -324,7 +324,7 @@ test("getCredentials accepts OIDC configurations", (t) => { ); const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON(oidcConfigurations), BuiltInLanguage.csharp, @@ -514,7 +514,7 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => { ]); const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, credentialsInput, BuiltInLanguage.java, @@ -533,7 +533,7 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => { }; t.throws(() => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON([{ ...baseInvalid, "replaces-base": null }]), BuiltInLanguage.java, @@ -541,7 +541,7 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => { ); t.throws(() => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON([{ ...baseInvalid, "replaces-base": 123 }]), BuiltInLanguage.java, @@ -549,7 +549,7 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => { ); t.throws(() => startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, toEncodedJSON([{ ...baseInvalid, "replaces-base": "true" }]), BuiltInLanguage.java, @@ -565,7 +565,7 @@ test("getCredentials returns only ALWAYS_ENABLED_REGISTRY_TYPE credentials for A ]); const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, credentialsInput, BuiltInLanguage.actions, @@ -597,7 +597,7 @@ test("getCredentials always returns ALWAYS_ENABLED_REGISTRY_TYPE credentials for // Test all languages. for (const language of Object.values(BuiltInLanguage)) { const credentials = startProxyExports.getCredentials( - getRunnerLogger(true), + new RecordingLogger(), undefined, credentialsInput, language, diff --git a/src/status-report.test.ts b/src/status-report.test.ts index 2b763da70..004161efa 100644 --- a/src/status-report.test.ts +++ b/src/status-report.test.ts @@ -6,7 +6,6 @@ import * as actionsUtil from "./actions-util"; import { Config } from "./config-utils"; import { EnvVar, RegistryProxyVars } from "./environment"; import { BuiltInLanguage } from "./languages"; -import { getRunnerLogger } from "./logging"; import { ToolsSource } from "./setup-codeql"; import type { Registry } from "./start-proxy"; import { @@ -154,7 +153,7 @@ test.serial("createStatusReportBase", async (t) => { languages: [BuiltInLanguage.java, BuiltInLanguage.swift], }), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ); @@ -203,7 +202,7 @@ test.serial("createStatusReportBase - empty configuration", async (t) => { new Date("May 19, 2023 05:19:00"), {}, { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), ); if (t.truthy(statusReport)) { @@ -225,7 +224,7 @@ test.serial("createStatusReportBase - partial configuration", async (t) => { languages: ["go"], }, { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), ); if (t.truthy(statusReport)) { @@ -248,7 +247,7 @@ test.serial("createStatusReportBase_firstParty", async (t) => { new Date("May 19, 2023 05:19:00"), createTestConfig({}), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ) @@ -264,7 +263,7 @@ test.serial("createStatusReportBase_firstParty", async (t) => { new Date("May 19, 2023 05:19:00"), createTestConfig({}), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ) @@ -281,7 +280,7 @@ test.serial("createStatusReportBase_firstParty", async (t) => { new Date("May 19, 2023 05:19:00"), createTestConfig({}), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ) @@ -297,7 +296,7 @@ test.serial("createStatusReportBase_firstParty", async (t) => { new Date("May 19, 2023 05:19:00"), createTestConfig({}), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ) @@ -314,7 +313,7 @@ test.serial("createStatusReportBase_firstParty", async (t) => { new Date("May 19, 2023 05:19:00"), createTestConfig({}), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ) @@ -330,7 +329,7 @@ test.serial("createStatusReportBase_firstParty", async (t) => { new Date("May 19, 2023 05:19:00"), createTestConfig({}), { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ) @@ -411,7 +410,7 @@ const testCreateInitWithConfigStatusReport = makeMacro({ new Date("May 19, 2023 05:19:00"), config, { numAvailableBytes: 100, numTotalBytes: 500 }, - getRunnerLogger(false), + new RecordingLogger(), "failure cause", "exception stack trace", ); diff --git a/src/tar.test.ts b/src/tar.test.ts index 48f4e866d..99d68b531 100644 --- a/src/tar.test.ts +++ b/src/tar.test.ts @@ -3,9 +3,8 @@ import * as stream from "stream"; import test from "ava"; -import { getRunnerLogger } from "./logging"; import { extractTarZst } from "./tar"; -import { setupTests } from "./testing-utils"; +import { RecordingLogger, setupTests } from "./testing-utils"; import { withTmpDir } from "./util"; setupTests(test); @@ -17,7 +16,7 @@ test("extractTarZst rejects if the input stream errors", async (t) => { archive, path.join(tmpDir, "dest"), { type: "gnu", version: "1.34" }, - getRunnerLogger(true), + new RecordingLogger(), ); archive.destroy( diff --git a/src/testing-utils.ts b/src/testing-utils.ts index 504fa2857..b857d0503 100644 --- a/src/testing-utils.ts +++ b/src/testing-utils.ts @@ -30,7 +30,7 @@ import { featureConfig, FeatureEnablement, } from "./feature-flags"; -import { Logger } from "./logging"; +import { joinMessageStrings, Loggable, LoggableError, Logger } from "./logging"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; import { getBundlePlatform } from "./platform"; import { ActionName } from "./status-report"; @@ -675,12 +675,13 @@ export class RecordingLogger implements Logger { constructor(private readonly logToConsole: boolean = true) {} - private addMessage(level: LogLevel, message: string | Error): void { - this.messages.push({ type: level, message }); + private addMessage(level: LogLevel, message: LoggableError): void { + const joinedMessage = joinMessageStrings(message); + this.messages.push({ type: level, message: joinedMessage }); if (this.logToConsole) { // eslint-disable-next-line no-console - console.debug(message); + console.debug(joinedMessage); } } @@ -701,19 +702,19 @@ export class RecordingLogger implements Logger { return true; } - debug(message: string) { + debug(message: Loggable) { this.addMessage("debug", message); } - info(message: string) { + info(message: Loggable) { this.addMessage("info", message); } - warning(message: string | Error) { + warning(message: LoggableError) { this.addMessage("warning", message); } - error(message: string | Error) { + error(message: LoggableError) { this.addMessage("error", message); } @@ -1048,6 +1049,7 @@ export function createTestConfig(overrides: Partial): Config { overlayModeSetExplicitly: false, repositoryProperties: {}, enableFileCoverageInformation: true, + successfullyInitialised: false, } satisfies Config, overrides, ); @@ -1058,3 +1060,15 @@ export function makeTestToken(length: number = 36) { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return chars.repeat(Math.ceil(length / chars.length)).slice(0, length); } + +/** + * Get a real, newly created CodeQL instance for testing. The instance refers to + * a non-existent placeholder codeql command, so tests that use this function + * should also stub the toolrunner.ToolRunner constructor. + */ +export async function getCodeQLForTesting( + cmd = "codeql-for-testing", + logger: Logger = new RecordingLogger(), +): Promise { + return codeql.getCodeQLForCmd(logger, cmd, false); +} diff --git a/src/tools-download.test.ts b/src/tools-download.test.ts index 407e2ea48..1ba980bbe 100644 --- a/src/tools-download.test.ts +++ b/src/tools-download.test.ts @@ -10,9 +10,8 @@ import { https } from "follow-redirects"; import nock from "nock"; import * as sinon from "sinon"; -import { getRunnerLogger } from "./logging"; import * as tar from "./tar"; -import { setupTests } from "./testing-utils"; +import { RecordingLogger, setupTests } from "./testing-utils"; import { downloadAndExtract } from "./tools-download"; import * as util from "./util"; @@ -34,7 +33,7 @@ test.serial( undefined, {}, undefined, - getRunnerLogger(true), + new RecordingLogger(), ); t.assert(Number.isInteger(statusReport.downloadDurationMs)); @@ -72,7 +71,7 @@ test.serial( undefined, {}, { type: "gnu", version: "1.34" }, - getRunnerLogger(true), + new RecordingLogger(), ); t.assert(Number.isInteger(statusReport.downloadDurationMs)); @@ -105,7 +104,7 @@ test.serial( undefined, {}, { type: "gnu", version: "1.34" }, - getRunnerLogger(true), + new RecordingLogger(), ), { instanceOf: util.HTTPError, @@ -149,7 +148,7 @@ test.serial( undefined, {}, { type: "gnu", version: "1.34" }, - getRunnerLogger(true), + new RecordingLogger(), ); t.assert(Number.isInteger(statusReport.downloadDurationMs)); @@ -189,7 +188,7 @@ test.serial( undefined, {}, { type: "gnu", version: "1.34" }, - getRunnerLogger(true), + new RecordingLogger(), ); t.is( @@ -237,7 +236,7 @@ test.serial( undefined, {}, { type: "gnu", version: "1.34" }, - getRunnerLogger(true), + new RecordingLogger(), ); t.assert(Number.isInteger(statusReport.totalDurationMs)); diff --git a/src/tracer-config.test.ts b/src/tracer-config.test.ts index a3c42d668..f410d47c1 100644 --- a/src/tracer-config.test.ts +++ b/src/tracer-config.test.ts @@ -4,11 +4,12 @@ import * as path from "path"; import test from "ava"; import * as sinon from "sinon"; -import { CodeQL, getCodeQLForTesting } from "./codeql"; +import type { CodeQL } from "./codeql"; import * as configUtils from "./config-utils"; import { BuiltInLanguage } from "./languages"; import { createTestConfig, + getCodeQLForTesting, makeVersionInfo, RecordingLogger, setupTests, diff --git a/src/trap-caching.test.ts b/src/trap-caching.test.ts index 15ab49481..99dc59706 100644 --- a/src/trap-caching.test.ts +++ b/src/trap-caching.test.ts @@ -6,29 +6,26 @@ import test from "ava"; import * as sinon from "sinon"; import * as actionsUtil from "./actions-util"; -import * as apiClient from "./api-client"; import { getTrapCachingExtractorConfigArgs, getTrapCachingExtractorConfigArgsForLang, createStubCodeQL, } from "./codeql"; import * as configUtils from "./config-utils"; -import { Feature } from "./feature-flags"; import * as gitUtils from "./git-utils"; import { BuiltInLanguage } from "./languages"; -import { getRunnerLogger } from "./logging"; import { - createFeatures, + callee, createTestConfig, getRecordingLogger, makeVersionInfo, setupTests, } from "./testing-utils"; import { - cleanupTrapCaches, downloadTrapCaches, getLanguagesSupportingCaching, uploadTrapCaches, + warnIfTrapCacheCleanupEnvVarIsSet, } from "./trap-caching"; import * as util from "./util"; @@ -200,83 +197,27 @@ test.serial( }, ); -test.serial("cleanup removes only old CodeQL TRAP caches", async (t) => { - await util.withTmpDir(async (tmpDir) => { - // This config specifies that we are analyzing JavaScript and Ruby, but not Swift. - const config = getTestConfigWithTempDir(tmpDir); - - sinon.stub(gitUtils, "getRef").resolves("refs/heads/main"); - sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true); - const listStub = sinon.stub(apiClient, "listActionsCaches").resolves([ - // Should be kept, since it's not relevant to CodeQL. In reality, the API shouldn't return - // this in the first place, but this is a defensive check. - { - id: 1, - key: "some-other-key", - created_at: "2024-05-23T14:25:00Z", - size_in_bytes: 100 * 1024 * 1024, - }, - // Should be kept, since it's the newest TRAP cache for JavaScript - { - id: 2, - key: "codeql-trap-1-2.0.0-javascript-newest", - created_at: "2024-04-23T14:25:00Z", - size_in_bytes: 50 * 1024 * 1024, - }, - // Should be cleaned up - { - id: 3, - key: "codeql-trap-1-2.0.0-javascript-older", - created_at: "2024-03-22T14:25:00Z", - size_in_bytes: 200 * 1024 * 1024, - }, - // Should be cleaned up - { - id: 4, - key: "codeql-trap-1-2.0.0-javascript-oldest", - created_at: "2024-02-21T14:25:00Z", - size_in_bytes: 300 * 1024 * 1024, - }, - // Should be kept, since it's the newest TRAP cache for Ruby - { - id: 5, - key: "codeql-trap-1-2.0.0-ruby-newest", - created_at: "2024-02-20T14:25:00Z", - size_in_bytes: 300 * 1024 * 1024, - }, - // Should be kept, since we aren't analyzing Swift - { - id: 6, - key: "codeql-trap-1-2.0.0-swift-newest", - created_at: "2024-02-22T14:25:00Z", - size_in_bytes: 300 * 1024 * 1024, - }, - // Should be kept, since we aren't analyzing Swift - { - id: 7, - key: "codeql-trap-1-2.0.0-swift-older", - created_at: "2024-02-21T14:25:00Z", - size_in_bytes: 300 * 1024 * 1024, - }, - ]); - - const deleteStub = sinon.stub(apiClient, "deleteActionsCache").resolves(); - - const statusReport = await cleanupTrapCaches( - config, - createFeatures([Feature.CleanupTrapCaches]), - getRunnerLogger(true), +// `CODEQL_ACTION_CLEANUP_TRAP_CACHES` has no effect with either value, so we warn even when it's +// set to `false`. +for (const value of ["true", "false"]) { + test(`warnIfTrapCacheCleanupEnvVarIsSet warns when CODEQL_ACTION_CLEANUP_TRAP_CACHES is '${value}'`, async (t) => { + const target = callee(warnIfTrapCacheCleanupEnvVarIsSet) + .withEnv((env) => env.set("CODEQL_ACTION_CLEANUP_TRAP_CACHES", value)) + .withArgs() + .logs( + t, + "CODEQL_ACTION_CLEANUP_TRAP_CACHES environment variable no longer has any effect", + ); + await target.passes(t.is, undefined); + t.deepEqual( + target.getLogger().messages.map((m) => m.type), + ["warning"], ); - - t.is(listStub.callCount, 1); - t.assert(listStub.calledWithExactly("codeql-trap", "refs/heads/main")); - - t.deepEqual(statusReport, { - trap_cache_cleanup_size_bytes: 500 * 1024 * 1024, - }); - - t.is(deleteStub.callCount, 2); - t.assert(deleteStub.calledWithExactly(3)); - t.assert(deleteStub.calledWithExactly(4)); }); +} + +test("warnIfTrapCacheCleanupEnvVarIsSet doesn't warn when CODEQL_ACTION_CLEANUP_TRAP_CACHES is unset", async (t) => { + const target = callee(warnIfTrapCacheCleanupEnvVarIsSet).withArgs(); + await target.passes(t.is, undefined); + t.deepEqual(target.getLogger().messages, []); }); diff --git a/src/trap-caching.ts b/src/trap-caching.ts index d93936832..58d96890f 100644 --- a/src/trap-caching.ts +++ b/src/trap-caching.ts @@ -3,22 +3,14 @@ import * as path from "path"; import * as actionsCache from "@actions/cache"; +import { type ActionState } from "./action-common"; import * as actionsUtil from "./actions-util"; -import * as apiClient from "./api-client"; import { type CodeQL } from "./codeql"; import { type Config } from "./config-utils"; -import { DocUrl } from "./doc-url"; -import { Feature, FeatureEnablement } from "./feature-flags"; import * as gitUtils from "./git-utils"; import { Language } from "./languages"; import { Logger } from "./logging"; -import { - asHTTPError, - getEnv, - getErrorMessage, - tryGetFolderBytes, - waitForResultWithTimeLimit, -} from "./util"; +import { getEnv, tryGetFolderBytes, waitForResultWithTimeLimit } from "./util"; // This constant should be bumped if we make a breaking change // to how the CodeQL Action stores or retrieves the TRAP cache, @@ -178,113 +170,25 @@ export async function uploadTrapCaches( return true; } -export interface TrapCacheCleanupStatusReport { - trap_cache_cleanup_error?: string; - trap_cache_cleanup_size_bytes?: number; - trap_cache_cleanup_skipped_because?: string; -} - -export async function cleanupTrapCaches( - config: Config, - features: FeatureEnablement, - logger: Logger, -): Promise { - const env = getEnv(); - - if (!(await features.getValue(Feature.CleanupTrapCaches))) { - return { - trap_cache_cleanup_skipped_because: "feature disabled", - }; - } - logger.warning( - "TRAP cache cleanup is deprecated and will be removed in May 2026. " + - "We recommend instead disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action.", - ); - if (!(await gitUtils.isAnalyzingDefaultBranch(env, config.repositoryRoot))) { - return { - trap_cache_cleanup_skipped_because: "not analyzing default branch", - }; - } - - try { - let totalBytesCleanedUp = 0; - - const allCaches = await apiClient.listActionsCaches( - CODEQL_TRAP_CACHE_PREFIX, - await gitUtils.getRef(env, config.repositoryRoot), +/** + * Warns if the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable is set to a non-empty + * value. That variable controlled the removed TRAP cache cleanup feature, so setting it has no + * effect. + */ +export function warnIfTrapCacheCleanupEnvVarIsSet({ + env, + logger, +}: ActionState<["Logger", "ReadOnlyEnv"]>): void { + if (env.getOptional("CODEQL_ACTION_CLEANUP_TRAP_CACHES") !== undefined) { + logger.warning( + "TRAP cache cleanup has been removed, so the CODEQL_ACTION_CLEANUP_TRAP_CACHES environment " + + "variable no longer has any effect and can be removed from your workflow. If you used TRAP " + + "cache cleanup to limit Actions cache usage, we recommend disabling TRAP caching instead by " + + "passing the `trap-caching: false` input to the `init` Action.", ); - - for (const language of config.languages) { - if (config.trapCaches[language]) { - const cachesToRemove = await getTrapCachesForLanguage( - allCaches, - language, - logger, - ); - // Dates returned by the API are in ISO 8601 format, so we can sort them lexicographically - cachesToRemove.sort((a, b) => a.created_at.localeCompare(b.created_at)); - // Keep the most recent cache - const mostRecentCache = cachesToRemove.pop(); - logger.debug( - `Keeping most recent TRAP cache (${JSON.stringify(mostRecentCache)})`, - ); - - if (cachesToRemove.length === 0) { - logger.info(`No TRAP caches to clean up for ${language}.`); - continue; - } - - for (const cache of cachesToRemove) { - logger.debug(`Cleaning up TRAP cache (${JSON.stringify(cache)})`); - await apiClient.deleteActionsCache(cache.id); - } - const bytesCleanedUp = cachesToRemove.reduce( - (acc, item) => acc + item.size_in_bytes, - 0, - ); - totalBytesCleanedUp += bytesCleanedUp; - const megabytesCleanedUp = (bytesCleanedUp / (1024 * 1024)).toFixed(2); - logger.info( - `Cleaned up ${megabytesCleanedUp} MiB of old TRAP caches for ${language}.`, - ); - } - } - return { trap_cache_cleanup_size_bytes: totalBytesCleanedUp }; - } catch (e) { - if (asHTTPError(e)?.status === 403) { - logger.warning( - "Could not cleanup TRAP caches as the token did not have the required permissions. " + - 'To clean up TRAP caches, ensure the token has the "actions:write" permission. ' + - `See ${DocUrl.ASSIGNING_PERMISSIONS_TO_JOBS} for more information.`, - ); - } else { - logger.info(`Failed to cleanup TRAP caches, continuing. Details: ${e}`); - } - return { trap_cache_cleanup_error: getErrorMessage(e) }; } } -async function getTrapCachesForLanguage( - allCaches: apiClient.ActionsCacheItem[], - language: Language, - logger: Logger, -): Promise>> { - logger.debug(`Listing TRAP caches for ${language}`); - - for (const cache of allCaches) { - if (!cache.created_at || !cache.id || !cache.key || !cache.size_in_bytes) { - throw new Error( - "An unexpected cache item was returned from the API that was missing one or " + - `more required fields: ${JSON.stringify(cache)}`, - ); - } - } - - return allCaches.filter((cache) => { - return cache.key?.includes(`-${language}-`); - }) as Array>; -} - export async function getLanguagesSupportingCaching( codeql: CodeQL, languages: Language[], diff --git a/src/upload-lib.test.ts b/src/upload-lib.test.ts index 2c27d56c1..ebfc5cd40 100644 --- a/src/upload-lib.test.ts +++ b/src/upload-lib.test.ts @@ -10,9 +10,9 @@ import * as analyses from "./analyses"; import { AnalysisKind, CodeQuality, CodeScanning } from "./analyses"; import * as api from "./api-client"; import * as diffUtils from "./diff-informed-analysis-utils"; -import { getRunnerLogger, Logger } from "./logging"; +import { Logger } from "./logging"; import * as sarif from "./sarif"; -import { setupTests } from "./testing-utils"; +import { RecordingLogger, setupTests } from "./testing-utils"; import * as uploadLib from "./upload-lib"; import { UploadPayload } from "./upload-lib/types"; import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util"; @@ -29,7 +29,7 @@ test.serial("validateSarifFileSchema - valid", (t) => { uploadLib.validateSarifFileSchema( uploadLib.readSarifFileOrThrow(inputFile), inputFile, - getRunnerLogger(true), + new RecordingLogger(), ), ); }); @@ -40,7 +40,7 @@ test.serial("validateSarifFileSchema - invalid", (t) => { uploadLib.validateSarifFileSchema( uploadLib.readSarifFileOrThrow(inputFile), inputFile, - getRunnerLogger(true), + new RecordingLogger(), ), ); }); @@ -179,7 +179,7 @@ test.serial("finding SARIF files", async (t) => { } const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths( - getRunnerLogger(true), + new RecordingLogger(), tmpDir, ); @@ -200,7 +200,7 @@ test.serial("getGroupedSarifFilePaths - Risk Assessment files", async (t) => { fs.writeFileSync(sarifPath, ""); const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths( - getRunnerLogger(true), + new RecordingLogger(), sarifPath, ); @@ -218,7 +218,7 @@ test.serial("getGroupedSarifFilePaths - Code Quality file", async (t) => { fs.writeFileSync(sarifPath, ""); const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths( - getRunnerLogger(true), + new RecordingLogger(), sarifPath, ); @@ -236,7 +236,7 @@ test.serial("getGroupedSarifFilePaths - Code Scanning file", async (t) => { fs.writeFileSync(sarifPath, ""); const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths( - getRunnerLogger(true), + new RecordingLogger(), sarifPath, ); @@ -254,7 +254,7 @@ test.serial("getGroupedSarifFilePaths - Other file", async (t) => { fs.writeFileSync(sarifPath, ""); const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths( - getRunnerLogger(true), + new RecordingLogger(), sarifPath, ); @@ -932,7 +932,7 @@ function uploadPayloadFixtures(analysis: analyses.AnalysisConfig) { owner: mockData.owner, repo: mockData.repo, }, - getRunnerLogger(true), + new RecordingLogger(), analysis, ); @@ -1019,7 +1019,7 @@ function runFilterAlertsByDiffRange( diffRanges: diffUtils.DiffThunkRange[], ): Partial { sinon.stub(diffUtils, "readDiffRangesJsonFile").returns(diffRanges); - return uploadLib.filterAlertsByDiffRange(getRunnerLogger(true), input); + return uploadLib.filterAlertsByDiffRange(new RecordingLogger(), input); } test.serial( diff --git a/src/upload-sarif.test.ts b/src/upload-sarif.test.ts index 033be70d0..a1ea5dbee 100644 --- a/src/upload-sarif.test.ts +++ b/src/upload-sarif.test.ts @@ -5,8 +5,12 @@ import test, { ExecutionContext } from "ava"; import * as sinon from "sinon"; import { AnalysisKind, getAnalysisConfig } from "./analyses"; -import { getRunnerLogger } from "./logging"; -import { createFeatures, makeMacro, setupTests } from "./testing-utils"; +import { + createFeatures, + makeMacro, + RecordingLogger, + setupTests, +} from "./testing-utils"; import { UploadResult } from "./upload-lib"; import * as uploadLib from "./upload-lib"; import { postProcessAndUploadSarif } from "./upload-sarif"; @@ -52,7 +56,7 @@ const postProcessAndUploadSarifMacro = makeMacro({ expectedResult: Partial>, ) => { await util.withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const testPath = sarifPath(tempDir); const features = createFeatures([]); @@ -210,7 +214,7 @@ test.serial( "postProcessAndUploadSarif doesn't upload if upload is disabled", async (t) => { await util.withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const features = createFeatures([]); const toFullPath = (filename: string) => path.join(tempDir, filename); @@ -244,7 +248,7 @@ test.serial( "postProcessAndUploadSarif writes post-processed SARIF files if output directory is provided", async (t) => { await util.withTmpDir(async (tempDir) => { - const logger = getRunnerLogger(true); + const logger = new RecordingLogger(); const features = createFeatures([]); const toFullPath = (filename: string) => path.join(tempDir, filename); diff --git a/src/util.test.ts b/src/util.test.ts index e7a7d3abd..af69171f3 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -10,8 +10,7 @@ import * as sinon from "sinon"; import * as api from "./api-client"; import { EnvVar } from "./environment"; -import { getRunnerLogger } from "./logging"; -import { getTestEnv, setupTests } from "./testing-utils"; +import { RecordingLogger, setupTests, getTestEnv } from "./testing-utils"; import * as util from "./util"; setupTests(test); @@ -92,7 +91,7 @@ for (const { test("getMemoryFlag() throws if the ram input is < 0 or NaN", async (t) => { for (const input of ["-1", "hello!"]) { - t.throws(() => util.getMemoryFlag(input, getRunnerLogger(true))); + t.throws(() => util.getMemoryFlag(input, new RecordingLogger())); } }); @@ -109,13 +108,13 @@ test("getThreadsFlag() should return the correct --threads flag", (t) => { ]; for (const [input, expectedFlag] of tests) { - const flag = util.getThreadsFlag(input, getRunnerLogger(true)); + const flag = util.getThreadsFlag(input, new RecordingLogger()); t.deepEqual(flag, expectedFlag); } }); test("getThreadsFlag() throws if the threads input is not an integer", (t) => { - t.throws(() => util.getThreadsFlag("hello!", getRunnerLogger(true))); + t.throws(() => util.getThreadsFlag("hello!", new RecordingLogger())); }); test.serial( @@ -385,7 +384,7 @@ test("getCgroupCpuCountFromCpus calculates the number of CPUs correctly", async const testCpuFile = `${tmpDir}/cpus-file`; fs.writeFileSync(testCpuFile, "1, 9-10\n", "utf-8"); t.deepEqual( - util.getCgroupCpuCountFromCpus(testCpuFile, getRunnerLogger(true)), + util.getCgroupCpuCountFromCpus(testCpuFile, new RecordingLogger()), 3, ); }); @@ -396,7 +395,7 @@ test("getCgroupCpuCountFromCpus returns undefined if the CPU file doesn't exist" const testCpuFile = `${tmpDir}/cpus-file`; t.false(fs.existsSync(testCpuFile)); t.deepEqual( - util.getCgroupCpuCountFromCpus(testCpuFile, getRunnerLogger(true)), + util.getCgroupCpuCountFromCpus(testCpuFile, new RecordingLogger()), undefined, ); }); @@ -407,7 +406,7 @@ test("getCgroupCpuCountFromCpus returns undefined if the CPU file exists but is const testCpuFile = `${tmpDir}/cpus-file`; fs.writeFileSync(testCpuFile, "\n", "utf-8"); t.deepEqual( - util.getCgroupCpuCountFromCpus(testCpuFile, getRunnerLogger(true)), + util.getCgroupCpuCountFromCpus(testCpuFile, new RecordingLogger()), undefined, ); }); @@ -417,7 +416,7 @@ test.serial( "checkDiskUsage succeeds and produces positive numbers", async (t) => { process.env["GITHUB_WORKSPACE"] = os.tmpdir(); - const diskUsage = await util.checkDiskUsage(getRunnerLogger(true)); + const diskUsage = await util.checkDiskUsage(new RecordingLogger()); if (t.truthy(diskUsage)) { t.true(diskUsage.numAvailableBytes > 0); t.true(diskUsage.numTotalBytes > 0); diff --git a/src/util.ts b/src/util.ts index ba0c3c7d4..b68824d6e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -900,6 +900,21 @@ export function getErrorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } +/** + * Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}. + * We only typically care about the `code` property, so that's what we check for here. + */ +export function isErrnoException( + error: unknown, +): error is NodeJS.ErrnoException { + return ( + typeof error === "object" && + error !== null && + "code" in error && + typeof error.code === "string" + ); +} + export function prettyPrintPack(pack: Pack) { return `${pack.name}${pack.version ? `@${pack.version}` : ""}${ pack.path ? `:${pack.path}` : "" diff --git a/src/workflow.test.ts b/src/workflow.test.ts index 2373e8bd1..c7b7aebc0 100644 --- a/src/workflow.test.ts +++ b/src/workflow.test.ts @@ -3,10 +3,11 @@ import * as yaml from "js-yaml"; import * as sinon from "sinon"; import * as actionsUtil from "./actions-util"; -import { createStubCodeQL, getCodeQLForTesting } from "./codeql"; +import { createStubCodeQL } from "./codeql"; import { ActionsEnvVars, EnvVar } from "./environment"; import { checkExpectedLogMessages, + getCodeQLForTesting, createTestConfig, getRecordingLogger, getTestEnv, diff --git a/unreleased-change-notes/remove-trap-cache-cleanup.md b/unreleased-change-notes/remove-trap-cache-cleanup.md new file mode 100644 index 000000000..b3cb822b8 --- /dev/null +++ b/unreleased-change-notes/remove-trap-cache-cleanup.md @@ -0,0 +1 @@ +- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable has been removed, as announced in the release notes for CodeQL Action 4.35.2. The `analyze` Action now warns if this environment variable is set. If you used TRAP cache cleanup to limit Actions cache usage, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#4243](https://github.com/github/codeql-action/pull/4243)