mirror of
https://github.com/github/codeql-action.git
synced 2026-08-05 21:06:13 -05:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bdf39710a2 | |||
| 74cfae9be6 | |||
| 6a90bf1f54 | |||
| c5995f544d | |||
| 76c44396d3 | |||
| fad141fa6c | |||
| 7d82f1132f | |||
| 37bdbde050 | |||
| 5595ccaf91 | |||
| ec9c75796a | |||
| 45c8742e17 | |||
| 6a9359a1bd | |||
| 065cdc0394 | |||
| f99dd5aeee | |||
| 1804b211a3 | |||
| 3020a2f462 | |||
| 93c3a5a40b | |||
| d1ba80a13d | |||
| e74600b0d9 | |||
| 266c7bdbd2 | |||
| daa7fe6fba | |||
| 1cd4d01d58 | |||
| d2bfc30bc3 | |||
| 68028fcb16 | |||
| c29563eeaa | |||
| 155e522997 | |||
| 2d3b351ea6 | |||
| 5d3eb98e4a | |||
| c5f739bd64 | |||
| 2e53f1cea2 | |||
| d0ee43ac62 | |||
| 06f1d4ffed | |||
| 82f035a501 | |||
| 60a57910be | |||
| 3502f79575 | |||
| 8f0a4f23c4 | |||
| 780c00da3f | |||
| f02afd42b0 | |||
| d57c3ffcba | |||
| 8e6fdffc32 | |||
| ba46ff760e | |||
| b411bbcd4a | |||
| 0cebd1d28d | |||
| d2f5cbbe91 | |||
| 36737508ec | |||
| 30c33c9286 | |||
| 3ca82bb259 | |||
| 42a3b94790 | |||
| 13d4882649 | |||
| e55a57b808 | |||
| 51d51e8121 | |||
| e893985e8b | |||
| eb692f8b49 | |||
| aac07d2a41 | |||
| de57c4a441 | |||
| 2e251072b0 | |||
| 94a12eb6f6 | |||
| e28cbacfa1 | |||
| 60834a0cd9 | |||
| e9831f72a2 | |||
| 766928d055 | |||
| 049af32c59 | |||
| c7ae51bb2d | |||
| 1f9caf0118 |
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
with:
|
||||
java-version: ${{ inputs.java-version || '17' }}
|
||||
distribution: temurin
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
with:
|
||||
java-version: ${{ inputs.java-version || '17' }}
|
||||
distribution: temurin
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ jobs:
|
||||
run: |
|
||||
cd "$RUNNER_TEMP/results"
|
||||
actual=$(jq -r '.runs[0].properties.jobRunUuid' javascript.sarif)
|
||||
if [[ "$actual" != "$JOB_RUN_UUID" ]]; then
|
||||
echo "Expected SARIF output to contain job run UUID '$JOB_RUN_UUID', but found '$actual'."
|
||||
if [[ "$actual" != "$CODEQL_ACTION_JOB_RUN_UUID" ]]; then
|
||||
echo "Expected SARIF output to contain job run UUID '$CODEQL_ACTION_JOB_RUN_UUID', but found '$actual'."
|
||||
exit 1
|
||||
else
|
||||
echo "Found job run UUID '$actual'."
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ jobs:
|
||||
use-all-platform-bundle: 'false'
|
||||
setup-kotlin: 'true'
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1.319.0
|
||||
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
repository: github/enterprise-releases
|
||||
token: ${{ secrets.ENTERPRISE_RELEASE_TOKEN }}
|
||||
token: ${{ secrets.CODEQL_CI_ENTERPRISE_RELEASE_PAT }}
|
||||
path: ${{ github.workspace }}/enterprise-releases/
|
||||
sparse-checkout: releases.json
|
||||
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.37.6 - 04 Aug 2026
|
||||
|
||||
- Changed the default filepath for the new remote file address format that was introduced in CodeQL Action 4.37.0 / 3.37.0 to `.github/codeql-config.yml` to align it with the suggested path that is used elsewhere. [#4070](https://github.com/github/codeql-action/pull/4070)
|
||||
|
||||
## 4.37.5 - 03 Aug 2026
|
||||
|
||||
- Fixed a bug where a network error while streaming the download of the CodeQL bundle could terminate the `init` Action instead of falling back to downloading the bundle before extracting it. [#4061](https://github.com/github/codeql-action/pull/4061)
|
||||
|
||||
## 4.37.4 - 29 Jul 2026
|
||||
|
||||
- This version of the CodeQL Action adds support for the `tools` input for the `codeql-action/init` step to be specified using a `github-codeql-tools` [repository property](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). This feature will gradually be rolled out following the release of this version. Once rolled out, this allows for the CodeQL CLI version that is used in GitHub-managed workflows, such as Default Setup, to be set to a custom value. For example, customers who run into issues with rate limits when a new CodeQL CLI version is released can set the value to `toolcache` to always use the CodeQL CLI version that is available in the runner toolcache. For Advanced Setup workflows, the value provided for `tools` in the workflow definition always takes precedence unless the value of the repository property starts with `!`. [#4037](https://github.com/github/codeql-action/pull/4037)
|
||||
|
||||
Generated
+903
-614
File diff suppressed because it is too large
Load Diff
Generated
+98
-98
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.37.4",
|
||||
"version": "4.37.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codeql",
|
||||
"version": "4.37.4",
|
||||
"version": "4.37.7",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"pr-checks"
|
||||
@@ -31,7 +31,7 @@
|
||||
"follow-redirects": "^1.16.0",
|
||||
"get-folder-size": "^5.0.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"js-yaml": "^5.2.1",
|
||||
"js-yaml": "^5.2.2",
|
||||
"jsonschema": "1.5.0",
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.4.0",
|
||||
@@ -63,9 +63,9 @@
|
||||
"glob": "^13.0.6",
|
||||
"globals": "^17.7.0",
|
||||
"nock": "^14.0.16",
|
||||
"sinon": "^22.0.0",
|
||||
"sinon": "^22.1.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.64.0"
|
||||
"typescript-eslint": "^8.65.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -374,9 +374,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@actions/artifact/node_modules/brace-expansion": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
|
||||
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
@@ -2591,17 +2591,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz",
|
||||
"integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz",
|
||||
"integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.64.0",
|
||||
"@typescript-eslint/type-utils": "8.64.0",
|
||||
"@typescript-eslint/utils": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0",
|
||||
"@typescript-eslint/scope-manager": "8.65.0",
|
||||
"@typescript-eslint/type-utils": "8.65.0",
|
||||
"@typescript-eslint/utils": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -2614,7 +2614,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.64.0",
|
||||
"@typescript-eslint/parser": "^8.65.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
@@ -2630,16 +2630,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz",
|
||||
"integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz",
|
||||
"integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.64.0",
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0",
|
||||
"@typescript-eslint/scope-manager": "8.65.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2673,14 +2673,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz",
|
||||
"integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
|
||||
"integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.64.0",
|
||||
"@typescript-eslint/types": "^8.64.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.65.0",
|
||||
"@typescript-eslint/types": "^8.65.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2713,14 +2713,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz",
|
||||
"integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
|
||||
"integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0"
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2731,9 +2731,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz",
|
||||
"integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
|
||||
"integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2748,15 +2748,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz",
|
||||
"integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz",
|
||||
"integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0",
|
||||
"@typescript-eslint/utils": "8.64.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0",
|
||||
"@typescript-eslint/utils": "8.65.0",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -2791,9 +2791,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz",
|
||||
"integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
|
||||
"integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2805,16 +2805,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz",
|
||||
"integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
|
||||
"integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.64.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.64.0",
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0",
|
||||
"@typescript-eslint/project-service": "8.65.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.65.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/visitor-keys": "8.65.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -2843,16 +2843,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/debug": {
|
||||
@@ -2874,13 +2874,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"version": "10.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
|
||||
"integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
"brace-expansion": "^5.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
@@ -2890,16 +2890,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz",
|
||||
"integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz",
|
||||
"integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.64.0",
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0"
|
||||
"@typescript-eslint/scope-manager": "8.65.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2914,13 +2914,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz",
|
||||
"integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
|
||||
"integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/types": "8.65.0",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3864,9 +3864,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -5115,16 +5115,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-import-x/node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-import-x/node_modules/minimatch": {
|
||||
@@ -6111,15 +6111,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
@@ -6981,9 +6981,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz",
|
||||
"integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.2.tgz",
|
||||
"integrity": "sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -8090,15 +8090,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/readdir-glob/node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/readdir-glob/node_modules/minimatch": {
|
||||
@@ -8556,9 +8556,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sinon": {
|
||||
"version": "22.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz",
|
||||
"integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==",
|
||||
"version": "22.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-22.1.0.tgz",
|
||||
"integrity": "sha512-n1ajF2rBWMTtEwbKcw4UdFg4nCnDdq/U6RDoxtOd7oapOlRoJ5ynwFx60owROyhDpA9QhMZi0pCO/xtmwFjG7w==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -9320,16 +9320,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz",
|
||||
"integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==",
|
||||
"version": "8.65.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz",
|
||||
"integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.64.0",
|
||||
"@typescript-eslint/parser": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0",
|
||||
"@typescript-eslint/utils": "8.64.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.65.0",
|
||||
"@typescript-eslint/parser": "8.65.0",
|
||||
"@typescript-eslint/typescript-estree": "8.65.0",
|
||||
"@typescript-eslint/utils": "8.65.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.37.4",
|
||||
"version": "4.37.7",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
@@ -39,7 +39,7 @@
|
||||
"follow-redirects": "^1.16.0",
|
||||
"get-folder-size": "^5.0.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"js-yaml": "^5.2.1",
|
||||
"js-yaml": "^5.2.2",
|
||||
"jsonschema": "1.5.0",
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.4.0",
|
||||
@@ -71,9 +71,9 @@
|
||||
"glob": "^13.0.6",
|
||||
"globals": "^17.7.0",
|
||||
"nock": "^14.0.16",
|
||||
"sinon": "^22.0.0",
|
||||
"sinon": "^22.1.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.64.0"
|
||||
"typescript-eslint": "^8.65.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@actions/tool-cache": {
|
||||
|
||||
@@ -21,8 +21,8 @@ steps:
|
||||
run: |
|
||||
cd "$RUNNER_TEMP/results"
|
||||
actual=$(jq -r '.runs[0].properties.jobRunUuid' javascript.sarif)
|
||||
if [[ "$actual" != "$JOB_RUN_UUID" ]]; then
|
||||
echo "Expected SARIF output to contain job run UUID '$JOB_RUN_UUID', but found '$actual'."
|
||||
if [[ "$actual" != "$CODEQL_ACTION_JOB_RUN_UUID" ]]; then
|
||||
echo "Expected SARIF output to contain job run UUID '$CODEQL_ACTION_JOB_RUN_UUID', but found '$actual'."
|
||||
exit 1
|
||||
else
|
||||
echo "Found job run UUID '$actual'."
|
||||
|
||||
@@ -5,7 +5,7 @@ versions:
|
||||
- default
|
||||
steps:
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1.319.0
|
||||
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
+2
-2
@@ -253,8 +253,8 @@ const languageSetups: LanguageSetups = {
|
||||
name: "Install Java",
|
||||
uses: pinnedUses(
|
||||
"actions/setup-java",
|
||||
"03ad4de0992f5dab5e18fcb136590ce7c4a0ac95",
|
||||
"v5.6.0",
|
||||
"b6effb05e454b25005698d916606bdc6ffcbf961",
|
||||
"v5.7.0",
|
||||
),
|
||||
with: {
|
||||
"java-version": `\${{ inputs.java-version || '${defaultLanguageVersions.java}' }}`,
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import * as core from "@actions/core";
|
||||
import test from "ava";
|
||||
import sinon from "sinon";
|
||||
|
||||
import * as common from "./action-common";
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as environment from "./environment";
|
||||
import * as logging from "./logging";
|
||||
import { ActionName } from "./status-report";
|
||||
import * as statusReport from "./status-report";
|
||||
import {
|
||||
getTestActionsEnv,
|
||||
getTestEnv,
|
||||
makeMacro,
|
||||
RecordingLogger,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import { getErrorMessage } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
interface RunInActionsTestOpts {
|
||||
runFn?: () => Promise<any>;
|
||||
expectedErrorMessage?: string;
|
||||
expectedTelemetryError?: string;
|
||||
}
|
||||
|
||||
const runInActionsMacro = makeMacro({
|
||||
exec: async (t, opts: RunInActionsTestOpts) => {
|
||||
const expectFailure = opts?.expectedErrorMessage !== undefined;
|
||||
|
||||
const logger = new RecordingLogger();
|
||||
const getActionsLogger = sinon
|
||||
.stub(logging, "getActionsLogger")
|
||||
.returns(logger);
|
||||
|
||||
const env = getTestEnv();
|
||||
const getEnv = sinon.stub(environment, "getEnv").returns(env);
|
||||
|
||||
const actionsEnv = getTestActionsEnv(env);
|
||||
const getActionsEnv = sinon
|
||||
.stub(actionsUtil, "getActionsEnv")
|
||||
.returns(actionsEnv);
|
||||
|
||||
const getJobUUID = sinon
|
||||
.stub(statusReport, "getJobUUID")
|
||||
.returns("test-job-uuid");
|
||||
|
||||
const setFailed = sinon.stub(core, "setFailed");
|
||||
const sendUnhandledErrorStatusReport = sinon.stub(
|
||||
statusReport,
|
||||
"sendUnhandledErrorStatusReport",
|
||||
);
|
||||
|
||||
const name = ActionName.Init;
|
||||
const run = sinon.stub();
|
||||
|
||||
if (opts?.runFn) {
|
||||
run.callsFake(opts.runFn);
|
||||
}
|
||||
|
||||
const transformTelemetryError = sinon
|
||||
.stub()
|
||||
.callsFake((err) => opts?.expectedTelemetryError ?? getErrorMessage(err));
|
||||
const testAction: common.Action = {
|
||||
name,
|
||||
run,
|
||||
transformTelemetryError,
|
||||
};
|
||||
|
||||
await common.runInActions(testAction);
|
||||
|
||||
// These always should have been called once.
|
||||
t.true(getActionsLogger.calledOnce);
|
||||
t.true(getEnv.calledOnce);
|
||||
t.true(getActionsEnv.calledOnce);
|
||||
|
||||
const expectedActionState = {
|
||||
actions: actionsEnv,
|
||||
env,
|
||||
logger,
|
||||
name: ActionName.Init,
|
||||
};
|
||||
|
||||
t.true(getJobUUID.calledOnceWithExactly(sinon.match(expectedActionState)));
|
||||
t.true(run.calledOnceWithExactly(sinon.match(expectedActionState)));
|
||||
|
||||
t.is(setFailed.calledOnce, expectFailure ?? false);
|
||||
t.is(sendUnhandledErrorStatusReport.calledOnce, expectFailure ?? false);
|
||||
|
||||
if (expectFailure) {
|
||||
t.true(
|
||||
setFailed.calledOnceWithExactly(
|
||||
`${statusReport.getDisplayActionName(name)} action failed: ${opts?.expectedErrorMessage}`,
|
||||
),
|
||||
);
|
||||
t.true(
|
||||
sendUnhandledErrorStatusReport.calledOnceWithExactly(
|
||||
name,
|
||||
sinon.match.any,
|
||||
opts?.expectedTelemetryError ?? opts?.expectedErrorMessage,
|
||||
logger,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
title: (providedTitle) => `runInActions - ${providedTitle}`,
|
||||
});
|
||||
|
||||
runInActionsMacro.serial("calls run", {});
|
||||
runInActionsMacro.serial("handles run exceptions", {
|
||||
runFn: () => {
|
||||
throw new Error("Test failure");
|
||||
},
|
||||
expectedErrorMessage: "Test failure",
|
||||
});
|
||||
runInActionsMacro.serial("transforms run exceptions", {
|
||||
runFn: () => {
|
||||
throw new Error("Test failure");
|
||||
},
|
||||
expectedErrorMessage: "Test failure",
|
||||
expectedTelemetryError: "Transformed failure message",
|
||||
});
|
||||
+26
-4
@@ -8,9 +8,10 @@ import { getActionsLogger, Logger } from "./logging";
|
||||
import {
|
||||
ActionName,
|
||||
getDisplayActionName,
|
||||
getJobUUID,
|
||||
sendUnhandledErrorStatusReport,
|
||||
} from "./status-report";
|
||||
import { getEnv, getErrorMessage } from "./util";
|
||||
import { getEnv, getErrorMessage, wrapError } from "./util";
|
||||
|
||||
/** Base state that is available to an Action on startup. */
|
||||
export interface BaseState {
|
||||
@@ -78,6 +79,12 @@ export interface Action {
|
||||
name: ActionName;
|
||||
/** The entry point for the Action. */
|
||||
run: ActionMain;
|
||||
/**
|
||||
* An optional function that transforms a caught error into a message suitable for
|
||||
* inclusion in a status report. This is primarily intended for the `start-proxy`
|
||||
* action to replace the thrown `Error`'s message with a safe one.
|
||||
*/
|
||||
transformTelemetryError?: (error: Error) => string;
|
||||
}
|
||||
|
||||
/** A generic entry point that sets up the basic environment for the `action` and runs it. */
|
||||
@@ -88,17 +95,32 @@ export async function runInActions(action: Action) {
|
||||
const actionsEnv = getActionsEnv();
|
||||
|
||||
try {
|
||||
await action.run({
|
||||
const actionState = {
|
||||
name: action.name,
|
||||
startedAt,
|
||||
logger,
|
||||
env,
|
||||
actions: actionsEnv,
|
||||
});
|
||||
};
|
||||
|
||||
// Create a unique identifier for this run.
|
||||
getJobUUID(actionState);
|
||||
|
||||
await action.run(actionState);
|
||||
} catch (error) {
|
||||
core.setFailed(
|
||||
`${getDisplayActionName(action.name)} action failed: ${getErrorMessage(error)}`,
|
||||
);
|
||||
await sendUnhandledErrorStatusReport(action.name, startedAt, error, logger);
|
||||
|
||||
const statusReportError =
|
||||
action.transformTelemetryError !== undefined
|
||||
? action.transformTelemetryError(wrapError(error))
|
||||
: error;
|
||||
await sendUnhandledErrorStatusReport(
|
||||
action.name,
|
||||
startedAt,
|
||||
statusReportError,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -27,14 +27,20 @@ declare const __CODEQL_ACTION_VERSION__: string;
|
||||
* global functions in tests.
|
||||
*/
|
||||
export interface ActionsEnv {
|
||||
getRequiredInput: (name: string) => string;
|
||||
getOptionalInput: (name: string) => string | undefined;
|
||||
exportVariable: (name: string, value: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real `ActionsEnv` used by production code.
|
||||
*/
|
||||
export function getActionsEnv(): ActionsEnv {
|
||||
return { getOptionalInput };
|
||||
return {
|
||||
getRequiredInput,
|
||||
getOptionalInput,
|
||||
exportVariable: core.exportVariable,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from "ava";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { getActionsEnv } from "../actions-util";
|
||||
import { ActionsEnv } from "../actions-util";
|
||||
import { Feature } from "../feature-flags";
|
||||
import { RepositoryPropertyName } from "../feature-flags/properties";
|
||||
import { callee } from "../testing-utils";
|
||||
@@ -22,32 +22,26 @@ const expectedRepositoryPropertyResult: ComputedInput = {
|
||||
value: "repo-property-input-value",
|
||||
};
|
||||
|
||||
function stubGetToolsInput() {
|
||||
const actions = getActionsEnv();
|
||||
function stubGetToolsInput(actions: ActionsEnv) {
|
||||
sinon
|
||||
.stub(actions, "getOptionalInput")
|
||||
.withArgs(InputName.Tools)
|
||||
.returns(expectedWorkflowResult.value);
|
||||
return actions;
|
||||
}
|
||||
|
||||
const workflowLogMessage = `Using ${InputName.Tools} input from workflow:`;
|
||||
|
||||
test("getToolsInput - returns workflow input if available", async (t) => {
|
||||
const actions = stubGetToolsInput();
|
||||
|
||||
await callee(getToolsInput)
|
||||
.withActions(actions)
|
||||
.withActions(stubGetToolsInput)
|
||||
.withArgs({})
|
||||
.logs(t, workflowLogMessage)
|
||||
.passes(t.deepEqual, expectedWorkflowResult);
|
||||
});
|
||||
|
||||
test("getToolsInput - returns repository property value if enforced", async (t) => {
|
||||
const actions = stubGetToolsInput();
|
||||
|
||||
const target = callee(getToolsInput)
|
||||
.withActions(actions)
|
||||
.withActions(stubGetToolsInput)
|
||||
.withArgs({
|
||||
[RepositoryPropertyName.TOOLS]: `!${expectedRepositoryPropertyResult.value}`,
|
||||
});
|
||||
@@ -65,10 +59,8 @@ test("getToolsInput - returns repository property value if enforced", async (t)
|
||||
});
|
||||
|
||||
test("getToolsInput - prefers workflow input", async (t) => {
|
||||
const actions = stubGetToolsInput();
|
||||
|
||||
const target = callee(getToolsInput)
|
||||
.withActions(actions)
|
||||
.withActions(stubGetToolsInput)
|
||||
.withArgs({
|
||||
[RepositoryPropertyName.TOOLS]: expectedRepositoryPropertyResult.value,
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface RemoteFileAddress {
|
||||
}
|
||||
|
||||
/** The default file path to use in configuration file shorthands. */
|
||||
export const DEFAULT_CONFIG_FILE_NAME = ".github/codeql-action.yaml";
|
||||
export const DEFAULT_CONFIG_FILE_NAME = ".github/codeql-config.yml";
|
||||
|
||||
/** The default ref to use in configuration file shorthands. */
|
||||
export const DEFAULT_CONFIG_FILE_REF = "main";
|
||||
|
||||
+6
-1
@@ -88,7 +88,7 @@ export enum EnvVar {
|
||||
LOG_VERSION_DEPRECATION = "CODEQL_ACTION_DID_LOG_VERSION_DEPRECATION",
|
||||
|
||||
/** UUID representing the current job run. */
|
||||
JOB_RUN_UUID = "JOB_RUN_UUID",
|
||||
JOB_RUN_UUID = "CODEQL_ACTION_JOB_RUN_UUID",
|
||||
|
||||
/** Status for the entire job, submitted to the status report in `init-post` */
|
||||
JOB_STATUS = "CODEQL_ACTION_JOB_STATUS",
|
||||
@@ -270,6 +270,11 @@ export class ReadOnlyEnv<T extends string | undefined = string | undefined> {
|
||||
return Object.create(this, { vars: { value: { ...this.vars } } }) as this;
|
||||
}
|
||||
|
||||
/** Gets a copy of the underlying environment. */
|
||||
public get(): Record<string, T> {
|
||||
return { ...this.vars };
|
||||
}
|
||||
|
||||
/** Tries to get the value for `name` and throws if there isn't one. */
|
||||
public getRequired(name: string): string {
|
||||
return getRequiredEnvVar(this.vars, name);
|
||||
|
||||
@@ -4,7 +4,6 @@ import * as path from "path";
|
||||
import * as core from "@actions/core";
|
||||
import * as io from "@actions/io";
|
||||
import * as semver from "semver";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import { Action, ActionState, runInActions } from "./action-common";
|
||||
import {
|
||||
@@ -255,11 +254,6 @@ async function run(
|
||||
);
|
||||
const repositoryProperties = repositoryPropertiesResult.orElse({});
|
||||
|
||||
// Create a unique identifier for this run.
|
||||
const jobRunUuid = uuidV4();
|
||||
logger.info(`Job run UUID is ${jobRunUuid}.`);
|
||||
core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
|
||||
|
||||
core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true");
|
||||
|
||||
// path.resolve() respects the intended semantics of source-root. If
|
||||
|
||||
+27
-5
@@ -35,6 +35,11 @@ export function isNumber(value: unknown): value is number {
|
||||
return typeof value === "number";
|
||||
}
|
||||
|
||||
/** Asserts that `value` is a boolean. */
|
||||
export function isBoolean(value: unknown): value is boolean {
|
||||
return typeof value === "boolean";
|
||||
}
|
||||
|
||||
/** Asserts that `value` is either a string or undefined. */
|
||||
export function isStringOrUndefined(
|
||||
value: unknown,
|
||||
@@ -62,14 +67,11 @@ function defaultCheck(
|
||||
return (arg) => ({ unknownKeys: [], invalidKeys: [], valid: validate(arg) });
|
||||
}
|
||||
|
||||
function makeValidator<T>(
|
||||
validate: (arg: unknown) => arg is T,
|
||||
required: boolean = true,
|
||||
) {
|
||||
function makeValidator<T>(validate: (arg: unknown) => arg is T) {
|
||||
return {
|
||||
validate,
|
||||
check: defaultCheck(validate),
|
||||
required,
|
||||
required: true,
|
||||
} as const satisfies Validator<T>;
|
||||
}
|
||||
|
||||
@@ -82,6 +84,9 @@ export const string = makeValidator(isString);
|
||||
/** A validator for number fields in schemas. */
|
||||
export const number = makeValidator(isNumber);
|
||||
|
||||
/** A validator for boolean fields in schemas. */
|
||||
export const boolean = makeValidator(isBoolean);
|
||||
|
||||
/** A validator for arrays. */
|
||||
export function array<T>(validator: Validator<T>) {
|
||||
const validate = (val: unknown) => {
|
||||
@@ -221,6 +226,23 @@ export function validateSchema<
|
||||
return result.valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that `arr` is an array whose elements satisfy at least `elementSchema`.
|
||||
* Additional keys are accepted in each element.
|
||||
*
|
||||
* @param elementSchema The schema to validate the elements against.
|
||||
* @param arr The array to validate.
|
||||
* @returns Asserts that `arr` has elements of `schema`'s type if validation is successful.
|
||||
*/
|
||||
export function validateArray<
|
||||
S extends Schema,
|
||||
T extends UnvalidatedArray = Array<FromSchema<S>>,
|
||||
>(elementSchema: S, arr: UnvalidatedArray): arr is T {
|
||||
const elementValidator = object(elementSchema);
|
||||
|
||||
return array(elementValidator).validate(arr);
|
||||
}
|
||||
|
||||
export interface CheckSchemaOptions {
|
||||
/** Whether to stop validation after the first error. */
|
||||
failFast?: boolean;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as core from "@actions/core";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import { Action, ActionState, runInActions } from "./action-common";
|
||||
import {
|
||||
@@ -95,7 +94,7 @@ async function sendCompletedStatusReport(
|
||||
|
||||
/** The main behaviour of this action. */
|
||||
async function run(
|
||||
actionState: ActionState<["Base", "Logger", "Actions"]>,
|
||||
actionState: ActionState<["Base", "Logger", "Env", "Actions"]>,
|
||||
): Promise<void> {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
@@ -140,10 +139,6 @@ async function run(
|
||||
|
||||
const actionStateWithFeatures = { ...actionState, features };
|
||||
|
||||
const jobRunUuid = uuidV4();
|
||||
logger.info(`Job run UUID is ${jobRunUuid}.`);
|
||||
core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.SetupCodeQL,
|
||||
"starting",
|
||||
|
||||
+14
-19
@@ -3,11 +3,12 @@ import * as path from "path";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import { Action, ActionState, runInActions } from "./action-common";
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { Logger } from "./logging";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import {
|
||||
credentialToStr,
|
||||
@@ -23,14 +24,14 @@ import {
|
||||
import { generateCertificateAuthority } from "./start-proxy/ca";
|
||||
import { checkProxyEnvironment } from "./start-proxy/environment";
|
||||
import { checkConnections } from "./start-proxy/reachability";
|
||||
import { ActionName, sendUnhandledErrorStatusReport } from "./status-report";
|
||||
import { ActionName } from "./status-report";
|
||||
import * as util from "./util";
|
||||
|
||||
async function run(startedAt: Date) {
|
||||
async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
const logger = getActionsLogger();
|
||||
const startedAt = action.startedAt;
|
||||
const logger = action.logger;
|
||||
let features: FeatureEnablement | undefined;
|
||||
let language: BuiltInLanguage | undefined;
|
||||
|
||||
@@ -122,21 +123,15 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
/** Defines the `start-proxy` Action. */
|
||||
const startProxyAction: Action = {
|
||||
name: ActionName.StartProxy,
|
||||
run,
|
||||
transformTelemetryError: getSafeErrorMessage,
|
||||
};
|
||||
|
||||
try {
|
||||
await run(startedAt);
|
||||
} catch (error) {
|
||||
core.setFailed(`start-proxy action failed: ${util.getErrorMessage(error)}`);
|
||||
await sendUnhandledErrorStatusReport(
|
||||
ActionName.StartProxy,
|
||||
startedAt,
|
||||
getSafeErrorMessage(util.wrapError(error)),
|
||||
logger,
|
||||
);
|
||||
}
|
||||
export async function runWrapper() {
|
||||
await runInActions(startProxyAction);
|
||||
}
|
||||
|
||||
async function startProxy(
|
||||
|
||||
+1
-7
@@ -83,12 +83,6 @@ export class StartProxyError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
@@ -112,7 +106,7 @@ export async function sendSuccessStatusReport(
|
||||
logger,
|
||||
);
|
||||
if (statusReportBase !== undefined) {
|
||||
const statusReport: StartProxyStatus = {
|
||||
const statusReport: StatusReportBase = {
|
||||
...statusReportBase,
|
||||
registry_types: registry_types.join(","),
|
||||
};
|
||||
|
||||
@@ -254,13 +254,19 @@ export function credentialToStr(credential: Credential): string {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** A package registry is identified by its type and address. */
|
||||
export type Registry = {
|
||||
/** The schema for `RegistryBase` objects. */
|
||||
export const registryBaseSchema = {
|
||||
/** The type of the package registry. */
|
||||
type: string;
|
||||
type: json.string,
|
||||
/** Whether the registry replaces the base registry for the ecosystem. */
|
||||
"replaces-base"?: boolean;
|
||||
} & Address;
|
||||
"replaces-base": json.optional(json.boolean),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Information about a registry, other than its address. */
|
||||
export type RegistryBase = json.FromSchema<typeof registryBaseSchema>;
|
||||
|
||||
/** A package registry is identified by its type and address. */
|
||||
export type Registry = RegistryBase & Address;
|
||||
|
||||
// If a registry has an `url`, then that takes precedence over the `host` which may or may
|
||||
// not be defined.
|
||||
|
||||
+104
-1
@@ -1,17 +1,21 @@
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { EnvVar, RegistryProxyVars } from "./environment";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import type { Registry } from "./start-proxy";
|
||||
import {
|
||||
ActionName,
|
||||
createInitWithConfigStatusReport,
|
||||
createStatusReportBase,
|
||||
getActionsStatus,
|
||||
getRegistryTypesFromEnv,
|
||||
getJobUUID,
|
||||
InitStatusReport,
|
||||
InitWithConfigStatusReport,
|
||||
} from "./status-report";
|
||||
@@ -20,11 +24,106 @@ import {
|
||||
setupActionsVars,
|
||||
createTestConfig,
|
||||
makeMacro,
|
||||
getTestEnv,
|
||||
RecordingLogger,
|
||||
callee,
|
||||
} from "./testing-utils";
|
||||
import { BuildMode, ConfigurationError, withTmpDir, wrapError } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("getRegistryTypesFromEnv - gets unique registry types from environment", async (t) => {
|
||||
const logger = new RecordingLogger(true);
|
||||
const env = getTestEnv({
|
||||
[RegistryProxyVars.PROXY_URLS]: JSON.stringify([
|
||||
{ type: "git_source", url: "https://example.com" },
|
||||
{ type: "git_source", url: "https://github.com" },
|
||||
{ type: "docker_registry", url: "https://registry.example.com" },
|
||||
] satisfies Array<Partial<Registry>>),
|
||||
});
|
||||
|
||||
const result = getRegistryTypesFromEnv(logger, env);
|
||||
t.deepEqual(result, ["git_source", "docker_registry"].sort().join(","));
|
||||
});
|
||||
|
||||
test("getRegistryTypesFromEnv - returns undefined if the env var is not set", async (t) => {
|
||||
const logger = new RecordingLogger(true);
|
||||
const env = getTestEnv({});
|
||||
|
||||
const result = getRegistryTypesFromEnv(logger, env);
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test("getRegistryTypesFromEnv - returns undefined if the env var is not valid JSON", async (t) => {
|
||||
const logger = new RecordingLogger(true);
|
||||
const env = getTestEnv({ [RegistryProxyVars.PROXY_URLS]: "[" });
|
||||
|
||||
const result = getRegistryTypesFromEnv(logger, env);
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test("getRegistryTypesFromEnv - returns undefined if the env var is unexpected JSON", async (t) => {
|
||||
const logger = new RecordingLogger(true);
|
||||
|
||||
t.is(
|
||||
getRegistryTypesFromEnv(
|
||||
logger,
|
||||
getTestEnv({
|
||||
// Top-level object rather than an array of objects.
|
||||
[RegistryProxyVars.PROXY_URLS]: JSON.stringify({ type: "git_source" }),
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
);
|
||||
t.is(
|
||||
getRegistryTypesFromEnv(
|
||||
logger,
|
||||
getTestEnv({
|
||||
// Object has no "type" key.
|
||||
[RegistryProxyVars.PROXY_URLS]: JSON.stringify([{}]),
|
||||
}),
|
||||
),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test("getJobUUID - generates valid UUIDs", async (t) => {
|
||||
await callee(getJobUUID)
|
||||
.withArgs()
|
||||
.logs(t, "Job run UUID is ")
|
||||
.hasEnv(t, (val) => {
|
||||
return {
|
||||
[EnvVar.JOB_RUN_UUID]: val,
|
||||
};
|
||||
})
|
||||
.passes((val) => {
|
||||
t.true(uuid.validate(val));
|
||||
});
|
||||
});
|
||||
|
||||
test("getJobUUID - retrieves existing job UUIDs", async (t) => {
|
||||
const existingJobUuid = uuid.v4();
|
||||
await callee(getJobUUID)
|
||||
.withArgs()
|
||||
.withEnv((env) => {
|
||||
env.set(EnvVar.JOB_RUN_UUID, existingJobUuid);
|
||||
})
|
||||
.logs(t, `Existing job run UUID is ${existingJobUuid}.`)
|
||||
.passes(t.deepEqual, existingJobUuid);
|
||||
});
|
||||
|
||||
test("getJobUUID - doesn't retrieve invalid UUIDs", async (t) => {
|
||||
const existingJobUuid = "not-a-uuid";
|
||||
await callee(getJobUUID)
|
||||
.withArgs()
|
||||
.withEnv((env) => {
|
||||
env.set(EnvVar.JOB_RUN_UUID, existingJobUuid);
|
||||
})
|
||||
.logs(t, `Job run UUID is `)
|
||||
.notLogs(t, `Existing job run UUID is ${existingJobUuid}.`)
|
||||
.passes(t.notDeepEqual, existingJobUuid);
|
||||
});
|
||||
|
||||
function setupEnvironmentAndStub(tmpDir: string) {
|
||||
setupActionsVars(tmpDir, tmpDir, {
|
||||
GITHUB_EVENT_NAME: "dynamic",
|
||||
@@ -34,6 +133,9 @@ function setupEnvironmentAndStub(tmpDir: string) {
|
||||
|
||||
process.env[EnvVar.ANALYSIS_KEY] = "analysis-key";
|
||||
process.env["ImageVersion"] = "2023.05.19.1";
|
||||
process.env[RegistryProxyVars.PROXY_URLS] = JSON.stringify([
|
||||
{ type: "maven_repository" },
|
||||
] satisfies Array<Partial<Registry>>);
|
||||
|
||||
const getRequiredInput = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
getRequiredInput.withArgs("matrix").resolves("input/matrix");
|
||||
@@ -77,6 +179,7 @@ test.serial("createStatusReportBase", async (t) => {
|
||||
t.is(typeof statusReport.job_run_uuid, "string");
|
||||
t.is(statusReport.languages, "java,swift");
|
||||
t.is(statusReport.ref, process.env["GITHUB_REF"]!);
|
||||
t.is(statusReport.registry_types, "maven_repository");
|
||||
t.is(statusReport.runner_available_disk_space_bytes, 100);
|
||||
t.is(statusReport.runner_image_version, process.env["ImageVersion"]);
|
||||
t.is(statusReport.runner_os, process.env["RUNNER_OS"]!);
|
||||
|
||||
+80
-1
@@ -1,7 +1,9 @@
|
||||
import * as os from "os";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
import type { ActionState } from "./action-common";
|
||||
import {
|
||||
getWorkflowEventName,
|
||||
getOptionalInput,
|
||||
@@ -17,12 +19,14 @@ import type { ComputedInput, InputName } from "./config/inputs";
|
||||
import { parseRegistriesWithoutCredentials } from "./config/pack-registries";
|
||||
import type { DependencyCacheRestoreStatusReport } from "./dependency-caching";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { EnvVar } from "./environment";
|
||||
import { EnvVar, getEnv, ReadOnlyEnv, RegistryProxyVars } from "./environment";
|
||||
import { getRef } from "./git-utils";
|
||||
import * as json from "./json";
|
||||
import type { Logger } from "./logging";
|
||||
import type { OverlayBaseDatabaseDownloadStats } from "./overlay/caching";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import type { ToolsSource } from "./setup-codeql";
|
||||
import { registryBaseSchema } from "./start-proxy/types";
|
||||
import {
|
||||
ConfigurationError,
|
||||
getRequiredEnvParam,
|
||||
@@ -59,6 +63,30 @@ export function getDisplayActionName(actionName: ActionName): string {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Either creates a UUIDv4 for the analysis or retrieves an existing one from the
|
||||
* environment and returns it.
|
||||
* If a new UUID is generated, it is also exported as an environment variable.
|
||||
*/
|
||||
export function getJobUUID(
|
||||
action: ActionState<["Logger", "ReadOnlyEnv", "Actions"]>,
|
||||
) {
|
||||
// Check if we already have a UUID for the analysis and return it if so.
|
||||
const existingJobRunUuid = action.env.getOptional(EnvVar.JOB_RUN_UUID);
|
||||
|
||||
if (existingJobRunUuid !== undefined && uuid.validate(existingJobRunUuid)) {
|
||||
action.logger.info(`Existing job run UUID is ${existingJobRunUuid}.`);
|
||||
return existingJobRunUuid;
|
||||
}
|
||||
|
||||
// Otherwise generate a new UUID.
|
||||
const jobRunUuid = uuid.v4();
|
||||
action.logger.info(`Job run UUID is ${jobRunUuid}.`);
|
||||
|
||||
action.actions.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
|
||||
return jobRunUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns a boolean indicating whether the analysis is considered to be first party.
|
||||
*
|
||||
@@ -159,6 +187,12 @@ export interface StatusReportBase {
|
||||
ml_powered_javascript_queries?: string;
|
||||
/** Ref that the workflow was triggered on. */
|
||||
ref: string;
|
||||
/**
|
||||
* A comma-separated list of private registry types which are configured for CodeQL.
|
||||
* This only includes registry types we support (as determined by the `start-proxy` action),
|
||||
* not all that are configured.
|
||||
*/
|
||||
registry_types?: string;
|
||||
/** Action runner hardware architecture (context runner.arch). */
|
||||
runner_arch?: string;
|
||||
/** Available disk space on the runner, in bytes. */
|
||||
@@ -262,6 +296,50 @@ export interface EventReport {
|
||||
started_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve a list of private registry types from the `CODEQL_PROXY_URLS` environment
|
||||
* variable and returns it as a comma-separated string if successful. Returns `undefined` otherwise.
|
||||
*/
|
||||
export function getRegistryTypesFromEnv(
|
||||
logger: Logger,
|
||||
env: ReadOnlyEnv = getEnv(),
|
||||
): string | undefined {
|
||||
// Try to get the value of the environment variable.
|
||||
const value = env.getOptional(RegistryProxyVars.PROXY_URLS);
|
||||
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try to parse the JSON we expect to find in it and return the comma-separated list of
|
||||
// (unique) registry types.
|
||||
try {
|
||||
const data = JSON.parse(value) as unknown;
|
||||
|
||||
// Check that the parsed JSON meets our expectations.
|
||||
if (!json.isArray(data)) {
|
||||
logger.debug(
|
||||
`Expected '${RegistryProxyVars.PROXY_URLS}' to contain a JSON array, but got '${typeof data}'.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
if (!json.validateArray(registryBaseSchema, data)) {
|
||||
logger.debug(
|
||||
`Expected '${RegistryProxyVars.PROXY_URLS}' to contain a JSON array of registry objects, but got something else.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const types = new Set(data.map((r) => r.type));
|
||||
return Array.from(types).sort().join(",");
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`Failed to parse '${RegistryProxyVars.PROXY_URLS}': ${getErrorMessage(err)}.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a StatusReport.
|
||||
*
|
||||
@@ -324,6 +402,7 @@ export async function createStatusReportBase(
|
||||
job_name: jobName,
|
||||
job_run_uuid: jobRunUUID,
|
||||
ref,
|
||||
registry_types: getRegistryTypesFromEnv(logger),
|
||||
runner_os: runnerOs,
|
||||
started_at: workflowStartedAt,
|
||||
status,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import * as path from "path";
|
||||
import * as stream from "stream";
|
||||
|
||||
import test from "ava";
|
||||
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { extractTarZst } from "./tar";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import { withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("extractTarZst rejects if the input stream errors", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const archive = new stream.PassThrough();
|
||||
const promise = extractTarZst(
|
||||
archive,
|
||||
path.join(tmpDir, "dest"),
|
||||
{ type: "gnu", version: "1.34" },
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
archive.destroy(
|
||||
Object.assign(new Error("socket hang up"), {
|
||||
code: "ECONNRESET",
|
||||
}),
|
||||
);
|
||||
|
||||
await t.throwsAsync(promise, {
|
||||
message: /Error while downloading and extracting tar/,
|
||||
});
|
||||
});
|
||||
});
|
||||
+9
-4
@@ -194,10 +194,15 @@ export async function extractTarZst(
|
||||
});
|
||||
|
||||
if (tar instanceof stream.Readable) {
|
||||
tar.pipe(tarProcess.stdin).on("error", (err) => {
|
||||
reject(
|
||||
new Error(`Error while downloading and extracting tar: ${err}`),
|
||||
);
|
||||
// Use `pipeline` rather than `pipe` so that an error on either stream is reported here
|
||||
// rather than being emitted as an unhandled `error` event, and so that `tar`'s standard
|
||||
// input is closed if the download fails partway through.
|
||||
stream.pipeline(tar, tarProcess.stdin, (err) => {
|
||||
if (err) {
|
||||
reject(
|
||||
new Error(`Error while downloading and extracting tar: ${err}`),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+83
-25
@@ -34,11 +34,14 @@ import { ActionName } from "./status-report";
|
||||
import {
|
||||
DEFAULT_DEBUG_ARTIFACT_NAME,
|
||||
DEFAULT_DEBUG_DATABASE_NAME,
|
||||
Failure,
|
||||
getEnv,
|
||||
GitHubVariant,
|
||||
GitHubVersion,
|
||||
HTTPError,
|
||||
resetCachedCodeQlVersion,
|
||||
Result,
|
||||
Success,
|
||||
} from "./util";
|
||||
|
||||
export const SAMPLE_DOTCOM_API_DETAILS = {
|
||||
@@ -182,13 +185,32 @@ export function getTestEnv(testEnv: NodeJS.ProcessEnv = {}): Env {
|
||||
return getEnv(testEnv);
|
||||
}
|
||||
|
||||
/** An implementation of `ActionsEnv` for use in tests. */
|
||||
class TestActionsEnv implements ActionsEnv {
|
||||
constructor(private readonly env: Env) {}
|
||||
|
||||
public clone(env: Env): this {
|
||||
return Object.create(this, { env: { value: env } }) as this;
|
||||
}
|
||||
|
||||
public getRequiredInput(name: string): string {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
|
||||
public getOptionalInput(_name: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public exportVariable(name: string, value: string): void {
|
||||
this.env.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `ActionsEnv` instance for use in tests.
|
||||
*/
|
||||
export function getTestActionsEnv(): ActionsEnv {
|
||||
return {
|
||||
getOptionalInput: () => undefined,
|
||||
};
|
||||
export function getTestActionsEnv(env: Env): TestActionsEnv {
|
||||
return new TestActionsEnv(env);
|
||||
}
|
||||
|
||||
/** For testing purposes, we make all available state features accessible in `TestEnv`. */
|
||||
@@ -206,12 +228,13 @@ type AllState = [
|
||||
export function initAllState(
|
||||
overrides?: Partial<ActionState<AllState>>,
|
||||
): ActionState<AllState> {
|
||||
const env = getTestEnv();
|
||||
return {
|
||||
name: ActionName.Init,
|
||||
startedAt: new Date(),
|
||||
logger: new RecordingLogger(),
|
||||
env: getTestEnv(),
|
||||
actions: getTestActionsEnv(),
|
||||
env,
|
||||
actions: getTestActionsEnv(env),
|
||||
apiClient: github.getOctokit("123"),
|
||||
features: createFeatures([]),
|
||||
...overrides,
|
||||
@@ -222,9 +245,13 @@ type DelayedCheck<
|
||||
Args extends readonly any[],
|
||||
R,
|
||||
Fs extends ReadonlyArray<AllState[number]>,
|
||||
> = (env: Readonly<BaseEnvBuilder<Args, R, Fs>>) => Promise<any>;
|
||||
> = (
|
||||
env: Readonly<BaseEnvBuilder<Args, R, Fs>>,
|
||||
result: Result<Awaited<R>, ThrownError<ErrorConstructor | Error>>,
|
||||
) => Promise<any>;
|
||||
|
||||
export type ValueOrMutation<T> = T | ((val: T) => void);
|
||||
export type Mutation<T> = (val: T) => void;
|
||||
export type ValueOrMutation<T> = T | Mutation<T>;
|
||||
|
||||
/**
|
||||
* Wraps a function that accepts an `ActionState` for testing in different environments.
|
||||
@@ -236,6 +263,7 @@ abstract class BaseEnvBuilder<
|
||||
> {
|
||||
protected readonly fn: (state: ActionState<Fs>, ...args: Args) => R;
|
||||
private logger: RecordingLogger;
|
||||
private actions: TestActionsEnv;
|
||||
protected state: ActionState<AllState>;
|
||||
protected checks: Array<DelayedCheck<Args, R, Fs>>;
|
||||
|
||||
@@ -245,15 +273,26 @@ abstract class BaseEnvBuilder<
|
||||
) {
|
||||
this.fn = fn;
|
||||
this.logger = new RecordingLogger();
|
||||
this.state =
|
||||
cloneFrom !== undefined
|
||||
? ({
|
||||
...cloneFrom.state,
|
||||
env: cloneFrom.state.env.clone(),
|
||||
actions: Object.create(cloneFrom.state.actions),
|
||||
logger: this.logger,
|
||||
} satisfies ActionState<AllState>)
|
||||
: initAllState({ logger: this.logger });
|
||||
|
||||
if (cloneFrom !== undefined) {
|
||||
const env = cloneFrom.state.env.clone();
|
||||
this.actions = cloneFrom.actions.clone(env);
|
||||
this.state = {
|
||||
...cloneFrom.state,
|
||||
env,
|
||||
actions: this.actions,
|
||||
logger: this.logger,
|
||||
} satisfies ActionState<AllState>;
|
||||
} else {
|
||||
const env = getTestEnv();
|
||||
this.actions = getTestActionsEnv(env);
|
||||
this.state = initAllState({
|
||||
logger: this.logger,
|
||||
env,
|
||||
actions: this.actions,
|
||||
});
|
||||
}
|
||||
|
||||
this.checks = [...(cloneFrom?.checks ?? [])];
|
||||
}
|
||||
|
||||
@@ -320,13 +359,10 @@ abstract class BaseEnvBuilder<
|
||||
return result;
|
||||
}
|
||||
|
||||
public withActions(arg: ValueOrMutation<ActionsEnv>): this {
|
||||
/** Applies `fn` to the `ActionsEnv`. */
|
||||
public withActions(fn: Mutation<ActionsEnv>): this {
|
||||
const result = this.clone();
|
||||
if (typeof arg === "function") {
|
||||
arg(result.state.actions);
|
||||
} else {
|
||||
result.state.actions = arg;
|
||||
}
|
||||
fn(result.state.actions);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -342,6 +378,28 @@ abstract class BaseEnvBuilder<
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a delayed check that the environment variables returned by `fn`
|
||||
* are present in the environment after the main assertion passes.
|
||||
*/
|
||||
public hasEnv(
|
||||
t: ExecutionContext<unknown>,
|
||||
fn: (
|
||||
value: Awaited<R> | undefined,
|
||||
error: ThrownError<ErrorConstructor | Error> | undefined,
|
||||
) => Record<string, string | undefined>,
|
||||
): this {
|
||||
const result = this.clone();
|
||||
result.checks.push(async (env, r) => {
|
||||
const value = r.orElse(undefined);
|
||||
const error = r.isFailure() ? r.value : undefined;
|
||||
const expected = fn(value, error);
|
||||
|
||||
t.like(env.getState().env.get(), expected);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a delayed check that `messages` are not logged. The check will be
|
||||
* performed after the main assertion passes.
|
||||
@@ -439,7 +497,7 @@ class CallableEnvBuilder<
|
||||
|
||||
// Run other delayed checks.
|
||||
for (const delayedCheck of this.checks) {
|
||||
await delayedCheck(this);
|
||||
await delayedCheck(this, new Success(result));
|
||||
}
|
||||
|
||||
// Return the results of the function call and the main assertion.
|
||||
@@ -465,7 +523,7 @@ class CallableEnvBuilder<
|
||||
|
||||
// Run other delayed checks.
|
||||
for (const delayedCheck of this.checks) {
|
||||
await delayedCheck(this);
|
||||
await delayedCheck(this, new Failure(error));
|
||||
}
|
||||
|
||||
// Return the error.
|
||||
|
||||
@@ -38,6 +38,43 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract falls back to downloading before extracting if streaming fails",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");
|
||||
const destination = path.join(tmpDir, "codeql");
|
||||
const downloadTool = sinon
|
||||
.stub(toolcache, "downloadTool")
|
||||
.resolves(archivePath);
|
||||
const extract = sinon.stub(tar, "extract").resolves(destination);
|
||||
const extractTarZst = sinon.stub(tar, "extractTarZst").resolves();
|
||||
const request = nock("https://example.com")
|
||||
.get("/codeql-bundle.tar.zst")
|
||||
.replyWithError(
|
||||
Object.assign(new Error("socket hang up"), { code: "ECONNRESET" }),
|
||||
);
|
||||
|
||||
const statusReport = await downloadAndExtract(
|
||||
"https://example.com/codeql-bundle.tar.zst",
|
||||
"zstd",
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
getRunnerLogger(true),
|
||||
);
|
||||
|
||||
t.assert(Number.isInteger(statusReport.downloadDurationMs));
|
||||
t.true(request.isDone());
|
||||
t.false(extractTarZst.called);
|
||||
t.true(downloadTool.calledOnce);
|
||||
t.true(extract.calledOnce);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"downloadAndExtract omits the download duration when streaming extraction",
|
||||
async (t) => {
|
||||
|
||||
+24
-4
@@ -19,6 +19,12 @@ import { cleanUpPath, getErrorMessage, getRequiredEnvParam } from "./util";
|
||||
*/
|
||||
const STREAMING_HIGH_WATERMARK_BYTES = 4 * 1024 * 1024; // 4 MiB
|
||||
|
||||
/**
|
||||
* How long the streaming download of the CodeQL tools may stall for before we abort it. This
|
||||
* applies both to establishing the connection and to gaps between chunks of the response body.
|
||||
*/
|
||||
const STREAMING_STALL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
/**
|
||||
* The name of the tool cache directory for the CodeQL tools.
|
||||
*/
|
||||
@@ -137,8 +143,8 @@ async function downloadAndExtractZstdWithStreaming(
|
||||
authorization ? { authorization } : {},
|
||||
headers,
|
||||
);
|
||||
const response = await new Promise<IncomingMessage>((resolve) =>
|
||||
https.get(
|
||||
const response = await new Promise<IncomingMessage>((resolve, reject) => {
|
||||
const request = https.get(
|
||||
codeqlURL,
|
||||
{
|
||||
headers,
|
||||
@@ -148,10 +154,24 @@ async function downloadAndExtractZstdWithStreaming(
|
||||
agent,
|
||||
} as unknown as RequestOptions,
|
||||
(r) => resolve(r),
|
||||
),
|
||||
);
|
||||
);
|
||||
// Without this listener, connection failures such as `ECONNRESET` are emitted as unhandled
|
||||
// `error` events, which terminate the process instead of letting us fall back to downloading
|
||||
// the bundle before extracting it. This listener stays attached after the response arrives, so
|
||||
// it also handles errors that occur while the response is being streamed.
|
||||
request.on("error", reject);
|
||||
request.setTimeout(STREAMING_STALL_TIMEOUT_MS, () => {
|
||||
request.destroy(
|
||||
new Error(
|
||||
`No data received for ${formatDuration(STREAMING_STALL_TIMEOUT_MS)}.`,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
// Discard the response body so that the connection can be released.
|
||||
response.resume();
|
||||
throw new Error(
|
||||
`Failed to download CodeQL bundle from ${codeqlURL}. HTTP status code: ${response.statusCode}.`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user