Compare commits

..
Author SHA1 Message Date
Michael B. Gale a473da35c4 Add changelog note 2025-11-05 11:18:45 +00:00
github-actions[bot] 74415abe39 Update default bundle to codeql-bundle-v2.23.4 2025-11-05 11:11:42 +00:00
Michael B. Gale 862f5666b3 Merge pull request #3275 from github/mbg/checks/filter-ccr
Filter CCR jobs in `update-required-checks.sh`
2025-11-05 10:15:57 +00:00
Michael B. Gale b00addd1d3 Merge pull request #3274 from github/mbg/macos-13 2025-11-05 09:01:53 +00:00
Michael B. Gale d03fd76232 Filter CCR jobs in update-required-checks.sh 2025-11-04 22:23:12 +00:00
Michael B. Gale 9d5565fba2 Remove macos-13 from codeql workflow 2025-11-04 21:29:25 +00:00
Henry Mercer 95b1867cf7 Merge pull request #3269 from github/dependabot/npm_and_yarn/actions/io-2.0.0
Bump @actions/io from 1.1.3 to 2.0.0
2025-11-04 14:23:27 +00:00
github-actions[bot] 238f5f2946 Rebuild 2025-11-04 14:00:06 +00:00
dependabot[bot] a53b4967d7 Bump @actions/io from 1.1.3 to 2.0.0
Bumps [@actions/io](https://github.com/actions/toolkit/tree/HEAD/packages/io) from 1.1.3 to 2.0.0.
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/io/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/io)

---
updated-dependencies:
- dependency-name: "@actions/io"
  dependency-version: 2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-04 13:58:17 +00:00
Henry Mercer 493ffd8e5c Merge pull request #3268 from github/dependabot/npm_and_yarn/types/archiver-7.0.0
Bump @types/archiver from 6.0.4 to 7.0.0
2025-11-04 13:56:51 +00:00
github-actions[bot] f23547cd26 Rebuild 2025-11-04 12:25:05 +00:00
dependabot[bot] 58f5e3dab8 Bump @types/archiver from 6.0.4 to 7.0.0
Bumps [@types/archiver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/archiver) from 6.0.4 to 7.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/archiver)

---
updated-dependencies:
- dependency-name: "@types/archiver"
  dependency-version: 7.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-04 12:19:24 +00:00
Henry Mercer 98e0ffef96 Merge pull request #3266 from github/dependabot/npm_and_yarn/npm-minor-29e8df1594
Bump the npm-minor group with 7 updates
2025-11-04 11:09:56 +00:00
github-actions[bot] 85eb524170 Rebuild 2025-11-04 10:34:56 +00:00
dependabot[bot] a3ea4ef532 Bump the npm-minor group with 7 updates
Bumps the npm-minor group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@octokit/request-error](https://github.com/octokit/request-error.js) | `7.0.1` | `7.0.2` |
| [octokit](https://github.com/octokit/octokit.js) | `5.0.4` | `5.0.5` |
| [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) | `1.4.0` | `1.4.1` |
| [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.38.0` | `9.39.0` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.46.2` | `8.46.3` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.46.2` | `8.46.3` |
| [esbuild](https://github.com/evanw/esbuild) | `0.25.11` | `0.25.12` |


Updates `@octokit/request-error` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/octokit/request-error.js/releases)
- [Commits](https://github.com/octokit/request-error.js/compare/v7.0.1...v7.0.2)

Updates `octokit` from 5.0.4 to 5.0.5
- [Release notes](https://github.com/octokit/octokit.js/releases)
- [Commits](https://github.com/octokit/octokit.js/compare/v5.0.4...v5.0.5)

Updates `@eslint/compat` from 1.4.0 to 1.4.1
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v1.4.1/packages/compat)

Updates `@eslint/js` from 9.38.0 to 9.39.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v9.39.0/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.46.2 to 8.46.3
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.3/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.46.2 to 8.46.3
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.3/packages/parser)

Updates `esbuild` from 0.25.11 to 0.25.12
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.11...v0.25.12)

---
updated-dependencies:
- dependency-name: "@octokit/request-error"
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: octokit
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@eslint/compat"
  dependency-version: 1.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@eslint/js"
  dependency-version: 9.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.46.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.46.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: esbuild
  dependency-version: 0.25.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 18:47:33 +00:00
Henry Mercer 97580d7533 Merge pull request #3267 from github/dependabot/npm_and_yarn/octokit/types-16.0.0
Bump @octokit/types from 15.0.1 to 16.0.0
2025-11-03 18:12:51 +00:00
Henry Mercer 7ad64f0258 Merge pull request #3270 from github/dependabot/npm_and_yarn/actions/http-client-3.0.0
Bump @actions/http-client from 2.2.3 to 3.0.0
2025-11-03 18:11:30 +00:00
github-actions[bot] 61bcb70dce Rebuild 2025-11-03 17:22:15 +00:00
github-actions[bot] 5313cd14a8 Rebuild 2025-11-03 17:21:50 +00:00
dependabot[bot] 8fa298d060 Bump @actions/http-client from 2.2.3 to 3.0.0
Bumps [@actions/http-client](https://github.com/actions/toolkit/tree/HEAD/packages/http-client) from 2.2.3 to 3.0.0.
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/http-client/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/http-client)

---
updated-dependencies:
- dependency-name: "@actions/http-client"
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 17:20:38 +00:00
dependabot[bot] 7d1bed2dd0 Bump @octokit/types from 15.0.1 to 16.0.0
Bumps [@octokit/types](https://github.com/octokit/types.ts) from 15.0.1 to 16.0.0.
- [Release notes](https://github.com/octokit/types.ts/releases)
- [Commits](https://github.com/octokit/types.ts/compare/v15.0.1...v16.0.0)

---
updated-dependencies:
- dependency-name: "@octokit/types"
  dependency-version: 16.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 17:20:12 +00:00
Michael B. Gale 338146ca93 Merge pull request #3264 from github/mbg/ci/publish-on-tag
Create immutable action version on tag push
2025-10-31 16:48:43 +00:00
Michael B. Gale 64db1da706 Create immutable action version on tag push 2025-10-31 16:24:23 +00:00
Henry Mercer 9bd8638576 Merge pull request #3262 from github/mergeback/v4.31.2-to-main-0499de31
Mergeback v4.31.2 refs/heads/releases/v4 into main
2025-10-30 15:07:33 +00:00
github-actions[bot] 65da12b256 Rebuild 2025-10-30 14:36:57 +00:00
github-actions[bot] c228fecc25 Update changelog and version after v4.31.2 2025-10-30 14:33:10 +00:00
Henry Mercer 0499de31b9 Merge pull request #3261 from github/henrymercer/setup-python
Set up Python in mergeback workflow
2025-10-30 14:30:19 +00:00
Henry Mercer 3b96745d2b Set up Python in mergeback workflow 2025-10-30 14:06:12 +00:00
Michael B. Gale 8a06050a8c Merge pull request #3259 from github/update-v4.31.2-9576b5cbe
Merge main into releases/v4
2025-10-30 13:47:48 +00:00
github-actions[bot] 752a642cb2 Update changelog for v4.31.2 2025-10-30 13:27:33 +00:00
Michael B. Gale 9576b5cbe8 Merge pull request #3258 from github/mbg/enablement-errors/case-insensitive
Make `isEnablementError` case-insensitive
2025-10-30 11:59:43 +00:00
Henry Mercer cc8843728c Merge pull request #3257 from github/henrymercer/ubuntu-slim
Run lightweight workflows on `ubuntu-slim`
2025-10-30 11:44:22 +00:00
Michael B. Gale f0e9bf07f4 Make isEnablementError case-insensitive 2025-10-30 11:37:07 +00:00
Henry Mercer 2a3599c520 Run lightweight workflows on ubuntu-slim 2025-10-30 11:25:32 +00:00
Henry Mercer 514ff4d116 Merge pull request #3256 from github/henrymercer/resolve-bad-merge
Remove unused `console-log-level` dependency
2025-10-30 11:19:24 +00:00
Henry Mercer aab1c2f931 Merge pull request #3253 from github/mergeback/v4.31.1-to-main-5fe9434c
Mergeback v4.31.1 refs/heads/releases/v4 into main
2025-10-30 11:04:54 +00:00
Henry Mercer b2bffa615d Remove unused console-log-level dependency
This was added back by a bad merge.
2025-10-30 10:40:23 +00:00
github-actions[bot] e7811794d3 Rebuild 2025-10-30 10:31:33 +00:00
github-actions[bot] c7a5b09374 Merge remote-tracking branch 'origin/main' into mergeback/v4.31.1-to-main-5fe9434c 2025-10-30 10:30:29 +00:00
github-actions[bot] cbcb06a3ae Update changelog and version after v4.31.1 2025-10-30 10:29:22 +00:00
Henry Mercer 5fe9434cd2 Merge pull request #3252 from github/update-v4.31.1-777daa0c7
Merge main into releases/v4
2025-10-30 10:28:34 +00:00
Henry Mercer 8d50be301c Merge pull request #3245 from github/dependabot/github_actions/dot-github/workflows/actions/download-artifact-6
Bump actions/download-artifact from 5 to 6 in /.github/workflows
2025-10-30 10:02:36 +00:00
github-actions[bot] 237497c8f0 Update changelog for v4.31.1 2025-10-30 09:40:55 +00:00
Michael B. Gale 777daa0c71 Merge pull request #3239 from github/mbg/remove/add-snippets
Remove `add-snippets` input
2025-10-29 13:32:20 +00:00
Michael B. GaleandEsben Sparre Andreasen 74c8748a6f Update analyze/action.yml
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
2025-10-29 10:34:13 +00:00
Michael B. Gale 34c50c1d29 Merge pull request #3251 from github/mbg/user-error/enablement
Turn enablement errors into configuration errors
2025-10-29 09:57:38 +00:00
Michael B. Gale 4ae68afd84 Warn if the add-snippets input is used 2025-10-29 09:29:28 +00:00
Michael B. Gale 52a7bd7b6e Check for 403 status 2025-10-29 08:35:19 +00:00
Michael B. Gale 194ba0ee2d Make error message tests less brittle 2025-10-29 08:29:11 +00:00
Michael B. Gale 53acf0b8aa Turn enablement errors into configuration errors 2025-10-28 21:17:30 +00:00
Henry Mercer ac9aeee226 Merge pull request #3249 from github/henrymercer/api-logging
Use Actions logger in API client
2025-10-28 17:05:58 +00:00
Henry Mercer d49e837b8c Merge branch 'main' into henrymercer/api-logging 2025-10-28 16:49:10 +00:00
Henry Mercer 3d988b275a Pass minimal copy of core 2025-10-28 16:33:21 +00:00
Henry Mercer 8cc18acfa4 Merge pull request #3250 from github/henrymercer/prefer-fs-delete
Use Node `fs` APIs instead of `del`
2025-10-28 15:45:56 +00:00
Henry Mercer ea5cb4a016 Merge branch 'main' into henrymercer/prefer-fs-delete 2025-10-28 15:26:42 +00:00
Henry Mercer e1c8976a56 Merge pull request #3247 from github/henrymercer/disk-usage-node-api
Check disk usage using Node.js API
2025-10-28 15:24:12 +00:00
Henry Mercer 4256e2e2a0 Merge branch 'main' into henrymercer/disk-usage-node-api 2025-10-28 15:05:13 +00:00
Henry Mercer 66459ea37c Apply suggestion 2025-10-28 15:04:31 +00:00
Henry Mercer 1af9394995 Merge pull request #3244 from github/dependabot/github_actions/dot-github/workflows/actions-minor-b11285d543
Bump ruby/setup-ruby from 1.265.0 to 1.267.0 in /.github/workflows in the actions-minor group across 1 directory
2025-10-28 13:28:36 +00:00
Henry Mercer 311fc42780 Merge pull request #3242 from github/dependabot/npm_and_yarn/npm-minor-75b724c14c
Bump the npm-minor group with 4 updates
2025-10-28 13:25:25 +00:00
Henry Mercer 284bf9b047 Merge pull request #3241 from github/dependabot/github_actions/dot-github/workflows/actions/upload-artifact-5
Bump actions/upload-artifact from 4 to 5 in /.github/workflows
2025-10-28 13:23:08 +00:00
Henry Mercer a53e78ee2a Merge pull request #3243 from github/dependabot/npm_and_yarn/actions/artifact-4.0.0
Bump @actions/artifact from 2.3.1 to 4.0.0
2025-10-28 13:12:23 +00:00
Henry Mercer d84f470a9a Improve method naming 2025-10-28 13:06:14 +00:00
Henry Mercer 41c0a26213 Use Node fs APIs instead of del 2025-10-28 13:00:25 +00:00
Henry Mercer d4ba404a20 Tweak assertions 2025-10-28 12:50:22 +00:00
Henry Mercer 55895ef678 Stub GITHUB_WORKSPACE in test 2025-10-28 12:45:22 +00:00
Henry Mercer fe16891f40 Add unit test for checkDiskUsage 2025-10-28 12:40:23 +00:00
Henry Mercer 57c7b6afb6 Disable SIP disablement check 2025-10-28 12:35:41 +00:00
Michael B. Gale 44aeac1a37 Merge branch 'main' into dependabot/github_actions/dot-github/workflows/actions/download-artifact-6 2025-10-28 12:21:17 +00:00
Henry Mercer 8b1e55d11e Use Actions logger in API client
This allows us to remove the `console-log-level` dependency.
2025-10-28 12:15:03 +00:00
Henry Mercer 20900ee769 Build: Run npm install when package-lock.json out of date 2025-10-28 11:52:42 +00:00
Michael B. Gale ad8ad9829e Merge pull request #3240 from github/mbg/allow-skip-workflow-validation
Support skipping workflow validation
2025-10-28 11:46:08 +00:00
Henry Mercer 239e305d18 Check disk usage using Node.js API
This was introduced in Node.js 18
2025-10-27 18:34:23 +00:00
dependabot[bot] 9c39f0afb0 Bump actions/download-artifact from 5 to 6 in /.github/workflows
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 18:05:01 +00:00
github-actions[bot] fcc1377ac6 Rebuild 2025-10-27 17:54:41 +00:00
dependabot[bot] b5bbb5ab73 Bump ruby/setup-ruby
Bumps the actions-minor group with 1 update in the /.github/workflows directory: [ruby/setup-ruby](https://github.com/ruby/setup-ruby).


Updates `ruby/setup-ruby` from 1.265.0 to 1.267.0
- [Release notes](https://github.com/ruby/setup-ruby/releases)
- [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb)
- [Commits](https://github.com/ruby/setup-ruby/compare/ab177d40ee5483edb974554986f56b33477e21d0...d5126b9b3579e429dd52e51e68624dda2e05be25)

---
updated-dependencies:
- dependency-name: ruby/setup-ruby
  dependency-version: 1.267.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 17:52:58 +00:00
github-actions[bot] 723a9469fd Rebuild 2025-10-27 17:41:35 +00:00
dependabot[bot] f9eed03ba2 Bump @actions/artifact from 2.3.1 to 4.0.0
Bumps [@actions/artifact](https://github.com/actions/toolkit/tree/HEAD/packages/artifact) from 2.3.1 to 4.0.0.
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/artifact/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/@actions/cache@4.0.0/packages/artifact)

---
updated-dependencies:
- dependency-name: "@actions/artifact"
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 17:39:59 +00:00
github-actions[bot] df9e49e9e8 Rebuild 2025-10-27 17:37:24 +00:00
dependabot[bot] c9d47e2ee9 Bump the npm-minor group with 4 updates
Bumps the npm-minor group with 4 updates: [@octokit/types](https://github.com/octokit/types.ts), [@types/archiver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/archiver), [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@octokit/types` from 15.0.0 to 15.0.1
- [Release notes](https://github.com/octokit/types.ts/releases)
- [Commits](https://github.com/octokit/types.ts/compare/v15.0.0...v15.0.1)

Updates `@types/archiver` from 6.0.3 to 6.0.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/archiver)

Updates `@typescript-eslint/eslint-plugin` from 8.46.1 to 8.46.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.2/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.46.1 to 8.46.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.2/packages/parser)

---
updated-dependencies:
- dependency-name: "@octokit/types"
  dependency-version: 15.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@types/archiver"
  dependency-version: 6.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.46.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.46.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 17:35:52 +00:00
github-actions[bot] 714962e17a Rebuild 2025-10-27 17:30:37 +00:00
dependabot[bot] 42f957bb51 Bump actions/upload-artifact from 4 to 5 in /.github/workflows
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 17:28:57 +00:00
Michael B. Gale 52cec4178d Downgrade log message from warning to debug level 2025-10-27 17:02:01 +00:00
Michael B. Gale 55c083790a Move checkWorkflow to workflow.ts 2025-10-27 17:01:23 +00:00
Michael B. Gale 50601762ea Also skip workflow validation for dynamic workflows 2025-10-27 16:10:08 +00:00
Michael B. Gale 06fbd897c4 Move workflow check to a function in init.ts and add tests 2025-10-27 15:57:44 +00:00
Michael B. Gale 127851b399 Add environment variable for skipping workflow validation 2025-10-27 15:42:43 +00:00
Kasper Svendsen 8d77149e0c Merge pull request #3238 from github/kaspersv/extract-diff-range-computation
Move diff-range computation into utils
2025-10-27 15:40:12 +01:00
Michael B. Gale db47d17142 Remove add-snippets input 2025-10-27 12:53:23 +00:00
Kasper Svendsen cc17bed958 Move diff-range computation tests 2025-10-27 09:46:16 +01:00
Kasper Svendsen 91ec0ed58f Move diff-range computation into utils for reuse 2025-10-27 09:43:11 +01:00
Kasper Svendsen 4e0b2cd814 Merge pull request #3232 from github/kaspersv/unique-overlay-base-keys
Ensure uniqueness of overlay-base database cache keys
2025-10-27 08:36:12 +01:00
Michael B. Gale ae78991f55 Merge pull request #3236 from github/mergeback/v4.31.0-to-main-4e94bd11
Mergeback v4.31.0 refs/heads/releases/v4 into main
2025-10-24 18:30:37 +01:00
github-actions[bot] dd565f3332 Rebuild 2025-10-24 17:11:09 +00:00
github-actions[bot] fa46f22b12 Update changelog and version after v4.31.0 2025-10-24 17:08:58 +00:00
Kasper Svendsen 66759e57b2 Improve error handling for overlay-base cache key creation 2025-10-24 15:49:26 +02:00
Kasper Svendsen cbcae45fff Reorder components of overlay-base cache key postfix 2025-10-24 15:46:17 +02:00
Kasper Svendsen b4ce335286 Ensure uniqueness of overlay-base database cache keys 2025-10-24 11:11:57 +02:00
Chuan-kai Lin c4b73722ba Add overlay-base database cache key tests 2025-10-24 10:47:17 +02:00
69 changed files with 72220 additions and 76741 deletions

No files matched your search

@@ -16,9 +16,9 @@ runs:
shell: bash
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.12
python-version: '3.12'
- name: Install dependencies
run: |
+1 -1
View File
@@ -79,7 +79,7 @@ jobs:
output: ${{ runner.temp }}/results
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.os }}-zstd-bundle.sarif
path: ${{ runner.temp }}/results/javascript.sarif
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
output: ${{ runner.temp }}/results
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: ${{ runner.temp }}/results/javascript.sarif
+1 -1
View File
@@ -78,7 +78,7 @@ jobs:
output: ${{ runner.temp }}/results
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: ${{ runner.temp }}/results/javascript.sarif
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
with:
output: ${{ runner.temp }}/results
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: ${{ runner.temp }}/results/javascript.sarif
+1 -1
View File
@@ -64,7 +64,7 @@ jobs:
with:
output: ${{ runner.temp }}/results
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: ${{ runner.temp }}/results/javascript.sarif
+3 -3
View File
@@ -83,7 +83,7 @@ jobs:
post-processed-sarif-path: ${{ runner.temp }}/post-processed
- name: Upload security SARIF
if: contains(matrix.analysis-kinds, 'code-scanning')
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
@@ -91,14 +91,14 @@ jobs:
retention-days: 7
- name: Upload quality SARIF
if: contains(matrix.analysis-kinds, 'code-quality')
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
path: ${{ runner.temp }}/results/javascript.quality.sarif
retention-days: 7
- name: Upload post-processed SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: |
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- name: Set up Ruby
uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0
uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
@@ -15,7 +15,7 @@ defaults:
jobs:
check-expected-release-files:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
permissions:
contents: read
+1 -1
View File
@@ -81,7 +81,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-13,macos-14,macos-15]
os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-14,macos-15]
tools: ${{ fromJson(needs.check-codeql-versions.outputs.versions) }}
runs-on: ${{ matrix.os }}
@@ -79,7 +79,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
- name: Check expected artifacts exist
run: |
LANGUAGES="cpp csharp go java javascript python"
+1 -1
View File
@@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
- name: Check expected artifacts exist
run: |
VERSIONS="stable-v2.20.3 default linked nightly-latest"
+1 -1
View File
@@ -16,7 +16,7 @@ permissions:
jobs:
sizeup:
name: Label PR with size
runs-on: ubuntu-latest
runs-on: ubuntu-slim
steps:
- name: Run sizeup
+4 -1
View File
@@ -24,7 +24,7 @@ defaults:
jobs:
merge-back:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
environment: Automation
if: github.repository == 'github/codeql-action'
env:
@@ -48,6 +48,9 @@ jobs:
with:
fetch-depth: 0 # ensure we have all tags and can push commits
- uses: actions/setup-node@v6
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Update git config
run: |
+1 -1
View File
@@ -29,7 +29,7 @@ defaults:
jobs:
prepare:
name: "Prepare release"
runs-on: ubuntu-latest
runs-on: ubuntu-slim
if: github.repository == 'github/codeql-action'
permissions:
+8 -20
View File
@@ -1,8 +1,10 @@
name: 'Publish Immutable Action Version'
on:
release:
types: [published]
push:
tags:
# Match version tags, but not the major version tags.
- 'v[0-9]+.**'
defaults:
run:
@@ -10,30 +12,16 @@ defaults:
jobs:
publish:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
permissions:
contents: read
id-token: write
packages: write
steps:
- name: Check release name
id: check
env:
RELEASE_NAME: ${{ github.event.release.name }}
run: |
echo "Release name: ${{ github.event.release.name }}"
if [[ $RELEASE_NAME == v* ]]; then
echo "This is a CodeQL Action release. Create an Immutable Action"
echo "is-action-release=true" >> $GITHUB_OUTPUT
else
echo "This is a CodeQL Bundle release. Do not create an Immutable Action"
echo "is-action-release=false" >> $GITHUB_OUTPUT
fi
- name: Checking out
if: steps.check.outputs.is-action-release == 'true'
- name: Checkout repository
uses: actions/checkout@v5
- name: Publish
if: steps.check.outputs.is-action-release == 'true'
- name: Publish immutable release
id: publish
uses: actions/publish-immutable-action@v0.0.4
@@ -29,7 +29,7 @@ fi
echo "Getting checks for $GITHUB_SHA"
# Ignore any checks with "https://", CodeQL, LGTM, Update, and ESLint checks.
CHECKS="$(gh api repos/github/codeql-action/commits/"${GITHUB_SHA}"/check-runs --paginate | jq --slurp --compact-output --raw-output '[.[].check_runs.[] | select(.conclusion != "skipped") | .name | select(contains("https://") or . == "CodeQL" or . == "Dependabot" or . == "check-expected-release-files" or contains("Update") or contains("ESLint") or contains("update") or contains("test-setup-python-scripts") | not)] | unique | sort')"
CHECKS="$(gh api repos/github/codeql-action/commits/"${GITHUB_SHA}"/check-runs --paginate | jq --slurp --compact-output --raw-output '[.[].check_runs.[] | select(.conclusion != "skipped") | .name | select(contains("https://") or . == "CodeQL" or . == "Dependabot" or . == "check-expected-release-files" or contains("Update") or contains("ESLint") or contains("update") or contains("test-setup-python-scripts") or . == "Agent" or . == "Cleanup artifacts" or . == "Prepare" or . == "Upload results" | not)] | unique | sort')"
echo "$CHECKS" | jq
+1 -1
View File
@@ -20,7 +20,7 @@ defaults:
jobs:
update-bundle:
if: github.event.release.prerelease && startsWith(github.event.release.tag_name, 'codeql-bundle-')
runs-on: ubuntu-latest
runs-on: ubuntu-slim
permissions:
contents: write # needed to push commits
pull-requests: write # needed to create pull requests
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
update:
timeout-minutes: 45
runs-on: ubuntu-latest
runs-on: ubuntu-slim
if: github.event_name == 'workflow_dispatch'
needs: [prepare]
env:
@@ -77,7 +77,7 @@ jobs:
backport:
timeout-minutes: 45
runs-on: ubuntu-latest
runs-on: ubuntu-slim
environment: Automation
needs: [prepare]
if: ${{ (github.event_name == 'push') && needs.prepare.outputs.backport_target_branches != '[]' }}
@@ -9,7 +9,7 @@ jobs:
update-supported-enterprise-server-versions:
name: Update Supported Enterprise Server Versions
timeout-minutes: 45
runs-on: ubuntu-latest
runs-on: ubuntu-slim
if: github.repository == 'github/codeql-action'
permissions:
contents: write # needed to push commits
+12
View File
@@ -2,6 +2,18 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## [UNRELEASED]
- Update default CodeQL bundle version to 2.23.4. [#3276](https://github.com/github/codeql-action/pull/3276)
## 4.31.2 - 30 Oct 2025
No user facing changes.
## 4.31.1 - 30 Oct 2025
- The `add-snippets` input has been removed from the `analyze` action. This input has been deprecated since CodeQL Action 3.26.4 in August 2024 when this removal was announced.
## 4.31.0 - 24 Oct 2025
- Bump minimum CodeQL bundle version to 2.17.6. [#3223](https://github.com/github/codeql-action/pull/3223)
+2 -6
View File
@@ -32,14 +32,10 @@ inputs:
and 13GB for macOS).
required: false
add-snippets:
description: Specify whether or not to add code snippets to the output sarif file.
description: Does not have any effect.
required: false
default: "false"
deprecationMessage: >-
The input "add-snippets" is deprecated and will be removed on the first release in August 2025.
When this input is set to true it is expected to add code snippets with an alert to the SARIF file.
However, since Code Scanning ignores code snippets provided as part of a SARIF file this is currently
a no operation. No alternative is available.
The input "add-snippets" has been removed and no longer has any effect.
skip-queries:
description: If this option is set, the CodeQL database will be built but no queries will be run on it. Thus, no results will be produced.
required: false
+7279 -6511
View File
File diff suppressed because it is too large. Load diff
+5476 -8013
View File
File diff suppressed because it is too large. Load diff
+4894 -822
View File
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.23.3",
"cliVersion": "2.23.3",
"priorBundleVersion": "codeql-bundle-v2.23.2",
"priorCliVersion": "2.23.2"
"bundleVersion": "codeql-bundle-v2.23.4",
"cliVersion": "2.23.4",
"priorBundleVersion": "codeql-bundle-v2.23.3",
"priorCliVersion": "2.23.3"
}
+8310 -14323
View File
File diff suppressed because it is too large. Load diff
+5378 -7912
View File
File diff suppressed because it is too large. Load diff
+4896 -824
View File
File diff suppressed because it is too large. Load diff
+5209 -7753
View File
File diff suppressed because it is too large. Load diff
+7249 -6480
View File
File diff suppressed because it is too large. Load diff
+4379 -1069
View File
File diff suppressed because it is too large. Load diff
+5182 -7570
View File
File diff suppressed because it is too large. Load diff
+7243 -6474
View File
File diff suppressed because it is too large. Load diff
+5250 -7794
View File
File diff suppressed because it is too large. Load diff
+693 -620
View File
File diff suppressed because it is too large. Load diff
+12 -16
View File
@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.31.0",
"version": "4.31.3",
"private": true,
"description": "CodeQL action",
"scripts": {
@@ -24,23 +24,20 @@
},
"license": "MIT",
"dependencies": {
"@actions/artifact": "^2.3.1",
"@actions/artifact": "^4.0.0",
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
"@actions/cache": "^4.1.0",
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^2.2.3",
"@actions/io": "^1.1.3",
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^2.0.2",
"@octokit/plugin-retry": "^6.0.0",
"@octokit/request-error": "^7.0.1",
"@octokit/request-error": "^7.0.2",
"@schemastore/package": "0.0.10",
"archiver": "^7.0.1",
"check-disk-space": "^3.4.0",
"console-log-level": "^1.4.1",
"del": "^8.0.0",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.15.11",
"get-folder-size": "^5.0.0",
@@ -48,29 +45,28 @@
"jsonschema": "1.4.1",
"long": "^5.3.2",
"node-forge": "^1.3.1",
"octokit": "^5.0.4",
"octokit": "^5.0.5",
"semver": "^7.7.3",
"uuid": "^13.0.0"
},
"devDependencies": {
"@ava/typescript": "6.0.0",
"@eslint/compat": "^1.4.0",
"@eslint/compat": "^1.4.1",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.38.0",
"@eslint/js": "^9.39.0",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@octokit/types": "^15.0.0",
"@types/archiver": "^6.0.3",
"@types/console-log-level": "^1.4.5",
"@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-forge": "^1.3.14",
"@types/semver": "^7.7.1",
"@types/sinon": "^17.0.4",
"@typescript-eslint/eslint-plugin": "^8.46.1",
"@typescript-eslint/eslint-plugin": "^8.46.3",
"@typescript-eslint/parser": "^8.41.0",
"ava": "^6.4.1",
"esbuild": "^0.25.11",
"esbuild": "^0.25.12",
"eslint": "^8.57.1",
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-filenames": "^1.3.2",
+1 -1
View File
@@ -27,7 +27,7 @@ steps:
output: ${{ runner.temp }}/results
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.os }}-zstd-bundle.sarif
path: ${{ runner.temp }}/results/javascript.sarif
+1 -1
View File
@@ -12,7 +12,7 @@ steps:
output: "${{ runner.temp }}/results"
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"
+1 -1
View File
@@ -25,7 +25,7 @@ steps:
output: "${{ runner.temp }}/results"
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"
@@ -17,7 +17,7 @@ steps:
with:
output: "${{ runner.temp }}/results"
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"
+1 -1
View File
@@ -11,7 +11,7 @@ steps:
with:
output: "${{ runner.temp }}/results"
- name: Upload SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"
+3 -3
View File
@@ -39,7 +39,7 @@ steps:
post-processed-sarif-path: "${{ runner.temp }}/post-processed"
- name: Upload security SARIF
if: contains(matrix.analysis-kinds, 'code-scanning')
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
@@ -47,14 +47,14 @@ steps:
retention-days: 7
- name: Upload quality SARIF
if: contains(matrix.analysis-kinds, 'code-quality')
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
path: "${{ runner.temp }}/results/javascript.quality.sarif"
retention-days: 7
- name: Upload post-processed SARIF
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: |
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
+1 -1
View File
@@ -4,7 +4,7 @@ description: "Tests using RuboCop to analyze a multi-language repository and the
versions: ["default"]
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0
uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+9 -3
View File
@@ -9,9 +9,15 @@ if [ "$GITHUB_ACTIONS" = "true" ]; then
fi
# Check if npm install is likely needed before proceeding
if [ ! -d node_modules ] || [ package-lock.json -nt node_modules/.package-lock.json ]; then
echo "Running 'npm install' because 'node_modules/.package-lock.json' appears to be outdated..."
if [ ! -d node_modules ]; then
echo "Running 'npm install' because 'node_modules' directory is missing."
npm install
elif [ package.json -nt package-lock.json ]; then
echo "Running 'npm install' because 'package-lock.json' appears to be outdated."
npm install
elif [ package-lock.json -nt node_modules/.package-lock.json ]; then
echo "Running 'npm install' because 'node_modules/.package-lock.json' appears to be outdated."
npm install
else
echo "Skipping 'npm install' because 'node_modules/.package-lock.json' appears to be up-to-date."
echo "Skipping 'npm install' because everything appears to be up-to-date."
fi
+1 -1
View File
@@ -78,7 +78,7 @@ test("analyze action with RAM & threads from environment variables", async (t) =
t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1");
t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=4992");
t.assert(runQueriesStub.calledOnce);
t.deepEqual(runQueriesStub.firstCall.args[3], "--threads=-1");
t.deepEqual(runQueriesStub.firstCall.args[2], "--threads=-1");
t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=4992");
});
});
+1 -1
View File
@@ -76,7 +76,7 @@ test("analyze action with RAM & threads from action inputs", async (t) => {
t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1");
t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=3012");
t.assert(runQueriesStub.calledOnce);
t.deepEqual(runQueriesStub.firstCall.args[3], "--threads=-1");
t.deepEqual(runQueriesStub.firstCall.args[2], "--threads=-1");
t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=3012");
});
});
+7 -1
View File
@@ -324,10 +324,16 @@ async function run() {
);
if (actionsUtil.getRequiredInput("skip-queries") !== "true") {
// Warn if the removed `add-snippets` input is used.
if (actionsUtil.getOptionalInput("add-snippets") !== undefined) {
logger.warning(
"The `add-snippets` input has been removed and no longer has any effect.",
);
}
runStats = await runQueries(
outputDir,
memory,
util.getAddSnippetsFlag(actionsUtil.getRequiredInput("add-snippets")),
threads,
diffRangePackDir,
actionsUtil.getOptionalInput("category"),
-202
View File
@@ -4,10 +4,8 @@ import * as path from "path";
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { CodeQuality, CodeScanning } from "./analyses";
import {
exportedForTesting,
runQueries,
defaultSuites,
resolveQuerySuiteAlias,
@@ -39,7 +37,6 @@ test("status report fields", async (t) => {
setupActionsVars(tmpDir, tmpDir);
const memoryFlag = "";
const addSnippetsFlag = "";
const threadsFlag = "";
sinon.stub(uploadLib, "validateSarifFileSchema");
@@ -105,7 +102,6 @@ test("status report fields", async (t) => {
const statusReport = await runQueries(
tmpDir,
memoryFlag,
addSnippetsFlag,
threadsFlag,
undefined,
undefined,
@@ -131,204 +127,6 @@ test("status report fields", async (t) => {
});
});
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
sinon
.stub(actionsUtil, "getRequiredInput")
.withArgs("checkout_path")
.returns("/checkout/path");
return exportedForTesting.getDiffRanges(
{
filename: "test.txt",
changes,
patch: patch?.join("\n"),
},
getRunnerLogger(true),
);
}
test("getDiffRanges: file unchanged", async (t) => {
const diffRanges = runGetDiffRanges(0, undefined);
t.deepEqual(diffRanges, []);
});
test("getDiffRanges: file diff too large", async (t) => {
const diffRanges = runGetDiffRanges(1000000, undefined);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 0,
endLine: 0,
},
]);
});
test("getDiffRanges: diff thunk with single addition range", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,6 +50,8 @@",
" a",
" b",
" c",
"+1",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 54,
},
]);
});
test("getDiffRanges: diff thunk with single deletion range", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,8 +50,6 @@",
" a",
" b",
" c",
"-1",
"-2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, []);
});
test("getDiffRanges: diff thunk with single update range", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,7 +50,7 @@",
" a",
" b",
" c",
"-1",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 53,
},
]);
});
test("getDiffRanges: diff thunk with addition ranges", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,7 +50,9 @@",
" a",
" b",
" c",
"+1",
" c",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 53,
},
{
path: "/checkout/path/test.txt",
startLine: 55,
endLine: 55,
},
]);
});
test("getDiffRanges: diff thunk with mixed ranges", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,7 +50,7 @@",
" a",
" b",
" c",
"-1",
" d",
"-2",
"+3",
" e",
" f",
"+4",
"+5",
" g",
" h",
" i",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 54,
endLine: 54,
},
{
path: "/checkout/path/test.txt",
startLine: 57,
endLine: 58,
},
]);
});
test("getDiffRanges: multiple diff thunks", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,6 +50,8 @@",
" a",
" b",
" c",
"+1",
"+2",
" d",
" e",
" f",
"@@ -130,6 +150,8 @@",
" a",
" b",
" c",
"+1",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 54,
},
{
path: "/checkout/path/test.txt",
startLine: 153,
endLine: 154,
},
]);
});
test("getDiffRanges: no diff context lines", async (t) => {
const diffRanges = runGetDiffRanges(2, ["@@ -30 +50,2 @@", "+1", "+2"]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 50,
endLine: 51,
},
]);
});
test("getDiffRanges: malformed thunk header", async (t) => {
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
t.deepEqual(diffRanges, undefined);
});
test("resolveQuerySuiteAlias", (t) => {
// default query suite names should resolve to something language-specific ending in `.qls`.
for (const suite of defaultSuites) {
+3 -194
View File
@@ -3,16 +3,10 @@ import * as path from "path";
import { performance } from "perf_hooks";
import * as io from "@actions/io";
import * as del from "del";
import * as yaml from "js-yaml";
import {
getRequiredInput,
getTemporaryDirectory,
PullRequestBranches,
} from "./actions-util";
import { getTemporaryDirectory, PullRequestBranches } from "./actions-util";
import * as analyses from "./analyses";
import { getApiClient } from "./api-client";
import { setupCppAutobuild } from "./autobuild";
import { type CodeQL } from "./codeql";
import * as configUtils from "./config-utils";
@@ -21,13 +15,13 @@ import { addDiagnostic, makeDiagnostic } from "./diagnostics";
import {
DiffThunkRange,
writeDiffRangesJsonFile,
getPullRequestEditedDiffRanges,
} from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { FeatureEnablement, Feature } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
import { OverlayDatabaseMode } from "./overlay-database-utils";
import { getRepositoryNwoFromEnv } from "./repository";
import { DatabaseCreationTimings, EventReport } from "./status-report";
import { endTracingForCluster } from "./tracer-config";
import * as util from "./util";
@@ -313,185 +307,6 @@ export async function setupDiffInformedQueryRun(
);
}
/**
* Return the file line ranges that were added or modified in the pull request.
*
* @param branches The base and head branches of the pull request.
* @param logger
* @returns An array of tuples, where each tuple contains the absolute path of a
* file, the start line and the end line (both 1-based and inclusive) of an
* added or modified range in that file. Returns `undefined` if the action was
* not triggered by a pull request or if there was an error.
*/
async function getPullRequestEditedDiffRanges(
branches: PullRequestBranches,
logger: Logger,
): Promise<DiffThunkRange[] | undefined> {
const fileDiffs = await getFileDiffsWithBasehead(branches, logger);
if (fileDiffs === undefined) {
return undefined;
}
if (fileDiffs.length >= 300) {
// The "compare two commits" API returns a maximum of 300 changed files. If
// we see that many changed files, it is possible that there could be more,
// with the rest being truncated. In this case, we should not attempt to
// compute the diff ranges, as the result would be incomplete.
logger.warning(
`Cannot retrieve the full diff because there are too many ` +
`(${fileDiffs.length}) changed files in the pull request.`,
);
return undefined;
}
const results: DiffThunkRange[] = [];
for (const filediff of fileDiffs) {
const diffRanges = getDiffRanges(filediff, logger);
if (diffRanges === undefined) {
return undefined;
}
results.push(...diffRanges);
}
return results;
}
/**
* This interface is an abbreviated version of the file diff object returned by
* the GitHub API.
*/
interface FileDiff {
filename: string;
changes: number;
// A patch may be absent if the file is binary, if the file diff is too large,
// or if the file is unchanged.
patch?: string | undefined;
}
async function getFileDiffsWithBasehead(
branches: PullRequestBranches,
logger: Logger,
): Promise<FileDiff[] | undefined> {
// Check CODE_SCANNING_REPOSITORY first. If it is empty or not set, fall back
// to GITHUB_REPOSITORY.
const repositoryNwo = getRepositoryNwoFromEnv(
"CODE_SCANNING_REPOSITORY",
"GITHUB_REPOSITORY",
);
const basehead = `${branches.base}...${branches.head}`;
try {
const response = await getApiClient().rest.repos.compareCommitsWithBasehead(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
basehead,
per_page: 1,
},
);
logger.debug(
`Response from compareCommitsWithBasehead(${basehead}):` +
`\n${JSON.stringify(response, null, 2)}`,
);
return response.data.files;
} catch (error: any) {
if (error.status) {
logger.warning(`Error retrieving diff ${basehead}: ${error.message}`);
logger.debug(
`Error running compareCommitsWithBasehead(${basehead}):` +
`\nRequest: ${JSON.stringify(error.request, null, 2)}` +
`\nError Response: ${JSON.stringify(error.response, null, 2)}`,
);
return undefined;
} else {
throw error;
}
}
}
function getDiffRanges(
fileDiff: FileDiff,
logger: Logger,
): DiffThunkRange[] | undefined {
// Diff-informed queries expect the file path to be absolute. CodeQL always
// uses forward slashes as the path separator, so on Windows we need to
// replace any backslashes with forward slashes.
const filename = path
.join(getRequiredInput("checkout_path"), fileDiff.filename)
.replaceAll(path.sep, "/");
if (fileDiff.patch === undefined) {
if (fileDiff.changes === 0) {
// There are situations where a changed file legitimately has no diff.
// For example, the file may be a binary file, or that the file may have
// been renamed with no changes to its contents. In these cases, the
// file would be reported as having 0 changes, and we can return an empty
// array to indicate no diff range in this file.
return [];
}
// If a file is reported to have nonzero changes but no patch, that may be
// due to the file diff being too large. In this case, we should fall back
// to a special diff range that covers the entire file.
return [
{
path: filename,
startLine: 0,
endLine: 0,
},
];
}
// The 1-based file line number of the current line
let currentLine = 0;
// The 1-based file line number that starts the current range of added lines
let additionRangeStartLine: number | undefined = undefined;
const diffRanges: DiffThunkRange[] = [];
const diffLines = fileDiff.patch.split("\n");
// Adding a fake context line at the end ensures that the following loop will
// always terminate the last range of added lines.
diffLines.push(" ");
for (const diffLine of diffLines) {
if (diffLine.startsWith("-")) {
// Ignore deletions completely -- we do not even want to consider them when
// calculating consecutive ranges of added lines.
continue;
}
if (diffLine.startsWith("+")) {
if (additionRangeStartLine === undefined) {
additionRangeStartLine = currentLine;
}
currentLine++;
continue;
}
if (additionRangeStartLine !== undefined) {
// Any line that does not start with a "+" or "-" terminates the current
// range of added lines.
diffRanges.push({
path: filename,
startLine: additionRangeStartLine,
endLine: currentLine - 1,
});
additionRangeStartLine = undefined;
}
if (diffLine.startsWith("@@ ")) {
// A new hunk header line resets the current line number.
const match = diffLine.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
if (match === null) {
logger.warning(
`Cannot parse diff hunk header for ${fileDiff.filename}: ${diffLine}`,
);
return undefined;
}
currentLine = parseInt(match[1], 10);
continue;
}
if (diffLine.startsWith(" ")) {
// An unchanged context line advances the current line number.
currentLine++;
continue;
}
}
return diffRanges;
}
/**
* Create an extension pack in the temporary directory that contains the file
* line ranges that were added or modified in the pull request.
@@ -621,7 +436,6 @@ export function addSarifExtension(
export async function runQueries(
sarifFolder: string,
memoryFlag: string,
addSnippetsFlag: string,
threadsFlag: string,
diffRangePackDir: string | undefined,
automationDetailsId: string | undefined,
@@ -811,7 +625,6 @@ export async function runQueries(
databasePath,
queries,
sarifFile,
addSnippetsFlag,
threadsFlag,
enableDebugLogging ? "-vv" : "-v",
sarifRunPropertyFlag,
@@ -855,7 +668,7 @@ export async function runFinalize(
logger: Logger,
): Promise<DatabaseCreationTimings> {
try {
await del.deleteAsync(outputDir, { force: true });
await fs.promises.rm(outputDir, { force: true, recursive: true });
} catch (error: any) {
if (error?.code !== "ENOENT") {
throw error;
@@ -922,7 +735,3 @@ export async function warnIfGoInstalledAfterInit(
}
}
}
export const exportedForTesting = {
getDiffRanges,
};
+28
View File
@@ -169,4 +169,32 @@ test("wrapApiConfigurationError correctly wraps specific configuration errors",
res,
new util.ConfigurationError("Resource not accessible by integration"),
);
// Enablement errors.
const enablementErrorMessages = [
"Code Security must be enabled for this repository to use code scanning",
"Advanced Security must be enabled for this repository to use code scanning",
"Code Scanning is not enabled for this repository. Please enable code scanning in the repository settings.",
];
const transforms = [
(msg: string) => msg,
(msg: string) => msg.toLowerCase(),
(msg: string) => msg.toLocaleUpperCase(),
];
for (const enablementErrorMessage of enablementErrorMessages) {
for (const transform of transforms) {
const enablementError = new util.HTTPError(
transform(enablementErrorMessage),
403,
);
res = api.wrapApiConfigurationError(enablementError);
t.deepEqual(
res,
new util.ConfigurationError(
api.getFeatureEnablementError(enablementError.message),
),
);
}
}
});
+25 -2
View File
@@ -1,7 +1,6 @@
import * as core from "@actions/core";
import * as githubUtils from "@actions/github/lib/utils";
import * as retry from "@octokit/plugin-retry";
import consoleLogLevel from "console-log-level";
import { getActionVersion, getRequiredInput } from "./actions-util";
import { Logger } from "./logging";
@@ -50,7 +49,12 @@ function createApiClientWithDetails(
githubUtils.getOctokitOptions(auth, {
baseUrl: apiDetails.apiURL,
userAgent: `CodeQL-Action/${getActionVersion()}`,
log: consoleLogLevel({ level: "debug" }),
log: {
debug: core.debug,
info: core.info,
warn: core.warning,
error: core.error,
},
}),
);
}
@@ -279,6 +283,20 @@ export async function getRepositoryProperties(repositoryNwo: RepositoryNwo) {
});
}
function isEnablementError(msg: string) {
return [
/Code Security must be enabled/i,
/Advanced Security must be enabled/i,
/Code Scanning is not enabled/i,
].some((pattern) => pattern.test(msg));
}
// TODO: Move to `error-messages.ts` after refactoring import order to avoid cycle
// since `error-messages.ts` currently depends on this file.
export function getFeatureEnablementError(message: string): string {
return `Please verify that the necessary features are enabled: ${message}`;
}
export function wrapApiConfigurationError(e: unknown) {
const httpError = asHTTPError(e);
if (httpError !== undefined) {
@@ -300,6 +318,11 @@ export function wrapApiConfigurationError(e: unknown) {
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
);
}
if (httpError.status === 403 && isEnablementError(httpError.message)) {
return new ConfigurationError(
getFeatureEnablementError(httpError.message),
);
}
if (httpError.status === 429) {
return new ConfigurationError("API rate limit exceeded");
}
+2 -3
View File
@@ -5,7 +5,6 @@ import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io";
import * as toolcache from "@actions/tool-cache";
import test, { ExecutionContext } from "ava";
import * as del from "del";
import * as yaml from "js-yaml";
import nock from "nock";
import * as sinon from "sinon";
@@ -557,7 +556,7 @@ const injectedConfigMacro = test.macro({
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
t.deepEqual(augmentedConfig, expectedConfig);
await del.deleteAsync(configFile, { force: true });
await fs.promises.rm(configFile, { force: true });
});
},
@@ -1046,7 +1045,7 @@ test("Avoids duplicating --overwrite flag if specified in CODEQL_ACTION_EXTRA_OP
);
t.truthy(configArg, "Should have injected a codescanning config");
const configFile = configArg!.split("=")[1];
await del.deleteAsync(configFile, { force: true });
await fs.promises.rm(configFile, { force: true });
});
export function stubToolRunnerConstructor(
-3
View File
@@ -167,7 +167,6 @@ export interface CodeQL {
databasePath: string,
querySuitePaths: string[] | undefined,
sarifFile: string,
addSnippetsFlag: string,
threadsFlag: string,
verbosityFlag: string | undefined,
sarifRunPropertyFlag: string | undefined,
@@ -817,7 +816,6 @@ export async function getCodeQLForCmd(
databasePath: string,
querySuitePaths: string[] | undefined,
sarifFile: string,
addSnippetsFlag: string,
threadsFlag: string,
verbosityFlag: string,
sarifRunPropertyFlag: string | undefined,
@@ -836,7 +834,6 @@ export async function getCodeQLForCmd(
"--format=sarif-latest",
verbosityFlag,
`--output=${sarifFile}`,
addSnippetsFlag,
"--print-diagnostics-summary",
"--print-metrics-summary",
"--sarif-add-baseline-file-info",
+1 -2
View File
@@ -5,7 +5,6 @@ import * as artifact from "@actions/artifact";
import * as artifactLegacy from "@actions/artifact-legacy";
import * as core from "@actions/core";
import archiver from "archiver";
import * as del from "del";
import { getOptionalInput, getTemporaryDirectory } from "./actions-util";
import { dbIsFinalized } from "./analyze";
@@ -345,7 +344,7 @@ async function createPartialDatabaseBundle(
);
// See `bundleDb` for explanation behind deleting existing db bundle.
if (fs.existsSync(databaseBundlePath)) {
await del.deleteAsync(databaseBundlePath, { force: true });
await fs.promises.rm(databaseBundlePath, { force: true });
}
const output = fs.createWriteStream(databaseBundlePath);
const zip = archiver("zip");
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.23.3",
"cliVersion": "2.23.3",
"priorBundleVersion": "codeql-bundle-v2.23.2",
"priorCliVersion": "2.23.2"
"bundleVersion": "codeql-bundle-v2.23.4",
"cliVersion": "2.23.4",
"priorBundleVersion": "codeql-bundle-v2.23.3",
"priorCliVersion": "2.23.3"
}
+202 -1
View File
@@ -4,7 +4,10 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import type { PullRequestBranches } from "./actions-util";
import * as apiClient from "./api-client";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import {
shouldPerformDiffInformedAnalysis,
exportedForTesting,
} from "./diff-informed-analysis-utils";
import { Feature, Features } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
@@ -183,3 +186,201 @@ test(
},
false,
);
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
sinon
.stub(actionsUtil, "getRequiredInput")
.withArgs("checkout_path")
.returns("/checkout/path");
return exportedForTesting.getDiffRanges(
{
filename: "test.txt",
changes,
patch: patch?.join("\n"),
},
getRunnerLogger(true),
);
}
test("getDiffRanges: file unchanged", async (t) => {
const diffRanges = runGetDiffRanges(0, undefined);
t.deepEqual(diffRanges, []);
});
test("getDiffRanges: file diff too large", async (t) => {
const diffRanges = runGetDiffRanges(1000000, undefined);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 0,
endLine: 0,
},
]);
});
test("getDiffRanges: diff thunk with single addition range", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,6 +50,8 @@",
" a",
" b",
" c",
"+1",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 54,
},
]);
});
test("getDiffRanges: diff thunk with single deletion range", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,8 +50,6 @@",
" a",
" b",
" c",
"-1",
"-2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, []);
});
test("getDiffRanges: diff thunk with single update range", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,7 +50,7 @@",
" a",
" b",
" c",
"-1",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 53,
},
]);
});
test("getDiffRanges: diff thunk with addition ranges", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,7 +50,9 @@",
" a",
" b",
" c",
"+1",
" c",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 53,
},
{
path: "/checkout/path/test.txt",
startLine: 55,
endLine: 55,
},
]);
});
test("getDiffRanges: diff thunk with mixed ranges", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,7 +50,7 @@",
" a",
" b",
" c",
"-1",
" d",
"-2",
"+3",
" e",
" f",
"+4",
"+5",
" g",
" h",
" i",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 54,
endLine: 54,
},
{
path: "/checkout/path/test.txt",
startLine: 57,
endLine: 58,
},
]);
});
test("getDiffRanges: multiple diff thunks", async (t) => {
const diffRanges = runGetDiffRanges(2, [
"@@ -30,6 +50,8 @@",
" a",
" b",
" c",
"+1",
"+2",
" d",
" e",
" f",
"@@ -130,6 +150,8 @@",
" a",
" b",
" c",
"+1",
"+2",
" d",
" e",
" f",
]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 53,
endLine: 54,
},
{
path: "/checkout/path/test.txt",
startLine: 153,
endLine: 154,
},
]);
});
test("getDiffRanges: no diff context lines", async (t) => {
const diffRanges = runGetDiffRanges(2, ["@@ -30 +50,2 @@", "+1", "+2"]);
t.deepEqual(diffRanges, [
{
path: "/checkout/path/test.txt",
startLine: 50,
endLine: 51,
},
]);
});
test("getDiffRanges: malformed thunk header", async (t) => {
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
t.deepEqual(diffRanges, undefined);
});
+185 -1
View File
@@ -3,12 +3,25 @@ import * as path from "path";
import * as actionsUtil from "./actions-util";
import type { PullRequestBranches } from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { getApiClient, getGitHubVersion } from "./api-client";
import type { CodeQL } from "./codeql";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Logger } from "./logging";
import { getRepositoryNwoFromEnv } from "./repository";
import { GitHubVariant, satisfiesGHESVersion } from "./util";
/**
* This interface is an abbreviated version of the file diff object returned by
* the GitHub API.
*/
interface FileDiff {
filename: string;
changes: number;
// A patch may be absent if the file is binary, if the file diff is too large,
// or if the file is unchanged.
patch?: string | undefined;
}
/**
* Check if the action should perform diff-informed analysis.
*/
@@ -93,3 +106,174 @@ export function readDiffRangesJsonFile(
);
return JSON.parse(jsonContents) as DiffThunkRange[];
}
/**
* Return the file line ranges that were added or modified in the pull request.
*
* @param branches The base and head branches of the pull request.
* @param logger
* @returns An array of tuples, where each tuple contains the absolute path of a
* file, the start line and the end line (both 1-based and inclusive) of an
* added or modified range in that file. Returns `undefined` if the action was
* not triggered by a pull request or if there was an error.
*/
export async function getPullRequestEditedDiffRanges(
branches: PullRequestBranches,
logger: Logger,
): Promise<DiffThunkRange[] | undefined> {
const fileDiffs = await getFileDiffsWithBasehead(branches, logger);
if (fileDiffs === undefined) {
return undefined;
}
if (fileDiffs.length >= 300) {
// The "compare two commits" API returns a maximum of 300 changed files. If
// we see that many changed files, it is possible that there could be more,
// with the rest being truncated. In this case, we should not attempt to
// compute the diff ranges, as the result would be incomplete.
logger.warning(
`Cannot retrieve the full diff because there are too many ` +
`(${fileDiffs.length}) changed files in the pull request.`,
);
return undefined;
}
const results: DiffThunkRange[] = [];
for (const filediff of fileDiffs) {
const diffRanges = getDiffRanges(filediff, logger);
if (diffRanges === undefined) {
return undefined;
}
results.push(...diffRanges);
}
return results;
}
async function getFileDiffsWithBasehead(
branches: PullRequestBranches,
logger: Logger,
): Promise<FileDiff[] | undefined> {
// Check CODE_SCANNING_REPOSITORY first. If it is empty or not set, fall back
// to GITHUB_REPOSITORY.
const repositoryNwo = getRepositoryNwoFromEnv(
"CODE_SCANNING_REPOSITORY",
"GITHUB_REPOSITORY",
);
const basehead = `${branches.base}...${branches.head}`;
try {
const response = await getApiClient().rest.repos.compareCommitsWithBasehead(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
basehead,
per_page: 1,
},
);
logger.debug(
`Response from compareCommitsWithBasehead(${basehead}):` +
`\n${JSON.stringify(response, null, 2)}`,
);
return response.data.files;
} catch (error: any) {
if (error.status) {
logger.warning(`Error retrieving diff ${basehead}: ${error.message}`);
logger.debug(
`Error running compareCommitsWithBasehead(${basehead}):` +
`\nRequest: ${JSON.stringify(error.request, null, 2)}` +
`\nError Response: ${JSON.stringify(error.response, null, 2)}`,
);
return undefined;
} else {
throw error;
}
}
}
function getDiffRanges(
fileDiff: FileDiff,
logger: Logger,
): DiffThunkRange[] | undefined {
// Diff-informed queries expect the file path to be absolute. CodeQL always
// uses forward slashes as the path separator, so on Windows we need to
// replace any backslashes with forward slashes.
const filename = path
.join(actionsUtil.getRequiredInput("checkout_path"), fileDiff.filename)
.replaceAll(path.sep, "/");
if (fileDiff.patch === undefined) {
if (fileDiff.changes === 0) {
// There are situations where a changed file legitimately has no diff.
// For example, the file may be a binary file, or that the file may have
// been renamed with no changes to its contents. In these cases, the
// file would be reported as having 0 changes, and we can return an empty
// array to indicate no diff range in this file.
return [];
}
// If a file is reported to have nonzero changes but no patch, that may be
// due to the file diff being too large. In this case, we should fall back
// to a special diff range that covers the entire file.
return [
{
path: filename,
startLine: 0,
endLine: 0,
},
];
}
// The 1-based file line number of the current line
let currentLine = 0;
// The 1-based file line number that starts the current range of added lines
let additionRangeStartLine: number | undefined = undefined;
const diffRanges: DiffThunkRange[] = [];
const diffLines = fileDiff.patch.split("\n");
// Adding a fake context line at the end ensures that the following loop will
// always terminate the last range of added lines.
diffLines.push(" ");
for (const diffLine of diffLines) {
if (diffLine.startsWith("-")) {
// Ignore deletions completely -- we do not even want to consider them when
// calculating consecutive ranges of added lines.
continue;
}
if (diffLine.startsWith("+")) {
if (additionRangeStartLine === undefined) {
additionRangeStartLine = currentLine;
}
currentLine++;
continue;
}
if (additionRangeStartLine !== undefined) {
// Any line that does not start with a "+" or "-" terminates the current
// range of added lines.
diffRanges.push({
path: filename,
startLine: additionRangeStartLine,
endLine: currentLine - 1,
});
additionRangeStartLine = undefined;
}
if (diffLine.startsWith("@@ ")) {
// A new hunk header line resets the current line number.
const match = diffLine.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
if (match === null) {
logger.warning(
`Cannot parse diff hunk header for ${fileDiff.filename}: ${diffLine}`,
);
return undefined;
}
currentLine = parseInt(match[1], 10);
continue;
}
if (diffLine.startsWith(" ")) {
// An unchanged context line advances the current line number.
currentLine++;
continue;
}
}
return diffRanges;
}
export const exportedForTesting = {
getDiffRanges,
};
+6
View File
@@ -137,4 +137,10 @@ export enum EnvVar {
* This setting is more specific than `CODEQL_ACTION_TEST_MODE`, which implies this option.
*/
SKIP_SARIF_UPLOAD = "CODEQL_ACTION_SKIP_SARIF_UPLOAD",
/**
* Whether to skip workflow validation. Intended for internal use, where we know that
* the workflow is valid and validation is not necessary.
*/
SKIP_WORKFLOW_VALIDATION = "CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION",
}
+4 -11
View File
@@ -86,7 +86,7 @@ import {
getErrorMessage,
BuildMode,
} from "./util";
import { validateWorkflow } from "./workflow";
import { checkWorkflow } from "./workflow";
/**
* Sends a status report indicating that the `init` Action is starting.
@@ -288,16 +288,9 @@ async function run() {
toolsSource = initCodeQLResult.toolsSource;
zstdAvailability = initCodeQLResult.zstdAvailability;
core.startGroup("Validating workflow");
const validateWorkflowResult = await validateWorkflow(codeql, logger);
if (validateWorkflowResult === undefined) {
logger.info("Detected no issues with the code scanning workflow.");
} else {
logger.warning(
`Unable to validate code scanning workflow: ${validateWorkflowResult}`,
);
}
core.endGroup();
// Check the workflow for problems. If there are any problems, they are reported
// to the workflow log. No exceptions are thrown.
await checkWorkflow(logger, codeql);
// Set CODEQL_ENABLE_EXPERIMENTAL_FEATURES for Rust if between 2.19.3 (included) and 2.22.1 (excluded)
// We need to set this environment variable before initializing the config, otherwise Rust
+9 -1
View File
@@ -13,7 +13,15 @@ export interface Logger {
}
export function getActionsLogger(): Logger {
return core;
return {
debug: core.debug,
info: core.info,
warning: core.warning,
error: core.error,
isDebug: core.isDebug,
startGroup: core.startGroup,
endGroup: core.endGroup,
};
}
export function getRunnerLogger(debugMode: boolean): Logger {
+47
View File
@@ -11,6 +11,8 @@ import * as gitUtils from "./git-utils";
import { getRunnerLogger } from "./logging";
import {
downloadOverlayBaseDatabaseFromCache,
getCacheRestoreKeyPrefix,
getCacheSaveKey,
OverlayDatabaseMode,
writeBaseDatabaseOidsFile,
writeOverlayChangesFile,
@@ -261,3 +263,48 @@ test(
},
false,
);
test("overlay-base database cache keys remain stable", async (t) => {
const logger = getRunnerLogger(true);
const config = createTestConfig({ languages: ["python", "javascript"] });
const codeQlVersion = "2.23.0";
const commitOid = "abc123def456";
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(gitUtils, "getCommitOid").resolves(commitOid);
sinon.stub(actionsUtil, "getWorkflowRunID").returns(12345);
sinon.stub(actionsUtil, "getWorkflowRunAttempt").returns(1);
const saveKey = await getCacheSaveKey(
config,
codeQlVersion,
"checkout-path",
logger,
);
const expectedSaveKey =
"codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.23.0-abc123def456-12345-1";
t.is(
saveKey,
expectedSaveKey,
"Cache save key changed unexpectedly. " +
"This may indicate breaking changes in the cache key generation logic.",
);
const restoreKeyPrefix = await getCacheRestoreKeyPrefix(
config,
codeQlVersion,
);
const expectedRestoreKeyPrefix =
"codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.23.0-";
t.is(
restoreKeyPrefix,
expectedRestoreKeyPrefix,
"Cache restore key prefix changed unexpectedly. " +
"This may indicate breaking changes in the cache key generation logic.",
);
t.true(
saveKey.startsWith(restoreKeyPrefix),
`Expected save key "${saveKey}" to start with restore key prefix "${restoreKeyPrefix}"`,
);
});
+22 -4
View File
@@ -4,13 +4,19 @@ import * as path from "path";
import * as actionsCache from "@actions/cache";
import { getRequiredInput, getTemporaryDirectory } from "./actions-util";
import {
getRequiredInput,
getTemporaryDirectory,
getWorkflowRunAttempt,
getWorkflowRunID,
} from "./actions-util";
import { getAutomationID } from "./api-client";
import { type CodeQL } from "./codeql";
import { type Config } from "./config-utils";
import { getCommitOid, getFileOidsUnderPath } from "./git-utils";
import { Logger, withGroupAsync } from "./logging";
import {
getErrorMessage,
isInTestMode,
tryGetFolderBytes,
waitForResultWithTimeLimit,
@@ -266,6 +272,7 @@ export async function uploadOverlayBaseDatabaseToCache(
config,
codeQlVersion,
checkoutPath,
logger,
);
logger.info(
`Uploading overlay-base database to Actions cache with key ${cacheSaveKey}`,
@@ -443,17 +450,28 @@ export async function downloadOverlayBaseDatabaseFromCache(
* The key consists of the restore key prefix (which does not include the
* commit SHA) and the commit SHA of the current checkout.
*/
async function getCacheSaveKey(
export async function getCacheSaveKey(
config: Config,
codeQlVersion: string,
checkoutPath: string,
logger: Logger,
): Promise<string> {
let runId = 1;
let attemptId = 1;
try {
runId = getWorkflowRunID();
attemptId = getWorkflowRunAttempt();
} catch (e) {
logger.warning(
`Failed to get workflow run ID or attempt ID. Reason: ${getErrorMessage(e)}`,
);
}
const sha = await getCommitOid(checkoutPath);
const restoreKeyPrefix = await getCacheRestoreKeyPrefix(
config,
codeQlVersion,
);
return `${restoreKeyPrefix}${sha}`;
return `${restoreKeyPrefix}${sha}-${runId}-${attemptId}`;
}
/**
@@ -470,7 +488,7 @@ async function getCacheSaveKey(
* not include the commit SHA. This allows us to restore the most recent
* compatible overlay-base database.
*/
async function getCacheRestoreKeyPrefix(
export async function getCacheRestoreKeyPrefix(
config: Config,
codeQlVersion: string,
): Promise<string> {
+2 -2
View File
@@ -9,7 +9,7 @@ import * as semver from "semver";
import { CommandInvocationError } from "./actions-util";
import { Logger } from "./logging";
import { assertNever, cleanUpGlob, isBinaryAccessible } from "./util";
import { assertNever, cleanUpPath, isBinaryAccessible } from "./util";
const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
const MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
@@ -217,7 +217,7 @@ export async function extractTarZst(
});
});
} catch (e) {
await cleanUpGlob(dest, "extraction destination directory", logger);
await cleanUpPath(dest, "extraction destination directory", logger);
throw e;
}
}
+3 -3
View File
@@ -12,7 +12,7 @@ import * as semver from "semver";
import { formatDuration, Logger } from "./logging";
import * as tar from "./tar";
import { cleanUpGlob, getErrorMessage, getRequiredEnvParam } from "./util";
import { cleanUpPath, getErrorMessage, getRequiredEnvParam } from "./util";
/**
* High watermark to use when streaming the download and extraction of the CodeQL tools.
@@ -130,7 +130,7 @@ export async function downloadAndExtract(
// If we failed during processing, we want to clean up the destination directory
// before we try again.
await cleanUpGlob(dest, "CodeQL bundle", logger);
await cleanUpPath(dest, "CodeQL bundle", logger);
}
const toolsDownloadStart = performance.now();
@@ -167,7 +167,7 @@ export async function downloadAndExtract(
)}).`,
);
} finally {
await cleanUpGlob(archivedBundlePath, "CodeQL bundle archive", logger);
await cleanUpPath(archivedBundlePath, "CodeQL bundle archive", logger);
}
return {
+9 -10
View File
@@ -101,16 +101,6 @@ test("getMemoryFlag() throws if the ram input is < 0 or NaN", async (t) => {
}
});
test("getAddSnippetsFlag() should return the correct flag", (t) => {
t.deepEqual(util.getAddSnippetsFlag(true), "--sarif-add-snippets");
t.deepEqual(util.getAddSnippetsFlag("true"), "--sarif-add-snippets");
t.deepEqual(util.getAddSnippetsFlag(false), "--no-sarif-add-snippets");
t.deepEqual(util.getAddSnippetsFlag(undefined), "--no-sarif-add-snippets");
t.deepEqual(util.getAddSnippetsFlag("false"), "--no-sarif-add-snippets");
t.deepEqual(util.getAddSnippetsFlag("foo bar"), "--no-sarif-add-snippets");
});
test("getThreadsFlag() should return the correct --threads flag", (t) => {
const numCpus = os.cpus().length;
@@ -534,3 +524,12 @@ test("getCgroupCpuCountFromCpus returns undefined if the CPU file exists but is
);
});
});
test("checkDiskUsage succeeds and produces positive numbers", async (t) => {
process.env["GITHUB_WORKSPACE"] = os.tmpdir();
const diskUsage = await util.checkDiskUsage(getRunnerLogger(true));
if (t.truthy(diskUsage)) {
t.true(diskUsage.numAvailableBytes > 0);
t.true(diskUsage.numTotalBytes > 0);
}
});
+17 -46
View File
@@ -1,12 +1,11 @@
import * as fs from "fs";
import * as fsPromises from "fs/promises";
import * as os from "os";
import * as path from "path";
import * as core from "@actions/core";
import * as exec from "@actions/exec/lib/exec";
import * as io from "@actions/io";
import checkDiskSpace from "check-disk-space";
import * as del from "del";
import getFolderSize from "get-folder-size";
import * as yaml from "js-yaml";
import * as semver from "semver";
@@ -167,7 +166,7 @@ export async function withTmpDir<T>(
): Promise<T> {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeql-action-"));
const result = await body(tmpDir);
await del.deleteAsync(tmpDir, { force: true });
await fs.promises.rm(tmpDir, { force: true, recursive: true });
return result;
}
@@ -343,21 +342,6 @@ export function getMemoryFlag(
return `--ram=${megabytes}`;
}
/**
* Get the codeql flag to specify whether to add code snippets to the sarif file.
*
* @returns string
*/
export function getAddSnippetsFlag(
userInput: string | boolean | undefined,
): string {
if (typeof userInput === "string") {
// have to process specifically because any non-empty string is truthy
userInput = userInput.toLowerCase() === "true";
}
return userInput ? "--sarif-add-snippets" : "--no-sarif-add-snippets";
}
/**
* Get the value of the codeql `--threads` flag specified for the `threads`
* input. If no value was specified, all available threads will be used.
@@ -756,7 +740,7 @@ export async function bundleDb(
// from somewhere else or someone trying to make the action upload a
// non-database file.
if (fs.existsSync(databaseBundlePath)) {
await del.deleteAsync(databaseBundlePath, { force: true });
await fs.promises.rm(databaseBundlePath, { force: true });
}
await codeql.databaseBundle(databasePath, databaseBundlePath, dbName);
return databaseBundlePath;
@@ -1099,24 +1083,17 @@ export async function checkDiskUsage(
logger: Logger,
): Promise<DiskUsage | undefined> {
try {
// We avoid running the `df` binary under the hood for macOS ARM runners with SIP disabled.
if (
process.platform === "darwin" &&
(process.arch === "arm" || process.arch === "arm64") &&
!(await checkSipEnablement(logger))
) {
return undefined;
}
const diskUsage = await checkDiskSpace(
const diskUsage = await fsPromises.statfs(
getRequiredEnvParam("GITHUB_WORKSPACE"),
);
const mbInBytes = 1024 * 1024;
const gbInBytes = 1024 * 1024 * 1024;
if (diskUsage.free < 2 * gbInBytes) {
const blockSizeInBytes = diskUsage.bsize;
const numBlocksPerMb = (1024 * 1024) / blockSizeInBytes;
const numBlocksPerGb = (1024 * 1024 * 1024) / blockSizeInBytes;
if (diskUsage.bavail < 2 * numBlocksPerGb) {
const message =
"The Actions runner is running low on disk space " +
`(${(diskUsage.free / mbInBytes).toPrecision(4)} MB available).`;
`(${(diskUsage.bavail / numBlocksPerMb).toPrecision(4)} MB available).`;
if (process.env[EnvVar.HAS_WARNED_ABOUT_DISK_SPACE] !== "true") {
logger.warning(message);
} else {
@@ -1125,8 +1102,8 @@ export async function checkDiskUsage(
core.exportVariable(EnvVar.HAS_WARNED_ABOUT_DISK_SPACE, "true");
}
return {
numAvailableBytes: diskUsage.free,
numTotalBytes: diskUsage.size,
numAvailableBytes: diskUsage.bavail * blockSizeInBytes,
numTotalBytes: diskUsage.blocks * blockSizeInBytes,
};
} catch (error) {
logger.warning(
@@ -1263,19 +1240,13 @@ export async function checkSipEnablement(
}
}
export async function cleanUpGlob(glob: string, name: string, logger: Logger) {
export async function cleanUpPath(file: string, name: string, logger: Logger) {
logger.debug(`Cleaning up ${name}.`);
try {
const deletedPaths = await del.deleteAsync(glob, { force: true });
if (deletedPaths.length === 0) {
logger.warning(
`Failed to clean up ${name}: no files found matching ${glob}.`,
);
} else if (deletedPaths.length === 1) {
logger.debug(`Cleaned up ${name}.`);
} else {
logger.debug(`Cleaned up ${name} (${deletedPaths.length} files).`);
}
await fs.promises.rm(file, {
force: true,
recursive: true,
});
} catch (e) {
logger.warning(`Failed to clean up ${name}: ${e}.`);
}
+86 -2
View File
@@ -2,9 +2,17 @@ import test, { ExecutionContext } from "ava";
import * as yaml from "js-yaml";
import * as sinon from "sinon";
import { getCodeQLForTesting } from "./codeql";
import { setupTests } from "./testing-utils";
import * as actionsUtil from "./actions-util";
import { createStubCodeQL, getCodeQLForTesting } from "./codeql";
import { EnvVar } from "./environment";
import {
checkExpectedLogMessages,
getRecordingLogger,
LoggedMessage,
setupTests,
} from "./testing-utils";
import {
checkWorkflow,
CodedError,
formatWorkflowCause,
formatWorkflowErrors,
@@ -13,6 +21,7 @@ import {
Workflow,
WorkflowErrors,
} from "./workflow";
import * as workflow from "./workflow";
function errorCodes(
actual: CodedError[],
@@ -870,3 +879,78 @@ test("getCategoryInputOrThrow throws error for workflow with multiple calls to a
},
);
});
test("checkWorkflow - validates workflow if `SKIP_WORKFLOW_VALIDATION` is not set", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
validateWorkflow.resolves(undefined);
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.calledOnce,
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
);
checkExpectedLogMessages(t, messages, [
"Detected no issues with the code scanning workflow.",
]);
});
test("checkWorkflow - logs problems with workflow validation", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
validateWorkflow.resolves("problem");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.calledOnce,
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
);
checkExpectedLogMessages(t, messages, [
"Unable to validate code scanning workflow: problem",
]);
});
test("checkWorkflow - skips validation if `SKIP_WORKFLOW_VALIDATION` is `true`", async (t) => {
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] = "true";
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.notCalled,
"`checkWorkflow` called `validateWorkflow` unexpectedly",
);
t.is(messages.length, 0);
});
test("checkWorkflow - skips validation for `dynamic` workflows", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
const isDynamicWorkflow = sinon
.stub(actionsUtil, "isDynamicWorkflow")
.returns(true);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(isDynamicWorkflow.calledOnce);
t.assert(
validateWorkflow.notCalled,
"`checkWorkflow` called `validateWorkflow` unexpectedly",
);
t.is(messages.length, 0);
});
+36 -1
View File
@@ -5,8 +5,10 @@ import zlib from "zlib";
import * as core from "@actions/core";
import * as yaml from "js-yaml";
import { isDynamicWorkflow } from "./actions-util";
import * as api from "./api-client";
import { CodeQL } from "./codeql";
import { EnvVar } from "./environment";
import { Logger } from "./logging";
import {
getRequiredEnvParam,
@@ -216,7 +218,7 @@ function hasWorkflowTrigger(triggerName: string, doc: Workflow): boolean {
return Object.prototype.hasOwnProperty.call(doc.on, triggerName);
}
export async function validateWorkflow(
async function validateWorkflow(
codeql: CodeQL,
logger: Logger,
): Promise<undefined | string> {
@@ -462,3 +464,36 @@ export function getCheckoutPathInputOrThrow(
) || getRequiredEnvParam("GITHUB_WORKSPACE") // if unspecified, checkout_path defaults to ${{ github.workspace }}
);
}
/**
* A wrapper around `validateWorkflow` which reports the outcome.
*
* @param logger The logger to use.
* @param codeql The CodeQL instance.
*/
export async function checkWorkflow(logger: Logger, codeql: CodeQL) {
// Check the workflow for problems, unless `SKIP_WORKFLOW_VALIDATION` is `true`
// or the workflow trigger is `dynamic`.
if (
!isDynamicWorkflow() &&
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] !== "true"
) {
core.startGroup("Validating workflow");
const validateWorkflowResult = await internal.validateWorkflow(
codeql,
logger,
);
if (validateWorkflowResult === undefined) {
logger.info("Detected no issues with the code scanning workflow.");
} else {
logger.debug(
`Unable to validate code scanning workflow: ${validateWorkflowResult}`,
);
}
core.endGroup();
}
}
export const internal = {
validateWorkflow,
};