Compare commits

...
Author SHA1 Message Date
github-actions[bot] 3c3df83d9b Rebuild 2026-11-11 18:00:07 +00:00
dependabot[bot] b7384929d0 Bump the actions-minor group across 1 directory with 2 updates
Bumps the actions-minor group with 2 updates in the /.github/workflows directory: [actions/setup-dotnet](https://github.com/actions/setup-dotnet) and [actions/setup-java](https://github.com/actions/setup-java).


Updates `actions/setup-dotnet` from 6.0.0 to 6.1.0
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/a98b56852c35b8e3190ac28c8c2271da59106c68...0f4a354dd5547a28c70b7853dbd95d5b67886e73)

Updates `actions/setup-java` from 6.1.0 to 6.2.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/b843ebf9b913146b38da2eaa7aeddf76074ae1ad...77dd5b2ce225ea11469928ac317850245a74ae78)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor
- dependency-name: actions/setup-java
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-11 17:56:06 +00:00
Mario Campos 3ea37a44b6 Merge pull request #4217 from github/mario-campos/warn-on-changelog-write
Add `post-comment` Action
2026-11-11 04:38:32 +00:00
Henry Mercer 592d162ba3 Merge pull request #4312 from github/henrymercer/all-platform-bundle-checks
Make the "All-platform bundle" PR check use the all-platform bundle
2026-11-10 18:07:39 +00:00
Henry MercerandCopilot App 4e3aa4135f Remove the CodeQL Bundle All PR check, which duplicates all-platform-bundle
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-09 17:13:44 +00:00
Henry MercerandCopilot App 91bae3ac68 Check that the all-platform-bundle PR check uses an all-platform bundle
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-09 17:13:32 +00:00
Henry MercerandCopilot App cfb3940966 Respect use-all-platform-bundle for the latest nightly in prepare-test
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-09 17:13:08 +00:00
Michael B. Gale fbfa9f3747 Merge pull request #4311 from github/henrymercer/exclude-notify-from-required-checks
Exclude `Notify` from required checks
2026-11-09 13:24:24 +00:00
Henry Mercer ce8b5602e2 Merge pull request #4302 from github/henrymercer/toolcache-cleanup-duration
Report how long toolcache cleanup takes
2026-11-09 12:03:29 +00:00
Henry Mercer e4ce43d857 Merge pull request #4309 from github/henrymercer/replace-macos-14-xlarge-runner
Remove `macos-14-xlarge` from `codeql` workflow
2026-11-09 11:54:44 +00:00
Henry MercerandCopilot App 822e91c47d Exclude Notify from required checks
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-06 18:24:29 +00:00
Henry MercerandCopilot App d2a785ca9c Remove macos-14-xlarge from codeql workflow
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-06 18:17:27 +00:00
Sam Robson 0911ee96d2 Merge pull request #4303 from github/mergeback/v4.39.3-to-main-33d7b85d
Mergeback v4.39.3 refs/heads/releases/v4 into main
2026-11-04 17:52:12 +00:00
github-actions[bot] d11b2246b1 Rebuild 2026-11-04 16:49:37 +00:00
github-actions[bot] 8e42d3873f Update changelog and version after v4.39.3 2026-11-04 16:49:21 +00:00
Sam Robson 33d7b85da1 Merge pull request #4299 from github/update-v4.39.3-064910ba2
Merge main into releases/v4
2026-11-04 16:47:01 +00:00
Michael B. Gale 8aab3d8601 Merge pull request #4298 from github/mbg/decouple-features-enum
Move `Feature` enum to a separate file
2026-11-04 15:50:28 +00:00
github-actions[bot] 24393f8692 Update changelog for v4.39.3 2026-11-04 15:21:56 +00:00
Henry MercerandCopilot App e70c0414bf Record how long the toolcache cleanup takes in telemetry
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-04 14:31:49 +00:00
Henry MercerandCopilot App e7ce43a4dc Log how long deleting each CodeQL version from the toolcache takes
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-04 14:27:07 +00:00
Michael B. Gale 064910ba27 Merge pull request #4297 from github/mbg/update-glob
Update `glob` to `14.0.3`
2026-11-04 13:56:56 +00:00
Michael B. Gale f532ac5951 Update glob to 14.0.3 2026-11-04 13:08:55 +00:00
Mario CamposandCopilot App 05870b623f Defer workflow activation to a follow-up PR
Keep the post-comment implementation independent of the workflow that loads it from main.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-11-03 11:13:53 -06:00
Mario Campos 9d1378fb6e Use new case expression for evaluating conditional --update-only flag 2026-11-02 09:23:54 -06:00
Mario Campos 4094755f8c Polish post-comment.ts usage comment 2026-11-02 09:19:54 -06:00
Mario Campos d71593936c Replace --create-if-missing to --update-only
`--update-only` is disabled by default, whereas `--create-if-missing` should've been enabled by default but was not.
2026-10-30 10:41:05 -05:00
Mario Campos 9de5eacd23 Make error a bit clearer and more actionable 2026-10-30 09:56:15 -05:00
Mario Campos 97a381d7b6 Split long line 2026-10-30 09:51:41 -05:00
Mario Campos eec64423e6 Make precedence explicit with parens 2026-10-30 09:50:21 -05:00
Mario Campos 8ba2b06960 Use parseRepositoryNwo instead of splitting manually 2026-10-30 09:49:42 -05:00
Mario Campos b25bc2e8e6 Change RepositoryNwo import to type 2026-10-30 09:43:29 -05:00
Mario Campos 0bc39f9ffc Prefer last comment when there are multiple to choose from 2026-10-30 09:35:55 -05:00
Mario Campos a5878670a9 Update actions/setup-node action to version 7.1.0 2026-10-30 09:28:58 -05:00
Mario Campos 1a18e9047f Merge branch 'main' into mario-campos/warn-on-changelog-write 2026-10-30 09:27:10 -05:00
Mario Campos 68a9dcbb35 Prefer == true over != false 2026-10-29 19:46:00 -05:00
Mario CamposandCopilot Autofix powered by AI 1304cc4df1 Potential fix for pull request finding 'CodeQL / Missing space in string concatenation'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-10-29 11:37:40 -05:00
Mario CamposandCopilot App 94f0998581 Avoid creating resolved comments without an existing warning
Use a boolean --create-if-missing flag, supplied by the composite action by default and omitted for resolution updates.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-29 11:32:57 -05:00
Mario Campos e9651489af Avoid non-null assertion operators by asserting arguments 2026-10-29 10:44:10 -05:00
Mario Campos 9359b6b571 Wrap --marker argument in HTML comment tags automatically 2026-10-29 10:34:12 -05:00
Mario Campos 19e1ab5452 Update pr-checks/post-comment.ts description comment 2026-10-29 10:28:26 -05:00
Mario Campos f13bcf383f Warn if more than one marked comment was found 2026-10-29 10:20:21 -05:00
Mario Campos 5bb3554330 Make input-validation assertions more granular 2026-10-29 10:13:06 -05:00
Mario Campos 87cac24cdd Avoid sparse checkout
This codebase is a tad coupled; it could end up biting us.
2026-10-29 10:03:32 -05:00
Mario Campos 2902eeab4d Checkout main instead of PR's base for the post-pr-comments job 2026-10-29 10:01:27 -05:00
Mario Campos 56fa196def Use YAML multiline string for the Action's inputs' descriptions 2026-10-29 09:58:48 -05:00
Mario Campos 3fb676f638 Use existing RepositoryNwo type 2026-10-29 09:56:18 -05:00
Mario Campos afaf324646 Move resolveToken to pr-checks/api-client.ts 2026-10-29 09:51:48 -05:00
Mario CamposandCopilot App c069d49e81 Test sticky comments using observable state
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 16:46:41 -05:00
Mario CamposandCopilot App 36f220a96c Remove unused post-comment step IDs
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 16:08:22 -05:00
Mario CamposandCopilot App 03137fc68d Rename changelog outputs to modified
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 16:06:08 -05:00
Mario CamposandCopilot App 94488778fd Simplify post-comment to a single set-comment mode
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 16:00:37 -05:00
Mario CamposandCopilot App ae23865548 Move comment branching into workflow steps
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 15:53:36 -05:00
Mario CamposandCopilot App 90d1bc983c Remove bespoke post-comment argument validation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 15:39:43 -05:00
Mario CamposandCopilot App ae9f97bade Make post-comment marker configurable
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-28 15:30:49 -05:00
Mario Campos e7dadca872 Refactor comment handling logic into performAction for improved testability 2026-10-09 17:35:09 -05:00
Mario Campos f1419b8c76 Reject empty --marker 2026-10-09 17:11:18 -05:00
Mario CamposandCopilot Autofix powered by AI 3c452a182d Use HTTP link to unreleased-change-notes/README.md
Update link in PR checks workflow to point to the correct README.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-09 10:36:48 -05:00
Mario CamposandCopilot Autofix powered by AI 4d464b0697 Improve issue ID parsing and validation logic
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-09 10:35:34 -05:00
Mario Campos 14cefaa03c Replace bash/run step with local Action post-comment 2026-10-08 23:03:55 -05:00
Mario Campos 8c8b0827d5 Add script and Action for managing PR comments 2026-10-08 23:02:25 -05:00
Mario Campos 9fe741e77d Simplify if condition of CHANGELOG-comment-post step 2026-10-08 15:26:56 -05:00
Mario CamposandMichael B. Gale 27d37d97e2 Format all file references as inline Markdown code spans
Co-authored-by: Michael B. Gale <mbg@github.com>
2026-10-08 15:19:38 -05:00
Mario Campos 0707124ab8 Delete CHANGELOG.md-modification comment if CHANGELOG.md no longer modified 2026-10-08 13:11:34 -05:00
Mario CamposandCopilot Autofix powered by AI 928548a094 Refine exclusion conditions for CHANGELOG-modification comment
Refactor conditions for PR title checks in workflow.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-08 12:57:52 -05:00
Mario CamposandCopilot Autofix powered by AI ccdf69089a Escape backticks PR comment body
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-08 12:55:10 -05:00
Mario Campos d97a656f70 Update CHANGELOG.md warning message to reference change-notes directory 2026-10-08 12:41:52 -05:00
Mario Campos 6e4d3fb63d Use a sticky comment to warn about CHANGELOG.md changes 2026-10-08 12:31:10 -05:00
Mario Campos ae75d466ce Exclude "Mergeback" and "Update default bundle" PRs from CHANGELOG.md change warning 2026-10-08 12:11:29 -05:00
Mario Campos 780e947861 Skip CHANGELOG.md modification warning for release and backport PRs 2026-10-08 11:48:09 -05:00
Mario Campos f7aa9dc1ba Move post-comment step to post-pr-comments job 2026-10-08 11:40:38 -05:00
Mario Campos 43ac64c267 Use gh api instead of third-party Action 2026-10-08 11:16:19 -05:00
Mario Campos fbd33ac6fc Gate comment-post step on successful fetch-base 2026-10-08 10:50:34 -05:00
Mario Campos b2b77a81a2 Add CHANGELOG.md modification check and warning comment 2026-10-08 10:47:46 -05:00
43 changed files with 2815 additions and 1733 deletions

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
name: "Post comment"
description: "Create or update a sticky PR comment identified by a caller-provided marker."
inputs:
marker:
description: >
Non-empty plain-text identifier for the comment. Use a unique, stable value
for each distinct comment; it is automatically wrapped in a hidden HTML comment.
required: true
body:
description: "Full comment markdown."
required: true
update-only:
description: Do nothing if no comment with the specified marker exists.
required: false
default: "false"
issue-id:
description: >
The issue or pull request number to post/update the comment on.
Defaults to the current pull request if not provided.
required: false
token:
description: "The GitHub token to authenticate with."
required: true
runs:
using: composite
steps:
- name: Post comment
shell: bash
env:
BODY: ${{ inputs.body }}
MARKER: ${{ inputs.marker }}
UPDATE_ONLY: ${{ case(inputs.update-only == 'true', '--update-only', '') }}
GH_TOKEN: ${{ inputs.token }}
ISSUE_ID: ${{ inputs.issue-id || github.event.pull_request.number }}
run: |
npx tsx ./pr-checks/post-comment.ts \
--body "$BODY" \
--marker "$MARKER" \
$UPDATE_ONLY \
--issue-id "$ISSUE_ID" \
--repository "$GITHUB_REPOSITORY"
+8 -1
View File
@@ -36,7 +36,14 @@ runs:
set -e # Fail this Action if `gh release list` fails.
if [[ "$VERSION" == "nightly" || "$VERSION" == "nightly-latest" ]]; then
echo "tools-url=nightly" >> "$GITHUB_OUTPUT"
if [[ "$USE_ALL_PLATFORM_BUNDLE" == "true" ]]; then
# With `tools: nightly`, the Action downloads the bundle for the runner's platform, so look
# up the latest nightly release here to get its all-platform bundle instead.
tag=$(gh release list --repo dsp-testing/codeql-cli-nightlies --limit 1 --json tagName --jq '.[0].tagName')
echo "tools-url=https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/$tag/codeql-bundle.tar.gz" >> "$GITHUB_OUTPUT"
else
echo "tools-url=nightly" >> "$GITHUB_OUTPUT"
fi
exit 0
elif [[ "$VERSION" == "linked" ]]; then
echo "tools-url=linked" >> "$GITHUB_OUTPUT"
+13 -1
View File
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
@@ -92,6 +92,18 @@ jobs:
# Swift is not supported on Ubuntu so we manually exclude it from the list here
languages: cpp,csharp,go,java,javascript,python,ruby
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Check that the bundle contains tools for all platforms
env:
CODEQL_PATH: ${{ steps.init.outputs.codeql-path }}
run: |
# Platform-specific bundles only contain the tools for their own platform.
tools_dir="$(dirname "$CODEQL_PATH")/tools"
for platform in linux64 osx64 win64; do
if [[ ! -d "$tools_dir/$platform" ]]; then
echo "::error::Expected an all-platform CodeQL bundle, but '$tools_dir' does not contain a '$platform' directory."
exit 1
fi
done
- name: Build code
run: ./build.sh
- uses: ./../action/analyze
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -61,7 +61,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Prepare test
@@ -63,7 +63,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Java
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
uses: actions/setup-java@77dd5b2ce225ea11469928ac317850245a74ae78 # v6.2.0
with:
java-version: ${{ inputs.java-version || '17' }}
distribution: temurin
+1 -1
View File
@@ -63,7 +63,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Java
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
uses: actions/setup-java@77dd5b2ce225ea11469928ac317850245a74ae78 # v6.2.0
with:
java-version: ${{ inputs.java-version || '17' }}
distribution: temurin
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -101,7 +101,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -77,7 +77,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -74,7 +74,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
+1 -1
View File
@@ -68,7 +68,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: ${{ inputs.dotnet-version || '9.x' }}
- name: Install Go
-1
View File
@@ -79,7 +79,6 @@ jobs:
- ubuntu-24.04
- windows-2022
- windows-2025
- macos-14-xlarge
- macos-15-xlarge
tools: ${{ fromJson(needs.check-codeql-versions.outputs.versions) }}
runs-on: ${{ matrix.os }}
@@ -63,7 +63,7 @@ jobs:
with:
go-version: ^1.13.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: "9.x"
- name: Assert best-effort artifact scan completed
+1 -1
View File
@@ -59,7 +59,7 @@ jobs:
with:
go-version: ^1.13.1
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
uses: actions/setup-dotnet@0f4a354dd5547a28c70b7853dbd95d5b67886e73 # v6.1.0
with:
dotnet-version: "9.x"
- name: Assert best-effort artifact scan completed
-1
View File
@@ -31,7 +31,6 @@ on:
- "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"
@@ -1,62 +0,0 @@
name: "PR Check - CodeQL Bundle All"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GO111MODULE: auto
on:
push:
branches:
- main
- releases/v*
pull_request:
merge_group:
types: [checks_requested]
schedule:
- cron: "0 5 * * *"
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
jobs:
test-codeql-bundle-all:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-26.04
version: nightly-latest
name: "CodeQL Bundle All"
if: github.triggering_actor != 'dependabot[bot]'
permissions:
contents: read
security-events: read
timeout-minutes: 45
runs-on: ${{ matrix.os }}
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Prepare test
id: prepare-test
uses: ./.github/actions/prepare-test
with:
version: ${{ matrix.version }}
use-all-platform-bundle: true
- name: Install .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: "9.x"
- id: init
uses: ./../action/init
with:
# We manually exclude Swift from the languages list here, as it is not supported on Ubuntu
languages: cpp,csharp,go,java,javascript,python,ruby
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Build code
run: ./build.sh
- uses: ./../action/analyze
env:
CODEQL_ACTION_TEST_MODE: true
+4
View File
@@ -6,6 +6,10 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
No user facing changes.
## 4.39.3 - 04 Nov 2026
- Fixed diff-informed alert filtering incorrectly removing alerts in changed files whose paths contain spaces, special characters, or non-ASCII characters. [#4283](https://github.com/github/codeql-action/pull/4283)
## 4.39.2 - 03 Nov 2026
- Update default CodeQL bundle version to [2.27.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.4). [#4284](https://github.com/github/codeql-action/pull/4284)
+2091 -1594
View File
File diff suppressed because it is too large. Load diff
+26 -34
View File
@@ -1,12 +1,12 @@
{
"name": "codeql",
"version": "4.39.3",
"version": "4.39.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.39.3",
"version": "4.39.4",
"license": "MIT",
"workspaces": [
"pr-checks"
@@ -59,7 +59,7 @@
"eslint-plugin-import-x": "^4.17.1",
"eslint-plugin-jsdoc": "^65.4.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^13.0.6",
"glob": "^14.0.3",
"globals": "^17.13.0",
"nock": "^14.0.17",
"prettier": "^3.9.10",
@@ -1811,6 +1811,12 @@
"node": ">=18.0.0"
}
},
"node_modules/@isaacs/nonrecursive-glob-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.0.2.tgz",
"integrity": "sha512-GJPHIgPPtFl+nLhFHyF0zZoFcOFLsZWbHFFzo+Zr+PWDXJbl9hHxb2w+9SiF2fHmLgKaXIpUzQo3SbBBZ9WObw==",
"license": "BlueOak-1.0.0"
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz",
@@ -3524,7 +3530,6 @@
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -3534,7 +3539,6 @@
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -4439,7 +4443,6 @@
},
"node_modules/color-convert": {
"version": "2.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -4450,7 +4453,6 @@
},
"node_modules/color-name": {
"version": "1.1.4",
"dev": true,
"license": "MIT"
},
"node_modules/comment-parser": {
@@ -4575,7 +4577,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -4830,7 +4831,6 @@
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/es-abstract": {
@@ -6273,14 +6273,14 @@
}
},
"node_modules/glob": {
"version": "13.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-14.0.3.tgz",
"integrity": "sha512-A1vjIwC3qpdCso45Iw127gnp7sPhqgY8lUQhDrArIs+GbmfaaVPFuoyy7WyLT9EPZCRZ7j7aLdwUhQmHWesEow==",
"license": "BlueOak-1.0.0",
"dependencies": {
"minimatch": "^10.2.2",
"minimatch": "^11.0.2",
"minipass": "^7.1.3",
"path-scurry": "^2.0.2"
"path-scurry": "^3.0.0"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -6324,12 +6324,13 @@
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.2.tgz",
"integrity": "sha512-pWTgz5CN3tjqyZNSK2tePlSmu9IwM41q8yn8Usdqqwe9TIHh+9dEpLLjF4uApsfPEAZY71qtSiA2B2APEr+1Yw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
"@isaacs/nonrecursive-glob-match": "^1.0.2",
"brace-expansion": "^5.0.12"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -6858,7 +6859,6 @@
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -7169,7 +7169,6 @@
},
"node_modules/isexe": {
"version": "2.0.0",
"dev": true,
"license": "ISC"
},
"node_modules/js-string-escape": {
@@ -7479,9 +7478,9 @@
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
},
"node_modules/lru-cache": {
"version": "11.5.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
"integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
"version": "11.5.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz",
"integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
@@ -8572,7 +8571,6 @@
},
"node_modules/path-key": {
"version": "3.1.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -8584,9 +8582,9 @@
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-3.0.0.tgz",
"integrity": "sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==",
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^11.0.0",
@@ -9172,7 +9170,6 @@
},
"node_modules/shebang-command": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -9183,7 +9180,6 @@
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -9269,7 +9265,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"engines": {
"node": ">=14"
},
@@ -9456,7 +9451,6 @@
},
"node_modules/string-width": {
"version": "4.2.3",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -9542,7 +9536,6 @@
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -10280,7 +10273,6 @@
},
"node_modules/which": {
"version": "2.0.2",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -10570,7 +10562,7 @@
"@octokit/core": "^7.0.8",
"@octokit/plugin-paginate-rest": ">=16.0.0",
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
"glob": "^13.0.6",
"glob": "^14.0.3",
"lite-matter": "^0.1.2",
"mdast-util-from-markdown": "^2.1.0",
"semver": "^7.8.5",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.39.3",
"version": "4.39.4",
"private": true,
"description": "CodeQL action",
"scripts": {
@@ -69,7 +69,7 @@
"eslint-plugin-import-x": "^4.17.1",
"eslint-plugin-jsdoc": "^65.4.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^13.0.6",
"glob": "^14.0.3",
"globals": "^17.13.0",
"nock": "^14.0.17",
"prettier": "^3.9.10",
@@ -96,7 +96,7 @@
"eslint-plugin-jsx-a11y": {
"semver": ">=6.3.1"
},
"glob": "^13.0.6",
"glob": "^14.0.3",
"undici": "^7.30.1"
}
}
+11
View File
@@ -8,3 +8,14 @@ export function getApiClient(token: string): ApiClient {
const opts = githubUtils.getOctokitOptions(token);
return new githubUtils.GitHub(opts);
}
/** Resolves the GitHub API token to use, from `GH_TOKEN` or `GITHUB_TOKEN`. */
export function resolveToken(env: NodeJS.ProcessEnv): string {
const token = env.GH_TOKEN?.trim() || env.GITHUB_TOKEN?.trim();
if (!token) {
throw new Error(
"Missing authentication token. Set GH_TOKEN or GITHUB_TOKEN.",
);
}
return token;
}
+12
View File
@@ -17,6 +17,18 @@ steps:
# Swift is not supported on Ubuntu so we manually exclude it from the list here
languages: cpp,csharp,go,java,javascript,python,ruby
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Check that the bundle contains tools for all platforms
env:
CODEQL_PATH: ${{ steps.init.outputs.codeql-path }}
run: |
# Platform-specific bundles only contain the tools for their own platform.
tools_dir="$(dirname "$CODEQL_PATH")/tools"
for platform in linux64 osx64 win64; do
if [[ ! -d "$tools_dir/$platform" ]]; then
echo "::error::Expected an all-platform CodeQL bundle, but '$tools_dir' does not contain a '$platform' directory."
exit 1
fi
done
- name: Build code
run: ./build.sh
- uses: ./../action/analyze
+2
View File
@@ -24,3 +24,5 @@ is:
# PRs), so requiring them would block the PRs they don't run on.
- "github-advanced-security"
- "License compliance check"
# Only runs when a PR is removed from the merge queue.
- "Notify"
+344
View File
@@ -0,0 +1,344 @@
#!/usr/bin/env npx tsx
/*
Tests for post-comment.ts.
*/
import * as assert from "node:assert/strict";
import { describe, it } from "node:test";
import { type ApiClient, resolveToken } from "./api-client";
import {
findExistingComment,
type Options,
parseOptions,
setComment,
} from "./post-comment";
/** A baseline set of valid CLI flags, as an array, for use in parseOptions tests. */
const validFlags = [
"--body",
"hello",
"--marker",
"marker",
"--issue-id",
"42",
"--repository",
"github/codeql-action",
];
/** Returns a copy of `validFlags` with the value following `flag` replaced by `value`. */
function withFlag(flag: string, value: string): string[] {
const flags = [...validFlags];
const index = flags.indexOf(flag);
flags[index + 1] = value;
return flags;
}
/** Returns a copy of `validFlags` with `flag` (and its value) removed entirely. */
function withoutFlag(flag: string): string[] {
const flags = [...validFlags];
const index = flags.indexOf(flag);
flags.splice(index, 2);
return flags;
}
describe("parseOptions", async () => {
await it("parses valid flags", () => {
const options = parseOptions(validFlags);
assert.deepEqual(options, {
body: "hello",
marker: "marker",
updateOnly: false,
issueId: 42,
repository: { owner: "github", repo: "codeql-action" },
});
});
await it("enables update-only mode when --update-only is supplied", () => {
const options = parseOptions([...validFlags, "--update-only"]);
assert.equal(options.updateOnly, true);
});
await it("rejects a missing --body", () => {
assert.throws(
() => parseOptions(withoutFlag("--body")),
/Missing required flag --body/,
);
});
await it("rejects an empty --body", () => {
assert.throws(
() => parseOptions(withFlag("--body", "")),
/The value for --body cannot be empty/,
);
});
await it("rejects a whitespace-only --body", () => {
assert.throws(
() => parseOptions(withFlag("--body", " \t\n")),
/The value for --body cannot be empty/,
);
});
await it("rejects a missing --marker", () => {
assert.throws(
() => parseOptions(withoutFlag("--marker")),
/Missing required flag --marker/,
);
});
await it("rejects an empty --marker", () => {
assert.throws(
() => parseOptions(withFlag("--marker", "")),
/The value for --marker cannot be empty/,
);
});
await it("rejects a whitespace-only --marker", () => {
assert.throws(
() => parseOptions(withFlag("--marker", " \t\n")),
/The value for --marker cannot be empty/,
);
});
await it("rejects a missing --repository", () => {
assert.throws(
() => parseOptions(withoutFlag("--repository")),
/Missing required flag --repository/,
);
});
await it("rejects an empty --repository", () => {
assert.throws(
() => parseOptions(withFlag("--repository", "")),
/The value for --repository cannot be empty/,
);
});
await it("rejects a whitespace-only --repository", () => {
assert.throws(
() => parseOptions(withFlag("--repository", " \t\n")),
/The value for --repository cannot be empty/,
);
});
await it("rejects an invalid --repository value", () => {
assert.throws(
() => parseOptions(withFlag("--repository", "github/codeql/action")),
/Invalid --repository value/,
);
});
});
describe("resolveToken", async () => {
await it("reads the token from GH_TOKEN", () => {
assert.equal(
resolveToken({ GH_TOKEN: "gh-token", GITHUB_TOKEN: "gha-token" }),
"gh-token",
);
});
await it("falls back to GITHUB_TOKEN", () => {
assert.equal(resolveToken({ GITHUB_TOKEN: "gha-token" }), "gha-token");
});
await it("throws when no token is set", () => {
assert.throws(() => resolveToken({}), /Missing authentication token/);
});
});
/** A minimal fake of the subset of `ApiClient` used by post-comment.ts. */
function fakeClient(existingComments: Array<{ id: number; body?: string }>) {
const comments = existingComments.map((comment) => ({ ...comment }));
let nextId =
comments.reduce((maxId, comment) => Math.max(maxId, comment.id), 0) + 1;
const client = {
paginate: async () => comments,
rest: {
issues: {
listComments: () => {},
createComment: async ({ body }: { body: string }) => {
comments.push({ id: nextId++, body });
},
updateComment: async ({
comment_id,
body,
}: {
comment_id: number;
body: string;
}) => {
const comment = comments.find(({ id }) => id === comment_id);
assert.ok(comment, `Comment ${comment_id} does not exist.`);
comment.body = body;
},
},
},
};
return { client: client as unknown as ApiClient, comments };
}
describe("findExistingComment", async () => {
await it("finds the id of the first comment containing the marker", async () => {
const { client } = fakeClient([
{ id: 1, body: "unrelated" },
{ id: 2, body: "<!-- marker -->\nhello" },
{ id: 3, body: "<!-- marker -->\nanother" },
]);
const id = await findExistingComment(
client,
{ owner: "owner", repo: "repo" },
1,
"marker",
);
assert.equal(id, 3);
});
await it("returns undefined when no comment matches", async () => {
const { client } = fakeClient([{ id: 1, body: "unrelated" }]);
const id = await findExistingComment(
client,
{ owner: "owner", repo: "repo" },
1,
"marker",
);
assert.equal(id, undefined);
});
});
/** A baseline set of `Options`, for use in `setComment` tests. */
const baseOptions: Options = {
body: "hello",
marker: "marker",
updateOnly: false,
issueId: 42,
repository: { owner: "owner", repo: "repo" },
};
function assertCommentContent(
comment: { body?: string },
marker: string,
body: string,
): void {
assert.ok(comment.body?.startsWith(`<!-- ${marker} -->`));
assert.ok(comment.body?.endsWith(body));
}
describe("setComment", async () => {
await it("does not create a comment in update-only mode when none exists", async () => {
const { client, comments } = fakeClient([]);
await setComment(client, { ...baseOptions, updateOnly: true });
// Resolution does not introduce a warning that was never posted.
assert.deepEqual(comments, []);
});
await it("leaves unrelated comments unchanged in update-only mode", async () => {
const originalComments = [{ id: 7, body: "unrelated" }];
const { client, comments } = fakeClient(originalComments);
await setComment(client, { ...baseOptions, updateOnly: true });
// Unrelated comments neither trigger resolution nor get overwritten.
assert.deepEqual(comments, originalComments);
});
await it("uses the supplied marker without affecting other comments", async () => {
const marker = "custom-warning";
const { client, comments } = fakeClient([
{ id: 1, body: "<!-- marker -->\nunrelated warning" },
{ id: 2, body: "<!-- custom-warning -->\nold warning" },
]);
const options = parseOptions(withFlag("--marker", marker));
await setComment(client, options);
// Only the comment with the supplied marker changes.
assert.equal(comments.length, 2);
assert.deepEqual(comments[0], {
id: 1,
body: "<!-- marker -->\nunrelated warning",
});
assert.equal(comments[1].id, 2);
assertCommentContent(comments[1], marker, options.body);
});
await it("creates a new comment with the marker prepended", async () => {
const { client, comments } = fakeClient([]);
// No comment has the marker before posting.
assert.equal(comments.length, 0);
assert.ok(
!comments.some(({ body }) => body?.startsWith("<!-- marker -->")),
);
await setComment(client, baseOptions);
// Posting adds exactly one comment with the marker before the body.
assert.equal(comments.length, 1);
assertCommentContent(comments[0], baseOptions.marker, baseOptions.body);
});
await it("creates a new comment when no existing comment matches", async () => {
const originalComments = [
{ id: 7, body: "unrelated" },
{ id: 2, body: "another unrelated comment" },
];
const { client, comments } = fakeClient(originalComments);
// Existing comments do not match the marker.
assert.ok(
!comments.some(({ body }) => body?.startsWith("<!-- marker -->")),
);
await setComment(client, baseOptions);
// A new comment is added without changing unrelated comments.
assert.equal(comments.length, originalComments.length + 1);
assert.deepEqual(
comments.slice(0, originalComments.length),
originalComments,
);
const markedComments = comments.filter(({ body }) =>
body?.startsWith("<!-- marker -->"),
);
// Exactly one comment contains the expected marker and body.
assert.equal(markedComments.length, 1);
assertCommentContent(
markedComments[0],
baseOptions.marker,
baseOptions.body,
);
// The new comment's ID does not collide with existing IDs.
assert.equal(new Set(comments.map(({ id }) => id)).size, comments.length);
});
await it("updates the existing comment when one is found", async () => {
const { client, comments } = fakeClient([
{ id: 7, body: "<!-- marker -->\nold" },
]);
await setComment(client, baseOptions);
// Updating preserves the comment's ID without creating a duplicate.
assert.equal(comments.length, 1);
assert.equal(comments[0].id, 7);
assertCommentContent(comments[0], baseOptions.marker, baseOptions.body);
});
await it("replaces a warning with the resolved state using the same marker", async () => {
const { client, comments } = fakeClient([
{ id: 9, body: "<!-- marker -->\nold" },
]);
const body =
"`CHANGELOG.md` is not modified in this PR. No changelog action is needed.";
await setComment(client, { ...baseOptions, body, updateOnly: true });
// The resolved state replaces the warning on the same marked comment.
assert.equal(comments.length, 1);
assert.equal(comments[0].id, 9);
assertCommentContent(comments[0], baseOptions.marker, body);
});
});
+180
View File
@@ -0,0 +1,180 @@
#!/usr/bin/env npx tsx
/*
Creates or updates a pull-request comment identified by a marker.
The marker is supplied via `--marker` and must be a non-empty plain-text identifier.
The CLI wraps the marker in a hidden HTML comment, prepends it to `--body`,
and uses it to find the comment on later runs.
Supply `--update-only` to suppress creation of comment; omit it
to update or create comments.
Example:
```sh
GH_TOKEN=<token> npx tsx ./pr-checks/post-comment.ts \
--body "The changelog needs updating." \
--marker "my-bot" \
--issue-id 123 \
--repository github/codeql-action
```
*/
import assert from "node:assert/strict";
import { parseArgs } from "node:util";
import { parseRepositoryNwo, type RepositoryNwo } from "../src/repository";
import { type ApiClient, getApiClient, resolveToken } from "./api-client";
/** Represents the command-line options. */
export interface Options {
/** The full comment body. */
body: string;
/** Plain-text identifier wrapped in a hidden HTML comment. */
marker: string;
/** Whether to only update a comment if a matching marker exists. */
updateOnly: boolean;
/** The issue/pull request number to post/update the comment on. */
issueId: number;
/** The repository to operate on. */
repository: RepositoryNwo;
}
function hiddenMarker(marker: string): string {
return `<!-- ${marker} -->`;
}
/** Parses CLI arguments supplied by the composite action into `Options`. */
export function parseOptions(argv?: string[]): Options {
const { values } = parseArgs({
args: argv,
options: {
body: { type: "string" },
marker: { type: "string" },
"update-only": { type: "boolean", default: false },
"issue-id": { type: "string" },
repository: { type: "string" },
},
strict: true,
});
assert(values.body !== undefined, "Missing required flag --body.");
assert(values.body.trim() !== "", "The value for --body cannot be empty.");
assert(values.marker !== undefined, "Missing required flag --marker.");
assert(
values.marker.trim() !== "",
"The value for --marker cannot be empty.",
);
assert(
values.repository !== undefined,
"Missing required flag --repository.",
);
assert(
values.repository.trim() !== "",
"The value for --repository cannot be empty.",
);
assert(
/^[\w.-]+\/[\w.-]+$/.test(values.repository),
"Invalid --repository value.",
);
return {
body: values.body,
marker: values.marker,
updateOnly: values["update-only"],
issueId: Number(values["issue-id"]),
repository: parseRepositoryNwo(values.repository),
};
}
/**
* Finds the first comment on the given issue/pull request whose body starts with `marker`, if any.
*/
export async function findExistingComment(
client: ApiClient,
repository: RepositoryNwo,
issueNumber: number,
marker: string,
): Promise<number | undefined> {
const formattedMarker = hiddenMarker(marker);
const comments = await client.paginate(client.rest.issues.listComments, {
...repository,
issue_number: issueNumber,
per_page: 100,
});
const markedComments = comments.filter((comment) =>
comment.body?.startsWith(formattedMarker),
);
if (markedComments.length > 1) {
console.warn(
`Found ${markedComments.length} comments with marker "${formattedMarker}". ` +
"Updating the first one and leaving the others unchanged. " +
"Use a unique marker for each distinct managed comment to avoid this situation.",
);
}
return markedComments.at(-1)?.id;
}
/**
* Creates or updates a marked comment using `client` to talk to the GitHub API.
*/
export async function setComment(
client: ApiClient,
options: Options,
): Promise<void> {
const { issueId, marker, repository } = options;
const body = `${hiddenMarker(marker)}\n${options.body}`;
const existingCommentId = await findExistingComment(
client,
repository,
issueId,
marker,
);
if (existingCommentId === undefined) {
if (options.updateOnly) {
console.info(
"No existing comment found; skipping creation because --update-only was specified.",
);
return;
}
console.info("No existing comment found; creating a new comment.");
await client.rest.issues.createComment({
...repository,
issue_number: issueId,
body,
});
} else {
console.info(`Updating existing comment ${existingCommentId}.`);
await client.rest.issues.updateComment({
...repository,
comment_id: existingCommentId,
body,
});
}
}
async function main(): Promise<number> {
const options = parseOptions();
const token = resolveToken(process.env);
const client = getApiClient(token);
await setComment(client, options);
return 0;
}
async function run(): Promise<void> {
try {
process.exit(await main());
} catch (err) {
console.error(err instanceof Error ? err.message : String(err));
process.exit(1);
}
}
if (import.meta.main) {
void run();
}
+4 -4
View File
@@ -285,8 +285,8 @@ const languageSetups: LanguageSetups = {
name: "Install Java",
uses: pinnedUses(
"actions/setup-java",
"b843ebf9b913146b38da2eaa7aeddf76074ae1ad",
"v6.1.0",
"77dd5b2ce225ea11469928ac317850245a74ae78",
"v6.2.0",
),
with: {
"java-version": `\${{ inputs.java-version || '${defaultLanguageVersions.java}' }}`,
@@ -320,8 +320,8 @@ const languageSetups: LanguageSetups = {
name: "Install .NET",
uses: pinnedUses(
"actions/setup-dotnet",
"a98b56852c35b8e3190ac28c8c2271da59106c68",
"v6.0.0",
"0f4a354dd5547a28c70b7853dbd95d5b67886e73",
"v6.1.0",
),
with: {
"dotnet-version": `\${{ inputs.dotnet-version || '${defaultLanguageVersions.csharp}' }}`,
+47 -6
View File
@@ -1821,7 +1821,8 @@ function createToolcacheEntry(
/**
* Stubs out the download and the diagnostic sink, then downloads the CodeQL tools.
*
* @returns the extraction directory and the toolcache cleanup diagnostic, if emitted.
* @returns the extraction directory and, if the toolcache cleanup diagnostic was emitted, the
* cleanup duration it reports and its other attributes.
*/
async function runDownloadCodeQL(
toolcacheRoot: string,
@@ -1837,6 +1838,7 @@ async function runDownloadCodeQL(
): Promise<{
codeqlFolder: string;
cleanupDiagnostic: toolsDownload.ToolcacheCleanupResult | undefined;
cleanupDurationMs: number | undefined;
}> {
stubDownloadAndExtract();
const addDiagnostic = sinon.stub(diagnostics, "addNoLanguageDiagnostic");
@@ -1866,11 +1868,21 @@ async function runDownloadCodeQL(
.map((call) => call.args[1])
.find((d) => d.source?.id === "codeql-action/toolcache-bundle-cleanup");
return {
codeqlFolder,
cleanupDiagnostic: diagnostic?.attributes as
toolsDownload.ToolcacheCleanupResult | undefined,
};
if (diagnostic === undefined) {
return {
codeqlFolder,
cleanupDiagnostic: undefined,
cleanupDurationMs: undefined,
};
}
// The duration varies between runs, so split it out to let tests compare the other attributes
// exactly.
const { durationMs, ...cleanupDiagnostic } =
diagnostic.attributes as toolsDownload.ToolcacheCleanupResult & {
durationMs: number;
};
return { codeqlFolder, cleanupDiagnostic, cleanupDurationMs: durationMs };
}
/**
@@ -1991,6 +2003,35 @@ test.serial(
},
);
test.serial(
"downloadCodeQLBundle reports the total time taken to clean up the toolcache",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
createToolcacheEntry(tmpDir, "CodeQL", CLEANUP_CLI_VERSION);
createToolcacheEntry(tmpDir, "CodeQL", CLEANUP_STALE_VERSION);
// Advance the clock by 1.5s each time a path is deleted, so that deleting both versions
// takes 3s.
let elapsedMs = 1000;
sinon.stub(performance, "now").callsFake(() => elapsedMs);
const rm = fs.promises.rm;
sinon.stub(fs.promises, "rm").callsFake(async (...args) => {
elapsedMs += 1500;
await rm(...args);
});
const { cleanupDurationMs } = await runDownloadCodeQL(
tmpDir,
CLEANUP_BUNDLE_VERSION,
);
t.is(cleanupDurationMs, 3000);
});
},
);
test.serial(
"downloadCodeQLBundle reports no deleted versions when the toolcache has no CodeQL bundles",
async (t) => {
+2 -1
View File
@@ -814,6 +814,7 @@ async function tryDeleteToolcacheBundles({
return;
}
const startTime = performance.now();
const result = await deleteToolcacheBundles({ env, logger });
addNoLanguageDiagnostic(
@@ -821,7 +822,7 @@ async function tryDeleteToolcacheBundles({
makeTelemetryDiagnostic(
"codeql-action/toolcache-bundle-cleanup",
"Toolcache CodeQL bundle cleanup",
{ ...result },
{ ...result, durationMs: util.durationMsSince(startTime) },
),
);
}
+4 -1
View File
@@ -342,13 +342,16 @@ export async function deleteToolcacheBundles({
const versionDirectory = path.join(toolDirectory, entry.name);
try {
const deletionStart = performance.now();
await fs.promises.rm(versionDirectory, {
force: true,
recursive: true,
});
deletedVersions.push(entry.name);
logger.info(
`Deleted the CodeQL tools at '${versionDirectory}' from the toolcache to free up disk space.`,
`Deleted the CodeQL tools at '${versionDirectory}' from the toolcache to free up disk space (${formatDuration(
durationMsSince(deletionStart),
)}).`,
);
} catch (e) {
failed = true;
@@ -1 +0,0 @@
- Fixed diff-informed alert filtering incorrectly removing alerts in changed files whose paths contain spaces, special characters, or non-ASCII characters. [#4283](https://github.com/github/codeql-action/pull/4283)