mirror of
https://github.com/github/codeql-action.git
synced 2026-05-24 08:08:36 +00:00
Compare commits
314 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 458d36d7d4 | |||
| 70a1165f9a | |||
| 4b79f1ba10 | |||
| b2dd803f6e | |||
| 0e6a98bb2f | |||
| cb4dbcd837 | |||
| cad7075882 | |||
| 9e0d7b8d25 | |||
| 6d7d59927c | |||
| 51f7e38c69 | |||
| d4b485515e | |||
| 127de8117f | |||
| 7fde13f26a | |||
| dfa61e7305 | |||
| 52aafec073 | |||
| 0d08c01f78 | |||
| 14085a675c | |||
| eb17ca4f4d | |||
| a41c444cd9 | |||
| d7e50c23fe | |||
| bb30f3132d | |||
| 336884853e | |||
| 2f137c9dc6 | |||
| 4795ef8153 | |||
| f0489abddd | |||
| 2e202367c7 | |||
| 9d7243005b | |||
| 237b03b3c3 | |||
| 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 | |||
| 7fd177fa68 | |||
| efc9b0a9e3 | |||
| ea18e99ca3 | |||
| 272ada693f | |||
| 610a6682b6 | |||
| 19f4654991 | |||
| 8f15c6f1ad | |||
| 349cedea3b | |||
| f0ffd5714c | |||
| 1627096569 | |||
| 68bde559de | |||
| 9739ad2d18 | |||
| b81d0d250f | |||
| a16cb53dd8 | |||
| 803d9e8c3c | |||
| 0c80cee806 | |||
| d032ee8c47 | |||
| 0fd9c7d135 | |||
| 922d6fb888 | |||
| df77e87896 | |||
| 6e3f985e4f | |||
| e7a347dfb1 | |||
| 17eabb2500 | |||
| aaef09c48d | |||
| ae1b9155d3 | |||
| 9f82f88f07 | |||
| 7525c68ea1 | |||
| 01bc9be56a | |||
| 9d6b456c59 | |||
| e259d26055 | |||
| 817b68489e | |||
| 1b5632783c | |||
| 1848b73afa | |||
| d1e9792bc8 | |||
| 2c9cd77837 | |||
| b967fdfbdc | |||
| 55d6319f96 | |||
| b0942116d7 | |||
| bc0b696b41 | |||
| a796e3e4ed | |||
| f9bb0e001c | |||
| 4b7faf0b3d | |||
| 09a1d9ec2a | |||
| f64a4491cf | |||
| 7fc86e0c37 | |||
| 5997e25ad9 | |||
| 7587714d0a | |||
| 8ab64a211d | |||
| 0daab03d71 | |||
| a723e99345 | |||
| 30f0c9d081 | |||
| 3530cdd15e | |||
| 8280251823 | |||
| e511c7b2e0 | |||
| 50052a22af | |||
| fbba1e03be | |||
| 933238e8d5 | |||
| e46ed2cbd0 | |||
| b73d1d1634 | |||
| 24e0bb00a9 | |||
| ec298daba7 | |||
| f8b93c30a6 | |||
| 8c6e48dbe0 | |||
| 80a72986d3 | |||
| e9e36aec74 | |||
| 719098349e | |||
| 2bb209555a | |||
| 4ed52dcbfa | |||
| 3cc8dd3e59 | |||
| 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 | |||
| 5ded561dcd | |||
| 19b3a84f58 | |||
| faca00d3ae | |||
| 858a6149c1 | |||
| 5d1c58464f | |||
| 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 | |||
| ce64ddcb0d | |||
| c186c7b484 | |||
| ca7d6d3b79 | |||
| 8bcc8f23a2 | |||
| 834786ac9b | |||
| 047c547345 | |||
| 7ca215887b | |||
| 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 | |||
| 5c8a8a642e | |||
| 353802f9f2 | |||
| cc7db4a1f9 | |||
| 646729a1e2 | |||
| c983cb8e74 | |||
| 557b58c47c | |||
| 8bb5bdb9fd | |||
| 4d2fde9e07 | |||
| 6010f9d8e2 | |||
| c10b8064de | |||
| c5ffd06837 | |||
| d6d1743b8e | |||
| 999119ba45 | |||
| 65d2efa733 | |||
| 2437b20ab3 | |||
| f13c600724 | |||
| 7dcea06663 | |||
| ea5f71947c | |||
| 45ceeea896 | |||
| 24448c9843 | |||
| 7c51060631 | |||
| 36791d8d66 | |||
| 22eba96a28 |
@@ -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:
|
||||
|
||||
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@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.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
|
||||
|
||||
@@ -6,13 +6,6 @@ env:
|
||||
# Diff informed queries add an additional query filter which is not yet
|
||||
# taken into account by these tests.
|
||||
CODEQL_ACTION_DIFF_INFORMED_QUERIES: false
|
||||
# Specify overlay enablement manually to ensure stability around the exclude-from-incremental
|
||||
# query filter. Here we only enable for the default code scanning suite.
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS: true
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS_JAVASCRIPT: false
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_JAVASCRIPT: true
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_CHECK: false
|
||||
CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS: true
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -79,33 +72,13 @@ jobs:
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
|
||||
# On PRs, overlay analysis may change the config that is passed to the CLI.
|
||||
# Therefore, we have two variants of the following test, one for PRs and one for other events.
|
||||
- name: Empty file (non-PR)
|
||||
if: github.event_name != 'pull_request'
|
||||
- name: Empty file
|
||||
uses: ./../action/.github/actions/check-codescanning-config
|
||||
with:
|
||||
expected-config-file-contents: "{}"
|
||||
languages: javascript
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
|
||||
- name: Empty file (PR)
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: ./../action/.github/actions/check-codescanning-config
|
||||
with:
|
||||
expected-config-file-contents: |
|
||||
{
|
||||
"query-filters": [
|
||||
{
|
||||
"exclude": {
|
||||
"tags": "exclude-from-incremental"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
languages: javascript
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
|
||||
- name: Packs from input
|
||||
if: success() || failure()
|
||||
uses: ./../action/.github/actions/check-codescanning-config
|
||||
|
||||
@@ -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@v3.0.0
|
||||
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@v3.0.0
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.AUTOMATION_APP_ID }}
|
||||
|
||||
@@ -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@v3.0.0
|
||||
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
+1
-1
@@ -19,7 +19,7 @@
|
||||
"scope": "javascript, typescript",
|
||||
"prefix": "testMacro",
|
||||
"body": [
|
||||
"const ${1:nameMacro} = test.macro({",
|
||||
"const ${1:nameMacro} = makeMacro({",
|
||||
" exec: async (t: ExecutionContext<unknown>) => {},",
|
||||
"",
|
||||
" title: (providedTitle = \"\") => `${2:common title} - \\${providedTitle}`,",
|
||||
|
||||
@@ -2,6 +2,37 @@
|
||||
|
||||
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
||||
|
||||
## 3.35.5 - 15 May 2026
|
||||
|
||||
- We have improved how the JavaScript bundles for the CodeQL Action are generated to avoid duplication across bundles and reduce the size of the repository by around 70%. This should have no effect on the runtime behaviour of the CodeQL Action. [#3899](https://github.com/github/codeql-action/pull/3899)
|
||||
- For performance and accuracy reasons, [improved incremental analysis](https://github.com/github/roadmap/issues/1158) will now only be enabled on a pull request when diff-informed analysis is also enabled for that run. If diff-informed analysis is unavailable (for example, because the PR diff ranges could not be computed), the action will fall back to a full analysis. [#3791](https://github.com/github/codeql-action/pull/3791)
|
||||
- If multiple inputs are provided for the GitHub-internal `analysis-kinds` input, only `code-scanning` will be enabled. The `analysis-kinds` input is experimental, for GitHub-internal use only, and may change without notice at any time. [#3892](https://github.com/github/codeql-action/pull/3892)
|
||||
- Added an experimental change which, when running a Code Scanning analysis for a PR with [improved incremental analysis](https://github.com/github/roadmap/issues/1158) enabled, prefers CodeQL CLI versions that have a cached overlay-base database for the configured languages. This speeds up analysis for a repository when there is not yet a cached overlay-base database for the latest CLI version. We expect to roll this change out to everyone in May. [#3880](https://github.com/github/codeql-action/pull/3880)
|
||||
|
||||
## 3.35.4 - 07 May 2026
|
||||
|
||||
- Update default CodeQL bundle version to [2.25.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.4). [#3881](https://github.com/github/codeql-action/pull/3881)
|
||||
|
||||
## 3.35.3 - 01 May 2026
|
||||
|
||||
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.19.3 and earlier. These versions of CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise Server 3.15, and will be unsupported by the next minor release of the CodeQL Action. [#3837](https://github.com/github/codeql-action/pull/3837)
|
||||
- Configurations for private registries that use Cloudsmith or GCP OIDC are now accepted. [#3850](https://github.com/github/codeql-action/pull/3850)
|
||||
- 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)
|
||||
|
||||
## 3.35.2 - 15 Apr 2026
|
||||
|
||||
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable is deprecated and will be removed in May 2026. If you are affected by this, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#3795](https://github.com/github/codeql-action/pull/3795)
|
||||
- The Git version 2.36.0 requirement for improved incremental analysis now only applies to repositories that contain submodules. [#3789](https://github.com/github/codeql-action/pull/3789)
|
||||
- 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)
|
||||
|
||||
## 3.35.1 - 27 Mar 2026
|
||||
|
||||
- Fix incorrect minimum required Git version for [improved incremental analysis](https://github.com/github/roadmap/issues/1158): it should have been 2.36.0, not 2.11.0. [#3781](https://github.com/github/codeql-action/pull/3781)
|
||||
|
||||
## 3.35.0 - 27 Mar 2026
|
||||
|
||||
- Reduced the minimum Git version required for [improved incremental analysis](https://github.com/github/roadmap/issues/1158) from 2.38.0 to 2.11.0. [#3767](https://github.com/github/codeql-action/pull/3767)
|
||||
|
||||
@@ -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 | |
|
||||
|
||||
+2
-2
@@ -95,5 +95,5 @@ outputs:
|
||||
description: The ID of the uploaded SARIF file.
|
||||
runs:
|
||||
using: node20
|
||||
main: "../lib/analyze-action.js"
|
||||
post: "../lib/analyze-action-post.js"
|
||||
main: "../lib/analyze-entry.js"
|
||||
post: "../lib/analyze-post-entry.js"
|
||||
|
||||
@@ -16,4 +16,4 @@ inputs:
|
||||
required: false
|
||||
runs:
|
||||
using: node20
|
||||
main: '../lib/autobuild-action.js'
|
||||
main: '../lib/autobuild-entry.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { copyFile, rm } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { copyFile, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import * as esbuild from "esbuild";
|
||||
@@ -62,19 +62,131 @@ const onEndPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
/** The name of the virtual `entry-points` module. */
|
||||
const SHARED_ENTRYPOINT = "entry-points";
|
||||
|
||||
/**
|
||||
* This plugin finds all source files that contain action entry points.
|
||||
* It then generates the virtual `entry-points` module which imports all identifies files,
|
||||
* and re-exports their `runWrapper` functions with suitable aliases.
|
||||
* A tiny stub file is emitted for each Action entrypoint. Each stub imports the shared bundle
|
||||
* and calls the respective entry point.
|
||||
*
|
||||
* @type {esbuild.Plugin}
|
||||
*/
|
||||
const entryPointsPlugin = {
|
||||
name: "entry-points",
|
||||
setup(build) {
|
||||
const namespace = "actions";
|
||||
const actions = [];
|
||||
|
||||
const toPascal = (s) =>
|
||||
s.replace(/(^|-)([a-z0-9])/gi, (_, __, c) => c.toUpperCase());
|
||||
|
||||
// Find the source files containing action entry points.
|
||||
build.onStart(() => {
|
||||
const actionFiles = globSync("src/*-action{,-post}.ts");
|
||||
for (const actionFile of actionFiles) {
|
||||
const match = basename(actionFile).match(/(.*)-action(-post)?/);
|
||||
|
||||
if (match.length < 2) {
|
||||
throw new Error(`'${actionFile}' didn't match expected pattern.`);
|
||||
}
|
||||
|
||||
const actionName = match[1];
|
||||
const isPost = match[2] !== undefined;
|
||||
|
||||
actions.push({
|
||||
path: actionFile,
|
||||
name: actionName,
|
||||
isPost,
|
||||
pascalCaseName: `${toPascal(actionName)}${isPost ? "Post" : ""}Action`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Resolve the virtual `entry-points` file and set the corresponding namespace.
|
||||
// Ideally, we'd `RegExp.escape` the entrypoint here, but that API isn't supported in Node 20.
|
||||
// Since we're dealing with a hardcoded string, this isn't too much of a problem.
|
||||
build.onResolve({ filter: new RegExp(`^${SHARED_ENTRYPOINT}$`) }, () => {
|
||||
return { path: SHARED_ENTRYPOINT, namespace };
|
||||
});
|
||||
|
||||
// Generate the virtual `entry-points` file based on the actions we discovered.
|
||||
// Restrict using the namespace. The path filter does not need to discriminate any further.
|
||||
build.onLoad({ filter: /.*/, namespace }, async () => {
|
||||
const wrapperTemplatePath = "entry-wrapper.js.tpl";
|
||||
const wrapperTemplate = await readFile(
|
||||
join(SRC_DIR, wrapperTemplatePath),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const actionsSorted = actions.sort((a, b) =>
|
||||
a.name.localeCompare(b.name),
|
||||
);
|
||||
const imports = actionsSorted
|
||||
.map(
|
||||
(action) =>
|
||||
`import * as ${action.pascalCaseName} from "./src/${basename(action.path)}"`,
|
||||
)
|
||||
.join("\n");
|
||||
const wrappers = actionsSorted
|
||||
.map((action) =>
|
||||
wrapperTemplate.replaceAll("__ACTION__", action.pascalCaseName),
|
||||
)
|
||||
.join("\n\n");
|
||||
|
||||
return {
|
||||
contents: `"use strict";\n${imports}\n\n${wrappers}\n`,
|
||||
resolveDir: ".",
|
||||
loader: "ts",
|
||||
};
|
||||
});
|
||||
|
||||
// Emit entry point stubs for each action using the entry template.
|
||||
build.onEnd(async (result) => {
|
||||
// Read the entry point template.
|
||||
const templatePath = "action-entry.js.tpl";
|
||||
const template = await readFile(join(SRC_DIR, templatePath), "utf-8");
|
||||
|
||||
const makeHeader = (sourceFile) =>
|
||||
`// Automatically generated from '${templatePath}' for 'src/${basename(sourceFile)}'.\n\n`;
|
||||
|
||||
// Write entry point stubs for each action.
|
||||
for (const action of actions) {
|
||||
await writeFile(
|
||||
join(
|
||||
OUT_DIR,
|
||||
`${action.name}${action.isPost ? "-post" : ""}-entry.js`,
|
||||
),
|
||||
makeHeader(action.path) +
|
||||
template.replaceAll("__ACTION__", action.pascalCaseName),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
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: [
|
||||
{ in: SHARED_ENTRYPOINT, out: SHARED_ENTRYPOINT },
|
||||
join(SRC_DIR, "upload-lib.ts"),
|
||||
],
|
||||
bundle: true,
|
||||
format: "cjs",
|
||||
outdir: OUT_DIR,
|
||||
platform: "node",
|
||||
plugins: [cleanPlugin, copyDefaultsPlugin, onEndPlugin],
|
||||
external: ["./entry-points"],
|
||||
plugins: [cleanPlugin, copyDefaultsPlugin, entryPointsPlugin, 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();
|
||||
|
||||
+2
-2
@@ -171,5 +171,5 @@ outputs:
|
||||
description: The version of the CodeQL binary used for analysis
|
||||
runs:
|
||||
using: node20
|
||||
main: '../lib/init-action.js'
|
||||
post: '../lib/init-action-post.js'
|
||||
main: '../lib/init-entry.js'
|
||||
post: '../lib/init-post-entry.js'
|
||||
|
||||
Generated
-163791
File diff suppressed because one or more lines are too long
Generated
-113693
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/analyze-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runAnalyzeAction)();
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/analyze-action-post.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runAnalyzePostAction)();
|
||||
Generated
-106089
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/autobuild-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runAutobuildAction)();
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.25.1",
|
||||
"cliVersion": "2.25.1",
|
||||
"priorBundleVersion": "codeql-bundle-v2.24.3",
|
||||
"priorCliVersion": "2.24.3"
|
||||
"bundleVersion": "codeql-bundle-v2.25.4",
|
||||
"cliVersion": "2.25.4",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.3",
|
||||
"priorCliVersion": "2.25.3"
|
||||
}
|
||||
|
||||
+71243
-81222
File diff suppressed because one or more lines are too long
Generated
-110681
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/init-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runInitAction)();
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/init-action-post.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runInitPostAction)();
|
||||
Generated
-105652
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/resolve-environment-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runResolveEnvironmentAction)();
|
||||
Generated
-107159
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/setup-codeql-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runSetupCodeqlAction)();
|
||||
Generated
-162618
File diff suppressed because one or more lines are too long
Generated
-122823
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/start-proxy-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runStartProxyAction)();
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/start-proxy-action-post.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runStartProxyPostAction)();
|
||||
Generated
+2634
-20269
File diff suppressed because one or more lines are too long
Generated
-162643
File diff suppressed because one or more lines are too long
Generated
-112051
File diff suppressed because one or more lines are too long
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/upload-sarif-action.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runUploadSarifAction)();
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// Automatically generated from 'action-entry.js.tpl' for 'src/upload-sarif-action-post.ts'.
|
||||
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.runUploadSarifPostAction)();
|
||||
Generated
+934
-460
File diff suppressed because it is too large
Load Diff
+22
-23
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "3.35.0",
|
||||
"version": "3.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.3",
|
||||
"@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.4",
|
||||
"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.2",
|
||||
"eslint-plugin-jsdoc": "^62.8.0",
|
||||
"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.3",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.57.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@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.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
|
||||
|
||||
@@ -8,3 +8,16 @@ 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",
|
||||
);
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
"@octokit/core": "^7.0.6",
|
||||
"@octokit/plugin-paginate-rest": ">=9.2.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
||||
"yaml": "^2.8.3"
|
||||
"yaml": "^2.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.9",
|
||||
"@types/node": "^20.19.39",
|
||||
"tsx": "^4.21.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,9 @@
|
||||
import * as fs from "fs";
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
import * as githubUtils from "@actions/github/lib/utils";
|
||||
import { type Octokit } from "@octokit/core";
|
||||
import { type PaginateInterface } from "@octokit/plugin-paginate-rest";
|
||||
import { type Api } from "@octokit/plugin-rest-endpoint-methods";
|
||||
import * as yaml from "yaml";
|
||||
|
||||
import { type ApiClient, getApiClient } from "./api-client";
|
||||
import {
|
||||
OLDEST_SUPPORTED_MAJOR_VERSION,
|
||||
PR_CHECK_EXCLUDED_FILE,
|
||||
@@ -49,15 +46,6 @@ function loadExclusions(): Exclusions {
|
||||
) as Exclusions;
|
||||
}
|
||||
|
||||
/** The type of the Octokit client. */
|
||||
type ApiClient = Octokit & Api & { paginate: PaginateInterface };
|
||||
|
||||
/** Constructs an `ApiClient` using `token` for authentication. */
|
||||
function getApiClient(token: string): ApiClient {
|
||||
const opts = githubUtils.getOctokitOptions(token);
|
||||
return new githubUtils.GitHub(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+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();
|
||||
@@ -22,4 +22,4 @@ outputs:
|
||||
description: The inferred build environment configuration.
|
||||
runs:
|
||||
using: node20
|
||||
main: '../lib/resolve-environment-action.js'
|
||||
main: '../lib/resolve-environment-entry.js'
|
||||
|
||||
+20
-1
@@ -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 }}
|
||||
@@ -36,4 +55,4 @@ outputs:
|
||||
description: The version of the CodeQL binary that was installed.
|
||||
runs:
|
||||
using: node20
|
||||
main: '../lib/setup-codeql-action.js'
|
||||
main: '../lib/setup-codeql-entry.js'
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.run__ACTION__)();
|
||||
+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;
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analyze from "./analyze";
|
||||
import { runWrapper } from "./analyze-action";
|
||||
import * as api from "./api-client";
|
||||
import * as configUtils from "./config-utils";
|
||||
import * as gitUtils from "./git-utils";
|
||||
@@ -62,14 +63,8 @@ test("analyze action with RAM & threads from environment variables", async (t) =
|
||||
|
||||
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
|
||||
const runQueriesStub = sinon.stub(analyze, "runQueries");
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const analyzeAction = require("./analyze-action");
|
||||
|
||||
// When analyze-action.ts loads, it runs an async function from the top
|
||||
// level but does not wait for it to finish. To ensure that calls to
|
||||
// runFinalize and runQueries are correctly captured by spies, we explicitly
|
||||
// wait for the action promise to complete before starting verification.
|
||||
await analyzeAction.runPromise;
|
||||
await runWrapper();
|
||||
|
||||
t.assert(
|
||||
runFinalizeStub.calledOnceWith(
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analyze from "./analyze";
|
||||
import { runWrapper } from "./analyze-action";
|
||||
import * as api from "./api-client";
|
||||
import * as configUtils from "./config-utils";
|
||||
import * as gitUtils from "./git-utils";
|
||||
@@ -60,14 +61,8 @@ test("analyze action with RAM & threads from action inputs", async (t) => {
|
||||
|
||||
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
|
||||
const runQueriesStub = sinon.stub(analyze, "runQueries");
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const analyzeAction = require("./analyze-action");
|
||||
|
||||
// When analyze-action.ts loads, it runs an async function from the top
|
||||
// level but does not wait for it to finish. To ensure that calls to
|
||||
// runFinalize and runQueries are correctly captured by spies, we explicitly
|
||||
// wait for the action promise to complete before starting verification.
|
||||
await analyzeAction.runPromise;
|
||||
await runWrapper();
|
||||
|
||||
t.assert(
|
||||
runFinalizeStub.calledOnceWith(
|
||||
|
||||
@@ -20,7 +20,7 @@ import { EnvVar } from "./environment";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
@@ -72,5 +72,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
+14
-15
@@ -30,9 +30,9 @@ import {
|
||||
} from "./dependency-caching";
|
||||
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 {
|
||||
@@ -135,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
|
||||
@@ -169,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) {
|
||||
@@ -182,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.",
|
||||
);
|
||||
@@ -205,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) {
|
||||
@@ -519,14 +523,11 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
// Module-level startedAt so it can be accessed by runWrapper for error reporting
|
||||
const startedAt = new Date();
|
||||
export const runPromise = run(startedAt);
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
await runPromise;
|
||||
await run(startedAt);
|
||||
} catch (error) {
|
||||
core.setFailed(`analyze action failed: ${util.getErrorMessage(error)}`);
|
||||
await sendUnhandledErrorStatusReport(
|
||||
@@ -538,5 +539,3 @@ async function runWrapper() {
|
||||
}
|
||||
await util.checkForTimeout();
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
+6
-6
@@ -14,7 +14,7 @@ import {
|
||||
} 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,
|
||||
@@ -41,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 (
|
||||
@@ -130,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",
|
||||
);
|
||||
}
|
||||
@@ -145,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),
|
||||
|
||||
+16
-28
@@ -21,9 +21,9 @@ import {
|
||||
} 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";
|
||||
@@ -41,7 +41,7 @@ export class CodeQLAnalysisError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
type KnownLanguageKey = keyof typeof KnownLanguage;
|
||||
type BuiltInLanguageKey = keyof typeof BuiltInLanguage;
|
||||
|
||||
type RunQueriesDurationStatusReport = {
|
||||
/**
|
||||
@@ -50,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
|
||||
@@ -115,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);
|
||||
@@ -131,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))
|
||||
) {
|
||||
@@ -251,16 +251,9 @@ export async function setupDiffInformedQueryRun(
|
||||
diffRanges,
|
||||
checkoutPath,
|
||||
);
|
||||
if (packDir === undefined) {
|
||||
logger.warning(
|
||||
"Cannot create diff range extension pack for diff-informed queries; " +
|
||||
"reverting to performing full analysis.",
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
`Successfully created diff range extension pack at ${packDir}.`,
|
||||
);
|
||||
}
|
||||
logger.info(
|
||||
`Successfully created diff range extension pack at ${packDir}.`,
|
||||
);
|
||||
return packDir;
|
||||
},
|
||||
);
|
||||
@@ -314,18 +307,13 @@ extensions:
|
||||
* @param ranges The file line ranges, as returned by
|
||||
* `getPullRequestEditedDiffRanges`.
|
||||
* @param checkoutPath The path at which the repository was checked out.
|
||||
* @returns The absolute path of the directory containing the extension pack, or
|
||||
* `undefined` if no extension pack was created.
|
||||
* @returns The absolute path of the directory containing the extension pack.
|
||||
*/
|
||||
function writeDiffRangeDataExtensionPack(
|
||||
logger: Logger,
|
||||
ranges: DiffThunkRange[] | undefined,
|
||||
ranges: DiffThunkRange[],
|
||||
checkoutPath: string,
|
||||
): string | undefined {
|
||||
if (ranges === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
): 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
|
||||
@@ -698,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,9 +141,9 @@ test("scanArtifactsForTokens handles files without tokens", async (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
// `scanArchiveFile` does not support Windows, so we skip this test there.
|
||||
if (os.platform() !== "win32") {
|
||||
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
|
||||
t.timeout(15000); // 15 seconds
|
||||
const messages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(messages, { logToConsole: false });
|
||||
// The zip here is a regression test based on
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
@@ -142,7 +142,7 @@ async function run(startedAt: Date) {
|
||||
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -157,5 +157,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
+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:
|
||||
|
||||
+11
-8
@@ -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,17 +282,17 @@ 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++";
|
||||
@@ -308,6 +305,8 @@ const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
|
||||
* @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
|
||||
@@ -320,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,
|
||||
@@ -343,6 +344,8 @@ export async function setupCodeQL(
|
||||
tempDir,
|
||||
variant,
|
||||
defaultCliVersion,
|
||||
rawLanguages,
|
||||
useOverlayAwareDefaultCliVersion,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
+238
-191
File diff suppressed because it is too large
Load Diff
+90
-39
@@ -31,7 +31,7 @@ import {
|
||||
addNoLanguageDiagnostic,
|
||||
makeTelemetryDiagnostic,
|
||||
} from "./diagnostics";
|
||||
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||
import { prepareDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import * as errorMessages from "./error-messages";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
@@ -43,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";
|
||||
@@ -272,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;
|
||||
}
|
||||
@@ -405,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[] {
|
||||
@@ -945,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.
|
||||
@@ -969,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. ` +
|
||||
@@ -977,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({
|
||||
@@ -1028,13 +1037,13 @@ async function setCppTrapCachingEnvironmentVariables(
|
||||
config: Config,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
if (config.languages.includes(KnownLanguage.cpp)) {
|
||||
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[KnownLanguage.cpp]) {
|
||||
} else if (config.trapCaches[BuiltInLanguage.cpp]) {
|
||||
logger.info("Enabling TRAP caching for C/C++.");
|
||||
core.exportVariable(envVar, "true");
|
||||
} else {
|
||||
@@ -1068,6 +1077,48 @@ function hasQueryCustomisation(userConfig: UserConfig): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the incremental-analysis configuration for this run.
|
||||
*
|
||||
* Overlay analysis has only been validated in combination with diff-informed
|
||||
* analysis, so if `Overlay` mode was selected for a pull request but the diff
|
||||
* ranges could not be computed, fall back to a full non-overlay analysis.
|
||||
*
|
||||
* Query exclusions for incremental-only queries are then applied whenever the
|
||||
* diff ranges are available — which, after the fallback above, is exactly the
|
||||
* set of runs where any kind of incremental analysis (overlay or
|
||||
* diff-informed) is in effect.
|
||||
*/
|
||||
export async function applyIncrementalAnalysisSettings(
|
||||
config: Config,
|
||||
hasDiffRanges: boolean,
|
||||
codeql: CodeQL,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
if (
|
||||
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay &&
|
||||
!hasDiffRanges
|
||||
) {
|
||||
logger.info(
|
||||
`Reverting overlay database mode to ${OverlayDatabaseMode.None} ` +
|
||||
"because the PR diff ranges could not be computed.",
|
||||
);
|
||||
config.overlayDatabaseMode = OverlayDatabaseMode.None;
|
||||
config.useOverlayDatabaseCaching = false;
|
||||
await addOverlayDisablementDiagnostics(
|
||||
config,
|
||||
codeql,
|
||||
OverlayDisabledReason.DiffInformedAnalysisNotEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
if (hasDiffRanges) {
|
||||
config.extraQueryExclusions.push({
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return the config.
|
||||
*
|
||||
@@ -1222,18 +1273,18 @@ export async function initConfig(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
|
||||
(await shouldPerformDiffInformedAnalysis(
|
||||
inputs.codeql,
|
||||
inputs.features,
|
||||
logger,
|
||||
))
|
||||
) {
|
||||
config.extraQueryExclusions.push({
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
const hasDiffRanges = await prepareDiffInformedAnalysis(
|
||||
inputs.codeql,
|
||||
inputs.features,
|
||||
logger,
|
||||
);
|
||||
|
||||
await applyIncrementalAnalysisSettings(
|
||||
config,
|
||||
hasDiffRanges,
|
||||
inputs.codeql,
|
||||
logger,
|
||||
);
|
||||
|
||||
if (await isTrapCachingEnabled(features, config.overlayDatabaseMode)) {
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
@@ -1531,7 +1582,7 @@ export async function parseBuildModeInput(
|
||||
}
|
||||
|
||||
if (
|
||||
languages.includes(KnownLanguage.csharp) &&
|
||||
languages.includes(BuiltInLanguage.csharp) &&
|
||||
(await features.getValue(Feature.DisableCsharpBuildless))
|
||||
) {
|
||||
logger.warning(
|
||||
@@ -1541,7 +1592,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.25.1",
|
||||
"cliVersion": "2.25.1",
|
||||
"priorBundleVersion": "codeql-bundle-v2.24.3",
|
||||
"priorCliVersion": "2.24.3"
|
||||
"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));
|
||||
|
||||
@@ -5,17 +5,20 @@ import * as actionsUtil from "./actions-util";
|
||||
import type { PullRequestBranches } from "./actions-util";
|
||||
import * as apiClient from "./api-client";
|
||||
import {
|
||||
shouldPerformDiffInformedAnalysis,
|
||||
getDiffInformedAnalysisBranches,
|
||||
prepareDiffInformedAnalysis,
|
||||
exportedForTesting,
|
||||
} from "./diff-informed-analysis-utils";
|
||||
import { Feature, initFeatures } from "./feature-flags";
|
||||
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
setupTests,
|
||||
createFeatures,
|
||||
mockCodeQLVersion,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
setupActionsVars,
|
||||
makeMacro,
|
||||
} from "./testing-utils";
|
||||
import { GitHubVariant, withTmpDir } from "./util";
|
||||
import type { GitHubVersion } from "./util";
|
||||
@@ -42,10 +45,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,
|
||||
) => {
|
||||
@@ -80,13 +82,13 @@ const testShouldPerformDiffInformedAnalysis = test.macro({
|
||||
.stub(actionsUtil, "getPullRequestBranches")
|
||||
.returns(testCase.pullRequestBranches);
|
||||
|
||||
const result = await shouldPerformDiffInformedAnalysis(
|
||||
const branches = await getDiffInformedAnalysisBranches(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, expectedResult);
|
||||
t.is(branches !== undefined, expectedResult);
|
||||
|
||||
delete process.env.CODEQL_ACTION_DIFF_INFORMED_QUERIES;
|
||||
|
||||
@@ -94,18 +96,16 @@ const testShouldPerformDiffInformedAnalysis = test.macro({
|
||||
getPullRequestBranchesStub.restore();
|
||||
});
|
||||
},
|
||||
title: (_, title) => `shouldPerformDiffInformedAnalysis: ${title}`,
|
||||
title: (title) => `getDiffInformedAnalysisBranches: ${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 +113,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 +122,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 +131,7 @@ test.serial(
|
||||
true,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false for CodeQL version 2.20.0",
|
||||
{
|
||||
codeQLVersion: "2.20.0",
|
||||
@@ -142,8 +139,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false for invalid GHES version",
|
||||
{
|
||||
gitHubVersion: {
|
||||
@@ -154,8 +150,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false for GHES version 3.18.5",
|
||||
{
|
||||
gitHubVersion: {
|
||||
@@ -166,8 +161,7 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns true for GHES version 3.19.0",
|
||||
{
|
||||
gitHubVersion: {
|
||||
@@ -178,8 +172,7 @@ test.serial(
|
||||
true,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
testShouldPerformDiffInformedAnalysis,
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
"returns false when not a pull request",
|
||||
{
|
||||
pullRequestBranches: undefined,
|
||||
@@ -187,6 +180,135 @@ test.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns false when not a pull request",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
const features = createFeatures([Feature.DiffInformedQueries]);
|
||||
|
||||
sinon.stub(actionsUtil, "getPullRequestBranches").returns(undefined);
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves({ type: GitHubVariant.DOTCOM });
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.false(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns false when applicability check throws",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
// A features implementation whose getValue rejects, simulating an
|
||||
// unexpected failure when determining whether diff-informed analysis
|
||||
// should run.
|
||||
const features: FeatureEnablement = {
|
||||
getEnabledDefaultCliVersions: async () => {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
getValue: async () => {
|
||||
throw new Error("feature flag lookup failed");
|
||||
},
|
||||
};
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.false(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns true when the diff is fetched successfully",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
const features = createFeatures([Feature.DiffInformedQueries]);
|
||||
|
||||
sinon
|
||||
.stub(actionsUtil, "getPullRequestBranches")
|
||||
.returns({ base: "main", head: "feature" });
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves({ type: GitHubVariant.DOTCOM });
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
sinon.stub(apiClient, "getApiClient").returns({
|
||||
rest: {
|
||||
repos: {
|
||||
compareCommitsWithBasehead: sinon
|
||||
.stub()
|
||||
.resolves({ data: { files: [] } }),
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.true(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns false when the diff API call fails",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
const features = createFeatures([Feature.DiffInformedQueries]);
|
||||
|
||||
sinon
|
||||
.stub(actionsUtil, "getPullRequestBranches")
|
||||
.returns({ base: "main", head: "feature" });
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves({ type: GitHubVariant.DOTCOM });
|
||||
const notFoundError: any = new Error("Not Found");
|
||||
notFoundError.status = 404;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
sinon.stub(apiClient, "getApiClient").returns({
|
||||
rest: {
|
||||
repos: {
|
||||
compareCommitsWithBasehead: sinon.stub().rejects(notFoundError),
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.false(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
|
||||
return exportedForTesting.getDiffRanges(
|
||||
{
|
||||
|
||||
@@ -5,9 +5,9 @@ import type { PullRequestBranches } from "./actions-util";
|
||||
import { getApiClient, getGitHubVersion } from "./api-client";
|
||||
import type { CodeQL } from "./codeql";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import { getRepositoryNwoFromEnv } from "./repository";
|
||||
import { GitHubVariant, satisfiesGHESVersion } from "./util";
|
||||
import { getErrorMessage, GitHubVariant, satisfiesGHESVersion } from "./util";
|
||||
|
||||
/**
|
||||
* This interface is an abbreviated version of the file diff object returned by
|
||||
@@ -21,20 +21,6 @@ interface FileDiff {
|
||||
patch?: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the action should perform diff-informed analysis.
|
||||
*/
|
||||
export async function shouldPerformDiffInformedAnalysis(
|
||||
codeql: CodeQL,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
return (
|
||||
(await getDiffInformedAnalysisBranches(codeql, features, logger)) !==
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the branches to use for diff-informed analysis.
|
||||
*
|
||||
@@ -69,6 +55,46 @@ export async function getDiffInformedAnalysisBranches(
|
||||
return branches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the diff ranges needed for diff-informed analysis for the current
|
||||
* run.
|
||||
*
|
||||
* @returns `true` if the diff ranges were successfully computed and persisted
|
||||
* and are therefore available for use, `false` otherwise.
|
||||
*/
|
||||
export async function prepareDiffInformedAnalysis(
|
||||
codeql: CodeQL,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
let branches: PullRequestBranches | undefined;
|
||||
try {
|
||||
branches = await getDiffInformedAnalysisBranches(codeql, features, logger);
|
||||
} catch (e) {
|
||||
// If we cannot determine whether diff-informed analysis applies (for
|
||||
// example, because a feature-flag lookup failed), treat it as not
|
||||
// applicable rather than triggering the overlay fallback.
|
||||
logger.warning(
|
||||
`Failed to determine branch information for diff-informed analysis: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!branches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return await withGroupAsync("Computing PR diff ranges", async () => {
|
||||
try {
|
||||
return await computeAndPersistDiffRanges(branches, logger);
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface DiffThunkRange {
|
||||
/** Relative path from the repository root, using forward slashes as separators. */
|
||||
path: string;
|
||||
@@ -151,6 +177,33 @@ export async function getPullRequestEditedDiffRanges(
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and persist the diff ranges for a pull request. This fetches the
|
||||
* diff from the GitHub API and writes it to the diff ranges JSON file so that
|
||||
* CodeQL can use it for diff-informed analysis.
|
||||
*
|
||||
* @param branches The base and head branches of the pull request, as returned
|
||||
* by `getDiffInformedAnalysisBranches`.
|
||||
* @param logger
|
||||
* @returns `true` if the diff ranges were successfully computed and persisted,
|
||||
* otherwise `false`.
|
||||
*/
|
||||
export async function computeAndPersistDiffRanges(
|
||||
branches: PullRequestBranches,
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
|
||||
if (ranges === undefined) {
|
||||
return false;
|
||||
}
|
||||
writeDiffRangesJsonFile(logger, ranges);
|
||||
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
|
||||
logger.info(
|
||||
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getFileDiffsWithBasehead(
|
||||
branches: PullRequestBranches,
|
||||
logger: Logger,
|
||||
|
||||
@@ -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/",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export async function run__ACTION__() {
|
||||
return await __ACTION__.runWrapper();
|
||||
}
|
||||
+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,
|
||||
});
|
||||
|
||||
|
||||
+82
-31
@@ -29,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;
|
||||
}
|
||||
|
||||
@@ -44,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",
|
||||
@@ -72,6 +97,19 @@ export enum Feature {
|
||||
OverlayAnalysisGo = "overlay_analysis_go",
|
||||
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.
|
||||
@@ -85,7 +123,6 @@ export enum Feature {
|
||||
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",
|
||||
PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib",
|
||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||
/** Note that this currently only disables baseline file coverage information. */
|
||||
SkipFileCoverageOnPrs = "skip_file_coverage_on_prs",
|
||||
@@ -125,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",
|
||||
@@ -278,6 +320,16 @@ export const featureConfig = {
|
||||
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",
|
||||
@@ -298,12 +350,6 @@ export const featureConfig = {
|
||||
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",
|
||||
@@ -353,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>;
|
||||
@@ -378,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],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -393,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -525,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,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
|
||||
@@ -632,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;
|
||||
@@ -641,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(
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a18 0\tlib/git-utils.js\n" +
|
||||
"100644 d89514599a9a99f22b4085766d40af7b99974827 0\tlib/git-utils.js.map\n" +
|
||||
"100644 a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96 0\tsrc/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", "--stage"],
|
||||
"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(
|
||||
"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"',
|
||||
);
|
||||
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(
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a18 0\tlib/git-utils.js\n" +
|
||||
"invalid-line-format\n" +
|
||||
"100644 a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96 0\tsrc/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',
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+31
-9
@@ -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,11 +16,11 @@ import {
|
||||
import { ConfigurationError, getRequiredEnvParam } from "./util";
|
||||
|
||||
/**
|
||||
* Minimum Git version required for overlay analysis. The
|
||||
* `git ls-files --recurse-submodules` option, which is used by
|
||||
* `getFileOidsUnderPath`, was introduced in Git 2.11.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.11.0";
|
||||
export const GIT_MINIMUM_VERSION_FOR_OVERLAY_WITH_SUBMODULES = "2.36.0";
|
||||
|
||||
/**
|
||||
* Git version information
|
||||
@@ -245,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
|
||||
@@ -261,11 +273,21 @@ export const getFileOidsUnderPath = async function (
|
||||
// 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 --stage has been available since Git 2.11.0,
|
||||
// while --format was only introduced in Git 2.38.0, which would limit overlay rollout.
|
||||
// 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", "--stage"],
|
||||
args,
|
||||
"Cannot list Git OIDs of tracked files.",
|
||||
);
|
||||
|
||||
@@ -280,8 +302,8 @@ export const getFileOidsUnderPath = async function (
|
||||
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}`);
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ import { EnvVar } from "./environment";
|
||||
import { Feature } from "./feature-flags";
|
||||
import * as initActionPostHelper from "./init-action-post-helper";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import * as overlayStatus from "./overlay/status";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
createFeatures,
|
||||
createTestConfig,
|
||||
DEFAULT_ACTIONS_VARS,
|
||||
makeMacro,
|
||||
makeVersionInfo,
|
||||
RecordingLogger,
|
||||
setupActionsVars,
|
||||
@@ -601,7 +602,7 @@ async function testFailedSarifUpload(
|
||||
uploadFiles.resolves({
|
||||
sarifID: "42",
|
||||
statusReport: { raw_upload_size_bytes: 20, zipped_upload_size_bytes: 10 },
|
||||
} as uploadLib.UploadResult);
|
||||
});
|
||||
const waitForProcessing = sinon.stub(uploadLib, "waitForProcessing");
|
||||
|
||||
const features = [] as Feature[];
|
||||
@@ -796,7 +797,7 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
const skippedUploadTest = test.macro({
|
||||
const skippedUploadTest = makeMacro({
|
||||
exec: async (
|
||||
t: ExecutionContext<unknown>,
|
||||
config: Partial<configUtils.Config>,
|
||||
@@ -823,9 +824,8 @@ const skippedUploadTest = test.macro({
|
||||
`tryUploadSarifIfRunFailed - skips upload ${providedTitle}`,
|
||||
});
|
||||
|
||||
test.serial(
|
||||
skippedUploadTest.serial(
|
||||
"without CodeQL command",
|
||||
skippedUploadTest,
|
||||
// No codeQLCmd
|
||||
{
|
||||
analysisKinds: [AnalysisKind.RiskAssessment],
|
||||
@@ -834,9 +834,8 @@ test.serial(
|
||||
"CodeQL command not found",
|
||||
);
|
||||
|
||||
test.serial(
|
||||
skippedUploadTest.serial(
|
||||
"if no language is configured",
|
||||
skippedUploadTest,
|
||||
// No explicit language configuration
|
||||
{
|
||||
analysisKinds: [AnalysisKind.RiskAssessment],
|
||||
@@ -845,9 +844,8 @@ test.serial(
|
||||
"Unexpectedly, the configuration is not for a single language.",
|
||||
);
|
||||
|
||||
test.serial(
|
||||
skippedUploadTest.serial(
|
||||
"if multiple languages is configured",
|
||||
skippedUploadTest,
|
||||
// Multiple explicit languages configured
|
||||
{
|
||||
analysisKinds: [AnalysisKind.RiskAssessment],
|
||||
|
||||
@@ -21,7 +21,7 @@ import * as dependencyCaching from "./dependency-caching";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import {
|
||||
createOverlayStatus,
|
||||
OverlayStatus,
|
||||
|
||||
@@ -207,7 +207,7 @@ function getJobStatusFromEnvironment(): JobStatus | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -222,5 +222,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user