mirror of
https://github.com/github/codeql-action.git
synced 2026-11-04 20:20:23 +00:00
Compare commits
62 Commits
mbg/track-
...
codeql-bun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77591e2c4a | ||
|
|
7a44a9db3f | ||
|
|
e2ac371513 | ||
|
|
7deb0a15d3 | ||
|
|
f959778b39 | ||
|
|
d38ad56358 | ||
|
|
bc9796e2e0 | ||
|
|
ab5b0e3aab | ||
|
|
57a47f44df | ||
|
|
076d055bee | ||
|
|
6d4cd5d744 | ||
|
|
42fb267c1c | ||
|
|
832a783bd4 | ||
|
|
160e695297 | ||
|
|
8aac4e47ac | ||
|
|
e8d7df4f04 | ||
|
|
c1bba77db0 | ||
|
|
6bc82e05fd | ||
|
|
42f00f2d33 | ||
|
|
cedee6de9f | ||
|
|
f52cbc8309 | ||
|
|
c5aaca4bb9 | ||
|
|
3e58739c65 | ||
|
|
a6ccefb47c | ||
|
|
0e64858573 | ||
|
|
beb9f533db | ||
|
|
a1c70789a3 | ||
|
|
d94d88d717 | ||
|
|
a6d296a341 | ||
|
|
28f6d316c0 | ||
|
|
1d0f911837 | ||
|
|
05bd050f34 | ||
|
|
325a3a2ae3 | ||
|
|
6394750070 | ||
|
|
f1588cde0c | ||
|
|
f985be5b50 | ||
|
|
4dcc8a9cdc | ||
|
|
fbe3ae9de8 | ||
|
|
2a384c1c14 | ||
|
|
0c8e06dfb2 | ||
|
|
b2ff80ddac | ||
|
|
48f3548141 | ||
|
|
800dfbe5e1 | ||
|
|
1314d3d17e | ||
|
|
f7f9d3f341 | ||
|
|
eb5bd2d0b9 | ||
|
|
9aa0515f67 | ||
|
|
0720e13f0b | ||
|
|
38ba96d2aa | ||
|
|
679da45cc3 | ||
|
|
d5dd165f8b | ||
|
|
fbf75ebd7b | ||
|
|
6a50972d16 | ||
|
|
5cb12c41c2 | ||
|
|
e8f487178f | ||
|
|
a0671be58e | ||
|
|
9ea34c5169 | ||
|
|
9fda641d8d | ||
|
|
d9e374ef85 | ||
|
|
f4b47e7013 | ||
|
|
4e14537b54 | ||
|
|
e142eee9b4 |
2
.github/workflows/__bundle-from-toolcache.yml
generated
vendored
2
.github/workflows/__bundle-from-toolcache.yml
generated
vendored
@@ -56,7 +56,7 @@ jobs:
|
|||||||
use-all-platform-bundle: 'false'
|
use-all-platform-bundle: 'false'
|
||||||
setup-kotlin: 'true'
|
setup-kotlin: 'true'
|
||||||
- name: Install @actions/tool-cache
|
- name: Install @actions/tool-cache
|
||||||
run: npm install @actions/tool-cache
|
run: npm install @actions/tool-cache@3
|
||||||
- name: Check toolcache contains CodeQL
|
- name: Check toolcache contains CodeQL
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v8
|
||||||
|
|||||||
2
.github/workflows/__bundle-toolcache.yml
generated
vendored
2
.github/workflows/__bundle-toolcache.yml
generated
vendored
@@ -68,7 +68,7 @@ jobs:
|
|||||||
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
|
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
|
||||||
fs.rmdirSync(codeqlPath, { recursive: true });
|
fs.rmdirSync(codeqlPath, { recursive: true });
|
||||||
- name: Install @actions/tool-cache
|
- name: Install @actions/tool-cache
|
||||||
run: npm install @actions/tool-cache
|
run: npm install @actions/tool-cache@3
|
||||||
- name: Check toolcache does not contain CodeQL
|
- name: Check toolcache does not contain CodeQL
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
|
|||||||
12
.github/workflows/__global-proxy.yml
generated
vendored
12
.github/workflows/__global-proxy.yml
generated
vendored
@@ -48,18 +48,6 @@ jobs:
|
|||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
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
|
- name: Check out repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
- name: Prepare test
|
- name: Prepare test
|
||||||
|
|||||||
2
.github/workflows/rebuild.yml
vendored
2
.github/workflows/rebuild.yml
vendored
@@ -111,7 +111,7 @@ jobs:
|
|||||||
# Otherwise, just commit the changes.
|
# Otherwise, just commit the changes.
|
||||||
if git rev-parse --verify MERGE_HEAD >/dev/null 2>&1; then
|
if git rev-parse --verify MERGE_HEAD >/dev/null 2>&1; then
|
||||||
echo "In progress merge detected, finishing it up."
|
echo "In progress merge detected, finishing it up."
|
||||||
git merge --continue
|
git merge --continue --no-edit
|
||||||
else
|
else
|
||||||
echo "No in-progress merge detected, committing changes."
|
echo "No in-progress merge detected, committing changes."
|
||||||
git commit -m "Rebuild"
|
git commit -m "Rebuild"
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
|
|||||||
|
|
||||||
No user facing changes.
|
No user facing changes.
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
## 4.32.0 - 26 Jan 2026
|
## 4.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)
|
- 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)
|
||||||
|
|||||||
37961
lib/analyze-action-post.js
generated
37961
lib/analyze-action-post.js
generated
File diff suppressed because one or more lines are too long
37342
lib/analyze-action.js
generated
37342
lib/analyze-action.js
generated
File diff suppressed because one or more lines are too long
37338
lib/autobuild-action.js
generated
37338
lib/autobuild-action.js
generated
File diff suppressed because one or more lines are too long
37963
lib/init-action-post.js
generated
37963
lib/init-action-post.js
generated
File diff suppressed because one or more lines are too long
41380
lib/init-action.js
generated
41380
lib/init-action.js
generated
File diff suppressed because one or more lines are too long
37338
lib/resolve-environment-action.js
generated
37338
lib/resolve-environment-action.js
generated
File diff suppressed because one or more lines are too long
37344
lib/setup-codeql-action.js
generated
37344
lib/setup-codeql-action.js
generated
File diff suppressed because one or more lines are too long
37961
lib/start-proxy-action-post.js
generated
37961
lib/start-proxy-action-post.js
generated
File diff suppressed because one or more lines are too long
43760
lib/start-proxy-action.js
generated
43760
lib/start-proxy-action.js
generated
File diff suppressed because one or more lines are too long
37340
lib/upload-lib.js
generated
37340
lib/upload-lib.js
generated
File diff suppressed because one or more lines are too long
37927
lib/upload-sarif-action-post.js
generated
37927
lib/upload-sarif-action-post.js
generated
File diff suppressed because one or more lines are too long
37346
lib/upload-sarif-action.js
generated
37346
lib/upload-sarif-action.js
generated
File diff suppressed because one or more lines are too long
165
package-lock.json
generated
165
package-lock.json
generated
@@ -1,24 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "4.32.1",
|
"version": "4.32.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "4.32.1",
|
"version": "4.32.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": "^5.0.2",
|
"@actions/artifact": "^5.0.3",
|
||||||
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
||||||
"@actions/cache": "^5.0.3",
|
"@actions/cache": "^5.0.5",
|
||||||
"@actions/core": "^2.0.2",
|
"@actions/core": "^2.0.3",
|
||||||
"@actions/exec": "^2.0.0",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/github": "^8.0.0",
|
"@actions/github": "^8.0.0",
|
||||||
"@actions/glob": "^0.5.0",
|
"@actions/glob": "^0.5.0",
|
||||||
"@actions/http-client": "^3.0.0",
|
"@actions/http-client": "^3.0.0",
|
||||||
"@actions/io": "^2.0.0",
|
"@actions/io": "^2.0.0",
|
||||||
"@actions/tool-cache": "^3.0.0",
|
"@actions/tool-cache": "^3.0.1",
|
||||||
"@octokit/plugin-retry": "^8.0.0",
|
"@octokit/plugin-retry": "^8.0.0",
|
||||||
"@schemastore/package": "0.0.10",
|
"@schemastore/package": "0.0.10",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"eslint-plugin-filenames": "^1.3.2",
|
"eslint-plugin-filenames": "^1.3.2",
|
||||||
"eslint-plugin-github": "^5.1.8",
|
"eslint-plugin-github": "^5.1.8",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-jsdoc": "^62.2.0",
|
"eslint-plugin-jsdoc": "^62.3.0",
|
||||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||||
"glob": "^11.1.0",
|
"glob": "^11.1.0",
|
||||||
"nock": "^14.0.10",
|
"nock": "^14.0.10",
|
||||||
@@ -72,14 +72,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/artifact": {
|
"node_modules/@actions/artifact": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.3.tgz",
|
||||||
"integrity": "sha512-NmA+THO1s/Ubqf5y34EZofB1FRORcToa3T8Mb3/Z87TCCozTtvBUaxDn8OPPwgyYQ4ft0nd01Z4kFSu2DTWqGw==",
|
"integrity": "sha512-FIEG8Kum0wABZnktJvFi1xuVPc31xrunhZwLCvjrCGISQOm0ifyo7cjqf6PHiEeqoWMa5HIGOsB+lGM4aKCseA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^2.0.0",
|
"@actions/core": "^2.0.0",
|
||||||
"@actions/github": "^6.0.1",
|
"@actions/github": "^6.0.1",
|
||||||
"@actions/http-client": "^3.0.1",
|
"@actions/http-client": "^3.0.2",
|
||||||
"@azure/storage-blob": "^12.29.1",
|
"@azure/storage-blob": "^12.29.1",
|
||||||
"@octokit/core": "^5.2.1",
|
"@octokit/core": "^5.2.1",
|
||||||
"@octokit/plugin-request-log": "^1.0.4",
|
"@octokit/plugin-request-log": "^1.0.4",
|
||||||
@@ -351,15 +351,15 @@
|
|||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@actions/cache": {
|
"node_modules/@actions/cache": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.5.tgz",
|
||||||
"integrity": "sha512-9joY8Oup+nIpksSBlkuf9/mltnhWx3lydk1tA2PVnXaxFLIIrKqrWDN2CZXlJ+PEErcBARKYn4mHiUCTyMh4Vg==",
|
"integrity": "sha512-jiQSg0gfd+C2KPgcmdCOq7dCuCIQQWQ4b1YfGIRaaA9w7PJbRwTOcCz4LiFEUnqZGf0ha/8OKL3BeNwetHzYsQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^2.0.0",
|
"@actions/core": "^2.0.0",
|
||||||
"@actions/exec": "^2.0.0",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/glob": "^0.5.0",
|
"@actions/glob": "^0.5.1",
|
||||||
"@actions/http-client": "^3.0.1",
|
"@actions/http-client": "^3.0.2",
|
||||||
"@actions/io": "^2.0.0",
|
"@actions/io": "^2.0.0",
|
||||||
"@azure/abort-controller": "^1.1.0",
|
"@azure/abort-controller": "^1.1.0",
|
||||||
"@azure/core-rest-pipeline": "^1.22.0",
|
"@azure/core-rest-pipeline": "^1.22.0",
|
||||||
@@ -376,13 +376,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/core": {
|
"node_modules/@actions/core": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.3.tgz",
|
||||||
"integrity": "sha512-Ast1V7yHbGAhplAsuVlnb/5J8Mtr/Zl6byPPL+Qjq3lmfIgWF1ak1iYfF/079cRERiuTALTXkSuEUdZeDCfGtA==",
|
"integrity": "sha512-Od9Thc3T1mQJYddvVPM4QGiLUewdh+3txmDYHHxoNdkqysR1MbCT+rFOtNUxYAz+7+6RIsqipVahY2GJqGPyxA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/exec": "^2.0.0",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/http-client": "^3.0.1"
|
"@actions/http-client": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/exec": {
|
"node_modules/@actions/exec": {
|
||||||
@@ -440,57 +440,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/glob": {
|
"node_modules/@actions/glob": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.1.tgz",
|
||||||
"integrity": "sha512-tST2rjPvJLRZLuT9NMUtyBjvj9Yo0MiJS3ow004slMvm8GFM+Zv9HvMJ7HWzfUyJnGrJvDsYkWBaaG3YKXRtCw==",
|
"integrity": "sha512-+dv/t2aKQdKp9WWSp+1yIXVJzH5Q38M0Mta26pzIbeec14EcIleMB7UU6N7sNgbEuYfyuVGpE5pOKjl6j1WXkA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.9.1",
|
"@actions/core": "^2.0.3",
|
||||||
"minimatch": "^3.0.4"
|
"minimatch": "^3.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": "^5.25.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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": {
|
"node_modules/@actions/http-client": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
|
||||||
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
|
"integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
"undici": "^5.28.5"
|
"undici": "^6.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/io": {
|
"node_modules/@actions/io": {
|
||||||
@@ -500,14 +475,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@actions/tool-cache": {
|
"node_modules/@actions/tool-cache": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-3.0.1.tgz",
|
||||||
"integrity": "sha512-JBx8gEWuu8Lqaqx/hEnL6QdKvF06suBR4y+dBDi9vJbHx1r+p6QtmBKhQYhiKjDUYIoDX1bUrbyAYPChoPK+XA==",
|
"integrity": "sha512-euK7sID37jMg1yWGkdXkLPI5Te7x/+2QMUPeHXogcpzUZ81mqlDZ+CgYhQo3LtB8LpVnnQyjs+hTTU0Ir4Y0RQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^2.0.1",
|
"@actions/core": "^2.0.1",
|
||||||
"@actions/exec": "^2.0.0",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/http-client": "^3.0.1",
|
"@actions/http-client": "^3.0.2",
|
||||||
"@actions/io": "^2.0.0",
|
"@actions/io": "^2.0.0",
|
||||||
"semver": "^6.1.0"
|
"semver": "^6.1.0"
|
||||||
}
|
}
|
||||||
@@ -833,26 +808,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@es-joy/jsdoccomment": {
|
"node_modules/@es-joy/jsdoccomment": {
|
||||||
"version": "0.81.0",
|
"version": "0.82.0",
|
||||||
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.81.0.tgz",
|
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.82.0.tgz",
|
||||||
"integrity": "sha512-4V4A0hFAB19id7w9iwiosV/rqwlH+PXEuYnnu1Cyc5jUjTwsE2G1qsX9TOCmfCmsWYBg6xeDC/XDFUzXAxDg3A==",
|
"integrity": "sha512-xs3OTxPefjTZaoDS7H1X2pV33enAmZg+8YldjmeYk7XZnq420phdnp6o0JtrsHBdSRJ5+RTocgyED9TL3epgpw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.8",
|
"@types/estree": "^1.0.8",
|
||||||
"@typescript-eslint/types": "^8.53.0",
|
"@typescript-eslint/types": "^8.53.1",
|
||||||
"comment-parser": "1.4.4",
|
"comment-parser": "1.4.4",
|
||||||
"esquery": "^1.7.0",
|
"esquery": "^1.7.0",
|
||||||
"jsdoc-type-pratt-parser": "~7.0.0"
|
"jsdoc-type-pratt-parser": "~7.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": {
|
"node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.53.1",
|
"version": "8.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
|
||||||
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
|
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1525,9 +1500,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@isaacs/brace-expansion": {
|
"node_modules/@isaacs/brace-expansion": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
|
||||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/balanced-match": "^4.0.1"
|
"@isaacs/balanced-match": "^4.0.1"
|
||||||
@@ -4901,19 +4876,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-jsdoc": {
|
"node_modules/eslint-plugin-jsdoc": {
|
||||||
"version": "62.2.0",
|
"version": "62.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.3.0.tgz",
|
||||||
"integrity": "sha512-juIekE88Gy1FnjsQuwkrZ3OW4UCkQuhiBf9QqF9U1xgqtan4rQFCOUn4cKeHR1ObBqJ8/pG8a0lZlAuKlDLpdw==",
|
"integrity": "sha512-Gc5Ls5qQC6NwqtQTtJ2JE5BwvX348ZCZ+4+QiZ9RpoQ1TCcxFF8Z0E5jaLkTyYNqyhx+uKAvljNHE0B7PBw+iw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@es-joy/jsdoccomment": "~0.81.0",
|
"@es-joy/jsdoccomment": "~0.82.0",
|
||||||
"@es-joy/resolve.exports": "1.2.0",
|
"@es-joy/resolve.exports": "1.2.0",
|
||||||
"are-docs-informative": "^0.0.2",
|
"are-docs-informative": "^0.0.2",
|
||||||
"comment-parser": "1.4.4",
|
"comment-parser": "1.4.4",
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"espree": "^11.0.0",
|
"espree": "^11.1.0",
|
||||||
"esquery": "^1.7.0",
|
"esquery": "^1.7.0",
|
||||||
"html-entities": "^2.6.0",
|
"html-entities": "^2.6.0",
|
||||||
"object-deep-merge": "^2.0.0",
|
"object-deep-merge": "^2.0.0",
|
||||||
@@ -4974,9 +4949,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-jsdoc/node_modules/espree": {
|
"node_modules/eslint-plugin-jsdoc/node_modules/espree": {
|
||||||
"version": "11.0.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-11.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz",
|
||||||
"integrity": "sha512-+gMeWRrIh/NsG+3NaLeWHuyeyk70p2tbvZIWBYcqQ4/7Xvars6GYTZNhF1sIeLcc6Wb11He5ffz3hsHyXFrw5A==",
|
"integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5467,9 +5442,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-xml-parser": {
|
"node_modules/fast-xml-parser": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz",
|
||||||
"integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==",
|
"integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -6511,9 +6486,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsdoc-type-pratt-parser": {
|
"node_modules/jsdoc-type-pratt-parser": {
|
||||||
"version": "7.0.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.0.tgz",
|
||||||
"integrity": "sha512-c7YbokssPOSHmqTbSAmTtnVgAVa/7lumWNYqomgd5KOMyPrRve2anx6lonfOsXEQacqF9FKVUj7bLg4vRSvdYA==",
|
"integrity": "sha512-SX7q7XyCwzM/MEDCYz0l8GgGbJAACGFII9+WfNYr5SLEKukHWRy2Jk3iWRe7P+lpYJNs7oQ+OSei4JtKGUjd7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -8159,9 +8134,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar": {
|
"node_modules/tar": {
|
||||||
"version": "7.5.6",
|
"version": "7.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
|
||||||
"integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==",
|
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "4.32.1",
|
"version": "4.32.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "CodeQL action",
|
"description": "CodeQL action",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -24,16 +24,16 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": "^5.0.2",
|
"@actions/artifact": "^5.0.3",
|
||||||
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
||||||
"@actions/cache": "^5.0.3",
|
"@actions/cache": "^5.0.5",
|
||||||
"@actions/core": "^2.0.2",
|
"@actions/core": "^2.0.3",
|
||||||
"@actions/exec": "^2.0.0",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/github": "^8.0.0",
|
"@actions/github": "^8.0.0",
|
||||||
"@actions/glob": "^0.5.0",
|
"@actions/glob": "^0.5.0",
|
||||||
"@actions/http-client": "^3.0.0",
|
"@actions/http-client": "^3.0.0",
|
||||||
"@actions/io": "^2.0.0",
|
"@actions/io": "^2.0.0",
|
||||||
"@actions/tool-cache": "^3.0.0",
|
"@actions/tool-cache": "^3.0.1",
|
||||||
"@octokit/plugin-retry": "^8.0.0",
|
"@octokit/plugin-retry": "^8.0.0",
|
||||||
"@schemastore/package": "0.0.10",
|
"@schemastore/package": "0.0.10",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"eslint-plugin-filenames": "^1.3.2",
|
"eslint-plugin-filenames": "^1.3.2",
|
||||||
"eslint-plugin-github": "^5.1.8",
|
"eslint-plugin-github": "^5.1.8",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-jsdoc": "^62.2.0",
|
"eslint-plugin-jsdoc": "^62.3.0",
|
||||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||||
"glob": "^11.1.0",
|
"glob": "^11.1.0",
|
||||||
"nock": "^14.0.10",
|
"nock": "^14.0.10",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ versions:
|
|||||||
- toolcache
|
- toolcache
|
||||||
steps:
|
steps:
|
||||||
- name: Install @actions/tool-cache
|
- name: Install @actions/tool-cache
|
||||||
run: npm install @actions/tool-cache
|
run: npm install @actions/tool-cache@3
|
||||||
- name: Check toolcache contains CodeQL
|
- name: Check toolcache contains CodeQL
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v8
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ steps:
|
|||||||
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
|
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
|
||||||
fs.rmdirSync(codeqlPath, { recursive: true });
|
fs.rmdirSync(codeqlPath, { recursive: true });
|
||||||
- name: Install @actions/tool-cache
|
- name: Install @actions/tool-cache
|
||||||
run: npm install @actions/tool-cache
|
run: npm install @actions/tool-cache@3
|
||||||
- name: Check toolcache does not contain CodeQL
|
- name: Check toolcache does not contain CodeQL
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -3,19 +3,6 @@ description: "Tests using a proxy specified by the https_proxy environment varia
|
|||||||
versions: ["linked", "nightly-latest"]
|
versions: ["linked", "nightly-latest"]
|
||||||
container:
|
container:
|
||||||
image: ubuntu:22.04
|
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:
|
services:
|
||||||
squid-proxy:
|
squid-proxy:
|
||||||
image: ubuntu/squid:latest
|
image: ubuntu/squid:latest
|
||||||
|
|||||||
@@ -306,7 +306,8 @@ export function wrapApiConfigurationError(e: unknown) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
httpError.message.includes("Bad credentials") ||
|
httpError.message.includes("Bad credentials") ||
|
||||||
httpError.message.includes("Not Found")
|
httpError.message.includes("Not Found") ||
|
||||||
|
httpError.message.includes("Requires authentication")
|
||||||
) {
|
) {
|
||||||
return new ConfigurationError(
|
return new ConfigurationError(
|
||||||
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
|
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ import {
|
|||||||
parseUserConfig,
|
parseUserConfig,
|
||||||
UserConfig,
|
UserConfig,
|
||||||
} from "./config/db-config";
|
} from "./config/db-config";
|
||||||
import { addDiagnostic, makeTelemetryDiagnostic } from "./diagnostics";
|
import {
|
||||||
|
addNoLanguageDiagnostic,
|
||||||
|
makeTelemetryDiagnostic,
|
||||||
|
} from "./diagnostics";
|
||||||
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||||
import { EnvVar } from "./environment";
|
import { EnvVar } from "./environment";
|
||||||
import * as errorMessages from "./error-messages";
|
import * as errorMessages from "./error-messages";
|
||||||
@@ -1416,11 +1419,8 @@ async function logGitVersionTelemetry(
|
|||||||
gitVersion: GitVersionInfo,
|
gitVersion: GitVersionInfo,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (config.languages.length > 0) {
|
if (config.languages.length > 0) {
|
||||||
addDiagnostic(
|
addNoLanguageDiagnostic(
|
||||||
config,
|
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(
|
makeTelemetryDiagnostic(
|
||||||
"codeql-action/git-version-telemetry",
|
"codeql-action/git-version-telemetry",
|
||||||
"Git version telemetry",
|
"Git version telemetry",
|
||||||
@@ -1446,11 +1446,8 @@ async function logGeneratedFilesTelemetry(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addDiagnostic(
|
addNoLanguageDiagnostic(
|
||||||
config,
|
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(
|
makeTelemetryDiagnostic(
|
||||||
"codeql-action/generated-files-telemetry",
|
"codeql-action/generated-files-telemetry",
|
||||||
"Generated files telemetry",
|
"Generated files telemetry",
|
||||||
|
|||||||
@@ -117,6 +117,20 @@ 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.
|
* Writes the given diagnostic to the database.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -78,11 +78,17 @@ export async function loadPropertiesFromApi(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Loaded the following values for the repository properties:");
|
if (Object.keys(properties).length === 0) {
|
||||||
for (const [property, value] of Object.entries(properties).sort(
|
logger.debug("No known repository properties were found.");
|
||||||
([nameA], [nameB]) => nameA.localeCompare(nameB),
|
} else {
|
||||||
)) {
|
logger.debug(
|
||||||
logger.debug(` ${property}: ${value}`);
|
"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;
|
return properties;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as fs from "fs";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
|
import * as github from "@actions/github";
|
||||||
import * as io from "@actions/io";
|
import * as io from "@actions/io";
|
||||||
import * as semver from "semver";
|
import * as semver from "semver";
|
||||||
import { v4 as uuidV4 } from "uuid";
|
import { v4 as uuidV4 } from "uuid";
|
||||||
@@ -30,14 +31,18 @@ import {
|
|||||||
} from "./dependency-caching";
|
} from "./dependency-caching";
|
||||||
import {
|
import {
|
||||||
addDiagnostic,
|
addDiagnostic,
|
||||||
|
addNoLanguageDiagnostic,
|
||||||
flushDiagnostics,
|
flushDiagnostics,
|
||||||
logUnwrittenDiagnostics,
|
logUnwrittenDiagnostics,
|
||||||
makeDiagnostic,
|
makeDiagnostic,
|
||||||
makeTelemetryDiagnostic,
|
makeTelemetryDiagnostic,
|
||||||
} from "./diagnostics";
|
} from "./diagnostics";
|
||||||
import { EnvVar } from "./environment";
|
import { EnvVar } from "./environment";
|
||||||
import { Feature, Features } from "./feature-flags";
|
import { Feature, FeatureEnablement, Features } from "./feature-flags";
|
||||||
import { loadPropertiesFromApi } from "./feature-flags/properties";
|
import {
|
||||||
|
loadPropertiesFromApi,
|
||||||
|
RepositoryProperties,
|
||||||
|
} from "./feature-flags/properties";
|
||||||
import {
|
import {
|
||||||
checkInstallPython311,
|
checkInstallPython311,
|
||||||
checkPacksForOverlayCompatibility,
|
checkPacksForOverlayCompatibility,
|
||||||
@@ -54,7 +59,7 @@ import {
|
|||||||
OverlayBaseDatabaseDownloadStats,
|
OverlayBaseDatabaseDownloadStats,
|
||||||
OverlayDatabaseMode,
|
OverlayDatabaseMode,
|
||||||
} from "./overlay-database-utils";
|
} from "./overlay-database-utils";
|
||||||
import { getRepositoryNwo } from "./repository";
|
import { getRepositoryNwo, RepositoryNwo } from "./repository";
|
||||||
import { ToolsSource } from "./setup-codeql";
|
import { ToolsSource } from "./setup-codeql";
|
||||||
import {
|
import {
|
||||||
ActionName,
|
ActionName,
|
||||||
@@ -88,6 +93,8 @@ import {
|
|||||||
checkActionVersion,
|
checkActionVersion,
|
||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
BuildMode,
|
BuildMode,
|
||||||
|
GitHubVersion,
|
||||||
|
Result,
|
||||||
} from "./util";
|
} from "./util";
|
||||||
import { checkWorkflow } from "./workflow";
|
import { checkWorkflow } from "./workflow";
|
||||||
|
|
||||||
@@ -238,12 +245,12 @@ async function run(startedAt: Date) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Fetch the values of known repository properties that affect us.
|
// Fetch the values of known repository properties that affect us.
|
||||||
const enableRepoProps = await features.getValue(
|
const repositoryPropertiesResult = await loadRepositoryProperties(
|
||||||
Feature.UseRepositoryProperties,
|
repositoryNwo,
|
||||||
|
gitHubVersion,
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
);
|
);
|
||||||
const repositoryProperties = enableRepoProps
|
|
||||||
? await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo)
|
|
||||||
: {};
|
|
||||||
|
|
||||||
// Create a unique identifier for this run.
|
// Create a unique identifier for this run.
|
||||||
const jobRunUuid = uuidV4();
|
const jobRunUuid = uuidV4();
|
||||||
@@ -365,7 +372,7 @@ async function run(startedAt: Date) {
|
|||||||
githubVersion: gitHubVersion,
|
githubVersion: gitHubVersion,
|
||||||
apiDetails,
|
apiDetails,
|
||||||
features,
|
features,
|
||||||
repositoryProperties,
|
repositoryProperties: repositoryPropertiesResult.orElse({}),
|
||||||
enableFileCoverageInformation: await getFileCoverageInformationEnabled(
|
enableFileCoverageInformation: await getFileCoverageInformationEnabled(
|
||||||
debugMode,
|
debugMode,
|
||||||
repositoryNwo,
|
repositoryNwo,
|
||||||
@@ -374,6 +381,19 @@ async function run(startedAt: Date) {
|
|||||||
logger,
|
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);
|
await checkInstallPython311(config.languages, codeql);
|
||||||
} catch (unwrappedError) {
|
} catch (unwrappedError) {
|
||||||
const error = wrapError(unwrappedError);
|
const error = wrapError(unwrappedError);
|
||||||
@@ -436,11 +456,8 @@ async function run(startedAt: Date) {
|
|||||||
|
|
||||||
// Log CodeQL download telemetry, if appropriate
|
// Log CodeQL download telemetry, if appropriate
|
||||||
if (toolsDownloadStatusReport) {
|
if (toolsDownloadStatusReport) {
|
||||||
addDiagnostic(
|
addNoLanguageDiagnostic(
|
||||||
config,
|
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(
|
makeTelemetryDiagnostic(
|
||||||
"codeql-action/bundle-download-telemetry",
|
"codeql-action/bundle-download-telemetry",
|
||||||
"CodeQL bundle download telemetry",
|
"CodeQL bundle download telemetry",
|
||||||
@@ -782,6 +799,49 @@ 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 {
|
function getTrapCachingEnabled(): boolean {
|
||||||
// If the workflow specified something always respect that
|
// If the workflow specified something always respect that
|
||||||
const trapCaching = getOptionalInput("trap-caching");
|
const trapCaching = getOptionalInput("trap-caching");
|
||||||
@@ -798,11 +858,8 @@ async function recordZstdAvailability(
|
|||||||
config: configUtils.Config,
|
config: configUtils.Config,
|
||||||
zstdAvailability: ZstdAvailability,
|
zstdAvailability: ZstdAvailability,
|
||||||
) {
|
) {
|
||||||
addDiagnostic(
|
addNoLanguageDiagnostic(
|
||||||
config,
|
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(
|
makeTelemetryDiagnostic(
|
||||||
"codeql-action/zstd-availability",
|
"codeql-action/zstd-availability",
|
||||||
"Zstandard availability",
|
"Zstandard availability",
|
||||||
|
|||||||
@@ -2,29 +2,22 @@ import { ChildProcess, spawn } from "child_process";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import * as toolcache from "@actions/tool-cache";
|
|
||||||
import { pki } from "node-forge";
|
import { pki } from "node-forge";
|
||||||
|
|
||||||
import * as actionsUtil from "./actions-util";
|
import * as actionsUtil from "./actions-util";
|
||||||
import { getApiDetails, getAuthorizationHeaderFor } from "./api-client";
|
|
||||||
import { Config } from "./config-utils";
|
|
||||||
import { KnownLanguage } from "./languages";
|
import { KnownLanguage } from "./languages";
|
||||||
import { getActionsLogger, Logger } from "./logging";
|
import { getActionsLogger, Logger } from "./logging";
|
||||||
import {
|
import {
|
||||||
Credential,
|
Credential,
|
||||||
|
credentialToStr,
|
||||||
getCredentials,
|
getCredentials,
|
||||||
getDownloadUrl,
|
getProxyBinaryPath,
|
||||||
|
getSafeErrorMessage,
|
||||||
parseLanguage,
|
parseLanguage,
|
||||||
UPDATEJOB_PROXY,
|
sendFailedStatusReport,
|
||||||
|
sendSuccessStatusReport,
|
||||||
} from "./start-proxy";
|
} from "./start-proxy";
|
||||||
import {
|
import { ActionName, sendUnhandledErrorStatusReport } from "./status-report";
|
||||||
ActionName,
|
|
||||||
createStatusReportBase,
|
|
||||||
getActionsStatus,
|
|
||||||
sendStatusReport,
|
|
||||||
sendUnhandledErrorStatusReport,
|
|
||||||
StatusReportBase,
|
|
||||||
} from "./status-report";
|
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
const KEY_SIZE = 2048;
|
const KEY_SIZE = 2048;
|
||||||
@@ -94,35 +87,6 @@ function generateCertificateAuthority(): CertificateAuthority {
|
|||||||
return { cert: pem, key };
|
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,
|
|
||||||
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) {
|
async function run(startedAt: Date) {
|
||||||
// To capture errors appropriately, keep as much code within the try-catch as
|
// To capture errors appropriately, keep as much code within the try-catch as
|
||||||
// possible, and only use safe functions outside.
|
// possible, and only use safe functions outside.
|
||||||
@@ -181,25 +145,7 @@ async function run(startedAt: Date) {
|
|||||||
logger,
|
logger,
|
||||||
);
|
);
|
||||||
} catch (unwrappedError) {
|
} catch (unwrappedError) {
|
||||||
const error = util.wrapError(unwrappedError);
|
await sendFailedStatusReport(logger, startedAt, language, 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],
|
|
||||||
},
|
|
||||||
await util.checkDiskUsage(logger),
|
|
||||||
logger,
|
|
||||||
"Error from start-proxy Action omitted",
|
|
||||||
);
|
|
||||||
if (errorStatusReportBase !== undefined) {
|
|
||||||
await sendStatusReport(errorStatusReportBase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +160,7 @@ async function runWrapper() {
|
|||||||
await sendUnhandledErrorStatusReport(
|
await sendUnhandledErrorStatusReport(
|
||||||
ActionName.StartProxy,
|
ActionName.StartProxy,
|
||||||
startedAt,
|
startedAt,
|
||||||
new Error("Error from start-proxy Action omitted"),
|
getSafeErrorMessage(util.wrapError(error)),
|
||||||
logger,
|
logger,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -277,42 +223,4 @@ async function startProxy(
|
|||||||
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
|
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();
|
void runWrapper();
|
||||||
|
|||||||
@@ -1,21 +1,110 @@
|
|||||||
import test from "ava";
|
import * as filepath from "path";
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import * as toolcache from "@actions/tool-cache";
|
||||||
|
import test, { ExecutionContext } from "ava";
|
||||||
import sinon from "sinon";
|
import sinon from "sinon";
|
||||||
|
|
||||||
import * as apiClient from "./api-client";
|
import * as apiClient from "./api-client";
|
||||||
import * as defaults from "./defaults.json";
|
import * as defaults from "./defaults.json";
|
||||||
import { KnownLanguage } from "./languages";
|
import { KnownLanguage } from "./languages";
|
||||||
import { getRunnerLogger } from "./logging";
|
import { getRunnerLogger, Logger } from "./logging";
|
||||||
import * as startProxyExports from "./start-proxy";
|
import * as startProxyExports from "./start-proxy";
|
||||||
import { parseLanguage } from "./start-proxy";
|
import { parseLanguage } from "./start-proxy";
|
||||||
|
import * as statusReport from "./status-report";
|
||||||
import {
|
import {
|
||||||
checkExpectedLogMessages,
|
checkExpectedLogMessages,
|
||||||
getRecordingLogger,
|
getRecordingLogger,
|
||||||
makeTestToken,
|
makeTestToken,
|
||||||
setupTests,
|
setupTests,
|
||||||
|
withRecordingLoggerAsync,
|
||||||
} from "./testing-utils";
|
} from "./testing-utils";
|
||||||
|
import { ConfigurationError } from "./util";
|
||||||
|
|
||||||
setupTests(test);
|
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) =>
|
const toEncodedJSON = (data: any) =>
|
||||||
Buffer.from(JSON.stringify(data)).toString("base64");
|
Buffer.from(JSON.stringify(data)).toString("base64");
|
||||||
|
|
||||||
@@ -301,3 +390,209 @@ test("getDownloadUrl returns matching release asset", async (t) => {
|
|||||||
t.is(info.version, defaults.cliVersion);
|
t.is(info.version, defaults.cliVersion);
|
||||||
t.is(info.url, "url-we-want");
|
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,12 +1,161 @@
|
|||||||
import * as core from "@actions/core";
|
import * as path from "path";
|
||||||
|
|
||||||
import { getApiClient } from "./api-client";
|
import * as core from "@actions/core";
|
||||||
|
import * as toolcache from "@actions/tool-cache";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getApiClient,
|
||||||
|
getApiDetails,
|
||||||
|
getAuthorizationHeaderFor,
|
||||||
|
} from "./api-client";
|
||||||
import * as artifactScanner from "./artifact-scanner";
|
import * as artifactScanner from "./artifact-scanner";
|
||||||
|
import { Config } from "./config-utils";
|
||||||
import * as defaults from "./defaults.json";
|
import * as defaults from "./defaults.json";
|
||||||
import { KnownLanguage } from "./languages";
|
import { KnownLanguage } from "./languages";
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
|
import {
|
||||||
|
ActionName,
|
||||||
|
createStatusReportBase,
|
||||||
|
getActionsStatus,
|
||||||
|
sendStatusReport,
|
||||||
|
StatusReportBase,
|
||||||
|
} from "./status-report";
|
||||||
|
import * as util from "./util";
|
||||||
import { ConfigurationError, getErrorMessage, isDefined } 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 = "update-job-proxy";
|
||||||
export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
|
export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
|
||||||
const UPDATEJOB_PROXY_URL_PREFIX =
|
const UPDATEJOB_PROXY_URL_PREFIX =
|
||||||
@@ -277,3 +426,125 @@ export async function getDownloadUrl(
|
|||||||
version: UPDATEJOB_PROXY_VERSION,
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -208,6 +208,23 @@ 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. */
|
/** Mock the HTTP request to the feature flags enablement API endpoint. */
|
||||||
export function mockFeatureFlagApiEndpoint(
|
export function mockFeatureFlagApiEndpoint(
|
||||||
responseStatusCode: number,
|
responseStatusCode: number,
|
||||||
|
|||||||
@@ -563,3 +563,28 @@ test("joinAtMost - truncates list if array is > than limit", (t) => {
|
|||||||
t.assert(result.includes("test5"));
|
t.assert(result.includes("test5"));
|
||||||
t.false(result.includes("test6"));
|
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
src/util.ts
40
src/util.ts
@@ -1292,3 +1292,43 @@ export function joinAtMost(
|
|||||||
|
|
||||||
return array.join(separator);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user