Compare commits

..

5 Commits

Author SHA1 Message Date
Michael B. Gale 9fe90880f7 Report undefined when FFs are unavailable 2026-01-28 13:14:57 +00:00
Michael B. Gale 1bfa34a866 Fix keys instead of values in test 2026-01-28 13:12:43 +00:00
Michael B. Gale 7159d66148 Fix typo in comment 2026-01-28 13:11:42 +00:00
Michael B. Gale f1779e26a8 Report queried feature flags in status reports 2026-01-28 12:57:45 +00:00
Michael B. Gale 9d2031faa4 Track outcomes of querying FFs with getValue 2026-01-28 12:03:10 +00:00
45 changed files with 138371 additions and 409937 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache@3
run: npm install @actions/tool-cache
- name: Check toolcache contains CodeQL
continue-on-error: true
uses: actions/github-script@v8
+1 -1
View File
@@ -68,7 +68,7 @@ jobs:
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
fs.rmdirSync(codeqlPath, { recursive: true });
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache@3
run: npm install @actions/tool-cache
- name: Check toolcache does not contain CodeQL
uses: actions/github-script@v8
with:
+12
View File
@@ -48,6 +48,18 @@ jobs:
timeout-minutes: 45
runs-on: ${{ matrix.os }}
steps:
# These steps are required to initialise the `gh` cli in a container that doesn't
# come pre-installed with it. The reason for that is that this is later
# needed by the `prepare-test` workflow to find the latest release of CodeQL.
- name: Set up GitHub CLI
run: |
apt update
apt install -y curl libreadline8 gnupg2 software-properties-common zstd
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
apt-key add /usr/share/keyrings/githubcli-archive-keyring.gpg
apt-add-repository https://cli.github.com/packages
apt install -y gh
env: {}
- name: Check out repository
uses: actions/checkout@v6
- name: Prepare test
+1 -1
View File
@@ -111,7 +111,7 @@ jobs:
# Otherwise, just commit the changes.
if git rev-parse --verify MERGE_HEAD >/dev/null 2>&1; then
echo "In progress merge detected, finishing it up."
git merge --continue --no-edit
git merge --continue
else
echo "No in-progress merge detected, committing changes."
git commit -m "Rebuild"
+2 -7
View File
@@ -2,14 +2,9 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## 4.32.2 - 05 Feb 2026
## [UNRELEASED]
- 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
- 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)
No user facing changes.
## 4.32.0 - 26 Jan 2026
+11283 -45002
View File
File diff suppressed because one or more lines are too long
+10953 -27960
View File
File diff suppressed because one or more lines are too long
+10944 -27956
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.24.1",
"cliVersion": "2.24.1",
"priorBundleVersion": "codeql-bundle-v2.24.0",
"priorCliVersion": "2.24.0"
"bundleVersion": "codeql-bundle-v2.24.0",
"cliVersion": "2.24.0",
"priorBundleVersion": "codeql-bundle-v2.23.9",
"priorCliVersion": "2.23.9"
}
+11310 -45007
View File
File diff suppressed because one or more lines are too long
+12941 -30017
View File
File diff suppressed because one or more lines are too long
+10932 -27954
View File
File diff suppressed because one or more lines are too long
+10952 -27961
View File
File diff suppressed because one or more lines are too long
+11283 -45002
View File
File diff suppressed because one or more lines are too long
+14115 -31200
View File
File diff suppressed because one or more lines are too long
+10923 -27956
View File
File diff suppressed because one or more lines are too long
+11249 -44968
View File
File diff suppressed because one or more lines are too long
+10953 -27962
View File
File diff suppressed because one or more lines are too long
+206 -190
View File
@@ -1,24 +1,24 @@
{
"name": "codeql",
"version": "4.32.2",
"version": "4.32.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.32.2",
"version": "4.32.1",
"license": "MIT",
"dependencies": {
"@actions/artifact": "^5.0.3",
"@actions/artifact": "^5.0.2",
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
"@actions/cache": "^5.0.5",
"@actions/core": "^2.0.3",
"@actions/cache": "^5.0.3",
"@actions/core": "^2.0.2",
"@actions/exec": "^2.0.0",
"@actions/github": "^8.0.1",
"@actions/github": "^8.0.0",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.1",
"@actions/tool-cache": "^3.0.0",
"@octokit/plugin-retry": "^8.0.0",
"@schemastore/package": "0.0.10",
"archiver": "^7.0.1",
@@ -46,7 +46,7 @@
"@types/node-forge": "^1.3.14",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/eslint-plugin": "^8.53.1",
"@typescript-eslint/parser": "^8.48.0",
"ava": "^6.4.1",
"esbuild": "^0.27.2",
@@ -55,7 +55,7 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-github": "^5.1.8",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "^62.4.1",
"eslint-plugin-jsdoc": "^62.2.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"nock": "^14.0.10",
@@ -72,14 +72,14 @@
}
},
"node_modules/@actions/artifact": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.3.tgz",
"integrity": "sha512-FIEG8Kum0wABZnktJvFi1xuVPc31xrunhZwLCvjrCGISQOm0ifyo7cjqf6PHiEeqoWMa5HIGOsB+lGM4aKCseA==",
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.2.tgz",
"integrity": "sha512-NmA+THO1s/Ubqf5y34EZofB1FRORcToa3T8Mb3/Z87TCCozTtvBUaxDn8OPPwgyYQ4ft0nd01Z4kFSu2DTWqGw==",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/github": "^6.0.1",
"@actions/http-client": "^3.0.2",
"@actions/http-client": "^3.0.1",
"@azure/storage-blob": "^12.29.1",
"@octokit/core": "^5.2.1",
"@octokit/plugin-request-log": "^1.0.4",
@@ -351,15 +351,15 @@
"license": "Apache-2.0"
},
"node_modules/@actions/cache": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.5.tgz",
"integrity": "sha512-jiQSg0gfd+C2KPgcmdCOq7dCuCIQQWQ4b1YfGIRaaA9w7PJbRwTOcCz4LiFEUnqZGf0ha/8OKL3BeNwetHzYsQ==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.3.tgz",
"integrity": "sha512-9joY8Oup+nIpksSBlkuf9/mltnhWx3lydk1tA2PVnXaxFLIIrKqrWDN2CZXlJ+PEErcBARKYn4mHiUCTyMh4Vg==",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/exec": "^2.0.0",
"@actions/glob": "^0.5.1",
"@actions/http-client": "^3.0.2",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.1",
"@actions/io": "^2.0.0",
"@azure/abort-controller": "^1.1.0",
"@azure/core-rest-pipeline": "^1.22.0",
@@ -376,13 +376,13 @@
}
},
"node_modules/@actions/core": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.3.tgz",
"integrity": "sha512-Od9Thc3T1mQJYddvVPM4QGiLUewdh+3txmDYHHxoNdkqysR1MbCT+rFOtNUxYAz+7+6RIsqipVahY2GJqGPyxA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.2.tgz",
"integrity": "sha512-Ast1V7yHbGAhplAsuVlnb/5J8Mtr/Zl6byPPL+Qjq3lmfIgWF1ak1iYfF/079cRERiuTALTXkSuEUdZeDCfGtA==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.2"
"@actions/http-client": "^3.0.1"
}
},
"node_modules/@actions/exec": {
@@ -395,18 +395,18 @@
}
},
"node_modules/@actions/github": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-8.0.1.tgz",
"integrity": "sha512-cue7mS+kx1/2Dnc/094pitRUm+0uPXVXYVaqOdZwD15BsXATWYHW3idJDYOlyBc5gJlzAQ/w5YLU4LR8D7hjVg==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-8.0.0.tgz",
"integrity": "sha512-bBukvVRvIf7NshWEXFVnBaXs/ZyGzDvx+jbB5AdJij9CUG4VFEYJJD6T6y06dDkuULalQQUNvKXIH71yqw7SoQ==",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^3.0.2",
"@actions/http-client": "^3.0.1",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@octokit/request": "^10.0.7",
"@octokit/request-error": "^7.1.0",
"undici": "^6.23.0"
"undici": "^5.28.5"
}
},
"node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest": {
@@ -439,42 +439,58 @@
"@octokit/core": ">=6"
}
},
"node_modules/@actions/github/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/@actions/glob": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.1.tgz",
"integrity": "sha512-+dv/t2aKQdKp9WWSp+1yIXVJzH5Q38M0Mta26pzIbeec14EcIleMB7UU6N7sNgbEuYfyuVGpE5pOKjl6j1WXkA==",
"license": "MIT",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.0.tgz",
"integrity": "sha512-tST2rjPvJLRZLuT9NMUtyBjvj9Yo0MiJS3ow004slMvm8GFM+Zv9HvMJ7HWzfUyJnGrJvDsYkWBaaG3YKXRtCw==",
"dependencies": {
"@actions/core": "^2.0.3",
"@actions/core": "^1.9.1",
"minimatch": "^3.0.4"
}
},
"node_modules/@actions/http-client": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
"integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
"node_modules/@actions/glob/node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
}
},
"node_modules/@actions/glob/node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"license": "MIT",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/glob/node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^6.23.0"
"undici": "^5.25.4"
}
},
"node_modules/@actions/http-client/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"node_modules/@actions/glob/node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"license": "MIT"
},
"node_modules/@actions/http-client": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"engines": {
"node": ">=18.17"
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.28.5"
}
},
"node_modules/@actions/io": {
@@ -484,14 +500,14 @@
"license": "MIT"
},
"node_modules/@actions/tool-cache": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-3.0.1.tgz",
"integrity": "sha512-euK7sID37jMg1yWGkdXkLPI5Te7x/+2QMUPeHXogcpzUZ81mqlDZ+CgYhQo3LtB8LpVnnQyjs+hTTU0Ir4Y0RQ==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-3.0.0.tgz",
"integrity": "sha512-JBx8gEWuu8Lqaqx/hEnL6QdKvF06suBR4y+dBDi9vJbHx1r+p6QtmBKhQYhiKjDUYIoDX1bUrbyAYPChoPK+XA==",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.1",
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.2",
"@actions/http-client": "^3.0.1",
"@actions/io": "^2.0.0",
"semver": "^6.1.0"
}
@@ -817,26 +833,26 @@
}
},
"node_modules/@es-joy/jsdoccomment": {
"version": "0.83.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.83.0.tgz",
"integrity": "sha512-e1MHSEPJ4m35zkBvNT6kcdeH1SvMaJDsPC3Xhfseg3hvF50FUE3f46Yn36jgbrPYYXezlWUQnevv23c+lx2MCA==",
"version": "0.81.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.81.0.tgz",
"integrity": "sha512-4V4A0hFAB19id7w9iwiosV/rqwlH+PXEuYnnu1Cyc5jUjTwsE2G1qsX9TOCmfCmsWYBg6xeDC/XDFUzXAxDg3A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.8",
"@typescript-eslint/types": "^8.53.1",
"comment-parser": "1.4.5",
"@typescript-eslint/types": "^8.53.0",
"comment-parser": "1.4.4",
"esquery": "^1.7.0",
"jsdoc-type-pratt-parser": "~7.1.0"
"jsdoc-type-pratt-parser": "~7.0.0"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1509,9 +1525,9 @@
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"license": "MIT",
"dependencies": {
"@isaacs/balanced-match": "^4.0.1"
@@ -2192,17 +2208,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz",
"integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz",
"integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/type-utils": "8.54.0",
"@typescript-eslint/utils": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/type-utils": "8.53.1",
"@typescript-eslint/utils": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
@@ -2215,7 +2231,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.54.0",
"@typescript-eslint/parser": "^8.53.1",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -2240,14 +2256,14 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz",
"integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
"integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0"
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2258,9 +2274,9 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2272,16 +2288,16 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz",
"integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
"integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.54.0",
"@typescript-eslint/tsconfig-utils": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"@typescript-eslint/project-service": "8.53.1",
"@typescript-eslint/tsconfig-utils": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -2300,16 +2316,16 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz",
"integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz",
"integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0"
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2324,13 +2340,13 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz",
"integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
"integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/types": "8.53.1",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2422,16 +2438,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz",
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz",
"integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"debug": "^4.4.3"
},
"engines": {
@@ -2447,14 +2463,14 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz",
"integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
"integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0"
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2465,9 +2481,9 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2479,16 +2495,16 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz",
"integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
"integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.54.0",
"@typescript-eslint/tsconfig-utils": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"@typescript-eslint/project-service": "8.53.1",
"@typescript-eslint/tsconfig-utils": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -2507,13 +2523,13 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz",
"integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
"integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/types": "8.53.1",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2595,14 +2611,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz",
"integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz",
"integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.54.0",
"@typescript-eslint/types": "^8.54.0",
"@typescript-eslint/tsconfig-utils": "^8.53.1",
"@typescript-eslint/types": "^8.53.1",
"debug": "^4.4.3"
},
"engines": {
@@ -2617,9 +2633,9 @@
}
},
"node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2667,9 +2683,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz",
"integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz",
"integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2684,15 +2700,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz",
"integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz",
"integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0",
"@typescript-eslint/utils": "8.54.0",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1",
"@typescript-eslint/utils": "8.53.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
@@ -2728,14 +2744,14 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz",
"integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
"integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0"
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2746,9 +2762,9 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2760,16 +2776,16 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz",
"integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
"integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.54.0",
"@typescript-eslint/tsconfig-utils": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"@typescript-eslint/project-service": "8.53.1",
"@typescript-eslint/tsconfig-utils": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -2788,16 +2804,16 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz",
"integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz",
"integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0"
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2812,13 +2828,13 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz",
"integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==",
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
"integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/types": "8.53.1",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -3975,9 +3991,9 @@
"license": "MIT"
},
"node_modules/comment-parser": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz",
"integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.4.tgz",
"integrity": "sha512-0D6qSQ5IkeRrGJFHRClzaMOenMeT0gErz3zIw3AprKMqhRN6LNU2jQOdkPG/FZ+8bCgXE1VidrgSzlBBDZRr8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4885,19 +4901,19 @@
}
},
"node_modules/eslint-plugin-jsdoc": {
"version": "62.4.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.4.1.tgz",
"integrity": "sha512-HgX2iN4j104D/mCUqRbhtzSZbph+KO9jfMHiIJjJ19Q+IwLQ5Na2IqvOJYq4S+4kgvEk1w6KYF4vVus6H2wcHg==",
"version": "62.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.2.0.tgz",
"integrity": "sha512-juIekE88Gy1FnjsQuwkrZ3OW4UCkQuhiBf9QqF9U1xgqtan4rQFCOUn4cKeHR1ObBqJ8/pG8a0lZlAuKlDLpdw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@es-joy/jsdoccomment": "~0.83.0",
"@es-joy/jsdoccomment": "~0.81.0",
"@es-joy/resolve.exports": "1.2.0",
"are-docs-informative": "^0.0.2",
"comment-parser": "1.4.5",
"comment-parser": "1.4.4",
"debug": "^4.4.3",
"escape-string-regexp": "^4.0.0",
"espree": "^11.1.0",
"espree": "^11.0.0",
"esquery": "^1.7.0",
"html-entities": "^2.6.0",
"object-deep-merge": "^2.0.0",
@@ -4958,9 +4974,9 @@
}
},
"node_modules/eslint-plugin-jsdoc/node_modules/espree": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz",
"integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==",
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-11.0.0.tgz",
"integrity": "sha512-+gMeWRrIh/NsG+3NaLeWHuyeyk70p2tbvZIWBYcqQ4/7Xvars6GYTZNhF1sIeLcc6Wb11He5ffz3hsHyXFrw5A==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -5451,9 +5467,9 @@
"license": "MIT"
},
"node_modules/fast-xml-parser": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz",
"integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==",
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz",
"integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==",
"funding": [
{
"type": "github",
@@ -6495,9 +6511,9 @@
}
},
"node_modules/jsdoc-type-pratt-parser": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.0.tgz",
"integrity": "sha512-SX7q7XyCwzM/MEDCYz0l8GgGbJAACGFII9+WfNYr5SLEKukHWRy2Jk3iWRe7P+lpYJNs7oQ+OSei4JtKGUjd7A==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.0.0.tgz",
"integrity": "sha512-c7YbokssPOSHmqTbSAmTtnVgAVa/7lumWNYqomgd5KOMyPrRve2anx6lonfOsXEQacqF9FKVUj7bLg4vRSvdYA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8143,9 +8159,9 @@
}
},
"node_modules/tar": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz",
"integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.32.2",
"version": "4.32.1",
"private": true,
"description": "CodeQL action",
"scripts": {
@@ -24,16 +24,16 @@
},
"license": "MIT",
"dependencies": {
"@actions/artifact": "^5.0.3",
"@actions/artifact": "^5.0.2",
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
"@actions/cache": "^5.0.5",
"@actions/core": "^2.0.3",
"@actions/cache": "^5.0.3",
"@actions/core": "^2.0.2",
"@actions/exec": "^2.0.0",
"@actions/github": "^8.0.1",
"@actions/github": "^8.0.0",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.1",
"@actions/tool-cache": "^3.0.0",
"@octokit/plugin-retry": "^8.0.0",
"@schemastore/package": "0.0.10",
"archiver": "^7.0.1",
@@ -61,7 +61,7 @@
"@types/node-forge": "^1.3.14",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/eslint-plugin": "^8.53.1",
"@typescript-eslint/parser": "^8.48.0",
"ava": "^6.4.1",
"esbuild": "^0.27.2",
@@ -70,7 +70,7 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-github": "^5.1.8",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "^62.4.1",
"eslint-plugin-jsdoc": "^62.2.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"nock": "^14.0.10",
+1 -1
View File
@@ -4,7 +4,7 @@ versions:
- toolcache
steps:
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache@3
run: npm install @actions/tool-cache
- name: Check toolcache contains CodeQL
continue-on-error: true
uses: actions/github-script@v8
+1 -1
View File
@@ -16,7 +16,7 @@ steps:
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
fs.rmdirSync(codeqlPath, { recursive: true });
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache@3
run: npm install @actions/tool-cache
- name: Check toolcache does not contain CodeQL
uses: actions/github-script@v8
with:
+13
View File
@@ -3,6 +3,19 @@ description: "Tests using a proxy specified by the https_proxy environment varia
versions: ["linked", "nightly-latest"]
container:
image: ubuntu:22.04
container-init-steps:
# These steps are required to initialise the `gh` cli in a container that doesn't
# come pre-installed with it. The reason for that is that this is later
# needed by the `prepare-test` workflow to find the latest release of CodeQL.
name: Set up GitHub CLI
run: |
apt update
apt install -y curl libreadline8 gnupg2 software-properties-common zstd
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
apt-key add /usr/share/keyrings/githubcli-archive-keyring.gpg
apt-add-repository https://cli.github.com/packages
apt install -y gh
env: {}
services:
squid-proxy:
image: ubuntu/squid:latest
+9 -1
View File
@@ -76,6 +76,7 @@ interface FinishWithTrapUploadStatusReport extends FinishStatusReport {
async function sendStatusReport(
startedAt: Date,
config: Config | undefined,
features: Features | undefined,
stats: AnalysisStatusReport | undefined,
error: Error | undefined,
trapCacheUploadTime: number | undefined,
@@ -92,6 +93,7 @@ async function sendStatusReport(
status,
startedAt,
config,
features?.getQueriedFeatures(),
await util.checkDiskUsage(logger),
logger,
error?.message,
@@ -218,6 +220,7 @@ async function run(startedAt: Date) {
| undefined = undefined;
let runStats: QueriesStatusReport | undefined = undefined;
let config: Config | undefined = undefined;
let features: Features | undefined = undefined;
let trapCacheCleanupTelemetry: TrapCacheCleanupStatusReport | undefined =
undefined;
let trapCacheUploadTime: number | undefined = undefined;
@@ -239,6 +242,7 @@ async function run(startedAt: Date) {
"starting",
startedAt,
config,
undefined,
await util.checkDiskUsage(logger),
logger,
);
@@ -293,7 +297,7 @@ async function run(startedAt: Date) {
util.checkActionVersion(actionsUtil.getActionVersion(), gitHubVersion);
const features = new Features(
features = new Features(
gitHubVersion,
repositoryNwo,
actionsUtil.getTemporaryDirectory(),
@@ -460,6 +464,7 @@ async function run(startedAt: Date) {
await sendStatusReport(
startedAt,
config,
features,
error instanceof CodeQLAnalysisError
? error.queriesStatusReport
: undefined,
@@ -482,6 +487,7 @@ async function run(startedAt: Date) {
await sendStatusReport(
startedAt,
config,
features,
{
...runStats,
...uploadResults[analyses.AnalysisKind.CodeScanning].statusReport,
@@ -499,6 +505,7 @@ async function run(startedAt: Date) {
await sendStatusReport(
startedAt,
config,
features,
{ ...runStats },
undefined,
trapCacheUploadTime,
@@ -513,6 +520,7 @@ async function run(startedAt: Date) {
await sendStatusReport(
startedAt,
config,
features,
undefined,
undefined,
trapCacheUploadTime,
+1 -2
View File
@@ -306,8 +306,7 @@ export function wrapApiConfigurationError(e: unknown) {
}
if (
httpError.message.includes("Bad credentials") ||
httpError.message.includes("Not Found") ||
httpError.message.includes("Requires authentication")
httpError.message.includes("Not Found")
) {
return new ConfigurationError(
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
+2
View File
@@ -54,6 +54,7 @@ async function sendCompletedStatusReport(
status,
startedAt,
config,
undefined,
await checkDiskUsage(logger),
logger,
cause?.message,
@@ -83,6 +84,7 @@ async function run(startedAt: Date) {
"starting",
startedAt,
config,
undefined,
await checkDiskUsage(logger),
logger,
);
+9 -6
View File
@@ -26,10 +26,7 @@ import {
parseUserConfig,
UserConfig,
} from "./config/db-config";
import {
addNoLanguageDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import { addDiagnostic, makeTelemetryDiagnostic } from "./diagnostics";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import * as errorMessages from "./error-messages";
@@ -1419,8 +1416,11 @@ async function logGitVersionTelemetry(
gitVersion: GitVersionInfo,
): Promise<void> {
if (config.languages.length > 0) {
addNoLanguageDiagnostic(
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
makeTelemetryDiagnostic(
"codeql-action/git-version-telemetry",
"Git version telemetry",
@@ -1446,8 +1446,11 @@ async function logGeneratedFilesTelemetry(
return;
}
addNoLanguageDiagnostic(
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
makeTelemetryDiagnostic(
"codeql-action/generated-files-telemetry",
"Generated files telemetry",
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.24.1",
"cliVersion": "2.24.1",
"priorBundleVersion": "codeql-bundle-v2.24.0",
"priorCliVersion": "2.24.0"
"bundleVersion": "codeql-bundle-v2.24.0",
"cliVersion": "2.24.0",
"priorBundleVersion": "codeql-bundle-v2.23.9",
"priorCliVersion": "2.23.9"
}
-14
View File
@@ -117,20 +117,6 @@ export function addDiagnostic(
}
}
/** Adds a diagnostic that is not specific to any language. */
export function addNoLanguageDiagnostic(
config: Config,
diagnostic: DiagnosticMessage,
) {
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
diagnostic,
);
}
/**
* Writes the given diagnostic to the database.
*
+26 -1
View File
@@ -92,6 +92,31 @@ test(`Feature flags are requested in GHEC-DR`, async (t) => {
});
});
test("Queried feature flags are recorded", async (t) => {
await withTmpDir(async (tmpDir) => {
const loggedMessages = [];
const features = setUpFeatureFlagTests(
tmpDir,
getRecordingLogger(loggedMessages),
{ type: GitHubVariant.DOTCOM },
);
mockFeatureFlagApiEndpoint(200, initializeFeatures(true));
// No features should have been queried initially.
t.is(Object.keys(features.getQueriedFeatures()).length, 0);
// Query all features.
const allFeatures = Object.values(Feature);
for (const feature of allFeatures) {
await getFeatureIncludingCodeQlIfRequired(features, feature);
}
// All features should have a been queried.
t.is(Object.keys(features.getQueriedFeatures()).length, allFeatures.length);
});
});
test("API response missing and features use default value", async (t) => {
await withTmpDir(async (tmpDir) => {
const loggedMessages: LoggedMessage[] = [];
@@ -562,7 +587,7 @@ function setUpFeatureFlagTests(
tmpDir: string,
logger = getRunnerLogger(true),
gitHubVersion = { type: GitHubVariant.DOTCOM } as util.GitHubVersion,
): FeatureEnablement {
): Features {
setupActionsVars(tmpDir, tmpDir);
return new Features(gitHubVersion, testRepositoryNwo, tmpDir, logger);
+28
View File
@@ -345,6 +345,17 @@ export interface FeatureEnablement {
*/
type GitHubFeatureFlagsApiResponse = Partial<Record<Feature, boolean>>;
// Even though we are currently only tracking the value of queried features, we use an object
// here rather than just a boolean to keep open the possibility of also tracking the reason
// for why a particular value was resolved (i.e. because of an environment variable or API result)
// in the future.
export interface QueriedFeatureStatus {
value: boolean;
}
/** A partial mapping of feature flags to the outcome of querying them. */
export type QueriedFeatures = Partial<Record<Feature, QueriedFeatureStatus>>;
export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
/**
@@ -355,6 +366,9 @@ export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
export class Features implements FeatureEnablement {
private gitHubFeatureFlags: GitHubFeatureFlags;
// Tracks features that have been queried at some point and the outcome.
private queriedFeatures: QueriedFeatures = {};
constructor(
gitHubVersion: util.GitHubVersion,
repositoryNwo: RepositoryNwo,
@@ -369,6 +383,11 @@ export class Features implements FeatureEnablement {
);
}
/** Gets a record of features that were queried and the corresponding outcomes. */
public getQueriedFeatures() {
return this.queriedFeatures;
}
async getDefaultCliVersion(
variant: util.GitHubVariant,
): Promise<CodeQLDefaultVersionInfo> {
@@ -388,6 +407,15 @@ export class Features implements FeatureEnablement {
* @throws if a `minimumVersion` is specified for the feature, and `codeql` is not provided.
*/
async getValue(feature: Feature, codeql?: CodeQL): Promise<boolean> {
const value = await this.getValueInternal(feature, codeql);
this.queriedFeatures[feature] = { value };
return value;
}
private async getValueInternal(
feature: Feature,
codeql?: CodeQL,
): Promise<boolean> {
// Narrow the type to FeatureConfig to avoid type errors. To avoid unsafe use of `as`, we
// check that the required properties exist using `satisfies`.
const config = featureConfig[
+5 -11
View File
@@ -78,17 +78,11 @@ export async function loadPropertiesFromApi(
}
}
if (Object.keys(properties).length === 0) {
logger.debug("No known repository properties were found.");
} else {
logger.debug(
"Loaded the following values for the repository properties:",
);
for (const [property, value] of Object.entries(properties).sort(
([nameA], [nameB]) => nameA.localeCompare(nameB),
)) {
logger.debug(` ${property}: ${value}`);
}
logger.debug("Loaded the following values for the repository properties:");
for (const [property, value] of Object.entries(properties).sort(
([nameA], [nameB]) => nameA.localeCompare(nameB),
)) {
logger.debug(` ${property}: ${value}`);
}
return properties;
+4 -1
View File
@@ -50,6 +50,7 @@ async function run(startedAt: Date) {
const logger = getActionsLogger();
let config: Config | undefined;
let features: Features | undefined = undefined;
let uploadFailedSarifResult:
| initActionPostHelper.UploadFailedSarifResult
| undefined;
@@ -62,7 +63,7 @@ async function run(startedAt: Date) {
checkGitHubVersionInRange(gitHubVersion, logger);
const repositoryNwo = getRepositoryNwo();
const features = new Features(
features = new Features(
gitHubVersion,
repositoryNwo,
getTemporaryDirectory(),
@@ -107,6 +108,7 @@ async function run(startedAt: Date) {
getActionsStatus(error),
startedAt,
config,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
error.message,
@@ -125,6 +127,7 @@ async function run(startedAt: Date) {
"success",
startedAt,
config,
features.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
);
+31 -76
View File
@@ -2,7 +2,6 @@ import * as fs from "fs";
import * as path from "path";
import * as core from "@actions/core";
import * as github from "@actions/github";
import * as io from "@actions/io";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";
@@ -31,18 +30,14 @@ import {
} from "./dependency-caching";
import {
addDiagnostic,
addNoLanguageDiagnostic,
flushDiagnostics,
logUnwrittenDiagnostics,
makeDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement, Features } from "./feature-flags";
import {
loadPropertiesFromApi,
RepositoryProperties,
} from "./feature-flags/properties";
import { Feature, Features } from "./feature-flags";
import { loadPropertiesFromApi } from "./feature-flags/properties";
import {
checkInstallPython311,
checkPacksForOverlayCompatibility,
@@ -59,7 +54,7 @@ import {
OverlayBaseDatabaseDownloadStats,
OverlayDatabaseMode,
} from "./overlay-database-utils";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
import { getRepositoryNwo } from "./repository";
import { ToolsSource } from "./setup-codeql";
import {
ActionName,
@@ -93,8 +88,6 @@ import {
checkActionVersion,
getErrorMessage,
BuildMode,
GitHubVersion,
Result,
} from "./util";
import { checkWorkflow } from "./workflow";
@@ -115,6 +108,7 @@ export const CODEQL_VERSION_JAR_MINIMIZATION = "2.23.0";
async function sendStartingStatusReport(
startedAt: Date,
config: Partial<configUtils.Config> | undefined,
features: Features | undefined,
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
@@ -122,6 +116,7 @@ async function sendStartingStatusReport(
"starting",
startedAt,
config,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
);
@@ -133,6 +128,7 @@ async function sendStartingStatusReport(
async function sendCompletedStatusReport(
startedAt: Date,
config: configUtils.Config | undefined,
features: Features | undefined,
configFile: string | undefined,
toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined,
toolsFeatureFlagsValid: boolean | undefined,
@@ -148,6 +144,7 @@ async function sendCompletedStatusReport(
getActionsStatus(error),
startedAt,
config,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
error?.message,
@@ -210,7 +207,7 @@ async function run(startedAt: Date) {
let config: configUtils.Config | undefined;
let configFile: string | undefined;
let codeql: CodeQL;
let features: Features;
let features: Features | undefined = undefined;
let sourceRoot: string;
let toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined;
let toolsFeatureFlagsValid: boolean | undefined;
@@ -245,12 +242,12 @@ async function run(startedAt: Date) {
);
// Fetch the values of known repository properties that affect us.
const repositoryPropertiesResult = await loadRepositoryProperties(
repositoryNwo,
gitHubVersion,
features,
logger,
const enableRepoProps = await features.getValue(
Feature.UseRepositoryProperties,
);
const repositoryProperties = enableRepoProps
? await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo)
: {};
// Create a unique identifier for this run.
const jobRunUuid = uuidV4();
@@ -284,7 +281,12 @@ async function run(startedAt: Date) {
}
// Send a status report indicating that an analysis is starting.
await sendStartingStatusReport(startedAt, { analysisKinds }, logger);
await sendStartingStatusReport(
startedAt,
{ analysisKinds },
features,
logger,
);
// Throw a `ConfigurationError` if the `setup-codeql` action has been run.
if (process.env[EnvVar.SETUP_CODEQL_ACTION_HAS_RUN] === "true") {
@@ -372,7 +374,7 @@ async function run(startedAt: Date) {
githubVersion: gitHubVersion,
apiDetails,
features,
repositoryProperties: repositoryPropertiesResult.orElse({}),
repositoryProperties,
enableFileCoverageInformation: await getFileCoverageInformationEnabled(
debugMode,
repositoryNwo,
@@ -381,19 +383,6 @@ async function run(startedAt: Date) {
logger,
});
if (repositoryPropertiesResult.isFailure()) {
addNoLanguageDiagnostic(
config,
makeTelemetryDiagnostic(
"codeql-action/repository-properties-load-failure",
"Failed to load repository properties",
{
error: getErrorMessage(repositoryPropertiesResult.value),
},
),
);
}
await checkInstallPython311(config.languages, codeql);
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
@@ -403,6 +392,7 @@ async function run(startedAt: Date) {
error instanceof ConfigurationError ? "user-error" : "aborted",
startedAt,
config,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
error.message,
@@ -456,8 +446,11 @@ async function run(startedAt: Date) {
// Log CodeQL download telemetry, if appropriate
if (toolsDownloadStatusReport) {
addNoLanguageDiagnostic(
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
makeTelemetryDiagnostic(
"codeql-action/bundle-download-telemetry",
"CodeQL bundle download telemetry",
@@ -771,6 +764,7 @@ async function run(startedAt: Date) {
await sendCompletedStatusReport(
startedAt,
config,
features,
undefined, // We only report config info on success.
toolsDownloadStatusReport,
toolsFeatureFlagsValid,
@@ -788,6 +782,7 @@ async function run(startedAt: Date) {
await sendCompletedStatusReport(
startedAt,
config,
features,
configFile,
toolsDownloadStatusReport,
toolsFeatureFlagsValid,
@@ -799,49 +794,6 @@ async function run(startedAt: Date) {
);
}
/**
* Loads [repository properties](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization) if applicable.
*/
async function loadRepositoryProperties(
repositoryNwo: RepositoryNwo,
gitHubVersion: GitHubVersion,
features: FeatureEnablement,
logger: Logger,
): Promise<Result<RepositoryProperties, unknown>> {
// See if we can skip loading repository properties early. In particular,
// repositories owned by users cannot have repository properties, so we can
// skip the API call entirely in that case.
const repositoryOwnerType = github.context.payload.repository?.owner.type;
logger.debug(
`Repository owner type is '${repositoryOwnerType ?? "unknown"}'.`,
);
if (repositoryOwnerType === "User") {
logger.debug(
"Skipping loading repository properties because the repository is owned by a user and " +
"therefore cannot have repository properties.",
);
return Result.success({});
}
if (!(await features.getValue(Feature.UseRepositoryProperties))) {
logger.debug(
"Skipping loading repository properties because the UseRepositoryProperties feature flag is disabled.",
);
return Result.success({});
}
try {
return Result.success(
await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo),
);
} catch (error) {
logger.warning(
`Failed to load repository properties: ${getErrorMessage(error)}`,
);
return Result.failure(error);
}
}
function getTrapCachingEnabled(): boolean {
// If the workflow specified something always respect that
const trapCaching = getOptionalInput("trap-caching");
@@ -858,8 +810,11 @@ async function recordZstdAvailability(
config: configUtils.Config,
zstdAvailability: ZstdAvailability,
) {
addNoLanguageDiagnostic(
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
makeTelemetryDiagnostic(
"codeql-action/zstd-availability",
"Zstandard availability",
+3
View File
@@ -44,6 +44,7 @@ async function run(startedAt: Date) {
"starting",
startedAt,
config,
undefined,
await checkDiskUsage(logger),
logger,
);
@@ -91,6 +92,7 @@ async function run(startedAt: Date) {
getActionsStatus(error),
startedAt,
config,
undefined,
await checkDiskUsage(logger),
logger,
error.message,
@@ -109,6 +111,7 @@ async function run(startedAt: Date) {
"success",
startedAt,
config,
undefined,
await checkDiskUsage(logger),
logger,
);
+7 -1
View File
@@ -42,6 +42,7 @@ import {
*/
async function sendCompletedStatusReport(
startedAt: Date,
features: Features | undefined,
toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined,
toolsFeatureFlagsValid: boolean | undefined,
toolsSource: ToolsSource,
@@ -54,6 +55,7 @@ async function sendCompletedStatusReport(
getActionsStatus(error),
startedAt,
undefined,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
error?.message,
@@ -97,6 +99,7 @@ async function run(startedAt: Date): Promise<void> {
let toolsFeatureFlagsValid: boolean | undefined;
let toolsSource: ToolsSource;
let toolsVersion: string;
let features: Features | undefined = undefined;
try {
initializeEnvironment(getActionVersion());
@@ -114,7 +117,7 @@ async function run(startedAt: Date): Promise<void> {
const repositoryNwo = getRepositoryNwo();
const features = new Features(
features = new Features(
gitHubVersion,
repositoryNwo,
getTemporaryDirectory(),
@@ -130,6 +133,7 @@ async function run(startedAt: Date): Promise<void> {
"starting",
startedAt,
undefined,
features.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
);
@@ -166,6 +170,7 @@ async function run(startedAt: Date): Promise<void> {
error instanceof ConfigurationError ? "user-error" : "failure",
startedAt,
undefined,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
error.message,
@@ -179,6 +184,7 @@ async function run(startedAt: Date): Promise<void> {
await sendCompletedStatusReport(
startedAt,
features,
toolsDownloadStatusReport,
toolsFeatureFlagsValid,
toolsSource,
+102 -8
View File
@@ -2,22 +2,29 @@ import { ChildProcess, spawn } from "child_process";
import * as path from "path";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import { pki } from "node-forge";
import * as actionsUtil from "./actions-util";
import { getApiDetails, getAuthorizationHeaderFor } from "./api-client";
import { Config } from "./config-utils";
import { KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import {
Credential,
credentialToStr,
getCredentials,
getProxyBinaryPath,
getSafeErrorMessage,
getDownloadUrl,
parseLanguage,
sendFailedStatusReport,
sendSuccessStatusReport,
UPDATEJOB_PROXY,
} from "./start-proxy";
import { ActionName, sendUnhandledErrorStatusReport } from "./status-report";
import {
ActionName,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
sendUnhandledErrorStatusReport,
StatusReportBase,
} from "./status-report";
import * as util from "./util";
const KEY_SIZE = 2048;
@@ -87,6 +94,36 @@ function generateCertificateAuthority(): CertificateAuthority {
return { cert: pem, key };
}
interface StartProxyStatus extends StatusReportBase {
// A comma-separated list of registry types which are configured for CodeQL.
// This only includes registry types we support, not all that are configured.
registry_types: string;
}
async function sendSuccessStatusReport(
startedAt: Date,
config: Partial<Config>,
registry_types: string[],
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.StartProxy,
"success",
startedAt,
config,
undefined,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
const statusReport: StartProxyStatus = {
...statusReportBase,
registry_types: registry_types.join(","),
};
await sendStatusReport(statusReport);
}
}
async function run(startedAt: Date) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
@@ -145,7 +182,26 @@ async function run(startedAt: Date) {
logger,
);
} catch (unwrappedError) {
await sendFailedStatusReport(logger, startedAt, language, unwrappedError);
const error = util.wrapError(unwrappedError);
core.setFailed(`start-proxy action failed: ${error.message}`);
// We skip sending the error message and stack trace here to avoid the possibility
// of leaking any sensitive information into the telemetry.
const errorStatusReportBase = await createStatusReportBase(
ActionName.StartProxy,
getActionsStatus(error),
startedAt,
{
languages: language && [language],
},
undefined,
await util.checkDiskUsage(logger),
logger,
"Error from start-proxy Action omitted",
);
if (errorStatusReportBase !== undefined) {
await sendStatusReport(errorStatusReportBase);
}
}
}
@@ -160,7 +216,7 @@ async function runWrapper() {
await sendUnhandledErrorStatusReport(
ActionName.StartProxy,
startedAt,
getSafeErrorMessage(util.wrapError(error)),
new Error("Error from start-proxy Action omitted"),
logger,
);
}
@@ -223,4 +279,42 @@ async function startProxy(
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
}
async function getProxyBinaryPath(logger: Logger): Promise<string> {
const proxyFileName =
process.platform === "win32" ? `${UPDATEJOB_PROXY}.exe` : UPDATEJOB_PROXY;
const proxyInfo = await getDownloadUrl(logger);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {
const apiDetails = getApiDetails();
const authorization = getAuthorizationHeaderFor(
logger,
apiDetails,
proxyInfo.url,
);
const temp = await toolcache.downloadTool(
proxyInfo.url,
undefined,
authorization,
{
accept: "application/octet-stream",
},
);
const extracted = await toolcache.extractTar(temp);
proxyBin = await toolcache.cacheDir(
extracted,
proxyFileName,
proxyInfo.version,
);
}
proxyBin = path.join(proxyBin, proxyFileName);
return proxyBin;
}
function credentialToStr(c: Credential): string {
return `Type: ${c.type}; Host: ${c.host}; Url: ${c.url} Username: ${
c.username
}; Password: ${c.password !== undefined}; Token: ${c.token !== undefined}`;
}
void runWrapper();
+2 -297
View File
@@ -1,110 +1,21 @@
import * as filepath from "path";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import test, { ExecutionContext } from "ava";
import test from "ava";
import sinon from "sinon";
import * as apiClient from "./api-client";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { getRunnerLogger, Logger } from "./logging";
import { getRunnerLogger } from "./logging";
import * as startProxyExports from "./start-proxy";
import { parseLanguage } from "./start-proxy";
import * as statusReport from "./status-report";
import {
checkExpectedLogMessages,
getRecordingLogger,
makeTestToken,
setupTests,
withRecordingLoggerAsync,
} from "./testing-utils";
import { ConfigurationError } from "./util";
setupTests(test);
const sendFailedStatusReportTest = test.macro({
exec: async (
t: ExecutionContext<unknown>,
err: Error,
expectedMessage: string,
expectedStatus: statusReport.ActionStatus = "failure",
) => {
const now = new Date();
// Override core.setFailed to avoid it setting the program's exit code
sinon.stub(core, "setFailed").returns();
const createStatusReportBase = sinon.stub(
statusReport,
"createStatusReportBase",
);
createStatusReportBase.resolves(undefined);
await withRecordingLoggerAsync(async (logger) => {
await startProxyExports.sendFailedStatusReport(
logger,
now,
undefined,
err,
);
// Check that the stub has been called exactly once, with the expected arguments,
// but not with the message from the error.
sinon.assert.calledOnceWithExactly(
createStatusReportBase,
statusReport.ActionName.StartProxy,
expectedStatus,
now,
sinon.match.any,
sinon.match.any,
sinon.match.any,
expectedMessage,
);
t.false(
createStatusReportBase.calledWith(
statusReport.ActionName.StartProxy,
expectedStatus,
now,
sinon.match.any,
sinon.match.any,
sinon.match.any,
sinon.match((msg: string) => msg.includes(err.message)),
),
"createStatusReportBase was called with the error message",
);
});
},
title: (providedTitle = "") => `sendFailedStatusReport - ${providedTitle}`,
});
test(
"reports generic error message for non-StartProxyError error",
sendFailedStatusReportTest,
new Error("Something went wrong today"),
"Error from start-proxy Action omitted (Error).",
);
test(
"reports generic error message for non-StartProxyError error with safe error message",
sendFailedStatusReportTest,
new Error(
startProxyExports.getStartProxyErrorMessage(
startProxyExports.StartProxyErrorType.DownloadFailed,
),
),
"Error from start-proxy Action omitted (Error).",
);
test(
"reports generic error message for ConfigurationError error",
sendFailedStatusReportTest,
new ConfigurationError("Something went wrong today"),
"Error from start-proxy Action omitted (ConfigurationError).",
"user-error",
);
const toEncodedJSON = (data: any) =>
Buffer.from(JSON.stringify(data)).toString("base64");
@@ -390,209 +301,3 @@ test("getDownloadUrl returns matching release asset", async (t) => {
t.is(info.version, defaults.cliVersion);
t.is(info.url, "url-we-want");
});
test("credentialToStr - hides passwords", (t) => {
const secret = "password123";
const credential = {
type: "maven_credential",
password: secret,
};
const str = startProxyExports.credentialToStr(credential);
t.false(str.includes(secret));
t.is(
"Type: maven_credential; Host: undefined; Url: undefined Username: undefined; Password: true; Token: false",
str,
);
});
test("credentialToStr - hides tokens", (t) => {
const secret = "password123";
const credential = {
type: "maven_credential",
token: secret,
};
const str = startProxyExports.credentialToStr(credential);
t.false(str.includes(secret));
t.is(
"Type: maven_credential; Host: undefined; Url: undefined Username: undefined; Password: false; Token: true",
str,
);
});
test("getSafeErrorMessage - returns actual message for `StartProxyError`", (t) => {
const error = new startProxyExports.StartProxyError(
startProxyExports.StartProxyErrorType.DownloadFailed,
);
t.is(
startProxyExports.getSafeErrorMessage(error),
startProxyExports.getStartProxyErrorMessage(error.errorType),
);
});
test("getSafeErrorMessage - does not return message for arbitrary errors", (t) => {
const error = new Error(
startProxyExports.getStartProxyErrorMessage(
startProxyExports.StartProxyErrorType.DownloadFailed,
),
);
const message = startProxyExports.getSafeErrorMessage(error);
t.not(message, error.message);
t.assert(message.startsWith("Error from start-proxy Action omitted"));
t.assert(message.includes(error.name));
});
const wrapFailureTest = test.macro({
exec: async (
t: ExecutionContext<unknown>,
setup: () => void,
fn: (logger: Logger) => Promise<void>,
) => {
await withRecordingLoggerAsync(async (logger) => {
setup();
await t.throwsAsync(fn(logger), {
instanceOf: startProxyExports.StartProxyError,
});
});
},
title: (providedTitle) => `${providedTitle} - wraps errors on failure`,
});
test("downloadProxy - returns file path on success", async (t) => {
await withRecordingLoggerAsync(async (logger) => {
const testPath = "/some/path";
sinon.stub(toolcache, "downloadTool").resolves(testPath);
const result = await startProxyExports.downloadProxy(
logger,
"url",
undefined,
);
t.is(result, testPath);
});
});
test(
"downloadProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "downloadTool").throws();
},
async (logger) => {
await startProxyExports.downloadProxy(logger, "url", undefined);
},
);
test("extractProxy - returns file path on success", async (t) => {
await withRecordingLoggerAsync(async (logger) => {
const testPath = "/some/path";
sinon.stub(toolcache, "extractTar").resolves(testPath);
const result = await startProxyExports.extractProxy(logger, "/other/path");
t.is(result, testPath);
});
});
test(
"extractProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "extractTar").throws();
},
async (logger) => {
await startProxyExports.extractProxy(logger, "path");
},
);
test("cacheProxy - returns file path on success", async (t) => {
await withRecordingLoggerAsync(async (logger) => {
const testPath = "/some/path";
sinon.stub(toolcache, "cacheDir").resolves(testPath);
const result = await startProxyExports.cacheProxy(
logger,
"/other/path",
"proxy",
"1.0",
);
t.is(result, testPath);
});
});
test(
"cacheProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "cacheDir").throws();
},
async (logger) => {
await startProxyExports.cacheProxy(logger, "/other/path", "proxy", "1.0");
},
);
test("getProxyBinaryPath - returns path from tool cache if available", async (t) => {
mockGetReleaseByTag();
await withRecordingLoggerAsync(async (logger) => {
const toolcachePath = "/path/to/proxy/dir";
sinon.stub(toolcache, "find").returns(toolcachePath);
const path = await startProxyExports.getProxyBinaryPath(logger);
t.assert(path);
t.is(
path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
});
});
test("getProxyBinaryPath - downloads proxy if not in cache", async (t) => {
const downloadUrl = "url-we-want";
mockGetReleaseByTag([
{ name: startProxyExports.getProxyPackage(), url: downloadUrl },
]);
await withRecordingLoggerAsync(async (logger) => {
const toolcachePath = "/path/to/proxy/dir";
const find = sinon.stub(toolcache, "find").returns("");
const getApiDetails = sinon.stub(apiClient, "getApiDetails").returns({
auth: "",
url: "",
apiURL: "",
});
const getAuthorizationHeaderFor = sinon
.stub(apiClient, "getAuthorizationHeaderFor")
.returns(undefined);
const archivePath = "/path/to/archive";
const downloadTool = sinon
.stub(toolcache, "downloadTool")
.resolves(archivePath);
const extractedPath = "/path/to/extracted";
const extractTar = sinon
.stub(toolcache, "extractTar")
.resolves(extractedPath);
const cacheDir = sinon.stub(toolcache, "cacheDir").resolves(toolcachePath);
const path = await startProxyExports.getProxyBinaryPath(logger);
t.assert(find.calledOnce);
t.assert(getApiDetails.calledOnce);
t.assert(getAuthorizationHeaderFor.calledOnce);
t.assert(downloadTool.calledOnceWith(downloadUrl));
t.assert(extractTar.calledOnceWith(archivePath));
t.assert(cacheDir.calledOnceWith(extractedPath));
t.assert(path);
t.is(
path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
});
});
+1 -272
View File
@@ -1,161 +1,12 @@
import * as path from "path";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import {
getApiClient,
getApiDetails,
getAuthorizationHeaderFor,
} from "./api-client";
import { getApiClient } from "./api-client";
import * as artifactScanner from "./artifact-scanner";
import { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { Logger } from "./logging";
import {
ActionName,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
StatusReportBase,
} from "./status-report";
import * as util from "./util";
import { ConfigurationError, getErrorMessage, isDefined } from "./util";
/**
* Enumerates specific error types for which we have corresponding error messages that
* are safe to include in status reports.
*/
export enum StartProxyErrorType {
DownloadFailed,
ExtractionFailed,
CacheFailed,
}
/**
* @returns The error message corresponding to the error type.
*/
export function getStartProxyErrorMessage(
errorType: StartProxyErrorType,
): string {
switch (errorType) {
case StartProxyErrorType.DownloadFailed:
return "Failed to download proxy archive.";
case StartProxyErrorType.ExtractionFailed:
return "Failed to extract proxy archive.";
case StartProxyErrorType.CacheFailed:
return "Failed to add proxy to toolcache";
}
}
/**
* We want to avoid accidentally leaking secrets that may be contained in exception
* messages in the `start-proxy` action. Consequently, we don't report the messages
* of arbitrary exceptions. This type of error ensures that the message is one from
* `StartProxyErrorType` and therefore safe to include in a status report.
*/
export class StartProxyError extends Error {
public readonly errorType: StartProxyErrorType;
constructor(errorType: StartProxyErrorType) {
super();
this.errorType = errorType;
}
}
interface StartProxyStatus extends StatusReportBase {
// A comma-separated list of registry types which are configured for CodeQL.
// This only includes registry types we support, not all that are configured.
registry_types: string;
}
/**
* Sends a status report for the `start-proxy` action indicating a successful outcome.
*
* @param startedAt When the action was started.
* @param config The configuration used.
* @param registry_types The types of registries that are configured.
* @param logger The logger to use.
*/
export async function sendSuccessStatusReport(
startedAt: Date,
config: Partial<Config>,
registry_types: string[],
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.StartProxy,
"success",
startedAt,
config,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
const statusReport: StartProxyStatus = {
...statusReportBase,
registry_types: registry_types.join(","),
};
await sendStatusReport(statusReport);
}
}
/**
* Returns an error message for `error` that can safely be reported in a status report,
* i.e. that does not contain sensitive information.
*
* @param error The error for which to get an error message.
*/
export function getSafeErrorMessage(error: Error): string {
// If the error is a `StartProxyError`, resolve the error type to the corresponding
// error message.
if (error instanceof StartProxyError) {
return getStartProxyErrorMessage(error.errorType);
}
// Otherwise, omit the actual error message.
return `Error from start-proxy Action omitted (${error.constructor.name}).`;
}
/**
* Sends a status report for the `start-proxy` action indicating a failure.
*
* @param logger The logger to use.
* @param startedAt When the action was started.
* @param language The language provided as input, if any.
* @param unwrappedError The exception that was thrown.
*/
export async function sendFailedStatusReport(
logger: Logger,
startedAt: Date,
language: KnownLanguage | undefined,
unwrappedError: unknown,
) {
const error = util.wrapError(unwrappedError);
core.setFailed(`start-proxy action failed: ${error.message}`);
// To avoid the possibility of leaking sensitive information into the telemetry,
// we don't include arbitrary error messages. Instead, `getSafeErrorMessage` will
// return a generic message that includes the type of the error, unless it can decide
// that the message is safe to include.
const statusReportMessage = getSafeErrorMessage(error);
const errorStatusReportBase = await createStatusReportBase(
ActionName.StartProxy,
getActionsStatus(error),
startedAt,
{
languages: language && [language],
},
await util.checkDiskUsage(logger),
logger,
statusReportMessage,
);
if (errorStatusReportBase !== undefined) {
await sendStatusReport(errorStatusReportBase);
}
}
export const UPDATEJOB_PROXY = "update-job-proxy";
export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
const UPDATEJOB_PROXY_URL_PREFIX =
@@ -426,125 +277,3 @@ export async function getDownloadUrl(
version: UPDATEJOB_PROXY_VERSION,
};
}
/**
* Pretty-prints a `Credential` value to a string, but hides the actual password or token values.
*
* @param c The credential to convert to a string.
*/
export function credentialToStr(c: Credential): string {
return `Type: ${c.type}; Host: ${c.host}; Url: ${c.url} Username: ${
c.username
}; Password: ${c.password !== undefined}; Token: ${c.token !== undefined}`;
}
/**
* Attempts to download a file from `url` into the toolcache.
*
* @param logger The logger to use.
* @param url The URL to download the proxy binary from.
* @param authorization The authorization information to use.
* @returns If successful, the path to the downloaded file.
*/
export async function downloadProxy(
logger: Logger,
url: string,
authorization: string | undefined,
) {
try {
// Download the proxy archive from `url`. We let `downloadTool` choose where
// to store it. The path to the downloaded file will be returned if successful.
return toolcache.downloadTool(url, /* dest: */ undefined, authorization, {
accept: "application/octet-stream",
});
} catch (error) {
logger.error(
`Failed to download proxy archive from ${url}: ${getErrorMessage(error)}`,
);
throw new StartProxyError(StartProxyErrorType.DownloadFailed);
}
}
/**
* Attempts to extract the proxy binary from the `archive`.
*
* @param logger The logger to use.
* @param archive The archive to extract.
* @returns The path to the extracted file(s).
*/
export async function extractProxy(logger: Logger, archive: string) {
try {
return await toolcache.extractTar(archive);
} catch (error) {
logger.error(
`Failed to extract proxy archive from ${archive}: ${getErrorMessage(error)}`,
);
throw new StartProxyError(StartProxyErrorType.ExtractionFailed);
}
}
/**
* Attempts to store the proxy in the toolcache.
*
* @param logger The logger to use.
* @param source The source path to add to the toolcache.
* @param filename The filename of the proxy binary.
* @param version The version of the proxy.
* @returns The path to the directory in the toolcache.
*/
export async function cacheProxy(
logger: Logger,
source: string,
filename: string,
version: string,
) {
try {
return await toolcache.cacheDir(source, filename, version);
} catch (error) {
logger.error(
`Failed to add proxy archive from ${source} to toolcache: ${getErrorMessage(error)}`,
);
throw new StartProxyError(StartProxyErrorType.CacheFailed);
}
}
/**
* Returns the platform-specific filename of the proxy binary.
*/
export function getProxyFilename() {
return process.platform === "win32"
? `${UPDATEJOB_PROXY}.exe`
: UPDATEJOB_PROXY;
}
/**
* Gets a path to the proxy binary. If possible, this function will find the proxy in the
* runner's tool cache. Otherwise, it downloads and extracts the proxy binary,
* and stores it in the tool cache.
*
* @param logger The logger to use.
* @returns The path to the proxy binary.
*/
export async function getProxyBinaryPath(logger: Logger): Promise<string> {
const proxyFileName = getProxyFilename();
const proxyInfo = await getDownloadUrl(logger);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {
const apiDetails = getApiDetails();
const authorization = getAuthorizationHeaderFor(
logger,
apiDetails,
proxyInfo.url,
);
const temp = await downloadProxy(logger, proxyInfo.url, authorization);
const extracted = await extractProxy(logger, temp);
proxyBin = await cacheProxy(
logger,
extracted,
proxyFileName,
proxyInfo.version,
);
}
return path.join(proxyBin, proxyFileName);
}
+18
View File
@@ -4,6 +4,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { Config } from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, QueriedFeatures } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import { ToolsSource } from "./setup-codeql";
@@ -46,6 +47,10 @@ test("createStatusReportBase", async (t) => {
await withTmpDir(async (tmpDir: string) => {
setupEnvironmentAndStub(tmpDir);
const features: QueriedFeatures = Object.fromEntries(
Object.values(Feature).map((key) => [key, { value: true }]),
);
const statusReport = await createStatusReportBase(
ActionName.Init,
"failure",
@@ -54,6 +59,7 @@ test("createStatusReportBase", async (t) => {
buildMode: BuildMode.None,
languages: [KnownLanguage.java, KnownLanguage.swift],
}),
features,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -75,6 +81,9 @@ test("createStatusReportBase", async (t) => {
t.is(statusReport.cause, "failure cause");
t.is(statusReport.commit_oid, process.env["GITHUB_SHA"]!);
t.is(statusReport.exception, "exception stack trace");
if (t.assert(statusReport.feature_flags)) {
t.is(statusReport.feature_flags.length, Object.keys(features).length);
}
t.is(statusReport.job_name, process.env["GITHUB_JOB"] || "");
t.is(typeof statusReport.job_run_uuid, "string");
t.is(statusReport.languages, "java,swift");
@@ -101,6 +110,7 @@ test("createStatusReportBase - empty configuration", async (t) => {
"success",
new Date("May 19, 2023 05:19:00"),
{},
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
);
@@ -123,6 +133,7 @@ test("createStatusReportBase - partial configuration", async (t) => {
{
languages: ["go"],
},
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
);
@@ -146,6 +157,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure",
new Date("May 19, 2023 05:19:00"),
createTestConfig({}),
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -162,6 +174,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure",
new Date("May 19, 2023 05:19:00"),
createTestConfig({}),
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -179,6 +192,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure",
new Date("May 19, 2023 05:19:00"),
createTestConfig({}),
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -195,6 +209,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure",
new Date("May 19, 2023 05:19:00"),
createTestConfig({}),
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -212,6 +227,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure",
new Date("May 19, 2023 05:19:00"),
createTestConfig({}),
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -228,6 +244,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure",
new Date("May 19, 2023 05:19:00"),
createTestConfig({}),
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
@@ -307,6 +324,7 @@ const testCreateInitWithConfigStatusReport = test.macro({
"failure",
new Date("May 19, 2023 05:19:00"),
config,
undefined,
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
"failure cause",
+24 -1
View File
@@ -16,6 +16,7 @@ import { parseRegistriesWithoutCredentials, type Config } from "./config-utils";
import { DependencyCacheRestoreStatusReport } from "./dependency-caching";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { QueriedFeatures, QueriedFeatureStatus } from "./feature-flags";
import { getRef } from "./git-utils";
import { Logger } from "./logging";
import { OverlayBaseDatabaseDownloadStats } from "./overlay-database-utils";
@@ -83,6 +84,12 @@ export enum JobStatus {
ConfigErrorStatus = "JOB_STATUS_CONFIGURATION_ERROR",
}
/** Information about the outcome of a querying a feature flag during this step. */
export interface QueriedFeatureStatusReport extends QueriedFeatureStatus {
// The name of the feature.
feature: string;
}
export interface StatusReportBase {
/** Name of the action being executed. */
action_name: ActionName;
@@ -112,6 +119,8 @@ export interface StatusReportBase {
completed_at?: string;
/** Stack trace of the failure (or undefined if status is not failure). */
exception?: string;
/** Outcomes of querying feature flags during this step. */
feature_flags?: QueriedFeatureStatusReport[];
/** Whether this is a first-party (CodeQL) run of the action. */
first_party_analysis: boolean;
/** Job name from the workflow. */
@@ -253,6 +262,10 @@ export interface EventReport {
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
* @param status The status. Must be 'success', 'failure', or 'starting'
* @param actionStartedAt The time this action started executing.
* @param config The configuration for the action, even partially, if any.
* @param queriedFeatures Information about queried feature flags, if available.
* @param diskInfo Information about disk usage, if available.
* @param logger The logger to use.
* @param cause Cause of failure (only supply if status is 'failure')
* @param exception Exception (only supply if status is 'failure')
* @returns undefined if an exception was thrown.
@@ -262,6 +275,7 @@ export async function createStatusReportBase(
status: ActionStatus,
actionStartedAt: Date,
config: Partial<Config> | undefined,
queriedFeatures: QueriedFeatures | undefined,
diskInfo: DiskUsage | undefined,
logger: Logger,
cause?: string,
@@ -294,6 +308,13 @@ export async function createStatusReportBase(
const isSteadyStateDefaultSetupRun =
process.env["CODE_SCANNING_IS_STEADY_STATE_DEFAULT_SETUP"] === "true";
let featureFlags: QueriedFeatureStatusReport[] | undefined = undefined;
if (queriedFeatures) {
featureFlags = Object.entries(queriedFeatures).map(
([feature, outcome]) => ({ feature, ...outcome }),
);
}
const statusReport: StatusReportBase = {
action_name: actionName,
action_oid: "unknown", // TODO decide if it's possible to fill this in
@@ -304,6 +325,7 @@ export async function createStatusReportBase(
analysis_key,
build_mode: config?.buildMode,
commit_oid: commitOid,
feature_flags: featureFlags,
first_party_analysis: isFirstPartyAnalysis(actionName),
job_name: jobName,
job_run_uuid: jobRunUUID,
@@ -518,7 +540,7 @@ export interface InitWithConfigStatusReport extends InitStatusReport {
/** Stringified JSON array of registry configuration objects, from the 'registries' config field
or workflow input. **/
registries: string;
/** Stringified JSON object representing a query-filters, from the 'query-filters' config field. **/
/** Stringified JSON object representing query-filters, from the 'query-filters' config field. **/
query_filters: string;
/** Path to the specified code scanning config file, from the 'config-file' config field. */
config_file: string;
@@ -623,6 +645,7 @@ export async function sendUnhandledErrorStatusReport(
"failure",
actionStartedAt,
undefined,
{},
undefined,
logger,
`Unhandled CodeQL Action error: ${getErrorMessage(error)}`,
-17
View File
@@ -208,23 +208,6 @@ export function checkExpectedLogMessages(
}
}
/**
* Initialises a recording logger and calls `body` with it.
*
* @param body The test that requires a recording logger.
* @returns The logged messages.
*/
export async function withRecordingLoggerAsync(
body: (logger: Logger) => Promise<void>,
): Promise<LoggedMessage[]> {
const messages = [];
const logger = getRecordingLogger(messages);
await body(logger);
return messages;
}
/** Mock the HTTP request to the feature flags enablement API endpoint. */
export function mockFeatureFlagApiEndpoint(
responseStatusCode: number,
+7 -1
View File
@@ -34,6 +34,7 @@ interface UploadSarifStatusReport
async function sendSuccessStatusReport(
startedAt: Date,
features: Features | undefined,
uploadStats: upload_lib.UploadStatusReport,
logger: Logger,
) {
@@ -42,6 +43,7 @@ async function sendSuccessStatusReport(
"success",
startedAt,
undefined,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
);
@@ -59,6 +61,7 @@ async function run(startedAt: Date) {
// possible, and only use safe functions outside.
const logger = getActionsLogger();
let features: Features | undefined = undefined;
try {
initializeEnvironment(getActionVersion());
@@ -70,7 +73,7 @@ async function run(startedAt: Date) {
actionsUtil.persistInputs();
const repositoryNwo = getRepositoryNwo();
const features = new Features(
features = new Features(
gitHubVersion,
repositoryNwo,
getTemporaryDirectory(),
@@ -82,6 +85,7 @@ async function run(startedAt: Date) {
"starting",
startedAt,
undefined,
features.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
);
@@ -135,6 +139,7 @@ async function run(startedAt: Date) {
}
await sendSuccessStatusReport(
startedAt,
features,
codeScanningResult?.statusReport || {},
logger,
);
@@ -152,6 +157,7 @@ async function run(startedAt: Date) {
getActionsStatus(error),
startedAt,
undefined,
features?.getQueriedFeatures(),
await checkDiskUsage(logger),
logger,
message,
-25
View File
@@ -563,28 +563,3 @@ test("joinAtMost - truncates list if array is > than limit", (t) => {
t.assert(result.includes("test5"));
t.false(result.includes("test6"));
});
test("Result.success creates a success result", (t) => {
const result = util.Result.success("test value");
t.true(result.isSuccess());
t.false(result.isFailure());
t.is(result.value, "test value");
});
test("Result.failure creates a failure result", (t) => {
const error = new Error("test error");
const result = util.Result.failure(error);
t.false(result.isSuccess());
t.true(result.isFailure());
t.is(result.value, error);
});
test("Result.orElse returns the value for a success result", (t) => {
const result = util.Result.success("success value");
t.is(result.orElse("default value"), "success value");
});
test("Result.orElse returns the default value for a failure result", (t) => {
const result = util.Result.failure(new Error("test error"));
t.is(result.orElse("default value"), "default value");
});
-40
View File
@@ -1292,43 +1292,3 @@ export function joinAtMost(
return array.join(separator);
}
/** A success result. */
type Success<T> = Result<T, never>;
/** A failure result. */
type Failure<E> = Result<never, E>;
/**
* A simple result type representing either a success or a failure.
*/
export class Result<T, E> {
private constructor(
private readonly _ok: boolean,
public readonly value: T | E,
) {}
/** Creates a success result. */
static success<T>(value: T): Success<T> {
return new Result(true, value) as Success<T>;
}
/** Creates a failure result. */
static failure<E>(value: E): Failure<E> {
return new Result(false, value) as Failure<E>;
}
/** Whether this result represents a success. */
isSuccess(): this is Success<T> {
return this._ok;
}
/** Whether this result represents a failure. */
isFailure(): this is Failure<E> {
return !this._ok;
}
/** Get the value if this is a success, or return the default value if this is a failure. */
orElse<U>(defaultValue: U): T | U {
return this.isSuccess() ? this.value : defaultValue;
}
}