mirror of
https://github.com/github/codeql-action.git
synced 2026-10-21 02:01:27 +00:00
Compare commits
273 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42947a3404 | ||
|
|
771aacb2ad | ||
|
|
0692998c5c | ||
|
|
57a845ca5d | ||
|
|
2b5f0e6784 | ||
|
|
77571dc418 | ||
|
|
f3712979fa | ||
|
|
2a073c8c9a | ||
|
|
5f47b57df6 | ||
|
|
272e6d3b25 | ||
|
|
4da5eae9f4 | ||
|
|
8b2ec470a0 | ||
|
|
c4dd10e44a | ||
|
|
2ba473f582 | ||
|
|
b4d147f9bc | ||
|
|
f6580d2b8f | ||
|
|
5e7686d132 | ||
|
|
324dd69df0 | ||
|
|
200d550080 | ||
|
|
e60ea984bd | ||
|
|
01b30f9112 | ||
|
|
f44d029dbc | ||
|
|
a7b3986136 | ||
|
|
5667eabe3b | ||
|
|
2c538e6403 | ||
|
|
fa3b8d4aca | ||
|
|
85722ca3f8 | ||
|
|
93539ac81b | ||
|
|
a2225dced3 | ||
|
|
4d950cddc8 | ||
|
|
b7ae332965 | ||
|
|
a2983b8bed | ||
|
|
30d6d12c17 | ||
|
|
14e2e60bf8 | ||
|
|
5d3f01e177 | ||
|
|
3f405c7726 | ||
|
|
19d11a3383 | ||
|
|
4187e74d05 | ||
|
|
ae71ce6a2d | ||
|
|
d2305d0114 | ||
|
|
994a21a0b3 | ||
|
|
0c52f3f086 | ||
|
|
933494d539 | ||
|
|
08d09a53f0 | ||
|
|
74406f0856 | ||
|
|
8dfbacbba5 | ||
|
|
ab2931b320 | ||
|
|
c87e7fae54 | ||
|
|
5a1ba3bdf6 | ||
|
|
b7351df727 | ||
|
|
27e669d3d6 | ||
|
|
1138fefa2b | ||
|
|
3fa858af16 | ||
|
|
4b646a0e5d | ||
|
|
44c5914b66 | ||
|
|
0d531cfd92 | ||
|
|
02c5e83432 | ||
|
|
af1b30ee40 | ||
|
|
530c909a55 | ||
|
|
0507814306 | ||
|
|
157d34d3cf | ||
|
|
b8c8426f7c | ||
|
|
411c4c9a36 | ||
|
|
6153c156f7 | ||
|
|
66185e671d | ||
|
|
51edd47ebb | ||
|
|
a90bc064bf | ||
|
|
ac8484ec8f | ||
|
|
dd903d2e4f | ||
|
|
0d313a3b02 | ||
|
|
ee98575270 | ||
|
|
7d9a983ed4 | ||
|
|
6016976cee | ||
|
|
72c906de81 | ||
|
|
d77b13a0df | ||
|
|
e816d2e818 | ||
|
|
b6eff7b44a | ||
|
|
3b37ae1c9b | ||
|
|
2587a4beb2 | ||
|
|
c02b552b1a | ||
|
|
03e4368ac7 | ||
|
|
bc887cab05 | ||
|
|
a9739a6bf0 | ||
|
|
2be5c61e31 | ||
|
|
9b64c8b76e | ||
|
|
c45c87a4c5 | ||
|
|
458d36d7d4 | ||
|
|
70a1165f9a | ||
|
|
4b79f1ba10 | ||
|
|
b2dd803f6e | ||
|
|
0e6a98bb2f | ||
|
|
cb4dbcd837 | ||
|
|
cad7075882 | ||
|
|
7fd177fa68 | ||
|
|
ea18e99ca3 | ||
|
|
19f4654991 | ||
|
|
8f15c6f1ad | ||
|
|
349cedea3b | ||
|
|
f0ffd5714c | ||
|
|
0daab03d71 | ||
|
|
30f0c9d081 | ||
|
|
3530cdd15e | ||
|
|
8280251823 | ||
|
|
e511c7b2e0 | ||
|
|
50052a22af | ||
|
|
ce64ddcb0d | ||
|
|
c186c7b484 | ||
|
|
8bcc8f23a2 | ||
|
|
834786ac9b | ||
|
|
047c547345 | ||
|
|
7ca215887b | ||
|
|
5c8a8a642e | ||
|
|
646729a1e2 | ||
|
|
c983cb8e74 | ||
|
|
557b58c47c | ||
|
|
8bb5bdb9fd | ||
|
|
4d2fde9e07 | ||
|
|
13efb23391 | ||
|
|
b1a5f00cf1 | ||
|
|
124f6eec3b | ||
|
|
a88fb3cde0 | ||
|
|
57d591c67c | ||
|
|
dba1849cf2 | ||
|
|
ebcb5b36de | ||
|
|
97fd992228 | ||
|
|
95a562052b | ||
|
|
ae8b37eb31 | ||
|
|
d75030c604 | ||
|
|
a777590c0f | ||
|
|
dfad8f8ebc | ||
|
|
c146cd2193 | ||
|
|
3fef31e9b5 | ||
|
|
3d7478b23a | ||
|
|
f874badee7 | ||
|
|
1c3843e226 | ||
|
|
603b797f8b | ||
|
|
9ed0d758ce | ||
|
|
2da877a512 | ||
|
|
4ccf9a5deb | ||
|
|
e50ab6dc1d | ||
|
|
ee6db5e4f5 | ||
|
|
820e3160e2 | ||
|
|
dabb34c95a | ||
|
|
c0e7770e36 | ||
|
|
dbc2ac9b7a | ||
|
|
e61b8b4cf5 | ||
|
|
b7ebceaf1a | ||
|
|
ae9ef3a1d2 | ||
|
|
b58ecf644d | ||
|
|
e3632d0ee3 | ||
|
|
cdcb071e67 | ||
|
|
177cb24be1 | ||
|
|
2427cfc4a9 | ||
|
|
45580472a5 | ||
|
|
a3696cdbdf | ||
|
|
147ec67ee5 | ||
|
|
acb91bd91f | ||
|
|
88d9aba91d | ||
|
|
72edeaa05b | ||
|
|
f5c2471be7 | ||
|
|
70a71a57dd | ||
|
|
676a1ceb5c | ||
|
|
e127ec2647 | ||
|
|
f5e6f52190 | ||
|
|
e2a90d3e23 | ||
|
|
b5ebac6f4c | ||
|
|
bb159524f9 | ||
|
|
6b68dd5d27 | ||
|
|
24e739f51f | ||
|
|
e5a63de15c | ||
|
|
c2d57b0fc7 | ||
|
|
2588666de8 | ||
|
|
fd13ffa22c | ||
|
|
ce04bc5815 | ||
|
|
27eb5f56eb | ||
|
|
44f67f0887 | ||
|
|
5d24c86a89 | ||
|
|
439137e1b5 | ||
|
|
f5ab452606 | ||
|
|
56c8e1c8a8 | ||
|
|
4f5ca6f9a5 | ||
|
|
92f3a2822b | ||
|
|
e9bf22fb0e | ||
|
|
38e701f46e | ||
|
|
c9e0329cc4 | ||
|
|
9ffacc75e8 | ||
|
|
21961f3b6f | ||
|
|
8233700206 | ||
|
|
23e84a39f0 | ||
|
|
4bdb89f480 | ||
|
|
ed629463c0 | ||
|
|
6252d140cd | ||
|
|
84cf4b44bb | ||
|
|
52cebb523a | ||
|
|
fc6e643fe1 | ||
|
|
f8ee3fcc9a | ||
|
|
45c373516f | ||
|
|
311b632b9d | ||
|
|
d300581d5e | ||
|
|
7348876640 | ||
|
|
4f34645a82 | ||
|
|
e7c7a2d323 | ||
|
|
f47c8e6a9b | ||
|
|
74951318a2 | ||
|
|
5676d1f64a | ||
|
|
c1bea80e56 | ||
|
|
2d9c0b97af | ||
|
|
827017f97b | ||
|
|
bffd034ab1 | ||
|
|
817dbfb39b | ||
|
|
793f7006bb | ||
|
|
d2e9832330 | ||
|
|
c2e4b7785f | ||
|
|
66d7f51a10 | ||
|
|
497990dfed | ||
|
|
89cb79a131 | ||
|
|
dbf6819ebd | ||
|
|
5af51f4048 | ||
|
|
e439418aab | ||
|
|
249860e323 | ||
|
|
d3ced5c96c | ||
|
|
c12d7c1f2d | ||
|
|
2e2a1cf1ef | ||
|
|
e2cca77d06 | ||
|
|
801a18bea6 | ||
|
|
1c715a714c | ||
|
|
c3d42c5d08 | ||
|
|
9031cd9330 | ||
|
|
f58938aee2 | ||
|
|
1f1c162805 | ||
|
|
7ab96a0e6f | ||
|
|
e3cb86275a | ||
|
|
f94c9befff | ||
|
|
e5971bdba6 | ||
|
|
c5a9d29dc9 | ||
|
|
9f1109665d | ||
|
|
f8f60f3a2b | ||
|
|
f4d10b9ef7 | ||
|
|
5d5cd550d3 | ||
|
|
c6eb09db21 | ||
|
|
09db9044dc | ||
|
|
d3cd47d8d6 | ||
|
|
8e9caa5100 | ||
|
|
23a6333b88 | ||
|
|
c503cb4fbb | ||
|
|
c2805e0a04 | ||
|
|
c0d3370b54 | ||
|
|
ddd0dc746a | ||
|
|
2f607936ce | ||
|
|
37e7dfbaa0 | ||
|
|
d198d2fabf | ||
|
|
9e3918e481 | ||
|
|
7dd1575dac | ||
|
|
28fc48d83c | ||
|
|
12c6008004 | ||
|
|
d3019effb0 | ||
|
|
42213152a8 | ||
|
|
e677e67801 | ||
|
|
5f3f3164ad | ||
|
|
ba42101490 | ||
|
|
f11af5849b | ||
|
|
ba5430dc86 | ||
|
|
13e883e119 | ||
|
|
755f44910c | ||
|
|
948223fe01 | ||
|
|
a37add20d4 | ||
|
|
ab163cf08b | ||
|
|
319796f085 | ||
|
|
bd1ac56295 | ||
|
|
a8d1ac45b9 | ||
|
|
c551c50310 | ||
|
|
01f1a24033 | ||
|
|
b264e15259 |
2
.github/actions/check-sarif/action.yml
vendored
2
.github/actions/check-sarif/action.yml
vendored
@@ -16,5 +16,5 @@ inputs:
|
||||
Comma separated list of query ids that should NOT be included in this SARIF file.
|
||||
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: index.js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Verify that the best-effort debug artifact scan completed
|
||||
description: Verifies that the best-effort debug artifact scan completed successfully during tests
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: index.js
|
||||
post: post.js
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
applyTo: "CHANGELOG.md,src/defaults.json,lib/defaults.json,src/api-compatibility.json"
|
||||
---
|
||||
|
||||
# Merging release, mergeback, and backport PRs
|
||||
|
||||
The release process creates a cascade of PRs (`main` → `releases/vN`, then
|
||||
`releases/vN` → `main` mergeback, then `releases/vN` → `releases/v(N-1)`
|
||||
backport). These PRs reliably touch `CHANGELOG.md`, `src/defaults.json` /
|
||||
`lib/defaults.json` (bundle/CLI version bump), and `src/api-compatibility.json`.
|
||||
|
||||
Such PRs **must be merged with a merge commit**. Never squash or rebase, as
|
||||
that breaks the branch linkage the release automation relies on.
|
||||
|
||||
When arming auto-merge on these PRs, use `--merge` (e.g. `gh pr merge --merge`),
|
||||
not `--squash` or `--rebase`.
|
||||
2
.github/workflows/__all-platform-bundle.yml
generated
vendored
2
.github/workflows/__all-platform-bundle.yml
generated
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
version: nightly-latest
|
||||
- os: macos-latest-xlarge
|
||||
- os: macos-latest
|
||||
version: nightly-latest
|
||||
- os: windows-latest
|
||||
version: nightly-latest
|
||||
|
||||
2
.github/workflows/__autobuild-direct-tracing-with-working-dir.yml
generated
vendored
2
.github/workflows/__autobuild-direct-tracing-with-working-dir.yml
generated
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
with:
|
||||
java-version: ${{ inputs.java-version || '17' }}
|
||||
distribution: temurin
|
||||
|
||||
2
.github/workflows/__build-mode-autobuild.yml
generated
vendored
2
.github/workflows/__build-mode-autobuild.yml
generated
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
with:
|
||||
java-version: ${{ inputs.java-version || '17' }}
|
||||
distribution: temurin
|
||||
|
||||
106
.github/workflows/__linux-arm64.yml
generated
vendored
106
.github/workflows/__linux-arm64.yml
generated
vendored
@@ -1,106 +0,0 @@
|
||||
# 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 - Linux Arm64
|
||||
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:
|
||||
dotnet-version:
|
||||
type: string
|
||||
description: The version of .NET to install
|
||||
required: false
|
||||
default: 9.x
|
||||
go-version:
|
||||
type: string
|
||||
description: The version of Go to install
|
||||
required: false
|
||||
default: '>=1.21.0'
|
||||
workflow_call:
|
||||
inputs:
|
||||
dotnet-version:
|
||||
type: string
|
||||
description: The version of .NET to install
|
||||
required: false
|
||||
default: 9.x
|
||||
go-version:
|
||||
type: string
|
||||
description: The version of Go to install
|
||||
required: false
|
||||
default: '>=1.21.0'
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
concurrency:
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
|
||||
group: linux-arm64-${{github.ref}}-${{inputs.dotnet-version}}-${{inputs.go-version}}
|
||||
jobs:
|
||||
linux-arm64:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04-arm
|
||||
version: nightly-latest
|
||||
name: Linux Arm64
|
||||
if: github.triggering_actor != 'dependabot[bot]'
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: read
|
||||
timeout-minutes: 45
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install .NET
|
||||
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
|
||||
with:
|
||||
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version: ${{ inputs.go-version || '>=1.21.0' }}
|
||||
cache: false
|
||||
- name: Prepare test
|
||||
id: prepare-test
|
||||
uses: ./.github/actions/prepare-test
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
use-all-platform-bundle: 'false'
|
||||
setup-kotlin: 'true'
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: ${{ env.LANGUAGES }}
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
- name: Build code
|
||||
run: ./build.sh
|
||||
- uses: ./../action/analyze
|
||||
with:
|
||||
upload-database: false
|
||||
- name: Assert databases exist
|
||||
run: |
|
||||
cd "$RUNNER_TEMP/codeql_databases"
|
||||
for lang in ${LANGUAGES//,/ }; do
|
||||
if [[ ! -d "$lang" ]]; then
|
||||
echo "Did not find a database for $lang"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found database for $lang"
|
||||
done
|
||||
env:
|
||||
LANGUAGES: cpp,csharp,go,java,javascript,python,ruby
|
||||
CODEQL_ACTION_TEST_MODE: true
|
||||
1
.github/workflows/__multi-language-autodetect.yml
generated
vendored
1
.github/workflows/__multi-language-autodetect.yml
generated
vendored
@@ -191,6 +191,5 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES: true
|
||||
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
|
||||
CODEQL_ACTION_TEST_MODE: true
|
||||
|
||||
6
.github/workflows/__swift-custom-build.yml
generated
vendored
6
.github/workflows/__swift-custom-build.yml
generated
vendored
@@ -54,11 +54,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-latest-xlarge
|
||||
- os: macos-latest
|
||||
version: linked
|
||||
- os: macos-latest-xlarge
|
||||
- os: macos-latest
|
||||
version: default
|
||||
- os: macos-latest-xlarge
|
||||
- os: macos-latest
|
||||
version: nightly-latest
|
||||
name: Swift analysis using a custom build command
|
||||
if: github.triggering_actor != 'dependabot[bot]'
|
||||
|
||||
34
.github/workflows/pr-checks.yml
vendored
34
.github/workflows/pr-checks.yml
vendored
@@ -67,43 +67,11 @@ jobs:
|
||||
|
||||
- name: Upload sarif
|
||||
uses: ./upload-sarif
|
||||
# The merge queue deletes its `gh-readonly-queue` ref as soon as the queue entry resolves,
|
||||
# so uploading against it races with that deletion. Both the `merge_group` run and the
|
||||
# paired `push` run that the queue branch creates use that ref, so gate on the ref itself
|
||||
# rather than the event. The same results are uploaded by the `pull_request` run and again
|
||||
# by the `push` run on `main`.
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 24 && !startsWith(github.ref, 'refs/heads/gh-readonly-queue/')
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 24
|
||||
with:
|
||||
sarif_file: eslint.sarif
|
||||
category: eslint
|
||||
|
||||
changetool-tests:
|
||||
name: changetool unit tests
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-slim
|
||||
timeout-minutes: 10
|
||||
|
||||
concurrency:
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
|
||||
group: pr-checks-changetool-tests-${{ github.ref }}-${{ github.event_name }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run changetool unit tests
|
||||
run: npm --workspace changetool test
|
||||
|
||||
# These checks do not need to be run as part of the same matrix that we use for the `unit-tests`
|
||||
# job.
|
||||
other-checks:
|
||||
|
||||
100
CHANGELOG.md
100
CHANGELOG.md
@@ -2,88 +2,78 @@
|
||||
|
||||
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
||||
|
||||
## 4.38.0 - 09 Sept 2026
|
||||
|
||||
- On GitHub-hosted runners, the CodeQL Action now deletes unused CodeQL bundles from the toolcache before downloading a different bundle, which frees up disk space for the analysis. We expect to roll this change out to everyone in September. [#4124](https://github.com/github/codeql-action/pull/4124)
|
||||
- The CodeQL Action now supports CodeQL releases that are compatible with Linux Arm64 and downloads the native `linux-arm64` CodeQL bundle when available. [#4072](https://github.com/github/codeql-action/pull/4072)
|
||||
- Update default CodeQL bundle version to [2.27.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.0). [#4129](https://github.com/github/codeql-action/pull/4129)
|
||||
|
||||
## 4.37.9 - 26 Aug 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.26.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.4). [#4106](https://github.com/github/codeql-action/pull/4106)
|
||||
|
||||
## 4.37.8 - 21 Aug 2026
|
||||
## 3.37.8 - 21 Aug 2026
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.37.7 - 13 Aug 2026
|
||||
## 3.37.7 - 13 Aug 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.26.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.3). [#4085](https://github.com/github/codeql-action/pull/4085)
|
||||
|
||||
## 4.37.6 - 04 Aug 2026
|
||||
## 3.37.6 - 04 Aug 2026
|
||||
|
||||
- Changed the default filepath for the new remote file address format that was introduced in CodeQL Action 4.37.0 / 3.37.0 to `.github/codeql-config.yml` to align it with the suggested path that is used elsewhere. [#4070](https://github.com/github/codeql-action/pull/4070)
|
||||
|
||||
## 4.37.5 - 03 Aug 2026
|
||||
## 3.37.5 - 03 Aug 2026
|
||||
|
||||
- Fixed a bug where a network error while streaming the download of the CodeQL bundle could terminate the `init` Action instead of falling back to downloading the bundle before extracting it. [#4061](https://github.com/github/codeql-action/pull/4061)
|
||||
|
||||
## 4.37.4 - 29 Jul 2026
|
||||
## 3.37.4 - 29 Jul 2026
|
||||
|
||||
- This version of the CodeQL Action adds support for the `tools` input for the `codeql-action/init` step to be specified using a `github-codeql-tools` [repository property](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). This feature will gradually be rolled out following the release of this version. Once rolled out, this allows for the CodeQL CLI version that is used in GitHub-managed workflows, such as Default Setup, to be set to a custom value. For example, customers who run into issues with rate limits when a new CodeQL CLI version is released can set the value to `toolcache` to always use the CodeQL CLI version that is available in the runner toolcache. For Advanced Setup workflows, the value provided for `tools` in the workflow definition always takes precedence unless the value of the repository property starts with `!`. [#4037](https://github.com/github/codeql-action/pull/4037)
|
||||
- Update default CodeQL bundle version to [2.26.2](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.2). [#4051](https://github.com/github/codeql-action/pull/4051)
|
||||
|
||||
## 4.37.3 - 22 Jul 2026
|
||||
## 3.37.3 - 22 Jul 2026
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.37.2 - 21 Jul 2026
|
||||
## 3.37.2 - 21 Jul 2026
|
||||
|
||||
- The new address format for the `config-file` input that was introduced in CodeQL Action 4.37.0 is now enabled by default. In addition to the format described there, the `remote=` prefix can now be used to explicitly indicate that the input refers to a remote file. All previous input formats continue to be accepted as well. [#4023](https://github.com/github/codeql-action/pull/4023)
|
||||
- The CodeQL Action can now make use of [configured private registries](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries) in Default Setup to retrieve CodeQL configuration files from remote repositories that require authentication. This will allow customers to store their CodeQL configuration in a single repository that can then be referenced by Default Setup workflows in other repositories. We expect to roll this and other, related changes out to everyone in July. [#4007](https://github.com/github/codeql-action/pull/4007)
|
||||
|
||||
## 4.37.1 - 16 Jul 2026
|
||||
## 3.37.1 - 16 Jul 2026
|
||||
|
||||
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.20.6 and earlier. These versions of CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise Server 3.16, and will be unsupported by the next minor release of the CodeQL Action. [#3956](https://github.com/github/codeql-action/pull/3956)
|
||||
- Update default CodeQL bundle version to [2.26.1](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.1). [#4019](https://github.com/github/codeql-action/pull/4019)
|
||||
|
||||
## 4.37.0 - 08 Jul 2026
|
||||
## 3.37.0 - 08 Jul 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.26.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.0). [#3995](https://github.com/github/codeql-action/pull/3995)
|
||||
- In addition to the existing input format, the `config-file` input for the `codeql-action/init` step will soon support a new `[owner/]repo[@ref][:path]` format. All components except the repository name are optional. If omitted, `owner` defaults to the same owner as the repository the analysis is running for, `ref` to `main`, and `path` to `.github/codeql-action.yaml`. Support for this format ships in this version of the CodeQL Action, but will only be enabled over the coming weeks. [#3973](https://github.com/github/codeql-action/pull/3973)
|
||||
|
||||
## 4.36.3 - 01 Jul 2026
|
||||
## 3.36.3 - 01 Jul 2026
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.36.2 - 04 Jun 2026
|
||||
## 3.36.2 - 04 Jun 2026
|
||||
|
||||
- Cache CodeQL CLI version information across Actions steps. [#3943](https://github.com/github/codeql-action/pull/3943)
|
||||
- Reduce requests while waiting for analysis processing by using exponential backoff when polling SARIF processing status. [#3937](https://github.com/github/codeql-action/pull/3937)
|
||||
- Update default CodeQL bundle version to [2.25.6](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.6). [#3948](https://github.com/github/codeql-action/pull/3948)
|
||||
|
||||
## 4.36.1 - 02 Jun 2026
|
||||
## 3.36.1 - 02 Jun 2026
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.36.0 - 22 May 2026
|
||||
## 3.36.0 - 22 May 2026
|
||||
|
||||
- _Breaking change_: Bump the minimum required CodeQL bundle version to 2.19.4. [#3894](https://github.com/github/codeql-action/pull/3894)
|
||||
- Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893)
|
||||
- Update default CodeQL bundle version to [2.25.5](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.5). [#3926](https://github.com/github/codeql-action/pull/3926)
|
||||
|
||||
## 4.35.5 - 15 May 2026
|
||||
## 3.35.5 - 15 May 2026
|
||||
|
||||
- We have improved how the JavaScript bundles for the CodeQL Action are generated to avoid duplication across bundles and reduce the size of the repository by around 70%. This should have no effect on the runtime behaviour of the CodeQL Action. [#3899](https://github.com/github/codeql-action/pull/3899)
|
||||
- For performance and accuracy reasons, [improved incremental analysis](https://github.com/github/roadmap/issues/1158) will now only be enabled on a pull request when diff-informed analysis is also enabled for that run. If diff-informed analysis is unavailable (for example, because the PR diff ranges could not be computed), the action will fall back to a full analysis. [#3791](https://github.com/github/codeql-action/pull/3791)
|
||||
- If multiple inputs are provided for the GitHub-internal `analysis-kinds` input, only `code-scanning` will be enabled. The `analysis-kinds` input is experimental, for GitHub-internal use only, and may change without notice at any time. [#3892](https://github.com/github/codeql-action/pull/3892)
|
||||
- Added an experimental change which, when running a Code Scanning analysis for a PR with [improved incremental analysis](https://github.com/github/roadmap/issues/1158) enabled, prefers CodeQL CLI versions that have a cached overlay-base database for the configured languages. This speeds up analysis for a repository when there is not yet a cached overlay-base database for the latest CLI version. We expect to roll this change out to everyone in May. [#3880](https://github.com/github/codeql-action/pull/3880)
|
||||
|
||||
## 4.35.4 - 07 May 2026
|
||||
## 3.35.4 - 07 May 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.25.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.4). [#3881](https://github.com/github/codeql-action/pull/3881)
|
||||
|
||||
## 4.35.3 - 01 May 2026
|
||||
## 3.35.3 - 01 May 2026
|
||||
|
||||
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.19.3 and earlier. These versions of CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise Server 3.15, and will be unsupported by the next minor release of the CodeQL Action. [#3837](https://github.com/github/codeql-action/pull/3837)
|
||||
- Configurations for private registries that use Cloudsmith or GCP OIDC are now accepted. [#3850](https://github.com/github/codeql-action/pull/3850)
|
||||
@@ -91,7 +81,7 @@ No user facing changes.
|
||||
- Fixed a bug where two diagnostics produced within the same millisecond could overwrite each other on disk, causing one of them to be lost. [#3852](https://github.com/github/codeql-action/pull/3852)
|
||||
- Update default CodeQL bundle version to [2.25.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.3). [#3865](https://github.com/github/codeql-action/pull/3865)
|
||||
|
||||
## 4.35.2 - 15 Apr 2026
|
||||
## 3.35.2 - 15 Apr 2026
|
||||
|
||||
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable is deprecated and will be removed in May 2026. If you are affected by this, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#3795](https://github.com/github/codeql-action/pull/3795)
|
||||
- The Git version 2.36.0 requirement for improved incremental analysis now only applies to repositories that contain submodules. [#3789](https://github.com/github/codeql-action/pull/3789)
|
||||
@@ -99,26 +89,26 @@ No user facing changes.
|
||||
- Fixed a bug in the validation of OIDC configurations for private registries that was added in CodeQL Action 4.33.0 / 3.33.0. [#3807](https://github.com/github/codeql-action/pull/3807)
|
||||
- Update default CodeQL bundle version to [2.25.2](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.2). [#3823](https://github.com/github/codeql-action/pull/3823)
|
||||
|
||||
## 4.35.1 - 27 Mar 2026
|
||||
## 3.35.1 - 27 Mar 2026
|
||||
|
||||
- Fix incorrect minimum required Git version for [improved incremental analysis](https://github.com/github/roadmap/issues/1158): it should have been 2.36.0, not 2.11.0. [#3781](https://github.com/github/codeql-action/pull/3781)
|
||||
|
||||
## 4.35.0 - 27 Mar 2026
|
||||
## 3.35.0 - 27 Mar 2026
|
||||
|
||||
- Reduced the minimum Git version required for [improved incremental analysis](https://github.com/github/roadmap/issues/1158) from 2.38.0 to 2.11.0. [#3767](https://github.com/github/codeql-action/pull/3767)
|
||||
- Update default CodeQL bundle version to [2.25.1](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.1). [#3773](https://github.com/github/codeql-action/pull/3773)
|
||||
|
||||
## 4.34.1 - 20 Mar 2026
|
||||
## 3.34.1 - 20 Mar 2026
|
||||
|
||||
- Downgrade default CodeQL bundle version to [2.24.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.3) due to issues with a small percentage of Actions and JavaScript analyses. [#3762](https://github.com/github/codeql-action/pull/3762)
|
||||
|
||||
## 4.34.0 - 20 Mar 2026
|
||||
## 3.34.0 - 20 Mar 2026
|
||||
|
||||
- Added an experimental change which disables TRAP caching when [improved incremental analysis](https://github.com/github/roadmap/issues/1158) is enabled, since improved incremental analysis supersedes TRAP caching. This will improve performance and reduce Actions cache usage. We expect to roll this change out to everyone in March. [#3569](https://github.com/github/codeql-action/pull/3569)
|
||||
- We are rolling out improved incremental analysis to C/C++ analyses that use build mode `none`. We expect this rollout to be complete by the end of April 2026. [#3584](https://github.com/github/codeql-action/pull/3584)
|
||||
- Update default CodeQL bundle version to [2.25.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.0). [#3585](https://github.com/github/codeql-action/pull/3585)
|
||||
|
||||
## 4.33.0 - 16 Mar 2026
|
||||
## 3.33.0 - 16 Mar 2026
|
||||
|
||||
- Upcoming change: Starting April 2026, the CodeQL Action will skip collecting file coverage information on pull requests to improve analysis performance. File coverage information will still be computed on non-PR analyses. Pull request analyses will log a warning about this upcoming change. [#3562](https://github.com/github/codeql-action/pull/3562)
|
||||
|
||||
@@ -132,11 +122,11 @@ No user facing changes.
|
||||
- Fixed the retry mechanism for database uploads. Previously this would fail with the error "Response body object should not be disturbed or locked". [#3564](https://github.com/github/codeql-action/pull/3564)
|
||||
- A warning is now emitted if the CodeQL Action detects a repository property whose name suggests that it relates to the CodeQL Action, but which is not one of the properties recognised by the current version of the CodeQL Action. [#3570](https://github.com/github/codeql-action/pull/3570)
|
||||
|
||||
## 4.32.6 - 05 Mar 2026
|
||||
## 3.32.6 - 05 Mar 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.24.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.3). [#3548](https://github.com/github/codeql-action/pull/3548)
|
||||
|
||||
## 4.32.5 - 02 Mar 2026
|
||||
## 3.32.5 - 02 Mar 2026
|
||||
|
||||
- Repositories owned by an organization can now set up the `github-codeql-disable-overlay` custom repository property to disable [improved incremental analysis for CodeQL](https://github.com/github/roadmap/issues/1158). First, create a custom repository property with the name `github-codeql-disable-overlay` and the type "True/false" in the organization's settings. Then in the repository's settings, set this property to `true` to disable improved incremental analysis. For more information, see [Managing custom properties for repositories in your organization](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). This feature is not yet available on GitHub Enterprise Server. [#3507](https://github.com/github/codeql-action/pull/3507)
|
||||
- Added an experimental change so that when [improved incremental analysis](https://github.com/github/roadmap/issues/1158) fails on a runner — potentially due to insufficient disk space — the failure is recorded in the Actions cache so that subsequent runs will automatically skip improved incremental analysis until something changes (e.g. a larger runner is provisioned or a new CodeQL version is released). We expect to roll this change out to everyone in March. [#3487](https://github.com/github/codeql-action/pull/3487)
|
||||
@@ -146,7 +136,7 @@ No user facing changes.
|
||||
- Added an experimental change which allows the `start-proxy` action to resolve the CodeQL CLI version from feature flags instead of using the linked CLI bundle version. We expect to roll this change out to everyone in March. [#3512](https://github.com/github/codeql-action/pull/3512)
|
||||
- The previously experimental changes from versions 4.32.3, 4.32.4, 3.32.3 and 3.32.4 are now enabled by default. [#3503](https://github.com/github/codeql-action/pull/3503), [#3504](https://github.com/github/codeql-action/pull/3504)
|
||||
|
||||
## 4.32.4 - 20 Feb 2026
|
||||
## 3.32.4 - 20 Feb 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.24.2](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.2). [#3493](https://github.com/github/codeql-action/pull/3493)
|
||||
- Added an experimental change which improves how certificates are generated for the authentication proxy that is used by the CodeQL Action in Default Setup when [private package registries are configured](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries). This is expected to generate more widely compatible certificates and should have no impact on analyses which are working correctly already. We expect to roll this change out to everyone in February. [#3473](https://github.com/github/codeql-action/pull/3473)
|
||||
@@ -154,89 +144,89 @@ No user facing changes.
|
||||
- Added a setting which allows the CodeQL Action to enable network debugging for Java programs. This will help GitHub staff support customers with troubleshooting issues in GitHub-managed CodeQL workflows, such as Default Setup. This setting can only be enabled by GitHub staff. [#3485](https://github.com/github/codeql-action/pull/3485)
|
||||
- Added a setting which enables GitHub-managed workflows, such as Default Setup, to use a [nightly CodeQL CLI release](https://github.com/dsp-testing/codeql-cli-nightlies) instead of the latest, stable release that is used by default. This will help GitHub staff support customers whose analyses for a given repository or organization require early access to a change in an upcoming CodeQL CLI release. This setting can only be enabled by GitHub staff. [#3484](https://github.com/github/codeql-action/pull/3484)
|
||||
|
||||
## 4.32.3 - 13 Feb 2026
|
||||
## 3.32.3 - 13 Feb 2026
|
||||
|
||||
- Added experimental support for testing connections to [private package registries](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries). This feature is not currently enabled for any analysis. In the future, it may be enabled by default for Default Setup. [#3466](https://github.com/github/codeql-action/pull/3466)
|
||||
|
||||
## 4.32.2 - 05 Feb 2026
|
||||
## 3.32.2 - 05 Feb 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.24.1](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.1). [#3460](https://github.com/github/codeql-action/pull/3460)
|
||||
|
||||
## 4.32.1 - 02 Feb 2026
|
||||
## 3.32.1 - 02 Feb 2026
|
||||
|
||||
- A warning is now shown in Default Setup workflow logs if a [private package registry is configured](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries) using a GitHub Personal Access Token (PAT), but no username is configured. [#3422](https://github.com/github/codeql-action/pull/3422)
|
||||
- Fixed a bug which caused the CodeQL Action to fail when repository properties cannot successfully be retrieved. [#3421](https://github.com/github/codeql-action/pull/3421)
|
||||
|
||||
## 4.32.0 - 26 Jan 2026
|
||||
## 3.32.0 - 26 Jan 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.24.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.0). [#3425](https://github.com/github/codeql-action/pull/3425)
|
||||
|
||||
## 4.31.11 - 23 Jan 2026
|
||||
## 3.31.11 - 23 Jan 2026
|
||||
|
||||
- When running a Default Setup workflow with [Actions debugging enabled](https://docs.github.com/en/actions/how-tos/monitor-workflows/enable-debug-logging), the CodeQL Action will now use more unique names when uploading logs from the Dependabot authentication proxy as workflow artifacts. This ensures that the artifact names do not clash between multiple jobs in a build matrix. [#3409](https://github.com/github/codeql-action/pull/3409)
|
||||
- Improved error handling throughout the CodeQL Action. [#3415](https://github.com/github/codeql-action/pull/3415)
|
||||
- Added experimental support for automatically excluding [generated files](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github) from the analysis. This feature is not currently enabled for any analysis. In the future, it may be enabled by default for some GitHub-managed analyses. [#3318](https://github.com/github/codeql-action/pull/3318)
|
||||
- The changelog extracts that are included with releases of the CodeQL Action are now shorter to avoid duplicated information from appearing in Dependabot PRs. [#3403](https://github.com/github/codeql-action/pull/3403)
|
||||
|
||||
## 4.31.10 - 12 Jan 2026
|
||||
## 3.31.10 - 12 Jan 2026
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.9. [#3393](https://github.com/github/codeql-action/pull/3393)
|
||||
|
||||
## 4.31.9 - 16 Dec 2025
|
||||
## 3.31.9 - 16 Dec 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.8 - 11 Dec 2025
|
||||
## 3.31.8 - 11 Dec 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.8. [#3354](https://github.com/github/codeql-action/pull/3354)
|
||||
|
||||
## 4.31.7 - 05 Dec 2025
|
||||
## 3.31.7 - 05 Dec 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.7. [#3343](https://github.com/github/codeql-action/pull/3343)
|
||||
|
||||
## 4.31.6 - 01 Dec 2025
|
||||
## 3.31.6 - 01 Dec 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.5 - 24 Nov 2025
|
||||
## 3.31.5 - 24 Nov 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.6. [#3321](https://github.com/github/codeql-action/pull/3321)
|
||||
|
||||
## 4.31.4 - 18 Nov 2025
|
||||
## 3.31.4 - 18 Nov 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.3 - 13 Nov 2025
|
||||
## 3.31.3 - 13 Nov 2025
|
||||
|
||||
- CodeQL Action v3 will be deprecated in December 2026. The Action now logs a warning for customers who are running v3 but could be running v4. For more information, see [Upcoming deprecation of CodeQL Action v3](https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/).
|
||||
- Update default CodeQL bundle version to 2.23.5. [#3288](https://github.com/github/codeql-action/pull/3288)
|
||||
|
||||
## 4.31.2 - 30 Oct 2025
|
||||
## 3.31.2 - 30 Oct 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.31.1 - 30 Oct 2025
|
||||
## 3.31.1 - 30 Oct 2025
|
||||
|
||||
- The `add-snippets` input has been removed from the `analyze` action. This input has been deprecated since CodeQL Action 3.26.4 in August 2024 when this removal was announced.
|
||||
|
||||
## 4.31.0 - 24 Oct 2025
|
||||
## 3.31.0 - 24 Oct 2025
|
||||
|
||||
- Bump minimum CodeQL bundle version to 2.17.6. [#3223](https://github.com/github/codeql-action/pull/3223)
|
||||
- When SARIF files are uploaded by the `analyze` or `upload-sarif` actions, the CodeQL Action automatically performs post-processing steps to prepare the data for the upload. Previously, these post-processing steps were only performed before an upload took place. We are now changing this so that the post-processing steps will always be performed, even when the SARIF files are not uploaded. This does not change anything for the `upload-sarif` action. For `analyze`, this may affect Advanced Setup for CodeQL users who specify a value other than `always` for the `upload` input. [#3222](https://github.com/github/codeql-action/pull/3222)
|
||||
|
||||
## 4.30.9 - 17 Oct 2025
|
||||
## 3.30.9 - 17 Oct 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.3. [#3205](https://github.com/github/codeql-action/pull/3205)
|
||||
- Experimental: A new `setup-codeql` action has been added which is similar to `init`, except it only installs the CodeQL CLI and does not initialize a database. Do not use this in production as it is part of an internal experiment and subject to change at any time. [#3204](https://github.com/github/codeql-action/pull/3204)
|
||||
|
||||
## 4.30.8 - 10 Oct 2025
|
||||
## 3.30.8 - 10 Oct 2025
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.30.7 - 06 Oct 2025
|
||||
## 3.30.7 - 06 Oct 2025
|
||||
|
||||
- [v4+ only] The CodeQL Action now runs on Node.js v24. [#3169](https://github.com/github/codeql-action/pull/3169)
|
||||
|
||||
No user facing changes.
|
||||
## 3.30.6 - 02 Oct 2025
|
||||
|
||||
- Update default CodeQL bundle version to 2.23.2. [#3168](https://github.com/github/codeql-action/pull/3168)
|
||||
|
||||
@@ -60,13 +60,10 @@ Here are a few things you can do that will increase the likelihood of your pull
|
||||
This workflow goes through the pull requests that have been merged to `main` since the last release, creates a changelog, then opens a pull request to merge the changes since the last release into the `releases/v3` release branch.
|
||||
|
||||
You can start a release by triggering this workflow via [workflow dispatch](https://github.com/github/codeql-action/actions/workflows/update-release-branch.yml).
|
||||
1. The workflow run will open a pull request titled "Merge main into releases/v3". Follow the steps on the checklist in the pull request. Once you've checked off all but the last two of these, approve the PR and automerge it **with a merge commit** (`gh pr merge --merge`).
|
||||
1. The workflow run will open a pull request titled "Merge main into releases/v3". Follow the steps on the checklist in the pull request. Once you've checked off all but the last two of these, approve the PR and automerge it.
|
||||
1. When the "Merge main into releases/v3" pull request is merged into the `releases/v3` branch, a mergeback pull request to `main` will be automatically created. This mergeback pull request incorporates the changelog updates into `main`, tags the release using the merge commit of the "Merge main into releases/v3" pull request, and bumps the patch version of the CodeQL Action.
|
||||
1. If a backport to an older major version is required, a pull request targeting that version's branch will also be automatically created.
|
||||
1. Approve the mergeback and backport pull request (if applicable) and automerge them **with a merge commit** (`gh pr merge --merge`).
|
||||
|
||||
> [!NOTE]
|
||||
> The release, mergeback, and backport pull requests must always be merged with a merge commit — **never squash or rebase**. The mergeback tags the release using the merge commit of the "Merge main into releases/v3" pull request, so squashing or rebasing breaks tagging and the branch linkage the release automation relies on.
|
||||
1. Approve the mergeback and backport pull request (if applicable) and automerge them.
|
||||
|
||||
Once the mergeback and backport pull request have been merged, the release is complete.
|
||||
|
||||
|
||||
@@ -94,6 +94,6 @@ outputs:
|
||||
sarif-id:
|
||||
description: The ID of the uploaded SARIF file.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: "../lib/analyze-entry.js"
|
||||
post: "../lib/analyze-post-entry.js"
|
||||
|
||||
@@ -15,5 +15,5 @@ inputs:
|
||||
$GITHUB_WORKSPACE as its working directory.
|
||||
required: false
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/autobuild-entry.js'
|
||||
|
||||
@@ -209,18 +209,4 @@ export default [
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["scripts/changetool/**/*.ts"],
|
||||
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: "./scripts/changetool/tsconfig.json",
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
"import/extensions": "off",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -164,19 +164,12 @@ inputs:
|
||||
[Internal] The ID of the check run, as provided by the Actions runtime environment. Do not set this value manually.
|
||||
default: ${{ job.check_run_id }}
|
||||
required: false
|
||||
job-status:
|
||||
description: >-
|
||||
[Internal] The status of the job, as provided by the Actions runtime environment. This is how the
|
||||
post step learns whether the job as a whole succeeded, failed, or was cancelled. Do not set this
|
||||
value manually.
|
||||
default: ${{ job.status }}
|
||||
required: false
|
||||
outputs:
|
||||
codeql-path:
|
||||
description: The path of the CodeQL binary used for analysis
|
||||
codeql-version:
|
||||
description: The version of the CodeQL binary used for analysis
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/init-entry.js'
|
||||
post: '../lib/init-post-entry.js'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.27.0",
|
||||
"cliVersion": "2.27.0",
|
||||
"priorBundleVersion": "codeql-bundle-v2.26.4",
|
||||
"priorCliVersion": "2.26.4"
|
||||
"bundleVersion": "codeql-bundle-v2.26.3",
|
||||
"cliVersion": "2.26.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.26.2",
|
||||
"priorCliVersion": "2.26.2"
|
||||
}
|
||||
|
||||
2192
lib/entry-points.js
generated
2192
lib/entry-points.js
generated
File diff suppressed because it is too large
Load Diff
1749
package-lock.json
generated
1749
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.38.0",
|
||||
"version": "3.37.8",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
@@ -17,8 +17,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"pr-checks",
|
||||
"scripts/changetool"
|
||||
"pr-checks"
|
||||
],
|
||||
"dependencies": {
|
||||
"@actions/artifact": "^5.0.3",
|
||||
@@ -32,27 +31,27 @@
|
||||
"@actions/io": "^2.0.0",
|
||||
"@actions/tool-cache": "^3.0.1",
|
||||
"@octokit/core": "^7.0.7",
|
||||
"@octokit/plugin-paginate-rest": "^15.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
|
||||
"@octokit/plugin-paginate-rest": "^14.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
||||
"@octokit/plugin-retry": "^8.1.1",
|
||||
"archiver": "^8.0.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"follow-redirects": "^1.16.0",
|
||||
"get-folder-size": "^5.0.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"js-yaml": "^5.4.0",
|
||||
"js-yaml": "^5.2.3",
|
||||
"jsonschema": "1.5.0",
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.4.0",
|
||||
"semver": "^7.8.5",
|
||||
"uuid": "^14.0.2",
|
||||
"uuid": "^14.0.1",
|
||||
"undici": "^6.28.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^17.0.0",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"@types/archiver": "^8.0.0",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
@@ -62,19 +61,19 @@
|
||||
"@types/semver": "^7.8.0",
|
||||
"@types/sinon": "^22.0.0",
|
||||
"ava": "^6.4.1",
|
||||
"esbuild": "^0.28.2",
|
||||
"esbuild": "^0.28.1",
|
||||
"eslint": "^9.39.5",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"eslint-plugin-github": "^6.1.2",
|
||||
"eslint-plugin-import-x": "^4.17.1",
|
||||
"eslint-plugin-jsdoc": "^64.2.1",
|
||||
"eslint-plugin-jsdoc": "^62.9.0",
|
||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||
"glob": "^13.0.6",
|
||||
"globals": "^17.11.0",
|
||||
"globals": "^17.9.0",
|
||||
"nock": "^14.0.17",
|
||||
"sinon": "^22.1.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.68.0"
|
||||
"typescript-eslint": "^8.66.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@actions/tool-cache": {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import * as githubUtils from "@actions/github/lib/utils";
|
||||
import { type Octokit } from "@octokit/core";
|
||||
import { type PaginateInterface } from "@octokit/plugin-paginate-rest";
|
||||
import { type Api } from "@octokit/plugin-rest-endpoint-methods";
|
||||
|
||||
/** The type of the Octokit client. */
|
||||
export type ApiClient = InstanceType<typeof githubUtils.GitHub>;
|
||||
export type ApiClient = Octokit & Api & { paginate: PaginateInterface };
|
||||
|
||||
/** Constructs an `ApiClient` using `token` for authentication. */
|
||||
export function getApiClient(token: string): ApiClient {
|
||||
|
||||
@@ -2,8 +2,7 @@ name: "All-platform bundle"
|
||||
description: "Tests using an all-platform CodeQL Bundle"
|
||||
operatingSystems:
|
||||
- ubuntu
|
||||
- os: macos
|
||||
runner-image: macos-latest-xlarge
|
||||
- macos
|
||||
- windows
|
||||
versions:
|
||||
- nightly-latest
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
name: "Linux Arm64"
|
||||
description: "An end-to-end integration test running on a Linux Arm64 runner, checking that the native linux-arm64 CodeQL bundle is downloaded and can analyze interpreted and compiled code"
|
||||
operatingSystems:
|
||||
- os: ubuntu
|
||||
runner-image: ubuntu-24.04-arm
|
||||
# The native linux-arm64 CodeQL bundle is only available in recent CLI releases, so we restrict this
|
||||
# check to `nightly-latest`, which is guaranteed to ship it. Older stable versions do not have an
|
||||
# arm64 asset, and `prepare-test` would resolve an x64 bundle URL for them on this runner.
|
||||
versions:
|
||||
- nightly-latest
|
||||
installGo: true
|
||||
installDotNet: true
|
||||
# The set of languages CodeQL supports on this platform, excluding Swift (macOS only).
|
||||
env:
|
||||
LANGUAGES: cpp,csharp,go,java,javascript,python,ruby
|
||||
steps:
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: ${{ env.LANGUAGES }}
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
- name: Build code
|
||||
run: ./build.sh
|
||||
- uses: ./../action/analyze
|
||||
with:
|
||||
upload-database: false
|
||||
- name: Assert databases exist
|
||||
run: |
|
||||
cd "$RUNNER_TEMP/codeql_databases"
|
||||
for lang in ${LANGUAGES//,/ }; do
|
||||
if [[ ! -d "$lang" ]]; then
|
||||
echo "Did not find a database for $lang"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found database for $lang"
|
||||
done
|
||||
@@ -15,7 +15,6 @@ operatingSystems:
|
||||
- stable-v2.21.4
|
||||
- stable-v2.22.4
|
||||
env:
|
||||
CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES: true
|
||||
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
|
||||
installGo: true
|
||||
installDotNet: true
|
||||
|
||||
@@ -5,8 +5,7 @@ versions:
|
||||
- default
|
||||
- nightly-latest
|
||||
operatingSystems:
|
||||
- os: macos
|
||||
runner-image: macos-latest-xlarge
|
||||
- macos
|
||||
installGo: true
|
||||
installDotNet: true
|
||||
env:
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
"@actions/core": "^2.0.3",
|
||||
"@actions/github": "^8.0.1",
|
||||
"@octokit/core": "^7.0.7",
|
||||
"@octokit/plugin-paginate-rest": ">=15.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
|
||||
"@octokit/plugin-paginate-rest": ">=9.2.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
||||
"semver": "^7.8.5",
|
||||
"yaml": "^2.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.43",
|
||||
"tsx": "^4.23.12"
|
||||
"tsx": "^4.23.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,8 +253,8 @@ const languageSetups: LanguageSetups = {
|
||||
name: "Install Java",
|
||||
uses: pinnedUses(
|
||||
"actions/setup-java",
|
||||
"dd06d9cba3e5552c54d9f8ea23572deb30010f7c",
|
||||
"v6.0.0",
|
||||
"b6effb05e454b25005698d916606bdc6ffcbf961",
|
||||
"v5.7.0",
|
||||
),
|
||||
with: {
|
||||
"java-version": `\${{ inputs.java-version || '${defaultLanguageVersions.java}' }}`,
|
||||
|
||||
@@ -21,5 +21,5 @@ outputs:
|
||||
environment:
|
||||
description: The inferred build environment configuration.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/resolve-environment-entry.js'
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
isValidChangenoteContent,
|
||||
isValidChangenoteFile,
|
||||
isValidChangenoteFilename,
|
||||
hasValidChangenoteCategory,
|
||||
VALID_CHANGE_NOTE_CATEGORIES,
|
||||
} from "./validate.ts";
|
||||
|
||||
async function withTmpFile<T>(
|
||||
baseFileName: string,
|
||||
contents: string,
|
||||
body: (filePath: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const tmpDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "changetool-validate-test-"),
|
||||
);
|
||||
try {
|
||||
const filePath = path.join(tmpDir, baseFileName);
|
||||
fs.writeFileSync(filePath, contents);
|
||||
return await body(filePath);
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
await describe("isValidChangenoteContent", async () => {
|
||||
await it("recognizes an unordered Markdown list", async () => {
|
||||
const inputs = [
|
||||
"- One changenote entry",
|
||||
"- First item\n- Second item",
|
||||
"\n\n\n\n- Fixed a bug\n- Added a feature",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteContent(input), true);
|
||||
}
|
||||
});
|
||||
|
||||
await it("does not recognize non-Markdown text", async () => {
|
||||
const inputs = [
|
||||
"This is not a list.",
|
||||
'["this", "is", "JSON"]',
|
||||
"---",
|
||||
"***",
|
||||
"___",
|
||||
"paragraph",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteContent(input), false);
|
||||
}
|
||||
});
|
||||
|
||||
await it("does not recognize ordered Markdown lists", async () => {
|
||||
const inputs = [
|
||||
"1. First item\n2. Second item",
|
||||
"\n\n\n1. First item\n1. Second item",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteContent(input), false);
|
||||
}
|
||||
});
|
||||
|
||||
await it("requires all list items to use a hyphen bullet", async () => {
|
||||
const inputs = [
|
||||
"* Fixed a bug\n* Added feature",
|
||||
"+ Fixed a bug\n+ Added feature",
|
||||
"- Fixed a bug\n* Added feature",
|
||||
"- Fixed a bug\n+ Added feature",
|
||||
"- Fixed a bug\n * Added feature\n + Updated docs",
|
||||
"\n\n\n* Fixed a bug",
|
||||
"\n\n\n+ Fixed a bug",
|
||||
"---\n* Fixed a bug\n* Added feature",
|
||||
] as const;
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteContent(input), false);
|
||||
}
|
||||
});
|
||||
|
||||
await it("does not contain other Markdown elements", async () => {
|
||||
const inputs = [
|
||||
"- Fixed a bug\n\nParagraph of text",
|
||||
"- Fixed a bug\n\n* Added a feature",
|
||||
"# Header\n- Fixed a bug",
|
||||
"- Fixed a bug\n## Subheader",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteContent(input), false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await describe("isValidChangenoteFilename", async () => {
|
||||
await it("accepts valid filenames", async () => {
|
||||
const inputs = [
|
||||
"2023-01-01-fix-bug.md",
|
||||
"2023-12-31-add-feature.md",
|
||||
"2023-06-15-update-docs.md",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteFilename(input), true);
|
||||
}
|
||||
});
|
||||
|
||||
await it("rejects invalid filenames", async () => {
|
||||
const inputs = [
|
||||
"missing-date-from-filename.md",
|
||||
"2021-01-01.md",
|
||||
"2026-12-19-wrong-file-name-extension.txt",
|
||||
];
|
||||
|
||||
for (const input of inputs) {
|
||||
assert.equal(isValidChangenoteFilename(input), false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await describe("hasValidChangenoteCategory", async () => {
|
||||
await it("accepts valid categories", async () => {
|
||||
for (const category of Object.keys(VALID_CHANGE_NOTE_CATEGORIES)) {
|
||||
const frontmatter = { category };
|
||||
assert.equal(hasValidChangenoteCategory(frontmatter), true);
|
||||
}
|
||||
});
|
||||
|
||||
await it("rejects invalid categories", async () => {
|
||||
const inputs = [
|
||||
"",
|
||||
"invalid-category",
|
||||
"bug-fix",
|
||||
"new-feature",
|
||||
"security-patch",
|
||||
"miscellaneous",
|
||||
"documentation",
|
||||
];
|
||||
|
||||
for (const category of inputs) {
|
||||
const frontmatter = { category };
|
||||
assert.equal(hasValidChangenoteCategory(frontmatter), false);
|
||||
}
|
||||
});
|
||||
|
||||
await it("reject missing category", async () => {
|
||||
assert.equal(hasValidChangenoteCategory({}), false);
|
||||
assert.equal(hasValidChangenoteCategory({ category: null }), false);
|
||||
assert.equal(hasValidChangenoteCategory({ category: undefined }), false);
|
||||
});
|
||||
});
|
||||
|
||||
await describe("isValidChangenoteFile", async () => {
|
||||
await it("accepts a valid change-note file", async () => {
|
||||
await withTmpFile(
|
||||
"2026-01-01-fix-bug.md",
|
||||
"---\ncategory: fix\n---\n- Fixed a bug\n",
|
||||
async (filePath) => {
|
||||
assert.equal(isValidChangenoteFile(filePath), true);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await it("rejects invalid filename", async () => {
|
||||
await withTmpFile(
|
||||
"fix-bug.md",
|
||||
"---\ncategory: fix\n---\n- Fixed a bug\n",
|
||||
async (filePath) => {
|
||||
assert.equal(isValidChangenoteFile(filePath), false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await it("rejects missing frontmatter", async () => {
|
||||
await withTmpFile(
|
||||
"2026-01-01-fix-bug.md",
|
||||
"- Fixed a bug\n",
|
||||
async (filePath) => {
|
||||
assert.equal(isValidChangenoteFile(filePath), false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await it("rejects invalid Markdown", async () => {
|
||||
await withTmpFile(
|
||||
"2026-01-01-fix-bug.md",
|
||||
"---\ncategory: fix\n---\n* Fixed a bug\n",
|
||||
async (filePath) => {
|
||||
assert.equal(isValidChangenoteFile(filePath), false);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { matter } from "lite-matter";
|
||||
import type { List, ListItem } from "mdast";
|
||||
import { fromMarkdown } from "mdast-util-from-markdown";
|
||||
|
||||
// Regex for filename: YYYY-MM-DD-id.md
|
||||
const VALID_CHANGE_NOTE_FILENAME_PATTERN =
|
||||
/^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])-([a-z0-9]+(?:-[a-z0-9]+)*)\.md$/;
|
||||
|
||||
export const VALID_CHANGE_NOTE_CATEGORIES = {
|
||||
breaking: "Breaking Changes",
|
||||
feature: "New Features",
|
||||
improvement: "Improvements",
|
||||
securityFix: "Security Fixes",
|
||||
fix: "Bug Fixes",
|
||||
unship: "Removed Features",
|
||||
deprecation: "Deprecations",
|
||||
knownIssue: "Known Issues",
|
||||
misc: "Miscellaneous",
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates that the given Markdown string meets the criteria for a change-note, which is:
|
||||
* - A single unordered list
|
||||
* - Each list item must start with a hyphen (-)
|
||||
* - No other Markdown elements are allowed
|
||||
* @param content The Markdown string to validate
|
||||
* @returns True if the string is a valid change-note, false otherwise
|
||||
*/
|
||||
export function isValidChangenoteContent(content: string): boolean {
|
||||
const ast = fromMarkdown(content);
|
||||
const lines = content.split("\n");
|
||||
|
||||
function listHasHyphenBullets(node: List | ListItem): boolean {
|
||||
if (node.type === "list") {
|
||||
return node.children.every(listHasHyphenBullets);
|
||||
}
|
||||
|
||||
const line = lines[node.position!.start.line - 1].trim();
|
||||
return (
|
||||
line.startsWith("-") &&
|
||||
node.children.every(
|
||||
(child) => child.type !== "list" || listHasHyphenBullets(child),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
ast.children.length === 1 &&
|
||||
ast.children[0].type === "list" &&
|
||||
ast.children[0].ordered === false &&
|
||||
listHasHyphenBullets(ast.children[0])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given filename meets the criteria for a change-note filename.
|
||||
* @param filename The name of the change-note file to validate.
|
||||
* @returns True if the filename is valid, false otherwise.
|
||||
*/
|
||||
export function isValidChangenoteFilename(filename: string): boolean {
|
||||
return filename.match(VALID_CHANGE_NOTE_FILENAME_PATTERN) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given frontmatter has a valid change-note category.
|
||||
* @param frontmatter The frontmatter object to validate.
|
||||
* @returns True if the frontmatter has a valid category, false otherwise.
|
||||
*/
|
||||
export function hasValidChangenoteCategory(
|
||||
frontmatter: Record<string, unknown>,
|
||||
): boolean {
|
||||
const category = frontmatter["category"];
|
||||
return (
|
||||
typeof category === "string" &&
|
||||
Object.hasOwn(VALID_CHANGE_NOTE_CATEGORIES, category)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given change-note file meets all of the criteria for a change-note.
|
||||
* @param filename The name of the change-note file to validate.
|
||||
* @returns True if the file is a valid change-note, false otherwise.
|
||||
*/
|
||||
export function isValidChangenoteFile(filename: string): boolean {
|
||||
let isValid: boolean = true;
|
||||
|
||||
let fileData: string | undefined;
|
||||
try {
|
||||
fileData = fs.readFileSync(filename, "utf8");
|
||||
} catch (error) {
|
||||
console.error(`${filename}: failed to read file`, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const { data: frontmatter, content } = matter(fileData);
|
||||
|
||||
if (!isValidChangenoteFilename(path.basename(filename))) {
|
||||
isValid = false;
|
||||
console.error(
|
||||
`${filename}: invalid filename; must match pattern YYYY-MM-DD-id.md`,
|
||||
);
|
||||
}
|
||||
if (!hasValidChangenoteCategory(frontmatter)) {
|
||||
isValid = false;
|
||||
const categories = Object.keys(VALID_CHANGE_NOTE_CATEGORIES).join(", ");
|
||||
console.error(
|
||||
`${filename}: invalid category; must be one of: ${categories}`,
|
||||
);
|
||||
}
|
||||
if (!isValidChangenoteContent(content)) {
|
||||
isValid = false;
|
||||
console.error(
|
||||
`${filename}: invalid Markdown; content must be a single unordered list with hyphen bullets and no other Markdown elements`,
|
||||
);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
import { isValidChangenoteFile } from "./cli/validate.ts";
|
||||
|
||||
const entryPoint = process.argv[1];
|
||||
if (entryPoint && import.meta.url === pathToFileURL(entryPoint).href) {
|
||||
try {
|
||||
process.exit(main());
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function main(): number {
|
||||
const { positionals } = parseArgs({
|
||||
allowPositionals: true,
|
||||
strict: true,
|
||||
});
|
||||
const [command, ...paths] = positionals;
|
||||
switch (command) {
|
||||
case undefined:
|
||||
case "help":
|
||||
return usage();
|
||||
case "validate":
|
||||
return validate(paths);
|
||||
default:
|
||||
console.error(`Unknown command: ${command}`);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function usage(): number {
|
||||
console.log("Usage: changetool validate <path> [<path> ...]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
function validate(paths: string[]): number {
|
||||
let valid = true;
|
||||
if (paths.length === 0) {
|
||||
console.error("error: no paths provided (see 'help' command for usage)");
|
||||
return 1;
|
||||
}
|
||||
for (const path of paths) {
|
||||
if (!isValidChangenoteFile(path)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid ? 0 : 1;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "changetool",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "Validates change-notes and merges them into CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "tsx index.ts",
|
||||
"test": "node --test --experimental-strip-types cli/*.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.2.0",
|
||||
"tsx": "^4.23.12",
|
||||
"typescript": "^7.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"lite-matter": "^0.1.2",
|
||||
"mdast-util-from-markdown": "^2.0.3"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "preserve",
|
||||
"allowImportingTsExtensions": true,
|
||||
"rootDir": ".",
|
||||
"sourceMap": false
|
||||
},
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -54,5 +54,5 @@ outputs:
|
||||
codeql-version:
|
||||
description: The version of the CodeQL binary that was installed.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/setup-codeql-entry.js'
|
||||
|
||||
@@ -283,19 +283,6 @@ export function isSelfHostedRunner(env: Env = getEnv()) {
|
||||
return env.getOptional(ActionsEnvVars.RUNNER_ENVIRONMENT) === "self-hosted";
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the job is running on a runner that GitHub hosts, and whose toolcache is therefore thrown
|
||||
* away once the job has finished.
|
||||
*
|
||||
* Unlike `looksLikeHostedRunner`, this is based on what the service reports for the job rather than
|
||||
* on how the runner's filesystem happens to be laid out, so it does not match self-hosted runners
|
||||
* that are configured to resemble hosted ones, such as those that mount a persistent volume at
|
||||
* `/opt/hostedtoolcache`.
|
||||
*/
|
||||
export function isGitHubHostedRunner(env: Env = getEnv()) {
|
||||
return env.getOptional(ActionsEnvVars.RUNNER_ENVIRONMENT) === "github-hosted";
|
||||
}
|
||||
|
||||
/** Determines whether the workflow trigger is `dynamic`. */
|
||||
export function isDynamicWorkflow(env: Env = getEnv()): boolean {
|
||||
return getWorkflowEventName(env) === "dynamic";
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as githubUtils from "@actions/github/lib/utils";
|
||||
import { type Octokit } from "@octokit/core";
|
||||
import { type PaginateInterface } from "@octokit/plugin-paginate-rest";
|
||||
import { type Api } from "@octokit/plugin-rest-endpoint-methods";
|
||||
import * as retry from "@octokit/plugin-retry";
|
||||
import { RequestRequestOptions } from "@octokit/types";
|
||||
import {
|
||||
@@ -125,7 +128,7 @@ export function makeProxyRequestOptions(
|
||||
}
|
||||
|
||||
/** The type of GitHub API client we use. */
|
||||
export type ApiClient = InstanceType<typeof githubUtils.GitHub>;
|
||||
export type ApiClient = Octokit & Api & { paginate: PaginateInterface };
|
||||
|
||||
/** Options for `createApiClientWithDetails`. */
|
||||
interface CreateApiClientOptions {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"maximumVersion":"3.23","minimumVersion":"3.17"}
|
||||
{"maximumVersion": "3.22", "minimumVersion": "3.17"}
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as core from "@actions/core";
|
||||
import { getOptionalInput, isDefaultSetup } from "./actions-util";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Logger } from "./logging";
|
||||
import { looksLikeHostedRunner, tryGetFolderBytes } from "./util";
|
||||
import { isHostedRunner, tryGetFolderBytes } from "./util";
|
||||
|
||||
/**
|
||||
* Returns the total size of all the specified paths.
|
||||
@@ -109,7 +109,7 @@ export function getDependencyCachingEnabled(): CachingKind {
|
||||
if (dependencyCaching !== undefined) return getCachingKind(dependencyCaching);
|
||||
|
||||
// On self-hosted runners which may have dependencies installed centrally, disable caching by default
|
||||
if (!looksLikeHostedRunner()) return CachingKind.None;
|
||||
if (!isHostedRunner()) return CachingKind.None;
|
||||
|
||||
// Disable in advanced workflows by default.
|
||||
if (!isDefaultSetup()) return CachingKind.None;
|
||||
|
||||
@@ -128,6 +128,7 @@ test("CliError constructor with empty stderr", (t) => {
|
||||
|
||||
for (const [platform, arch] of [
|
||||
["weird_plat", "x64"],
|
||||
["linux", "arm64"],
|
||||
["win32", "arm64"],
|
||||
]) {
|
||||
test.serial(
|
||||
@@ -156,34 +157,20 @@ for (const [platform, arch] of [
|
||||
);
|
||||
}
|
||||
|
||||
for (const [platform, arch] of [
|
||||
["linux", "x64"],
|
||||
["linux", "arm64"],
|
||||
["win32", "x64"],
|
||||
["darwin", "x64"],
|
||||
["darwin", "arm64"],
|
||||
]) {
|
||||
test.serial(
|
||||
`wrapCliConfigurationError - ${platform}/${arch} supported`,
|
||||
(t) => {
|
||||
sinon.stub(process, "platform").value(platform);
|
||||
sinon.stub(process, "arch").value(arch);
|
||||
const commandError = new CommandInvocationError(
|
||||
"codeql",
|
||||
["version"],
|
||||
1,
|
||||
"Some error",
|
||||
);
|
||||
const cliError = new CliError(commandError);
|
||||
|
||||
const wrappedError = wrapCliConfigurationError(cliError);
|
||||
|
||||
// Should return the original error since the platform is supported, rather
|
||||
// than replacing it with the unsupported-platform ConfigurationError.
|
||||
t.is(wrappedError, cliError);
|
||||
},
|
||||
test("wrapCliConfigurationError - supported platform", (t) => {
|
||||
const commandError = new CommandInvocationError(
|
||||
"codeql",
|
||||
["version"],
|
||||
1,
|
||||
"Some error",
|
||||
);
|
||||
}
|
||||
const cliError = new CliError(commandError);
|
||||
|
||||
const wrappedError = wrapCliConfigurationError(cliError);
|
||||
|
||||
// Should return the original error since platform is supported
|
||||
t.is(wrappedError, cliError);
|
||||
});
|
||||
|
||||
test("wrapCliConfigurationError - autobuild error", (t) => {
|
||||
const commandError = new CommandInvocationError(
|
||||
|
||||
@@ -8,7 +8,6 @@ import { ConfigurationError } from "./util";
|
||||
|
||||
const SUPPORTED_PLATFORMS = [
|
||||
["linux", "x64"],
|
||||
["linux", "arm64"],
|
||||
["win32", "x64"],
|
||||
["darwin", "x64"],
|
||||
["darwin", "arm64"],
|
||||
|
||||
@@ -3,11 +3,12 @@ import path from "path";
|
||||
|
||||
import test from "ava";
|
||||
|
||||
import { EnvVar } from "../environment";
|
||||
import { getRunnerLogger } from "../logging";
|
||||
import { setupTests } from "../testing-utils";
|
||||
import { getTestEnv, setupTests } from "../testing-utils";
|
||||
import * as util from "../util";
|
||||
|
||||
import { getCachedCodeQlVersion } from "./output-cache";
|
||||
import * as outputCache from "./output-cache";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
@@ -17,18 +18,18 @@ test.serial(
|
||||
"getCachedCodeQlVersion reuses a version persisted by an earlier step",
|
||||
async (t) => {
|
||||
await util.withTmpDir(async (tmpDir: string) => {
|
||||
const cacheFilePath = path.join(tmpDir, "cache.json");
|
||||
|
||||
const cacheFile = path.join(tmpDir, "codeql-action-command-cache.json");
|
||||
fs.writeFileSync(
|
||||
cacheFilePath,
|
||||
cacheFile,
|
||||
JSON.stringify({
|
||||
cmd: "/path/to/codeql",
|
||||
entries: { version: { version: "2.20.0" } },
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
|
||||
t.deepEqual(
|
||||
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
|
||||
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
|
||||
{
|
||||
version: "2.20.0",
|
||||
},
|
||||
@@ -41,17 +42,18 @@ test.serial(
|
||||
"getCachedCodeQlVersion ignores a persisted version from a different CLI",
|
||||
async (t) => {
|
||||
await util.withTmpDir(async (tmpDir: string) => {
|
||||
const cacheFilePath = path.join(tmpDir, "cache.json");
|
||||
const cacheFile = path.join(tmpDir, "version.json");
|
||||
fs.writeFileSync(
|
||||
cacheFilePath,
|
||||
cacheFile,
|
||||
JSON.stringify({
|
||||
cmd: "/path/to/other-codeql",
|
||||
entries: { version: { version: "2.20.0" } },
|
||||
version: { version: "2.20.0" },
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
|
||||
t.is(
|
||||
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
|
||||
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
@@ -62,10 +64,11 @@ test.serial(
|
||||
"getCachedCodeQlVersion ignores a malformed persisted value",
|
||||
async (t) => {
|
||||
await util.withTmpDir(async (tmpDir: string) => {
|
||||
const cacheFilePath = path.join(tmpDir, "cache.json");
|
||||
fs.writeFileSync(cacheFilePath, "not valid json", "utf8");
|
||||
const cacheFile = path.join(tmpDir, "version.json");
|
||||
fs.writeFileSync(cacheFile, "not valid json", "utf8");
|
||||
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
|
||||
t.is(
|
||||
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
|
||||
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
@@ -76,7 +79,9 @@ test.serial(
|
||||
"getCachedCodeQlVersion ignores a persisted value with the wrong structure",
|
||||
async (t) => {
|
||||
await util.withTmpDir(async (tmpDir: string) => {
|
||||
const cacheFilePath = path.join(tmpDir, "cache.json");
|
||||
const cacheFile = path.join(tmpDir, "version.json");
|
||||
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
|
||||
|
||||
const testValues = [
|
||||
{ cmd: "/path/to/codeql" },
|
||||
{ entries: { version: { version: "2.20.0" } } },
|
||||
@@ -99,9 +104,9 @@ test.serial(
|
||||
].map((v) => JSON.stringify(v));
|
||||
|
||||
for (const value of testValues) {
|
||||
fs.writeFileSync(cacheFilePath, value, "utf8");
|
||||
fs.writeFileSync(cacheFile, value, "utf8");
|
||||
t.is(
|
||||
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
|
||||
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
|
||||
undefined,
|
||||
value,
|
||||
);
|
||||
@@ -112,10 +117,10 @@ test.serial(
|
||||
|
||||
test.serial("getCachedCodeQlVersion ignores non-existent file", async (t) => {
|
||||
await util.withTmpDir(async (tmpDir: string) => {
|
||||
const cacheFilePath = path.join(tmpDir, "cache.json");
|
||||
const env = getTestEnv({ [EnvVar.TEMP]: tmpDir });
|
||||
t.notThrows(() => {
|
||||
t.is(
|
||||
getCachedCodeQlVersion(logger, cacheFilePath, "/path/to/codeql"),
|
||||
outputCache.getCachedCodeQlVersion(logger, env, "/path/to/codeql"),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,10 +3,9 @@ import path from "path";
|
||||
|
||||
import { getTemporaryDirectory } from "../actions-util";
|
||||
import { Env } from "../environment";
|
||||
import * as json from "../json";
|
||||
import { Logger } from "../logging";
|
||||
|
||||
import { VersionInfo, versionInfoBaseSchema } from "./types";
|
||||
import type { VersionInfo } from "./types";
|
||||
|
||||
/**
|
||||
* The keys of the command cache. Each key corresponds to a command whose output we cache.
|
||||
@@ -14,19 +13,12 @@ import { VersionInfo, versionInfoBaseSchema } from "./types";
|
||||
export type CommandCacheKey = string;
|
||||
|
||||
/**
|
||||
* The JSON schema of the command cache that is persisted to disk.
|
||||
* The type of the command cache that is persisted to disk.
|
||||
*/
|
||||
const outputCacheSchema = {
|
||||
cmd: json.string,
|
||||
entries: json.object({}),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/**
|
||||
* The type that describes the command cache that is persisted to disk.
|
||||
*/
|
||||
export type OutputCache = json.FromSchema<typeof outputCacheSchema> & {
|
||||
entries: { version: VersionInfo };
|
||||
};
|
||||
export interface OutputCache {
|
||||
cmd: string;
|
||||
entries: Record<CommandCacheKey, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the temporary file that backs the on-disk cache of
|
||||
@@ -51,18 +43,18 @@ export function resetCachedCodeQlVersion(): void {
|
||||
* Returns the path to the temporary file that backs the
|
||||
* on-disk cache of CLI responses between workflow steps.
|
||||
*/
|
||||
export function getCommandCacheFilePath(env: Env): string {
|
||||
function getCommandCacheFilePath(env: Env): string {
|
||||
return path.join(getTemporaryDirectory(env), COMMAND_CACHE_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the CodeQL CLI version both in-memory and on disk.
|
||||
* @param cacheFilePath The path to the cache file.
|
||||
* @param env The environment variables to use.
|
||||
* @param cmd The path to the CodeQL CLI.
|
||||
* @param version The version information to cache.
|
||||
*/
|
||||
export function cacheCodeQlVersion(
|
||||
cacheFilePath: string,
|
||||
env: Env,
|
||||
cmd: string,
|
||||
version: VersionInfo,
|
||||
): void {
|
||||
@@ -78,18 +70,22 @@ export function cacheCodeQlVersion(
|
||||
// processes, can reuse it rather than invoking `codeql version` again. We
|
||||
// record the CLI path so that a different step using a different CodeQL bundle
|
||||
// doesn't pick up a stale version.
|
||||
fs.writeFileSync(cacheFilePath, JSON.stringify(outputCache), "utf8");
|
||||
fs.writeFileSync(
|
||||
getCommandCacheFilePath(env),
|
||||
JSON.stringify(outputCache),
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached CodeQL CLI version, if any.
|
||||
* @param logger The logger to use for logging messages.
|
||||
* @param cacheFilePath The path to the cache file.
|
||||
* @param env The environment variables to use.
|
||||
* @param cmd The path to the CodeQL CLI.
|
||||
*/
|
||||
export function getCachedCodeQlVersion(
|
||||
logger: Logger,
|
||||
cacheFilePath: string,
|
||||
env: Env,
|
||||
cmd?: string,
|
||||
): undefined | VersionInfo {
|
||||
if (cachedCodeQlVersion !== undefined) {
|
||||
@@ -100,9 +96,11 @@ export function getCachedCodeQlVersion(
|
||||
// invokes `codeql version` instead.
|
||||
let serialized: string;
|
||||
try {
|
||||
serialized = fs.readFileSync(cacheFilePath, "utf8");
|
||||
serialized = fs.readFileSync(getCommandCacheFilePath(env), "utf8");
|
||||
} catch (e) {
|
||||
logger.debug(`Cannot read CLI-cache file ${cacheFilePath}: ${e}`);
|
||||
logger.debug(
|
||||
`Cannot read CLI-cache file ${getCommandCacheFilePath(env)}: ${e}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
let persisted: unknown;
|
||||
@@ -129,7 +127,17 @@ export function getCachedCodeQlVersion(
|
||||
* @param x The value to test
|
||||
*/
|
||||
function isVersionInfo(x: unknown): x is VersionInfo {
|
||||
return json.isObject(x) && json.validateSchema(versionInfoBaseSchema, x);
|
||||
const candidate = x as Partial<VersionInfo> | null;
|
||||
return (
|
||||
typeof candidate === "object" &&
|
||||
candidate !== null &&
|
||||
typeof candidate.version === "string" &&
|
||||
(candidate.features === undefined ||
|
||||
(typeof candidate.features === "object" &&
|
||||
candidate.features !== null)) &&
|
||||
(candidate.overlayVersion === undefined ||
|
||||
typeof candidate.overlayVersion === "number")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,10 +145,12 @@ function isVersionInfo(x: unknown): x is VersionInfo {
|
||||
* @param x The value to test
|
||||
*/
|
||||
function isOutputCache(x: unknown): x is OutputCache {
|
||||
const candidate = x as Partial<OutputCache> | null;
|
||||
return (
|
||||
json.isObject(x) &&
|
||||
json.validateSchema(outputCacheSchema, x) &&
|
||||
json.isObject<{ version: unknown }>(x.entries) &&
|
||||
isVersionInfo(x.entries.version)
|
||||
typeof candidate === "object" &&
|
||||
candidate !== null &&
|
||||
typeof candidate.cmd === "string" &&
|
||||
candidate.entries !== undefined &&
|
||||
isVersionInfo(candidate.entries.version)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import * as json from "../json";
|
||||
|
||||
/**
|
||||
* The JSON schema of the expected output of the `codeql version` command.
|
||||
*/
|
||||
export const versionInfoBaseSchema = {
|
||||
version: json.string,
|
||||
features: json.optional(json.object({})),
|
||||
export interface VersionInfo {
|
||||
version: string;
|
||||
features?: { [name: string]: boolean };
|
||||
/**
|
||||
* The overlay version helps deal with backward incompatible changes for
|
||||
* overlay analysis. When a precompiled query pack reports the same overlay
|
||||
@@ -14,17 +9,5 @@ export const versionInfoBaseSchema = {
|
||||
* or if either the pack or the CLI does not report an overlay version,
|
||||
* we need to revert to non-overlay analysis.
|
||||
*/
|
||||
overlayVersion: json.optional(json.number),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/**
|
||||
* The base type that describes the expected output of the `codeql version` command.
|
||||
*/
|
||||
export type VersionInfoBase = json.FromSchema<typeof versionInfoBaseSchema>;
|
||||
|
||||
/**
|
||||
* The full type that describes the expected output of the `codeql version` command.
|
||||
*/
|
||||
export type VersionInfo = Omit<VersionInfoBase, "features"> & {
|
||||
features?: { [name: string]: boolean };
|
||||
};
|
||||
overlayVersion?: number;
|
||||
}
|
||||
|
||||
@@ -510,12 +510,7 @@ async function getCodeQLForCmd(
|
||||
return cmd;
|
||||
},
|
||||
async getVersion() {
|
||||
const cacheFilePath = outputCache.getCommandCacheFilePath(getEnv());
|
||||
let result = outputCache.getCachedCodeQlVersion(
|
||||
logger,
|
||||
cacheFilePath,
|
||||
cmd,
|
||||
);
|
||||
let result = outputCache.getCachedCodeQlVersion(logger, getEnv(), cmd);
|
||||
if (result === undefined) {
|
||||
result = await runCliJson<VersionInfo>(
|
||||
cmd,
|
||||
@@ -524,7 +519,7 @@ async function getCodeQLForCmd(
|
||||
noStreamStdout: true,
|
||||
},
|
||||
);
|
||||
outputCache.cacheCodeQlVersion(cacheFilePath, cmd, result);
|
||||
outputCache.cacheCodeQlVersion(getEnv(), cmd, result);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
@@ -90,8 +90,9 @@ import {
|
||||
Result,
|
||||
Success,
|
||||
Failure,
|
||||
looksLikeHostedRunner,
|
||||
isHostedRunner,
|
||||
} from "./util";
|
||||
|
||||
export { type Config } from "./config/action-config";
|
||||
|
||||
/**
|
||||
@@ -937,7 +938,7 @@ export async function isTrapCachingEnabled(
|
||||
if (trapCaching !== undefined) return trapCaching === "true";
|
||||
|
||||
// On self-hosted runners which may have slow network access, disable TRAP caching by default.
|
||||
if (!looksLikeHostedRunner()) return false;
|
||||
if (!isHostedRunner()) return false;
|
||||
|
||||
// If overlay analysis is enabled, then disable TRAP caching since overlay analysis supersedes it.
|
||||
// This change is gated behind a feature flag.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.27.0",
|
||||
"cliVersion": "2.27.0",
|
||||
"priorBundleVersion": "codeql-bundle-v2.26.4",
|
||||
"priorCliVersion": "2.26.4"
|
||||
"bundleVersion": "codeql-bundle-v2.26.3",
|
||||
"cliVersion": "2.26.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.26.2",
|
||||
"priorCliVersion": "2.26.2"
|
||||
}
|
||||
|
||||
@@ -63,12 +63,6 @@ export enum EnvVar {
|
||||
/** Whether the CodeQL Action has already warned the user about low disk space. */
|
||||
HAS_WARNED_ABOUT_DISK_SPACE = "CODEQL_ACTION_HAS_WARNED_ABOUT_DISK_SPACE",
|
||||
|
||||
/**
|
||||
* Whether a step in this job has already set up CodeQL. Steps that run afterwards may be holding
|
||||
* a path into the toolcache, so we must not delete anything from it.
|
||||
*/
|
||||
HAS_SET_UP_CODEQL = "CODEQL_ACTION_HAS_SET_UP_CODEQL",
|
||||
|
||||
/** Whether the `setup-codeql` action has been run. */
|
||||
SETUP_CODEQL_ACTION_HAS_RUN = "CODEQL_ACTION_SETUP_CODEQL_HAS_RUN",
|
||||
|
||||
|
||||
@@ -74,11 +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",
|
||||
/**
|
||||
* Controls whether we delete CodeQL bundles that we are not going to use from the toolcache
|
||||
* before downloading a different bundle, in order to reclaim disk space.
|
||||
*/
|
||||
CleanupToolcacheBundles = "cleanup_toolcache_bundles",
|
||||
CleanupTrapCaches = "cleanup_trap_caches",
|
||||
/** Whether to allow the `config-file` input to be specified via a repository property. */
|
||||
ConfigFileRepositoryProperty = "config_file_repository_property",
|
||||
@@ -216,11 +211,6 @@ export const featureConfig = {
|
||||
envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.CleanupToolcacheBundles]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.CleanupTrapCaches]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_CLEANUP_TRAP_CACHES",
|
||||
|
||||
@@ -15,12 +15,10 @@ import { getRunnerLogger } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import * as overlayStatus from "./overlay/status";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import { JobStatus } from "./status-report";
|
||||
import {
|
||||
createFeatures,
|
||||
createTestConfig,
|
||||
DEFAULT_ACTIONS_VARS,
|
||||
getTestEnv,
|
||||
makeMacro,
|
||||
makeVersionInfo,
|
||||
RecordingLogger,
|
||||
@@ -60,8 +58,6 @@ test.serial("init-post action with debug mode off", async (t) => {
|
||||
createTestConfig({ debugMode: false }),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([]),
|
||||
"success",
|
||||
getTestEnv(),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
@@ -84,8 +80,6 @@ test.serial("init-post action with debug mode on", async (t) => {
|
||||
createTestConfig({ debugMode: true }),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([]),
|
||||
"success",
|
||||
getTestEnv(),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
@@ -381,8 +375,6 @@ test.serial(
|
||||
}),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([Feature.OverlayAnalysisStatusSave]),
|
||||
"success",
|
||||
getTestEnv(),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
@@ -451,8 +443,6 @@ test.serial(
|
||||
}),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([]),
|
||||
"success",
|
||||
getTestEnv(),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
@@ -467,13 +457,8 @@ test.serial(
|
||||
test.serial("does not save overlay status when build successful", async (t) => {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
// Mark analyze as having completed successfully. `tryUploadSarifIfRunFailed` reads this from
|
||||
// the process environment, while `recordOverlayStatus` reads it from the environment it is
|
||||
// given.
|
||||
// Mark analyze as having completed successfully.
|
||||
process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] = "true";
|
||||
const env = getTestEnv({
|
||||
[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY]: "true",
|
||||
});
|
||||
|
||||
sinon.stub(util, "checkDiskUsage").resolves({
|
||||
numAvailableBytes: 100 * NUM_BYTES_PER_GIB,
|
||||
@@ -495,8 +480,6 @@ test.serial("does not save overlay status when build successful", async (t) => {
|
||||
}),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([Feature.OverlayAnalysisStatusSave]),
|
||||
"success",
|
||||
env,
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
@@ -534,8 +517,6 @@ test.serial(
|
||||
}),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([]),
|
||||
"success",
|
||||
getTestEnv(),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
@@ -547,137 +528,6 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Runs `uploadFailureInfo` for an overlay-base job that did not complete successfully, with the
|
||||
* given job status from the Actions runtime environment.
|
||||
*/
|
||||
async function runOverlayPostStep({
|
||||
jobStatus,
|
||||
codeQlReportedError = false,
|
||||
}: {
|
||||
jobStatus: string | undefined;
|
||||
codeQlReportedError?: boolean;
|
||||
}) {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
delete process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY];
|
||||
const env = getTestEnv(
|
||||
codeQlReportedError
|
||||
? { [EnvVar.JOB_STATUS]: JobStatus.FailureStatus }
|
||||
: {},
|
||||
);
|
||||
|
||||
sinon.stub(util, "checkDiskUsage").resolves({
|
||||
numAvailableBytes: 100 * NUM_BYTES_PER_GIB,
|
||||
numTotalBytes: 200 * NUM_BYTES_PER_GIB,
|
||||
});
|
||||
|
||||
const saveOverlayStatusStub = sinon
|
||||
.stub(overlayStatus, "saveOverlayStatus")
|
||||
.resolves(true);
|
||||
|
||||
await initActionPostHelper.uploadFailureInfo(
|
||||
sinon.spy(),
|
||||
sinon.spy(),
|
||||
codeql.createStubCodeQL({}),
|
||||
createTestConfig({
|
||||
debugMode: false,
|
||||
languages: ["javascript"],
|
||||
overlayDatabaseMode: OverlayDatabaseMode.OverlayBase,
|
||||
}),
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([Feature.OverlayAnalysisStatusSave]),
|
||||
jobStatus,
|
||||
env,
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
return { saveOverlayStatusStub };
|
||||
});
|
||||
}
|
||||
|
||||
test.serial(
|
||||
"does not save overlay status when the job was cancelled",
|
||||
async (t) => {
|
||||
const { saveOverlayStatusStub } = await runOverlayPostStep({
|
||||
jobStatus: "cancelled",
|
||||
});
|
||||
|
||||
t.true(
|
||||
saveOverlayStatusStub.notCalled,
|
||||
"a cancellation tells us nothing about whether the analysis would have succeeded",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"does not save overlay status when the job status is not recognised",
|
||||
async (t) => {
|
||||
const { saveOverlayStatusStub } = await runOverlayPostStep({
|
||||
jobStatus: "some-new-status",
|
||||
});
|
||||
|
||||
t.true(
|
||||
saveOverlayStatusStub.notCalled,
|
||||
"a status we do not recognise tells us nothing about whether the analysis would have succeeded",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"does not save overlay status when the job status is unavailable",
|
||||
async (t) => {
|
||||
const { saveOverlayStatusStub } = await runOverlayPostStep({
|
||||
jobStatus: undefined,
|
||||
});
|
||||
|
||||
t.true(
|
||||
saveOverlayStatusStub.notCalled,
|
||||
"without a job status we cannot tell whether the analysis would have succeeded",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"saves overlay status when the job failed rather than being cancelled",
|
||||
async (t) => {
|
||||
const { saveOverlayStatusStub } = await runOverlayPostStep({
|
||||
jobStatus: "failure",
|
||||
});
|
||||
|
||||
t.true(
|
||||
saveOverlayStatusStub.calledOnce,
|
||||
"a failed job indicates that the analysis itself failed",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("saves overlay status when the job succeeded", async (t) => {
|
||||
const { saveOverlayStatusStub } = await runOverlayPostStep({
|
||||
jobStatus: "success",
|
||||
});
|
||||
|
||||
t.true(
|
||||
saveOverlayStatusStub.calledOnce,
|
||||
"the analysis did not complete successfully even though the job as a whole succeeded",
|
||||
);
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"saves overlay status when a CodeQL Action reported an error before the run was cancelled",
|
||||
async (t) => {
|
||||
const { saveOverlayStatusStub } = await runOverlayPostStep({
|
||||
jobStatus: "cancelled",
|
||||
codeQlReportedError: true,
|
||||
});
|
||||
|
||||
t.true(
|
||||
saveOverlayStatusStub.calledOnce,
|
||||
"the analysis genuinely failed, even though the run was later cancelled",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
function createTestWorkflow(
|
||||
steps: workflow.WorkflowJobStep[],
|
||||
): workflow.Workflow {
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
sanitizeArtifactName,
|
||||
} from "./debug-artifacts";
|
||||
import * as dependencyCaching from "./dependency-caching";
|
||||
import { EnvVar, ReadOnlyEnv } from "./environment";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
@@ -316,8 +316,6 @@ export async function tryUploadSarifIfRunFailed(
|
||||
* @param config The CodeQL Action configuration.
|
||||
* @param repositoryNwo The name and owner of the repository.
|
||||
* @param features Information about enabled features.
|
||||
* @param jobStatus The status of the job, as reported by the Actions runtime environment.
|
||||
* @param env The environment to read variables from.
|
||||
* @param logger The logger to use.
|
||||
* @returns The results of uploading the SARIF file for the failure.
|
||||
*/
|
||||
@@ -333,11 +331,9 @@ export async function uploadFailureInfo(
|
||||
config: Config,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
features: FeatureEnablement,
|
||||
jobStatus: string | undefined,
|
||||
env: ReadOnlyEnv,
|
||||
logger: Logger,
|
||||
): Promise<UploadFailedSarifResult> {
|
||||
await recordOverlayStatus(codeql, config, features, jobStatus, env, logger);
|
||||
await recordOverlayStatus(codeql, config, features, logger);
|
||||
|
||||
const uploadFailedSarifResult = await tryUploadSarifIfRunFailed(
|
||||
config,
|
||||
@@ -416,37 +412,6 @@ export async function uploadFailureInfo(
|
||||
return uploadFailedSarifResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether one of the CodeQL Actions reported an error for this job, which means the analysis
|
||||
* genuinely failed.
|
||||
*
|
||||
* Note that the converse does not hold: an Action that is terminated abruptly, or that fails before
|
||||
* it can gather telemetry, does not get to report anything.
|
||||
*/
|
||||
function didCodeQlReportError(env: ReadOnlyEnv): boolean {
|
||||
const jobStatus = env.getOptional(EnvVar.JOB_STATUS);
|
||||
return (
|
||||
jobStatus === JobStatus.FailureStatus ||
|
||||
jobStatus === JobStatus.ConfigErrorStatus
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the job status tells us anything about whether the analysis itself would have succeeded.
|
||||
*
|
||||
* We check for the statuses we know to be meaningful rather than excluding the ones that are not,
|
||||
* so that a status we do not recognise is treated as inconclusive.
|
||||
*/
|
||||
function isConclusiveJobStatus(jobStatus: string | undefined): boolean {
|
||||
switch (jobStatus?.trim().toLowerCase()) {
|
||||
case "failure":
|
||||
case "success":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If overlay base database creation was attempted but the analysis did not complete
|
||||
* successfully, save the failure status to the Actions cache so that subsequent runs
|
||||
@@ -456,30 +421,16 @@ async function recordOverlayStatus(
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
features: FeatureEnablement,
|
||||
jobStatus: string | undefined,
|
||||
env: ReadOnlyEnv,
|
||||
logger: Logger,
|
||||
) {
|
||||
if (
|
||||
config.overlayDatabaseMode !== OverlayDatabaseMode.OverlayBase ||
|
||||
env.getOptional(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY) === "true" ||
|
||||
process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] === "true" ||
|
||||
!(await features.getValue(Feature.OverlayAnalysisStatusSave))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only record a failure when the job outcome tells us something about the analysis. A cancelled
|
||||
// job, or a status we do not recognise, says nothing about whether the analysis would have
|
||||
// succeeded, so recording a failure would disable overlay analysis needlessly. We still record
|
||||
// one if a CodeQL Action reported an error before the job ended.
|
||||
if (!isConclusiveJobStatus(jobStatus) && !didCodeQlReportError(env)) {
|
||||
logger.info(
|
||||
"Not recording an improved incremental analysis failure for this job because the job " +
|
||||
`status (${jobStatus ?? "unset"}) does not tell us whether the analysis itself failed.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const checkRunIdInput = actionsUtil.getOptionalInput("check-run-id");
|
||||
const checkRunId =
|
||||
checkRunIdInput !== undefined ? parseInt(checkRunIdInput, 10) : undefined;
|
||||
|
||||
@@ -8,7 +8,6 @@ import * as core from "@actions/core";
|
||||
|
||||
import {
|
||||
restoreInputs,
|
||||
getOptionalInput,
|
||||
getTemporaryDirectory,
|
||||
printDebugLogs,
|
||||
} from "./actions-util";
|
||||
@@ -21,7 +20,7 @@ import {
|
||||
DependencyCachingUsageReport,
|
||||
getDependencyCacheUsage,
|
||||
} from "./dependency-caching";
|
||||
import { EnvVar, getEnv } from "./environment";
|
||||
import { EnvVar } from "./environment";
|
||||
import { initFeatures } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import * as initActionPostHelper from "./init-action-post-helper";
|
||||
@@ -56,11 +55,6 @@ async function run(startedAt: Date) {
|
||||
| undefined;
|
||||
let dependencyCachingUsage: DependencyCachingUsageReport | undefined;
|
||||
try {
|
||||
// Read the job status before restoring inputs, since it is provided by the Actions runtime
|
||||
// environment for this step and would otherwise be overwritten by the value that the `init`
|
||||
// Action saw, which is always a success.
|
||||
const jobStatus = getOptionalInput("job-status");
|
||||
|
||||
// Restore inputs from `init` Action.
|
||||
restoreInputs();
|
||||
|
||||
@@ -90,8 +84,6 @@ async function run(startedAt: Date) {
|
||||
config,
|
||||
repositoryNwo,
|
||||
features,
|
||||
jobStatus,
|
||||
getEnv(),
|
||||
logger,
|
||||
);
|
||||
|
||||
|
||||
@@ -174,14 +174,6 @@ async function sendCompletedStatusReport(
|
||||
initToolsDownloadFields.tools_download_duration_ms =
|
||||
toolsDownloadStatusReport.downloadDurationMs;
|
||||
}
|
||||
if (toolsDownloadStatusReport?.extractionDurationMs !== undefined) {
|
||||
initToolsDownloadFields.tools_extraction_duration_ms =
|
||||
toolsDownloadStatusReport.extractionDurationMs;
|
||||
}
|
||||
if (toolsDownloadStatusReport?.totalDurationMs !== undefined) {
|
||||
initToolsDownloadFields.tools_total_duration_ms =
|
||||
toolsDownloadStatusReport.totalDurationMs;
|
||||
}
|
||||
if (toolsFeatureFlagsValid !== undefined) {
|
||||
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
||||
}
|
||||
|
||||
@@ -85,14 +85,6 @@ async function sendCompletedStatusReport(
|
||||
initToolsDownloadFields.tools_download_duration_ms =
|
||||
toolsDownloadStatusReport.downloadDurationMs;
|
||||
}
|
||||
if (toolsDownloadStatusReport?.extractionDurationMs !== undefined) {
|
||||
initToolsDownloadFields.tools_extraction_duration_ms =
|
||||
toolsDownloadStatusReport.extractionDurationMs;
|
||||
}
|
||||
if (toolsDownloadStatusReport?.totalDurationMs !== undefined) {
|
||||
initToolsDownloadFields.tools_total_duration_ms =
|
||||
toolsDownloadStatusReport.totalDurationMs;
|
||||
}
|
||||
if (toolsFeatureFlagsValid !== undefined) {
|
||||
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import * as github from "@actions/github";
|
||||
@@ -9,8 +7,7 @@ import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import * as diagnostics from "./diagnostics";
|
||||
import { ActionsEnvVars, EnvVar, ReadOnlyEnv } from "./environment";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
|
||||
@@ -30,7 +27,6 @@ import {
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import * as toolsDownload from "./tools-download";
|
||||
import {
|
||||
getErrorMessage,
|
||||
GitHubVariant,
|
||||
@@ -124,42 +120,24 @@ test.serial(
|
||||
const LINKED_BUNDLE_TEST_CASES = [
|
||||
{
|
||||
platform: "linux",
|
||||
arch: "x64",
|
||||
tarSupportsZstd: true,
|
||||
expectedBundleName: "codeql-bundle-linux64.tar.zst",
|
||||
expectedCompressionMethod: "zstd",
|
||||
},
|
||||
{
|
||||
platform: "linux",
|
||||
arch: "arm64",
|
||||
tarSupportsZstd: true,
|
||||
expectedBundleName: "codeql-bundle-linux-arm64.tar.zst",
|
||||
expectedCompressionMethod: "zstd",
|
||||
},
|
||||
{
|
||||
platform: "darwin",
|
||||
arch: "arm64",
|
||||
tarSupportsZstd: true,
|
||||
expectedBundleName: "codeql-bundle-osx64.tar.zst",
|
||||
expectedCompressionMethod: "zstd",
|
||||
},
|
||||
{
|
||||
platform: "darwin",
|
||||
arch: "x64",
|
||||
tarSupportsZstd: true,
|
||||
expectedBundleName: "codeql-bundle-osx64.tar.zst",
|
||||
expectedCompressionMethod: "zstd",
|
||||
},
|
||||
{
|
||||
platform: "win32",
|
||||
arch: "x64",
|
||||
tarSupportsZstd: true,
|
||||
expectedBundleName: "codeql-bundle-win64.tar.gz",
|
||||
expectedCompressionMethod: "gzip",
|
||||
},
|
||||
{
|
||||
platform: "linux",
|
||||
arch: "x64",
|
||||
tarSupportsZstd: false,
|
||||
expectedBundleName: "codeql-bundle-linux64.tar.gz",
|
||||
expectedCompressionMethod: "gzip",
|
||||
@@ -168,17 +146,15 @@ const LINKED_BUNDLE_TEST_CASES = [
|
||||
|
||||
for (const {
|
||||
platform,
|
||||
arch,
|
||||
tarSupportsZstd,
|
||||
expectedBundleName,
|
||||
expectedCompressionMethod,
|
||||
} of LINKED_BUNDLE_TEST_CASES) {
|
||||
test.serial(
|
||||
`getCodeQLSource selects ${expectedBundleName} for linked tools on ${platform}/${arch}`,
|
||||
`getCodeQLSource selects ${expectedBundleName} for linked tools`,
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
sinon.stub(process, "platform").value(platform);
|
||||
sinon.stub(process, "arch").value(arch);
|
||||
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
@@ -258,7 +234,6 @@ test.serial(
|
||||
codeqlFolder: "codeql",
|
||||
statusReport: {
|
||||
downloadDurationMs: 200,
|
||||
totalDurationMs: 300,
|
||||
},
|
||||
toolsVersion: LINKED_CLI_VERSION.cliVersion,
|
||||
});
|
||||
@@ -311,7 +286,6 @@ test.serial(
|
||||
codeqlFolder: "codeql",
|
||||
statusReport: {
|
||||
downloadDurationMs: 200,
|
||||
totalDurationMs: 300,
|
||||
},
|
||||
toolsVersion: expectedVersion,
|
||||
});
|
||||
@@ -943,616 +917,3 @@ test.serial(
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
/** 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. */
|
||||
const CLEANUP_BUNDLE_VERSION = "20240101";
|
||||
/** A version of the CodeQL tools that is already in the toolcache but that we are not going to use. */
|
||||
const CLEANUP_STALE_VERSION = "2.20.0";
|
||||
|
||||
/** Creates a directory in the toolcache that looks like a tool that `tool-cache` has cached. */
|
||||
function createToolcacheEntry(
|
||||
toolcacheRoot: string,
|
||||
tool: string,
|
||||
version: string,
|
||||
): string {
|
||||
const versionDirectory = path.join(toolcacheRoot, tool, version);
|
||||
const archDirectory = path.join(versionDirectory, os.arch());
|
||||
fs.mkdirSync(archDirectory, { recursive: true });
|
||||
fs.writeFileSync(path.join(archDirectory, "contents"), "x".repeat(1024));
|
||||
fs.writeFileSync(`${archDirectory}.complete`, "");
|
||||
return versionDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stubs out the download and the diagnostic sink, then downloads the CodeQL tools.
|
||||
*
|
||||
* @returns the extraction directory and the toolcache cleanup diagnostic, if emitted.
|
||||
*/
|
||||
async function runDownloadCodeQL(
|
||||
toolcacheRoot: string,
|
||||
features: Feature[],
|
||||
bundleVersion: string | undefined,
|
||||
): Promise<{
|
||||
codeqlFolder: string;
|
||||
cleanupDiagnostic: toolsDownload.ToolcacheCleanupResult | undefined;
|
||||
}> {
|
||||
sinon
|
||||
.stub(toolsDownload, "downloadAndExtract")
|
||||
.callsFake(async (_url, _compressionMethod, dest) => {
|
||||
// The real implementation creates the destination directory, which matters here because the
|
||||
// cleanup deletes it first and `writeToolcacheMarkerFile` writes into its parent afterwards.
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
return { totalDurationMs: 1 };
|
||||
});
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addNoLanguageDiagnostic");
|
||||
|
||||
const { codeqlFolder } = await setupCodeql.downloadCodeQL(
|
||||
"https://example.com/codeql-bundle.tar.gz",
|
||||
"gzip",
|
||||
bundleVersion,
|
||||
CLEANUP_CLI_VERSION,
|
||||
SAMPLE_DOTCOM_API_DETAILS,
|
||||
undefined, // tarVersion
|
||||
toolcacheRoot, // tempDir
|
||||
createFeatures(features),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
const diagnostic = addDiagnostic
|
||||
.getCalls()
|
||||
.map((call) => call.args[1])
|
||||
.find((d) => d.source?.id === "codeql-action/toolcache-bundle-cleanup");
|
||||
|
||||
return {
|
||||
codeqlFolder,
|
||||
cleanupDiagnostic: diagnostic?.attributes as
|
||||
| toolsDownload.ToolcacheCleanupResult
|
||||
| undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a toolcache containing the version of the CodeQL tools that we are about to download, a
|
||||
* different version of the CodeQL tools, and an unrelated tool, then downloads the CodeQL tools.
|
||||
*/
|
||||
async function testToolcacheCleanup(
|
||||
t: ExecutionContext<unknown>,
|
||||
{
|
||||
features,
|
||||
runnerEnvironment,
|
||||
setUp,
|
||||
}: {
|
||||
features: Feature[];
|
||||
runnerEnvironment: string | undefined;
|
||||
setUp?: () => void;
|
||||
},
|
||||
check: (context: {
|
||||
cleanupDiagnostic: toolsDownload.ToolcacheCleanupResult | undefined;
|
||||
destinationDirectory: string;
|
||||
staleDirectory: string;
|
||||
}) => void,
|
||||
) {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
if (runnerEnvironment === undefined) {
|
||||
delete process.env[ActionsEnvVars.RUNNER_ENVIRONMENT];
|
||||
} else {
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = runnerEnvironment;
|
||||
}
|
||||
setUp?.();
|
||||
|
||||
// The extraction of the bundle would normally create this directory.
|
||||
const destinationDirectory = createToolcacheEntry(
|
||||
tmpDir,
|
||||
"CodeQL",
|
||||
CLEANUP_CLI_VERSION,
|
||||
);
|
||||
const staleDirectory = createToolcacheEntry(
|
||||
tmpDir,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
const otherToolDirectory = createToolcacheEntry(tmpDir, "Node", "20.0.0");
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
tmpDir,
|
||||
features,
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
t.true(
|
||||
fs.existsSync(otherToolDirectory),
|
||||
"Should never delete other tools from the toolcache.",
|
||||
);
|
||||
|
||||
check({ cleanupDiagnostic, destinationDirectory, staleDirectory });
|
||||
});
|
||||
}
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not clean up the toolcache when the feature flag is disabled",
|
||||
async (t) => {
|
||||
await testToolcacheCleanup(
|
||||
t,
|
||||
{ features: [], runnerEnvironment: "github-hosted" },
|
||||
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
|
||||
t.true(fs.existsSync(staleDirectory));
|
||||
t.true(fs.existsSync(destinationDirectory));
|
||||
t.is(cleanupDiagnostic, undefined);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not clean up the toolcache when the runner is not GitHub-hosted",
|
||||
async (t) => {
|
||||
await testToolcacheCleanup(
|
||||
t,
|
||||
{
|
||||
features: [Feature.CleanupToolcacheBundles],
|
||||
runnerEnvironment: "self-hosted",
|
||||
},
|
||||
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
|
||||
t.true(fs.existsSync(staleDirectory));
|
||||
t.true(fs.existsSync(destinationDirectory));
|
||||
t.is(cleanupDiagnostic, undefined);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not clean up the toolcache when the runner environment is unknown",
|
||||
async (t) => {
|
||||
// A runner that doesn't report its environment must be treated as not GitHub-hosted, since its
|
||||
// toolcache may well outlive the job.
|
||||
await testToolcacheCleanup(
|
||||
t,
|
||||
{
|
||||
features: [Feature.CleanupToolcacheBundles],
|
||||
runnerEnvironment: undefined,
|
||||
},
|
||||
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
|
||||
t.true(fs.existsSync(staleDirectory));
|
||||
t.true(fs.existsSync(destinationDirectory));
|
||||
t.is(cleanupDiagnostic, undefined);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL deletes other CodeQL bundles from the toolcache when enabled on a GitHub-hosted runner",
|
||||
async (t) => {
|
||||
await testToolcacheCleanup(
|
||||
t,
|
||||
{
|
||||
features: [Feature.CleanupToolcacheBundles],
|
||||
runnerEnvironment: "github-hosted",
|
||||
},
|
||||
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
|
||||
t.false(
|
||||
fs.existsSync(staleDirectory),
|
||||
"Should delete the version directory, including the `tool-cache` marker file it contains.",
|
||||
);
|
||||
t.false(
|
||||
fs.existsSync(path.join(destinationDirectory, os.arch(), "contents")),
|
||||
"Should also delete a partial entry for the version we are about to download, rather " +
|
||||
"than extracting over it.",
|
||||
);
|
||||
t.deepEqual(cleanupDiagnostic, {
|
||||
deletedVersions: [CLEANUP_STALE_VERSION, CLEANUP_CLI_VERSION].sort(),
|
||||
failed: false,
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL reports no deleted versions when the toolcache has no CodeQL bundles",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
// A toolcache with other tools in it, but no CodeQL.
|
||||
const otherToolDirectory = createToolcacheEntry(tmpDir, "Node", "20.0.0");
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
tmpDir,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
t.true(fs.existsSync(otherToolDirectory));
|
||||
t.deepEqual(cleanupDiagnostic, { deletedVersions: [], failed: false });
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL continues when deleting a CodeQL bundle from the toolcache fails",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
createToolcacheEntry(tmpDir, "CodeQL", CLEANUP_CLI_VERSION);
|
||||
const staleDirectory = createToolcacheEntry(
|
||||
tmpDir,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
|
||||
const rmStub = sinon
|
||||
.stub(fs.promises, "rm")
|
||||
.rejects(new Error("EACCES: permission denied"));
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
tmpDir,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
// Restore before `withTmpDir` cleans up after itself.
|
||||
rmStub.restore();
|
||||
|
||||
t.true(fs.existsSync(staleDirectory));
|
||||
t.deepEqual(
|
||||
cleanupDiagnostic,
|
||||
{ deletedVersions: [], failed: true },
|
||||
"Should not report versions that we failed to delete.",
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"deleteToolcacheBundles reports a failure when the toolcache location is unknown",
|
||||
async (t) => {
|
||||
const messages: LoggedMessage[] = [];
|
||||
|
||||
const result = await toolsDownload.deleteToolcacheBundles({
|
||||
env: new ReadOnlyEnv({}),
|
||||
logger: getRecordingLogger(messages),
|
||||
});
|
||||
|
||||
t.deepEqual(
|
||||
result,
|
||||
{ deletedVersions: [], failed: true },
|
||||
"Should report a failure rather than throwing, so the download can continue.",
|
||||
);
|
||||
checkExpectedLogMessages(t, messages, [
|
||||
"Unable to determine toolcache directory: RUNNER_TOOL_CACHE environment variable must be set",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"deleteToolcacheBundles uses the supplied environment rather than process.env",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const ambientRoot = path.join(tmpDir, "ambient");
|
||||
const injectedRoot = path.join(tmpDir, "injected");
|
||||
setupActionsVars(tmpDir, ambientRoot);
|
||||
|
||||
const ambientVersion = createToolcacheEntry(
|
||||
ambientRoot,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
const injectedVersion = createToolcacheEntry(
|
||||
injectedRoot,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
const fileEntry = path.join(injectedRoot, "CodeQL", "not-a-directory");
|
||||
fs.writeFileSync(fileEntry, "keep");
|
||||
|
||||
const result = await toolsDownload.deleteToolcacheBundles({
|
||||
env: new ReadOnlyEnv({
|
||||
[ActionsEnvVars.RUNNER_TOOL_CACHE]: injectedRoot,
|
||||
}),
|
||||
logger: getRunnerLogger(true),
|
||||
});
|
||||
|
||||
t.deepEqual(result, {
|
||||
deletedVersions: [CLEANUP_STALE_VERSION],
|
||||
failed: false,
|
||||
});
|
||||
t.false(fs.existsSync(injectedVersion));
|
||||
t.true(fs.existsSync(ambientVersion));
|
||||
t.is(fs.readFileSync(fileEntry, "utf8"), "keep");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"deleteToolcacheBundles reports a failure when the toolcache directory cannot be read",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const versionDirectory = createToolcacheEntry(
|
||||
tmpDir,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
const messages: LoggedMessage[] = [];
|
||||
const readdir = sinon
|
||||
.stub(fs.promises, "readdir")
|
||||
.rejects(new Error("permission denied"));
|
||||
|
||||
try {
|
||||
const result = await toolsDownload.deleteToolcacheBundles({
|
||||
env: new ReadOnlyEnv({
|
||||
[ActionsEnvVars.RUNNER_TOOL_CACHE]: tmpDir,
|
||||
}),
|
||||
logger: getRecordingLogger(messages),
|
||||
});
|
||||
|
||||
t.deepEqual(result, { deletedVersions: [], failed: true });
|
||||
t.true(fs.existsSync(versionDirectory));
|
||||
checkExpectedLogMessages(t, messages, [
|
||||
`Failed to clean up the CodeQL toolcache at '${path.join(tmpDir, "CodeQL")}': permission denied`,
|
||||
]);
|
||||
} finally {
|
||||
readdir.restore();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not follow a symlinked CodeQL toolcache directory",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const toolcacheRoot = path.join(tmpDir, "toolcache");
|
||||
setupActionsVars(tmpDir, toolcacheRoot);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
// Somewhere the toolcache cleanup must never reach.
|
||||
const outsideDirectory = path.join(tmpDir, "outside");
|
||||
createToolcacheEntry(outsideDirectory, "CodeQL", CLEANUP_STALE_VERSION);
|
||||
createToolcacheEntry(outsideDirectory, "CodeQL", CLEANUP_CLI_VERSION);
|
||||
|
||||
fs.mkdirSync(toolcacheRoot, { recursive: true });
|
||||
fs.symlinkSync(
|
||||
path.join(outsideDirectory, "CodeQL"),
|
||||
path.join(toolcacheRoot, "CodeQL"),
|
||||
);
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
toolcacheRoot,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
t.true(
|
||||
fs.existsSync(
|
||||
path.join(outsideDirectory, "CodeQL", CLEANUP_STALE_VERSION),
|
||||
),
|
||||
"Should not delete anything through a symlinked CodeQL directory.",
|
||||
);
|
||||
t.deepEqual(cleanupDiagnostic, { deletedVersions: [], failed: true });
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not clean up the toolcache once a step has already set up CodeQL",
|
||||
async (t) => {
|
||||
// `.github/workflows/codeql.yml` sets up CodeQL twice and then runs both returned paths. If the
|
||||
// second setup downloads, it must not delete the bundle the first one handed out.
|
||||
await testToolcacheCleanup(
|
||||
t,
|
||||
{
|
||||
features: [Feature.CleanupToolcacheBundles],
|
||||
runnerEnvironment: "github-hosted",
|
||||
setUp: () => {
|
||||
process.env[EnvVar.HAS_SET_UP_CODEQL] = "true";
|
||||
},
|
||||
},
|
||||
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
|
||||
t.true(fs.existsSync(staleDirectory));
|
||||
t.true(fs.existsSync(destinationDirectory));
|
||||
t.is(cleanupDiagnostic, undefined);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"setupCodeQLBundle records that this job has set up CodeQL",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
delete process.env[EnvVar.HAS_SET_UP_CODEQL];
|
||||
|
||||
sinon.stub(setupCodeql, "downloadCodeQL").resolves({
|
||||
codeqlFolder: "codeql",
|
||||
statusReport: { totalDurationMs: 1 },
|
||||
toolsVersion: LINKED_CLI_VERSION.cliVersion,
|
||||
});
|
||||
|
||||
await setupCodeql.setupCodeQLBundle(
|
||||
"linked",
|
||||
SAMPLE_DOTCOM_API_DETAILS,
|
||||
tmpDir,
|
||||
GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
createFeatures([]),
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
t.is(
|
||||
process.env[EnvVar.HAS_SET_UP_CODEQL],
|
||||
"true",
|
||||
"A later step must be able to tell that the toolcache is in use.",
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL cleans up the toolcache even when the download will not be cached",
|
||||
async (t) => {
|
||||
// A `tools` URL we can't derive a bundle version from is extracted to a temporary directory
|
||||
// rather than the toolcache, but the toolcache is on the same filesystem, so emptying it still
|
||||
// frees up space for the analysis.
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
const staleDirectory = createToolcacheEntry(
|
||||
tmpDir,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
|
||||
const { codeqlFolder, cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
tmpDir,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
undefined, // bundleVersion
|
||||
);
|
||||
|
||||
t.is(path.dirname(codeqlFolder), tmpDir);
|
||||
t.not(codeqlFolder, path.join(tmpDir, "CodeQL"));
|
||||
t.true(fs.existsSync(codeqlFolder));
|
||||
t.false(fs.existsSync(`${codeqlFolder}.complete`));
|
||||
t.false(fs.existsSync(staleDirectory));
|
||||
t.deepEqual(cleanupDiagnostic, {
|
||||
deletedVersions: [CLEANUP_STALE_VERSION],
|
||||
failed: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL reports a failure when the toolcache cannot be inspected",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
createToolcacheEntry(tmpDir, "CodeQL", CLEANUP_STALE_VERSION);
|
||||
|
||||
const lstatStub = sinon.stub(fs.promises, "lstat").rejects(
|
||||
Object.assign(new Error("permission denied"), {
|
||||
code: "EACCES",
|
||||
}),
|
||||
);
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
tmpDir,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
lstatStub.restore();
|
||||
|
||||
t.deepEqual(
|
||||
cleanupDiagnostic,
|
||||
{ deletedVersions: [], failed: true },
|
||||
"An error other than the toolcache being absent must not be reported as success.",
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not clean up a toolcache on a different filesystem to the workspace",
|
||||
async (t) => {
|
||||
// Some runner images keep the toolcache on a different volume to the workspace, in which case
|
||||
// deleting the tools frees up disk space that the analysis cannot use.
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
const staleDirectory = createToolcacheEntry(
|
||||
tmpDir,
|
||||
"CodeQL",
|
||||
CLEANUP_STALE_VERSION,
|
||||
);
|
||||
|
||||
sinon
|
||||
.stub(toolsDownload, "isToolcacheOnWorkspaceFilesystem")
|
||||
.returns(false);
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
tmpDir,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
t.true(fs.existsSync(staleDirectory));
|
||||
t.is(cleanupDiagnostic, undefined);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"isToolcacheOnWorkspaceFilesystem assumes a different filesystem when it cannot tell",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
t.true(toolsDownload.isToolcacheOnWorkspaceFilesystem(logger));
|
||||
|
||||
// If we can't tell, we assume the toolcache is not somewhere we can reclaim space from.
|
||||
process.env[ActionsEnvVars.RUNNER_TOOL_CACHE] = path.join(
|
||||
tmpDir,
|
||||
"does-not-exist",
|
||||
);
|
||||
t.false(toolsDownload.isToolcacheOnWorkspaceFilesystem(logger));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadCodeQL does not delete through a symlinked version directory",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const toolcacheRoot = path.join(tmpDir, "toolcache");
|
||||
setupActionsVars(tmpDir, toolcacheRoot);
|
||||
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = "github-hosted";
|
||||
|
||||
createToolcacheEntry(toolcacheRoot, "CodeQL", CLEANUP_STALE_VERSION);
|
||||
|
||||
// Somewhere outside the toolcache that a version directory points at.
|
||||
const outsideDirectory = path.join(tmpDir, "outside");
|
||||
fs.mkdirSync(outsideDirectory, { recursive: true });
|
||||
fs.writeFileSync(path.join(outsideDirectory, "contents"), "x");
|
||||
fs.symlinkSync(
|
||||
outsideDirectory,
|
||||
path.join(toolcacheRoot, "CodeQL", "9.9.9"),
|
||||
);
|
||||
|
||||
const { cleanupDiagnostic } = await runDownloadCodeQL(
|
||||
toolcacheRoot,
|
||||
[Feature.CleanupToolcacheBundles],
|
||||
CLEANUP_BUNDLE_VERSION,
|
||||
);
|
||||
|
||||
t.true(
|
||||
fs.existsSync(path.join(outsideDirectory, "contents")),
|
||||
"Should not delete anything through a symlinked version directory.",
|
||||
);
|
||||
t.true(
|
||||
fs
|
||||
.lstatSync(path.join(toolcacheRoot, "CodeQL", "9.9.9"))
|
||||
.isSymbolicLink(),
|
||||
);
|
||||
t.deepEqual(cleanupDiagnostic, {
|
||||
deletedVersions: [CLEANUP_STALE_VERSION],
|
||||
failed: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2,17 +2,14 @@ import * as fs from "fs";
|
||||
import { OutgoingHttpHeaders } from "http";
|
||||
import * as path from "path";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import * as toolcache from "@actions/tool-cache";
|
||||
import { default as deepEqual } from "fast-deep-equal";
|
||||
import * as semver from "semver";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import { ActionState } from "./action-common";
|
||||
import {
|
||||
isAnalyzingPullRequest,
|
||||
isDynamicWorkflow,
|
||||
isGitHubHostedRunner,
|
||||
isRunningLocalAction,
|
||||
} from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
@@ -22,7 +19,6 @@ import {
|
||||
makeDiagnostic,
|
||||
makeTelemetryDiagnostic,
|
||||
} from "./diagnostics";
|
||||
import { EnvVar, getEnv } from "./environment";
|
||||
import {
|
||||
CODEQL_VERSION_ZSTD_BUNDLE,
|
||||
CodeQLDefaultVersionInfo,
|
||||
@@ -34,10 +30,8 @@ import { Logger } from "./logging";
|
||||
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
|
||||
import * as tar from "./tar";
|
||||
import {
|
||||
deleteToolcacheBundles,
|
||||
downloadAndExtract,
|
||||
getToolcacheDirectory,
|
||||
isToolcacheOnWorkspaceFilesystem,
|
||||
ToolsDownloadStatusReport,
|
||||
writeToolcacheMarkerFile,
|
||||
} from "./tools-download";
|
||||
@@ -81,7 +75,7 @@ export function getCodeQLBundleName(
|
||||
if (process.platform === "win32") {
|
||||
platform = "win64";
|
||||
} else if (process.platform === "linux") {
|
||||
platform = process.arch === "arm64" ? "linux-arm64" : "linux64";
|
||||
platform = "linux64";
|
||||
} else if (process.platform === "darwin") {
|
||||
platform = "osx64";
|
||||
} else {
|
||||
@@ -790,7 +784,6 @@ export const downloadCodeQL = async function (
|
||||
apiDetails: api.GitHubApiDetails,
|
||||
tarVersion: tar.TarVersion | undefined,
|
||||
tempDir: string,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<{
|
||||
codeqlFolder: string;
|
||||
@@ -824,8 +817,6 @@ export const downloadCodeQL = async function (
|
||||
const extractedBundlePath =
|
||||
toolcacheInfo?.path ?? getTempExtractionDir(tempDir);
|
||||
|
||||
await tryDeleteToolcacheBundles({ env: getEnv(), features, logger });
|
||||
|
||||
const statusReport = await downloadAndExtract(
|
||||
codeqlURL,
|
||||
compressionMethod,
|
||||
@@ -878,48 +869,6 @@ function getToolcacheDestinationInfo(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reclaims disk space by deleting the CodeQL tools from the toolcache, if enabled.
|
||||
*
|
||||
* On GitHub-hosted runners the toolcache shares a filesystem with the workspace, so tools left in
|
||||
* the toolcache take up space that the analysis could use instead. This holds wherever we extract
|
||||
* the tools we are obtaining, since the toolcache is on that filesystem either way.
|
||||
*/
|
||||
async function tryDeleteToolcacheBundles({
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
}: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>): Promise<void> {
|
||||
// A step that has already set up CodeQL may hand out a path into the toolcache that a later step
|
||||
// runs, so only the first step to set it up can know that nothing else relies on the toolcache.
|
||||
if (env.getOptional(EnvVar.HAS_SET_UP_CODEQL) !== undefined) {
|
||||
logger.debug(
|
||||
"Not deleting the CodeQL tools from the toolcache since a previous step in this job has " +
|
||||
"already set up CodeQL.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!isGitHubHostedRunner() ||
|
||||
!isToolcacheOnWorkspaceFilesystem(logger) ||
|
||||
!(await features.getValue(Feature.CleanupToolcacheBundles))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await deleteToolcacheBundles({ env, logger });
|
||||
|
||||
addNoLanguageDiagnostic(
|
||||
undefined,
|
||||
makeTelemetryDiagnostic(
|
||||
"codeql-action/toolcache-bundle-cleanup",
|
||||
"Toolcache CodeQL bundle cleanup",
|
||||
{ ...result },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getCodeQLURLVersion(url: string): string {
|
||||
const match = url.match(/\/codeql-bundle-(.*)\//);
|
||||
if (match === null || match.length < 2) {
|
||||
@@ -1029,7 +978,6 @@ export async function setupCodeQLBundle(
|
||||
apiDetails,
|
||||
zstdAvailability.version,
|
||||
tempDir,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
toolsVersion = result.toolsVersion;
|
||||
@@ -1041,11 +989,6 @@ export async function setupCodeQLBundle(
|
||||
default:
|
||||
util.assertNever(source);
|
||||
}
|
||||
|
||||
// Record that this job now has a copy of the CodeQL tools, so that a later step doesn't delete
|
||||
// the toolcache out from under the path we are about to return.
|
||||
core.exportVariable(EnvVar.HAS_SET_UP_CODEQL, "true");
|
||||
|
||||
return {
|
||||
codeqlFolder,
|
||||
toolsDownloadStatusReport,
|
||||
|
||||
@@ -14,10 +14,7 @@ import {
|
||||
isSelfHostedRunner,
|
||||
} from "./actions-util";
|
||||
import { getAnalysisKey, getApiClient } from "./api-client";
|
||||
import {
|
||||
getCachedCodeQlVersion,
|
||||
getCommandCacheFilePath,
|
||||
} from "./cli/output-cache";
|
||||
import { getCachedCodeQlVersion } from "./cli/output-cache";
|
||||
import type { Config } from "./config/action-config";
|
||||
import type { ComputedInput, InputName } from "./config/inputs";
|
||||
import { parseRegistriesWithoutCredentials } from "./config/pack-registries";
|
||||
@@ -379,10 +376,7 @@ export async function createStatusReportBase(
|
||||
core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt);
|
||||
}
|
||||
const runnerOs = getRequiredEnvParam("RUNNER_OS");
|
||||
const codeQlCliVersion = getCachedCodeQlVersion(
|
||||
logger,
|
||||
getCommandCacheFilePath(getEnv()),
|
||||
);
|
||||
const codeQlCliVersion = getCachedCodeQlVersion(logger, getEnv());
|
||||
const actionRef = process.env["GITHUB_ACTION_REF"] || "";
|
||||
const testingEnvironment = getTestingEnvironment();
|
||||
// re-export the testing environment variable so that it is available to subsequent steps,
|
||||
@@ -626,21 +620,8 @@ export interface InitWithConfigStatusReport extends InitStatusReport {
|
||||
|
||||
/** Fields of the init status report populated when the tools source is `download`. */
|
||||
export interface InitToolsDownloadFields {
|
||||
/**
|
||||
* Time taken to download the bundle, in milliseconds. Not populated when the bundle is downloaded
|
||||
* and extracted concurrently.
|
||||
*/
|
||||
/** Time taken to download the bundle, in milliseconds. */
|
||||
tools_download_duration_ms?: number;
|
||||
/**
|
||||
* Time taken to extract the bundle, in milliseconds. Not populated when the bundle is downloaded
|
||||
* and extracted concurrently.
|
||||
*/
|
||||
tools_extraction_duration_ms?: number;
|
||||
/**
|
||||
* Total time taken to make the bundle available on disk, in milliseconds. This includes any time
|
||||
* spent on a streaming attempt that failed and fell back to downloading before extracting.
|
||||
*/
|
||||
tools_total_duration_ms?: number;
|
||||
/**
|
||||
* Whether the relevant tools dotcom feature flags have been misconfigured.
|
||||
* Only populated if we attempt to determine the default version based on the dotcom feature flags. */
|
||||
|
||||
@@ -937,9 +937,7 @@ export function mockBundleDownloadApi({
|
||||
process.platform === "win32"
|
||||
? "win64"
|
||||
: process.platform === "linux"
|
||||
? process.arch === "arm64"
|
||||
? "linux-arm64"
|
||||
: "linux64"
|
||||
? "linux64"
|
||||
: "osx64";
|
||||
|
||||
const baseUrl = apiDetails?.url ?? "https://example.com";
|
||||
|
||||
@@ -15,7 +15,7 @@ import { withTmpDir } from "./util";
|
||||
setupTests(test);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract reports the durations when downloading before extracting",
|
||||
"downloadAndExtract reports the duration when downloading before extracting",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.gz");
|
||||
@@ -34,8 +34,6 @@ test.serial(
|
||||
);
|
||||
|
||||
t.assert(Number.isInteger(statusReport.downloadDurationMs));
|
||||
t.assert(Number.isInteger(statusReport.extractionDurationMs));
|
||||
t.assert(Number.isInteger(statusReport.totalDurationMs));
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -69,7 +67,6 @@ test.serial(
|
||||
);
|
||||
|
||||
t.assert(Number.isInteger(statusReport.downloadDurationMs));
|
||||
t.assert(Number.isInteger(statusReport.totalDurationMs));
|
||||
t.true(request.isDone());
|
||||
t.false(extractTarZst.called);
|
||||
t.true(downloadTool.calledOnce);
|
||||
@@ -79,7 +76,7 @@ test.serial(
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract reports only the total duration when streaming extraction",
|
||||
"downloadAndExtract omits the download duration when streaming extraction",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
@@ -109,9 +106,7 @@ test.serial(
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
t.assert(Number.isInteger(statusReport.totalDurationMs));
|
||||
t.is(statusReport.downloadDurationMs, undefined);
|
||||
t.is(statusReport.extractionDurationMs, undefined);
|
||||
t.deepEqual(statusReport, {});
|
||||
t.false(downloadTool.called);
|
||||
t.true(extractTarZst.calledOnce);
|
||||
t.true(request.isDone());
|
||||
|
||||
@@ -10,8 +10,6 @@ import * as toolcache from "@actions/tool-cache";
|
||||
import { https } from "follow-redirects";
|
||||
import * as semver from "semver";
|
||||
|
||||
import { ActionState } from "./action-common";
|
||||
import { ActionsEnvVars, getEnv, ReadOnlyEnv } from "./environment";
|
||||
import { formatDuration, Logger } from "./logging";
|
||||
import * as tar from "./tar";
|
||||
import { cleanUpPath, getErrorMessage, getRequiredEnvParam } from "./util";
|
||||
@@ -33,21 +31,7 @@ const STREAMING_STALL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
||||
const TOOLCACHE_TOOL_NAME = "CodeQL";
|
||||
|
||||
export type ToolsDownloadStatusReport = {
|
||||
/**
|
||||
* Time spent downloading the bundle, in milliseconds. Not populated when the bundle is downloaded
|
||||
* and extracted concurrently, since the two cannot be told apart.
|
||||
*/
|
||||
downloadDurationMs?: number;
|
||||
/**
|
||||
* Time spent extracting the bundle, in milliseconds. Not populated when the bundle is downloaded
|
||||
* and extracted concurrently, since the two cannot be told apart.
|
||||
*/
|
||||
extractionDurationMs?: number;
|
||||
/**
|
||||
* Total time taken to make the bundle available on disk, in milliseconds. This includes any time
|
||||
* spent on a streaming attempt that failed and fell back to downloading before extracting.
|
||||
*/
|
||||
totalDurationMs: number;
|
||||
};
|
||||
|
||||
export async function downloadAndExtract(
|
||||
@@ -63,12 +47,11 @@ export async function downloadAndExtract(
|
||||
`Downloading CodeQL tools from ${codeqlURL} . This may take a while.`,
|
||||
);
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
try {
|
||||
if (compressionMethod === "zstd" && process.platform === "linux") {
|
||||
logger.info(`Streaming the extraction of the CodeQL bundle.`);
|
||||
|
||||
const toolsInstallStart = performance.now();
|
||||
await downloadAndExtractZstdWithStreaming(
|
||||
codeqlURL,
|
||||
dest,
|
||||
@@ -78,14 +61,16 @@ export async function downloadAndExtract(
|
||||
logger,
|
||||
);
|
||||
|
||||
const totalDurationMs = Math.round(performance.now() - startTime);
|
||||
const combinedDurationMs = Math.round(
|
||||
performance.now() - toolsInstallStart,
|
||||
);
|
||||
logger.info(
|
||||
`Finished downloading and extracting CodeQL bundle to ${dest} (${formatDuration(
|
||||
totalDurationMs,
|
||||
combinedDurationMs,
|
||||
)}).`,
|
||||
);
|
||||
|
||||
return { totalDurationMs };
|
||||
return {};
|
||||
}
|
||||
} catch (e) {
|
||||
core.warning(
|
||||
@@ -113,7 +98,7 @@ export async function downloadAndExtract(
|
||||
)}).`,
|
||||
);
|
||||
|
||||
let extractionDurationMs: number | undefined;
|
||||
let extractionDurationMs: number;
|
||||
|
||||
try {
|
||||
logger.info("Extracting CodeQL bundle.");
|
||||
@@ -135,11 +120,7 @@ export async function downloadAndExtract(
|
||||
await cleanUpPath(archivedBundlePath, "CodeQL bundle archive", logger);
|
||||
}
|
||||
|
||||
return {
|
||||
downloadDurationMs,
|
||||
extractionDurationMs,
|
||||
totalDurationMs: Math.round(performance.now() - startTime),
|
||||
};
|
||||
return { downloadDurationMs };
|
||||
}
|
||||
|
||||
async function downloadAndExtractZstdWithStreaming(
|
||||
@@ -199,154 +180,16 @@ async function downloadAndExtractZstdWithStreaming(
|
||||
await tar.extractTarZst(response, dest, tarVersion, logger);
|
||||
}
|
||||
|
||||
/** Gets the path to the toolcache directory that holds all versions of the CodeQL tools. */
|
||||
function getToolcacheToolDirectory(env: ReadOnlyEnv): string {
|
||||
return path.join(
|
||||
env.getRequired(ActionsEnvVars.RUNNER_TOOL_CACHE),
|
||||
TOOLCACHE_TOOL_NAME,
|
||||
);
|
||||
}
|
||||
|
||||
/** Gets the name of the toolcache directory that holds the given version of the CodeQL tools. */
|
||||
function getToolcacheVersionDirectoryName(version: string): string {
|
||||
return semver.clean(version) || version;
|
||||
}
|
||||
|
||||
/** Gets the path to the toolcache directory for the specified version of the CodeQL tools. */
|
||||
export function getToolcacheDirectory(version: string): string {
|
||||
return path.join(
|
||||
getToolcacheToolDirectory(getEnv()),
|
||||
getToolcacheVersionDirectoryName(version),
|
||||
getRequiredEnvParam("RUNNER_TOOL_CACHE"),
|
||||
TOOLCACHE_TOOL_NAME,
|
||||
semver.clean(version) || version,
|
||||
os.arch() || "",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the toolcache is on the same filesystem as the workspace, and so whether deleting the
|
||||
* tools frees up disk space that the analysis can use.
|
||||
*
|
||||
* These are separate volumes on some runner images. Windows runners, for example, keep the
|
||||
* toolcache on `C:` while the workspace is on `D:`.
|
||||
*/
|
||||
export function isToolcacheOnWorkspaceFilesystem(logger: Logger): boolean {
|
||||
try {
|
||||
return (
|
||||
fs.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev ===
|
||||
fs.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev
|
||||
);
|
||||
} catch (e) {
|
||||
logger.debug(
|
||||
`Could not determine whether the toolcache is on the same filesystem as the workspace: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** The outcome of trying to reclaim disk space by deleting the CodeQL tools from the toolcache. */
|
||||
export interface ToolcacheCleanupResult {
|
||||
/** The versions of the CodeQL tools that were deleted. */
|
||||
deletedVersions: string[];
|
||||
/**
|
||||
* Whether we hit an error while trying to delete the tools. Distinguishes a toolcache that had
|
||||
* nothing to reclaim from one we failed to clean up.
|
||||
*/
|
||||
failed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes every version of the CodeQL tools from the toolcache.
|
||||
*
|
||||
* Only safe to call when we are about to download the tools, since that means we did not resolve
|
||||
* them from the toolcache and so nothing in there is in use by this job.
|
||||
*
|
||||
* This only ever touches the CodeQL directory of the toolcache. Cleanup errors are logged and
|
||||
* returned as `failed: true` rather than thrown.
|
||||
*
|
||||
* @returns the versions that were deleted, and whether we hit an error while trying.
|
||||
*/
|
||||
export async function deleteToolcacheBundles({
|
||||
env,
|
||||
logger,
|
||||
}: ActionState<["Logger", "ReadOnlyEnv"]>): Promise<ToolcacheCleanupResult> {
|
||||
let toolDirectory: string;
|
||||
|
||||
try {
|
||||
toolDirectory = getToolcacheToolDirectory(env);
|
||||
} catch (e) {
|
||||
logger.info(
|
||||
`Unable to determine toolcache directory: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return { deletedVersions: [], failed: true };
|
||||
}
|
||||
|
||||
try {
|
||||
// Refuse to follow a symlinked CodeQL directory, so that we can only ever delete paths that are
|
||||
// really inside the toolcache.
|
||||
if ((await fs.promises.lstat(toolDirectory)).isSymbolicLink()) {
|
||||
logger.info(
|
||||
`Not deleting the CodeQL tools from the toolcache since '${toolDirectory}' is a symlink.`,
|
||||
);
|
||||
return { deletedVersions: [], failed: true };
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e?.code === "ENOENT") {
|
||||
logger.debug(
|
||||
`There are no CodeQL tools at '${toolDirectory}' to delete from the toolcache.`,
|
||||
);
|
||||
return { deletedVersions: [], failed: false };
|
||||
}
|
||||
logger.info(
|
||||
`Failed to inspect the CodeQL tools at '${toolDirectory}': ${getErrorMessage(e)}`,
|
||||
);
|
||||
return { deletedVersions: [], failed: true };
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = await fs.promises.readdir(toolDirectory, {
|
||||
withFileTypes: true,
|
||||
});
|
||||
|
||||
const deletedVersions: string[] = [];
|
||||
let failed = false;
|
||||
|
||||
for (const entry of entries) {
|
||||
// `isDirectory` is false for a symlink, so we never delete a version directory that is
|
||||
// really somewhere else.
|
||||
if (!entry.isDirectory()) {
|
||||
logger.debug(
|
||||
`Not deleting '${entry.name}' from the CodeQL toolcache since it is not a directory.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const versionDirectory = path.join(toolDirectory, entry.name);
|
||||
|
||||
try {
|
||||
await fs.promises.rm(versionDirectory, {
|
||||
force: true,
|
||||
recursive: true,
|
||||
});
|
||||
deletedVersions.push(entry.name);
|
||||
logger.info(
|
||||
`Deleted the CodeQL tools at '${versionDirectory}' from the toolcache to free up disk space.`,
|
||||
);
|
||||
} catch (e) {
|
||||
failed = true;
|
||||
logger.info(
|
||||
`Failed to delete the CodeQL tools at '${versionDirectory}' from the toolcache: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { deletedVersions: deletedVersions.sort(), failed };
|
||||
} catch (e) {
|
||||
logger.info(
|
||||
`Failed to clean up the CodeQL toolcache at '${toolDirectory}': ${getErrorMessage(e)}`,
|
||||
);
|
||||
return { deletedVersions: [], failed: true };
|
||||
}
|
||||
}
|
||||
|
||||
export function writeToolcacheMarkerFile(
|
||||
extractedPath: string,
|
||||
logger: Logger,
|
||||
|
||||
@@ -842,13 +842,9 @@ export async function checkForTimeout() {
|
||||
* directory with the name hostedtoolcache which is present on
|
||||
* GitHub-hosted runners.
|
||||
*
|
||||
* Since this is a heuristic over how the runner happens to be named and laid out, it also matches
|
||||
* self-hosted runners that are configured to resemble hosted ones. Prefer
|
||||
* `isGitHubHostedRunner` when you need the answer the Actions service reports.
|
||||
*
|
||||
* @returns true iff the runner looks like it is hosted by GitHub
|
||||
* @returns true iff the runner is hosted by GitHub
|
||||
*/
|
||||
export function looksLikeHostedRunner() {
|
||||
export function isHostedRunner() {
|
||||
return (
|
||||
// Name of the runner on hosted Windows runners
|
||||
process.env["RUNNER_NAME"]?.includes("Hosted Agent") ||
|
||||
|
||||
@@ -29,6 +29,6 @@ outputs:
|
||||
proxy_urls:
|
||||
description: A stringified JSON array of objects containing the types and URLs of the configured registries.
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: "../lib/start-proxy-entry.js"
|
||||
post: "../lib/start-proxy-post-entry.js"
|
||||
|
||||
@@ -37,5 +37,5 @@
|
||||
"@octokit/core/dist-types/types": ["./node_modules/@octokit/core/dist-types/types.d.ts"]
|
||||
},
|
||||
},
|
||||
"exclude": ["node_modules", "pr-checks", "scripts/changetool"]
|
||||
"exclude": ["node_modules", "pr-checks"]
|
||||
}
|
||||
|
||||
@@ -41,6 +41,6 @@ outputs:
|
||||
|
||||
{ "code-scanning": "some-id", "code-quality": "some-other-id" }
|
||||
runs:
|
||||
using: node24
|
||||
using: node20
|
||||
main: '../lib/upload-sarif-entry.js'
|
||||
post: '../lib/upload-sarif-post-entry.js'
|
||||
|
||||
Reference in New Issue
Block a user