mirror of
https://github.com/github/codeql-action.git
synced 2026-05-25 00:24:42 +00:00
Compare commits
378 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 436aa0401d | |||
| ea37b337cd | |||
| ba0a2f91b7 | |||
| 4041a11865 | |||
| 2a6fe1608c | |||
| 3d6ea97f26 | |||
| 7d25a3e590 | |||
| 4dc72761a6 | |||
| c559992c9e | |||
| 8d217609b0 | |||
| 257b3d3fc8 | |||
| 201a96b541 | |||
| 312a2fee96 | |||
| 2ca0fbdca8 | |||
| 12c1d88854 | |||
| 70419e3273 | |||
| b62aaa99a5 | |||
| 2f2dbd2e78 | |||
| b4ea7aa65a | |||
| 87ac48dae6 | |||
| 42d7f62579 | |||
| 540699dcca | |||
| 9a85234875 | |||
| 2a950b930c | |||
| 4f815a68d3 | |||
| 0aedbb71d8 | |||
| 868e2ea564 | |||
| 792c223bc1 | |||
| efc9b0a9e3 | |||
| 272ada693f | |||
| 610a6682b6 | |||
| 1627096569 | |||
| 68bde559de | |||
| 9739ad2d18 | |||
| b81d0d250f | |||
| a16cb53dd8 | |||
| 803d9e8c3c | |||
| 0c80cee806 | |||
| d032ee8c47 | |||
| 0fd9c7d135 | |||
| 922d6fb888 | |||
| df77e87896 | |||
| 6e3f985e4f | |||
| e7a347dfb1 | |||
| 17eabb2500 | |||
| aaef09c48d | |||
| ae1b9155d3 | |||
| 9f82f88f07 | |||
| 7525c68ea1 | |||
| 01bc9be56a | |||
| 817b68489e | |||
| 1b5632783c | |||
| 1848b73afa | |||
| d1e9792bc8 | |||
| 2c9cd77837 | |||
| b967fdfbdc | |||
| 55d6319f96 | |||
| b0942116d7 | |||
| bc0b696b41 | |||
| a796e3e4ed | |||
| f9bb0e001c | |||
| 4b7faf0b3d | |||
| 09a1d9ec2a | |||
| f64a4491cf | |||
| 7fc86e0c37 | |||
| 5997e25ad9 | |||
| 7587714d0a | |||
| a723e99345 | |||
| fbba1e03be | |||
| 933238e8d5 | |||
| e46ed2cbd0 | |||
| b73d1d1634 | |||
| 24e0bb00a9 | |||
| ec298daba7 | |||
| 8c6e48dbe0 | |||
| 719098349e | |||
| 2bb209555a | |||
| 7851e55dc3 | |||
| 262a15f6cf | |||
| a6109b1c07 | |||
| 022ff3c73f | |||
| 0a4d574ac4 | |||
| d1edf2e4de | |||
| facd53f789 | |||
| b77983290b | |||
| fcf29e3d86 | |||
| 1fed3e9ba8 | |||
| 549683cee5 | |||
| 7a6ed56219 | |||
| 91fbc51606 | |||
| 35715ef8fe | |||
| bac7fdaf42 | |||
| 1517969c90 | |||
| f073360456 | |||
| 5145c112e7 | |||
| 7108503ac6 | |||
| 4fe9b1e243 | |||
| 56733fb5ae | |||
| 0a636086c9 | |||
| 97be3af35a | |||
| de303a9db5 | |||
| 7a818e6977 | |||
| 30e0f4391d | |||
| 7c5585e5cf | |||
| 245f6828c4 | |||
| c109008fac | |||
| e73c940c9b | |||
| cdb655d6d4 | |||
| 6153577cab | |||
| 8f02cfa11d | |||
| 0ed734b61b | |||
| efdcb31f11 | |||
| 4d2c7c6e10 | |||
| 70b2658d23 | |||
| 530fcb3bbf | |||
| 2acf81942b | |||
| d2a54a4507 | |||
| bc4097bbe1 | |||
| c8e26e209a | |||
| 0752451507 | |||
| 243c274daf | |||
| 19b3a84f58 | |||
| 858a6149c1 | |||
| c60c75576d | |||
| 59aede2113 | |||
| 6c35f8607b | |||
| c486cacf49 | |||
| 365478cc5b | |||
| f0e6490756 | |||
| 860353f245 | |||
| 4fb8483ef0 | |||
| c2574efbee | |||
| 4cbe7bef85 | |||
| f6a5638305 | |||
| 1279e8d41c | |||
| af1f613989 | |||
| 5026833be5 | |||
| 201ddc275d | |||
| 1dcdb940d5 | |||
| 0b7b740d4c | |||
| 0ac85966ba | |||
| 5019ed041c | |||
| d64d81d41f | |||
| 6777c894e9 | |||
| 79f9c0517c | |||
| 3b3a77544b | |||
| 9f95de42d6 | |||
| e2d518d895 | |||
| 9df9e9176e | |||
| 6847a42aa8 | |||
| f820c80d4d | |||
| ca7d6d3b79 | |||
| 8d9c36a0ce | |||
| 95e58e9a2c | |||
| 6f31bfe060 | |||
| d2e135a73a | |||
| 60abb65df0 | |||
| 5a0a562209 | |||
| f8b62132ab | |||
| 65216971a1 | |||
| 3c45af2dd2 | |||
| f1c339364c | |||
| 1024fc496c | |||
| 9dd4cfed96 | |||
| c1403f094c | |||
| 90d7616015 | |||
| 1aef4ed505 | |||
| cb52ba6486 | |||
| 7c9e131894 | |||
| 130ab2d721 | |||
| 8cf2dc52f9 | |||
| 8339b9254e | |||
| 97bcdd8c1e | |||
| e6c21da23c | |||
| bad0a744dd | |||
| ee09113642 | |||
| b669eab7e3 | |||
| 4e8c9ce33c | |||
| 1cf0431149 | |||
| a26cb68cc7 | |||
| 60991e61ac | |||
| 7197c2b792 | |||
| 597e12aa85 | |||
| d277a56348 | |||
| 111a537cd9 | |||
| 51d833290e | |||
| 5a17511bf0 | |||
| 43d8420a42 | |||
| 76a687e1d8 | |||
| 751f3e2f7c | |||
| 808513f048 | |||
| e452857e57 | |||
| b623f5fd57 | |||
| 35a38985d3 | |||
| 14ed573199 | |||
| 43d8864b35 | |||
| f8aff3ad8b | |||
| e6c83948f5 | |||
| 347f0c676d | |||
| 6eed62b035 | |||
| de1752b85d | |||
| 1065967b50 | |||
| e25c0a535a | |||
| 5f323cad05 | |||
| 212e28374b | |||
| 36075a4980 | |||
| 34950e1b11 | |||
| 57ec7e1000 | |||
| 311573e58e | |||
| 1f4c852aeb | |||
| 2e3aaaefca | |||
| e2203c62cf | |||
| 7b0c5b1669 | |||
| faf45e07f9 | |||
| 8b5e60477c | |||
| 99b8dd4d57 | |||
| c618c9bddb | |||
| 9fd9b64766 | |||
| 0c7c298b2a | |||
| a507a542a4 | |||
| be0a156326 | |||
| f98bf5e347 | |||
| 3db32b5d27 | |||
| 4e0952a3c0 | |||
| 0592832ed8 | |||
| 88a7e5118e | |||
| 6643a7d207 | |||
| 47f1709a3c | |||
| b1981a5480 | |||
| a899987af2 | |||
| 4ed3c0efe6 | |||
| 4ea3a4b4af | |||
| 191d7c6f13 | |||
| aa69c483cd | |||
| fe775da508 | |||
| 353802f9f2 | |||
| cc7db4a1f9 | |||
| 6010f9d8e2 | |||
| c10b8064de | |||
| c5ffd06837 | |||
| d6d1743b8e | |||
| 999119ba45 | |||
| 65d2efa733 | |||
| 2437b20ab3 | |||
| f13c600724 | |||
| 7dcea06663 | |||
| ea5f71947c | |||
| 45ceeea896 | |||
| 24448c9843 | |||
| 7c51060631 | |||
| b8bb9f28b8 | |||
| e9cf68bb33 | |||
| 36791d8d66 | |||
| 22eba96a28 | |||
| 0078ad667e | |||
| fa7a15b909 | |||
| 8c29faa7ab | |||
| f94817b9f0 | |||
| dd060970a5 | |||
| 5cc552f43e | |||
| 6b1a9f2131 | |||
| 9d3ec5727a | |||
| 3ff82aacd0 | |||
| 4bdd4e7526 | |||
| 23a0098b57 | |||
| ea7b090925 | |||
| a663d0174a | |||
| b659882aae | |||
| d5bb39fa0b | |||
| 521c3536d3 | |||
| 972365e142 | |||
| 8a0b4f2746 | |||
| a5418e172c | |||
| fae4c28b51 | |||
| 661a8fbbe3 | |||
| e7c7b68c5f | |||
| fa568ebc69 | |||
| 0da3139813 | |||
| 0abe92ed20 | |||
| 07f235e5f2 | |||
| 9fd40ff508 | |||
| 75ed461aaa | |||
| cfc18781e0 | |||
| 9fe42f69b7 | |||
| c5a984e1aa | |||
| 0543156694 | |||
| 4cec5d2830 | |||
| 74dd691a45 | |||
| a5244bf7dd | |||
| 1bc611ed0c | |||
| d2008eee7c | |||
| 9481177f3d | |||
| 9813849e61 | |||
| 4867f5927a | |||
| 49af37b7ab | |||
| b72f4fec40 | |||
| 0d87a75829 | |||
| 3db9a05c73 | |||
| aa2773169b | |||
| 054745baee | |||
| 3d564d9359 | |||
| 137e0dec2b | |||
| d128e5daa8 | |||
| eedab83377 | |||
| 8c023a6b07 | |||
| 28f56f2bed | |||
| d48d054533 | |||
| 72c0b0efb7 | |||
| 05b1a5d28f | |||
| 8dc2e5d9d2 | |||
| 8fd6c0e573 | |||
| 3869755554 | |||
| 20e68ac12b | |||
| 095e0fe505 | |||
| 47b94fe61c | |||
| 51a1d6917f | |||
| 510cf736e3 | |||
| 89f0c86efa | |||
| c3f90ba975 | |||
| c6f931105c | |||
| eeb9b3f424 | |||
| 64507ed148 | |||
| 1a45a9b9d0 | |||
| 30c555a528 | |||
| 39191bd27f | |||
| 147e93e5dc | |||
| e6d83bce6d | |||
| 0d057ccbce | |||
| 074a0dbd16 | |||
| ab3b6fd199 | |||
| ce4a1feb6a | |||
| 899a672743 | |||
| f4be604881 | |||
| 0bc1b6f632 | |||
| 3d8036cf7f | |||
| 9fecf32c77 | |||
| 07d509fbaf | |||
| 23674c1f2a | |||
| ecd1c77ffa | |||
| 5b630489d6 | |||
| 582d08c553 | |||
| 60a0dce0ad | |||
| 7da6361ba5 | |||
| 08d1198b01 | |||
| 5e54629286 | |||
| f254006ed7 | |||
| 573e7dd341 | |||
| f88d49ee5d | |||
| 28f515d9ad | |||
| de06821112 | |||
| ddafddb826 | |||
| 740f177889 | |||
| 0393130759 | |||
| f86097dfdb | |||
| 6e67ef61f2 | |||
| 193dd19c2d | |||
| fd1ca02d0d | |||
| a0e3ed6555 | |||
| fbb2eb9556 | |||
| 926e6dfee5 | |||
| b1f1e7bd31 | |||
| a91b7a3e57 | |||
| 556dd79c4b | |||
| 19544bb9b4 | |||
| d74701caa1 | |||
| d05b50b13f | |||
| 70d5cccce1 | |||
| b04e63ffdf | |||
| b0f877255d | |||
| 9082319f5c | |||
| cdafc35ccb | |||
| c10020e6a8 | |||
| b2de4934cf | |||
| 1443f5865e | |||
| 4eb247591f | |||
| df4e1992c0 | |||
| d18f3acf74 | |||
| 035c1179af |
@@ -22,7 +22,8 @@ runs:
|
||||
MAJOR_VERSION: ${{ inputs.major_version }}
|
||||
LATEST_TAG: ${{ inputs.latest_tag }}
|
||||
run: |
|
||||
python ${{ github.action_path }}/release-branches.py \
|
||||
npm ci
|
||||
npx tsx ./pr-checks/release-branches.ts \
|
||||
--major-version "$MAJOR_VERSION" \
|
||||
--latest-tag "$LATEST_TAG"
|
||||
shell: bash
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import configparser
|
||||
|
||||
# Name of the remote
|
||||
ORIGIN = 'origin'
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
grandparent_dir = os.path.dirname(os.path.dirname(script_dir))
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
with open(os.path.join(grandparent_dir, 'releases.ini')) as stream:
|
||||
config.read_string('[default]\n' + stream.read())
|
||||
|
||||
OLDEST_SUPPORTED_MAJOR_VERSION = int(config['default']['OLDEST_SUPPORTED_MAJOR_VERSION'])
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--major-version", required=True, type=str, help="The major version of the release")
|
||||
parser.add_argument("--latest-tag", required=True, type=str, help="The most recent tag published to the repository")
|
||||
args = parser.parse_args()
|
||||
|
||||
major_version = args.major_version
|
||||
latest_tag = args.latest_tag
|
||||
|
||||
print("major_version: " + major_version)
|
||||
print("latest_tag: " + latest_tag)
|
||||
|
||||
# If this is a primary release, we backport to all supported branches,
|
||||
# so we check whether the major_version taken from the package.json
|
||||
# is greater than or equal to the latest tag pulled from the repo.
|
||||
# For example...
|
||||
# 'v1' >= 'v2' is False # we're operating from an older release branch and should not backport
|
||||
# 'v2' >= 'v2' is True # the normal case where we're updating the current version
|
||||
# 'v3' >= 'v2' is True # in this case we are making the first release of a new major version
|
||||
consider_backports = ( major_version >= latest_tag.split(".")[0] )
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
||||
|
||||
f.write(f"backport_source_branch=releases/{major_version}\n")
|
||||
|
||||
backport_target_branches = []
|
||||
|
||||
if consider_backports:
|
||||
for i in range(int(major_version.strip("v"))-1, 0, -1):
|
||||
branch_name = f"releases/v{i}"
|
||||
if i >= OLDEST_SUPPORTED_MAJOR_VERSION:
|
||||
backport_target_branches.append(branch_name)
|
||||
|
||||
f.write("backport_target_branches="+json.dumps(backport_target_branches)+"\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -15,6 +15,12 @@ runs:
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
shell: bash
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "CodeQL config"
|
||||
queries:
|
||||
queries:
|
||||
- name: Run custom queries
|
||||
uses: ./queries
|
||||
# Run all extra query suites, both because we want to
|
||||
@@ -13,3 +13,5 @@ queries:
|
||||
paths-ignore:
|
||||
- lib
|
||||
- tests
|
||||
- "**/*.test.ts"
|
||||
- "**/testing-util.ts"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
directories:
|
||||
- "/"
|
||||
- "/pr-checks"
|
||||
schedule:
|
||||
interval: weekly
|
||||
cooldown:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
OLDEST_SUPPORTED_MAJOR_VERSION=3
|
||||
Generated
+2
-2
@@ -60,12 +60,12 @@ jobs:
|
||||
setup-kotlin: 'true'
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: C#,java-kotlin,swift,typescript
|
||||
languages: C#,java-kotlin,typescript
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
|
||||
- name: 'Check languages'
|
||||
run: |
|
||||
expected_languages="csharp,java,swift,javascript"
|
||||
expected_languages="csharp,java,javascript"
|
||||
actual_languages=$(jq -r '.languages | join(",")' "$RUNNER_TEMP"/config)
|
||||
|
||||
if [ "$expected_languages" != "$actual_languages" ]; then
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ jobs:
|
||||
use-all-platform-bundle: 'false'
|
||||
setup-kotlin: 'true'
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
|
||||
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
Generated
+18
-1
@@ -71,7 +71,17 @@ jobs:
|
||||
id: proxy
|
||||
uses: ./../action/start-proxy
|
||||
with:
|
||||
registry_secrets: '[{ "type": "nuget_feed", "url": "https://api.nuget.org/v3/index.json" }]'
|
||||
registry_secrets: |
|
||||
[
|
||||
{
|
||||
"type": "maven_repository",
|
||||
"url": "https://repo.maven.apache.org/maven2/"
|
||||
},
|
||||
{
|
||||
"type": "maven_repository",
|
||||
"url": "https://repo1.maven.org/maven2"
|
||||
}
|
||||
]
|
||||
|
||||
- name: Print proxy outputs
|
||||
run: |
|
||||
@@ -82,5 +92,12 @@ jobs:
|
||||
- name: Fail if proxy outputs are not set
|
||||
if: (!steps.proxy.outputs.proxy_host) || (!steps.proxy.outputs.proxy_port) || (!steps.proxy.outputs.proxy_ca_certificate) || (!steps.proxy.outputs.proxy_urls)
|
||||
run: exit 1
|
||||
|
||||
- name: Fail if proxy_urls does not contain all registries
|
||||
if: |
|
||||
join(fromJSON(steps.proxy.outputs.proxy_urls)[*].type, ',') != 'maven_repository,maven_repository'
|
||||
|| !contains(steps.proxy.outputs.proxy_urls, 'https://repo.maven.apache.org/maven2/')
|
||||
|| !contains(steps.proxy.outputs.proxy_urls, 'https://repo1.maven.org/maven2')
|
||||
run: exit 1
|
||||
env:
|
||||
CODEQL_ACTION_TEST_MODE: true
|
||||
|
||||
@@ -66,6 +66,7 @@ jobs:
|
||||
uses: ./../action/.github/actions/verify-debug-artifact-scan-completed
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: cpp,csharp,go,java,javascript,python
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
debug: true
|
||||
debug-artifact-name: my-debug-artifacts
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Workflow runs on main, on a release branch, and that were triggered as part of a merge group have
|
||||
# already passed CI before being merged. Therefore if they fail, we should make sure that there
|
||||
# wasn't a transient failure by rerunning the failed jobs once before investigating further.
|
||||
name: Deflake
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
types: [completed]
|
||||
# Exclude workflows that have significant side effects, like publishing releases. It's OK to
|
||||
# retry CodeQL analysis.
|
||||
workflows:
|
||||
- Check Expected Release Files
|
||||
- Code-Scanning config CLI tests
|
||||
- CodeQL action
|
||||
- Manual Check - go
|
||||
- "PR Check - All-platform bundle"
|
||||
- "PR Check - Analysis kinds"
|
||||
- "PR Check - Analyze: 'ref' and 'sha' from inputs"
|
||||
- "PR Check - autobuild-action"
|
||||
- "PR Check - Autobuild direct tracing (custom working directory)"
|
||||
- "PR Check - Autobuild working directory"
|
||||
- "PR Check - Build mode autobuild"
|
||||
- "PR Check - Build mode manual"
|
||||
- "PR Check - Build mode none"
|
||||
- "PR Check - Build mode rollback"
|
||||
- "PR Check - Bundle: Caching checks"
|
||||
- "PR Check - Bundle: From nightly"
|
||||
- "PR Check - Bundle: From toolcache"
|
||||
- "PR Check - Bundle: Zstandard checks"
|
||||
- "PR Check - C/C\\+\\+: autoinstalling dependencies (Linux)"
|
||||
- "PR Check - C/C\\+\\+: autoinstalling dependencies is skipped (macOS)"
|
||||
- "PR Check - C/C\\+\\+: disabling autoinstalling dependencies (Linux)"
|
||||
- "PR Check - Clean up database cluster directory"
|
||||
- "PR Check - CodeQL Bundle All"
|
||||
- "PR Check - Config export"
|
||||
- "PR Check - Config input"
|
||||
- "PR Check - Custom source root"
|
||||
- "PR Check - Debug artifact upload"
|
||||
- "PR Check - Debug artifacts after failure"
|
||||
- "PR Check - Diagnostic export"
|
||||
- "PR Check - Export file baseline information"
|
||||
- "PR Check - Extractor ram and threads options test"
|
||||
- "PR Check - Go: Custom queries"
|
||||
- "PR Check - Go: diagnostic when Go is changed after init step"
|
||||
- "PR Check - Go: diagnostic when `file` is not installed"
|
||||
- "PR Check - Go: tracing with autobuilder step"
|
||||
- "PR Check - Go: tracing with custom build steps"
|
||||
- "PR Check - Go: tracing with legacy workflow"
|
||||
- "PR Check - Go: workaround for indirect tracing"
|
||||
- "PR Check - Job run UUID added to SARIF"
|
||||
- "PR Check - Language aliases"
|
||||
- "PR Check - Local CodeQL bundle"
|
||||
- "PR Check - Multi-language repository"
|
||||
- "PR Check - Overlay database init fallback"
|
||||
- "PR Check - Packaging: Action input"
|
||||
- "PR Check - Packaging: Config and input"
|
||||
- "PR Check - Packaging: Config and input passed to the CLI"
|
||||
- "PR Check - Packaging: Config file"
|
||||
- "PR Check - Packaging: Download using registries"
|
||||
- "PR Check - Proxy test"
|
||||
- "PR Check - Remote config file"
|
||||
- "PR Check - Resolve environment"
|
||||
- "PR Check - RuboCop multi-language"
|
||||
- "PR Check - Ruby analysis"
|
||||
- "PR Check - Rust analysis"
|
||||
- "PR Check - Split workflow"
|
||||
- "PR Check - Start proxy"
|
||||
- "PR Check - Submit SARIF after failure"
|
||||
- "PR Check - Swift analysis using a custom build command"
|
||||
- "PR Check - Swift analysis using autobuild"
|
||||
- "PR Check - Test different uses of `upload-sarif`"
|
||||
- "PR Check - Test unsetting environment variables"
|
||||
- "PR Check - Upload-sarif: ref and sha from inputs"
|
||||
- "PR Check - Use a custom `checkout_path`"
|
||||
- PR Checks
|
||||
- Query filters tests
|
||||
- Test that the workaround for python 3.12 on windows works
|
||||
|
||||
jobs:
|
||||
rerun-on-failure:
|
||||
name: Rerun failed jobs
|
||||
if: >-
|
||||
github.event.workflow_run.conclusion == 'failure' &&
|
||||
github.event.workflow_run.run_attempt == 1 &&
|
||||
(
|
||||
github.event.workflow_run.head_branch == 'main' ||
|
||||
startsWith(github.event.workflow_run.head_branch, 'releases/') ||
|
||||
github.event.workflow_run.event == 'merge_group'
|
||||
)
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Rerun failed jobs in ${{ github.event.workflow_run.name }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
RUN_NAME: ${{ github.event.workflow_run.name }}
|
||||
RUN_URL: ${{ github.event.workflow_run.html_url }}
|
||||
run: |
|
||||
echo "Rerunning failed jobs for workflow run ${RUN_ID}"
|
||||
gh run rerun "${RUN_ID}" --failed
|
||||
echo "### Reran failed jobs :recycle:" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Workflow: [${RUN_NAME}](${RUN_URL})" >> "$GITHUB_STEP_SUMMARY"
|
||||
@@ -24,7 +24,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
merge-back:
|
||||
runs-on: ubuntu-slim
|
||||
runs-on: ubuntu-latest
|
||||
environment: Automation
|
||||
if: github.repository == 'github/codeql-action'
|
||||
env:
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Generate token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.AUTOMATION_APP_ID }}
|
||||
|
||||
@@ -29,7 +29,7 @@ defaults:
|
||||
jobs:
|
||||
prepare:
|
||||
name: "Prepare release"
|
||||
runs-on: ubuntu-slim
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'github/codeql-action'
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
|
||||
- name: Generate token
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.AUTOMATION_APP_ID }}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Update the required checks based on the current branch.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
REPO_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
GRANDPARENT_DIR="$(dirname "$REPO_DIR")"
|
||||
source "$GRANDPARENT_DIR/releases.ini"
|
||||
|
||||
if ! gh auth status 2>/dev/null; then
|
||||
gh auth status
|
||||
echo "Failed: Not authorized. This script requires admin access to github/codeql-action through the gh CLI."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -eq 1 ]; then
|
||||
# If we were passed an argument, use that as the SHA
|
||||
GITHUB_SHA="$1"
|
||||
elif [ "$#" -gt 1 ]; then
|
||||
echo "Usage: $0 [SHA]"
|
||||
echo "Update the required checks based on the SHA, or main."
|
||||
exit 1
|
||||
elif [ -z "$GITHUB_SHA" ]; then
|
||||
# If we don't have a SHA, use main
|
||||
GITHUB_SHA="$(git rev-parse main)"
|
||||
fi
|
||||
|
||||
echo "Getting checks for $GITHUB_SHA"
|
||||
|
||||
# Ignore any checks with "https://", CodeQL, LGTM, Update, and ESLint checks.
|
||||
CHECKS="$(gh api repos/github/codeql-action/commits/"${GITHUB_SHA}"/check-runs --paginate | jq --slurp --compact-output --raw-output '[.[].check_runs.[] | select(.conclusion != "skipped") | .name | select(contains("https://") or . == "CodeQL" or . == "Dependabot" or . == "check-expected-release-files" or contains("Update") or contains("ESLint") or contains("update") or contains("test-setup-python-scripts") or . == "Agent" or . == "Cleanup artifacts" or . == "Prepare" or . == "Upload results" or . == "Label PR with size" | not)] | unique | sort')"
|
||||
|
||||
echo "$CHECKS" | jq
|
||||
|
||||
# Fail if there are no checks
|
||||
if [ -z "$CHECKS" ] || [ "$(echo "$CHECKS" | jq '. | length')" -eq 0 ]; then
|
||||
echo "No checks found for $GITHUB_SHA"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "{\"contexts\": ${CHECKS}}" > checks.json
|
||||
|
||||
echo "Updating main"
|
||||
gh api --silent -X "PATCH" "repos/github/codeql-action/branches/main/protection/required_status_checks" --input checks.json
|
||||
|
||||
# list all branchs on origin remote matching releases/v*
|
||||
BRANCHES="$(git ls-remote --heads origin 'releases/v*' | sed 's?.*refs/heads/??' | sort -V)"
|
||||
|
||||
for BRANCH in $BRANCHES; do
|
||||
|
||||
# strip exact 'releases/v' prefix from $BRANCH using count of characters
|
||||
VERSION="${BRANCH:10}"
|
||||
|
||||
if [ "$VERSION" -lt "$OLDEST_SUPPORTED_MAJOR_VERSION" ]; then
|
||||
echo "Skipping $BRANCH"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Updating $BRANCH"
|
||||
gh api --silent -X "PATCH" "repos/github/codeql-action/branches/$BRANCH/protection/required_status_checks" --input checks.json
|
||||
done
|
||||
|
||||
rm checks.json
|
||||
@@ -20,7 +20,7 @@ defaults:
|
||||
jobs:
|
||||
update-bundle:
|
||||
if: github.event.release.prerelease && startsWith(github.event.release.tag_name, 'codeql-bundle-')
|
||||
runs-on: ubuntu-slim
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # needed to push commits
|
||||
pull-requests: write # needed to create pull requests
|
||||
@@ -57,6 +57,17 @@ jobs:
|
||||
- name: Update bundle
|
||||
uses: ./.github/actions/update-bundle
|
||||
|
||||
- name: Set up CodeQL CLI from new bundle
|
||||
id: setup-codeql
|
||||
uses: ./setup-codeql
|
||||
with:
|
||||
tools: https://github.com/github/codeql-action/releases/download/${{ github.event.release.tag_name }}/codeql-bundle-linux64.tar.gz
|
||||
|
||||
- name: Update built-in languages
|
||||
run: npx tsx pr-checks/update-builtin-languages.ts "$CODEQL_PATH"
|
||||
env:
|
||||
CODEQL_PATH: ${{ steps.setup-codeql.outputs.codeql-path }}
|
||||
|
||||
- name: Bump Action minor version if new CodeQL minor version series
|
||||
id: bump-action-version
|
||||
run: |
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
update:
|
||||
timeout-minutes: 45
|
||||
runs-on: ubuntu-slim
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
needs: [prepare]
|
||||
env:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
|
||||
backport:
|
||||
timeout-minutes: 45
|
||||
runs-on: ubuntu-slim
|
||||
runs-on: ubuntu-latest
|
||||
environment: Automation
|
||||
needs: [prepare]
|
||||
if: ${{ (github.event_name == 'push') && needs.prepare.outputs.backport_target_branches != '[]' }}
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
pull-requests: write # needed to create pull request
|
||||
steps:
|
||||
- name: Generate token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.AUTOMATION_APP_ID }}
|
||||
|
||||
@@ -11,3 +11,5 @@ build/
|
||||
eslint.sarif
|
||||
# for local incremental compilation
|
||||
tsconfig.tsbuildinfo
|
||||
# esbuild metadata file
|
||||
meta.json
|
||||
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
// Place your codeql-action workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
// "Print to console": {
|
||||
// "scope": "javascript,typescript",
|
||||
// "prefix": "log",
|
||||
// "body": [
|
||||
// "console.log('$1');",
|
||||
// "$2"
|
||||
// ],
|
||||
// "description": "Log output to console"
|
||||
// }
|
||||
"Test Macro": {
|
||||
"scope": "javascript, typescript",
|
||||
"prefix": "testMacro",
|
||||
"body": [
|
||||
"const ${1:nameMacro} = makeMacro({",
|
||||
" exec: async (t: ExecutionContext<unknown>) => {},",
|
||||
"",
|
||||
" title: (providedTitle = \"\") => `${2:common title} - \\${providedTitle}`,",
|
||||
"});",
|
||||
],
|
||||
"description": "An Ava test macro",
|
||||
},
|
||||
}
|
||||
@@ -2,6 +2,50 @@
|
||||
|
||||
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
- If multiple inputs are provided for the GitHub-internal `analysis-kinds` input, only `code-scanning` will be enabled. The `analysis-kinds` input is experimental, for GitHub-internal use only, and may change without notice at any time. [#3892](https://github.com/github/codeql-action/pull/3892)
|
||||
- Added an experimental change which, when running a Code Scanning analysis for a PR with [improved incremental analysis](https://github.com/github/roadmap/issues/1158) enabled, prefers CodeQL CLI versions that have a cached overlay-base database for the configured languages. This speeds up analysis for a repository when there is not yet a cached overlay-base database for the latest CLI version. We expect to roll this change out to everyone in May. [#3880](https://github.com/github/codeql-action/pull/3880)
|
||||
|
||||
## 4.35.4 - 07 May 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.25.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.4). [#3881](https://github.com/github/codeql-action/pull/3881)
|
||||
|
||||
## 4.35.3 - 01 May 2026
|
||||
|
||||
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.19.3 and earlier. These versions of CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise Server 3.15, and will be unsupported by the next minor release of the CodeQL Action. [#3837](https://github.com/github/codeql-action/pull/3837)
|
||||
- Configurations for private registries that use Cloudsmith or GCP OIDC are now accepted. [#3850](https://github.com/github/codeql-action/pull/3850)
|
||||
- Best-effort connection tests for private registries now use `GET` requests instead of `HEAD` for better compatibility with various registry implementations. For NuGet feeds, the test is now always performed against the service index. [#3853](https://github.com/github/codeql-action/pull/3853)
|
||||
- Fixed a bug where two diagnostics produced within the same millisecond could overwrite each other on disk, causing one of them to be lost. [#3852](https://github.com/github/codeql-action/pull/3852)
|
||||
- Update default CodeQL bundle version to [2.25.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.3). [#3865](https://github.com/github/codeql-action/pull/3865)
|
||||
|
||||
## 4.35.2 - 15 Apr 2026
|
||||
|
||||
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable is deprecated and will be removed in May 2026. If you are affected by this, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#3795](https://github.com/github/codeql-action/pull/3795)
|
||||
- The Git version 2.36.0 requirement for improved incremental analysis now only applies to repositories that contain submodules. [#3789](https://github.com/github/codeql-action/pull/3789)
|
||||
- Python analysis on GHES no longer extracts the standard library, relying instead on models of the standard library. This should result in significantly faster extraction and analysis times, while the effect on alerts should be minimal. [#3794](https://github.com/github/codeql-action/pull/3794)
|
||||
- Fixed a bug in the validation of OIDC configurations for private registries that was added in CodeQL Action 4.33.0 / 3.33.0. [#3807](https://github.com/github/codeql-action/pull/3807)
|
||||
- Update default CodeQL bundle version to [2.25.2](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.2). [#3823](https://github.com/github/codeql-action/pull/3823)
|
||||
|
||||
## 4.35.1 - 27 Mar 2026
|
||||
|
||||
- Fix incorrect minimum required Git version for [improved incremental analysis](https://github.com/github/roadmap/issues/1158): it should have been 2.36.0, not 2.11.0. [#3781](https://github.com/github/codeql-action/pull/3781)
|
||||
|
||||
## 4.35.0 - 27 Mar 2026
|
||||
|
||||
- Reduced the minimum Git version required for [improved incremental analysis](https://github.com/github/roadmap/issues/1158) from 2.38.0 to 2.11.0. [#3767](https://github.com/github/codeql-action/pull/3767)
|
||||
- Update default CodeQL bundle version to [2.25.1](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.1). [#3773](https://github.com/github/codeql-action/pull/3773)
|
||||
|
||||
## 4.34.1 - 20 Mar 2026
|
||||
|
||||
- Downgrade default CodeQL bundle version to [2.24.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.3) due to issues with a small percentage of Actions and JavaScript analyses. [#3762](https://github.com/github/codeql-action/pull/3762)
|
||||
|
||||
## 4.34.0 - 20 Mar 2026
|
||||
|
||||
- Added an experimental change which disables TRAP caching when [improved incremental analysis](https://github.com/github/roadmap/issues/1158) is enabled, since improved incremental analysis supersedes TRAP caching. This will improve performance and reduce Actions cache usage. We expect to roll this change out to everyone in March. [#3569](https://github.com/github/codeql-action/pull/3569)
|
||||
- We are rolling out improved incremental analysis to C/C++ analyses that use build mode `none`. We expect this rollout to be complete by the end of April 2026. [#3584](https://github.com/github/codeql-action/pull/3584)
|
||||
- Update default CodeQL bundle version to [2.25.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.0). [#3585](https://github.com/github/codeql-action/pull/3585)
|
||||
|
||||
## 4.33.0 - 16 Mar 2026
|
||||
|
||||
- Upcoming change: Starting April 2026, the CodeQL Action will skip collecting file coverage information on pull requests to improve analysis performance. File coverage information will still be computed on non-PR analyses. Pull request analyses will log a warning about this upcoming change. [#3562](https://github.com/github/codeql-action/pull/3562)
|
||||
|
||||
+7
-5
@@ -69,12 +69,14 @@ Once the mergeback and backport pull request have been merged, the release is co
|
||||
|
||||
## Keeping the PR checks up to date (admin access required)
|
||||
|
||||
Since the `codeql-action` runs most of its testing through individual Actions workflows, there are over two hundred required jobs that need to pass in order for a PR to turn green. It would be too tedious to maintain that list manually. You can regenerate the set of required checks automatically by running the [update-required-checks.sh](.github/workflows/script/update-required-checks.sh) script:
|
||||
Since the `codeql-action` runs most of its testing through individual Actions workflows, there are over two hundred required jobs that need to pass in order for a PR to turn green. It would be too tedious to maintain that list manually. You can regenerate the set of required checks automatically by running the [sync-checks.ts](pr-checks/sync-checks.ts) script:
|
||||
|
||||
- If you run the script without an argument, it will retrieve the set of workflows that ran for the latest commit on `main`. Make sure that your local `main` branch is up to date before running the script.
|
||||
- You can specify a commit SHA as argument to retrieve the set of workflows for that commit instead. You will likely want to use this if you have a PR that removes or adds PR checks.
|
||||
- At a minimum, you must provide an argument for the `--token` input. For example, `--token "$(gh auth token)"` to use the same token that `gh` uses. If no token is provided or the token has insufficient permissions, the script will fail.
|
||||
- By default, the script performs a dry run and outputs information about the changes it would make to the branch protection rules. To actually apply the changes, specify the `--apply` flag.
|
||||
- If you run the script without any other arguments, it will retrieve the set of workflows that ran for the latest commit on `main`.
|
||||
- You can specify a different git ref with the `--ref` input. You will likely want to use this if you have a PR that removes or adds PR checks. For example, `--ref "some/branch/name"` to use the HEAD of the `some/branch/name` branch.
|
||||
|
||||
After running, go to the [branch protection rules settings page](https://github.com/github/codeql-action/settings/branches) and validate that the rules for `main`, `v3`, and any other currently supported major versions have been updated.
|
||||
After running, go to the [branch protection rules settings page](https://github.com/github/codeql-action/settings/branches) and validate that the rules for `main`, `v4`, and any other currently supported major versions have been updated.
|
||||
|
||||
Note that any updates to checks on `main` need to be backported to all currently supported major version branches, in order to maintain the same set of names for required checks.
|
||||
|
||||
@@ -122,7 +124,7 @@ To deprecate an older version of the Action:
|
||||
- Implement an Actions warning for customers using the deprecated version.
|
||||
1. Wait for the deprecation period to pass.
|
||||
1. Upgrade the Actions warning for customers using the deprecated version to a non-fatal error, and mention that this version of the Action is no longer supported.
|
||||
1. Make a PR to bump the `OLDEST_SUPPORTED_MAJOR_VERSION` in [releases.ini](.github/releases.ini). Once this PR is merged, the release process will no longer backport changes to the deprecated release version.
|
||||
1. Make a PR to bump the `OLDEST_SUPPORTED_MAJOR_VERSION` in [config.ts](pr-checks/config.ts). Once this PR is merged, the release process will no longer backport changes to the deprecated release version.
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ We typically release new minor versions of the CodeQL Action and Bundle when a n
|
||||
|
||||
| Minimum CodeQL Action | Minimum CodeQL Bundle Version | GitHub Environment | Notes |
|
||||
|-----------------------|-------------------------------|--------------------|-------|
|
||||
| `v4.33.0` | `2.24.3` | Enterprise Server 3.21 | |
|
||||
| `v4.31.10` | `2.23.9` | Enterprise Server 3.20 | |
|
||||
| `v3.29.11` | `2.22.4` | Enterprise Server 3.19 | |
|
||||
| `v3.28.21` | `2.21.3` | Enterprise Server 3.18 | |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { copyFile, rm } from "node:fs/promises";
|
||||
import { copyFile, rm, writeFile } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -47,6 +47,27 @@ const copyDefaultsPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark `lib/` as an ESM scope by writing `lib/package.json` with
|
||||
* `{ "type": "module" }`. This lets the bundles use the regular `.js`
|
||||
* extension while still being loaded as ESM by Node, without affecting
|
||||
* the rest of the repo (the root package.json stays CJS so the tsc
|
||||
* output in `build/` and any other consumers are unchanged).
|
||||
*
|
||||
* @type {esbuild.Plugin}
|
||||
*/
|
||||
const writeLibPackageJsonPlugin = {
|
||||
name: "write-lib-package-json",
|
||||
setup(build) {
|
||||
build.onEnd(async () => {
|
||||
await writeFile(
|
||||
join(OUT_DIR, "package.json"),
|
||||
JSON.stringify({ type: "module" }) + "\n",
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Log when the build ends.
|
||||
*
|
||||
@@ -62,19 +83,53 @@ const onEndPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
// Banner injected into every emitted ESM file so that bundled CommonJS
|
||||
// dependencies which call `require(...)` at runtime (e.g. parts of the
|
||||
// Azure SDK + undici stack pulled in transitively by `@actions/cache` and
|
||||
// `@actions/artifact`), or read `__filename` / `__dirname`, keep working.
|
||||
const esmCompatBanner = [
|
||||
`import { createRequire as __codeqlCreateRequire } from "module";`,
|
||||
`import { fileURLToPath as __codeqlFileURLToPath } from "url";`,
|
||||
`import { dirname as __codeqlDirname } from "path";`,
|
||||
`var require = __codeqlCreateRequire(import.meta.url);`,
|
||||
`var __filename = __codeqlFileURLToPath(import.meta.url);`,
|
||||
`var __dirname = __codeqlDirname(__filename);`,
|
||||
].join("");
|
||||
|
||||
const context = await esbuild.context({
|
||||
// Include upload-lib.ts as an entry point for use in testing environments.
|
||||
entryPoints: globSync([`${SRC_DIR}/*-action.ts`, `${SRC_DIR}/*-action-post.ts`, "src/upload-lib.ts"]),
|
||||
entryPoints: globSync([
|
||||
`${SRC_DIR}/*-action.ts`,
|
||||
`${SRC_DIR}/*-action-post.ts`,
|
||||
"src/upload-lib.ts",
|
||||
]),
|
||||
bundle: true,
|
||||
format: "cjs",
|
||||
// Use ESM with code splitting so shared modules (Azure storage, undici,
|
||||
// octokit, ...) live in shared chunk files instead of being duplicated
|
||||
// into every entry bundle. Node treats these `.js` files as ESM because
|
||||
// `writeLibPackageJsonPlugin` writes `lib/package.json` with
|
||||
// `"type": "module"`.
|
||||
format: "esm",
|
||||
splitting: true,
|
||||
minify: true,
|
||||
chunkNames: "chunks/chunk-[hash]",
|
||||
banner: { js: esmCompatBanner },
|
||||
outdir: OUT_DIR,
|
||||
platform: "node",
|
||||
plugins: [cleanPlugin, copyDefaultsPlugin, onEndPlugin],
|
||||
plugins: [
|
||||
cleanPlugin,
|
||||
copyDefaultsPlugin,
|
||||
writeLibPackageJsonPlugin,
|
||||
onEndPlugin,
|
||||
],
|
||||
target: ["node20"],
|
||||
define: {
|
||||
__CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version),
|
||||
},
|
||||
metafile: true,
|
||||
});
|
||||
|
||||
await context.rebuild();
|
||||
const result = await context.rebuild();
|
||||
await writeFile(join(__dirname, "meta.json"), JSON.stringify(result.metafile));
|
||||
|
||||
await context.dispose();
|
||||
|
||||
+21
-6
@@ -7,7 +7,11 @@ import noAsyncForeach from "eslint-plugin-no-async-foreach";
|
||||
import jsdoc from "eslint-plugin-jsdoc";
|
||||
import tseslint from "typescript-eslint";
|
||||
import globals from "globals";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const githubFlatConfigs = github.getFlatConfigs();
|
||||
|
||||
export default [
|
||||
@@ -43,7 +47,7 @@ export default [
|
||||
plugins: {
|
||||
"import-x": importX,
|
||||
"no-async-foreach": fixupPluginRules(noAsyncForeach),
|
||||
"jsdoc": jsdoc,
|
||||
jsdoc: jsdoc,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
@@ -67,7 +71,13 @@ export default [
|
||||
|
||||
typescript: {},
|
||||
},
|
||||
"import/ignore": ["sinon", "uuid", "@octokit/plugin-retry", "del", "get-folder-size"],
|
||||
"import/ignore": [
|
||||
"sinon",
|
||||
"uuid",
|
||||
"@octokit/plugin-retry",
|
||||
"del",
|
||||
"get-folder-size",
|
||||
],
|
||||
"import-x/resolver-next": [
|
||||
createTypeScriptImportResolver(),
|
||||
createNodeResolver({
|
||||
@@ -143,7 +153,7 @@ export default [
|
||||
// We don't currently require full JSDoc coverage, so this rule
|
||||
// should not error on missing @param annotations.
|
||||
disableMissingParamChecks: true,
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -162,9 +172,9 @@ export default [
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"args": "all",
|
||||
"argsIgnorePattern": "^_",
|
||||
}
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"func-style": "off",
|
||||
},
|
||||
@@ -183,6 +193,11 @@ export default [
|
||||
// The scripts in `pr-checks` are expected to output to the console.
|
||||
"no-console": "off",
|
||||
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{ packageDir: [__dirname, path.resolve(__dirname, "pr-checks")] },
|
||||
],
|
||||
|
||||
"@typescript-eslint/no-floating-promises": [
|
||||
"error",
|
||||
{
|
||||
|
||||
Generated
+2
-163308
File diff suppressed because one or more lines are too long
Generated
+2
-113365
File diff suppressed because one or more lines are too long
Generated
+2
-105606
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{g as E}from"./chunk-LYJYPMC2.js";import{$b as v,Bb as L,Da as w,Fa as d,H as g,Ma as C,O as m,c as p,ec as P,fa as c,h as A,i as F,k as V,m as $,n as h,sa as b}from"./chunk-V6LGBXSF.js";var R=p(V()),k=p(F()),S=p($()),O=p(A());import*as n from"fs";import*as l from"path";async function ne(e,o,t,r,i,a,u,s,f){f.startGroup("Setup CodeQL tools");let{codeql:y,toolsDownloadStatusReport:D,toolsSource:I,toolsVersion:_,zstdAvailability:T}=await E(e,o,t,r,i,a,u,s,f,!0);return await y.printVersion(),f.endGroup(),{codeql:y,toolsDownloadStatusReport:D,toolsSource:I,toolsVersion:_,zstdAvailability:T}}async function ie(e,o){return await L("Load language configuration",async()=>await v(e,o))}async function ae(e,o,t,r,i,a,u){n.mkdirSync(t.dbLocation,{recursive:!0}),await P(e,async()=>await o.databaseInitCluster(t,r,i,a,u))}async function se(e,o,t){let r=(await e.getVersion()).overlayVersion;if(r===void 0)return t.warning("The CodeQL CLI does not support overlay analysis."),!1;for(let i of o.languages){let a=g(o,i);if((await e.resolveQueriesStartingPacks([a])).some(s=>!Q(s,r,t)))return!1}return!0}function Q(e,o,t){try{let r=l.join(e,"qlpack.yml");if(n.existsSync(r)||(r=l.join(e,"codeql-pack.yml")),!h(n.readFileSync(r,"utf8")).buildMetadata)return!0;let a=l.join(e,".packinfo");if(!n.existsSync(a))return t.warning(`The query pack at ${e} does not have a .packinfo file, so it cannot support overlay analysis. Recompiling the query pack with the latest CodeQL CLI should solve this problem.`),!1;let s=JSON.parse(n.readFileSync(a,"utf8")).overlayVersion;if(typeof s!="number")return t.warning(`The .packinfo file for the query pack at ${e} does not have the overlayVersion field, which indicates that the pack is not compatible with overlay analysis.`),!1;if(s!==o)return t.warning(`The query pack at ${e} was compiled with overlay version ${s}, but the CodeQL CLI supports overlay version ${o}. The query pack needs to be recompiled to support overlay analysis.`),!1}catch(r){return t.warning(`Error while checking pack at ${e} for overlay compatibility: ${c(r)}`),!1}return!0}async function le(e,o){if(e.includes("python")&&process.platform==="win32"&&!(await o.getVersion()).features?.supportsPython312){let t=l.resolve(__dirname,"../python-setup","check_python12.ps1");await new k.ToolRunner(await O.which("powershell",!0),[t]).exec()}}function ue(e,o,t={},r=n.rmSync){if(n.existsSync(e.dbLocation)&&(n.statSync(e.dbLocation).isFile()||n.readdirSync(e.dbLocation).length>0)){t.disableExistingDirectoryWarning||o.warning(`The database cluster directory ${e.dbLocation} must be empty. Attempting to clean it up.`);try{r(e.dbLocation,{force:!0,maxRetries:3,recursive:!0}),o.info(`Cleaned up database cluster directory ${e.dbLocation}.`)}catch(i){let a=`The CodeQL Action requires an empty database cluster directory. ${b("db-location")?`This is currently configured to be ${e.dbLocation}. `:`By default, this is located at ${e.dbLocation}. You can customize it using the 'db-location' input to the init Action. `}An attempt was made to clean up the directory, but this failed.`;throw w()?new m(`${a} This can happen if another process is using the directory or the directory is owned by a different user. Please clean up the directory manually and rerun the job. Details: ${c(i)}`):new Error(`${a} This shouldn't typically happen on hosted runners. If you are using an advanced setup, please check your workflow, otherwise we recommend rerunning the job. Details: ${c(i)}`)}}}async function pe(e,o,t,r){return e?{enabled:!0,enabledByRepositoryProperty:!1,showDeprecationWarning:!1}:C()?(process.env.CODEQL_ACTION_FILE_COVERAGE_ON_PRS||"").toLocaleLowerCase()==="true"?{enabled:!0,enabledByRepositoryProperty:!1,showDeprecationWarning:!1}:r["github-codeql-file-coverage-on-prs"]===!0?{enabled:!0,enabledByRepositoryProperty:!0,showDeprecationWarning:!1}:await t.getValue("skip_file_coverage_on_prs",o)?{enabled:!1,enabledByRepositoryProperty:!1,showDeprecationWarning:!1}:{enabled:!0,enabledByRepositoryProperty:!1,showDeprecationWarning:!0}:{enabled:!0,enabledByRepositoryProperty:!1,showDeprecationWarning:!1}}function ce(e){if(process.env.CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION)return;let o=S.context.payload.repository?.owner.type,t="Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests to improve analysis performance. File coverage information will still be computed on non-PR analyses.",r="set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`.",i='create a custom repository property with the name `github-codeql-file-coverage-on-prs` and the type "True/false", then set this property to `true` in the repository\'s settings.';o==="Organization"?d()?t+=`
|
||||
|
||||
To opt out of this change, ${i}`:t+=`
|
||||
|
||||
To opt out of this change, ${r} Alternatively, ${i}`:d()?t+=`
|
||||
|
||||
To opt out of this change, switch to an advanced setup workflow and ${r}`:t+=`
|
||||
|
||||
To opt out of this change, ${r}`,e.warning(t),R.exportVariable("CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION","true")}export{ne as a,ie as b,ae as c,se as d,le as e,ue as f,pe as g,ce as h};
|
||||
@@ -0,0 +1,2 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{g as n,k as p,l as d,n as f}from"./chunk-XFYKKQKY.js";import{oa as l,rb as e}from"./chunk-V6LGBXSF.js";async function w(s,u,m,i,c,y,P){let b=await n(s,c),o={};for(let[t,g]of l(b)){let a=e(t),r=await p(s,u,i,g,y,a);await d(s,P,a,r),m==="always"&&(o[t]=await f(s,i,a,r))}return o}export{w as a};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{b as Y}from"./chunk-U2JW7LOC.js";import{a as j,b as H}from"./chunk-HIJVM6IW.js";import{e as z}from"./chunk-LYJYPMC2.js";import{Bb as k,Db as U,Eb as q,G as b,H as Q,Lb as B,c as te,ea as A,fa as N,h as ne,hc as v,jc as G,n as x,nb as R,o as O,pb as F,ra as $,ta as M}from"./chunk-V6LGBXSF.js";var K=te(ne());import*as c from"fs";import*as p from"path";import{performance as D}from"perf_hooks";var T=class extends Error{constructor(t,a,r){super(a);this.queriesStatusReport=t;this.message=a;this.error=r;this.name="CodeQLAnalysisError"}queriesStatusReport;message;error};async function ae(n){let e=process.env.CODEQL_PYTHON;e===void 0||e.length===0||n.warning(`The CODEQL_PYTHON environment variable is no longer supported. Please remove it from your workflow. This environment variable was originally used to specify a Python executable that included the dependencies of your Python code, however Python analysis no longer uses these dependencies.
|
||||
If you used CODEQL_PYTHON to force the version of Python to analyze as, please use CODEQL_EXTRACTOR_PYTHON_ANALYSIS_VERSION instead, such as 'CODEQL_EXTRACTOR_PYTHON_ANALYSIS_VERSION=2.7' or 'CODEQL_EXTRACTOR_PYTHON_ANALYSIS_VERSION=3.11'.`)}async function re(n,e,t,a){for(let r of t.languages){if(X(t,r,a)){a.debug(`Database for ${r} has already been finalized, skipping extraction.`);continue}await ie(n,t,r)&&(a.startGroup(`Extracting ${r}`),r==="python"&&await ae(a),t.buildMode?(r==="cpp"&&t.buildMode==="autobuild"&&await Y(n,a),r==="java"&&t.buildMode==="none"&&(process.env.CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_DEPENDENCY_DIR=j()),r==="csharp"&&t.buildMode==="none"&&await e.getValue("csharp_cache_bmn")&&(process.env.CODEQL_EXTRACTOR_CSHARP_OPTION_BUILDLESS_DEPENDENCY_DIR=H()),await n.extractUsingBuildMode(t,r)):await n.extractScannedLanguage(t,r),a.endGroup())}}async function ie(n,e,t){return e.buildMode==="none"||e.buildMode==="autobuild"&&process.env.CODEQL_ACTION_AUTOBUILD_DID_COMPLETE_SUCCESSFULLY!=="true"||!e.buildMode&&await n.isScannedLanguage(t)}function X(n,e,t){let a=b(n,e);try{return!("inProgress"in x(c.readFileSync(p.resolve(a,"codeql-database.yml"),"utf8")))}catch{return t.warning(`Could not check whether database for ${e} was finalized. Assuming it is not.`),!1}}async function se(n,e,t,a,r,o){let i=D.now();await re(n,e,t,o);let d=D.now()-i,_=D.now();for(let m of t.languages)X(t,m,o)?o.info(`There is already a finalized database for ${m} at the location where the CodeQL Action places databases, so we did not create one.`):(o.startGroup(`Finalizing ${m}`),await n.finalizeDatabase(b(t,m),a,r,t.debugMode),o.endGroup());let l=D.now()-_;return{scanned_language_extraction_duration_ms:Math.round(d),trap_import_duration_ms:Math.round(l)}}async function Me(n){return await k("Generating diff range extension pack",async()=>{let e=B(n);if(e===void 0){n.info("No precomputed diff ranges found; skipping diff-informed analysis stage.");return}let t=$("checkout_path"),a=ue(n,e,t);return n.info(`Successfully created diff range extension pack at ${a}.`),a})}function oe(n,e){let t=`
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/util
|
||||
extensible: restrictAlertsTo
|
||||
checkPresence: false
|
||||
data:
|
||||
`,a=n.map(r=>{let o=p.join(e,r.path).replaceAll(p.sep,"/");return` - [${O(o,{forceQuotes:!0}).trim()}, ${r.startLine}, ${r.endLine}]
|
||||
`}).join("");return a||(a=` - ["", 0, 0]
|
||||
`),t+a}function ue(n,e,t){e.length===0&&(e=[{path:"",startLine:0,endLine:0}]);let a=p.join(M(),"pr-diff-range");c.mkdirSync(a,{recursive:!0}),c.writeFileSync(p.join(a,"qlpack.yml"),`
|
||||
name: codeql-action/pr-diff-range
|
||||
version: 0.0.0
|
||||
library: true
|
||||
extensionTargets:
|
||||
codeql/util: '*'
|
||||
dataExtensions:
|
||||
- pr-diff-range.yml
|
||||
`);let r=oe(e,t),o=p.join(a,"pr-diff-range.yml");return c.writeFileSync(o,r),n.debug(`Wrote pr-diff-range extension pack to ${o}:
|
||||
${r}`),a}var le=new Set(["security-experimental","security-extended","security-and-quality","code-quality","code-scanning"]);function V(n,e){return le.has(e)?`${n}-${e}.qls`:e}function de(n,e){return`${e}${n.sarifExtension}`}async function Fe(n,e,t,a,r,o,i,d,_){let l={},m=[e,t],L=[];i.overlayDatabaseMode!=="overlay-base"&&m.push("--expect-discarded-cache"),l.analysis_is_diff_informed=a!==void 0,a&&(m.push(`--additional-packs=${a}`),m.push("--extension-packs=codeql-action/pr-diff-range"),L.push("diff-informed")),l.analysis_is_overlay=i.overlayDatabaseMode==="overlay",l.analysis_builds_overlay_base_database=i.overlayDatabaseMode==="overlay-base",i.overlayDatabaseMode==="overlay"&&L.push("overlay");let J=L.length>0?`--sarif-run-property=incrementalMode=${L.join(",")}`:void 0,W=G(i);for(let s of i.languages)try{let u=[];if(i.analysisKinds.length>1&&(u.push(Q(i,s)),v(i)))for(let C of R)u.push(V(s,C));d.startGroup(`Running queries for ${s}`);let y=new Date().getTime(),h=b(i,s);await o.databaseRunQueries(h,m,u),d.debug(`Finished running queries for ${s}.`),l[`analyze_builtin_queries_${s}_duration_ms`]=new Date().getTime()-y;let g=new Date,{summary:f,sarifFile:I}=await S(W,s,void 0,i.debugMode),w;i.analysisKinds.length>1&&v(i)&&(w=(await S(F,s,R.map(E=>V(s,E)),i.debugMode)).summary);let P=new Date;if(l[`interpret_results_${s}_duration_ms`]=P.getTime()-g.getTime(),d.endGroup(),f.trim()&&d.info(f),w?.trim()&&d.info(w),i.enableFileCoverageInformation||d.info("To speed up pull request analysis, file coverage information is only enabled when analyzing the default branch and protected branches."),await _.getValue("qa_telemetry_enabled")){let C=ee(I),E={event:"codeql database interpret-results",started_at:g.toISOString(),completed_at:P.toISOString(),exit_status:"success",language:s,properties:{alertCounts:C}};l.event_reports===void 0&&(l.event_reports=[]),l.event_reports.push(E)}}catch(u){throw l.analyze_failure_language=s,new T(l,`Error running analysis for ${s}: ${N(u)}`,A(u))}return l;async function S(s,u,y,h){d.info(`Interpreting ${s.name} results for ${u}`);let g=s.fixCategory(d,r),f=p.join(n,de(s,u));return{summary:await Z(u,y,f,h,g),sarifFile:f}}async function Z(s,u,y,h,g){let f=b(i,s);return await o.databaseInterpretResults(f,u,y,t,h?"-vv":"-v",J,g,i,_)}function ee(s){let u=JSON.parse(c.readFileSync(s,"utf8")),y={};for(let h of u.runs)if(h.results)for(let g of h.results){let f=g.rule?.id||g.ruleId;f&&(y[f]=(y[f]||0)+1)}return y}}async function ke(n,e,t,a,r,o,i){try{await c.promises.rm(e,{force:!0,recursive:!0})}catch(_){if(_?.code!=="ENOENT")throw _}await c.promises.mkdir(e,{recursive:!0});let d=await se(r,n,o,t,a,i);return process.env.CODEQL_ACTION_AUTOBUILD_DID_COMPLETE_SUCCESSFULLY!=="true"&&await z(r,o,i),d}async function Ue(n,e){let t=process.env.CODEQL_ACTION_GO_BINARY;if(process.env.CODEQL_ACTION_DID_AUTOBUILD_GOLANG!=="true"&&t!==void 0){let a=await K.which("go",!0);t!==a&&(e.warning(`Expected \`which go\` to return ${t}, but got ${a}: please ensure that the correct version of Go is installed before the \`codeql-action/init\` Action is used.`),q(n,"go",U("go/workflow/go-installed-after-codeql-init","Go was installed after the `codeql-action/init` Action was run",{markdownMessage:"To avoid interfering with the CodeQL analysis, perform all installation steps before calling the `github/codeql-action/init` Action.",visibility:{statusPage:!0,telemetry:!0,cliSummaryTable:!0},severity:"warning"})))}}export{T as a,X as b,Me as c,Fe as d,ke as e,Ue as f};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{c as F,fa as m,j as k}from"./chunk-V6LGBXSF.js";var l=F(k());import*as d from"fs";import*as h from"os";import*as f from"path";var S={type:"Personal Access Token (Classic)",pattern:/\bghp_[a-zA-Z0-9]{36}\b/g},z={type:"Personal Access Token (Fine-grained)",pattern:/\bgithub_pat_[a-zA-Z0-9_]+\b/g},b=[S,z,{type:"OAuth Access Token",pattern:/\bgho_[a-zA-Z0-9]{36}\b/g},{type:"User-to-Server Token",pattern:/\bghu_[a-zA-Z0-9]{36}\b/g},{type:"Server-to-Server Token",pattern:/\bghs_[a-zA-Z0-9]{36}\b/g},{type:"Refresh Token",pattern:/\bghr_[a-zA-Z0-9]{36}\b/g},{type:"App Installation Access Token",pattern:/\bghs_[a-zA-Z0-9]{255}\b/g}];function R(e,a=b){for(let{type:t,pattern:s}of a)if(e.match(s))return t}function x(e,a,t){let s=[];try{let i=d.readFileSync(e,"utf8");for(let{type:u,pattern:r}of b){let o=i.match(r);if(o){for(let c=0;c<o.length;c++)s.push({tokenType:u,filePath:a});t.debug(`Found ${o.length} ${u}(s) in ${a}`)}}return s}catch(i){return t.debug(`Could not scan file ${e} for tokens: ${m(i)}`),[]}}async function $(e,a,t,s,i=0){if(i>10)throw new Error(`Maximum archive extraction depth (10) reached for ${e}`);if(process.platform==="win32")throw new Error("Scanning archives is not supported on Windows.");let r={scannedFiles:0,findings:[]};try{let o=d.mkdtempSync(f.join(t,`extract-${i}-`)),c=f.basename(e).toLowerCase();if(c.endsWith(".tar.gz")||c.endsWith(".tgz"))s.debug(`Extracting tar.gz file: ${e}`),await l.exec("tar",["-xzf",e,"-C",o],{silent:!0});else if(c.endsWith(".tar.zst"))s.debug(`Extracting tar.zst file: ${e}`),await l.exec("tar",["--zstd","-xf",e,"-C",o],{silent:!0});else if(c.endsWith(".zst")){s.debug(`Extracting zst file: ${e}`);let p=f.join(o,f.basename(e,".zst"));await l.exec("zstd",["-d",e,"-o",p],{silent:!0})}else if(c.endsWith(".gz")){s.debug(`Extracting gz file: ${e}`);let p=f.join(o,f.basename(e,".gz"));await l.exec("gunzip",["-c",e],{outStream:d.createWriteStream(p),silent:!0})}else c.endsWith(".zip")&&(s.debug(`Extracting zip file: ${e}`),await l.exec("unzip",["-q","-o",e,"-d",o],{silent:!0}));let n=await T(o,a,s,i+1);r.scannedFiles+=n.scannedFiles,r.findings.push(...n.findings),d.rmSync(o,{recursive:!0,force:!0})}catch(o){s.debug(`Could not extract or scan archive file ${e}: ${m(o)}`)}return r}async function A(e,a,t,s,i=0){let u={scannedFiles:1,findings:[]},r=f.basename(e).toLowerCase();if(r.endsWith(".zip")||r.endsWith(".tar.gz")||r.endsWith(".tgz")||r.endsWith(".tar.zst")||r.endsWith(".zst")||r.endsWith(".gz")){let n=await $(e,a,t,s,i);u.scannedFiles+=n.scannedFiles,u.findings.push(...n.findings)}let c=x(e,a,s);return u.findings.push(...c),u}async function T(e,a,t,s=0){let i={scannedFiles:0,findings:[]},u=d.readdirSync(e,{withFileTypes:!0});for(let r of u){let o=f.join(e,r.name),c=f.join(a,r.name);if(r.isDirectory()){let n=await T(o,c,t,s);i.scannedFiles+=n.scannedFiles,i.findings.push(...n.findings)}else if(r.isFile()){let n=await A(o,c,f.dirname(o),t,s);i.scannedFiles+=n.scannedFiles,i.findings.push(...n.findings)}}return i}async function _(e,a){a.info("Starting best-effort check for potential GitHub tokens in debug artifacts (for testing purposes only)...");let t={scannedFiles:0,findings:[]},s=d.mkdtempSync(f.join(h.tmpdir(),"artifact-scan-"));try{for(let n of e){let p=d.statSync(n),y=f.basename(n);if(p.isDirectory()){let g=await T(n,y,a);t.scannedFiles+=g.scannedFiles,t.findings.push(...g.findings)}else if(p.isFile()){let g=await A(n,y,s,a);t.scannedFiles+=g.scannedFiles,t.findings.push(...g.findings)}}let i=new Map,u=new Set;for(let n of t.findings)i.set(n.tokenType,(i.get(n.tokenType)||0)+1),u.add(n.filePath);let r=Array.from(i.entries()).map(([n,p])=>`${p} ${n}${p>1?"s":""}`).join(", "),o=`scanned ${t.scannedFiles} files, found ${t.findings.length} potential token(s) in ${u.size} file(s)`,c=r?`${o} (${r})`:o;if(a.info(`Artifact check complete: ${c}`),t.findings.length>0){let n=Array.from(u).join(", ");throw new Error(`Found ${t.findings.length} potential GitHub token(s) (${r}) in debug artifacts at: ${n}. This is a best-effort check for testing purposes only.`)}}finally{try{d.rmSync(s,{recursive:!0,force:!0})}catch(i){a.debug(`Could not clean up temporary scan directory: ${m(i)}`)}}}export{S as a,z as b,R as c,_ as d};
|
||||
@@ -0,0 +1,4 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{M as w,Pb as F,Vb as H,Ya as E,c as D,fa as b,ta as C,tb as k,wb as _}from"./chunk-V6LGBXSF.js";var h=D(H()),l=D(F());import*as f from"os";import{join as d}from"path";var x="codeql-dependencies",v=1;function Q(){return d(C(),"codeql_java","repository")}async function K(){return[d(f.homedir(),".m2","repository"),d(f.homedir(),".gradle","caches"),Q()]}function U(){return d(C(),"codeql_csharp","repository")}async function A(e,n){let r=[d(f.homedir(),".nuget","packages")];return await n.getValue("csharp_cache_bmn",e)&&r.push(U()),r}async function z(e){if((await(await I(e)).glob()).length!==0)return e}var T=["**/packages.lock.json","**/paket.lock"],j=["**/*.csproj","**/packages.config","**/nuget.config"];async function M(e,n){let r=await m.makePatternCheck(T);if(r!==void 0)return r;if(await n.getValue("csharp_new_cache_key",e))return m.makePatternCheck(j)}var R={java:{getDependencyPaths:K,getHashPatterns:async()=>m.makePatternCheck(["**/pom.xml","**/*.gradle*","**/gradle-wrapper.properties","buildSrc/**/Versions.kt","buildSrc/**/Dependencies.kt","gradle/*.versions.toml","**/versions.properties"])},csharp:{getDependencyPaths:A,getHashPatterns:M},go:{getDependencyPaths:async()=>[d(f.homedir(),"go","pkg","mod")],getHashPatterns:async()=>m.makePatternCheck(["**/go.sum"])}};async function I(e){return l.create(e.join(`
|
||||
`))}async function S(e,n,r,t,a,o){let s=await t.getHashPatterns(e,n);return s===void 0&&o.info(`Skipping ${a} of dependency cache for ${r} as we cannot calculate a hash for the cache key.`),s}async function oe(e,n,r,t){let a=[],o=[];for(let s of r){let p=R[s];if(p===void 0){t.info(`Skipping download of dependency cache for ${s} as we have no caching configuration for it.`);continue}let c=await S(e,n,s,p,"download",t);if(c===void 0){a.push({language:s,hit_kind:"no-hash"});continue}let u=await L(e,n,s,c),i=[await $(e,n,s)];t.info(`Downloading cache for ${s} with key ${u} and restore keys ${i.join(", ")}`);let y=performance.now(),g=await h.restoreCache(await p.getDependencyPaths(e,n),u,i),N=Math.round(performance.now()-y);if(g!==void 0){t.info(`Cache hit on key ${g} for ${s}.`);let P="partial";g===u&&(P="exact"),a.push({language:s,hit_kind:P,download_duration_ms:N}),o.push(g)}else a.push({language:s,hit_kind:"miss"}),t.info(`No suitable cache found for ${s}.`)}return{statusReport:a,restoredKeys:o}}async function re(e,n,r,t){let a=[];for(let o of r.languages){let s=R[o];if(s===void 0){t.info(`Skipping upload of dependency cache for ${o} as we have no caching configuration for it.`);continue}let p=await S(e,n,o,s,"upload",t);if(p===void 0){a.push({language:o,result:"no-hash"});continue}let c=await L(e,n,o,p);if(r.dependencyCachingRestoredKeys.includes(c)){a.push({language:o,result:"duplicate"});continue}let u=await k(await s.getDependencyPaths(e,n),t,!0);if(u===0){a.push({language:o,result:"empty"}),t.info(`Skipping upload of dependency cache for ${o} since it is empty.`);continue}t.info(`Uploading cache of size ${u} for ${o} with key ${c}...`);try{let i=performance.now();await h.saveCache(await s.getDependencyPaths(e,n),c);let y=Math.round(performance.now()-i);a.push({language:o,result:"stored",upload_size_bytes:Math.round(u),upload_duration_ms:y})}catch(i){if(i instanceof h.ReserveCacheError)t.info(`Not uploading cache for ${o}, because ${c} is already in use.`),t.debug(i.message),a.push({language:o,result:"duplicate"});else throw i}}return a}async function L(e,n,r,t){let a=await l.hashFiles(t.join(`
|
||||
`));return`${await $(e,n,r)}${a}`}async function V(e,n,r){let t=[],a=async o=>{await n.getValue(o,e)&&t.push(o)};return r==="csharp"&&(await a("csharp_new_cache_key"),await a("csharp_cache_bmn")),t.length>0?`${_(t)}-`:""}async function $(e,n,r){let t=w("RUNNER_OS"),a=process.env.CODEQL_ACTION_DEPENDENCY_CACHE_PREFIX,o=x;a!==void 0&&a.length>0&&(o=`${o}-${a}`);let s=await V(e,n,r);return`${o}-${s}${v}-${t}-${r}-`}async function se(e){try{let n=await E(x),r=n.reduce((t,a)=>t+(a.size_in_bytes??0),0);return{count:n.length,size_bytes:r}}catch(n){e.warning(`Unable to retrieve information about dependency cache usage: ${b(n)}`)}}var m={makePatternCheck:z};export{Q as a,U as b,oe as c,re as d,se as e};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{Ea as C,M as w,Ua as $,X as W,Z as E,c as P,k as L,n as d}from"./chunk-V6LGBXSF.js";var u=P(L());import*as c from"fs";import*as x from"path";import O from"zlib";function T(e){return Object.entries(e).reduce((r,[t,o])=>(r[t]={message:o,code:t},r),{})}var h=T({MissingPushHook:"Please specify an on.push hook to analyze and see code scanning alerts from the default branch on the Security tab.",CheckoutWrongHead:"git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.",InconsistentActionVersion:"Not all workflow steps that use `github/codeql-action` actions use the same version. Please ensure that all such steps use the same version to avoid compatibility issues."});async function j(e,r){let t=await r.betterResolveLanguages();if(!t.aliases)return;let o=t.aliases,n={};for(let s of e){let a=o[s]||s;n[a]||(n[a]=[]),n[a].push(s)}return n}async function S(e,r){let t=[],o=process.env.GITHUB_JOB;if(o){let i=e?.jobs?.[o];if(i?.strategy?.matrix?.language){let f=i.strategy.matrix.language;if(Array.isArray(f)){let y=await j(f,r);if(y!==void 0)for(let[v,b]of Object.entries(y))b.length>1&&t.push({message:`CodeQL language '${v}' is referenced by more than one entry in the 'language' matrix parameter for job '${o}'. This may result in duplicate alerts. Please edit the 'language' matrix parameter to keep only one of the following: ${b.map(A=>`'${A}'`).join(", ")}.`,code:"DuplicateLanguageInMatrix"})}}let g=i?.steps;if(Array.isArray(g)){for(let f of g)if(f?.run==="git checkout HEAD^2"){t.push(h.CheckoutWrongHead);break}}}let n=[];for(let i of Object.values(e?.jobs||{}))if(Array.isArray(i.steps)){for(let g of i.steps)if(g.uses?.startsWith("github/codeql-action/")){let f=g.uses.split("@");f.length>=2&&n.push(f[f.length-1])}}n.length>0&&!n.every(i=>i===n[0])&&t.push(h.InconsistentActionVersion);let s=p("push",e),a=p("pull_request",e),l=p("workflow_call",e);return a&&!s&&!l&&t.push(h.MissingPushHook),t}function p(e,r){return r.on?typeof r.on=="string"?r.on===e:Array.isArray(r.on)?r.on.includes(e):Object.prototype.hasOwnProperty.call(r.on,e):!1}async function I(e,r){let t;try{t=await q(r)}catch(n){return`error: getWorkflow() failed: ${String(n)}`}let o;try{o=await S(t,e)}catch(n){return`error: getWorkflowErrors() failed: ${String(n)}`}if(o.length>0){let n;try{n=_(o)}catch(s){return`error: formatWorkflowErrors() failed: ${String(s)}`}u.warning(n)}return R(o)}function _(e){let r=e.length===1?"issue was":"issues were",t=e.map(o=>o.message).join(" ");return`${e.length} ${r} detected with this workflow: ${t}`}function R(e){if(e.length!==0)return e.map(r=>r.code).join(",")}async function q(e){let r=process.env.CODE_SCANNING_WORKFLOW_FILE;if(r)return e.debug("Using the workflow specified by the CODE_SCANNING_WORKFLOW_FILE environment variable."),d(O.gunzipSync(Buffer.from(r,"base64")).toString());let t=await D(e);return d(c.readFileSync(t,"utf-8"))}async function D(e){let r=await $(),t=x.join(w("GITHUB_WORKSPACE"),r);if(c.existsSync(t))return e.debug(`Derived the following absolute path for the currently executing workflow: ${t}.`),t;throw new Error(`Expected to find a code scanning workflow file at ${t}, but no such file existed. This can happen if the currently running workflow checks out a branch that doesn't contain the corresponding workflow file.`)}function H(e,r){if(e.uses)throw new Error(`Could not get steps calling ${r} since the job calls a reusable workflow.`);let t=e.steps;if(!Array.isArray(t))throw new Error(`Could not get steps calling ${r} since job.steps was not an array.`);return t.filter(o=>o.uses?.includes(r))}function k(e,r,t,o,n){let s=`Could not get ${o} input to ${t} since`;if(!e.jobs)throw new Error(`${s} the workflow has no jobs.`);if(!e.jobs[r])throw new Error(`${s} the workflow has no job named ${r}.`);let a=H(e.jobs[r],t);if(a.length===0)throw new Error(`${s} the ${r} job does not call ${t}.`);if(a.length>1)throw new Error(`${s} the ${r} job calls ${t} multiple times.`);let l=a[0].with?.[o]?.toString();if(l!==void 0&&n!==void 0){l=l.replace(/\${{\s+/,"${{").replace(/\s+}}/,"}}");for(let[i,g]of Object.entries(n))l=l.replace(`\${{matrix.${i}}}`,g)}if(l?.includes("${{"))throw new Error(`Could not get ${o} input to ${t} since it contained an unrecognized dynamic value.`);return l}function m(){return W()||E()==="codeql-action-pr-checks"?"./analyze":"github/codeql-action/analyze"}function M(e,r,t){return k(e,r,m(),"category",t)}function V(e,r,t){return k(e,r,m(),"upload",t)}function X(e,r,t){return k(e,r,m(),"checkout_path",t)||w("GITHUB_WORKSPACE")}async function Y(e,r){if(!C()&&process.env.CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION!=="true"){u.startGroup("Validating workflow");let t=await z.validateWorkflow(r,e);t===void 0?e.info("Detected no issues with the code scanning workflow."):e.debug(`Unable to validate code scanning workflow: ${t}`),u.endGroup()}}var z={validateWorkflow:I};export{q as a,M as b,V as c,X as d,Y as e};
|
||||
@@ -0,0 +1,2 @@
|
||||
import { createRequire as __codeqlCreateRequire } from "module";import { fileURLToPath as __codeqlFileURLToPath } from "url";import { dirname as __codeqlDirname } from "path";var require = __codeqlCreateRequire(import.meta.url);var __filename = __codeqlFileURLToPath(import.meta.url);var __dirname = __codeqlDirname(__filename);
|
||||
import{h as b}from"./chunk-LYJYPMC2.js";import{Oa as f,Ta as m,c as g,k as L,kb as p,la as s,lb as c,ta as l,va as d}from"./chunk-V6LGBXSF.js";var r=g(L());async function B(i,e,o){if(e.buildMode==="none"||e.buildMode==="manual"){o.info(`Using build mode "${e.buildMode}", nothing to autobuild. See https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#codeql-build-modes for more information.`);return}let t=await s(e.languages,async u=>await i.isTracedLanguage(u));if(t.length===0){o.info("None of the languages in this project require extra build steps");return}let n=t.filter(u=>u!=="go"),a=[];return n[0]!==void 0&&a.push(n[0]),t.length!==n.length&&a.push("go"),o.debug(`Will autobuild ${a.join(" and ")}.`),n.length>1&&o.warning(`We will only automatically build ${a.join(" and ")} code. If you wish to scan ${n.slice(1).join(" and ")}, you must replace the autobuild step of your workflow with custom build steps. See https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#about-specifying-build-steps-manually for more information.`),a}async function w(i,e){let o=p.cpp_dependency_installation_enabled.envVar,t="C++ automatic installation of dependencies",n=await m(),a=f();await c(n,a,l(),e).getValue("cpp_dependency_installation_enabled",i)?process.env.RUNNER_ENVIRONMENT==="self-hosted"&&process.env[o]!=="true"?(e.info(`Disabling ${t} as we are on a self-hosted runner.${d()!=="dynamic"?` To override this, set the ${o} environment variable to 'true' in your workflow. See https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow for more information.`:""}`),r.exportVariable(o,"false")):(e.info(`Enabling ${t}. This can be disabled by setting the ${o} environment variable to 'false'. See https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow for more information.`),r.exportVariable(o,"true")):(e.info(`Disabling ${t}.`),r.exportVariable(o,"false"))}async function Q(i,e,o){o.startGroup(`Attempting to automatically build ${e} code`);let t=await b(i.codeQLCmd);e==="cpp"&&await w(t,o),i.buildMode?await t.extractUsingBuildMode(i,e):await t.runAutobuild(i,e),e==="go"&&r.exportVariable("CODEQL_ACTION_DID_AUTOBUILD_GOLANG","true"),o.endGroup()}export{B as a,w as b,Q as c};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.24.3",
|
||||
"cliVersion": "2.24.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.24.2",
|
||||
"priorCliVersion": "2.24.2"
|
||||
"bundleVersion": "codeql-bundle-v2.25.4",
|
||||
"cliVersion": "2.25.4",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.3",
|
||||
"priorCliVersion": "2.25.3"
|
||||
}
|
||||
|
||||
Generated
+2
-170884
File diff suppressed because one or more lines are too long
Generated
+3
-110064
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"module"}
|
||||
Generated
+2
-105169
File diff suppressed because one or more lines are too long
Generated
+2
-106675
File diff suppressed because one or more lines are too long
Generated
+2
-162195
File diff suppressed because one or more lines are too long
Generated
+26
-122395
File diff suppressed because one or more lines are too long
Generated
+2
-110885
File diff suppressed because one or more lines are too long
Generated
+2
-162218
File diff suppressed because one or more lines are too long
Generated
+2
-111568
File diff suppressed because one or more lines are too long
Generated
+1009
-504
File diff suppressed because it is too large
Load Diff
+23
-24
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.33.0",
|
||||
"version": "4.35.5",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
@@ -12,7 +12,7 @@
|
||||
"ava": "npm run transpile && ava --verbose",
|
||||
"test": "npm run ava -- src/",
|
||||
"test-debug": "npm run test -- --timeout=20m",
|
||||
"transpile": "tsc --build --verbose"
|
||||
"transpile": "tsc --build --verbose tsconfig.json"
|
||||
},
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -29,47 +29,46 @@
|
||||
"@actions/http-client": "^3.0.0",
|
||||
"@actions/io": "^2.0.0",
|
||||
"@actions/tool-cache": "^3.0.1",
|
||||
"@octokit/plugin-retry": "^8.0.0",
|
||||
"@schemastore/package": "0.0.10",
|
||||
"@octokit/plugin-retry": "^8.1.0",
|
||||
"archiver": "^7.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"follow-redirects": "^1.15.11",
|
||||
"follow-redirects": "^1.16.0",
|
||||
"get-folder-size": "^5.0.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jsonschema": "1.4.1",
|
||||
"jsonschema": "1.5.0",
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.3.3",
|
||||
"node-forge": "^1.4.0",
|
||||
"semver": "^7.7.4",
|
||||
"uuid": "^13.0.0"
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@eslint/compat": "^2.0.2",
|
||||
"@ava/typescript": "7.0.0",
|
||||
"@eslint/compat": "^2.0.5",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"@types/archiver": "^7.0.0",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.19.9",
|
||||
"@types/node": "^20.19.39",
|
||||
"@types/node-forge": "^1.3.14",
|
||||
"@types/sarif": "^2.1.7",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/sinon": "^21.0.0",
|
||||
"@types/sinon": "^21.0.1",
|
||||
"ava": "^7.0.0",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-import-resolver-typescript": "^3.8.7",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-github": "^6.0.0",
|
||||
"eslint-plugin-import-x": "^4.16.1",
|
||||
"eslint-plugin-jsdoc": "^62.7.1",
|
||||
"eslint-plugin-import-x": "^4.16.2",
|
||||
"eslint-plugin-jsdoc": "^62.9.0",
|
||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||
"glob": "^11.1.0",
|
||||
"globals": "^17.4.0",
|
||||
"nock": "^14.0.11",
|
||||
"sinon": "^21.0.2",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.56.1"
|
||||
"globals": "^17.6.0",
|
||||
"nock": "^14.0.12",
|
||||
"sinon": "^21.1.2",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.59.2"
|
||||
},
|
||||
"overrides": {
|
||||
"@actions/tool-cache": {
|
||||
@@ -90,7 +89,7 @@
|
||||
"eslint-plugin-jsx-a11y": {
|
||||
"semver": ">=6.3.1"
|
||||
},
|
||||
"brace-expansion@2.0.1": "2.0.2",
|
||||
"glob": "^11.1.0"
|
||||
"glob": "^11.1.0",
|
||||
"undici": "^6.24.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import * as githubUtils from "@actions/github/lib/utils";
|
||||
import { type Octokit } from "@octokit/core";
|
||||
import { type PaginateInterface } from "@octokit/plugin-paginate-rest";
|
||||
import { type Api } from "@octokit/plugin-rest-endpoint-methods";
|
||||
|
||||
/** The type of the Octokit client. */
|
||||
export type ApiClient = Octokit & Api & { paginate: PaginateInterface };
|
||||
|
||||
/** Constructs an `ApiClient` using `token` for authentication. */
|
||||
export function getApiClient(token: string): ApiClient {
|
||||
const opts = githubUtils.getOctokitOptions(token);
|
||||
return new githubUtils.GitHub(opts);
|
||||
}
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
import * as fs from "node:fs/promises";
|
||||
|
||||
import { BUNDLE_METADATA_FILE } from "./config";
|
||||
|
||||
interface InputInfo {
|
||||
bytesInOutput: number;
|
||||
}
|
||||
|
||||
type Inputs = Record<string, InputInfo>;
|
||||
|
||||
interface Output {
|
||||
bytes: number;
|
||||
inputs: Inputs;
|
||||
}
|
||||
|
||||
interface Metadata {
|
||||
outputs: Record<string, Output>;
|
||||
}
|
||||
|
||||
function toMB(bytes: number): string {
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)}MB`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const fileContents = await fs.readFile(BUNDLE_METADATA_FILE);
|
||||
const metadata = JSON.parse(String(fileContents)) as Metadata;
|
||||
|
||||
for (const [outputFile, outputData] of Object.entries(
|
||||
metadata.outputs,
|
||||
).reverse()) {
|
||||
console.info(`${outputFile}: ${toMB(outputData.bytes)}`);
|
||||
|
||||
for (const [inputName, inputData] of Object.entries(outputData.inputs)) {
|
||||
// Ignore any inputs that make up less than 5% of the output.
|
||||
const percentage = (inputData.bytesInOutput / outputData.bytes) * 100.0;
|
||||
if (percentage < 5.0) continue;
|
||||
|
||||
console.info(` ${inputName}: ${toMB(inputData.bytesInOutput)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only call `main` if this script was run directly.
|
||||
if (require.main === module) {
|
||||
void main();
|
||||
}
|
||||
@@ -5,12 +5,12 @@ versions:
|
||||
steps:
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: C#,java-kotlin,swift,typescript
|
||||
languages: C#,java-kotlin,typescript
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
|
||||
- name: "Check languages"
|
||||
run: |
|
||||
expected_languages="csharp,java,swift,javascript"
|
||||
expected_languages="csharp,java,javascript"
|
||||
actual_languages=$(jq -r '.languages | join(",")' "$RUNNER_TEMP"/config)
|
||||
|
||||
if [ "$expected_languages" != "$actual_languages" ]; then
|
||||
|
||||
@@ -5,7 +5,7 @@ versions:
|
||||
- default
|
||||
steps:
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
|
||||
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
@@ -16,7 +16,17 @@ steps:
|
||||
id: proxy
|
||||
uses: ./../action/start-proxy
|
||||
with:
|
||||
registry_secrets: '[{ "type": "nuget_feed", "url": "https://api.nuget.org/v3/index.json" }]'
|
||||
registry_secrets: |
|
||||
[
|
||||
{
|
||||
"type": "maven_repository",
|
||||
"url": "https://repo.maven.apache.org/maven2/"
|
||||
},
|
||||
{
|
||||
"type": "maven_repository",
|
||||
"url": "https://repo1.maven.org/maven2"
|
||||
}
|
||||
]
|
||||
|
||||
- name: Print proxy outputs
|
||||
run: |
|
||||
@@ -27,3 +37,10 @@ steps:
|
||||
- name: Fail if proxy outputs are not set
|
||||
if: (!steps.proxy.outputs.proxy_host) || (!steps.proxy.outputs.proxy_port) || (!steps.proxy.outputs.proxy_ca_certificate) || (!steps.proxy.outputs.proxy_urls)
|
||||
run: exit 1
|
||||
|
||||
- name: Fail if proxy_urls does not contain all registries
|
||||
if: |
|
||||
join(fromJSON(steps.proxy.outputs.proxy_urls)[*].type, ',') != 'maven_repository,maven_repository'
|
||||
|| !contains(steps.proxy.outputs.proxy_urls, 'https://repo.maven.apache.org/maven2/')
|
||||
|| !contains(steps.proxy.outputs.proxy_urls, 'https://repo1.maven.org/maven2')
|
||||
run: exit 1
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import path from "path";
|
||||
|
||||
/** The oldest supported major version of the CodeQL Action. */
|
||||
export const OLDEST_SUPPORTED_MAJOR_VERSION = 3;
|
||||
|
||||
/** The `pr-checks` directory. */
|
||||
export const PR_CHECKS_DIR = __dirname;
|
||||
|
||||
/** The path of the file configuring which checks shouldn't be required. */
|
||||
export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml");
|
||||
|
||||
/** The path to the esbuild metadata file. */
|
||||
export const BUNDLE_METADATA_FILE = path.join(PR_CHECKS_DIR, "..", "meta.json");
|
||||
|
||||
/** The `src` directory. */
|
||||
const SOURCE_ROOT = path.join(PR_CHECKS_DIR, "..", "src");
|
||||
|
||||
/** The path to the built-in languages file. */
|
||||
export const BUILTIN_LANGUAGES_FILE = path.join(
|
||||
SOURCE_ROOT,
|
||||
"languages",
|
||||
"builtin.json",
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
# PR checks to exclude from required checks
|
||||
contains:
|
||||
- "https://"
|
||||
- "Update"
|
||||
- "ESLint"
|
||||
- "update"
|
||||
- "test-setup-python-scripts"
|
||||
is:
|
||||
- "CodeQL"
|
||||
- "Dependabot"
|
||||
- "check-expected-release-files"
|
||||
- "Agent"
|
||||
- "Cleanup artifacts"
|
||||
- "Prepare"
|
||||
- "Upload results"
|
||||
- "Label PR with size"
|
||||
@@ -2,11 +2,15 @@
|
||||
"private": true,
|
||||
"description": "Dependencies for the sync.ts",
|
||||
"dependencies": {
|
||||
"yaml": "^2.8.2"
|
||||
"@actions/core": "^2.0.3",
|
||||
"@actions/github": "^8.0.1",
|
||||
"@octokit/core": "^7.0.6",
|
||||
"@octokit/plugin-paginate-rest": ">=9.2.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
||||
"yaml": "^2.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.9",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
"@types/node": "^20.19.39",
|
||||
"tsx": "^4.21.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/*
|
||||
Tests for the release-branches.ts script
|
||||
*/
|
||||
|
||||
import * as assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import { computeBackportBranches } from "./release-branches";
|
||||
|
||||
describe("computeBackportBranches", async () => {
|
||||
await it("rejects invalid major versions", () => {
|
||||
// The majorVersion is expected to be in vN format.
|
||||
assert.throws(() => computeBackportBranches("3", "v4.28.0", 3));
|
||||
assert.throws(() => computeBackportBranches("v3.1", "v4.28.0", 3));
|
||||
});
|
||||
|
||||
await it("rejects invalid latest tags", () => {
|
||||
// The latestTag is expected to be in vN.M.P format.
|
||||
assert.throws(() => computeBackportBranches("v3", "v4", 3));
|
||||
assert.throws(() => computeBackportBranches("v3", "4", 3));
|
||||
assert.throws(() => computeBackportBranches("v3", "v4.28", 3));
|
||||
assert.throws(() => computeBackportBranches("v3", "4.28", 3));
|
||||
assert.throws(() => computeBackportBranches("v3", "4.28.0", 3));
|
||||
});
|
||||
|
||||
await it("sets backport source branch based on major version", () => {
|
||||
// Test that the backport source branch is releases/v{majorVersion}
|
||||
const result = computeBackportBranches("v3", "v4.28.0", 3);
|
||||
assert.equal(result.backportSourceBranch, "releases/v3");
|
||||
});
|
||||
|
||||
await it("no backport targets when major version is the oldest supported", () => {
|
||||
// When majorVersion equals the major version of latestTag and we do not support older major versions,
|
||||
// then there are no older supported branches to backport to.
|
||||
const result = computeBackportBranches("v3", "v3.28.0", 3);
|
||||
assert.deepEqual(result.backportTargetBranches, []);
|
||||
});
|
||||
|
||||
await it("backports to older supported major versions", () => {
|
||||
const result = computeBackportBranches("v4", "v4.1.0", 3);
|
||||
assert.equal(result.backportSourceBranch, "releases/v4");
|
||||
assert.deepEqual(result.backportTargetBranches, ["releases/v3"]);
|
||||
});
|
||||
|
||||
await it("backports to multiple older supported branches", () => {
|
||||
const result = computeBackportBranches("v5", "v5.0.0", 3);
|
||||
assert.equal(result.backportSourceBranch, "releases/v5");
|
||||
assert.deepEqual(result.backportTargetBranches, [
|
||||
"releases/v4",
|
||||
"releases/v3",
|
||||
]);
|
||||
});
|
||||
|
||||
await it("does not backport when major version is older than latest tag", () => {
|
||||
const result = computeBackportBranches("v2", "v3.28.0", 2);
|
||||
assert.equal(result.backportSourceBranch, "releases/v2");
|
||||
assert.deepEqual(result.backportTargetBranches, []);
|
||||
});
|
||||
});
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import { OLDEST_SUPPORTED_MAJOR_VERSION } from "./config";
|
||||
|
||||
/** The results of checking which release branches to backport to. */
|
||||
export interface BackportInfo {
|
||||
/** The source release branch. */
|
||||
backportSourceBranch: string;
|
||||
/**
|
||||
* The computed release branches we should backport to.
|
||||
* Will be empty if there are no branches we need to backport to.
|
||||
*/
|
||||
backportTargetBranches: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the backport source and target branches for a release.
|
||||
*
|
||||
* @param majorVersion - The major version string (e.g. "v4").
|
||||
* @param latestTag - The most recent tag published to the repository (e.g. "v4.32.6").
|
||||
* @param oldestSupportedMajorVersion - The oldest supported major version number.
|
||||
* @returns The names of the source branch and target branches.
|
||||
*/
|
||||
export function computeBackportBranches(
|
||||
majorVersion: string,
|
||||
latestTag: string,
|
||||
oldestSupportedMajorVersion: number,
|
||||
): BackportInfo {
|
||||
// Perform some sanity checks on the inputs.
|
||||
// For `majorVersion`, we expect exactly `vN` for some `N`.
|
||||
const majorVersionMatch = majorVersion.match(/^v(\d+)$/);
|
||||
if (!majorVersionMatch) {
|
||||
throw new Error("--major-version value must be in `vN` format.");
|
||||
}
|
||||
|
||||
// For latestTag, we expect something starting with `vN.M.P`
|
||||
const latestTagMatch = latestTag.match(/^v(\d+)\.\d+\.\d+/);
|
||||
if (!latestTagMatch) {
|
||||
throw new Error(
|
||||
`--latest-tag value must be in 'vN.M.P' format, but '${latestTag}' is not.`,
|
||||
);
|
||||
}
|
||||
|
||||
const majorVersionNumber = Number.parseInt(majorVersionMatch[1]);
|
||||
const latestTagMajor = Number.parseInt(latestTagMatch[1]);
|
||||
|
||||
// If this is a primary release, we backport to all supported branches,
|
||||
// so we check whether the majorVersion taken from the package.json
|
||||
// is greater than or equal to the latest tag pulled from the repo.
|
||||
// For example...
|
||||
// 'v1' >= 'v2' is False # we're operating from an older release branch and should not backport
|
||||
// 'v2' >= 'v2' is True # the normal case where we're updating the current version
|
||||
// 'v3' >= 'v2' is True # in this case we are making the first release of a new major version
|
||||
const considerBackports = majorVersionNumber >= latestTagMajor;
|
||||
|
||||
const backportSourceBranch = `releases/v${majorVersionNumber}`;
|
||||
const backportTargetBranches: string[] = [];
|
||||
|
||||
if (considerBackports) {
|
||||
for (let i = majorVersionNumber - 1; i > 0; i--) {
|
||||
const branchName = `releases/v${i}`;
|
||||
if (i >= oldestSupportedMajorVersion) {
|
||||
backportTargetBranches.push(branchName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { backportSourceBranch, backportTargetBranches };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { values: options } = parseArgs({
|
||||
options: {
|
||||
// The major version of the release in `vN` format (e.g. `v4`).
|
||||
"major-version": {
|
||||
type: "string",
|
||||
},
|
||||
// The most recent tag published to the repository (e.g. `v4.28.0`).
|
||||
"latest-tag": {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
|
||||
if (options["major-version"] === undefined) {
|
||||
throw Error("--major-version is required");
|
||||
}
|
||||
if (options["latest-tag"] === undefined) {
|
||||
throw Error("--latest-tag is required");
|
||||
}
|
||||
|
||||
const majorVersion = options["major-version"];
|
||||
const latestTag = options["latest-tag"];
|
||||
|
||||
console.log(`Major version: ${majorVersion}`);
|
||||
console.log(`Latest tag: ${latestTag}`);
|
||||
|
||||
const result = computeBackportBranches(
|
||||
majorVersion,
|
||||
latestTag,
|
||||
OLDEST_SUPPORTED_MAJOR_VERSION,
|
||||
);
|
||||
|
||||
core.setOutput("backport_source_branch", result.backportSourceBranch);
|
||||
core.setOutput(
|
||||
"backport_target_branches",
|
||||
JSON.stringify(result.backportTargetBranches),
|
||||
);
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Only call `main` if this script was run directly.
|
||||
if (require.main === module) {
|
||||
void main();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/*
|
||||
Tests for the sync-checks.ts script
|
||||
*/
|
||||
|
||||
import * as assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import { CheckInfo, Exclusions, Options, removeExcluded } from "./sync-checks";
|
||||
|
||||
const defaultOptions: Options = {
|
||||
apply: false,
|
||||
verbose: false,
|
||||
};
|
||||
|
||||
const toCheckInfo = (name: string) =>
|
||||
({ context: name, app_id: -1 }) satisfies CheckInfo;
|
||||
|
||||
const expectedPartialMatches = ["PR Check - Foo", "https://example.com"].map(
|
||||
toCheckInfo,
|
||||
);
|
||||
|
||||
const expectedExactMatches = ["CodeQL", "Update"].map(toCheckInfo);
|
||||
|
||||
const testChecks = expectedExactMatches.concat(expectedPartialMatches);
|
||||
|
||||
const emptyExclusions: Exclusions = {
|
||||
is: [],
|
||||
contains: [],
|
||||
};
|
||||
|
||||
describe("removeExcluded", async () => {
|
||||
await it("retains all checks if no exclusions are configured", () => {
|
||||
const retained = removeExcluded(
|
||||
defaultOptions,
|
||||
emptyExclusions,
|
||||
testChecks,
|
||||
);
|
||||
assert.deepEqual(retained, testChecks);
|
||||
});
|
||||
|
||||
await it("removes exact matches", () => {
|
||||
const retained = removeExcluded(
|
||||
defaultOptions,
|
||||
{ ...emptyExclusions, is: ["CodeQL", "Update"] },
|
||||
testChecks,
|
||||
);
|
||||
assert.deepEqual(retained, expectedPartialMatches);
|
||||
});
|
||||
|
||||
await it("removes partial matches", () => {
|
||||
const retained = removeExcluded(
|
||||
defaultOptions,
|
||||
{ ...emptyExclusions, contains: ["https://", "PR Check"] },
|
||||
testChecks,
|
||||
);
|
||||
assert.deepEqual(retained, expectedExactMatches);
|
||||
});
|
||||
});
|
||||
Executable
+287
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/** Update the required checks based on the current branch. */
|
||||
|
||||
import * as fs from "fs";
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
import * as yaml from "yaml";
|
||||
|
||||
import { type ApiClient, getApiClient } from "./api-client";
|
||||
import {
|
||||
OLDEST_SUPPORTED_MAJOR_VERSION,
|
||||
PR_CHECK_EXCLUDED_FILE,
|
||||
} from "./config";
|
||||
|
||||
/** Represents the command-line options. */
|
||||
export interface Options {
|
||||
/** The token to use to authenticate to the GitHub API. */
|
||||
token?: string;
|
||||
/** The git ref to use the checks for. */
|
||||
ref?: string;
|
||||
/** Whether to actually apply the changes or not. */
|
||||
apply: boolean;
|
||||
/** Whether to output additional information. */
|
||||
verbose: boolean;
|
||||
}
|
||||
|
||||
/** Identifies the CodeQL Action repository. */
|
||||
const codeqlActionRepo = {
|
||||
owner: "github",
|
||||
repo: "codeql-action",
|
||||
};
|
||||
|
||||
/** Represents a configuration of which checks should not be set up as required checks. */
|
||||
export interface Exclusions {
|
||||
/** A list of strings that, if contained in a check name, are excluded. */
|
||||
contains: string[];
|
||||
/** A list of check names that are excluded if their name is an exact match. */
|
||||
is: string[];
|
||||
}
|
||||
|
||||
/** Loads the configuration for which checks to exclude. */
|
||||
function loadExclusions(): Exclusions {
|
||||
return yaml.parse(
|
||||
fs.readFileSync(PR_CHECK_EXCLUDED_FILE, "utf-8"),
|
||||
) as Exclusions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents information about a check run. We track the `app_id` that generated the check,
|
||||
* because the API will require it in addition to the name in the future.
|
||||
*/
|
||||
export interface CheckInfo {
|
||||
/** The display name of the check. */
|
||||
context: string;
|
||||
/** The ID of the app that generated the check. */
|
||||
app_id: number;
|
||||
}
|
||||
|
||||
/** Removes entries from `checkInfos` based on the configuration. */
|
||||
export function removeExcluded(
|
||||
options: Options,
|
||||
exclusions: Exclusions,
|
||||
checkInfos: CheckInfo[],
|
||||
): CheckInfo[] {
|
||||
if (options.verbose) {
|
||||
console.log(exclusions);
|
||||
}
|
||||
|
||||
return checkInfos.filter((checkInfo) => {
|
||||
if (exclusions.is.includes(checkInfo.context)) {
|
||||
console.info(
|
||||
`Excluding '${checkInfo.context}' because it is an exact exclusion.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const containsStr of exclusions.contains) {
|
||||
if (checkInfo.context.includes(containsStr)) {
|
||||
console.info(
|
||||
`Excluding '${checkInfo.context}' because it contains '${containsStr}'.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep.
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/** Gets a list of check run names for `ref`. */
|
||||
async function getChecksFor(
|
||||
options: Options,
|
||||
client: ApiClient,
|
||||
ref: string,
|
||||
): Promise<CheckInfo[]> {
|
||||
console.info(`Getting checks for '${ref}'`);
|
||||
|
||||
const response = await client.paginate(
|
||||
"GET /repos/{owner}/{repo}/commits/{ref}/check-runs",
|
||||
{
|
||||
...codeqlActionRepo,
|
||||
ref,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.length === 0) {
|
||||
throw new Error(`No checks found for '${ref}'.`);
|
||||
}
|
||||
|
||||
console.info(`Retrieved ${response.length} check runs.`);
|
||||
|
||||
const notSkipped = response.filter(
|
||||
(checkRun) => checkRun.conclusion !== "skipped",
|
||||
);
|
||||
console.info(`Of those: ${notSkipped.length} were not skipped.`);
|
||||
|
||||
// We use the ID of the app that generated the check run when returned by the API,
|
||||
// but default to -1 to tell the API that any check with the given name should be
|
||||
// required.
|
||||
const checkInfos = notSkipped.map((check) => ({
|
||||
context: check.name,
|
||||
app_id: check.app?.id || -1,
|
||||
}));
|
||||
|
||||
// Load the configuration for which checks to exclude and apply it before
|
||||
// returning the checks.
|
||||
const exclusions = loadExclusions();
|
||||
return removeExcluded(options, exclusions, checkInfos);
|
||||
}
|
||||
|
||||
/** Gets the current list of release branches. */
|
||||
async function getReleaseBranches(client: ApiClient): Promise<string[]> {
|
||||
const refs = await client.rest.git.listMatchingRefs({
|
||||
...codeqlActionRepo,
|
||||
ref: "heads/releases/v",
|
||||
});
|
||||
return refs.data.map((ref) => ref.ref).sort();
|
||||
}
|
||||
|
||||
/** Updates the required status checks for `branch` to `checks`. */
|
||||
async function patchBranchProtectionRule(
|
||||
client: ApiClient,
|
||||
branch: string,
|
||||
checks: Set<string>,
|
||||
) {
|
||||
await client.rest.repos.setStatusCheckContexts({
|
||||
...codeqlActionRepo,
|
||||
branch,
|
||||
contexts: Array.from(checks),
|
||||
});
|
||||
}
|
||||
|
||||
/** Sets `checkNames` as required checks for `branch`. */
|
||||
async function updateBranch(
|
||||
options: Options,
|
||||
client: ApiClient,
|
||||
branch: string,
|
||||
checkNames: Set<string>,
|
||||
) {
|
||||
console.info(`Updating '${branch}'...`);
|
||||
|
||||
// Query the current set of required checks for this branch.
|
||||
const currentContexts = await client.rest.repos.getAllStatusCheckContexts({
|
||||
...codeqlActionRepo,
|
||||
branch,
|
||||
});
|
||||
|
||||
// Identify which required checks we will remove and which ones we will add.
|
||||
const currentCheckNames = new Set(currentContexts.data);
|
||||
let additions = 0;
|
||||
let removals = 0;
|
||||
let unchanged = 0;
|
||||
|
||||
for (const currentCheck of currentCheckNames) {
|
||||
if (!checkNames.has(currentCheck)) {
|
||||
console.info(`- Removing '${currentCheck}' for branch '${branch}'`);
|
||||
removals++;
|
||||
} else {
|
||||
unchanged++;
|
||||
}
|
||||
}
|
||||
for (const newCheck of checkNames) {
|
||||
if (!currentCheckNames.has(newCheck)) {
|
||||
console.info(`+ Adding '${newCheck}' for branch '${branch}'`);
|
||||
additions++;
|
||||
}
|
||||
}
|
||||
|
||||
console.info(
|
||||
`For '${branch}': ${removals} removals; ${additions} additions; ${unchanged} unchanged`,
|
||||
);
|
||||
|
||||
// Perform the update if there are changes and `--apply` was specified.
|
||||
if (unchanged === checkNames.size && removals === 0 && additions === 0) {
|
||||
console.info("Not applying changes because there is nothing to do.");
|
||||
} else if (options.apply) {
|
||||
await patchBranchProtectionRule(client, branch, checkNames);
|
||||
} else {
|
||||
console.info("Not applying changes because `--apply` was not specified.");
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const { values: options } = parseArgs({
|
||||
options: {
|
||||
// The token to use to authenticate to the API.
|
||||
token: {
|
||||
type: "string",
|
||||
},
|
||||
// The git ref for which to retrieve the check runs.
|
||||
ref: {
|
||||
type: "string",
|
||||
default: "main",
|
||||
},
|
||||
// By default, we perform a dry-run. Setting `apply` to `true` actually applies the changes.
|
||||
apply: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
// Whether to output additional information.
|
||||
verbose: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
|
||||
if (options.token === undefined) {
|
||||
throw new Error("Missing --token");
|
||||
}
|
||||
|
||||
console.info(
|
||||
`Oldest supported major version is: ${OLDEST_SUPPORTED_MAJOR_VERSION}`,
|
||||
);
|
||||
|
||||
// Initialise the API client.
|
||||
const client = getApiClient(options.token);
|
||||
|
||||
// Find the check runs for the specified `ref` that we will later set as the required checks
|
||||
// for the main and release branches.
|
||||
const checkInfos = await getChecksFor(options, client, options.ref);
|
||||
const checkNames = new Set(checkInfos.map((info) => info.context));
|
||||
|
||||
// Update the main branch.
|
||||
await updateBranch(options, client, "main", checkNames);
|
||||
|
||||
// Retrieve the refs of the release branches.
|
||||
const releaseBranches = await getReleaseBranches(client);
|
||||
console.info(
|
||||
`Found ${releaseBranches.length} release branches: ${releaseBranches.join(", ")}`,
|
||||
);
|
||||
|
||||
for (const releaseBranchRef of releaseBranches) {
|
||||
// Sanity check that the ref name is in the expected format and extract the major version.
|
||||
const releaseBranchMatch = releaseBranchRef.match(
|
||||
/^refs\/heads\/(releases\/v(\d+))/,
|
||||
);
|
||||
if (!releaseBranchMatch) {
|
||||
console.warn(
|
||||
`Branch ref '${releaseBranchRef}' not in the expected format.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const releaseBranch = releaseBranchMatch[1];
|
||||
const releaseBranchMajor = Number.parseInt(releaseBranchMatch[2]);
|
||||
|
||||
// Update the required checks for this major version if it is still supported.
|
||||
if (releaseBranchMajor < OLDEST_SUPPORTED_MAJOR_VERSION) {
|
||||
console.info(
|
||||
`Skipping '${releaseBranch}' since it is older than v${OLDEST_SUPPORTED_MAJOR_VERSION}`,
|
||||
);
|
||||
continue;
|
||||
} else {
|
||||
await updateBranch(options, client, releaseBranch, checkNames);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Only call `main` if this script was run directly.
|
||||
if (require.main === module) {
|
||||
void main();
|
||||
}
|
||||
+5
-5
@@ -5,7 +5,7 @@ import * as path from "path";
|
||||
|
||||
import * as yaml from "yaml";
|
||||
|
||||
import { KnownLanguage } from "../src/languages";
|
||||
import { BuiltInLanguage } from "../src/languages";
|
||||
|
||||
/** Known workflow input names. */
|
||||
enum KnownInputName {
|
||||
@@ -91,8 +91,8 @@ interface LanguageSetup {
|
||||
steps: Step[];
|
||||
}
|
||||
|
||||
/** Describes partial mappings from known languages to their specific setup information. */
|
||||
type LanguageSetups = Partial<Record<KnownLanguage, LanguageSetup>>;
|
||||
/** Describes partial mappings from built-in languages to their specific setup information. */
|
||||
type LanguageSetups = Partial<Record<BuiltInLanguage, LanguageSetup>>;
|
||||
|
||||
// The default set of CodeQL Bundle versions to use for the PR checks.
|
||||
const defaultTestVersions = [
|
||||
@@ -125,7 +125,7 @@ const defaultLanguageVersions = {
|
||||
java: "17",
|
||||
python: "3.13",
|
||||
csharp: "9.x",
|
||||
} as const satisfies Partial<Record<KnownLanguage, string>>;
|
||||
} as const satisfies Partial<Record<BuiltInLanguage, string>>;
|
||||
|
||||
/** A mapping from known input names to their specifications. */
|
||||
const inputSpecs: WorkflowInputs = {
|
||||
@@ -364,7 +364,7 @@ function getSetupSteps(checkSpecification: JobSpecification): {
|
||||
const inputs: Array<Set<KnownInputName>> = [];
|
||||
const steps: Step[] = [];
|
||||
|
||||
for (const language of Object.values(KnownLanguage).sort()) {
|
||||
for (const language of Object.values(BuiltInLanguage).sort()) {
|
||||
const setupSpec = languageSetups[language];
|
||||
|
||||
if (
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"lib": ["esnext"],
|
||||
"module": "preserve",
|
||||
"rootDir": "..",
|
||||
"sourceMap": false,
|
||||
"noEmit": true,
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/*
|
||||
* Updates src/languages/builtin.json by querying the CodeQL CLI for:
|
||||
* - Languages that have default queries (via codeql-extractor.yml)
|
||||
* - Language aliases (via `codeql resolve languages --format=betterjson --extractor-include-aliases`)
|
||||
*
|
||||
* Usage:
|
||||
* npx tsx pr-checks/update-builtin-languages.ts [path-to-codeql]
|
||||
*
|
||||
* If no path is given, falls back to "codeql".
|
||||
*/
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import * as yaml from "yaml";
|
||||
|
||||
import { EnvVar } from "../src/environment";
|
||||
|
||||
import { BUILTIN_LANGUAGES_FILE } from "./config";
|
||||
|
||||
/** Resolve all known language extractor directories. */
|
||||
function resolveLanguages(codeqlPath: string): Record<string, string[]> {
|
||||
return JSON.parse(
|
||||
execFileSync(codeqlPath, ["resolve", "languages", "--format=json"], {
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
[EnvVar.EXPERIMENTAL_FEATURES]: "true", // include experimental languages
|
||||
},
|
||||
}),
|
||||
) as Record<string, string[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sorted list of languages whose extractors ship default queries.
|
||||
*
|
||||
* @param extractorDirs - Map from language to list of extractor directories
|
||||
*/
|
||||
function findLanguagesWithDefaultQueries(
|
||||
extractorDirs: Record<string, string[]>,
|
||||
): string[] {
|
||||
const languages: string[] = [];
|
||||
|
||||
for (const [language, dirs] of Object.entries(extractorDirs)) {
|
||||
if (dirs.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected exactly one extractor directory for language '${language}', but found ${dirs.length}: ${dirs.join(
|
||||
", ",
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const extractorYmlPath = path.join(dirs[0], "codeql-extractor.yml");
|
||||
|
||||
if (!fs.existsSync(extractorYmlPath)) {
|
||||
throw new Error(
|
||||
`Extractor YAML not found for language '${language}' at expected path: ${extractorYmlPath}`,
|
||||
);
|
||||
}
|
||||
|
||||
const extractorYml = yaml.parse(fs.readFileSync(extractorYmlPath, "utf8"));
|
||||
const defaultQueries: unknown[] | undefined = extractorYml.default_queries;
|
||||
|
||||
if (Array.isArray(defaultQueries) && defaultQueries.length > 0) {
|
||||
console.log(
|
||||
` ✅ ${language}: included (default queries: ${JSON.stringify(defaultQueries)})`,
|
||||
);
|
||||
languages.push(language);
|
||||
} else {
|
||||
console.log(` ❌ ${language}: excluded (no default queries)`);
|
||||
}
|
||||
}
|
||||
|
||||
return languages.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve language aliases from the CodeQL CLI, keeping only those whose
|
||||
* target is in the given set of included languages.
|
||||
*/
|
||||
function resolveAliases(
|
||||
codeqlPath: string,
|
||||
includedLanguages: Set<string>,
|
||||
): Record<string, string> {
|
||||
const betterjsonOutput = JSON.parse(
|
||||
execFileSync(
|
||||
codeqlPath,
|
||||
[
|
||||
"resolve",
|
||||
"languages",
|
||||
"--format=betterjson",
|
||||
"--extractor-include-aliases",
|
||||
],
|
||||
{ encoding: "utf8" },
|
||||
),
|
||||
);
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries((betterjsonOutput.aliases ?? {}) as Record<string, string>)
|
||||
.filter(([, target]) => includedLanguages.has(target))
|
||||
.sort(([a], [b]) => a.localeCompare(b)),
|
||||
);
|
||||
}
|
||||
|
||||
/** Write the built-in languages data to disk. */
|
||||
function writeBuiltinLanguages(
|
||||
languages: string[],
|
||||
aliases: Record<string, string>,
|
||||
): void {
|
||||
const content = `${JSON.stringify({ languages, aliases }, null, 2)}\n`;
|
||||
fs.mkdirSync(path.dirname(BUILTIN_LANGUAGES_FILE), { recursive: true });
|
||||
fs.writeFileSync(BUILTIN_LANGUAGES_FILE, content);
|
||||
|
||||
console.log(`\nWrote ${BUILTIN_LANGUAGES_FILE}`);
|
||||
console.log(` Languages: ${languages.join(", ")}`);
|
||||
console.log(` Aliases: ${Object.keys(aliases).join(", ")}`);
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const codeqlPath = process.argv[2] || "codeql";
|
||||
|
||||
const extractorDirs = resolveLanguages(codeqlPath);
|
||||
const languages = findLanguagesWithDefaultQueries(extractorDirs);
|
||||
const aliases = resolveAliases(codeqlPath, new Set(languages));
|
||||
writeBuiltinLanguages(languages, aliases);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -19,6 +19,25 @@ inputs:
|
||||
If not specified, the Action will check in several places until it finds
|
||||
the CodeQL tools.
|
||||
required: false
|
||||
languages:
|
||||
description: >-
|
||||
A comma-separated list of CodeQL languages that will be analyzed in subsequent
|
||||
`github/codeql-action/init` and `github/codeql-action/analyze` invocations. If specified, the
|
||||
Action may use this list to select a CodeQL CLI version that is best suited to analyzing those
|
||||
languages, for example by preferring a version that has a cached overlay-base database for the
|
||||
specified languages. This input is not remembered and must also be passed to
|
||||
`github/codeql-action/init`.
|
||||
required: false
|
||||
analysis-kinds:
|
||||
description: >-
|
||||
[Internal] A comma-separated list of analysis kinds that subsequent
|
||||
`github/codeql-action/init` invocations will enable. If specified, the Action may use this
|
||||
list to select a CodeQL CLI version that is best suited to those analysis kinds. This input is
|
||||
not remembered and must also be passed to `github/codeql-action/init`.
|
||||
|
||||
Available options are the same as for the `analysis-kinds` input on the `init` Action.
|
||||
default: 'code-scanning'
|
||||
required: true
|
||||
token:
|
||||
description: GitHub token to use for authenticating with this instance of GitHub.
|
||||
default: ${{ github.token }}
|
||||
|
||||
@@ -53,6 +53,12 @@ export function getTemporaryDirectory(): string {
|
||||
: getRequiredEnvParam("RUNNER_TEMP");
|
||||
}
|
||||
|
||||
const PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json";
|
||||
|
||||
export function getDiffRangesJsonFilePath(): string {
|
||||
return path.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME);
|
||||
}
|
||||
|
||||
export function getActionVersion(): string {
|
||||
return __CODEQL_ACTION_VERSION__;
|
||||
}
|
||||
|
||||
+54
-9
@@ -16,7 +16,7 @@ import {
|
||||
} from "./analyses";
|
||||
import { EnvVar } from "./environment";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import { createFeatures, RecordingLogger, setupTests } from "./testing-utils";
|
||||
import { AssessmentPayload } from "./upload-lib/types";
|
||||
import { ConfigurationError } from "./util";
|
||||
|
||||
@@ -53,24 +53,56 @@ test("Parsing analysis kinds requires at least one analysis kind", async (t) =>
|
||||
test.serial(
|
||||
"getAnalysisKinds - returns expected analysis kinds for `analysis-kinds` input",
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "true";
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns("code-scanning,code-quality");
|
||||
const result = await getAnalysisKinds(getRunnerLogger(true), true);
|
||||
const result = await getAnalysisKinds(
|
||||
getRunnerLogger(true),
|
||||
features,
|
||||
true,
|
||||
);
|
||||
t.assert(result.includes(AnalysisKind.CodeScanning));
|
||||
t.assert(result.includes(AnalysisKind.CodeQuality));
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getAnalysisKinds - only use `code-scanning` for multiple analysis kinds outside of test mode",
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "false";
|
||||
const features = createFeatures([]);
|
||||
const logger = new RecordingLogger();
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns("code-scanning,code-quality");
|
||||
const result = await getAnalysisKinds(logger, features, true);
|
||||
t.deepEqual(result, [AnalysisKind.CodeScanning]);
|
||||
t.assert(
|
||||
logger.hasMessage(
|
||||
"Continuing with only `analysis-kinds: code-scanning`.",
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getAnalysisKinds - includes `code-quality` when deprecated `quality-queries` input is used",
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "true";
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("analysis-kinds").returns("code-scanning");
|
||||
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
|
||||
optionalInputStub.withArgs("quality-queries").returns("code-quality");
|
||||
const result = await getAnalysisKinds(getRunnerLogger(true), true);
|
||||
const result = await getAnalysisKinds(
|
||||
getRunnerLogger(true),
|
||||
features,
|
||||
true,
|
||||
);
|
||||
t.assert(result.includes(AnalysisKind.CodeScanning));
|
||||
t.assert(result.includes(AnalysisKind.CodeQuality));
|
||||
},
|
||||
@@ -79,9 +111,12 @@ test.serial(
|
||||
test.serial(
|
||||
"getAnalysisKinds - throws if `analysis-kinds` input is invalid",
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing");
|
||||
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true));
|
||||
await t.throwsAsync(
|
||||
getAnalysisKinds(getRunnerLogger(true), features, true),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -98,11 +133,17 @@ for (let i = 0; i < analysisKinds.length; i++) {
|
||||
test.serial(
|
||||
`getAnalysisKinds - allows ${analysisKind} with ${otherAnalysis}`,
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "true";
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns([analysisKind, otherAnalysis].join(","));
|
||||
const result = await getAnalysisKinds(getRunnerLogger(true), true);
|
||||
const result = await getAnalysisKinds(
|
||||
getRunnerLogger(true),
|
||||
features,
|
||||
true,
|
||||
);
|
||||
t.is(result.length, 2);
|
||||
},
|
||||
);
|
||||
@@ -110,14 +151,18 @@ for (let i = 0; i < analysisKinds.length; i++) {
|
||||
test.serial(
|
||||
`getAnalysisKinds - throws if ${analysisKind} is enabled with ${otherAnalysis}`,
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns([analysisKind, otherAnalysis].join(","));
|
||||
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true), {
|
||||
instanceOf: ConfigurationError,
|
||||
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
|
||||
});
|
||||
await t.throwsAsync(
|
||||
getAnalysisKinds(getRunnerLogger(true), features, true),
|
||||
{
|
||||
instanceOf: ConfigurationError,
|
||||
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
+22
-1
@@ -4,13 +4,14 @@ import {
|
||||
getRequiredInput,
|
||||
} from "./actions-util";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
AssessmentPayload,
|
||||
BasePayload,
|
||||
UploadPayload,
|
||||
} from "./upload-lib/types";
|
||||
import { ConfigurationError, getRequiredEnvParam } from "./util";
|
||||
import { ConfigurationError, getRequiredEnvParam, isInTestMode } from "./util";
|
||||
|
||||
export enum AnalysisKind {
|
||||
CodeScanning = "code-scanning",
|
||||
@@ -77,6 +78,7 @@ let cachedAnalysisKinds: AnalysisKind[] | undefined;
|
||||
*/
|
||||
export async function getAnalysisKinds(
|
||||
logger: Logger,
|
||||
features: FeatureEnablement,
|
||||
skipCache: boolean = false,
|
||||
): Promise<AnalysisKind[]> {
|
||||
if (!skipCache && cachedAnalysisKinds !== undefined) {
|
||||
@@ -120,6 +122,25 @@ export async function getAnalysisKinds(
|
||||
}
|
||||
}
|
||||
|
||||
// Log an error if we have multiple inputs for `analysis-kinds` outside of test mode,
|
||||
// and enable only `code-scanning`.
|
||||
if (
|
||||
!isInTestMode() &&
|
||||
analysisKinds.length > 1 &&
|
||||
!(await features.getValue(Feature.AllowMultipleAnalysisKinds))
|
||||
) {
|
||||
logger.error(
|
||||
"The `analysis-kinds` input is experimental and for GitHub-internal use only. " +
|
||||
"Its behaviour may change at any time or be removed entirely. " +
|
||||
"Specifying multiple values as input is no longer supported. " +
|
||||
"Continuing with only `analysis-kinds: code-scanning`.",
|
||||
);
|
||||
|
||||
// Only enable Code Scanning.
|
||||
cachedAnalysisKinds = [AnalysisKind.CodeScanning];
|
||||
return cachedAnalysisKinds;
|
||||
}
|
||||
|
||||
// Cache the analysis kinds and return them.
|
||||
cachedAnalysisKinds = analysisKinds;
|
||||
return cachedAnalysisKinds;
|
||||
|
||||
+13
-16
@@ -28,12 +28,11 @@ import {
|
||||
DependencyCacheUploadStatusReport,
|
||||
uploadDependencyCaches,
|
||||
} from "./dependency-caching";
|
||||
import { getDiffInformedAnalysisBranches } from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { initFeatures } from "./feature-flags";
|
||||
import { KnownLanguage } from "./languages";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { cleanupAndUploadOverlayBaseDatabaseToCache } from "./overlay";
|
||||
import { cleanupAndUploadOverlayBaseDatabaseToCache } from "./overlay/caching";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import * as statusReport from "./status-report";
|
||||
import {
|
||||
@@ -136,9 +135,13 @@ function hasBadExpectErrorInput(): boolean {
|
||||
function doesGoExtractionOutputExist(config: Config): boolean {
|
||||
const golangDbDirectory = util.getCodeQLDatabasePath(
|
||||
config,
|
||||
KnownLanguage.go,
|
||||
BuiltInLanguage.go,
|
||||
);
|
||||
const trapDirectory = path.join(
|
||||
golangDbDirectory,
|
||||
"trap",
|
||||
BuiltInLanguage.go,
|
||||
);
|
||||
const trapDirectory = path.join(golangDbDirectory, "trap", KnownLanguage.go);
|
||||
return (
|
||||
fs.existsSync(trapDirectory) &&
|
||||
fs
|
||||
@@ -170,7 +173,7 @@ function doesGoExtractionOutputExist(config: Config): boolean {
|
||||
* whether any extraction output already exists for Go.
|
||||
*/
|
||||
async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
|
||||
if (!config.languages.includes(KnownLanguage.go)) {
|
||||
if (!config.languages.includes(BuiltInLanguage.go)) {
|
||||
return;
|
||||
}
|
||||
if (config.buildMode) {
|
||||
@@ -183,7 +186,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
|
||||
logger.debug("Won't run Go autobuild since it has already been run.");
|
||||
return;
|
||||
}
|
||||
if (dbIsFinalized(config, KnownLanguage.go, logger)) {
|
||||
if (dbIsFinalized(config, BuiltInLanguage.go, logger)) {
|
||||
logger.debug(
|
||||
"Won't run Go autobuild since there is already a finalized database for Go.",
|
||||
);
|
||||
@@ -206,7 +209,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
|
||||
logger.debug(
|
||||
"Running Go autobuild because extraction output (TRAP files) for Go code has not been found.",
|
||||
);
|
||||
await runAutobuild(config, KnownLanguage.go, logger);
|
||||
await runAutobuild(config, BuiltInLanguage.go, logger);
|
||||
}
|
||||
|
||||
async function run(startedAt: Date) {
|
||||
@@ -305,14 +308,8 @@ async function run(startedAt: Date) {
|
||||
logger,
|
||||
);
|
||||
|
||||
const branches = await getDiffInformedAnalysisBranches(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
const diffRangePackDir = branches
|
||||
? await setupDiffInformedQueryRun(branches, logger)
|
||||
: undefined;
|
||||
// Setup diff informed analysis if needed (based on whether init created the file)
|
||||
const diffRangePackDir = await setupDiffInformedQueryRun(logger);
|
||||
|
||||
await warnIfGoInstalledAfterInit(config, logger);
|
||||
await runAutobuildIfLegacyGoWorkflow(config, logger);
|
||||
|
||||
+26
-6
@@ -10,10 +10,11 @@ import {
|
||||
defaultSuites,
|
||||
resolveQuerySuiteAlias,
|
||||
addSarifExtension,
|
||||
diffRangeExtensionPackContents,
|
||||
} from "./analyze";
|
||||
import { createStubCodeQL } from "./codeql";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { KnownLanguage } from "./languages";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import {
|
||||
setupTests,
|
||||
@@ -40,7 +41,7 @@ test.serial("status report fields", async (t) => {
|
||||
const threadsFlag = "";
|
||||
sinon.stub(uploadLib, "validateSarifFileSchema");
|
||||
|
||||
for (const language of Object.values(KnownLanguage)) {
|
||||
for (const language of Object.values(BuiltInLanguage)) {
|
||||
const codeql = createStubCodeQL({
|
||||
databaseRunQueries: async () => {},
|
||||
databaseInterpretResults: async (
|
||||
@@ -129,13 +130,13 @@ test.serial("status report fields", async (t) => {
|
||||
test("resolveQuerySuiteAlias", (t) => {
|
||||
// default query suite names should resolve to something language-specific ending in `.qls`.
|
||||
for (const suite of defaultSuites) {
|
||||
const resolved = resolveQuerySuiteAlias(KnownLanguage.go, suite);
|
||||
const resolved = resolveQuerySuiteAlias(BuiltInLanguage.go, suite);
|
||||
t.assert(
|
||||
path.extname(resolved) === ".qls",
|
||||
"Resolved default suite doesn't end in .qls",
|
||||
);
|
||||
t.assert(
|
||||
resolved.indexOf(KnownLanguage.go) >= 0,
|
||||
resolved.indexOf(BuiltInLanguage.go) >= 0,
|
||||
"Resolved default suite doesn't contain language name",
|
||||
);
|
||||
}
|
||||
@@ -144,12 +145,12 @@ test("resolveQuerySuiteAlias", (t) => {
|
||||
const names = ["foo", "bar", "codeql/go-queries@1.0"];
|
||||
|
||||
for (const name of names) {
|
||||
t.deepEqual(resolveQuerySuiteAlias(KnownLanguage.go, name), name);
|
||||
t.deepEqual(resolveQuerySuiteAlias(BuiltInLanguage.go, name), name);
|
||||
}
|
||||
});
|
||||
|
||||
test("addSarifExtension", (t) => {
|
||||
for (const language of Object.values(KnownLanguage)) {
|
||||
for (const language of Object.values(BuiltInLanguage)) {
|
||||
t.deepEqual(addSarifExtension(CodeScanning, language), `${language}.sarif`);
|
||||
t.deepEqual(
|
||||
addSarifExtension(CodeQuality, language),
|
||||
@@ -158,3 +159,22 @@ test("addSarifExtension", (t) => {
|
||||
t.is(addSarifExtension(RiskAssessment, language), `${language}.csra.sarif`);
|
||||
}
|
||||
});
|
||||
|
||||
test("diffRangeExtensionPackContents", (t) => {
|
||||
const output = diffRangeExtensionPackContents(
|
||||
[
|
||||
{
|
||||
path: "main.js",
|
||||
startLine: 10,
|
||||
endLine: 20,
|
||||
},
|
||||
],
|
||||
"/checkout/path",
|
||||
);
|
||||
|
||||
const expected = fs.readFileSync(
|
||||
`${__dirname}/../src/testdata/pr-diff-range.yml`,
|
||||
"utf8",
|
||||
);
|
||||
t.deepEqual(output, expected);
|
||||
});
|
||||
|
||||
+75
-64
@@ -5,7 +5,7 @@ import { performance } from "perf_hooks";
|
||||
import * as io from "@actions/io";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
import { getTemporaryDirectory, PullRequestBranches } from "./actions-util";
|
||||
import { getTemporaryDirectory, getRequiredInput } from "./actions-util";
|
||||
import * as analyses from "./analyses";
|
||||
import { setupCppAutobuild } from "./autobuild";
|
||||
import { type CodeQL } from "./codeql";
|
||||
@@ -17,14 +17,13 @@ import {
|
||||
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
|
||||
import {
|
||||
DiffThunkRange,
|
||||
writeDiffRangesJsonFile,
|
||||
getPullRequestEditedDiffRanges,
|
||||
readDiffRangesJsonFile,
|
||||
} from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { FeatureEnablement, Feature } from "./feature-flags";
|
||||
import { KnownLanguage, Language } from "./languages";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import type * as sarif from "./sarif";
|
||||
import { DatabaseCreationTimings, EventReport } from "./status-report";
|
||||
import { endTracingForCluster } from "./tracer-config";
|
||||
@@ -42,7 +41,7 @@ export class CodeQLAnalysisError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
type KnownLanguageKey = keyof typeof KnownLanguage;
|
||||
type BuiltInLanguageKey = keyof typeof BuiltInLanguage;
|
||||
|
||||
type RunQueriesDurationStatusReport = {
|
||||
/**
|
||||
@@ -51,12 +50,12 @@ type RunQueriesDurationStatusReport = {
|
||||
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||
* taken to run _all_ the queries.
|
||||
*/
|
||||
[L in KnownLanguageKey as `analyze_builtin_queries_${L}_duration_ms`]?: number;
|
||||
[L in BuiltInLanguageKey as `analyze_builtin_queries_${L}_duration_ms`]?: number;
|
||||
};
|
||||
|
||||
type InterpretResultsDurationStatusReport = {
|
||||
/** Time taken in ms to interpret results for the language (or undefined if this language was not analyzed). */
|
||||
[L in KnownLanguageKey as `interpret_results_${L}_duration_ms`]?: number;
|
||||
[L in BuiltInLanguageKey as `interpret_results_${L}_duration_ms`]?: number;
|
||||
};
|
||||
|
||||
export interface QueriesStatusReport
|
||||
@@ -116,12 +115,12 @@ export async function runExtraction(
|
||||
|
||||
if (await shouldExtractLanguage(codeql, config, language)) {
|
||||
logger.startGroup(`Extracting ${language}`);
|
||||
if (language === KnownLanguage.python) {
|
||||
if (language === BuiltInLanguage.python) {
|
||||
await setupPythonExtractor(logger);
|
||||
}
|
||||
if (config.buildMode) {
|
||||
if (
|
||||
language === KnownLanguage.cpp &&
|
||||
language === BuiltInLanguage.cpp &&
|
||||
config.buildMode === BuildMode.Autobuild
|
||||
) {
|
||||
await setupCppAutobuild(codeql, logger);
|
||||
@@ -132,14 +131,14 @@ export async function runExtraction(
|
||||
// a stable path that caches can be restored into and that we can cache at the
|
||||
// end of the workflow (i.e. that does not get removed when the scratch directory is).
|
||||
if (
|
||||
language === KnownLanguage.java &&
|
||||
language === BuiltInLanguage.java &&
|
||||
config.buildMode === BuildMode.None
|
||||
) {
|
||||
process.env["CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_DEPENDENCY_DIR"] =
|
||||
getJavaTempDependencyDir();
|
||||
}
|
||||
if (
|
||||
language === KnownLanguage.csharp &&
|
||||
language === BuiltInLanguage.csharp &&
|
||||
config.buildMode === BuildMode.None &&
|
||||
(await features.getValue(Feature.CsharpCacheBuildModeNone))
|
||||
) {
|
||||
@@ -233,32 +232,73 @@ async function finalizeDatabaseCreation(
|
||||
* the diff range information, or `undefined` if the feature is disabled.
|
||||
*/
|
||||
export async function setupDiffInformedQueryRun(
|
||||
branches: PullRequestBranches,
|
||||
logger: Logger,
|
||||
): Promise<string | undefined> {
|
||||
return await withGroupAsync(
|
||||
"Generating diff range extension pack",
|
||||
async () => {
|
||||
logger.info(
|
||||
`Calculating diff ranges for ${branches.base}...${branches.head}`,
|
||||
);
|
||||
const diffRanges = await getPullRequestEditedDiffRanges(branches, logger);
|
||||
const packDir = writeDiffRangeDataExtensionPack(logger, diffRanges);
|
||||
if (packDir === undefined) {
|
||||
logger.warning(
|
||||
"Cannot create diff range extension pack for diff-informed queries; " +
|
||||
"reverting to performing full analysis.",
|
||||
);
|
||||
} else {
|
||||
const diffRanges = readDiffRangesJsonFile(logger);
|
||||
if (diffRanges === undefined) {
|
||||
logger.info(
|
||||
`Successfully created diff range extension pack at ${packDir}.`,
|
||||
"No precomputed diff ranges found; skipping diff-informed analysis stage.",
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const checkoutPath = getRequiredInput("checkout_path");
|
||||
const packDir = writeDiffRangeDataExtensionPack(
|
||||
logger,
|
||||
diffRanges,
|
||||
checkoutPath,
|
||||
);
|
||||
logger.info(
|
||||
`Successfully created diff range extension pack at ${packDir}.`,
|
||||
);
|
||||
return packDir;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function diffRangeExtensionPackContents(
|
||||
ranges: DiffThunkRange[],
|
||||
checkoutPath: string,
|
||||
): string {
|
||||
const header = `
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/util
|
||||
extensible: restrictAlertsTo
|
||||
checkPresence: false
|
||||
data:
|
||||
`;
|
||||
|
||||
let data = ranges
|
||||
.map((range) => {
|
||||
// Diff-informed queries expect the file path to be absolute. CodeQL always
|
||||
// uses forward slashes as the path separator, so on Windows we need to
|
||||
// replace any backslashes with forward slashes.
|
||||
const filename = path
|
||||
.join(checkoutPath, range.path)
|
||||
.replaceAll(path.sep, "/");
|
||||
|
||||
// Using yaml.dump() with `forceQuotes: true` ensures that all special
|
||||
// characters are escaped, and that the path is always rendered as a
|
||||
// quoted string on a single line.
|
||||
return (
|
||||
` - [${yaml.dump(filename, { forceQuotes: true }).trim()}, ` +
|
||||
`${range.startLine}, ${range.endLine}]\n`
|
||||
);
|
||||
})
|
||||
.join("");
|
||||
if (!data) {
|
||||
// Ensure that the data extension is not empty, so that a pull request with
|
||||
// no edited lines would exclude (instead of accepting) all alerts.
|
||||
data = ' - ["", 0, 0]\n';
|
||||
}
|
||||
|
||||
return header + data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an extension pack in the temporary directory that contains the file
|
||||
* line ranges that were added or modified in the pull request.
|
||||
@@ -266,17 +306,14 @@ export async function setupDiffInformedQueryRun(
|
||||
* @param logger
|
||||
* @param ranges The file line ranges, as returned by
|
||||
* `getPullRequestEditedDiffRanges`.
|
||||
* @returns The absolute path of the directory containing the extension pack, or
|
||||
* `undefined` if no extension pack was created.
|
||||
* @param checkoutPath The path at which the repository was checked out.
|
||||
* @returns The absolute path of the directory containing the extension pack.
|
||||
*/
|
||||
function writeDiffRangeDataExtensionPack(
|
||||
logger: Logger,
|
||||
ranges: DiffThunkRange[] | undefined,
|
||||
): string | undefined {
|
||||
if (ranges === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
ranges: DiffThunkRange[],
|
||||
checkoutPath: string,
|
||||
): string {
|
||||
if (ranges.length === 0) {
|
||||
// An empty diff range means that there are no added or modified lines in
|
||||
// the pull request. But the `restrictAlertsTo` extensible predicate
|
||||
@@ -307,42 +344,16 @@ dataExtensions:
|
||||
`,
|
||||
);
|
||||
|
||||
const header = `
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/util
|
||||
extensible: restrictAlertsTo
|
||||
checkPresence: false
|
||||
data:
|
||||
`;
|
||||
|
||||
let data = ranges
|
||||
.map(
|
||||
(range) =>
|
||||
// Using yaml.dump() with `forceQuotes: true` ensures that all special
|
||||
// characters are escaped, and that the path is always rendered as a
|
||||
// quoted string on a single line.
|
||||
` - [${yaml.dump(range.path, { forceQuotes: true }).trim()}, ` +
|
||||
`${range.startLine}, ${range.endLine}]\n`,
|
||||
)
|
||||
.join("");
|
||||
if (!data) {
|
||||
// Ensure that the data extension is not empty, so that a pull request with
|
||||
// no edited lines would exclude (instead of accepting) all alerts.
|
||||
data = ' - ["", 0, 0]\n';
|
||||
}
|
||||
|
||||
const extensionContents = header + data;
|
||||
const extensionContents = diffRangeExtensionPackContents(
|
||||
ranges,
|
||||
checkoutPath,
|
||||
);
|
||||
const extensionFilePath = path.join(diffRangeDir, "pr-diff-range.yml");
|
||||
fs.writeFileSync(extensionFilePath, extensionContents);
|
||||
logger.debug(
|
||||
`Wrote pr-diff-range extension pack to ${extensionFilePath}:\n${extensionContents}`,
|
||||
);
|
||||
|
||||
// Write the diff ranges to a JSON file, for action-side alert filtering by the
|
||||
// upload-lib module.
|
||||
writeDiffRangesJsonFile(logger, ranges);
|
||||
|
||||
return diffRangeDir;
|
||||
}
|
||||
|
||||
@@ -675,7 +686,7 @@ export async function warnIfGoInstalledAfterInit(
|
||||
|
||||
addDiagnostic(
|
||||
config,
|
||||
KnownLanguage.go,
|
||||
BuiltInLanguage.go,
|
||||
makeDiagnostic(
|
||||
"go/workflow/go-installed-after-codeql-init",
|
||||
"Go was installed after the `codeql-action/init` Action was run",
|
||||
|
||||
+22
-5
@@ -128,6 +128,8 @@ export async function getGitHubVersionFromApi(
|
||||
|
||||
// Doesn't strictly have to be the meta endpoint as we're only
|
||||
// using the response headers which are available on every request.
|
||||
//
|
||||
// See https://docs.github.com/en/rest/meta/meta#get-github-meta-information.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
const response = await apiClient.rest.meta.get();
|
||||
|
||||
@@ -164,6 +166,9 @@ export async function getGitHubVersion(): Promise<GitHubVersion> {
|
||||
|
||||
/**
|
||||
* Get the path of the currently executing workflow relative to the repository root.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run
|
||||
* and https://docs.github.com/en/rest/actions/workflows#get-a-workflow.
|
||||
*/
|
||||
export async function getWorkflowRelativePath(): Promise<string> {
|
||||
const repo_nwo = getRepositoryNwo();
|
||||
@@ -252,9 +257,13 @@ export interface ActionsCacheItem {
|
||||
size_in_bytes?: number;
|
||||
}
|
||||
|
||||
/** List all Actions cache entries matching the provided key and ref. */
|
||||
/**
|
||||
* List all Actions cache entries starting with the provided key prefix and matching the provided ref.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository.
|
||||
*/
|
||||
export async function listActionsCaches(
|
||||
key: string,
|
||||
keyPrefix: string,
|
||||
ref?: string,
|
||||
): Promise<ActionsCacheItem[]> {
|
||||
const repositoryNwo = getRepositoryNwo();
|
||||
@@ -264,13 +273,17 @@ export async function listActionsCaches(
|
||||
{
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
key,
|
||||
key: keyPrefix,
|
||||
ref,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Delete an Actions cache item by its ID. */
|
||||
/**
|
||||
* Delete an Actions cache item by its ID.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id.
|
||||
*/
|
||||
export async function deleteActionsCache(id: number) {
|
||||
const repositoryNwo = getRepositoryNwo();
|
||||
|
||||
@@ -281,7 +294,11 @@ export async function deleteActionsCache(id: number) {
|
||||
});
|
||||
}
|
||||
|
||||
/** Retrieve all custom repository properties. */
|
||||
/**
|
||||
* Retrieve all custom repository properties.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/repos/custom-properties#get-all-custom-property-values-for-a-repository.
|
||||
*/
|
||||
export async function getRepositoryProperties(repositoryNwo: RepositoryNwo) {
|
||||
return getApiClient().request("GET /repos/:owner/:repo/properties/values", {
|
||||
owner: repositoryNwo.owner,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"maximumVersion": "3.21", "minimumVersion": "3.14"}
|
||||
{"maximumVersion": "3.21", "minimumVersion": "3.16"}
|
||||
|
||||
@@ -141,7 +141,12 @@ test("scanArtifactsForTokens handles files without tokens", async (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
if (os.platform() !== "win32") {
|
||||
// This test is slow (extracts and scans a zip artifact), so by default we only run it in CI. Set
|
||||
// RUN_SLOW_TESTS=1 to run it locally.
|
||||
if (
|
||||
os.platform() !== "win32" &&
|
||||
(process.env.CI === "true" || process.env.RUN_SLOW_TESTS === "1")
|
||||
) {
|
||||
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
|
||||
t.timeout(15000); // 15 seconds
|
||||
const messages: LoggedMessage[] = [];
|
||||
|
||||
@@ -156,6 +156,10 @@ async function scanArchiveFile(
|
||||
);
|
||||
}
|
||||
|
||||
if (process.platform === "win32") {
|
||||
throw new Error("Scanning archives is not supported on Windows.");
|
||||
}
|
||||
|
||||
const result: ScanResult = {
|
||||
scannedFiles: 0,
|
||||
findings: [],
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@ import * as configUtils from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, featureConfig, initFeatures } from "./feature-flags";
|
||||
import { KnownLanguage, Language } from "./languages";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import { asyncFilter, BuildMode } from "./util";
|
||||
@@ -72,7 +72,7 @@ export async function determineAutobuildLanguages(
|
||||
* version of the CodeQL Action.
|
||||
*/
|
||||
const autobuildLanguagesWithoutGo = autobuildLanguages.filter(
|
||||
(l) => l !== KnownLanguage.go,
|
||||
(l) => l !== BuiltInLanguage.go,
|
||||
);
|
||||
|
||||
const languages: Language[] = [];
|
||||
@@ -84,7 +84,7 @@ export async function determineAutobuildLanguages(
|
||||
// If Go is requested, run the Go autobuilder last to ensure it doesn't
|
||||
// interfere with the other autobuilder.
|
||||
if (autobuildLanguages.length !== autobuildLanguagesWithoutGo.length) {
|
||||
languages.push(KnownLanguage.go);
|
||||
languages.push(BuiltInLanguage.go);
|
||||
}
|
||||
|
||||
logger.debug(`Will autobuild ${languages.join(" and ")}.`);
|
||||
@@ -156,7 +156,7 @@ export async function runAutobuild(
|
||||
) {
|
||||
logger.startGroup(`Attempting to automatically build ${language} code`);
|
||||
const codeQL = await getCodeQL(config.codeQLCmd);
|
||||
if (language === KnownLanguage.cpp) {
|
||||
if (language === BuiltInLanguage.cpp) {
|
||||
await setupCppAutobuild(codeQL, logger);
|
||||
}
|
||||
if (config.buildMode) {
|
||||
@@ -164,7 +164,7 @@ export async function runAutobuild(
|
||||
} else {
|
||||
await codeQL.runAutobuild(config, language);
|
||||
}
|
||||
if (language === KnownLanguage.go) {
|
||||
if (language === BuiltInLanguage.go) {
|
||||
core.exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true");
|
||||
}
|
||||
logger.endGroup();
|
||||
|
||||
@@ -299,6 +299,20 @@ test("wrapCliConfigurationError - swift build failed", (t) => {
|
||||
t.true(wrappedError instanceof ConfigurationError);
|
||||
});
|
||||
|
||||
test("wrapCliConfigurationError - swift incompatible os", (t) => {
|
||||
const commandError = new CommandInvocationError(
|
||||
"codeql",
|
||||
["swift/tools/autobuild.sh"],
|
||||
1,
|
||||
"2026-04-01 18:35:00 EST ERRO [extractor/main] [incompatible-os] Currently, Swift analysis is only supported on macOS. (IncompatibleOs.cpp:26)",
|
||||
);
|
||||
const cliError = new CliError(commandError);
|
||||
|
||||
const wrappedError = wrapCliConfigurationError(cliError);
|
||||
|
||||
t.true(wrappedError instanceof ConfigurationError);
|
||||
});
|
||||
|
||||
test("wrapCliConfigurationError - pack cannot be found", (t) => {
|
||||
const commandError = new CommandInvocationError(
|
||||
"codeql",
|
||||
|
||||
@@ -144,6 +144,7 @@ export enum CliConfigErrorCategory {
|
||||
OutOfMemoryOrDisk = "OutOfMemoryOrDisk",
|
||||
PackCannotBeFound = "PackCannotBeFound",
|
||||
PackMissingAuth = "PackMissingAuth",
|
||||
SwiftIncompatibleOs = "SwiftIncompatibleOs",
|
||||
SwiftBuildFailed = "SwiftBuildFailed",
|
||||
UnsupportedBuildMode = "UnsupportedBuildMode",
|
||||
}
|
||||
@@ -281,6 +282,12 @@ const cliErrorsConfig: Record<CliConfigErrorCategory, CliErrorConfiguration> = {
|
||||
),
|
||||
],
|
||||
},
|
||||
[CliConfigErrorCategory.SwiftIncompatibleOs]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp("\\[incompatible-os\\]"),
|
||||
new RegExp("Swift analysis is only supported on macOS"),
|
||||
],
|
||||
},
|
||||
[CliConfigErrorCategory.UnsupportedBuildMode]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp(
|
||||
|
||||
+61
-39
@@ -21,7 +21,7 @@ import {
|
||||
import type { Config } from "./config-utils";
|
||||
import * as defaults from "./defaults.json";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { KnownLanguage } from "./languages";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import {
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
mockBundleDownloadApi,
|
||||
makeVersionInfo,
|
||||
createTestConfig,
|
||||
makeMacro,
|
||||
} from "./testing-utils";
|
||||
import { ToolsDownloadStatusReport } from "./tools-download";
|
||||
import * as util from "./util";
|
||||
@@ -46,7 +47,7 @@ test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
|
||||
stubConfig = createTestConfig({
|
||||
languages: [KnownLanguage.cpp],
|
||||
languages: [BuiltInLanguage.cpp],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,8 +71,10 @@ async function installIntoToolcache({
|
||||
tmpDir,
|
||||
util.GitHubVariant.GHES,
|
||||
cliVersion !== undefined
|
||||
? { cliVersion, tagName }
|
||||
? { enabledVersions: [{ cliVersion, tagName }] }
|
||||
: SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
createFeatures([]),
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -115,7 +118,7 @@ async function stubCodeql(): Promise<codeql.CodeQL> {
|
||||
sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6"));
|
||||
sinon
|
||||
.stub(codeqlObject, "isTracedLanguage")
|
||||
.withArgs(KnownLanguage.cpp)
|
||||
.withArgs(BuiltInLanguage.cpp)
|
||||
.resolves(true);
|
||||
return codeqlObject;
|
||||
}
|
||||
@@ -143,6 +146,8 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -175,6 +180,8 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -214,6 +221,8 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -264,6 +273,8 @@ for (const {
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -284,11 +295,11 @@ for (const {
|
||||
for (const toolcacheVersion of [
|
||||
// Test that we use the tools from the toolcache when `SAMPLE_DEFAULT_CLI_VERSION` is requested
|
||||
// and `SAMPLE_DEFAULT_CLI_VERSION-` is in the toolcache.
|
||||
SAMPLE_DEFAULT_CLI_VERSION.cliVersion,
|
||||
`${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}-20230101`,
|
||||
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
|
||||
`${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion}-20230101`,
|
||||
]) {
|
||||
test.serial(
|
||||
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.cliVersion} is requested and ` +
|
||||
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion} is requested and ` +
|
||||
`${toolcacheVersion} is installed`,
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
@@ -308,11 +319,16 @@ for (const toolcacheVersion of [
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
);
|
||||
t.is(result.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
|
||||
t.is(
|
||||
result.toolsVersion,
|
||||
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
|
||||
);
|
||||
t.is(result.toolsSource, ToolsSource.Toolcache);
|
||||
t.is(result.toolsDownloadStatusReport?.combinedDurationMs, undefined);
|
||||
t.is(result.toolsDownloadStatusReport?.downloadDurationMs, undefined);
|
||||
@@ -342,9 +358,15 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.GHES,
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
},
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -384,9 +406,15 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.GHES,
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
},
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -426,6 +454,8 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -467,6 +497,8 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.DOTCOM,
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
undefined, // rawLanguages
|
||||
false, // useOverlayAwareDefaultCliVersion
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
@@ -540,7 +572,7 @@ test.serial("getExtraOptions throws for bad content", (t) => {
|
||||
});
|
||||
|
||||
// Test macro for ensuring different variants of injected augmented configurations
|
||||
const injectedConfigMacro = test.macro({
|
||||
const injectedConfigMacro = makeMacro({
|
||||
exec: async (
|
||||
t: ExecutionContext<unknown>,
|
||||
augmentationProperties: AugmentationProperties,
|
||||
@@ -590,9 +622,8 @@ const injectedConfigMacro = test.macro({
|
||||
`databaseInitCluster() injected config: ${providedTitle}`,
|
||||
});
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"basic",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
},
|
||||
@@ -600,9 +631,8 @@ test.serial(
|
||||
{},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected packs from input",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
packsInput: ["xxx", "yyy"],
|
||||
@@ -613,9 +643,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected packs from input with existing packs combines",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
packsInputCombines: true,
|
||||
@@ -635,9 +664,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected packs from input with existing packs overrides",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
packsInput: ["xxx", "yyy"],
|
||||
@@ -655,9 +683,8 @@ test.serial(
|
||||
);
|
||||
|
||||
// similar, but with queries
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected queries from input",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInput: [{ uses: "xxx" }, { uses: "yyy" }],
|
||||
@@ -675,9 +702,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected queries from input overrides",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInput: [{ uses: "xxx" }, { uses: "yyy" }],
|
||||
@@ -699,9 +725,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected queries from input combines",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInputCombines: true,
|
||||
@@ -727,9 +752,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected queries from input combines 2",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInputCombines: true,
|
||||
@@ -749,9 +773,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"injected queries and packs, but empty",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInputCombines: true,
|
||||
@@ -768,9 +791,8 @@ test.serial(
|
||||
{},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"repo property queries have the highest precedence",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInputCombines: true,
|
||||
@@ -790,9 +812,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"repo property queries combines with queries input",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInputCombines: false,
|
||||
@@ -817,9 +838,8 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
injectedConfigMacro.serial(
|
||||
"repo property queries combines everything else",
|
||||
injectedConfigMacro,
|
||||
{
|
||||
...defaultAugmentationProperties,
|
||||
queriesInputCombines: true,
|
||||
@@ -956,7 +976,8 @@ test.serial("runTool summarizes autobuilder errors", async (t) => {
|
||||
sinon.stub(io, "which").resolves("");
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => await codeqlObject.runAutobuild(stubConfig, KnownLanguage.java),
|
||||
async () =>
|
||||
await codeqlObject.runAutobuild(stubConfig, BuiltInLanguage.java),
|
||||
{
|
||||
instanceOf: util.ConfigurationError,
|
||||
message:
|
||||
@@ -982,7 +1003,8 @@ test.serial("runTool truncates long autobuilder errors", async (t) => {
|
||||
sinon.stub(io, "which").resolves("");
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => await codeqlObject.runAutobuild(stubConfig, KnownLanguage.java),
|
||||
async () =>
|
||||
await codeqlObject.runAutobuild(stubConfig, BuiltInLanguage.java),
|
||||
{
|
||||
instanceOf: util.ConfigurationError,
|
||||
message:
|
||||
|
||||
+12
-28
@@ -24,11 +24,8 @@ import {
|
||||
import { isAnalyzingDefaultBranch } from "./git-utils";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
OverlayDatabaseMode,
|
||||
writeBaseDatabaseOidsFile,
|
||||
writeOverlayChangesFile,
|
||||
} from "./overlay";
|
||||
import { writeBaseDatabaseOidsFile, writeOverlayChangesFile } from "./overlay";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import * as setupCodeql from "./setup-codeql";
|
||||
import { ZstdAvailability } from "./tar";
|
||||
import { ToolsDownloadStatusReport } from "./tools-download";
|
||||
@@ -285,34 +282,21 @@ const CODEQL_MINIMUM_VERSION = "2.17.6";
|
||||
/**
|
||||
* This version will shortly become the oldest version of CodeQL that the Action will run with.
|
||||
*/
|
||||
const CODEQL_NEXT_MINIMUM_VERSION = "2.17.6";
|
||||
const CODEQL_NEXT_MINIMUM_VERSION = "2.19.4";
|
||||
|
||||
/**
|
||||
* This is the version of GHES that was most recently deprecated.
|
||||
*/
|
||||
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13";
|
||||
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15";
|
||||
|
||||
/**
|
||||
* This is the deprecation date for the version of GHES that was most recently deprecated.
|
||||
*/
|
||||
const GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19";
|
||||
const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09";
|
||||
|
||||
/** The CLI verbosity level to use for extraction in debug mode. */
|
||||
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
|
||||
|
||||
/*
|
||||
* Deprecated in favor of ToolsFeature.
|
||||
*
|
||||
* Versions of CodeQL that version-flag certain functionality in the Action.
|
||||
* For convenience, please keep these in descending order. Once a version
|
||||
* flag is older than the oldest supported version above, it may be removed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Versions 2.17.1+ of the CodeQL CLI support the `--cache-cleanup` option.
|
||||
*/
|
||||
const CODEQL_VERSION_CACHE_CLEANUP = "2.17.1";
|
||||
|
||||
/**
|
||||
* Set up CodeQL CLI access.
|
||||
*
|
||||
@@ -321,6 +305,8 @@ const CODEQL_VERSION_CACHE_CLEANUP = "2.17.1";
|
||||
* @param tempDir
|
||||
* @param variant
|
||||
* @param defaultCliVersion
|
||||
* @param rawLanguages Raw set of languages.
|
||||
* @param useOverlayAwareDefaultCliVersion Whether to select an overlay-aware default CLI version.
|
||||
* @param features Information about the features that are enabled.
|
||||
* @param logger
|
||||
* @param checkVersion Whether to check that CodeQL CLI meets the minimum
|
||||
@@ -333,6 +319,8 @@ export async function setupCodeQL(
|
||||
tempDir: string,
|
||||
variant: util.GitHubVariant,
|
||||
defaultCliVersion: CodeQLDefaultVersionInfo,
|
||||
rawLanguages: string[] | undefined,
|
||||
useOverlayAwareDefaultCliVersion: boolean,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
checkVersion: boolean,
|
||||
@@ -356,6 +344,8 @@ export async function setupCodeQL(
|
||||
tempDir,
|
||||
variant,
|
||||
defaultCliVersion,
|
||||
rawLanguages,
|
||||
useOverlayAwareDefaultCliVersion,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
@@ -891,19 +881,13 @@ async function getCodeQLForCmd(
|
||||
config: Config,
|
||||
cleanupLevel: CleanupLevel,
|
||||
): Promise<void> {
|
||||
const cacheCleanupFlag = (await util.codeQlVersionAtLeast(
|
||||
this,
|
||||
CODEQL_VERSION_CACHE_CLEANUP,
|
||||
))
|
||||
? "--cache-cleanup"
|
||||
: "--mode";
|
||||
for (const language of config.languages) {
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
"cleanup",
|
||||
databasePath,
|
||||
`${cacheCleanupFlag}=${cleanupLevel}`,
|
||||
`--cache-cleanup=${cleanupLevel}`,
|
||||
...getExtraOptionsFromEnv(["database", "cleanup"]),
|
||||
];
|
||||
await runCli(cmd, codeqlArgs);
|
||||
|
||||
+282
-194
File diff suppressed because it is too large
Load Diff
+100
-43
@@ -2,10 +2,12 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { performance } from "perf_hooks";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
import {
|
||||
getActionVersion,
|
||||
getOptionalInput,
|
||||
isAnalyzingPullRequest,
|
||||
isDynamicWorkflow,
|
||||
} from "./actions-util";
|
||||
@@ -41,17 +43,19 @@ import {
|
||||
getGeneratedFiles,
|
||||
getGitRoot,
|
||||
getGitVersionOrThrow,
|
||||
GIT_MINIMUM_VERSION_FOR_OVERLAY,
|
||||
GIT_MINIMUM_VERSION_FOR_OVERLAY_WITH_SUBMODULES,
|
||||
GitVersionInfo,
|
||||
hasSubmodules,
|
||||
isAnalyzingDefaultBranch,
|
||||
} from "./git-utils";
|
||||
import { KnownLanguage, Language } from "./languages";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { CODEQL_OVERLAY_MINIMUM_VERSION, OverlayDatabaseMode } from "./overlay";
|
||||
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
|
||||
import {
|
||||
addOverlayDisablementDiagnostics,
|
||||
OverlayDisabledReason,
|
||||
} from "./overlay/diagnostics";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import { shouldSkipOverlayAnalysis } from "./overlay/status";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import { ToolsFeature } from "./tools-features";
|
||||
@@ -72,6 +76,7 @@ import {
|
||||
Result,
|
||||
Success,
|
||||
Failure,
|
||||
isHostedRunner,
|
||||
} from "./util";
|
||||
|
||||
/**
|
||||
@@ -269,10 +274,10 @@ async function getSupportedLanguageMap(
|
||||
for (const extractor of Object.keys(resolveResult.extractors)) {
|
||||
// If the CLI supports resolving languages with default queries, use these
|
||||
// as the set of supported languages. Otherwise, require the language to be
|
||||
// a known language.
|
||||
// a built-in language.
|
||||
if (
|
||||
resolveSupportedLanguagesUsingCli ||
|
||||
KnownLanguage[extractor] !== undefined
|
||||
BuiltInLanguage[extractor] !== undefined
|
||||
) {
|
||||
supportedLanguages[extractor] = extractor;
|
||||
}
|
||||
@@ -402,6 +407,7 @@ export async function getLanguages(
|
||||
return languages;
|
||||
}
|
||||
|
||||
/** Splits the `languages` input into a list of raw languages without checking if they are supported by CodeQL. */
|
||||
export function getRawLanguagesNoAutodetect(
|
||||
languagesInput: string | undefined,
|
||||
): string[] {
|
||||
@@ -452,7 +458,6 @@ export interface InitConfigInputs {
|
||||
configInput: string | undefined;
|
||||
buildModeInput: string | undefined;
|
||||
ramInput: string | undefined;
|
||||
trapCachingEnabled: boolean;
|
||||
dependencyCachingEnabled: string | undefined;
|
||||
debugMode: boolean;
|
||||
debugArtifactName: string;
|
||||
@@ -482,7 +487,6 @@ export async function initActionState(
|
||||
packsInput,
|
||||
buildModeInput,
|
||||
dbLocation,
|
||||
trapCachingEnabled,
|
||||
dependencyCachingEnabled,
|
||||
debugMode,
|
||||
debugArtifactName,
|
||||
@@ -540,13 +544,6 @@ export async function initActionState(
|
||||
};
|
||||
}
|
||||
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
trapCachingEnabled,
|
||||
codeql,
|
||||
languages,
|
||||
logger,
|
||||
);
|
||||
|
||||
// Compute the full Code Scanning configuration that combines the configuration from the
|
||||
// configuration file / `config` input with other inputs, such as `queries`.
|
||||
const computedConfig = generateCodeScanningConfig(
|
||||
@@ -569,8 +566,8 @@ export async function initActionState(
|
||||
debugMode,
|
||||
debugArtifactName,
|
||||
debugDatabaseName,
|
||||
trapCaches,
|
||||
trapCacheDownloadTime,
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
dependencyCachingEnabled: getCachingKind(dependencyCachingEnabled),
|
||||
dependencyCachingRestoredKeys: [],
|
||||
extraQueryExclusions: [],
|
||||
@@ -582,7 +579,6 @@ export async function initActionState(
|
||||
}
|
||||
|
||||
async function downloadCacheWithTime(
|
||||
trapCachingEnabled: boolean,
|
||||
codeQL: CodeQL,
|
||||
languages: Language[],
|
||||
logger: Logger,
|
||||
@@ -590,13 +586,9 @@ async function downloadCacheWithTime(
|
||||
trapCaches: { [language: string]: string };
|
||||
trapCacheDownloadTime: number;
|
||||
}> {
|
||||
let trapCaches: { [language: string]: string } = {};
|
||||
let trapCacheDownloadTime = 0;
|
||||
if (trapCachingEnabled) {
|
||||
const start = performance.now();
|
||||
trapCaches = await downloadTrapCaches(codeQL, languages, logger);
|
||||
trapCacheDownloadTime = performance.now() - start;
|
||||
}
|
||||
const start = performance.now();
|
||||
const trapCaches = await downloadTrapCaches(codeQL, languages, logger);
|
||||
const trapCacheDownloadTime = performance.now() - start;
|
||||
return { trapCaches, trapCacheDownloadTime };
|
||||
}
|
||||
|
||||
@@ -636,6 +628,7 @@ async function loadUserConfig(
|
||||
* without an entry will have overlay analysis disabled.
|
||||
*/
|
||||
const OVERLAY_ANALYSIS_FEATURES: Partial<Record<Language, Feature>> = {
|
||||
cpp: Feature.OverlayAnalysisCpp,
|
||||
csharp: Feature.OverlayAnalysisCsharp,
|
||||
go: Feature.OverlayAnalysisGo,
|
||||
java: Feature.OverlayAnalysisJava,
|
||||
@@ -647,6 +640,7 @@ const OVERLAY_ANALYSIS_FEATURES: Partial<Record<Language, Feature>> = {
|
||||
const OVERLAY_ANALYSIS_CODE_SCANNING_FEATURES: Partial<
|
||||
Record<Language, Feature>
|
||||
> = {
|
||||
cpp: Feature.OverlayAnalysisCodeScanningCpp,
|
||||
csharp: Feature.OverlayAnalysisCodeScanningCsharp,
|
||||
go: Feature.OverlayAnalysisCodeScanningGo,
|
||||
java: Feature.OverlayAnalysisCodeScanningJava,
|
||||
@@ -954,7 +948,7 @@ async function validateOverlayDatabaseMode(
|
||||
await Promise.all(
|
||||
languages.map(
|
||||
async (l) =>
|
||||
l !== KnownLanguage.go && // Workaround to allow overlay analysis for Go with any build
|
||||
l !== BuiltInLanguage.go && // Workaround to allow overlay analysis for Go with any build
|
||||
// mode, since it does not yet support BMN. The Go autobuilder and/or extractor will
|
||||
// ensure that overlay-base databases are only created for supported Go build setups,
|
||||
// and that we'll fall back to full databases in other cases.
|
||||
@@ -978,7 +972,8 @@ async function validateOverlayDatabaseMode(
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.IncompatibleCodeQl);
|
||||
}
|
||||
if ((await getGitRoot(sourceRoot)) === undefined) {
|
||||
const gitRoot = await getGitRoot(sourceRoot);
|
||||
if (gitRoot === undefined) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
`the source root "${sourceRoot}" is not inside a git repository. ` +
|
||||
@@ -986,21 +981,26 @@ async function validateOverlayDatabaseMode(
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.NoGitRoot);
|
||||
}
|
||||
if (gitVersion === undefined) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
"the Git version could not be determined. " +
|
||||
"Falling back to creating a normal full database instead.",
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.IncompatibleGit);
|
||||
}
|
||||
if (!gitVersion.isAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY)) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
`the installed Git version is older than ${GIT_MINIMUM_VERSION_FOR_OVERLAY}. ` +
|
||||
"Falling back to creating a normal full database instead.",
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.IncompatibleGit);
|
||||
if (hasSubmodules(gitRoot)) {
|
||||
if (gitVersion === undefined) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
"the repository has submodules and the Git version could not be determined. " +
|
||||
"Falling back to creating a normal full database instead.",
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.IncompatibleGit);
|
||||
}
|
||||
if (
|
||||
!gitVersion.isAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY_WITH_SUBMODULES)
|
||||
) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
"the repository has submodules and the installed Git version is older " +
|
||||
`than ${GIT_MINIMUM_VERSION_FOR_OVERLAY_WITH_SUBMODULES}. ` +
|
||||
"Falling back to creating a normal full database instead.",
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.IncompatibleGit);
|
||||
}
|
||||
}
|
||||
|
||||
return new Success({
|
||||
@@ -1009,6 +1009,50 @@ async function validateOverlayDatabaseMode(
|
||||
});
|
||||
}
|
||||
|
||||
export async function isTrapCachingEnabled(
|
||||
features: FeatureEnablement,
|
||||
overlayDatabaseMode: OverlayDatabaseMode,
|
||||
): Promise<boolean> {
|
||||
// If the workflow specified something, always respect that.
|
||||
const trapCaching = getOptionalInput("trap-caching");
|
||||
if (trapCaching !== undefined) return trapCaching === "true";
|
||||
|
||||
// On self-hosted runners which may have slow network access, disable TRAP caching by default.
|
||||
if (!isHostedRunner()) return false;
|
||||
|
||||
// If overlay analysis is enabled, then disable TRAP caching since overlay analysis supersedes it.
|
||||
// This change is gated behind a feature flag.
|
||||
if (
|
||||
overlayDatabaseMode !== OverlayDatabaseMode.None &&
|
||||
(await features.getValue(Feature.OverlayAnalysisDisableTrapCaching))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, enable TRAP caching.
|
||||
return true;
|
||||
}
|
||||
|
||||
async function setCppTrapCachingEnvironmentVariables(
|
||||
config: Config,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
if (config.languages.includes(BuiltInLanguage.cpp)) {
|
||||
const envVar = "CODEQL_EXTRACTOR_CPP_TRAP_CACHING";
|
||||
if (process.env[envVar]) {
|
||||
logger.info(
|
||||
`Environment variable ${envVar} already set, leaving it unchanged.`,
|
||||
);
|
||||
} else if (config.trapCaches[BuiltInLanguage.cpp]) {
|
||||
logger.info("Enabling TRAP caching for C/C++.");
|
||||
core.exportVariable(envVar, "true");
|
||||
} else {
|
||||
logger.debug(`Disabling TRAP caching for C/C++.`);
|
||||
core.exportVariable(envVar, "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dbLocationOrDefault(
|
||||
dbLocation: string | undefined,
|
||||
tempDir: string,
|
||||
@@ -1199,6 +1243,19 @@ export async function initConfig(
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
|
||||
if (await isTrapCachingEnabled(features, config.overlayDatabaseMode)) {
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
inputs.codeql,
|
||||
config.languages,
|
||||
logger,
|
||||
);
|
||||
config.trapCaches = trapCaches;
|
||||
config.trapCacheDownloadTime = trapCacheDownloadTime;
|
||||
}
|
||||
|
||||
await setCppTrapCachingEnvironmentVariables(config, logger);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -1483,7 +1540,7 @@ export async function parseBuildModeInput(
|
||||
}
|
||||
|
||||
if (
|
||||
languages.includes(KnownLanguage.csharp) &&
|
||||
languages.includes(BuiltInLanguage.csharp) &&
|
||||
(await features.getValue(Feature.DisableCsharpBuildless))
|
||||
) {
|
||||
logger.warning(
|
||||
@@ -1493,7 +1550,7 @@ export async function parseBuildModeInput(
|
||||
}
|
||||
|
||||
if (
|
||||
languages.includes(KnownLanguage.java) &&
|
||||
languages.includes(BuiltInLanguage.java) &&
|
||||
(await features.getValue(Feature.DisableJavaBuildlessEnabled))
|
||||
) {
|
||||
logger.warning(
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import test, { ExecutionContext } from "ava";
|
||||
|
||||
import { RepositoryProperties } from "../feature-flags/properties";
|
||||
import { KnownLanguage, Language } from "../languages";
|
||||
import { BuiltInLanguage, Language } from "../languages";
|
||||
import { getRunnerLogger } from "../logging";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
getRecordingLogger,
|
||||
LoggedMessage,
|
||||
makeMacro,
|
||||
} from "../testing-utils";
|
||||
import { ConfigurationError, prettyPrintPack } from "../util";
|
||||
|
||||
@@ -15,7 +16,7 @@ import * as dbConfig from "./db-config";
|
||||
/**
|
||||
* Test macro for ensuring the packs block is valid
|
||||
*/
|
||||
const parsePacksMacro = test.macro({
|
||||
const parsePacksMacro = makeMacro({
|
||||
exec: (
|
||||
t: ExecutionContext<unknown>,
|
||||
packsInput: string,
|
||||
@@ -33,7 +34,7 @@ const parsePacksMacro = test.macro({
|
||||
/**
|
||||
* Test macro for testing when the packs block is invalid
|
||||
*/
|
||||
const parsePacksErrorMacro = test.macro({
|
||||
const parsePacksErrorMacro = makeMacro({
|
||||
exec: (
|
||||
t: ExecutionContext<unknown>,
|
||||
packsInput: string,
|
||||
@@ -49,45 +50,42 @@ const parsePacksErrorMacro = test.macro({
|
||||
/**
|
||||
* Test macro for testing when the packs block is invalid
|
||||
*/
|
||||
const invalidPackNameMacro = test.macro({
|
||||
exec: (t: ExecutionContext, name: string) =>
|
||||
parsePacksErrorMacro.exec(
|
||||
const invalidPackNameMacro = makeMacro({
|
||||
exec: (t: ExecutionContext, arg: string) =>
|
||||
parsePacksErrorMacro.fn(
|
||||
t,
|
||||
name,
|
||||
[KnownLanguage.cpp],
|
||||
new RegExp(`^"${name}" is not a valid pack$`),
|
||||
arg,
|
||||
[BuiltInLanguage.cpp],
|
||||
new RegExp(`^"${arg}" is not a valid pack$`),
|
||||
),
|
||||
title: (_providedTitle: string | undefined, arg: string | undefined) =>
|
||||
`Invalid pack string: ${arg}`,
|
||||
});
|
||||
|
||||
test("no packs", parsePacksMacro, "", [], undefined);
|
||||
test("two packs", parsePacksMacro, "a/b,c/d@1.2.3", [KnownLanguage.cpp], {
|
||||
[KnownLanguage.cpp]: ["a/b", "c/d@1.2.3"],
|
||||
parsePacksMacro("no packs", "", [], undefined);
|
||||
parsePacksMacro("two packs", "a/b,c/d@1.2.3", [BuiltInLanguage.cpp], {
|
||||
[BuiltInLanguage.cpp]: ["a/b", "c/d@1.2.3"],
|
||||
});
|
||||
test(
|
||||
parsePacksMacro(
|
||||
"two packs with spaces",
|
||||
parsePacksMacro,
|
||||
" a/b , c/d@1.2.3 ",
|
||||
[KnownLanguage.cpp],
|
||||
[BuiltInLanguage.cpp],
|
||||
{
|
||||
[KnownLanguage.cpp]: ["a/b", "c/d@1.2.3"],
|
||||
[BuiltInLanguage.cpp]: ["a/b", "c/d@1.2.3"],
|
||||
},
|
||||
);
|
||||
test(
|
||||
parsePacksErrorMacro(
|
||||
"two packs with language",
|
||||
parsePacksErrorMacro,
|
||||
"a/b,c/d@1.2.3",
|
||||
[KnownLanguage.cpp, KnownLanguage.java],
|
||||
[BuiltInLanguage.cpp, BuiltInLanguage.java],
|
||||
new RegExp(
|
||||
"Cannot specify a 'packs' input in a multi-language analysis. " +
|
||||
"Use a codeql-config.yml file instead and specify packs by language.",
|
||||
),
|
||||
);
|
||||
|
||||
test(
|
||||
parsePacksMacro(
|
||||
"packs with other valid names",
|
||||
parsePacksMacro,
|
||||
[
|
||||
// ranges are ok
|
||||
"c/d@1.0",
|
||||
@@ -106,9 +104,9 @@ test(
|
||||
// (globbing is not done)
|
||||
"c/d@1.2.3:+*)_(",
|
||||
].join(","),
|
||||
[KnownLanguage.cpp],
|
||||
[BuiltInLanguage.cpp],
|
||||
{
|
||||
[KnownLanguage.cpp]: [
|
||||
[BuiltInLanguage.cpp]: [
|
||||
"c/d@1.0",
|
||||
"c/d@~1.0.0",
|
||||
"c/d@~1.0.0:a/b",
|
||||
@@ -123,23 +121,23 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(invalidPackNameMacro, "c"); // all packs require at least a scope and a name
|
||||
test(invalidPackNameMacro, "c-/d");
|
||||
test(invalidPackNameMacro, "-c/d");
|
||||
test(invalidPackNameMacro, "c/d_d");
|
||||
test(invalidPackNameMacro, "c/d@@");
|
||||
test(invalidPackNameMacro, "c/d@1.0.0:");
|
||||
test(invalidPackNameMacro, "c/d:");
|
||||
test(invalidPackNameMacro, "c/d:/a");
|
||||
test(invalidPackNameMacro, "@1.0.0:a");
|
||||
test(invalidPackNameMacro, "c/d@../a");
|
||||
test(invalidPackNameMacro, "c/d@b/../a");
|
||||
test(invalidPackNameMacro, "c/d:z@1");
|
||||
invalidPackNameMacro.test("c"); // all packs require at least a scope and a name
|
||||
invalidPackNameMacro.test("c-/d");
|
||||
invalidPackNameMacro.test("-c/d");
|
||||
invalidPackNameMacro.test("c/d_d");
|
||||
invalidPackNameMacro.test("c/d@@");
|
||||
invalidPackNameMacro.test("c/d@1.0.0:");
|
||||
invalidPackNameMacro.test("c/d:");
|
||||
invalidPackNameMacro.test("c/d:/a");
|
||||
invalidPackNameMacro.test("@1.0.0:a");
|
||||
invalidPackNameMacro.test("c/d@../a");
|
||||
invalidPackNameMacro.test("c/d@b/../a");
|
||||
invalidPackNameMacro.test("c/d:z@1");
|
||||
|
||||
/**
|
||||
* Test macro for pretty printing pack specs
|
||||
*/
|
||||
const packSpecPrettyPrintingMacro = test.macro({
|
||||
const packSpecPrettyPrintingMacro = makeMacro({
|
||||
exec: (t: ExecutionContext, packStr: string, packObj: dbConfig.Pack) => {
|
||||
const parsed = dbConfig.parsePacksSpecification(packStr);
|
||||
t.deepEqual(parsed, packObj, "parsed pack spec is correct");
|
||||
@@ -163,36 +161,35 @@ const packSpecPrettyPrintingMacro = test.macro({
|
||||
) => `Prettyprint pack spec: '${packStr}'`,
|
||||
});
|
||||
|
||||
test(packSpecPrettyPrintingMacro, "a/b", {
|
||||
packSpecPrettyPrintingMacro.test("a/b", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: undefined,
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3", {
|
||||
packSpecPrettyPrintingMacro.test("a/b@~1.2.3", {
|
||||
name: "a/b",
|
||||
version: "~1.2.3",
|
||||
path: undefined,
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3:abc/def", {
|
||||
packSpecPrettyPrintingMacro.test("a/b@~1.2.3:abc/def", {
|
||||
name: "a/b",
|
||||
version: "~1.2.3",
|
||||
path: "abc/def",
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, "a/b:abc/def", {
|
||||
packSpecPrettyPrintingMacro.test("a/b:abc/def", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: "abc/def",
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, " a/b:abc/def ", {
|
||||
packSpecPrettyPrintingMacro.test(" a/b:abc/def ", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: "abc/def",
|
||||
});
|
||||
|
||||
const calculateAugmentationMacro = test.macro({
|
||||
const calculateAugmentationMacro = makeMacro({
|
||||
exec: async (
|
||||
t: ExecutionContext,
|
||||
_title: string,
|
||||
rawPacksInput: string | undefined,
|
||||
rawQueriesInput: string | undefined,
|
||||
languages: Language[],
|
||||
@@ -207,27 +204,25 @@ const calculateAugmentationMacro = test.macro({
|
||||
);
|
||||
t.deepEqual(actualAugmentationProperties, expectedAugmentationProperties);
|
||||
},
|
||||
title: (_, title) => `Calculate Augmentation: ${title}`,
|
||||
title: (title) => `Calculate Augmentation: ${title}`,
|
||||
});
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"All empty",
|
||||
undefined,
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
{
|
||||
...dbConfig.defaultAugmentationProperties,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"With queries",
|
||||
undefined,
|
||||
" a, b , c, d",
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
{
|
||||
...dbConfig.defaultAugmentationProperties,
|
||||
@@ -235,12 +230,11 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"With queries combining",
|
||||
undefined,
|
||||
" + a, b , c, d ",
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
{
|
||||
...dbConfig.defaultAugmentationProperties,
|
||||
@@ -249,12 +243,11 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"With packs",
|
||||
" codeql/a , codeql/b , codeql/c , codeql/d ",
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
{
|
||||
...dbConfig.defaultAugmentationProperties,
|
||||
@@ -262,12 +255,11 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"With packs combining",
|
||||
" + codeql/a, codeql/b, codeql/c, codeql/d",
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
{
|
||||
...dbConfig.defaultAugmentationProperties,
|
||||
@@ -276,12 +268,11 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"With repo property queries",
|
||||
undefined,
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{
|
||||
"github-codeql-extra-queries": "a, b, c, d",
|
||||
},
|
||||
@@ -294,12 +285,11 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationMacro,
|
||||
calculateAugmentationMacro(
|
||||
"With repo property queries combining",
|
||||
undefined,
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{
|
||||
"github-codeql-extra-queries": "+ a, b, c, d",
|
||||
},
|
||||
@@ -312,10 +302,9 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
const calculateAugmentationErrorMacro = test.macro({
|
||||
const calculateAugmentationErrorMacro = makeMacro({
|
||||
exec: async (
|
||||
t: ExecutionContext,
|
||||
_title: string,
|
||||
rawPacksInput: string | undefined,
|
||||
rawQueriesInput: string | undefined,
|
||||
languages: Language[],
|
||||
@@ -333,53 +322,48 @@ const calculateAugmentationErrorMacro = test.macro({
|
||||
{ message: expectedError },
|
||||
);
|
||||
},
|
||||
title: (_, title) => `Calculate Augmentation Error: ${title}`,
|
||||
title: (title) => `Calculate Augmentation Error: ${title}`,
|
||||
});
|
||||
|
||||
test(
|
||||
calculateAugmentationErrorMacro,
|
||||
calculateAugmentationErrorMacro(
|
||||
"Plus (+) with nothing else (queries)",
|
||||
undefined,
|
||||
" + ",
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
/The workflow property "queries" is invalid/,
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationErrorMacro,
|
||||
calculateAugmentationErrorMacro(
|
||||
"Plus (+) with nothing else (packs)",
|
||||
" + ",
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
/The workflow property "packs" is invalid/,
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationErrorMacro,
|
||||
calculateAugmentationErrorMacro(
|
||||
"Plus (+) with nothing else (repo property queries)",
|
||||
undefined,
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{
|
||||
"github-codeql-extra-queries": " + ",
|
||||
},
|
||||
/The repository property "github-codeql-extra-queries" is invalid/,
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationErrorMacro,
|
||||
calculateAugmentationErrorMacro(
|
||||
"Packs input with multiple languages",
|
||||
" + a/b, c/d ",
|
||||
undefined,
|
||||
[KnownLanguage.javascript, KnownLanguage.java],
|
||||
[BuiltInLanguage.javascript, BuiltInLanguage.java],
|
||||
{},
|
||||
/Cannot specify a 'packs' input in a multi-language analysis/,
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationErrorMacro,
|
||||
calculateAugmentationErrorMacro(
|
||||
"Packs input with no languages",
|
||||
" + a/b, c/d ",
|
||||
undefined,
|
||||
@@ -388,12 +372,11 @@ test(
|
||||
/No languages specified/,
|
||||
);
|
||||
|
||||
test(
|
||||
calculateAugmentationErrorMacro,
|
||||
calculateAugmentationErrorMacro(
|
||||
"Invalid packs",
|
||||
" a-pack-without-a-scope ",
|
||||
undefined,
|
||||
[KnownLanguage.javascript],
|
||||
[BuiltInLanguage.javascript],
|
||||
{},
|
||||
/"a-pack-without-a-scope" is not a valid pack/,
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createStubCodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { cleanupAndUploadDatabases } from "./database-upload";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { KnownLanguage } from "./languages";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
@@ -45,7 +45,7 @@ const testApiDetails: GitHubApiDetails = {
|
||||
|
||||
function getTestConfig(tmpDir: string): Config {
|
||||
return createTestConfig({
|
||||
languages: [KnownLanguage.javascript],
|
||||
languages: [BuiltInLanguage.javascript],
|
||||
dbLocation: tmpDir,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Config } from "./config-utils";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import * as util from "./util";
|
||||
import { asHTTPError, bundleDb, CleanupLevel, parseGitHubUrl } from "./util";
|
||||
|
||||
@@ -263,7 +263,7 @@ export function getArtifactSuffix(matrix: string | undefined): string {
|
||||
try {
|
||||
const matrixObject = JSON.parse(matrix);
|
||||
if (json.isObject(matrixObject)) {
|
||||
for (const matrixKey of Object.keys(matrixObject as object).sort())
|
||||
for (const matrixKey of Object.keys(matrixObject).sort())
|
||||
suffix += `-${matrixObject[matrixKey]}`;
|
||||
} else {
|
||||
core.warning("User-specified `matrix` input is not an object.");
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.24.3",
|
||||
"cliVersion": "2.24.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.24.2",
|
||||
"priorCliVersion": "2.24.2"
|
||||
"bundleVersion": "codeql-bundle-v2.25.4",
|
||||
"cliVersion": "2.25.4",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.3",
|
||||
"priorCliVersion": "2.25.3"
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
CacheStoreResult,
|
||||
} from "./dependency-caching";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { KnownLanguage } from "./languages";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import {
|
||||
setupTests,
|
||||
createFeatures,
|
||||
@@ -179,7 +179,7 @@ test("checkHashPatterns - logs when no patterns match", async (t) => {
|
||||
const result = await checkHashPatterns(
|
||||
codeql,
|
||||
features,
|
||||
KnownLanguage.csharp,
|
||||
BuiltInLanguage.csharp,
|
||||
config,
|
||||
"download",
|
||||
getRecordingLogger(messages),
|
||||
@@ -208,7 +208,7 @@ test("checkHashPatterns - returns patterns when patterns match", async (t) => {
|
||||
const result = await checkHashPatterns(
|
||||
codeql,
|
||||
features,
|
||||
KnownLanguage.csharp,
|
||||
BuiltInLanguage.csharp,
|
||||
config,
|
||||
"upload",
|
||||
getRecordingLogger(messages),
|
||||
@@ -270,7 +270,7 @@ test.serial(
|
||||
const keyWithFeature = await cacheKey(
|
||||
codeql,
|
||||
createFeatures([Feature.CsharpNewCacheKey]),
|
||||
KnownLanguage.csharp,
|
||||
BuiltInLanguage.csharp,
|
||||
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
|
||||
[],
|
||||
);
|
||||
@@ -288,12 +288,12 @@ test.serial(
|
||||
const result = await downloadDependencyCaches(
|
||||
codeql,
|
||||
createFeatures([]),
|
||||
[KnownLanguage.csharp],
|
||||
[BuiltInLanguage.csharp],
|
||||
logger,
|
||||
);
|
||||
const statusReport = result.statusReport;
|
||||
t.is(statusReport.length, 1);
|
||||
t.is(statusReport[0].language, KnownLanguage.csharp);
|
||||
t.is(statusReport[0].language, BuiltInLanguage.csharp);
|
||||
t.is(statusReport[0].hit_kind, CacheHitKind.Miss);
|
||||
t.deepEqual(result.restoredKeys, []);
|
||||
t.assert(restoreCacheStub.calledOnce);
|
||||
@@ -316,7 +316,7 @@ test.serial(
|
||||
const keyWithFeature = await cacheKey(
|
||||
codeql,
|
||||
features,
|
||||
KnownLanguage.csharp,
|
||||
BuiltInLanguage.csharp,
|
||||
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
|
||||
[],
|
||||
);
|
||||
@@ -334,14 +334,14 @@ test.serial(
|
||||
const result = await downloadDependencyCaches(
|
||||
codeql,
|
||||
features,
|
||||
[KnownLanguage.csharp],
|
||||
[BuiltInLanguage.csharp],
|
||||
logger,
|
||||
);
|
||||
|
||||
// Check that the status report for telemetry indicates that one cache was restored with an exact match.
|
||||
const statusReport = result.statusReport;
|
||||
t.is(statusReport.length, 1);
|
||||
t.is(statusReport[0].language, KnownLanguage.csharp);
|
||||
t.is(statusReport[0].language, BuiltInLanguage.csharp);
|
||||
t.is(statusReport[0].hit_kind, CacheHitKind.Exact);
|
||||
|
||||
// Check that the restored key has been returned.
|
||||
@@ -380,7 +380,7 @@ test.serial(
|
||||
const keyWithFeature = await cacheKey(
|
||||
codeql,
|
||||
features,
|
||||
KnownLanguage.csharp,
|
||||
BuiltInLanguage.csharp,
|
||||
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
|
||||
[],
|
||||
);
|
||||
@@ -398,14 +398,14 @@ test.serial(
|
||||
const result = await downloadDependencyCaches(
|
||||
codeql,
|
||||
features,
|
||||
[KnownLanguage.csharp],
|
||||
[BuiltInLanguage.csharp],
|
||||
logger,
|
||||
);
|
||||
|
||||
// Check that the status report for telemetry indicates that one cache was restored with a partial match.
|
||||
const statusReport = result.statusReport;
|
||||
t.is(statusReport.length, 1);
|
||||
t.is(statusReport[0].language, KnownLanguage.csharp);
|
||||
t.is(statusReport[0].language, BuiltInLanguage.csharp);
|
||||
t.is(statusReport[0].hit_kind, CacheHitKind.Partial);
|
||||
|
||||
// Check that the restored key has been returned.
|
||||
@@ -426,7 +426,7 @@ test("uploadDependencyCaches - skips upload for a language with no cache config"
|
||||
const logger = getRecordingLogger(messages);
|
||||
const features = createFeatures([]);
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.actions],
|
||||
languages: [BuiltInLanguage.actions],
|
||||
});
|
||||
|
||||
const result = await uploadDependencyCaches(codeql, features, config, logger);
|
||||
@@ -444,7 +444,7 @@ test.serial(
|
||||
const logger = getRecordingLogger(messages);
|
||||
const features = createFeatures([]);
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.go],
|
||||
languages: [BuiltInLanguage.go],
|
||||
});
|
||||
|
||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
||||
@@ -457,7 +457,7 @@ test.serial(
|
||||
logger,
|
||||
);
|
||||
t.is(result.length, 1);
|
||||
t.is(result[0].language, KnownLanguage.go);
|
||||
t.is(result[0].language, BuiltInLanguage.go);
|
||||
t.is(result[0].result, CacheStoreResult.NoHash);
|
||||
},
|
||||
);
|
||||
@@ -483,12 +483,12 @@ test.serial(
|
||||
const primaryCacheKey = await cacheKey(
|
||||
codeql,
|
||||
features,
|
||||
KnownLanguage.csharp,
|
||||
BuiltInLanguage.csharp,
|
||||
CSHARP_BASE_PATTERNS,
|
||||
);
|
||||
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.csharp],
|
||||
languages: [BuiltInLanguage.csharp],
|
||||
dependencyCachingRestoredKeys: [primaryCacheKey],
|
||||
});
|
||||
|
||||
@@ -499,7 +499,7 @@ test.serial(
|
||||
logger,
|
||||
);
|
||||
t.is(result.length, 1);
|
||||
t.is(result[0].language, KnownLanguage.csharp);
|
||||
t.is(result[0].language, BuiltInLanguage.csharp);
|
||||
t.is(result[0].result, CacheStoreResult.Duplicate);
|
||||
},
|
||||
);
|
||||
@@ -525,7 +525,7 @@ test.serial(
|
||||
sinon.stub(cachingUtils, "getTotalCacheSize").resolves(0);
|
||||
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.csharp],
|
||||
languages: [BuiltInLanguage.csharp],
|
||||
});
|
||||
|
||||
const result = await uploadDependencyCaches(
|
||||
@@ -535,7 +535,7 @@ test.serial(
|
||||
logger,
|
||||
);
|
||||
t.is(result.length, 1);
|
||||
t.is(result[0].language, KnownLanguage.csharp);
|
||||
t.is(result[0].language, BuiltInLanguage.csharp);
|
||||
t.is(result[0].result, CacheStoreResult.Empty);
|
||||
|
||||
checkExpectedLogMessages(t, messages, [
|
||||
@@ -566,7 +566,7 @@ test.serial(
|
||||
sinon.stub(actionsCache, "saveCache").resolves();
|
||||
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.csharp],
|
||||
languages: [BuiltInLanguage.csharp],
|
||||
});
|
||||
|
||||
const result = await uploadDependencyCaches(
|
||||
@@ -576,7 +576,7 @@ test.serial(
|
||||
logger,
|
||||
);
|
||||
t.is(result.length, 1);
|
||||
t.is(result[0].language, KnownLanguage.csharp);
|
||||
t.is(result[0].language, BuiltInLanguage.csharp);
|
||||
t.is(result[0].result, CacheStoreResult.Stored);
|
||||
t.is(result[0].upload_size_bytes, 1024);
|
||||
|
||||
@@ -608,7 +608,7 @@ test.serial(
|
||||
.throws(new actionsCache.ReserveCacheError("Already in use"));
|
||||
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.csharp],
|
||||
languages: [BuiltInLanguage.csharp],
|
||||
});
|
||||
|
||||
await t.notThrowsAsync(async () => {
|
||||
@@ -619,7 +619,7 @@ test.serial(
|
||||
logger,
|
||||
);
|
||||
t.is(result.length, 1);
|
||||
t.is(result[0].language, KnownLanguage.csharp);
|
||||
t.is(result[0].language, BuiltInLanguage.csharp);
|
||||
t.is(result[0].result, CacheStoreResult.Duplicate);
|
||||
|
||||
checkExpectedLogMessages(t, messages, ["Not uploading cache for"]);
|
||||
@@ -647,7 +647,7 @@ test.serial("uploadDependencyCaches - throws other exceptions", async (t) => {
|
||||
sinon.stub(actionsCache, "saveCache").throws();
|
||||
|
||||
const config = createTestConfig({
|
||||
languages: [KnownLanguage.csharp],
|
||||
languages: [BuiltInLanguage.csharp],
|
||||
});
|
||||
|
||||
await t.throwsAsync(async () => {
|
||||
@@ -659,7 +659,7 @@ test("getFeaturePrefix - returns empty string if no features are enabled", async
|
||||
const codeql = createStubCodeQL({});
|
||||
const features = createFeatures([]);
|
||||
|
||||
for (const knownLanguage of Object.values(KnownLanguage)) {
|
||||
for (const knownLanguage of Object.values(BuiltInLanguage)) {
|
||||
const result = await getFeaturePrefix(codeql, features, knownLanguage);
|
||||
t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`);
|
||||
}
|
||||
@@ -669,7 +669,11 @@ test("getFeaturePrefix - C# - returns prefix if CsharpNewCacheKey is enabled", a
|
||||
const codeql = createStubCodeQL({});
|
||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
||||
|
||||
const result = await getFeaturePrefix(codeql, features, KnownLanguage.csharp);
|
||||
const result = await getFeaturePrefix(
|
||||
codeql,
|
||||
features,
|
||||
BuiltInLanguage.csharp,
|
||||
);
|
||||
t.notDeepEqual(result, "");
|
||||
t.assert(result.endsWith("-"));
|
||||
// Check the length of the prefix, which should correspond to `cacheKeyHashLength` + 1 for the trailing `-`.
|
||||
@@ -680,9 +684,9 @@ test("getFeaturePrefix - non-C# - returns '' if CsharpNewCacheKey is enabled", a
|
||||
const codeql = createStubCodeQL({});
|
||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
||||
|
||||
for (const knownLanguage of Object.values(KnownLanguage)) {
|
||||
for (const knownLanguage of Object.values(BuiltInLanguage)) {
|
||||
// Skip C# since we expect a result for it, which is tested in the previous test.
|
||||
if (knownLanguage === KnownLanguage.csharp) {
|
||||
if (knownLanguage === BuiltInLanguage.csharp) {
|
||||
continue;
|
||||
}
|
||||
const result = await getFeaturePrefix(codeql, features, knownLanguage);
|
||||
@@ -694,7 +698,11 @@ test("getFeaturePrefix - C# - returns prefix if CsharpCacheBuildModeNone is enab
|
||||
const codeql = createStubCodeQL({});
|
||||
const features = createFeatures([Feature.CsharpCacheBuildModeNone]);
|
||||
|
||||
const result = await getFeaturePrefix(codeql, features, KnownLanguage.csharp);
|
||||
const result = await getFeaturePrefix(
|
||||
codeql,
|
||||
features,
|
||||
BuiltInLanguage.csharp,
|
||||
);
|
||||
t.notDeepEqual(result, "");
|
||||
t.assert(result.endsWith("-"));
|
||||
// Check the length of the prefix, which should correspond to `cacheKeyHashLength` + 1 for the trailing `-`.
|
||||
@@ -705,9 +713,9 @@ test("getFeaturePrefix - non-C# - returns '' if CsharpCacheBuildModeNone is enab
|
||||
const codeql = createStubCodeQL({});
|
||||
const features = createFeatures([Feature.CsharpCacheBuildModeNone]);
|
||||
|
||||
for (const knownLanguage of Object.values(KnownLanguage)) {
|
||||
for (const knownLanguage of Object.values(BuiltInLanguage)) {
|
||||
// Skip C# since we expect a result for it, which is tested in the previous test.
|
||||
if (knownLanguage === KnownLanguage.csharp) {
|
||||
if (knownLanguage === BuiltInLanguage.csharp) {
|
||||
continue;
|
||||
}
|
||||
const result = await getFeaturePrefix(codeql, features, knownLanguage);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { CodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { KnownLanguage, Language } from "./languages";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { getErrorMessage, getRequiredEnvParam } from "./util";
|
||||
|
||||
@@ -541,7 +541,7 @@ export async function getFeaturePrefix(
|
||||
}
|
||||
};
|
||||
|
||||
if (language === KnownLanguage.csharp) {
|
||||
if (language === BuiltInLanguage.csharp) {
|
||||
await addFeatureIfEnabled(Feature.CsharpNewCacheKey);
|
||||
await addFeatureIfEnabled(Feature.CsharpCacheBuildModeNone);
|
||||
}
|
||||
|
||||
+17
-2
@@ -72,6 +72,13 @@ let unwrittenDiagnostics: UnwrittenDiagnostic[] = [];
|
||||
*/
|
||||
let unwrittenDefaultLanguageDiagnostics: DiagnosticMessage[] = [];
|
||||
|
||||
/**
|
||||
* Counter used to generate a unique suffix for each diagnostic filename, so that
|
||||
* two diagnostics produced within the same millisecond do not overwrite each
|
||||
* other on disk.
|
||||
*/
|
||||
let diagnosticCounter = 0;
|
||||
|
||||
/**
|
||||
* Constructs a new diagnostic message with the specified id and name, as well as optional additional data.
|
||||
*
|
||||
@@ -167,10 +174,18 @@ function writeDiagnostic(
|
||||
// Create the directory if it doesn't exist yet.
|
||||
mkdirSync(diagnosticsPath, { recursive: true });
|
||||
|
||||
// Include a monotonically increasing suffix to avoid filename collisions
|
||||
// between diagnostics produced within the same millisecond.
|
||||
const uniqueSuffix = (diagnosticCounter++).toString();
|
||||
// We should only need to remove colons, but to be defensive, only allow a restricted set of
|
||||
// characters.
|
||||
const sanitizedTimestamp = diagnostic.timestamp.replace(
|
||||
/[^a-zA-Z0-9.-]/g,
|
||||
"",
|
||||
);
|
||||
const jsonPath = path.resolve(
|
||||
diagnosticsPath,
|
||||
// Remove colons from the timestamp as these are not allowed in Windows filenames.
|
||||
`codeql-action-${diagnostic.timestamp.replaceAll(":", "")}.json`,
|
||||
`codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`,
|
||||
);
|
||||
|
||||
writeFileSync(jsonPath, JSON.stringify(diagnostic));
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
mockCodeQLVersion,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
setupActionsVars,
|
||||
makeMacro,
|
||||
} from "./testing-utils";
|
||||
import { GitHubVariant, withTmpDir } from "./util";
|
||||
import type { GitHubVersion } from "./util";
|
||||
@@ -42,10 +43,9 @@ const defaultTestCase: DiffInformedAnalysisTestCase = {
|
||||
codeQLVersion: "2.21.0",
|
||||
};
|
||||
|
||||
const testShouldPerformDiffInformedAnalysis = test.macro({
|
||||
const testShouldPerformDiffInformedAnalysis = makeMacro({
|
||||
exec: async (
|
||||
t: ExecutionContext,
|
||||
_title: string,
|
||||
partialTestCase: Partial<DiffInformedAnalysisTestCase>,
|
||||
expectedResult: boolean,
|
||||
) => {
|
||||
@@ -94,18 +94,16 @@ const testShouldPerformDiffInformedAnalysis = test.macro({
|
||||
getPullRequestBranchesStub.restore();
|
||||
});
|
||||
},
|
||||
title: (_, title) => `shouldPerformDiffInformedAnalysis: ${title}`,
|
||||
title: (title) => `shouldPerformDiffInformedAnalysis: ${title}`,
|
||||
});
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns true in the default test case",
|
||||
{},
|
||||
true,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false when feature flag is disabled from the API",
|
||||
{
|
||||
featureEnabled: false,
|
||||
@@ -113,8 +111,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false when CODEQL_ACTION_DIFF_INFORMED_QUERIES is set to false",
|
||||
{
|
||||
featureEnabled: true,
|
||||
@@ -123,8 +120,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns true when CODEQL_ACTION_DIFF_INFORMED_QUERIES is set to true",
|
||||
{
|
||||
featureEnabled: false,
|
||||
@@ -133,8 +129,7 @@ test.serial(
|
||||
true,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false for CodeQL version 2.20.0",
|
||||
{
|
||||
codeQLVersion: "2.20.0",
|
||||
@@ -142,8 +137,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false for invalid GHES version",
|
||||
{
|
||||
gitHubVersion: {
|
||||
@@ -154,8 +148,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false for GHES version 3.18.5",
|
||||
{
|
||||
gitHubVersion: {
|
||||
@@ -166,8 +159,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns true for GHES version 3.19.0",
|
||||
{
|
||||
gitHubVersion: {
|
||||
@@ -178,8 +170,7 @@ test.serial(
|
||||
true,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false when not a pull request",
|
||||
{
|
||||
pullRequestBranches: undefined,
|
||||
@@ -188,10 +179,6 @@ test.serial(
|
||||
);
|
||||
|
||||
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
|
||||
sinon
|
||||
.stub(actionsUtil, "getRequiredInput")
|
||||
.withArgs("checkout_path")
|
||||
.returns("/checkout/path");
|
||||
return exportedForTesting.getDiffRanges(
|
||||
{
|
||||
filename: "test.txt",
|
||||
@@ -211,7 +198,7 @@ test.serial("getDiffRanges: file diff too large", async (t) => {
|
||||
const diffRanges = runGetDiffRanges(1000000, undefined);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 0,
|
||||
endLine: 0,
|
||||
},
|
||||
@@ -234,7 +221,7 @@ test.serial(
|
||||
]);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 53,
|
||||
endLine: 54,
|
||||
},
|
||||
@@ -274,7 +261,7 @@ test.serial("getDiffRanges: diff thunk with single update range", async (t) => {
|
||||
]);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 53,
|
||||
endLine: 53,
|
||||
},
|
||||
@@ -296,12 +283,12 @@ test.serial("getDiffRanges: diff thunk with addition ranges", async (t) => {
|
||||
]);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 53,
|
||||
endLine: 53,
|
||||
},
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 55,
|
||||
endLine: 55,
|
||||
},
|
||||
@@ -328,12 +315,12 @@ test.serial("getDiffRanges: diff thunk with mixed ranges", async (t) => {
|
||||
]);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 54,
|
||||
endLine: 54,
|
||||
},
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 57,
|
||||
endLine: 58,
|
||||
},
|
||||
@@ -363,12 +350,12 @@ test.serial("getDiffRanges: multiple diff thunks", async (t) => {
|
||||
]);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 53,
|
||||
endLine: 54,
|
||||
},
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 153,
|
||||
endLine: 154,
|
||||
},
|
||||
@@ -379,7 +366,7 @@ test.serial("getDiffRanges: no diff context lines", async (t) => {
|
||||
const diffRanges = runGetDiffRanges(2, ["@@ -30 +50,2 @@", "+1", "+2"]);
|
||||
t.deepEqual(diffRanges, [
|
||||
{
|
||||
path: "/checkout/path/test.txt",
|
||||
path: "test.txt",
|
||||
startLine: 50,
|
||||
endLine: 51,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import type { PullRequestBranches } from "./actions-util";
|
||||
@@ -71,21 +70,18 @@ export async function getDiffInformedAnalysisBranches(
|
||||
}
|
||||
|
||||
export interface DiffThunkRange {
|
||||
/** Relative path from the repository root, using forward slashes as separators. */
|
||||
path: string;
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
}
|
||||
|
||||
function getDiffRangesJsonFilePath(): string {
|
||||
return path.join(actionsUtil.getTemporaryDirectory(), "pr-diff-range.json");
|
||||
}
|
||||
|
||||
export function writeDiffRangesJsonFile(
|
||||
logger: Logger,
|
||||
ranges: DiffThunkRange[],
|
||||
): void {
|
||||
const jsonContents = JSON.stringify(ranges, null, 2);
|
||||
const jsonFilePath = getDiffRangesJsonFilePath();
|
||||
const jsonFilePath = actionsUtil.getDiffRangesJsonFilePath();
|
||||
fs.writeFileSync(jsonFilePath, jsonContents);
|
||||
logger.debug(
|
||||
`Wrote pr-diff-range JSON file to ${jsonFilePath}:\n${jsonContents}`,
|
||||
@@ -95,7 +91,7 @@ export function writeDiffRangesJsonFile(
|
||||
export function readDiffRangesJsonFile(
|
||||
logger: Logger,
|
||||
): DiffThunkRange[] | undefined {
|
||||
const jsonFilePath = getDiffRangesJsonFilePath();
|
||||
const jsonFilePath = actionsUtil.getDiffRangesJsonFilePath();
|
||||
if (!fs.existsSync(jsonFilePath)) {
|
||||
logger.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`);
|
||||
return undefined;
|
||||
@@ -104,7 +100,14 @@ export function readDiffRangesJsonFile(
|
||||
logger.debug(
|
||||
`Read pr-diff-range JSON file from ${jsonFilePath}:\n${jsonContents}`,
|
||||
);
|
||||
return JSON.parse(jsonContents) as DiffThunkRange[];
|
||||
try {
|
||||
return JSON.parse(jsonContents) as DiffThunkRange[];
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,8 +115,9 @@ export function readDiffRangesJsonFile(
|
||||
*
|
||||
* @param branches The base and head branches of the pull request.
|
||||
* @param logger
|
||||
* @returns An array of tuples, where each tuple contains the absolute path of a
|
||||
* file, the start line and the end line (both 1-based and inclusive) of an
|
||||
* @returns An array of tuples, where each tuple contains the relative path of a
|
||||
* file (relative to the repository root, as returned by the GitHub compare API),
|
||||
* the start line and the end line (both 1-based and inclusive) of an
|
||||
* added or modified range in that file. Returns `undefined` if the action was
|
||||
* not triggered by a pull request or if there was an error.
|
||||
*/
|
||||
@@ -191,13 +195,6 @@ function getDiffRanges(
|
||||
fileDiff: FileDiff,
|
||||
logger: Logger,
|
||||
): DiffThunkRange[] | undefined {
|
||||
// Diff-informed queries expect the file path to be absolute. CodeQL always
|
||||
// uses forward slashes as the path separator, so on Windows we need to
|
||||
// replace any backslashes with forward slashes.
|
||||
const filename = path
|
||||
.join(actionsUtil.getRequiredInput("checkout_path"), fileDiff.filename)
|
||||
.replaceAll(path.sep, "/");
|
||||
|
||||
if (fileDiff.patch === undefined) {
|
||||
if (fileDiff.changes === 0) {
|
||||
// There are situations where a changed file legitimately has no diff.
|
||||
@@ -212,7 +209,7 @@ function getDiffRanges(
|
||||
// to a special diff range that covers the entire file.
|
||||
return [
|
||||
{
|
||||
path: filename,
|
||||
path: fileDiff.filename,
|
||||
startLine: 0,
|
||||
endLine: 0,
|
||||
},
|
||||
@@ -247,7 +244,7 @@ function getDiffRanges(
|
||||
// Any line that does not start with a "+" or "-" terminates the current
|
||||
// range of added lines.
|
||||
diffRanges.push({
|
||||
path: filename,
|
||||
path: fileDiff.filename,
|
||||
startLine: additionRangeStartLine,
|
||||
endLine: currentLine - 1,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ export enum DocUrl {
|
||||
CODEQL_BUILD_MODES = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#codeql-build-modes",
|
||||
DEFINE_ENV_VARIABLES = "https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow",
|
||||
DELETE_ACTIONS_CACHE_ENTRIES = "https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manage-caches#deleting-cache-entries",
|
||||
PRIVATE_REGISTRY_LOGS = "https://docs.github.com/en/code-security/reference/code-scanning/code-scanning-logs#diagnostic-information-for-private-package-registries",
|
||||
SCANNING_ON_PUSH = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#scanning-on-push",
|
||||
SPECIFY_BUILD_STEPS_MANUALLY = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#about-specifying-build-steps-manually",
|
||||
SYSTEM_REQUIREMENTS = "https://codeql.github.com/docs/codeql-overview/system-requirements/",
|
||||
|
||||
+27
-12
@@ -451,12 +451,16 @@ test.serial(`selects CLI from defaults.json on GHES`, async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const features = setUpFeatureFlagTests(tmpDir);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(
|
||||
const defaultCliVersion = await features.getEnabledDefaultCliVersions(
|
||||
GitHubVariant.GHES,
|
||||
);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -482,10 +486,13 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
|
||||
false;
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(variant);
|
||||
const defaultCliVersion =
|
||||
await features.getEnabledDefaultCliVersions(variant);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: "2.20.1",
|
||||
tagName: "codeql-bundle-v2.20.1",
|
||||
enabledVersions: [
|
||||
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
|
||||
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
|
||||
],
|
||||
toolsFeatureFlagsValid: true,
|
||||
});
|
||||
});
|
||||
@@ -500,10 +507,15 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
|
||||
const expectedFeatureEnablement = initializeFeatures(true);
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(variant);
|
||||
const defaultCliVersion =
|
||||
await features.getEnabledDefaultCliVersions(variant);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
toolsFeatureFlagsValid: false,
|
||||
});
|
||||
});
|
||||
@@ -529,10 +541,13 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
|
||||
] = true;
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(variant);
|
||||
const defaultCliVersion =
|
||||
await features.getEnabledDefaultCliVersions(variant);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: "2.20.1",
|
||||
tagName: "codeql-bundle-v2.20.1",
|
||||
enabledVersions: [
|
||||
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
|
||||
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
|
||||
],
|
||||
toolsFeatureFlagsValid: true,
|
||||
});
|
||||
|
||||
|
||||
+122
-50
@@ -9,6 +9,7 @@ import * as defaults from "./defaults.json";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
CODEQL_OVERLAY_MINIMUM_VERSION,
|
||||
CODEQL_OVERLAY_MINIMUM_VERSION_CPP,
|
||||
CODEQL_OVERLAY_MINIMUM_VERSION_CSHARP,
|
||||
CODEQL_OVERLAY_MINIMUM_VERSION_GO,
|
||||
CODEQL_OVERLAY_MINIMUM_VERSION_JAVA,
|
||||
@@ -28,9 +29,32 @@ const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
|
||||
*/
|
||||
export const CODEQL_VERSION_ZSTD_BUNDLE = "2.19.0";
|
||||
|
||||
export interface CodeQLDefaultVersionInfo {
|
||||
const LINKED_CODEQL_VERSION: CodeQLVersionInfo = {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
};
|
||||
|
||||
export interface CodeQLVersionInfo {
|
||||
/** The version number of the CodeQL CLI, e.g. `2.19.0`. */
|
||||
cliVersion: string;
|
||||
/**
|
||||
* The tag name of the CodeQL Bundle associated with this version, e.g. `codeql-bundle-v2.19.0`.
|
||||
*/
|
||||
tagName: string;
|
||||
}
|
||||
|
||||
export interface CodeQLDefaultVersionInfo {
|
||||
/**
|
||||
* CodeQL CLI versions that are enabled as defaults, sorted from highest to lowest.
|
||||
*
|
||||
* Guaranteed to be non-empty. When feature flags are unavailable, this falls back to a single
|
||||
* entry containing the version pinned in `defaults.json`.
|
||||
*/
|
||||
enabledVersions: CodeQLVersionInfo[];
|
||||
/**
|
||||
* If accessed, whether the tools feature flags are valid, i.e. contain at least one enabled
|
||||
* version.
|
||||
*/
|
||||
toolsFeatureFlagsValid?: boolean;
|
||||
}
|
||||
|
||||
@@ -43,6 +67,8 @@ export interface CodeQLDefaultVersionInfo {
|
||||
* Legacy features should end with `_enabled`.
|
||||
*/
|
||||
export enum Feature {
|
||||
/** Controls whether we allow multiple values for the `analysis-kinds` input. */
|
||||
AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds",
|
||||
AllowToolcacheInput = "allow_toolcache_input",
|
||||
CleanupTrapCaches = "cleanup_trap_caches",
|
||||
CppDependencyInstallation = "cpp_dependency_installation_enabled",
|
||||
@@ -57,20 +83,33 @@ export enum Feature {
|
||||
IgnoreGeneratedFiles = "ignore_generated_files",
|
||||
JavaNetworkDebugging = "java_network_debugging",
|
||||
OverlayAnalysis = "overlay_analysis",
|
||||
OverlayAnalysisCodeScanningCpp = "overlay_analysis_code_scanning_cpp",
|
||||
OverlayAnalysisCodeScanningCsharp = "overlay_analysis_code_scanning_csharp",
|
||||
OverlayAnalysisCodeScanningGo = "overlay_analysis_code_scanning_go",
|
||||
OverlayAnalysisCodeScanningJava = "overlay_analysis_code_scanning_java",
|
||||
OverlayAnalysisCodeScanningJavascript = "overlay_analysis_code_scanning_javascript",
|
||||
OverlayAnalysisCodeScanningPython = "overlay_analysis_code_scanning_python",
|
||||
OverlayAnalysisCodeScanningRuby = "overlay_analysis_code_scanning_ruby",
|
||||
OverlayAnalysisCpp = "overlay_analysis_cpp",
|
||||
OverlayAnalysisCsharp = "overlay_analysis_csharp",
|
||||
/** Disable TRAP caching when overlay analysis is enabled. */
|
||||
OverlayAnalysisDisableTrapCaching = "overlay_analysis_disable_trap_caching",
|
||||
OverlayAnalysisGo = "overlay_analysis_go",
|
||||
/** Controls whether the Actions cache is checked for overlay build outcomes. */
|
||||
OverlayAnalysisStatusCheck = "overlay_analysis_status_check",
|
||||
/** Controls whether overlay build failures on are stored in the Actions cache. */
|
||||
OverlayAnalysisStatusSave = "overlay_analysis_status_save",
|
||||
OverlayAnalysisJava = "overlay_analysis_java",
|
||||
OverlayAnalysisJavascript = "overlay_analysis_javascript",
|
||||
/**
|
||||
* When set, chooses the default CodeQL CLI version as the highest version that is both enabled by
|
||||
* feature flags and present as an overlay-base database in the Actions cache for the configured
|
||||
* languages. Falls back to the highest feature flagged version if no intersecting overlay-base
|
||||
* database exists in the cache.
|
||||
*/
|
||||
OverlayAnalysisMatchCodeqlVersion = "overlay_analysis_match_codeql_version",
|
||||
/**
|
||||
* Like `OverlayAnalysisMatchCodeqlVersion`, but only logs a diagnostic with the version that
|
||||
* would have been chosen instead of actually changing the default CodeQL CLI version.
|
||||
* `OverlayAnalysisMatchCodeqlVersion` overrides this flag.
|
||||
*/
|
||||
OverlayAnalysisMatchCodeqlVersionDryRun = "overlay_analysis_match_codeql_version_dry_run",
|
||||
OverlayAnalysisPython = "overlay_analysis_python",
|
||||
/**
|
||||
* Controls whether lower disk space requirements are used for overlay hardware checks.
|
||||
@@ -80,7 +119,10 @@ export enum Feature {
|
||||
OverlayAnalysisRuby = "overlay_analysis_ruby",
|
||||
/** Controls whether hardware checks are skipped for overlay analysis. */
|
||||
OverlayAnalysisSkipResourceChecks = "overlay_analysis_skip_resource_checks",
|
||||
PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib",
|
||||
/** Controls whether the Actions cache is checked for overlay build outcomes. */
|
||||
OverlayAnalysisStatusCheck = "overlay_analysis_status_check",
|
||||
/** Controls whether overlay build failures on the default branch are stored in the Actions cache. */
|
||||
OverlayAnalysisStatusSave = "overlay_analysis_status_save",
|
||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||
/** Note that this currently only disables baseline file coverage information. */
|
||||
SkipFileCoverageOnPrs = "skip_file_coverage_on_prs",
|
||||
@@ -120,6 +162,11 @@ export type FeatureConfig = {
|
||||
};
|
||||
|
||||
export const featureConfig = {
|
||||
[Feature.AllowMultipleAnalysisKinds]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.AllowToolcacheInput]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_ALLOW_TOOLCACHE_INPUT",
|
||||
@@ -197,6 +244,11 @@ export const featureConfig = {
|
||||
// Per-language overlay feature flags. Each has minimumVersion set to the
|
||||
// minimum CLI version that supports overlay analysis for that language.
|
||||
// Only languages that are GA or in staff-ship should have feature flags here.
|
||||
[Feature.OverlayAnalysisCodeScanningCpp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_CPP",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_CPP,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningCsharp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_CSHARP",
|
||||
@@ -227,6 +279,11 @@ export const featureConfig = {
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_RUBY",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_RUBY,
|
||||
},
|
||||
[Feature.OverlayAnalysisCpp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CPP",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_CPP,
|
||||
},
|
||||
[Feature.OverlayAnalysisCsharp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CSHARP",
|
||||
@@ -237,16 +294,6 @@ export const featureConfig = {
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_GO",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_GO,
|
||||
},
|
||||
[Feature.OverlayAnalysisStatusCheck]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_CHECK",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisStatusSave]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_SAVE",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisJava]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_JAVA",
|
||||
@@ -262,27 +309,47 @@ export const featureConfig = {
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_PYTHON",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_PYTHON,
|
||||
},
|
||||
[Feature.OverlayAnalysisResourceChecksV2]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RESOURCE_CHECKS_V2",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisRuby]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RUBY",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION_RUBY,
|
||||
},
|
||||
// Other overlay-related feature flags
|
||||
[Feature.OverlayAnalysisDisableTrapCaching]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_DISABLE_TRAP_CACHING",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisMatchCodeqlVersion]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisMatchCodeqlVersionDryRun]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisResourceChecksV2]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RESOURCE_CHECKS_V2",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisStatusCheck]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_CHECK",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisStatusSave]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_SAVE",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisSkipResourceChecks]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.PythonDefaultIsToNotExtractStdlib]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_DISABLE_PYTHON_STANDARD_LIBRARY_EXTRACTION",
|
||||
minimumVersion: undefined,
|
||||
toolsFeature: ToolsFeature.PythonDefaultIsToNotExtractStdlib,
|
||||
},
|
||||
[Feature.QaTelemetryEnabled]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_QA_TELEMETRY",
|
||||
@@ -332,8 +399,12 @@ export type FeatureWithoutCLI = {
|
||||
}[keyof typeof featureConfig];
|
||||
|
||||
export interface FeatureEnablement {
|
||||
/** Gets the default version of the CodeQL tools. */
|
||||
getDefaultCliVersion(
|
||||
/**
|
||||
* Returns the set of default CodeQL CLI versions to consider, sorted from
|
||||
* highest to lowest. The first entry is the version that the CodeQL Action
|
||||
* will use by default. The list is always non-empty.
|
||||
*/
|
||||
getEnabledDefaultCliVersions(
|
||||
variant: util.GitHubVariant,
|
||||
): Promise<CodeQLDefaultVersionInfo>;
|
||||
getValue(feature: FeatureWithoutCLI): Promise<boolean>;
|
||||
@@ -357,12 +428,11 @@ export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
|
||||
class OfflineFeatures implements FeatureEnablement {
|
||||
constructor(protected readonly logger: Logger) {}
|
||||
|
||||
async getDefaultCliVersion(
|
||||
async getEnabledDefaultCliVersions(
|
||||
_variant: util.GitHubVariant,
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
return {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [LINKED_CODEQL_VERSION],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -372,7 +442,7 @@ class OfflineFeatures implements FeatureEnablement {
|
||||
getFeatureConfig(feature: Feature): FeatureConfig {
|
||||
// Narrow the type to FeatureConfig to avoid type errors. To avoid unsafe use of `as`, we
|
||||
// check that the required properties exist using `satisfies`.
|
||||
return featureConfig[feature] satisfies FeatureConfig as FeatureConfig;
|
||||
return featureConfig[feature] satisfies FeatureConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -504,13 +574,13 @@ class Features extends OfflineFeatures {
|
||||
);
|
||||
}
|
||||
|
||||
async getDefaultCliVersion(
|
||||
async getEnabledDefaultCliVersions(
|
||||
variant: util.GitHubVariant,
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
if (supportsFeatureFlags(variant)) {
|
||||
return await this.gitHubFeatureFlags.getDefaultCliVersionFromFlags();
|
||||
return await this.gitHubFeatureFlags.getEnabledDefaultCliVersionsFromFlags();
|
||||
}
|
||||
return super.getDefaultCliVersion(variant);
|
||||
return super.getEnabledDefaultCliVersions(variant);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,16 +656,22 @@ class GitHubFeatureFlags {
|
||||
return version;
|
||||
}
|
||||
|
||||
async getDefaultCliVersionFromFlags(): Promise<CodeQLDefaultVersionInfo> {
|
||||
/**
|
||||
* Returns CLI versions enabled by `default_codeql_version_*_enabled` feature
|
||||
* flags, sorted from highest to lowest. Falls back to the version pinned in
|
||||
* `defaults.json` if no such flags are enabled.
|
||||
*/
|
||||
async getEnabledDefaultCliVersionsFromFlags(): Promise<CodeQLDefaultVersionInfo> {
|
||||
const response = await this.getAllFeatures();
|
||||
|
||||
const enabledFeatureFlagCliVersions = Object.entries(response)
|
||||
const sortedCliVersions = Object.entries(response)
|
||||
.map(([f, isEnabled]) =>
|
||||
isEnabled ? this.getCliVersionFromFeatureFlag(f) : undefined,
|
||||
)
|
||||
.filter((f): f is string => f !== undefined);
|
||||
.filter((f): f is string => f !== undefined)
|
||||
.sort(semver.rcompare);
|
||||
|
||||
if (enabledFeatureFlagCliVersions.length === 0) {
|
||||
if (sortedCliVersions.length === 0) {
|
||||
// We expect at least one default CLI version to be enabled on Dotcom at any time. However if
|
||||
// the feature flags are misconfigured, rather than crashing, we fall back to the CLI version
|
||||
// shipped with the Action in defaults.json. This has the effect of immediately rolling out
|
||||
@@ -611,8 +687,7 @@ class GitHubFeatureFlags {
|
||||
`shipped with the Action. This is ${defaults.cliVersion}.`,
|
||||
);
|
||||
const result: CodeQLDefaultVersionInfo = {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [LINKED_CODEQL_VERSION],
|
||||
};
|
||||
if (this.hasAccessedRemoteFeatureFlags) {
|
||||
result.toolsFeatureFlagsValid = false;
|
||||
@@ -620,17 +695,14 @@ class GitHubFeatureFlags {
|
||||
return result;
|
||||
}
|
||||
|
||||
const maxCliVersion = enabledFeatureFlagCliVersions.reduce(
|
||||
(maxVersion, currentVersion) =>
|
||||
currentVersion > maxVersion ? currentVersion : maxVersion,
|
||||
enabledFeatureFlagCliVersions[0],
|
||||
);
|
||||
this.logger.debug(
|
||||
`Derived default CLI version of ${maxCliVersion} from feature flags.`,
|
||||
`Derived default CLI version of ${sortedCliVersions[0]} from feature flags.`,
|
||||
);
|
||||
return {
|
||||
cliVersion: maxCliVersion,
|
||||
tagName: `codeql-bundle-v${maxCliVersion}`,
|
||||
enabledVersions: sortedCliVersions.map((cliVersion) => ({
|
||||
cliVersion,
|
||||
tagName: `codeql-bundle-v${cliVersion}`,
|
||||
})),
|
||||
toolsFeatureFlagsValid: true,
|
||||
};
|
||||
}
|
||||
|
||||
+118
-51
@@ -343,75 +343,142 @@ test.serial("decodeGitFilePath quoted strings", async (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test.serial("getFileOidsUnderPath returns correct file mapping", async (t) => {
|
||||
const runGitCommandStub = sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(
|
||||
"30d998ded095371488be3a729eb61d86ed721a18_lib/git-utils.js\n" +
|
||||
"d89514599a9a99f22b4085766d40af7b99974827_lib/git-utils.js.map\n" +
|
||||
"a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts",
|
||||
);
|
||||
test.serial(
|
||||
"getFileOidsUnderPath uses --recurse-submodules when submodules exist",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
fs.writeFileSync(path.join(tmpDir, ".gitmodules"), "");
|
||||
const runGitCommandStub = sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return (
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a18 0\tlib/git-utils.js\n" +
|
||||
"100644 d89514599a9a99f22b4085766d40af7b99974827 0\tlib/git-utils.js.map\n" +
|
||||
"100644 a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96 0\tsrc/git-utils.ts"
|
||||
);
|
||||
});
|
||||
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
|
||||
t.deepEqual(result, {
|
||||
"lib/git-utils.js": "30d998ded095371488be3a729eb61d86ed721a18",
|
||||
"lib/git-utils.js.map": "d89514599a9a99f22b4085766d40af7b99974827",
|
||||
"src/git-utils.ts": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
|
||||
});
|
||||
t.deepEqual(result, {
|
||||
"lib/git-utils.js": "30d998ded095371488be3a729eb61d86ed721a18",
|
||||
"lib/git-utils.js.map": "d89514599a9a99f22b4085766d40af7b99974827",
|
||||
"src/git-utils.ts": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
|
||||
});
|
||||
|
||||
t.deepEqual(runGitCommandStub.firstCall.args, [
|
||||
"/fake/path",
|
||||
["ls-files", "--recurse-submodules", "--format=%(objectname)_%(path)"],
|
||||
"Cannot list Git OIDs of tracked files.",
|
||||
]);
|
||||
});
|
||||
// Second call (after getGitRoot) should include --recurse-submodules
|
||||
t.deepEqual(runGitCommandStub.secondCall.args[1], [
|
||||
"ls-files",
|
||||
"--recurse-submodules",
|
||||
"--stage",
|
||||
]);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getFileOidsUnderPath omits --recurse-submodules when no submodules exist",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const runGitCommandStub = sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return (
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a18 0\tlib/git-utils.js\n" +
|
||||
"100644 a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96 0\tsrc/git-utils.ts"
|
||||
);
|
||||
});
|
||||
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
|
||||
t.deepEqual(result, {
|
||||
"lib/git-utils.js": "30d998ded095371488be3a729eb61d86ed721a18",
|
||||
"src/git-utils.ts": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
|
||||
});
|
||||
|
||||
// Second call (after getGitRoot) should NOT include --recurse-submodules
|
||||
t.deepEqual(runGitCommandStub.secondCall.args[1], [
|
||||
"ls-files",
|
||||
"--stage",
|
||||
]);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("getFileOidsUnderPath handles quoted paths", async (t) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(
|
||||
"30d998ded095371488be3a729eb61d86ed721a18_lib/normal-file.js\n" +
|
||||
'd89514599a9a99f22b4085766d40af7b99974827_"lib/file with spaces.js"\n' +
|
||||
'a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_"lib/file\\twith\\ttabs.js"',
|
||||
);
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return (
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a18 0\tlib/normal-file.js\n" +
|
||||
'100644 d89514599a9a99f22b4085766d40af7b99974827 0\t"lib/file with spaces.js"\n' +
|
||||
'100644 a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96 0\t"lib/file\\twith\\ttabs.js"'
|
||||
);
|
||||
});
|
||||
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
|
||||
t.deepEqual(result, {
|
||||
"lib/normal-file.js": "30d998ded095371488be3a729eb61d86ed721a18",
|
||||
"lib/file with spaces.js": "d89514599a9a99f22b4085766d40af7b99974827",
|
||||
"lib/file\twith\ttabs.js": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
|
||||
t.deepEqual(result, {
|
||||
"lib/normal-file.js": "30d998ded095371488be3a729eb61d86ed721a18",
|
||||
"lib/file with spaces.js": "d89514599a9a99f22b4085766d40af7b99974827",
|
||||
"lib/file\twith\ttabs.js": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.serial("getFileOidsUnderPath handles empty output", async (t) => {
|
||||
sinon.stub(gitUtils as any, "runGitCommand").resolves("");
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
t.deepEqual(result, {});
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
t.deepEqual(result, {});
|
||||
});
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"getFileOidsUnderPath throws on unexpected output format",
|
||||
async (t) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(
|
||||
"30d998ded095371488be3a729eb61d86ed721a18_lib/git-utils.js\n" +
|
||||
"invalid-line-format\n" +
|
||||
"a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts",
|
||||
);
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return (
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a18 0\tlib/git-utils.js\n" +
|
||||
"invalid-line-format\n" +
|
||||
"100644 a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96 0\tsrc/git-utils.ts"
|
||||
);
|
||||
});
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
},
|
||||
{
|
||||
instanceOf: Error,
|
||||
message: 'Unexpected "git ls-files" output: invalid-line-format',
|
||||
},
|
||||
);
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
},
|
||||
{
|
||||
instanceOf: Error,
|
||||
message: 'Unexpected "git ls-files" output: invalid-line-format',
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+38
-11
@@ -1,4 +1,6 @@
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import { ExecOptions } from "@actions/exec";
|
||||
@@ -14,10 +16,11 @@ import {
|
||||
import { ConfigurationError, getRequiredEnvParam } from "./util";
|
||||
|
||||
/**
|
||||
* Minimum Git version required for overlay analysis. The `git ls-files --format`
|
||||
* option, which is used by `getFileOidsUnderPath`, was introduced in Git 2.38.0.
|
||||
* Minimum Git version required for overlay analysis in repositories that
|
||||
* contain submodules. Support for using the `git ls-files
|
||||
* --recurse-submodules` option with `--stage` was added in Git 2.36.0.
|
||||
*/
|
||||
export const GIT_MINIMUM_VERSION_FOR_OVERLAY = "2.38.0";
|
||||
export const GIT_MINIMUM_VERSION_FOR_OVERLAY_WITH_SUBMODULES = "2.36.0";
|
||||
|
||||
/**
|
||||
* Git version information
|
||||
@@ -244,6 +247,16 @@ export const getGitRoot = async function (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the Git repository has submodules registered (i.e. a
|
||||
* `.gitmodules` file exists at the repository root).
|
||||
*
|
||||
* @param gitRoot The root of the Git repository.
|
||||
*/
|
||||
export function hasSubmodules(gitRoot: string): boolean {
|
||||
return fs.existsSync(path.join(gitRoot, ".gitmodules"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Git OIDs of all tracked files (in the index and in the working
|
||||
* tree) that are under the given base path, including files in active
|
||||
@@ -252,31 +265,45 @@ export const getGitRoot = async function (
|
||||
*
|
||||
* @param basePath A path into the Git repository.
|
||||
* @returns a map from file paths (relative to `basePath`) to Git OIDs.
|
||||
* @throws {Error} if "git ls-tree" produces unexpected output.
|
||||
* @throws {Error} if "git ls-files" produces unexpected output.
|
||||
*/
|
||||
export const getFileOidsUnderPath = async function (
|
||||
basePath: string,
|
||||
): Promise<{ [key: string]: string }> {
|
||||
// Without the --full-name flag, the path is relative to the current working
|
||||
// directory of the git command, which is basePath.
|
||||
//
|
||||
// We use --stage rather than --format here because --format was only
|
||||
// introduced in Git 2.38.0, which would limit overlay rollout.
|
||||
//
|
||||
// We only pass --recurse-submodules when the repository actually has
|
||||
// submodules, because the combination of --recurse-submodules and --stage is
|
||||
// only supported since Git 2.36.0.
|
||||
const gitRoot = await getGitRoot(basePath);
|
||||
const mayHaveSubmodules =
|
||||
gitRoot === undefined ? true : hasSubmodules(gitRoot);
|
||||
const args = mayHaveSubmodules
|
||||
? ["ls-files", "--recurse-submodules", "--stage"]
|
||||
: ["ls-files", "--stage"];
|
||||
const stdout = await runGitCommand(
|
||||
basePath,
|
||||
["ls-files", "--recurse-submodules", "--format=%(objectname)_%(path)"],
|
||||
args,
|
||||
"Cannot list Git OIDs of tracked files.",
|
||||
);
|
||||
|
||||
const fileOidMap: { [key: string]: string } = {};
|
||||
// With --format=%(objectname)_%(path), the output is a list of lines like:
|
||||
// 30d998ded095371488be3a729eb61d86ed721a18_lib/git-utils.js
|
||||
// d89514599a9a99f22b4085766d40af7b99974827_lib/git-utils.js.map
|
||||
const regex = /^([0-9a-f]{40})_(.+)$/;
|
||||
// With --stage, the output is a list of lines like:
|
||||
// 100644 4c51bc1d9e86cd86e01b0f340cb8ce095c33b283 0\tsrc/git-utils.test.ts
|
||||
// 100644 6b792ea543ce75d7a8a03df591e3c85311ecb64f 0\tsrc/git-utils.ts
|
||||
// The fields are: <mode> <oid> <stage>\t<path>
|
||||
const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/;
|
||||
for (const line of stdout.split("\n")) {
|
||||
if (line) {
|
||||
const match = line.match(regex);
|
||||
if (match) {
|
||||
const oid = match[1];
|
||||
const path = decodeGitFilePath(match[2]);
|
||||
fileOidMap[path] = oid;
|
||||
const filePath = decodeGitFilePath(match[2]);
|
||||
fileOidMap[filePath] = oid;
|
||||
} else {
|
||||
throw new Error(`Unexpected "git ls-files" output: ${line}`);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user