Compare commits

..

188 Commits

Author SHA1 Message Date
Michael B. Gale 8041026692 Add NODE_ENV as safe environment variable 2026-05-22 15:14:24 +01:00
Michael B. Gale d3b3ffb888 Add basic eslint enforcement 2026-05-22 15:03:52 +01:00
Michael B. Gale dc5f2b964a Add wrapper around core.exportVariable 2026-05-22 15:03:52 +01:00
Michael B. Gale ffebdc8cf8 Move isInTestMode to environment.ts 2026-05-22 13:45:07 +01:00
Óscar San José 0fb8a6672b Merge pull request #3928 from github/mergeback/v4.36.0-to-main-7211b7c8
Mergeback v4.36.0 refs/heads/releases/v4 into main
2026-05-22 11:28:10 +00:00
github-actions[bot] 80795fb0d4 Rebuild 2026-05-22 11:08:00 +00:00
github-actions[bot] 0cd24d8654 Update changelog and version after v4.36.0 2026-05-22 11:07:48 +00:00
Óscar San José 7211b7c807 Merge pull request #3927 from github/update-v4.36.0-ebc2d9e2b
Merge main into releases/v4
2026-05-22 13:06:23 +02:00
github-actions[bot] 7740f2fb21 Update changelog for v4.36.0 2026-05-22 10:49:45 +00:00
Óscar San José ebc2d9e2bc Merge pull request #3926 from github/update-bundle/codeql-bundle-v2.25.5
Update default bundle to 2.25.5
2026-05-22 10:32:55 +00:00
github-actions[bot] d1f74b777c Add changelog note 2026-05-22 10:18:49 +00:00
github-actions[bot] 2dc40cec39 Update default bundle to codeql-bundle-v2.25.5 2026-05-22 10:18:43 +00:00
Henry Mercer 84498526a0 Merge pull request #3910 from github/henrymercer/repo-size-diff-check
Action size: Add a PR check that comments on significant repo size changes
2026-05-21 10:29:33 +00:00
Henry Mercer 72ac23c6d1 Update excluded required check list 2026-05-21 10:16:47 +01:00
Henry Mercer c5297a28a2 Merge pull request #3919 from github/henrymercer/workflow-concurrency
CI: Automatically cancel non-generated workflows
2026-05-20 16:26:50 +00:00
Henry Mercer 8ffeae7d05 CI: Automatically cancel non-generated workflows
Specify concurrency groups for non-generated workflows so we can cancel in-progress runs when new commits are pushed to a PR.
2026-05-20 16:39:16 +01:00
Henry Mercer f3f52bf568 Revert getErrorMessage import
To avoid requiring additional dependencies
2026-05-20 15:55:41 +01:00
Henry Mercer a14f75e3ac Address review comments 2026-05-20 15:39:14 +01:00
Henry Mercer 164c32a61e Merge pull request #3918 from github/henrymercer/upgrade-brace-expansion
Bump `brace-expansion`
2026-05-20 14:31:25 +00:00
Henry Mercer a134948b87 Bump brace-expansion
Address https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-jxxr-4gwj-5jf2
2026-05-20 15:17:16 +01:00
Henry Mercer f4d0a7abf7 Merge pull request #3912 from github/henrymercer/smaller-upload-lib
Action size: Reduce duplication between `upload-lib` and `entry-points`
2026-05-19 18:55:28 +00:00
Henry Mercer f62fbc9627 Merge pull request #3895 from github/mbg/analysis-kinds/warn-on-non-cs-advanced-setup
Log error for non-default `analysis-kinds` input outside of managed workflows
2026-05-19 18:43:18 +00:00
Henry Mercer 2a7340616e Address review comments 2026-05-19 19:41:51 +01:00
Henry Mercer 3b0e64cb09 Merge pull request #3914 from github/henrymercer/auto-rebuild-release-prs
Release process: Automatically rebuild PRs
2026-05-19 18:00:31 +00:00
Michael B. Gale 0a7280a837 Assert that nothing is logged 2026-05-19 14:22:23 +01:00
Michael B. Gale b79a976789 Merge remote-tracking branch 'origin/main' into mbg/analysis-kinds/warn-on-non-cs-advanced-setup 2026-05-19 14:18:19 +01:00
Henry Mercer 2c8faa5e9f Pass comment body file directly 2026-05-18 20:28:53 +01:00
Henry Mercer 15a712bbc2 Address review comments 2026-05-18 20:08:43 +01:00
Henry Mercer eb9a790d15 Apply suggestion from @henrymercer 2026-05-18 19:39:41 +01:00
Henry Mercer b8baf41834 Remove comments about npm cache 2026-05-18 19:31:53 +01:00
Henry Mercer 5e9ae56429 Add specific instruction about "Rebuild" commit 2026-05-18 19:28:49 +01:00
Henry Mercer 8442bc0af9 Release process: Automatically rebuild PRs 2026-05-18 19:06:49 +01:00
Henry Mercer 26a1e570a6 Merge pull request #3913 from github/henrymercer/downgrade-ava
Downgrade ava to version 6.4.1
2026-05-18 18:00:26 +00:00
Henry Mercer 9b6438e936 Tweak workflow 2026-05-18 18:25:26 +01:00
Henry Mercer b5b50d62f1 Merge branch 'main' into henrymercer/repo-size-diff-check 2026-05-18 18:20:16 +01:00
Henry Mercer 9665bc2f5a Downgrade ava to version 6.4.1
Since we run CI on Node 20 as well as Node 24
2026-05-18 18:07:08 +01:00
Henry Mercer 5a80681bb6 Address review comments 2026-05-18 17:53:50 +01:00
Henry Mercer bcffb2b658 Unify checks into a single job 2026-05-18 17:33:45 +01:00
Henry Mercer fcc1e3197f Action size: Reduce duplication between upload-lib and entry-points 2026-05-18 17:18:03 +01:00
Henry Mercer 6f8805e224 Default setup env vars: Restrict results to src 2026-05-18 17:15:30 +01:00
Henry Mercer 4fc0f3e51b Add a PR check that comments on significant repo size changes 2026-05-18 16:36:58 +01:00
Henry Mercer c8a3492b26 Merge pull request #3894 from github/henrymercer/require-codeql-2.19.4
Bump minimum CodeQL CLI version to 2.19.4
2026-05-18 14:55:40 +00:00
Henry Mercer e94195c896 Move changelog note to right place 2026-05-18 14:27:03 +01:00
Henry Mercer 05e8f288eb Merge branch 'main' into henrymercer/require-codeql-2.19.4 2026-05-15 18:13:43 +01:00
Michael B. Gale b71f5aebfc Merge pull request #3898 from github/dependabot/npm_and_yarn/sinon-22.0.0
Bump sinon from 21.1.2 to 22.0.0
2026-05-15 14:58:30 +00:00
Henry Mercer 2365a46087 Merge pull request #3908 from github/henrymercer/token-stdin
Update scripts to read tokens more securely
2026-05-15 14:54:00 +00:00
Henry Mercer cf51dca1af Merge pull request #3893 from github/henrymercer/sha256
Add support for SHA-256 Git object IDs
2026-05-15 14:53:55 +00:00
Michael B. Gale b30a935ea5 Merge branch 'main' into dependabot/npm_and_yarn/sinon-22.0.0 2026-05-15 15:42:13 +01:00
Henry Mercer 5b815f25ca Merge branch 'main' into henrymercer/sha256 2026-05-15 14:58:14 +01:00
Henry Mercer 93c8a9ed99 Update update-release-branch.py to take token from stdin 2026-05-15 14:53:43 +01:00
Henry Mercer 2a02de1a14 Read token from stdin in sync-checks.ts
Also allow specifying the token using an environment variable.
2026-05-15 14:53:43 +01:00
Henry Mercer 67f403822c Merge pull request #3903 from github/henrymercer/macos-larger-runners
PR checks: Run slowest macOS checks on larger runners
2026-05-15 13:32:01 +00:00
Michael B. Gale bbef5ff663 Merge pull request #3904 from github/mbg/esbuild/split-follow-up
Address review comments for #3899
2026-05-15 12:48:02 +00:00
Michael B. Gale 7187b6ecc7 Merge pull request #3906 from github/mergeback/v4.35.5-to-main-9e0d7b8d
Mergeback v4.35.5 refs/heads/releases/v4 into main
2026-05-15 11:50:42 +00:00
github-actions[bot] f1ce9f4421 Rebuild 2026-05-15 11:30:22 +00:00
github-actions[bot] 06c7e6fdd5 Update changelog and version after v4.35.5 2026-05-15 11:24:05 +00:00
Michael B. Gale 9e0d7b8d25 Merge pull request #3905 from github/update-v4.35.5-d4b485515
Merge main into releases/v4
2026-05-15 12:22:46 +01:00
Michael B. Gale 6d7d59927c Add changelog entry for #3899 2026-05-15 11:58:39 +01:00
github-actions[bot] 51f7e38c69 Update changelog for v4.35.5 2026-05-15 10:48:24 +00:00
Henry Mercer b43bb7bd69 Merge branch 'main' into henrymercer/sha256 2026-05-15 11:41:47 +01:00
Michael B. Gale 064674dfa3 Fix typo
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 11:35:47 +01:00
Michael B. Gale ab5047bf8f Add missing semicolons 2026-05-15 11:27:58 +01:00
Michael B. Gale 2320f9d058 "action" to "Action" in build.mjs 2026-05-15 11:26:51 +01:00
Michael B. Gale 46959216a2 Rename analyze-action-env.test.ts to analyze-action.test.ts 2026-05-15 11:25:12 +01:00
Michael B. Gale 9e1f914560 Merge analyze-action-input test into analyze-action-env file
The tests still can't run in parallel so I had to change `test` to `test.serial`, which caused a bunch of formatting changes.
2026-05-15 11:24:28 +01:00
Michael B. Gale db84cb5ccb Remove outdated comments for analyze-action tests 2026-05-15 11:22:17 +01:00
Michael B. Gale d4b485515e Merge pull request #3899 from github/mbg/esbuild/split
Reduce duplication across JS bundles by creating one bundle with smaller entry point wrappers
2026-05-15 10:11:56 +00:00
Henry Mercer 931147e852 Improve OS types and docs 2026-05-15 11:10:02 +01:00
Michael B. Gale 127de8117f Merge remote-tracking branch 'origin/main' into mbg/esbuild/split 2026-05-14 19:14:01 +01:00
Michael B. Gale 7fde13f26a Use src + basename in header to avoid issues on Windows 2026-05-14 19:10:19 +01:00
Michael B. Gale dfa61e7305 Improve pattern matching and error handling 2026-05-14 18:36:41 +01:00
Michael B. Gale 52aafec073 Import and call runWrapper normally in analyze tests 2026-05-14 18:32:40 +01:00
Michael B. Gale 0d08c01f78 Auto-generate shared bundle 2026-05-14 18:27:46 +01:00
Henry Mercer 1b65777c19 Address review comments 2026-05-14 18:13:20 +01:00
Michael B. Gale 14085a675c Auto-generate entry points 2026-05-14 18:13:01 +01:00
Henry Mercer a32db48565 Move checks back to default runners
These jobs are not rate-limiting so we don't need to run them on larger runners.
2026-05-14 17:57:11 +01:00
Henry Mercer aa005faaad PR checks: Run slowest macOS checks on larger runners 2026-05-14 17:29:44 +01:00
Henry Mercer fcdf5dd4cf Add PR checks shortcut to package.json 2026-05-14 17:22:02 +01:00
Henry Mercer e8d3fa290e Merge branch 'main' into henrymercer/sha256 2026-05-14 17:10:41 +01:00
Sam Robson eb17ca4f4d Merge pull request #3791 from github/sam-robson/overlay-fallback
Fall back to non-overlay analysis when diff-informed analysis is unavailable
2026-05-14 15:41:25 +00:00
Sam Robson a41c444cd9 Merge branch 'main' into sam-robson/overlay-fallback 2026-05-14 15:51:24 +01:00
Michael B. Gale d7e50c23fe Fix linter errors 2026-05-14 15:24:33 +01:00
Michael B. Gale bb30f3132d Avoid top-level promise in analyze-action 2026-05-14 15:14:03 +01:00
Henry Mercer 336884853e Merge pull request #3901 from github/henrymercer/minify-test-debug-artifacts
Minify test debug artifacts
2026-05-14 14:09:36 +00:00
Michael B. Gale 2f137c9dc6 Add remaining new entry points 2026-05-14 14:55:33 +01:00
Henry Mercer 4795ef8153 Remove now unnecessary test skipping 2026-05-14 14:47:33 +01:00
Michael B. Gale f0489abddd Update action specs for new entry points 2026-05-14 14:47:23 +01:00
Henry Mercer 2e202367c7 Reduce size of test debug artifacts 2026-05-14 14:47:13 +01:00
Sam Robson 9d7243005b Merge remote-tracking branch 'origin/main' into sam-robson/overlay-fallback
* origin/main: (40 commits)
  Bump the npm-minor group across 1 directory with 3 updates
  Bump actions/create-github-app-token
  Nit: Tweak JSDoc for `getRawLanguagesNoAutodetect`
  Enable only `code-scanning`
  Use overlay-aware version for code scanning exclusively
  Add changelog entry
  Rebuild
  Bump five transitive dependencies
  Throw error if multiple analysis kinds are specified
  Bump fast-xml-builder from 1.1.5 to 1.2.0
  Improve tests
  Improve error message
  Remove dead code
  Remove `makeOverlayMatchFeatures` indirection
  Add JSDoc for `getRawLanguagesNoAutodetect`
  Enable overlay-aware version selection in `setup-codeql`
  Minor: Introduce constant to avoid duplication
  Improve changelog note
  Rebuild
  Update changelog and version after v4.35.4
  ...

# Conflicts:
#	lib/init-action.js
#	src/diff-informed-analysis-utils.test.ts
2026-05-14 13:39:44 +01:00
Michael B. Gale 237b03b3c3 WIP: Reduce bundle duplication 2026-05-14 12:47:34 +01:00
dependabot[bot] d4eab006fa Bump sinon from 21.1.2 to 22.0.0
Bumps [sinon](https://github.com/sinonjs/sinon) from 21.1.2 to 22.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v21.1.2...v22.0.0)

---
updated-dependencies:
- dependency-name: sinon
  dependency-version: 22.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-14 10:34:00 +00:00
Henry Mercer ea37b337cd Merge pull request #3897 from github/dependabot/npm_and_yarn/npm-minor-afb85bbff8
Bump the npm-minor group across 1 directory with 3 updates
2026-05-14 10:09:31 +00:00
Henry Mercer ba0a2f91b7 Merge pull request #3896 from github/dependabot/github_actions/dot-github/workflows/actions-minor-9f1c31c749
Bump actions/create-github-app-token from 3.1.1 to 3.2.0 in /.github/workflows in the actions-minor group across 1 directory
2026-05-14 10:06:09 +00:00
dependabot[bot] 4041a11865 Bump the npm-minor group across 1 directory with 3 updates
Bumps the npm-minor group with 3 updates in the / directory: [globals](https://github.com/sindresorhus/globals), [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) and [yaml](https://github.com/eemeli/yaml).


Updates `globals` from 17.5.0 to 17.6.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.5.0...v17.6.0)

Updates `typescript-eslint` from 8.59.1 to 8.59.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.2/packages/typescript-eslint)

Updates `yaml` from 2.8.3 to 2.8.4
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.8.3...v2.8.4)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: typescript-eslint
  dependency-version: 8.59.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: yaml
  dependency-version: 2.8.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-13 18:29:17 +00:00
dependabot[bot] 2a6fe1608c Bump actions/create-github-app-token
Bumps the actions-minor group with 1 update in the /.github/workflows directory: [actions/create-github-app-token](https://github.com/actions/create-github-app-token).


Updates `actions/create-github-app-token` from 3.1.1 to 3.2.0
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/create-github-app-token/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 3.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-05-13 18:28:51 +00:00
Henry Mercer 3c8c0ae6cb Remove unnecessary sinon restore calls 2026-05-13 18:53:33 +01:00
Henry Mercer 93d215d874 Merge branch 'main' into henrymercer/sha256 2026-05-13 18:44:38 +01:00
Michael B. Gale 4235601f6f Log error for non-default analysis-kinds input outside of managed workflows 2026-05-13 17:43:16 +01:00
Mads Navntoft 3d6ea97f26 Merge pull request #3891 from github/navntoft/dep/remove-brace-expansion-override
Bump brace-expansion and 4 dev dependencies
2026-05-13 15:46:57 +00:00
Michael B. Gale 7d25a3e590 Merge pull request #3892 from github/mbg/analysis-kinds/warn-on-multiple
Log error and only enable `code-scanning` if multiple analysis kinds are specified
2026-05-13 15:44:21 +00:00
Michael B. Gale 4dc72761a6 Merge remote-tracking branch 'origin/main' into mbg/analysis-kinds/warn-on-multiple 2026-05-13 16:20:45 +01:00
Henry Mercer 9c3aedb4cd Update PR check testing matrix 2026-05-13 14:25:03 +01:00
Henry Mercer a66f7bbb5a Merge branch 'main' into henrymercer/sha256 2026-05-13 10:51:44 +01:00
Henry Mercer b986640672 Add note about CODEQL_VERSION_ZSTD_BUNDLE 2026-05-12 19:26:23 +01:00
Henry Mercer a333d64ec4 Remove DatabaseInterpretResultsSupportsSarifRunProperty tools feature
This feature has been supported since CodeQL CLI v2.19.0
2026-05-12 19:26:21 +01:00
Henry Mercer 97fb30df6b Remove ForceOverwrite tools feature
This feature has been supported since CodeQL CLI v2.18.0, which is below the new minimum version.
2026-05-12 19:26:20 +01:00
Henry Mercer d122da3c9f Bump minimum CodeQL CLI version to 2.19.4 2026-05-12 19:26:20 +01:00
Henry Mercer de3e561d12 Improve regex clarity 2026-05-12 19:06:04 +01:00
Henry Mercer c559992c9e Merge pull request #3880 from github/henrymercer/overlay-match-codeql-version
Overlay: Use overlay-aware CLI version when analyzing PRs
2026-05-12 17:36:31 +00:00
Henry Mercer 6a4e35fad9 Add support for SHA-256 Git object IDs 2026-05-12 18:24:21 +01:00
Henry Mercer 8d217609b0 Nit: Tweak JSDoc for getRawLanguagesNoAutodetect 2026-05-12 16:21:44 +01:00
Michael B. Gale 257b3d3fc8 Enable only code-scanning 2026-05-12 15:46:28 +01:00
Henry Mercer 201a96b541 Use overlay-aware version for code scanning exclusively 2026-05-12 15:25:40 +01:00
Michael B. Gale 312a2fee96 Add changelog entry 2026-05-12 15:03:58 +01:00
Mads Navntoft 2ca0fbdca8 Rebuild 2026-05-12 15:59:34 +02:00
Mads Navntoft 12c1d88854 Bump five transitive dependencies
Bumps the following to their latest patched versions:

brace-expansion (under readdir-glob): 2.0.2 → 2.1.0
picomatch (under micromatch): 2.3.1 → 2.3.2
picomatch (top level): 4.0.3 → 4.0.4
flatted: 3.3.3 → 3.4.2
js-yaml (under supertap): 3.14.1 → 3.14.2

The brace-expansion bump requires removing the brace-expansion override
in package.json, which had been pinning resolution below the existing
^2.0.1 constraint declared by readdir-glob.
2026-05-12 15:59:34 +02:00
Michael B. Gale 70419e3273 Throw error if multiple analysis kinds are specified 2026-05-12 14:54:11 +01:00
Michael B. Gale b62aaa99a5 Merge pull request #3889 from github/dependabot/npm_and_yarn/fast-xml-builder-1.2.0
Bump fast-xml-builder from 1.1.5 to 1.2.0
2026-05-11 14:59:28 +00:00
dependabot[bot] 2f2dbd2e78 Bump fast-xml-builder from 1.1.5 to 1.2.0
Bumps [fast-xml-builder](https://github.com/NaturalIntelligence/fast-xml-builder) from 1.1.5 to 1.2.0.
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-builder/blob/main/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-builder/compare/v1.1.5...v1.2.0)

---
updated-dependencies:
- dependency-name: fast-xml-builder
  dependency-version: 1.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-08 19:05:11 +00:00
Henry Mercer b4ea7aa65a Improve tests 2026-05-08 19:16:48 +01:00
Henry Mercer 87ac48dae6 Improve error message 2026-05-08 19:16:47 +01:00
Henry Mercer 42d7f62579 Remove dead code 2026-05-08 19:16:46 +01:00
Henry Mercer 540699dcca Remove makeOverlayMatchFeatures indirection 2026-05-08 19:14:05 +01:00
Henry Mercer 9a85234875 Add JSDoc for getRawLanguagesNoAutodetect 2026-05-08 19:14:05 +01:00
Henry Mercer 2a950b930c Enable overlay-aware version selection in setup-codeql 2026-05-08 19:14:05 +01:00
Henry Mercer 4f815a68d3 Minor: Introduce constant to avoid duplication 2026-05-08 19:14:04 +01:00
Henry Mercer 0aedbb71d8 Merge branch 'main' into henrymercer/overlay-match-codeql-version 2026-05-08 19:10:45 +01:00
Henry Mercer 868e2ea564 Merge pull request #3886 from github/mergeback/v4.35.4-to-main-68bde559
Mergeback v4.35.4 refs/heads/releases/v4 into main
2026-05-08 14:25:20 +00:00
Henry Mercer 792c223bc1 Merge pull request #3875 from github/dependabot/npm_and_yarn/npm-minor-c8e071f5f8
Bump the npm-minor group across 1 directory with 4 updates
2026-05-08 14:25:05 +00:00
Henry Mercer efc9b0a9e3 Improve changelog note
Co-authored-by: Michael B. Gale <mbg@github.com>
2026-05-07 18:44:08 +01:00
github-actions[bot] 272ada693f Rebuild 2026-05-07 15:58:38 +00:00
github-actions[bot] 610a6682b6 Merge remote-tracking branch 'origin/main' into mergeback/v4.35.4-to-main-68bde559 2026-05-07 15:57:56 +00:00
github-actions[bot] 1627096569 Update changelog and version after v4.35.4 2026-05-07 15:54:04 +00:00
Paolo Tranquilli 68bde559de Merge pull request #3885 from github/update-v4.35.4-803d9e8c3
Merge main into releases/v4
2026-05-07 17:52:37 +02:00
github-actions[bot] 9739ad2d18 Update changelog for v4.35.4 2026-05-07 15:21:52 +00:00
Henry Mercer b81d0d250f Merge pull request #3874 from github/henrymercer/slow-tests-ci-only
Tests: Run slow `scanArtifactsForTokens` test in CI only by default
2026-05-07 15:04:47 +00:00
Michael B. Gale a16cb53dd8 Merge pull request #3884 from github/mbg/dev/no-build-metadata
Do not run `bundle-metadata.ts` as part of `npm run build`
2026-05-07 15:02:21 +00:00
Michael B. Gale 803d9e8c3c Merge pull request #3883 from github/mbg/test/macro-wrapper
Add more strongly typed wrapper around `test.macro`
2026-05-07 14:46:34 +00:00
Henry Mercer 0c80cee806 Add explicit error on Windows 2026-05-07 15:39:42 +01:00
Michael B. Gale d032ee8c47 Do not run bundle-metadata.ts as part of npm run build 2026-05-07 15:38:28 +01:00
Michael B. Gale 0fd9c7d135 Merge pull request #3882 from github/dependabot/github_actions/dot-github/workflows/actions-minor-4a0b9de8bd
Bump ruby/setup-ruby from 1.305.0 to 1.306.0 in /.github/workflows in the actions-minor group across 1 directory
2026-05-07 14:17:36 +00:00
Michael B. Gale 922d6fb888 Use makeMacro instead of test.macro 2026-05-07 14:59:42 +01:00
Michael B. Gale df77e87896 Update test macro snippet 2026-05-07 14:59:42 +01:00
Michael B. Gale 6e3f985e4f Add wrapper for test.macro 2026-05-07 14:59:42 +01:00
Paolo Tranquilli e7a347dfb1 Merge pull request #3881 from github/update-bundle/codeql-bundle-v2.25.4
Update default bundle to 2.25.4
2026-05-07 13:41:36 +00:00
github-actions[bot] 17eabb2500 Rebuild 2026-05-07 13:23:54 +00:00
dependabot[bot] aaef09c48d 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.305.0 to 1.306.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/0cb964fd540e0a24c900370abf38a33466142735...c4e5b1316158f92e3d49443a9d58b31d25ac0f8f)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-07 13:21:45 +00:00
github-actions[bot] ae1b9155d3 Add changelog note 2026-05-07 12:49:22 +00:00
github-actions[bot] 9f82f88f07 Update default bundle to codeql-bundle-v2.25.4 2026-05-07 12:49:13 +00:00
Henry Mercer 7525c68ea1 Nit: Dedupe languages 2026-05-07 11:01:15 +01:00
Henry Mercer 01bc9be56a Filter to code scanning only 2026-05-07 11:00:54 +01:00
Sam Robson 9d6b456c59 Merge branch 'main' into sam-robson/overlay-fallback 2026-05-06 20:26:20 +01:00
Sam Robson e259d26055 refactor: rename overlay-disabled reason and add changelog entry 2026-05-06 20:23:20 +01:00
Henry Mercer 817b68489e Merge branch 'main' into henrymercer/overlay-match-codeql-version 2026-05-06 19:20:52 +01:00
Henry Mercer 1b5632783c Add changelog note 2026-05-06 19:13:25 +01:00
github-actions[bot] 1848b73afa Rebuild 2026-05-06 18:01:54 +00:00
dependabot[bot] d1e9792bc8 Bump the npm-minor group across 1 directory with 4 updates
Bumps the npm-minor group with 4 updates in the / directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [eslint](https://github.com/eslint/eslint), [typescript](https://github.com/microsoft/TypeScript) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


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

Updates `eslint` from 9.39.2 to 9.39.4
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.2...v9.39.4)

Updates `typescript` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v6.0.2...v6.0.3)

Updates `typescript-eslint` from 8.58.2 to 8.59.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 20.19.39
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: eslint
  dependency-version: 9.39.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: typescript-eslint
  dependency-version: 8.59.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-06 17:59:44 +00:00
Henry Mercer 2c9cd77837 Tests: Run slow scanArtifactsForTokens test in CI only by default 2026-05-06 18:45:24 +01:00
Henry Mercer b967fdfbdc Add dry run mode so we can dark ship 2026-05-06 18:30:24 +01:00
Henry Mercer 55d6319f96 Match CLI version to cached overlay-base database 2026-05-06 18:01:37 +01:00
Henry Mercer b0942116d7 Expose all enabled default CLI versions 2026-05-06 17:45:56 +01:00
Paolo Tranquilli bc0b696b41 Merge pull request #3785 from github/mbg/dep/update-undici
Bump `undici` to at least `6.24.0`
2026-05-06 15:24:07 +00:00
Henry Mercer a796e3e4ed Add OverlayAnalysisMatchCodeqlVersion feature flag 2026-05-06 15:14:04 +01:00
Michael B. Gale f9bb0e001c Merge branch 'main' into mbg/dep/update-undici 2026-05-06 14:16:25 +01:00
Henry Mercer 4b7faf0b3d Merge pull request #3809 from github/henrymercer/determine-overlay-version
Overlay: Determine which versions of CodeQL are compatible with cached base DBs
2026-05-06 12:30:56 +00:00
Henry Mercer 09a1d9ec2a Add note about cache eviction 2026-05-05 18:54:16 +01:00
Henry Mercer f64a4491cf Add links to API docs 2026-05-05 18:48:09 +01:00
Henry Mercer 7fc86e0c37 Update type import syntax 2026-05-05 18:43:10 +01:00
Henry Mercer 5997e25ad9 Update listActionsCaches doc 2026-05-05 18:43:01 +01:00
Henry Mercer 7587714d0a Revert "Mitigate caches being evicted before they can be downloaded"
This reverts commit 1279e8d41c.
2026-05-05 18:37:17 +01:00
Sam Robson 8ab64a211d Merge branch 'main' into sam-robson/overlay-fallback 2026-05-01 16:35:49 +01:00
Michael B. Gale a723e99345 Merge pull request #3868 from github/mergeback/v4.35.3-to-main-e46ed2cb
Mergeback v4.35.3 refs/heads/releases/v4 into main
2026-05-01 14:34:01 +00:00
github-actions[bot] fbba1e03be Rebuild 2026-05-01 14:09:49 +00:00
github-actions[bot] 933238e8d5 Update changelog and version after v4.35.3 2026-05-01 14:06:46 +00:00
Sam Robson f8b93c30a6 Merge branch 'main' into sam-robson/overlay-fallback 2026-05-01 11:28:43 +01:00
Sam Robson 80a72986d3 fix: re-import withGroupAsync in init-action.ts after merge 2026-04-30 16:48:45 +01:00
Sam Robson e9e36aec74 test: drop codescanning-config-cli scenario for overlay without diff-informed 2026-04-30 16:39:05 +01:00
Sam Robson 4ed52dcbfa Merge branch 'main' into sam-robson/overlay-fallback 2026-04-30 16:24:23 +01:00
Sam Robson 3cc8dd3e59 refactor: report missing PR diff ranges via OverlayDisabledReason and disable overlay 2026-04-30 16:12:30 +01:00
Óscar San José 8f02cfa11d Update from main and Rebuild 2026-04-27 19:30:21 +02:00
Sam Robson 5ded561dcd Merge branch 'main' into sam-robson/overlay-fallback 2026-04-24 06:39:07 +01:00
Sam Robson faca00d3ae refactor: address review feedback on overlay fallback 2026-04-23 20:38:10 +01:00
Sam Robson 5d1c58464f refactor: fall back to non-overlay analysis when diff-informed analysis is unavailable 2026-04-23 12:10:22 +01:00
Henry Mercer 1279e8d41c Mitigate caches being evicted before they can be downloaded 2026-04-22 00:04:57 +01:00
Henry Mercer af1f613989 Use type-only imports 2026-04-21 23:49:37 +01:00
Henry Mercer 5026833be5 Document exclusion of nightlies 2026-04-21 23:35:29 +01:00
Henry Mercer 201ddc275d Retrieve CodeQL versions associated with cached overlay base DBs 2026-04-21 22:18:59 +01:00
Michael B. Gale 4ea3a4b4af Bump undici to at least 6.24.0 2026-03-27 17:32:08 +00:00
133 changed files with 74075 additions and 1464237 deletions
@@ -41,7 +41,38 @@ runs:
git add .
git commit -m "Update changelog and version after ${VERSION}"
git push origin "${NEW_BRANCH}"
# Update the build artifacts with the new version number
- name: Rebuild the Action
shell: bash
run: |
set -exu
npm ci
npm run build
- name: Check for rebuild changes
id: rebuild_changes
shell: bash
run: |
set -exu
git add --all
if git diff --cached --quiet; then
echo "has_changes=false" >> "${GITHUB_OUTPUT}"
else
echo "has_changes=true" >> "${GITHUB_OUTPUT}"
fi
- name: Commit rebuild
if: steps.rebuild_changes.outputs.has_changes == 'true'
shell: bash
run: |
set -exu
git commit -m "Rebuild"
- name: Push mergeback branch
shell: bash
env:
NEW_BRANCH: "${{ inputs.branch }}"
run: git push origin "${NEW_BRANCH}"
- name: Create PR
shell: bash
@@ -60,8 +91,6 @@ runs:
Please do the following:
- [ ] Remove and re-add the "Rebuild" label to the PR to trigger just this workflow.
- [ ] Wait for the "Rebuild" workflow to push a commit updating the distribution files.
- [ ] Mark the PR as ready for review to trigger the full set of PR checks.
- [ ] Approve and merge the PR. When merging the PR, make sure "Create a merge commit" is
selected rather than "Squash and merge" or "Rebase and merge".
@@ -74,7 +103,6 @@ runs:
--head "${NEW_BRANCH}" \
--base "${BASE_BRANCH}" \
--title "${pr_title}" \
--label "Rebuild" \
--body "${pr_body}" \
--assignee "${GITHUB_ACTOR}" \
--draft
@@ -18,7 +18,7 @@ runs:
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: 'npm'
- name: Set up Python
+56 -20
View File
@@ -16,12 +16,27 @@ No user facing changes.
"""
# NB: This exact commit message is used to find commits for reverting during backports.
# Changing it requires a transition period where both old and new versions are supported.
# Changing it requires a transition period where both old and new versions are supported.
BACKPORT_COMMIT_MESSAGE = 'Update version and changelog for v'
# Commit message used for rebuild commits, both those produced by this script and those produced
# by the `Rebuild Action` workflow (`.github/workflows/rebuild.yml`).
REBUILD_COMMIT_MESSAGE = 'Rebuild'
# Name of the remote
ORIGIN = 'origin'
# Environment variables to check for a GitHub API token.
TOKEN_ENVIRONMENT_VARIABLES = ('GH_TOKEN', 'GITHUB_TOKEN')
# Gets a GitHub API token from one of the supported environment variables.
def get_github_token():
for variable_name in TOKEN_ENVIRONMENT_VARIABLES:
token = os.environ.get(variable_name, '').strip()
if token:
return token
raise Exception('Missing GitHub token. Set GITHUB_TOKEN or GH_TOKEN.')
# Runs git with the given args and returns the stdout.
# Raises an error if git does not exit successfully (unless passed
# allow_non_zero_exit_code=True).
@@ -32,6 +47,28 @@ def run_git(*args, allow_non_zero_exit_code=False):
raise Exception(f'Call to {" ".join(cmd)} exited with code {p.returncode} stderr: {p.stderr.decode("ascii")}.')
return p.stdout.decode('ascii')
# Runs the given command, streaming output to the console.
# Raises an error if the command does not exit successfully.
def run_command(*args):
cmd = list(args)
print(f'Running `{" ".join(cmd)}`.')
subprocess.run(cmd, check=True)
# Rebuilds the action and commits any changes.
def rebuild_action():
# For backports, the only source-level change vs the source branch is the new version number,
# so we just need to refresh the version embedded in `lib/`.
run_command('npm', 'ci')
run_command('npm', 'run', 'build')
run_git('add', '--all')
# `git diff --cached --quiet` exits 0 if there are no staged changes, 1 if there are.
if subprocess.run(['git', 'diff', '--cached', '--quiet']).returncode == 0:
print('Rebuild produced no changes; skipping Rebuild commit.')
else:
run_git('commit', '-m', REBUILD_COMMIT_MESSAGE)
print('Created Rebuild commit.')
# Returns true if the given branch exists on the origin remote
def branch_exists_on_remote(branch_name):
return run_git('ls-remote', '--heads', ORIGIN, branch_name).strip() != ''
@@ -87,9 +124,11 @@ def open_pr(
body.append('Please do the following:')
if len(conflicted_files) > 0:
body.append(' - [ ] Ensure `package.json` file contains the correct version.')
body.append(' - [ ] Add commits to this branch to resolve the merge conflicts ' +
body.append(' - [ ] Add a commit to this branch to resolve the merge conflicts ' +
'in the following files:')
body.extend([f' - [ ] `{file}`' for file in conflicted_files])
body.extend([f' - `{file}`' for file in conflicted_files])
body.append(' - [ ] Rebuild the Action locally (`npm run build`) and push any changes to the ' +
f'built output in `lib` as a separate commit named exactly `{REBUILD_COMMIT_MESSAGE}`.')
body.append(' - [ ] Ensure another maintainer has reviewed the additional commits you added to this ' +
'branch to resolve the merge conflicts.')
body.append(' - [ ] Ensure the CHANGELOG displays the correct version and date.')
@@ -97,10 +136,6 @@ def open_pr(
body.append(f' - [ ] Check that there are not any unexpected commits being merged into the `{target_branch}` branch.')
body.append(' - [ ] Ensure the docs team is aware of any documentation changes that need to be released.')
if not is_primary_release:
body.append(' - [ ] Remove and re-add the "Rebuild" label to the PR to trigger just this workflow.')
body.append(' - [ ] Wait for the "Rebuild" workflow to push a commit updating the distribution files.')
body.append(' - [ ] Mark the PR as ready for review to trigger the full set of PR checks.')
body.append(' - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.')
@@ -109,13 +144,11 @@ def open_pr(
body.append(' - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.')
title = f'Merge {source_branch} into {target_branch}'
labels = ['Rebuild'] if not is_primary_release else []
# Create the pull request
# PR checks won't be triggered on PRs created by Actions. Therefore mark the PR as draft so that
# a maintainer can take the PR out of draft, thereby triggering the PR checks.
pr = repo.create_pull(title=title, body='\n'.join(body), head=new_branch_name, base=target_branch, draft=True)
pr.add_to_labels(*labels)
print(f'Created PR #{str(pr.number)}')
# Assign the conductor
@@ -270,12 +303,6 @@ def update_changelog(version):
def main():
parser = argparse.ArgumentParser('update-release-branch.py')
parser.add_argument(
'--github-token',
type=str,
required=True,
help='GitHub token, typically from GitHub Actions.'
)
parser.add_argument(
'--repository-nwo',
type=str,
@@ -313,7 +340,7 @@ def main():
target_branch = args.target_branch
is_primary_release = args.is_primary_release
repo = Github(args.github_token).get_repo(args.repository_nwo)
repo = Github(get_github_token()).get_repo(args.repository_nwo)
# the target branch will be of the form releases/vN, where N is the major version number
target_branch_major_version = target_branch.strip('releases/v')
@@ -380,8 +407,9 @@ def main():
# releases.
run_git('revert', vOlder_update_commits[0], '--no-edit')
# Also revert the "Rebuild" commit created by Actions.
rebuild_commit = run_git('log', '--grep', '^Rebuild$', '--format=%H').split()[0]
# Also revert the "Rebuild" commit, whether created by this script or by the
# `Rebuild Action` workflow.
rebuild_commit = run_git('log', '--grep', f'^{REBUILD_COMMIT_MESSAGE}$', '--format=%H').split()[0]
print(f' Reverting {rebuild_commit}')
run_git('revert', rebuild_commit, '--no-edit')
@@ -396,9 +424,10 @@ def main():
run_git('add', '.')
run_git('commit', '--no-edit')
# Migrate the package version number from a vLatest version number to a vOlder version number
# Migrate the package version number from a vLatest version number to a vOlder version number.
# `package-lock.json` is updated as part of the subsequent rebuild step (see `rebuild_action`).
print(f'Setting version number to {version} in package.json')
replace_version_package_json(get_current_version(), version) # We rely on the `Rebuild` workflow to update package-lock.json
replace_version_package_json(get_current_version(), version)
run_git('add', 'package.json')
# Migrate the changelog notes from vLatest version numbers to vOlder version numbers
@@ -421,6 +450,13 @@ def main():
run_git('add', 'CHANGELOG.md')
run_git('commit', '-m', f'Update changelog for v{version}')
if not is_primary_release:
if len(conflicted_files) == 0:
print('Rebuilding the Action.')
rebuild_action()
else:
print(f'Skipping automatic rebuild because the merge produced conflicts in {conflicted_files}.')
run_git('push', ORIGIN, new_branch_name)
# Open a PR to update the branch
+4 -4
View File
@@ -49,10 +49,6 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
version: stable-v2.17.6
- os: ubuntu-latest
version: stable-v2.18.4
- os: ubuntu-latest
version: stable-v2.19.4
- os: ubuntu-latest
@@ -61,6 +57,10 @@ jobs:
version: stable-v2.21.4
- os: ubuntu-latest
version: stable-v2.22.4
- os: ubuntu-latest
version: stable-v2.23.9
- os: ubuntu-latest
version: stable-v2.24.3
- os: ubuntu-latest
version: default
- os: ubuntu-latest
+4 -4
View File
@@ -49,10 +49,6 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
version: stable-v2.17.6
- os: ubuntu-latest
version: stable-v2.18.4
- os: ubuntu-latest
version: stable-v2.19.4
- os: ubuntu-latest
@@ -61,6 +57,10 @@ jobs:
version: stable-v2.21.4
- os: ubuntu-latest
version: stable-v2.22.4
- os: ubuntu-latest
version: stable-v2.23.9
- os: ubuntu-latest
version: stable-v2.24.3
- os: ubuntu-latest
version: default
- os: ubuntu-latest
+4 -4
View File
@@ -49,10 +49,6 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
version: stable-v2.17.6
- os: ubuntu-latest
version: stable-v2.18.4
- os: ubuntu-latest
version: stable-v2.19.4
- os: ubuntu-latest
@@ -61,6 +57,10 @@ jobs:
version: stable-v2.21.4
- os: ubuntu-latest
version: stable-v2.22.4
- os: ubuntu-latest
version: stable-v2.23.9
- os: ubuntu-latest
version: stable-v2.24.3
- os: ubuntu-latest
version: default
- os: ubuntu-latest
+15 -15
View File
@@ -59,41 +59,41 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
version: stable-v2.17.6
- os: macos-latest
version: stable-v2.17.6
- os: ubuntu-latest
version: stable-v2.18.4
- os: macos-latest
version: stable-v2.18.4
- os: ubuntu-latest
version: stable-v2.19.4
- os: macos-latest
- os: macos-latest-xlarge
version: stable-v2.19.4
- os: ubuntu-latest
version: stable-v2.20.7
- os: macos-latest
- os: macos-latest-xlarge
version: stable-v2.20.7
- os: ubuntu-latest
version: stable-v2.21.4
- os: macos-latest
- os: macos-latest-xlarge
version: stable-v2.21.4
- os: ubuntu-latest
version: stable-v2.22.4
- os: macos-latest
- os: macos-latest-xlarge
version: stable-v2.22.4
- os: ubuntu-latest
version: stable-v2.23.9
- os: macos-latest-xlarge
version: stable-v2.23.9
- os: ubuntu-latest
version: stable-v2.24.3
- os: macos-latest-xlarge
version: stable-v2.24.3
- os: ubuntu-latest
version: default
- os: macos-latest
- os: macos-latest-xlarge
version: default
- os: ubuntu-latest
version: linked
- os: macos-latest
- os: macos-latest-xlarge
version: linked
- os: ubuntu-latest
version: nightly-latest
- os: macos-latest
- os: macos-latest-xlarge
version: nightly-latest
name: Multi-language repository
if: github.triggering_actor != 'dependabot[bot]'
+1 -1
View File
@@ -59,7 +59,7 @@ jobs:
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- name: Set up Ruby
uses: ruby/setup-ruby@0cb964fd540e0a24c900370abf38a33466142735 # v1.305.0
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
matrix:
include:
- os: ubuntu-latest
version: stable-v2.19.3
version: stable-v2.19.4
- os: ubuntu-latest
version: stable-v2.22.1
- os: ubuntu-latest
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
fail-fast: false
matrix:
include:
- os: macos-latest
- os: macos-latest-xlarge
version: nightly-latest
name: Swift analysis using autobuild
if: github.triggering_actor != 'dependabot[bot]'
@@ -9,6 +9,10 @@ on:
# by other workflows.
types: [opened, synchronize, reopened, ready_for_review]
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
+1 -1
View File
@@ -77,7 +77,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-14,macos-15]
os: [ubuntu-22.04,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 }}
+5 -28
View File
@@ -6,13 +6,6 @@ env:
# Diff informed queries add an additional query filter which is not yet
# taken into account by these tests.
CODEQL_ACTION_DIFF_INFORMED_QUERIES: false
# Specify overlay enablement manually to ensure stability around the exclude-from-incremental
# query filter. Here we only enable for the default code scanning suite.
CODEQL_ACTION_OVERLAY_ANALYSIS: true
CODEQL_ACTION_OVERLAY_ANALYSIS_JAVASCRIPT: false
CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_JAVASCRIPT: true
CODEQL_ACTION_OVERLAY_ANALYSIS_STATUS_CHECK: false
CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS: true
on:
push:
@@ -31,6 +24,10 @@ on:
- cron: '0 5 * * *'
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
@@ -79,33 +76,13 @@ jobs:
with:
version: ${{ matrix.version }}
# On PRs, overlay analysis may change the config that is passed to the CLI.
# Therefore, we have two variants of the following test, one for PRs and one for other events.
- name: Empty file (non-PR)
if: github.event_name != 'pull_request'
- name: Empty file
uses: ./../action/.github/actions/check-codescanning-config
with:
expected-config-file-contents: "{}"
languages: javascript
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Empty file (PR)
if: github.event_name == 'pull_request'
uses: ./../action/.github/actions/check-codescanning-config
with:
expected-config-file-contents: |
{
"query-filters": [
{
"exclude": {
"tags": "exclude-from-incremental"
}
}
]
}
languages: javascript
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Packs from input
if: success() || failure()
uses: ./../action/.github/actions/check-codescanning-config
@@ -20,6 +20,10 @@ on:
- cron: '0 5 * * *'
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
@@ -19,6 +19,10 @@ on:
- cron: '0 5 * * *'
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
+4 -1
View File
@@ -48,6 +48,9 @@ jobs:
with:
fetch-depth: 0 # ensure we have all tags and can push commits
- uses: actions/setup-node@v6
with:
node-version: 24
cache: 'npm'
- uses: actions/setup-python@v6
with:
python-version: '3.12'
@@ -131,7 +134,7 @@ jobs:
echo "::endgroup::"
- name: Generate token
uses: actions/create-github-app-token@v3.1.1
uses: actions/create-github-app-token@v3.2.0
id: app-token
with:
app-id: ${{ vars.AUTOMATION_APP_ID }}
+116 -32
View File
@@ -10,6 +10,10 @@ on:
types: [checks_requested]
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
@@ -29,6 +33,10 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 45
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: pr-checks-unit-tests-${{ github.ref }}-${{ github.event_name }}-${{ matrix.os }}-node${{ matrix['node-version'] }}
steps:
- name: Prepare git (Windows)
if: runner.os == 'Windows'
@@ -67,22 +75,21 @@ jobs:
sarif_file: eslint.sarif
category: eslint
# Verifying the PR checks are up-to-date requires Node 24. The PR checks are not dependent
# on the main codebase and therefore do not need to be run as part of the same matrix that
# we use for the `unit-tests` job.
verify-pr-checks:
name: Verify PR checks
# These checks do not need to be run as part of the same matrix that we use for the `unit-tests`
# job.
other-checks:
name: Other checks
if: github.triggering_actor != 'dependabot[bot]'
permissions:
contents: read
runs-on: ubuntu-slim
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Prepare git (Windows)
if: runner.os == 'Windows'
run: git config --global core.autocrlf false
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: pr-checks-pr-checks-${{ github.ref }}-${{ github.event_name }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
@@ -93,34 +100,22 @@ jobs:
cache: 'npm'
- name: Install dependencies
id: install-deps
run: npm ci
- name: Verify PR checks up to date
if: always()
if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
run: .github/workflows/script/verify-pr-checks.sh
- name: Run pr-checks tests
if: always()
if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
working-directory: pr-checks
run: npx tsx --test
check-node-version:
if: github.triggering_actor != 'dependabot[bot]'
name: Check Action Node versions
runs-on: ubuntu-latest
timeout-minutes: 45
env:
BASE_REF: ${{ github.base_ref }}
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- id: head-version
name: Verify all Actions use the same Node version
- name: Verify all Actions use the same Node version
id: head-version
run: |
NODE_VERSION=$(find . -name "action.yml" -exec yq -e '.runs.using' {} \; | grep node | sort | uniq)
NODE_VERSION=$(find . -path "*/node_modules" -prune -o -name "action.yml" -exec yq -o=json '.runs.using' {} \; | jq -rs '[.[] | select(. != null and startswith("node"))] | unique | .[]')
echo "NODE_VERSION: ${NODE_VERSION}"
if [[ $(echo "$NODE_VERSION" | wc -l) -gt 1 ]]; then
echo "::error::More than one node version used in 'action.yml' files."
@@ -128,22 +123,111 @@ jobs:
fi
echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT
- id: checkout-base
name: 'Backport: Check out base ref'
- name: Fetch base commit
id: fetch-base
# Forks and Dependabot PRs don't have permission to write comments, so skip the repo size
# check in those cases.
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Compare against the merge base so the size delta reflects only the commits actually
# added by this PR, ignoring any changes that have landed on the base branch since the
# PR branched off.
merge_base=$(gh api "repos/$GITHUB_REPOSITORY/compare/$BASE_SHA...$HEAD_SHA" --jq '.merge_base_commit.sha')
echo "merge_base=$merge_base" >> "$GITHUB_OUTPUT"
git fetch --no-tags --depth=1 origin "$merge_base" "$HEAD_SHA"
- name: Check repo size
if: steps.fetch-base.outcome == 'success'
working-directory: pr-checks
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
BASE_SHA: ${{ steps.fetch-base.outputs.merge_base }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: npx tsx check-repo-size.ts --output-dir "$RUNNER_TEMP/repo-size"
- name: Upload repo size comment
if: steps.fetch-base.outcome == 'success'
uses: actions/upload-artifact@v7
with:
name: repo-size-comment
path: ${{ runner.temp }}/repo-size/
if-no-files-found: error
- name: 'Backport: Check out base ref'
id: checkout-base
if: ${{ startsWith(github.head_ref, 'backport-') }}
uses: actions/checkout@v6
with:
ref: ${{ env.BASE_REF }}
ref: ${{ github.base_ref }}
- name: 'Backport: Verify Node versions unchanged'
if: steps.checkout-base.outcome == 'success'
env:
HEAD_VERSION: ${{ steps.head-version.outputs.node_version }}
run: |
BASE_VERSION=$(find . -name "action.yml" -exec yq -e '.runs.using' {} \; | grep node | sort | uniq)
BASE_VERSION=$(find . -path "*/node_modules" -prune -o -name "action.yml" -exec yq -o=json '.runs.using' {} \; | jq -rs '[.[] | select(. != null and startswith("node"))] | unique | .[]')
echo "HEAD_VERSION: ${HEAD_VERSION}"
echo "BASE_VERSION: ${BASE_VERSION}"
if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then
echo "::error::Cannot change the Node version of an Action in a backport PR."
exit 1
fi
post-repo-size-comment:
name: Post repo size comment
needs: other-checks
# Keep write permissions isolated from the job that checks out and tests PR code. This job only
# posts the candidate comment body produced by the read-only `pr-checks` job.
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]' &&
needs.other-checks.result == 'success'
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-slim
timeout-minutes: 10
concurrency:
cancel-in-progress: true
group: check-repo-size-${{ github.event.pull_request.number }}
steps:
- name: Download repo size comment
uses: actions/download-artifact@v8
with:
name: repo-size-comment
path: repo-size-comment
- name: Post repo size comment
env:
COMMENT_MARKER: "<!-- repo-size-diff-bot -->"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
significant=$(jq -r '.significant' repo-size-comment/metadata.json)
comment_id=$(
gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \
--paginate \
--jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" \
| head -n 1
)
if [[ -n "$comment_id" ]]; then
echo "Updating existing comment $comment_id."
gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" --field body=@repo-size-comment/body.md
elif [[ "$significant" == "true" ]]; then
echo "Creating new repo size comment."
gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" --field body=@repo-size-comment/body.md
else
echo "Skipping repo size comment because the delta is below the threshold and no sticky comment exists."
fi
+4
View File
@@ -14,6 +14,10 @@ on:
- cron: '0 0 * * 1'
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
+4
View File
@@ -17,6 +17,10 @@ on:
- cron: '0 5 * * *'
workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults:
run:
shell: bash
+1 -1
View File
@@ -136,7 +136,7 @@ jobs:
- name: Generate token
if: github.event_name == 'workflow_dispatch'
uses: actions/create-github-app-token@v3.1.1
uses: actions/create-github-app-token@v3.2.0
id: app-token
with:
app-id: ${{ vars.AUTOMATION_APP_ID }}
@@ -18,6 +18,11 @@ on:
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
+5 -3
View File
@@ -64,11 +64,12 @@ jobs:
- name: Update current release branch
if: github.event_name == 'workflow_dispatch'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo SOURCE_BRANCH=${REF_NAME}
echo TARGET_BRANCH=releases/${MAJOR_VERSION}
python .github/update-release-branch.py \
--github-token ${{ secrets.GITHUB_TOKEN }} \
--repository-nwo ${{ github.repository }} \
--source-branch '${{ env.REF_NAME }}' \
--target-branch 'releases/${{ env.MAJOR_VERSION }}' \
@@ -93,7 +94,7 @@ jobs:
pull-requests: write # needed to create pull request
steps:
- name: Generate token
uses: actions/create-github-app-token@v3.1.1
uses: actions/create-github-app-token@v3.2.0
id: app-token
with:
app-id: ${{ vars.AUTOMATION_APP_ID }}
@@ -107,11 +108,12 @@ jobs:
- uses: ./.github/actions/release-initialise
- name: Update older release branch
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo SOURCE_BRANCH=${SOURCE_BRANCH}
echo TARGET_BRANCH=${TARGET_BRANCH}
python .github/update-release-branch.py \
--github-token ${{ secrets.GITHUB_TOKEN }} \
--repository-nwo ${{ github.repository }} \
--source-branch ${SOURCE_BRANCH} \
--target-branch ${TARGET_BRANCH} \
+1 -1
View File
@@ -19,7 +19,7 @@
"scope": "javascript, typescript",
"prefix": "testMacro",
"body": [
"const ${1:nameMacro} = test.macro({",
"const ${1:nameMacro} = makeMacro({",
" exec: async (t: ExecutionContext<unknown>) => {},",
"",
" title: (providedTitle = \"\") => `${2:common title} - \\${providedTitle}`,",
+21
View File
@@ -2,6 +2,27 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## [UNRELEASED]
No user facing changes.
## 4.36.0 - 22 May 2026
- _Breaking change_: Bump the minimum required CodeQL bundle version to 2.19.4. [#3894](https://github.com/github/codeql-action/pull/3894)
- Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893)
- Update default CodeQL bundle version to [2.25.5](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.5). [#3926](https://github.com/github/codeql-action/pull/3926)
## 4.35.5 - 15 May 2026
- We have improved how the JavaScript bundles for the CodeQL Action are generated to avoid duplication across bundles and reduce the size of the repository by around 70%. This should have no effect on the runtime behaviour of the CodeQL Action. [#3899](https://github.com/github/codeql-action/pull/3899)
- For performance and accuracy reasons, [improved incremental analysis](https://github.com/github/roadmap/issues/1158) will now only be enabled on a pull request when diff-informed analysis is also enabled for that run. If diff-informed analysis is unavailable (for example, because the PR diff ranges could not be computed), the action will fall back to a full analysis. [#3791](https://github.com/github/codeql-action/pull/3791)
- If multiple inputs are provided for the GitHub-internal `analysis-kinds` input, only `code-scanning` will be enabled. The `analysis-kinds` input is experimental, for GitHub-internal use only, and may change without notice at any time. [#3892](https://github.com/github/codeql-action/pull/3892)
- Added an experimental change which, when running a Code Scanning analysis for a PR with [improved incremental analysis](https://github.com/github/roadmap/issues/1158) enabled, prefers CodeQL CLI versions that have a cached overlay-base database for the configured languages. This speeds up analysis for a repository when there is not yet a cached overlay-base database for the latest CLI version. We expect to roll this change out to everyone in May. [#3880](https://github.com/github/codeql-action/pull/3880)
## 4.35.4 - 07 May 2026
- Update default CodeQL bundle version to [2.25.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.4). [#3881](https://github.com/github/codeql-action/pull/3881)
## 4.35.3 - 01 May 2026
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.19.3 and earlier. These versions of CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise Server 3.15, and will be unsupported by the next minor release of the CodeQL Action. [#3837](https://github.com/github/codeql-action/pull/3837)
+1 -1
View File
@@ -71,7 +71,7 @@ Once the mergeback and backport pull request have been merged, the release is co
Since the `codeql-action` runs most of its testing through individual Actions workflows, there are over two hundred required jobs that need to pass in order for a PR to turn green. It would be too tedious to maintain that list manually. You can regenerate the set of required checks automatically by running the [sync-checks.ts](pr-checks/sync-checks.ts) script:
- At a minimum, you must provide an argument for the `--token` input. For example, `--token "$(gh auth token)"` to use the same token that `gh` uses. If no token is provided or the token has insufficient permissions, the script will fail.
- At a minimum, you must provide a token with permissions to update branch protection rules. For example, `gh auth token | pr-checks/sync-checks.ts --token-stdin` uses the same token that `gh` uses. You can also set the `GH_TOKEN` or `GITHUB_TOKEN` environment variable. If no token is provided or the token has insufficient permissions, the script will fail.
- By default, the script performs a dry run and outputs information about the changes it would make to the branch protection rules. To actually apply the changes, specify the `--apply` flag.
- If you run the script without any other arguments, it will retrieve the set of workflows that ran for the latest commit on `main`.
- You can specify a different git ref with the `--ref` input. You will likely want to use this if you have a PR that removes or adds PR checks. For example, `--ref "some/branch/name"` to use the HEAD of the `some/branch/name` branch.
-2
View File
@@ -78,8 +78,6 @@ We typically release new minor versions of the CodeQL Action and Bundle when a n
| `v3.28.21` | `2.21.3` | Enterprise Server 3.18 | |
| `v3.28.12` | `2.20.7` | Enterprise Server 3.17 | |
| `v3.28.6` | `2.20.3` | Enterprise Server 3.16 | |
| `v3.28.6` | `2.20.3` | Enterprise Server 3.15 | |
| `v3.28.6` | `2.20.3` | Enterprise Server 3.14 | |
See the full list of GHES release and deprecation dates at [GitHub Enterprise Server releases](https://docs.github.com/en/enterprise-server/admin/all-releases#releases-of-github-enterprise-server).
+2 -2
View File
@@ -95,5 +95,5 @@ outputs:
description: The ID of the uploaded SARIF file.
runs:
using: node24
main: "../lib/analyze-action.js"
post: "../lib/analyze-action-post.js"
main: "../lib/analyze-entry.js"
post: "../lib/analyze-post-entry.js"
+1 -1
View File
@@ -16,4 +16,4 @@ inputs:
required: false
runs:
using: node24
main: '../lib/autobuild-action.js'
main: '../lib/autobuild-entry.js'
+144 -9
View File
@@ -1,5 +1,5 @@
import { copyFile, rm, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { copyFile, readFile, rm, writeFile } from "node:fs/promises";
import { basename, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import * as esbuild from "esbuild";
@@ -62,18 +62,153 @@ const onEndPlugin = {
},
};
/** The name of the virtual `entry-points` module. */
const SHARED_ENTRYPOINT = "entry-points";
/** The property name under which `upload-lib`'s namespace is exposed in `entry-points`. */
const UPLOAD_LIB_EXPORT = "uploadLib";
/** The relative source path of the `upload-lib` module that we re-export from `entry-points`. */
const UPLOAD_LIB_SRC = "./src/upload-lib";
/**
* This plugin finds all source files that contain Action entry points. It then generates the
* virtual `entry-points` module which imports all identified files, and re-exports their
* `runWrapper` functions with suitable aliases.
*
* The virtual module additionally re-exports `upload-lib` under the `uploadLib` namespace so that
* external consumers can access it via the small `lib/upload-lib.js` stub emitted below.
*
* A tiny stub file is emitted for each Action entrypoint, and one for `upload-lib`. Each stub
* imports the shared bundle and calls/re-exports from the respective entry point.
*
* @type {esbuild.Plugin}
*/
const entryPointsPlugin = {
name: "entry-points",
setup(build) {
const namespace = "actions";
const actions = [];
const toPascal = (s) =>
s.replace(/(^|-)([a-z0-9])/gi, (_, __, c) => c.toUpperCase());
// Find the source files containing Action entry points.
build.onStart(() => {
const actionFiles = globSync("src/*-action{,-post}.ts");
for (const actionFile of actionFiles) {
const match = basename(actionFile).match(/(.*)-action(-post)?/);
if (match.length < 2) {
throw new Error(`'${actionFile}' didn't match expected pattern.`);
}
const actionName = match[1];
const isPost = match[2] !== undefined;
actions.push({
path: actionFile,
name: actionName,
isPost,
pascalCaseName: `${toPascal(actionName)}${isPost ? "Post" : ""}Action`,
});
}
});
// Resolve the virtual `entry-points` file and set the corresponding namespace.
// Ideally, we'd `RegExp.escape` the entrypoint here, but that API isn't supported in Node 20.
// Since we're dealing with a hardcoded string, this isn't too much of a problem.
build.onResolve({ filter: new RegExp(`^${SHARED_ENTRYPOINT}$`) }, () => {
return { path: SHARED_ENTRYPOINT, namespace };
});
// Generate the virtual `entry-points` file based on the Actions we discovered.
// Restrict using the namespace. The path filter does not need to discriminate any further.
build.onLoad({ filter: /.*/, namespace }, async () => {
const wrapperTemplatePath = "entry-wrapper.js.tpl";
const wrapperTemplate = await readFile(
join(SRC_DIR, wrapperTemplatePath),
"utf-8",
);
const actionsSorted = actions.sort((a, b) =>
a.name.localeCompare(b.name),
);
const imports = actionsSorted
.map(
(action) =>
`import * as ${action.pascalCaseName} from "./src/${basename(action.path)}";`,
)
.join("\n");
const wrappers = actionsSorted
.map((action) =>
wrapperTemplate.replaceAll("__ACTION__", action.pascalCaseName),
)
.join("\n\n");
// Also re-export the `upload-lib` namespace so that external consumers can reach it
// via the `lib/upload-lib.js` stub without us having to bundle a second copy.
const uploadLibReExport = `export * as ${UPLOAD_LIB_EXPORT} from "${UPLOAD_LIB_SRC}";`;
return {
contents: `"use strict";\n${imports}\n\n${uploadLibReExport}\n\n${wrappers}\n`,
resolveDir: ".",
loader: "ts",
};
});
// Emit entry point stubs for each Action using the entry template.
build.onEnd(async () => {
const makeHeader = (templatePath, sourceFile) =>
`// Automatically generated from '${templatePath}' for 'src/${basename(sourceFile)}'.\n\n`;
// Read the entry point template.
const actionTemplatePath = "action-entry.js.tpl";
const actionTemplate = await readFile(
join(SRC_DIR, actionTemplatePath),
"utf-8",
);
// Write entry point stubs for each Action.
for (const action of actions) {
await writeFile(
join(
OUT_DIR,
`${action.name}${action.isPost ? "-post" : ""}-entry.js`,
),
makeHeader(actionTemplatePath, action.path) +
actionTemplate.replaceAll("__ACTION__", action.pascalCaseName),
);
}
// Write a small stub for `upload-lib` that re-exports it from the shared bundle.
// External callers (e.g. internal testing environments) `require("./lib/upload-lib")`
// and expect the same shape as before, so we expose the namespace as `module.exports`.
const uploadLibStubTemplatePath = "upload-lib-stub.js.tpl";
const uploadLibStubTemplate = await readFile(
join(SRC_DIR, uploadLibStubTemplatePath),
"utf-8",
);
await writeFile(
join(OUT_DIR, "upload-lib.js"),
makeHeader(uploadLibStubTemplatePath, `${UPLOAD_LIB_SRC}.ts`) +
uploadLibStubTemplate.replaceAll(
"__UPLOAD_LIB_EXPORT__",
UPLOAD_LIB_EXPORT,
),
);
});
},
};
const context = await esbuild.context({
// Include upload-lib.ts as an entry point for use in testing environments.
entryPoints: globSync([
`${SRC_DIR}/*-action.ts`,
`${SRC_DIR}/*-action-post.ts`,
"src/upload-lib.ts",
]),
entryPoints: [{ in: SHARED_ENTRYPOINT, out: SHARED_ENTRYPOINT }],
bundle: true,
format: "cjs",
outdir: OUT_DIR,
platform: "node",
plugins: [cleanPlugin, copyDefaultsPlugin, onEndPlugin],
external: ["./entry-points"],
plugins: [cleanPlugin, copyDefaultsPlugin, entryPointsPlugin, onEndPlugin],
target: ["node20"],
define: {
__CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version),
+21
View File
@@ -140,6 +140,18 @@ export default [
"no-async-foreach/no-async-foreach": "error",
"no-sequences": "error",
"no-shadow": "off",
// A basic check that we don't use `exportVariable` from `@actions/core`. This rule depends on
// the module being imported as `core`, but that is a good enough check for us.
"no-restricted-syntax": [
"error",
{
selector:
"MemberExpression[object.name='core'][property.name='exportVariable']",
message: "Use `exportVariable` from `environment.ts` instead.",
},
],
// This is overly restrictive with unsetting `EnvVar`s
"@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/no-shadow": "error",
@@ -157,6 +169,15 @@ export default [
],
},
},
{
files: ["src/environment.ts"],
// We allow `exportVariable` from `@actions/core` to be used in this file
// since it defines the wrapper around it that other modules use.
rules: {
"no-restricted-syntax": "off",
},
},
{
files: ["**/*.ts", "**/*.js"],
+2 -2
View File
@@ -171,5 +171,5 @@ outputs:
description: The version of the CodeQL binary used for analysis
runs:
using: node24
main: '../lib/init-action.js'
post: '../lib/init-action-post.js'
main: '../lib/init-entry.js'
post: '../lib/init-post-entry.js'
-164097
View File
File diff suppressed because one or more lines are too long
-114006
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/analyze-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runAnalyzeAction)();
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/analyze-action-post.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runAnalyzePostAction)();
-106399
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/autobuild-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runAutobuildAction)();
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.25.3",
"cliVersion": "2.25.3",
"priorBundleVersion": "codeql-bundle-v2.25.2",
"priorCliVersion": "2.25.2"
"bundleVersion": "codeql-bundle-v2.25.5",
"cliVersion": "2.25.5",
"priorBundleVersion": "codeql-bundle-v2.25.4",
"priorCliVersion": "2.25.4"
}
+70319 -80562
View File
File diff suppressed because one or more lines are too long
-110989
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/init-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runInitAction)();
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/init-action-post.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runInitPostAction)();
-105958
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/resolve-environment-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runResolveEnvironmentAction)();
-107467
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/setup-codeql-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runSetupCodeqlAction)();
-162909
View File
File diff suppressed because one or more lines are too long
-123219
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/start-proxy-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runStartProxyAction)();
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/start-proxy-action-post.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runStartProxyPostAction)();
+3 -111670
View File
File diff suppressed because one or more lines are too long
-162934
View File
File diff suppressed because one or more lines are too long
-112357
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/upload-sarif-action.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runUploadSarifAction)();
+6
View File
@@ -0,0 +1,6 @@
// Automatically generated from 'action-entry.js.tpl' for 'src/upload-sarif-action-post.ts'.
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.runUploadSarifPostAction)();
+363 -474
View File
File diff suppressed because it is too large Load Diff
+14 -13
View File
@@ -1,18 +1,19 @@
{
"name": "codeql",
"version": "4.35.3",
"version": "4.36.1",
"private": true,
"description": "CodeQL action",
"scripts": {
"_build_comment": "echo 'Run the full build so we typecheck the project and can reuse the transpiled files in npm test'",
"build": "./scripts/check-node-modules.sh && npm run transpile && node build.mjs && npx tsx ./pr-checks/bundle-metadata.ts",
"build": "./scripts/check-node-modules.sh && npm run transpile && node build.mjs",
"lint": "eslint --report-unused-disable-directives --max-warnings=0 .",
"lint-ci": "SARIF_ESLINT_IGNORE_SUPPRESSED=true eslint --report-unused-disable-directives --max-warnings=0 . --format @microsoft/eslint-formatter-sarif --output-file=eslint.sarif",
"lint-fix": "eslint --report-unused-disable-directives --max-warnings=0 . --fix",
"ava": "npm run transpile && ava --verbose",
"test": "npm run ava -- src/",
"test-debug": "npm run test -- --timeout=20m",
"transpile": "tsc --build --verbose tsconfig.json"
"transpile": "tsc --build --verbose tsconfig.json",
"update-pr-checks": "./pr-checks/sync.sh"
},
"license": "MIT",
"workspaces": [
@@ -43,32 +44,32 @@
"uuid": "^14.0.0"
},
"devDependencies": {
"@ava/typescript": "7.0.0",
"@ava/typescript": "6.0.0",
"@eslint/compat": "^2.0.5",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@octokit/types": "^16.0.0",
"@types/archiver": "^7.0.0",
"@types/follow-redirects": "^1.14.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.19.9",
"@types/node": "^20.19.39",
"@types/node-forge": "^1.3.14",
"@types/sarif": "^2.1.7",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.1",
"ava": "^7.0.0",
"ava": "^6.4.1",
"esbuild": "^0.28.0",
"eslint": "^9.39.2",
"eslint": "^9.39.4",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.2",
"eslint-plugin-jsdoc": "^62.9.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"globals": "^17.5.0",
"globals": "^17.6.0",
"nock": "^14.0.12",
"sinon": "^21.1.2",
"typescript": "^6.0.2",
"typescript-eslint": "^8.58.2"
"sinon": "^22.0.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.2"
},
"overrides": {
"@actions/tool-cache": {
@@ -89,7 +90,7 @@
"eslint-plugin-jsx-a11y": {
"semver": ">=6.3.1"
},
"brace-expansion@2.0.1": "2.0.2",
"glob": "^11.1.0"
"glob": "^11.1.0",
"undici": "^6.24.0"
}
}
+259
View File
@@ -0,0 +1,259 @@
#!/usr/bin/env npx tsx
/*
Tests for check-repo-size.ts.
*/
import * as assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { randomBytes } from "node:crypto";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { afterEach, beforeEach, describe, it } from "node:test";
import {
COMMENT_MARKER,
DEFAULT_BASE_REF,
buildCommentBody,
formatBytes,
formatPercent,
isDeltaSignificant,
measureArchiveSize,
readArgs,
} from "./check-repo-size";
describe("formatBytes", async () => {
const cases: Array<[number, boolean, string]> = [
// Unsigned values, including sub-KiB amounts which round to 0.00.
[0, false, "0.00 KiB"],
[512, false, "0.50 KiB"],
[1024, false, "1.00 KiB"],
[1024 * 1024, false, "1024.00 KiB"],
[2 * 1024 * 1024, false, "2048.00 KiB"],
// Negative values always use a leading minus.
[-2 * 1024 * 1024, false, "-2048.00 KiB"],
// signed=true prepends a + to non-negative values.
[0, true, "+0.00 KiB"],
[2 * 1024 * 1024, true, "+2048.00 KiB"],
[-2 * 1024 * 1024, true, "-2048.00 KiB"],
];
for (const [bytes, signed, expected] of cases) {
await it(`formats ${bytes} (signed=${signed}) as ${expected}`, () => {
assert.equal(formatBytes(bytes, signed), expected);
});
}
});
describe("formatPercent", async () => {
await it("formats positive fractions with a leading +", () => {
assert.equal(formatPercent(0.1), "+10.00%");
assert.equal(formatPercent(0.0123), "+1.23%");
});
await it("formats negative fractions with a leading -", () => {
assert.equal(formatPercent(-0.1), "-10.00%");
});
await it("formats zero without a sign", () => {
assert.equal(formatPercent(0), "0.00%");
});
});
describe("isDeltaSignificant", async () => {
const cases: Array<[number, number, number, boolean]> = [
// At and above threshold (both signs).
[100, 1000, 0.1, true],
[101, 1000, 0.1, true],
[-100, 1000, 0.1, true],
// Below threshold (both signs, plus exact zero).
[99, 1000, 0.1, false],
[-99, 1000, 0.1, false],
[0, 1000, 0.1, false],
];
for (const [delta, base, fraction, expected] of cases) {
await it(`returns ${expected} for delta=${delta}, base=${base}, fraction=${fraction}`, () => {
assert.equal(isDeltaSignificant(delta, base, fraction), expected);
});
}
});
describe("buildCommentBody", async () => {
await it("includes the marker, the base/PR/delta rows, and the run URL", () => {
const body = buildCommentBody({
baseRef: "main",
baseSize: 2_000_000,
prSize: 2_300_000,
runUrl: "https://example.test/run",
});
assert.match(body, new RegExp(`^${escapeRegExp(COMMENT_MARKER)}`));
assert.match(body, /Base \(`main`\) \| 1953\.13 KiB \(2000000 bytes\)/);
assert.match(body, /This PR \| 2246\.09 KiB \(2300000 bytes\)/);
assert.match(
body,
/\*\*Delta\*\* \| \*\*\+292\.97 KiB \(\+300000 bytes, \+15\.00%\)\*\*/,
);
assert.match(body, /\[workflow run\]\(https:\/\/example\.test\/run\)/);
});
await it("formats negative deltas with a leading minus and omits the run URL when missing", () => {
const body = buildCommentBody({
baseRef: "main",
baseSize: 2_000_000,
prSize: 1_800_000,
});
assert.match(
body,
/\*\*Delta\*\* \| \*\*-195\.31 KiB \(-200000 bytes, -10\.00%\)\*\*/,
);
assert.doesNotMatch(body, /workflow run/);
});
});
describe("readArgs", async () => {
await it("defaults the base ref and head commit for local runs", () => {
const originalEnv = process.env;
const originalArgv = process.argv;
try {
process.env = {};
process.argv = ["node", "check-repo-size.ts", "--output-dir", "/tmp/out"];
const args = readArgs();
assert.equal(args.baseRef, DEFAULT_BASE_REF);
assert.equal(args.baseCommitish, `origin/${DEFAULT_BASE_REF}`);
assert.equal(args.headCommitish, "HEAD");
assert.equal(args.outputDir, "/tmp/out");
assert.equal(args.runUrl, undefined);
} finally {
process.env = originalEnv;
process.argv = originalArgv;
}
});
await it("uses the base and head SHAs when provided by the workflow", () => {
const originalEnv = process.env;
const originalArgv = process.argv;
try {
process.env = {
BASE_REF: "main",
BASE_SHA: "abc123",
HEAD_SHA: "def456",
RUN_URL: "https://example.test/run",
};
process.argv = ["node", "check-repo-size.ts", "--output-dir", "/tmp/out"];
const args = readArgs();
assert.equal(args.baseRef, "main");
assert.equal(args.baseCommitish, "abc123");
assert.equal(args.headCommitish, "def456");
assert.equal(args.outputDir, "/tmp/out");
assert.equal(args.runUrl, "https://example.test/run");
} finally {
process.env = originalEnv;
process.argv = originalArgv;
}
});
await it("throws when --output-dir is missing", () => {
const originalEnv = process.env;
const originalArgv = process.argv;
try {
process.env = {};
process.argv = ["node", "check-repo-size.ts"];
assert.throws(() => readArgs(), /--output-dir is required/);
} finally {
process.env = originalEnv;
process.argv = originalArgv;
}
});
});
let repoDir: string;
beforeEach(() => {
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), "check-repo-size-test-"));
execFileSync("git", ["init", "--initial-branch=main", "-q"], {
cwd: repoDir,
});
execFileSync("git", ["config", "user.email", "test@example.test"], {
cwd: repoDir,
});
execFileSync("git", ["config", "user.name", "Test"], { cwd: repoDir });
execFileSync("git", ["config", "commit.gpgsign", "false"], { cwd: repoDir });
});
afterEach(() => {
fs.rmSync(repoDir, { recursive: true, force: true });
});
function commit(name: string, content: string, message: string) {
fs.writeFileSync(path.join(repoDir, name), content);
execFileSync("git", ["add", name], { cwd: repoDir });
execFileSync("git", ["commit", "-q", "-m", message], { cwd: repoDir });
}
describe("measureArchiveSize", async () => {
await it("returns a positive byte count for a non-empty repo", async () => {
commit("a.txt", "hello world\n", "first");
const size = await measureArchiveSize("HEAD", repoDir);
assert.ok(size > 0, `expected size > 0, got ${size}`);
});
await it("returns the same size on repeated runs (deterministic)", async () => {
commit("a.txt", "hello world\n", "first");
const a = await measureArchiveSize("HEAD", repoDir);
const b = await measureArchiveSize("HEAD", repoDir);
assert.equal(a, b);
});
await it("returns a larger size when more content is added", async () => {
commit("a.txt", "hello world\n", "first");
const small = await measureArchiveSize("HEAD", repoDir);
// Use random bytes so the new content is incompressible and the archive
// is guaranteed to grow even after gzip.
commit("b.bin", randomBytes(8192).toString("base64"), "second");
const big = await measureArchiveSize("HEAD", repoDir);
assert.ok(
big > small,
`expected ${big} > ${small} after adding more content`,
);
});
await it("ignores untracked files (e.g. node_modules)", async () => {
commit("a.txt", "hello\n", "first");
commit(".gitignore", "node_modules/\n", "ignore node_modules");
const sizeBefore = await measureArchiveSize("HEAD", repoDir);
fs.mkdirSync(path.join(repoDir, "node_modules"));
fs.writeFileSync(
path.join(repoDir, "node_modules", "huge.bin"),
"x".repeat(1_000_000),
);
const sizeAfter = await measureArchiveSize("HEAD", repoDir);
assert.equal(
sizeAfter,
sizeBefore,
"untracked node_modules should not affect the archive size",
);
});
await it("rejects when the ref does not exist", async () => {
commit("a.txt", "hello\n", "first");
await assert.rejects(
() => measureArchiveSize("does-not-exist", repoDir),
/git archive does-not-exist exited with code/,
);
});
});
function escapeRegExp(s: string): string {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
+223
View File
@@ -0,0 +1,223 @@
#!/usr/bin/env npx tsx
/*
Measures the difference in the `.tar.gz`'d checkout size of the repo between the PR head and the PR
base. This size is relevant because it corresponds to the duration of the "Download action
repository" step that happens at the start of every job that uses this Action.
Writes the candidate sticky-comment body and a small metadata file to `--output-dir`. A separate
workflow job consumes those artifacts and decides whether to create or update a PR comment.
*/
import { spawn } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import { parseArgs } from "node:util";
import { REPO_ROOT } from "./config";
/** Hidden marker used to find the existing sticky comment on a PR. */
export const COMMENT_MARKER = "<!-- repo-size-diff-bot -->";
export const DEFAULT_BASE_REF = "main";
/**
* Fraction of the base archive size at which a delta is considered significant enough to warrant
* a new sticky comment. We always update an existing comment regardless, so the comment stays in
* sync as the diff evolves.
*/
export const SIGNIFICANT_DELTA_FRACTION = 0.1;
/**
* Stream `git archive --format=tar.gz <ref>` and count the compressed bytes.
*
* `git archive` only includes tracked files, so untracked directories like `node_modules` and
* `build` aren't counted in the size downloaded when starting up a CodeQL job.
*/
export async function measureArchiveSize(
ref: string,
cwd: string,
): Promise<number> {
const git = spawn("git", ["archive", "--format=tar.gz", ref], { cwd });
let stderr = "";
git.stderr.on("data", (chunk: Buffer) => {
stderr += chunk.toString();
});
let size = 0;
git.stdout.on("data", (chunk: Buffer) => {
size += chunk.length;
});
const exitCode = await new Promise<number>((resolve, reject) => {
git.on("error", reject);
git.on("close", resolve);
});
if (exitCode !== 0) {
throw new Error(
`git archive ${ref} exited with code ${exitCode}: ${stderr.trim()}`,
);
}
return size;
}
/**
* Format a byte count as KiB. If `signed` is true, a leading `+` is prepended for non-negative
* values so gains and losses are visually distinct.
*/
export function formatBytes(bytes: number, signed = false): string {
const sign = bytes < 0 ? "-" : signed ? "+" : "";
const kib = Math.abs(bytes) / 1024;
return `${sign}${kib.toFixed(2)} KiB`;
}
/** Format a fraction as a signed percentage with 2 decimal places. */
export function formatPercent(fraction: number): string {
const pct = fraction * 100;
const sign = pct > 0 ? "+" : "";
return `${sign}${pct.toFixed(2)}%`;
}
export interface CommentBodyOptions {
baseRef: string;
baseSize: number;
prSize: number;
/** Optional URL of the workflow run, included in the comment footer. */
runUrl?: string;
}
export function buildCommentBody(opts: CommentBodyOptions): string {
const { baseRef, baseSize, prSize, runUrl } = opts;
const delta = prSize - baseSize;
const signedDelta = delta >= 0 ? `+${delta}` : `${delta}`;
const runUrlLine = runUrl
? ` See the [workflow run](${runUrl}) for details.`
: "";
return [
COMMENT_MARKER,
"### Repository checkout size",
"",
"| | Compressed archive size |",
"|---|---|",
`| Base (\`${baseRef}\`) | ${formatBytes(baseSize)} (${baseSize} bytes) |`,
`| This PR | ${formatBytes(prSize)} (${prSize} bytes) |`,
`| **Delta** | **${formatBytes(delta, true)} (${signedDelta} bytes, ${formatPercent(delta / baseSize)})** |`,
"",
"Sizes are measured by streaming `git archive --format=tar.gz <ref>`, " +
"which includes tracked files and excludes untracked files such as " +
"`node_modules`. The compressed checkout is " +
"downloaded by every consumer of this Action, so changes here directly " +
`affect Action download time.${runUrlLine}`,
].join("\n");
}
/**
* Returns true when the absolute delta is at least `fraction` of the base size. Both increases and
* decreases are considered significant, so we report wins as well as losses.
*/
export function isDeltaSignificant(
delta: number,
baseSize: number,
fraction: number,
): boolean {
return Math.abs(delta) >= baseSize * fraction;
}
interface MainArgs {
/** Base ref of the PR. Defaults to `main`. Used as the label in the PR comment. */
baseRef: string;
/** Base commit-ish to archive. Defaults to `origin/<baseRef>` for local runs. */
baseCommitish: string;
/** Head commit-ish to archive. Defaults to `HEAD` for local runs. */
headCommitish: string;
/** Optional URL of the workflow run, surfaced in the comment footer. */
runUrl?: string;
/** Directory where `body.md` and `metadata.json` are written. */
outputDir: string;
}
export function readArgs(): MainArgs {
const { values } = parseArgs({
options: {
"output-dir": { type: "string" },
},
strict: true,
});
const outputDir = values["output-dir"];
if (!outputDir) {
throw new Error("--output-dir is required");
}
const baseRef = process.env.BASE_REF ?? DEFAULT_BASE_REF;
const baseCommitish = process.env.BASE_SHA ?? `origin/${baseRef}`;
const headCommitish = process.env.HEAD_SHA ?? "HEAD";
return {
baseRef,
baseCommitish,
headCommitish,
runUrl: process.env.RUN_URL,
outputDir,
};
}
async function main(): Promise<number> {
const args = readArgs();
console.log(`Measuring base archive size for ${args.baseCommitish}...`);
const baseSize = await measureArchiveSize(args.baseCommitish, REPO_ROOT);
console.log(` ${baseSize} bytes`);
console.log(`Measuring PR archive size for ${args.headCommitish}...`);
const prSize = await measureArchiveSize(args.headCommitish, REPO_ROOT);
console.log(` ${prSize} bytes`);
const delta = prSize - baseSize;
const significant = isDeltaSignificant(
delta,
baseSize,
SIGNIFICANT_DELTA_FRACTION,
);
console.log(
`Delta: ${delta} bytes (significant=${significant}, threshold=${(
SIGNIFICANT_DELTA_FRACTION * 100
).toFixed(2)}%)`,
);
const body = buildCommentBody({
baseRef: args.baseRef,
baseSize,
prSize,
runUrl: args.runUrl,
});
fs.mkdirSync(args.outputDir, { recursive: true });
fs.writeFileSync(path.join(args.outputDir, "body.md"), body);
fs.writeFileSync(
path.join(args.outputDir, "metadata.json"),
`${JSON.stringify(
{ significant, baseRef: args.baseRef, baseSize, prSize, delta },
null,
2,
)}\n`,
);
console.log(`Wrote body.md and metadata.json to ${args.outputDir}.`);
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 (require.main === module) {
void run();
}
@@ -2,7 +2,8 @@ name: "Multi-language repository"
description: "An end-to-end integration test of a multi-language repository using automatic language detection"
operatingSystems:
- ubuntu
- macos
- os: macos
runner-image: macos-latest-xlarge
env:
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
installGo: true
+1 -1
View File
@@ -5,7 +5,7 @@ versions:
- default
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@0cb964fd540e0a24c900370abf38a33466142735 # v1.305.0
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+1 -1
View File
@@ -2,7 +2,7 @@ name: "Rust analysis"
description: "Tests creation of a Rust database"
versions:
# experimental rust support introduced, requires action to set `CODEQL_ENABLE_EXPERIMENTAL_FEATURES`
- stable-v2.19.3
- stable-v2.19.4
# first public preview version
- stable-v2.22.1
- linked
+2 -1
View File
@@ -3,7 +3,8 @@ description: "Tests creation of a Swift database using autobuild"
versions:
- nightly-latest
operatingSystems:
- macos
- os: macos
runner-image: macos-latest-xlarge
steps:
- uses: ./../action/init
id: init
+5 -2
View File
@@ -6,14 +6,17 @@ export const OLDEST_SUPPORTED_MAJOR_VERSION = 3;
/** The `pr-checks` directory. */
export const PR_CHECKS_DIR = __dirname;
/** The repository root. */
export const REPO_ROOT = path.join(PR_CHECKS_DIR, "..");
/** The path of the file configuring which checks shouldn't be required. */
export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml");
/** The path to the esbuild metadata file. */
export const BUNDLE_METADATA_FILE = path.join(PR_CHECKS_DIR, "..", "meta.json");
export const BUNDLE_METADATA_FILE = path.join(REPO_ROOT, "meta.json");
/** The `src` directory. */
const SOURCE_ROOT = path.join(PR_CHECKS_DIR, "..", "src");
const SOURCE_ROOT = path.join(REPO_ROOT, "src");
/** The path to the built-in languages file. */
export const BUILTIN_LANGUAGES_FILE = path.join(
+8 -7
View File
@@ -1,16 +1,17 @@
# PR checks to exclude from required checks
contains:
- "https://"
- "Update"
- "ESLint"
- "update"
- "https://"
- "test-setup-python-scripts"
- "update"
- "Update"
is:
- "Agent"
- "check-expected-release-files"
- "Cleanup artifacts"
- "CodeQL"
- "Dependabot"
- "check-expected-release-files"
- "Agent"
- "Cleanup artifacts"
- "Label PR with size"
- "Post repo size comment"
- "Prepare"
- "Upload results"
- "Label PR with size"
+2 -2
View File
@@ -7,10 +7,10 @@
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": ">=9.2.2",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"yaml": "^2.8.3"
"yaml": "^2.8.4"
},
"devDependencies": {
"@types/node": "^20.19.9",
"@types/node": "^20.19.39",
"tsx": "^4.21.0"
}
}
+50 -1
View File
@@ -7,7 +7,13 @@ Tests for the sync-checks.ts script
import * as assert from "node:assert/strict";
import { describe, it } from "node:test";
import { CheckInfo, Exclusions, Options, removeExcluded } from "./sync-checks";
import {
CheckInfo,
Exclusions,
Options,
removeExcluded,
resolveToken,
} from "./sync-checks";
const defaultOptions: Options = {
apply: false,
@@ -58,3 +64,46 @@ describe("removeExcluded", async () => {
assert.deepEqual(retained, expectedExactMatches);
});
});
describe("resolveToken", async () => {
await it("reads the token from standard input", async () => {
const token = await resolveToken(
{ tokenStdin: true },
{ env: {}, readStdin: async () => " stdin-token\n" },
);
assert.equal(token, "stdin-token");
});
await it("reads the token from the GH_TOKEN environment variable", async () => {
const token = await resolveToken(
{},
{ env: { GH_TOKEN: "env-token" }, readStdin: async () => "" },
);
assert.equal(token, "env-token");
});
await it("reads the token from the GITHUB_TOKEN environment variable", async () => {
const token = await resolveToken(
{},
{ env: { GITHUB_TOKEN: "env-token" }, readStdin: async () => "" },
);
assert.equal(token, "env-token");
});
await it("rejects an empty standard input token", async () => {
await assert.rejects(
resolveToken(
{ tokenStdin: true },
{ env: {}, readStdin: async () => "\n" },
),
/No token received on standard input/,
);
});
await it("rejects missing token sources", async () => {
await assert.rejects(
resolveToken({}, { env: {}, readStdin: async () => "" }),
/Missing authentication token/,
);
});
});
+69 -9
View File
@@ -15,8 +15,8 @@ import {
/** Represents the command-line options. */
export interface Options {
/** The token to use to authenticate to the GitHub API. */
token?: string;
/** Whether to read the GitHub API token from standard input. */
tokenStdin?: boolean;
/** The git ref to use the checks for. */
ref?: string;
/** Whether to actually apply the changes or not. */
@@ -31,6 +31,65 @@ const codeqlActionRepo = {
repo: "codeql-action",
};
/** Environment variables to check for a GitHub API token. */
const TOKEN_ENVIRONMENT_VARIABLES = ["GH_TOKEN", "GITHUB_TOKEN"];
/** Represents the sources from which we can retrieve the GitHub API token. */
interface TokenSource {
/** Environment variables to inspect. */
env: NodeJS.ProcessEnv;
/** Reads a token from standard input. */
readStdin: () => Promise<string>;
}
/** Reads the GitHub API token from standard input. */
async function readTokenFromStdin(): Promise<string> {
let token = "";
process.stdin.setEncoding("utf8");
for await (const chunk of process.stdin) {
token += chunk;
}
return token.trim();
}
/** Gets a GitHub API token from one of the supported environment variables. */
function getTokenFromEnvironment(env: NodeJS.ProcessEnv): string | undefined {
for (const variableName of TOKEN_ENVIRONMENT_VARIABLES) {
const token = env[variableName]?.trim();
if (token) {
return token;
}
}
return undefined;
}
/** Gets the token to use to authenticate to the GitHub API. */
export async function resolveToken(
options: Pick<Options, "tokenStdin">,
tokenSource: TokenSource = {
env: process.env,
readStdin: readTokenFromStdin,
},
): Promise<string> {
if (options.tokenStdin) {
const token = (await tokenSource.readStdin()).trim();
if (token.length === 0) {
throw new Error("No token received on standard input.");
}
return token;
}
const environmentToken = getTokenFromEnvironment(tokenSource.env);
if (environmentToken !== undefined) {
return environmentToken;
}
throw new Error(
"Missing authentication token. Set GH_TOKEN/GITHUB_TOKEN or pipe a token " +
"to --token-stdin.",
);
}
/** Represents a configuration of which checks should not be set up as required checks. */
export interface Exclusions {
/** A list of strings that, if contained in a check name, are excluded. */
@@ -205,9 +264,10 @@ async function updateBranch(
async function main(): Promise<void> {
const { values: options } = parseArgs({
options: {
// The token to use to authenticate to the API.
token: {
type: "string",
// Read the token to use to authenticate to the API from standard input.
"token-stdin": {
type: "boolean",
default: false,
},
// The git ref for which to retrieve the check runs.
ref: {
@@ -228,16 +288,16 @@ async function main(): Promise<void> {
strict: true,
});
if (options.token === undefined) {
throw new Error("Missing --token");
}
const token = await resolveToken({
tokenStdin: options["token-stdin"],
});
console.info(
`Oldest supported major version is: ${OLDEST_SUPPORTED_MAJOR_VERSION}`,
);
// Initialise the API client.
const client = getApiClient(options.token);
const client = getApiClient(token);
// Find the check runs for the specified `ref` that we will later set as the required checks
// for the main and release branches.
+42 -11
View File
@@ -28,6 +28,24 @@ interface WorkflowInput {
/** A partial mapping from known input names to input definitions. */
type WorkflowInputs = Partial<Record<KnownInputName, WorkflowInput>>;
/** An operating system identifier. */
type OperatingSystemIdentifier = "ubuntu" | "macos" | "windows";
/**
* Represents an operating system matrix entry for a generated PR check workflow.
*
* Either a string containing the OS identifier or an object containing the OS identifier and an
* optional runner image label.
*/
type OperatingSystem =
| OperatingSystemIdentifier
| {
/** OS identifier. */
os: OperatingSystemIdentifier;
/** Optional runner image label. */
"runner-image"?: string;
};
/**
* Represents PR check specifications.
*/
@@ -36,8 +54,8 @@ interface Specification extends JobSpecification {
inputs?: Record<string, WorkflowInput>;
/** CodeQL bundle versions to test against. Defaults to `DEFAULT_TEST_VERSIONS`. */
versions?: string[];
/** Operating system prefixes used to select runner images (e.g. `["ubuntu", "macos"]`). */
operatingSystems?: string[];
/** Operating system prefixes, either as strings or with explicit runner image labels. */
operatingSystems?: OperatingSystem[];
/** Per-OS version overrides. If specified for an OS, only those versions are tested on that OS. */
osCodeQlVersions?: Record<string, string[]>;
/** Whether to use the all-platform CodeQL bundle. */
@@ -97,10 +115,6 @@ type LanguageSetups = Partial<Record<BuiltInLanguage, LanguageSetup>>;
// The default set of CodeQL Bundle versions to use for the PR checks.
const defaultTestVersions = [
// The oldest supported CodeQL version. If bumping, update `CODEQL_MINIMUM_VERSION` in `codeql.ts`
"stable-v2.17.6",
// The last CodeQL release in the 2.18 series.
"stable-v2.18.4",
// The last CodeQL release in the 2.19 series.
"stable-v2.19.4",
// The last CodeQL release in the 2.20 series.
"stable-v2.20.7",
@@ -108,6 +122,10 @@ const defaultTestVersions = [
"stable-v2.21.4",
// The last CodeQL release in the 2.22 series.
"stable-v2.22.4",
// The last CodeQL release in the 2.23 series.
"stable-v2.23.9",
// The last CodeQL release in the 2.24 series.
"stable-v2.24.3",
// The default version of CodeQL for Dotcom, as determined by feature flags.
"default",
// The version of CodeQL shipped with the Action in `defaults.json`. During the release process
@@ -311,10 +329,19 @@ function generateJobMatrix(
);
}
const runnerImages = ["ubuntu-latest", "macos-latest", "windows-latest"];
const defaultRunnerImages = [
"ubuntu-latest",
"macos-latest",
"windows-latest",
];
const operatingSystems = checkSpecification.operatingSystems ?? ["ubuntu"];
for (const operatingSystem of operatingSystems) {
for (const operatingSystemConfig of operatingSystems) {
const operatingSystem =
typeof operatingSystemConfig === "string"
? operatingSystemConfig
: operatingSystemConfig.os;
// If osCodeQlVersions is set for this OS, only include the specified CodeQL versions.
const allowedVersions =
checkSpecification.osCodeQlVersions?.[operatingSystem];
@@ -322,9 +349,13 @@ function generateJobMatrix(
continue;
}
const runnerImagesForOs = runnerImages.filter((image) =>
image.startsWith(operatingSystem),
);
const runnerImagesForOs =
typeof operatingSystemConfig === "string" ||
operatingSystemConfig["runner-image"] === undefined
? defaultRunnerImages.filter((image) =>
image.startsWith(operatingSystem),
)
: [operatingSystemConfig["runner-image"]];
for (const runnerImage of runnerImagesForOs) {
matrix.push({
@@ -23,7 +23,8 @@ predicate isSafeForDefaultSetup(string envVar) {
"GITHUB_BASE_REF", "GITHUB_EVENT_NAME", "GITHUB_JOB", "GITHUB_RUN_ATTEMPT", "GITHUB_RUN_ID",
"GITHUB_SHA", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "GITHUB_TOKEN", "GITHUB_WORKFLOW",
"GITHUB_WORKSPACE", "GOFLAGS", "ImageVersion", "JAVA_TOOL_OPTIONS", "RUNNER_ARCH",
"RUNNER_ENVIRONMENT", "RUNNER_NAME", "RUNNER_OS", "RUNNER_TEMP", "RUNNER_TOOL_CACHE"
"RUNNER_ENVIRONMENT", "RUNNER_NAME", "RUNNER_OS", "RUNNER_TEMP", "RUNNER_TOOL_CACHE",
"NODE_ENV"
]
}
@@ -43,6 +44,7 @@ predicate envVarRead(DataFlow::Node node, string envVar) {
from DataFlow::Node read, string envVar
where
envVarRead(read, envVar) and
read.getFile().getRelativePath().matches("src/%") and
not read.getFile().getBaseName().matches("%.test.ts") and
not isSafeForDefaultSetup(envVar)
select read,
+1 -1
View File
@@ -22,4 +22,4 @@ outputs:
description: The inferred build environment configuration.
runs:
using: node24
main: '../lib/resolve-environment-action.js'
main: '../lib/resolve-environment-entry.js'
+20 -1
View File
@@ -19,6 +19,25 @@ inputs:
If not specified, the Action will check in several places until it finds
the CodeQL tools.
required: false
languages:
description: >-
A comma-separated list of CodeQL languages that will be analyzed in subsequent
`github/codeql-action/init` and `github/codeql-action/analyze` invocations. If specified, the
Action may use this list to select a CodeQL CLI version that is best suited to analyzing those
languages, for example by preferring a version that has a cached overlay-base database for the
specified languages. This input is not remembered and must also be passed to
`github/codeql-action/init`.
required: false
analysis-kinds:
description: >-
[Internal] A comma-separated list of analysis kinds that subsequent
`github/codeql-action/init` invocations will enable. If specified, the Action may use this
list to select a CodeQL CLI version that is best suited to those analysis kinds. This input is
not remembered and must also be passed to `github/codeql-action/init`.
Available options are the same as for the `analysis-kinds` input on the `init` Action.
default: 'code-scanning'
required: true
token:
description: GitHub token to use for authenticating with this instance of GitHub.
default: ${{ github.token }}
@@ -36,4 +55,4 @@ outputs:
description: The version of the CodeQL binary that was installed.
runs:
using: node24
main: '../lib/setup-codeql-action.js'
main: '../lib/setup-codeql-entry.js'
+4
View File
@@ -0,0 +1,4 @@
"use strict";
const import_entry_points = require("./entry-points");
void (0, import_entry_points.run__ACTION__)();
+96 -9
View File
@@ -16,7 +16,12 @@ import {
} from "./analyses";
import { EnvVar } from "./environment";
import { getRunnerLogger } from "./logging";
import { setupTests } from "./testing-utils";
import {
createFeatures,
RecordingLogger,
setupBaseActionsVars,
setupTests,
} from "./testing-utils";
import { AssessmentPayload } from "./upload-lib/types";
import { ConfigurationError } from "./util";
@@ -53,24 +58,91 @@ test("Parsing analysis kinds requires at least one analysis kind", async (t) =>
test.serial(
"getAnalysisKinds - returns expected analysis kinds for `analysis-kinds` input",
async (t) => {
process.env[EnvVar.TEST_MODE] = "true";
const features = createFeatures([]);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns("code-scanning,code-quality");
const result = await getAnalysisKinds(getRunnerLogger(true), true);
const result = await getAnalysisKinds(
getRunnerLogger(true),
features,
true,
);
t.assert(result.includes(AnalysisKind.CodeScanning));
t.assert(result.includes(AnalysisKind.CodeQuality));
},
);
test.serial(
"getAnalysisKinds - only use `code-scanning` for multiple analysis kinds outside of test mode",
async (t) => {
setupBaseActionsVars();
process.env[EnvVar.TEST_MODE] = "false";
const features = createFeatures([]);
const logger = new RecordingLogger();
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns("code-scanning,code-quality");
const result = await getAnalysisKinds(logger, features, true);
t.deepEqual(result, [AnalysisKind.CodeScanning]);
t.assert(
logger.hasMessage(
"Continuing with only `analysis-kinds: code-scanning`.",
),
);
},
);
test.serial(
"getAnalysisKinds - logs error for non-default `analysis-kinds` in custom workflow",
async (t) => {
setupBaseActionsVars({ GITHUB_EVENT_NAME: "push" });
process.env[EnvVar.TEST_MODE] = "false";
const features = createFeatures([]);
const logger = new RecordingLogger();
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("analysis-kinds").returns("code-quality");
const result = await getAnalysisKinds(logger, features, true);
t.deepEqual(result, [AnalysisKind.CodeQuality]);
t.assert(
logger.hasMessage(
"An analysis kind other than `code-scanning` was specified in a custom workflow.",
),
);
},
);
test.serial(
"getAnalysisKinds - no error for non-default `analysis-kinds` in managed workflow",
async (t) => {
setupBaseActionsVars({ GITHUB_EVENT_NAME: "dynamic" });
process.env[EnvVar.TEST_MODE] = "false";
const features = createFeatures([]);
const logger = new RecordingLogger();
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("analysis-kinds").returns("code-quality");
const result = await getAnalysisKinds(logger, features, true);
t.deepEqual(result, [AnalysisKind.CodeQuality]);
t.deepEqual(logger.messages, []);
},
);
test.serial(
"getAnalysisKinds - includes `code-quality` when deprecated `quality-queries` input is used",
async (t) => {
process.env[EnvVar.TEST_MODE] = "true";
const features = createFeatures([]);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("analysis-kinds").returns("code-scanning");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("quality-queries").returns("code-quality");
const result = await getAnalysisKinds(getRunnerLogger(true), true);
const result = await getAnalysisKinds(
getRunnerLogger(true),
features,
true,
);
t.assert(result.includes(AnalysisKind.CodeScanning));
t.assert(result.includes(AnalysisKind.CodeQuality));
},
@@ -79,9 +151,12 @@ test.serial(
test.serial(
"getAnalysisKinds - throws if `analysis-kinds` input is invalid",
async (t) => {
const features = createFeatures([]);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing");
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true));
await t.throwsAsync(
getAnalysisKinds(getRunnerLogger(true), features, true),
);
},
);
@@ -98,11 +173,18 @@ for (let i = 0; i < analysisKinds.length; i++) {
test.serial(
`getAnalysisKinds - allows ${analysisKind} with ${otherAnalysis}`,
async (t) => {
setupBaseActionsVars();
process.env[EnvVar.TEST_MODE] = "true";
const features = createFeatures([]);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns([analysisKind, otherAnalysis].join(","));
const result = await getAnalysisKinds(getRunnerLogger(true), true);
const result = await getAnalysisKinds(
getRunnerLogger(true),
features,
true,
);
t.is(result.length, 2);
},
);
@@ -110,14 +192,19 @@ for (let i = 0; i < analysisKinds.length; i++) {
test.serial(
`getAnalysisKinds - throws if ${analysisKind} is enabled with ${otherAnalysis}`,
async (t) => {
setupBaseActionsVars();
const features = createFeatures([]);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns([analysisKind, otherAnalysis].join(","));
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true), {
instanceOf: ConfigurationError,
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
});
await t.throwsAsync(
getAnalysisKinds(getRunnerLogger(true), features, true),
{
instanceOf: ConfigurationError,
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
},
);
},
);
}
+58 -1
View File
@@ -2,15 +2,17 @@ import {
fixCodeQualityCategory,
getOptionalInput,
getRequiredInput,
isDynamicWorkflow,
} from "./actions-util";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Logger } from "./logging";
import {
AssessmentPayload,
BasePayload,
UploadPayload,
} from "./upload-lib/types";
import { ConfigurationError, getRequiredEnvParam } from "./util";
import { ConfigurationError, getRequiredEnvParam, isInTestMode } from "./util";
export enum AnalysisKind {
CodeScanning = "code-scanning",
@@ -64,6 +66,21 @@ export async function parseAnalysisKinds(
// Used to avoid re-parsing the input after we have done it once.
let cachedAnalysisKinds: AnalysisKind[] | undefined;
/** Determines whether `code-scanning` is the only enabled analysis kind in `analysisKinds`. */
function isOnlyCodeScanningEnabled(analysisKinds: AnalysisKind[]) {
return (
analysisKinds.length === 1 && analysisKinds[0] === AnalysisKind.CodeScanning
);
}
/** Prepends a generic message about the intended usage for `analysis-kinds` to `message`. */
function makeAnalysisKindUsageError(message: string) {
return (
"The `analysis-kinds` input is experimental and for GitHub-internal use only. " +
`Its behaviour may change at any time or be removed entirely. ${message}`
);
}
/**
* Initialises the analysis kinds for the analysis based on the `analysis-kinds` input.
* This function will also use the deprecated `quality-queries` input as an indicator to enable `code-quality`.
@@ -77,6 +94,7 @@ let cachedAnalysisKinds: AnalysisKind[] | undefined;
*/
export async function getAnalysisKinds(
logger: Logger,
features: FeatureEnablement,
skipCache: boolean = false,
): Promise<AnalysisKind[]> {
if (!skipCache && cachedAnalysisKinds !== undefined) {
@@ -87,6 +105,26 @@ export async function getAnalysisKinds(
getRequiredInput("analysis-kinds"),
);
// Log an error if we are outside of a GitHub-managed workflow and an analysis kind
// other than `code-scanning` is enabled.
if (
!isInTestMode() &&
!isDynamicWorkflow() &&
!isOnlyCodeScanningEnabled(analysisKinds)
) {
const codeQualityHint = analysisKinds.includes(AnalysisKind.CodeQuality)
? " If your intention is to use quality queries outside of Code Quality, " +
"use the `queries` input with `code-quality` instead."
: "";
logger.error(
makeAnalysisKindUsageError(
"An analysis kind other than `code-scanning` was specified in a custom workflow. " +
`This is not supported and will become a fatal error in a future version of the CodeQL Action.${codeQualityHint}`,
),
);
}
// Warn that `quality-queries` is deprecated if there is an argument for it.
const qualityQueriesInput = getOptionalInput("quality-queries");
@@ -120,6 +158,25 @@ export async function getAnalysisKinds(
}
}
// Log an error if we have multiple inputs for `analysis-kinds` outside of test mode,
// and enable only `code-scanning`.
if (
!isInTestMode() &&
analysisKinds.length > 1 &&
!(await features.getValue(Feature.AllowMultipleAnalysisKinds))
) {
logger.error(
makeAnalysisKindUsageError(
"Specifying multiple values as input is no longer supported. " +
"Continuing with only `analysis-kinds: code-scanning`.",
),
);
// Only enable Code Scanning.
cachedAnalysisKinds = [AnalysisKind.CodeScanning];
return cachedAnalysisKinds;
}
// Cache the analysis kinds and return them.
cachedAnalysisKinds = analysisKinds;
return cachedAnalysisKinds;
-90
View File
@@ -1,90 +0,0 @@
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as analyze from "./analyze";
import * as api from "./api-client";
import * as configUtils from "./config-utils";
import * as gitUtils from "./git-utils";
import * as statusReport from "./status-report";
import {
setupTests,
setupActionsVars,
mockFeatureFlagApiEndpoint,
} from "./testing-utils";
import * as util from "./util";
setupTests(test);
// This test needs to be in its own file so that ava would run it in its own
// nodejs process. The code being tested is in analyze-action.ts, which runs
// immediately on load. So the file needs to be loaded during part of the test,
// and that can happen only once per nodejs process. If multiple such tests are
// in the same test file, ava would run them in the same nodejs process, and all
// but the first test would fail.
test("analyze action with RAM & threads from environment variables", async (t) => {
// This test frequently times out on Windows with the default timeout, so we bump
// it a bit to 20s.
t.timeout(1000 * 20);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
sinon
.stub(statusReport, "createStatusReportBase")
.resolves({} as statusReport.StatusReportBase);
sinon.stub(statusReport, "sendStatusReport").resolves();
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
const gitHubVersion: util.GitHubVersion = {
type: util.GitHubVariant.DOTCOM,
};
sinon.stub(configUtils, "getConfig").resolves({
gitHubVersion,
augmentationProperties: {},
languages: [],
packs: [],
trapCaches: {},
} as unknown as configUtils.Config);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("token").returns("fake-token");
requiredInputStub.withArgs("upload-database").returns("false");
requiredInputStub.withArgs("output").returns("out");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("expect-error").returns("false");
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
mockFeatureFlagApiEndpoint(200, {});
// When there are no action inputs for RAM and threads, the action uses
// environment variables (passed down from the init action) to set RAM and
// threads usage.
process.env["CODEQL_THREADS"] = "-1";
process.env["CODEQL_RAM"] = "4992";
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
const runQueriesStub = sinon.stub(analyze, "runQueries");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const analyzeAction = require("./analyze-action");
// When analyze-action.ts loads, it runs an async function from the top
// level but does not wait for it to finish. To ensure that calls to
// runFinalize and runQueries are correctly captured by spies, we explicitly
// wait for the action promise to complete before starting verification.
await analyzeAction.runPromise;
t.assert(
runFinalizeStub.calledOnceWith(
sinon.match.any,
sinon.match.any,
"--threads=-1",
"--ram=4992",
),
);
t.assert(
runQueriesStub.calledOnceWith(
sinon.match.any,
"--ram=4992",
"--threads=-1",
),
);
});
});
-88
View File
@@ -1,88 +0,0 @@
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as analyze from "./analyze";
import * as api from "./api-client";
import * as configUtils from "./config-utils";
import * as gitUtils from "./git-utils";
import * as statusReport from "./status-report";
import {
setupTests,
setupActionsVars,
mockFeatureFlagApiEndpoint,
} from "./testing-utils";
import * as util from "./util";
setupTests(test);
// This test needs to be in its own file so that ava would run it in its own
// nodejs process. The code being tested is in analyze-action.ts, which runs
// immediately on load. So the file needs to be loaded during part of the test,
// and that can happen only once per nodejs process. If multiple such tests are
// in the same test file, ava would run them in the same nodejs process, and all
// but the first test would fail.
test("analyze action with RAM & threads from action inputs", async (t) => {
t.timeout(1000 * 20);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
sinon
.stub(statusReport, "createStatusReportBase")
.resolves({} as statusReport.StatusReportBase);
sinon.stub(statusReport, "sendStatusReport").resolves();
const gitHubVersion: util.GitHubVersion = {
type: util.GitHubVariant.DOTCOM,
};
sinon.stub(configUtils, "getConfig").resolves({
gitHubVersion,
augmentationProperties: {},
languages: [],
packs: [],
trapCaches: {},
} as unknown as configUtils.Config);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("token").returns("fake-token");
requiredInputStub.withArgs("upload-database").returns("false");
requiredInputStub.withArgs("output").returns("out");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("expect-error").returns("false");
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
mockFeatureFlagApiEndpoint(200, {});
process.env["CODEQL_THREADS"] = "1";
process.env["CODEQL_RAM"] = "4992";
// Action inputs have precedence over environment variables.
optionalInputStub.withArgs("threads").returns("-1");
optionalInputStub.withArgs("ram").returns("3012");
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
const runQueriesStub = sinon.stub(analyze, "runQueries");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const analyzeAction = require("./analyze-action");
// When analyze-action.ts loads, it runs an async function from the top
// level but does not wait for it to finish. To ensure that calls to
// runFinalize and runQueries are correctly captured by spies, we explicitly
// wait for the action promise to complete before starting verification.
await analyzeAction.runPromise;
t.assert(
runFinalizeStub.calledOnceWith(
sinon.match.any,
sinon.match.any,
"--threads=-1",
"--ram=3012",
),
);
t.assert(
runQueriesStub.calledOnceWith(
sinon.match.any,
"--ram=3012",
"--threads=-1",
),
);
});
});
+1 -3
View File
@@ -20,7 +20,7 @@ import { EnvVar } from "./environment";
import { getActionsLogger } from "./logging";
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
async function runWrapper() {
export async function runWrapper() {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
@@ -72,5 +72,3 @@ async function runWrapper() {
);
}
}
void runWrapper();
+142
View File
@@ -0,0 +1,142 @@
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as analyze from "./analyze";
import { runWrapper } from "./analyze-action";
import * as api from "./api-client";
import * as configUtils from "./config-utils";
import * as gitUtils from "./git-utils";
import * as statusReport from "./status-report";
import {
setupTests,
setupActionsVars,
mockFeatureFlagApiEndpoint,
} from "./testing-utils";
import * as util from "./util";
setupTests(test);
test.serial(
"analyze action with RAM & threads from environment variables",
async (t) => {
// This test frequently times out on Windows with the default timeout, so we bump
// it a bit to 20s.
t.timeout(1000 * 20);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
sinon
.stub(statusReport, "createStatusReportBase")
.resolves({} as statusReport.StatusReportBase);
sinon.stub(statusReport, "sendStatusReport").resolves();
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
const gitHubVersion: util.GitHubVersion = {
type: util.GitHubVariant.DOTCOM,
};
sinon.stub(configUtils, "getConfig").resolves({
gitHubVersion,
augmentationProperties: {},
languages: [],
packs: [],
trapCaches: {},
} as unknown as configUtils.Config);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("token").returns("fake-token");
requiredInputStub.withArgs("upload-database").returns("false");
requiredInputStub.withArgs("output").returns("out");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("expect-error").returns("false");
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
mockFeatureFlagApiEndpoint(200, {});
// When there are no action inputs for RAM and threads, the action uses
// environment variables (passed down from the init action) to set RAM and
// threads usage.
process.env["CODEQL_THREADS"] = "-1";
process.env["CODEQL_RAM"] = "4992";
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
const runQueriesStub = sinon.stub(analyze, "runQueries");
await runWrapper();
t.assert(
runFinalizeStub.calledOnceWith(
sinon.match.any,
sinon.match.any,
"--threads=-1",
"--ram=4992",
),
);
t.assert(
runQueriesStub.calledOnceWith(
sinon.match.any,
"--ram=4992",
"--threads=-1",
),
);
});
},
);
test.serial(
"analyze action with RAM & threads from action inputs",
async (t) => {
t.timeout(1000 * 20);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
sinon
.stub(statusReport, "createStatusReportBase")
.resolves({} as statusReport.StatusReportBase);
sinon.stub(statusReport, "sendStatusReport").resolves();
const gitHubVersion: util.GitHubVersion = {
type: util.GitHubVariant.DOTCOM,
};
sinon.stub(configUtils, "getConfig").resolves({
gitHubVersion,
augmentationProperties: {},
languages: [],
packs: [],
trapCaches: {},
} as unknown as configUtils.Config);
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("token").returns("fake-token");
requiredInputStub.withArgs("upload-database").returns("false");
requiredInputStub.withArgs("output").returns("out");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("expect-error").returns("false");
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
mockFeatureFlagApiEndpoint(200, {});
process.env["CODEQL_THREADS"] = "1";
process.env["CODEQL_RAM"] = "4992";
// Action inputs have precedence over environment variables.
optionalInputStub.withArgs("threads").returns("-1");
optionalInputStub.withArgs("ram").returns("3012");
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
const runQueriesStub = sinon.stub(analyze, "runQueries");
await runWrapper();
t.assert(
runFinalizeStub.calledOnceWith(
sinon.match.any,
sinon.match.any,
"--threads=-1",
"--ram=3012",
),
);
t.assert(
runQueriesStub.calledOnceWith(
sinon.match.any,
"--ram=3012",
"--threads=-1",
),
);
});
},
);
+6 -11
View File
@@ -28,7 +28,7 @@ import {
DependencyCacheUploadStatusReport,
uploadDependencyCaches,
} from "./dependency-caching";
import { EnvVar } from "./environment";
import { EnvVar, exportVariable } from "./environment";
import { initFeatures } from "./feature-flags";
import { BuiltInLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
@@ -284,7 +284,7 @@ async function run(startedAt: Date) {
const apiDetails = getApiDetails();
const outputDir = actionsUtil.getRequiredInput("output");
core.exportVariable(EnvVar.SARIF_RESULTS_OUTPUT_DIR, outputDir);
exportVariable(EnvVar.SARIF_RESULTS_OUTPUT_DIR, outputDir);
const threads = util.getThreadsFlag(
actionsUtil.getOptionalInput("threads") || process.env["CODEQL_THREADS"],
logger,
@@ -444,7 +444,7 @@ async function run(startedAt: Date) {
`expect-error input was set to true but no error was thrown.`,
);
}
core.exportVariable(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY, "true");
exportVariable(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY, "true");
} catch (unwrappedError) {
const error = util.wrapError(unwrappedError);
if (
@@ -523,14 +523,11 @@ async function run(startedAt: Date) {
}
}
// Module-level startedAt so it can be accessed by runWrapper for error reporting
const startedAt = new Date();
export const runPromise = run(startedAt);
async function runWrapper() {
export async function runWrapper() {
const startedAt = new Date();
const logger = getActionsLogger();
try {
await runPromise;
await run(startedAt);
} catch (error) {
core.setFailed(`analyze action failed: ${util.getErrorMessage(error)}`);
await sendUnhandledErrorStatusReport(
@@ -542,5 +539,3 @@ async function runWrapper() {
}
await util.checkForTimeout();
}
void runWrapper();
+24 -7
View File
@@ -3,7 +3,7 @@ import * as githubUtils from "@actions/github/lib/utils";
import * as retry from "@octokit/plugin-retry";
import { getActionVersion, getRequiredInput } from "./actions-util";
import { EnvVar } from "./environment";
import { EnvVar, exportVariable } from "./environment";
import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
import {
@@ -128,6 +128,8 @@ export async function getGitHubVersionFromApi(
// Doesn't strictly have to be the meta endpoint as we're only
// using the response headers which are available on every request.
//
// See https://docs.github.com/en/rest/meta/meta#get-github-meta-information.
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const response = await apiClient.rest.meta.get();
@@ -164,6 +166,9 @@ export async function getGitHubVersion(): Promise<GitHubVersion> {
/**
* Get the path of the currently executing workflow relative to the repository root.
*
* See https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run
* and https://docs.github.com/en/rest/actions/workflows#get-a-workflow.
*/
export async function getWorkflowRelativePath(): Promise<string> {
const repo_nwo = getRepositoryNwo();
@@ -211,7 +216,7 @@ export async function getAnalysisKey(): Promise<string> {
const jobName = getRequiredEnvParam("GITHUB_JOB");
analysisKey = `${workflowPath}:${jobName}`;
core.exportVariable(EnvVar.ANALYSIS_KEY, analysisKey);
exportVariable(EnvVar.ANALYSIS_KEY, analysisKey);
return analysisKey;
}
@@ -252,9 +257,13 @@ export interface ActionsCacheItem {
size_in_bytes?: number;
}
/** List all Actions cache entries matching the provided key and ref. */
/**
* List all Actions cache entries starting with the provided key prefix and matching the provided ref.
*
* See https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository.
*/
export async function listActionsCaches(
key: string,
keyPrefix: string,
ref?: string,
): Promise<ActionsCacheItem[]> {
const repositoryNwo = getRepositoryNwo();
@@ -264,13 +273,17 @@ export async function listActionsCaches(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
key,
key: keyPrefix,
ref,
},
);
}
/** Delete an Actions cache item by its ID. */
/**
* Delete an Actions cache item by its ID.
*
* See https://docs.github.com/en/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id.
*/
export async function deleteActionsCache(id: number) {
const repositoryNwo = getRepositoryNwo();
@@ -281,7 +294,11 @@ export async function deleteActionsCache(id: number) {
});
}
/** Retrieve all custom repository properties. */
/**
* Retrieve all custom repository properties.
*
* See https://docs.github.com/en/rest/repos/custom-properties#get-all-custom-property-values-for-a-repository.
*/
export async function getRepositoryProperties(repositoryNwo: RepositoryNwo) {
return getApiClient().request("GET /repos/:owner/:repo/properties/values", {
owner: repositoryNwo.owner,
+1 -1
View File
@@ -141,9 +141,9 @@ test("scanArtifactsForTokens handles files without tokens", async (t) => {
}
});
// `scanArchiveFile` does not support Windows, so we skip this test there.
if (os.platform() !== "win32") {
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
t.timeout(15000); // 15 seconds
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages, { logToConsole: false });
// The zip here is a regression test based on
+4
View File
@@ -156,6 +156,10 @@ async function scanArchiveFile(
);
}
if (process.platform === "win32") {
throw new Error("Scanning archives is not supported on Windows.");
}
const result: ScanResult = {
scannedFiles: 0,
findings: [],
+3 -5
View File
@@ -9,7 +9,7 @@ import { getGitHubVersion } from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
import { EnvVar } from "./environment";
import { EnvVar, exportVariable } from "./environment";
import { Language } from "./languages";
import { Logger, getActionsLogger } from "./logging";
import {
@@ -137,12 +137,12 @@ async function run(startedAt: Date) {
return;
}
core.exportVariable(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true");
exportVariable(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true");
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
}
async function runWrapper() {
export async function runWrapper() {
const startedAt = new Date();
const logger = getActionsLogger();
try {
@@ -157,5 +157,3 @@ async function runWrapper() {
);
}
}
void runWrapper();
+5 -7
View File
@@ -1,11 +1,9 @@
import * as core from "@actions/core";
import { getTemporaryDirectory, getWorkflowEventName } from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { EnvVar, exportVariable } from "./environment";
import { Feature, featureConfig, initFeatures } from "./feature-flags";
import { BuiltInLanguage, Language } from "./languages";
import { Logger } from "./logging";
@@ -136,16 +134,16 @@ export async function setupCppAutobuild(codeql: CodeQL, logger: Logger) {
: ""
}`,
);
core.exportVariable(envVar, "false");
exportVariable(envVar, "false");
} else {
logger.info(
`Enabling ${featureName}. This can be disabled by setting the ${envVar} environment variable to 'false'. See ${DocUrl.DEFINE_ENV_VARIABLES} for more information.`,
);
core.exportVariable(envVar, "true");
exportVariable(envVar, "true");
}
} else {
logger.info(`Disabling ${featureName}.`);
core.exportVariable(envVar, "false");
exportVariable(envVar, "false");
}
}
@@ -165,7 +163,7 @@ export async function runAutobuild(
await codeQL.runAutobuild(config, language);
}
if (language === BuiltInLanguage.go) {
core.exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true");
exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true");
}
logger.endGroup();
}
+58 -38
View File
@@ -33,6 +33,7 @@ import {
mockBundleDownloadApi,
makeVersionInfo,
createTestConfig,
makeMacro,
} from "./testing-utils";
import { ToolsDownloadStatusReport } from "./tools-download";
import * as util from "./util";
@@ -70,8 +71,10 @@ async function installIntoToolcache({
tmpDir,
util.GitHubVariant.GHES,
cliVersion !== undefined
? { cliVersion, tagName }
? { enabledVersions: [{ cliVersion, tagName }] }
: SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
createFeatures([]),
getRunnerLogger(true),
false,
@@ -143,6 +146,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -175,6 +180,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -214,6 +221,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -264,6 +273,8 @@ for (const {
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -284,11 +295,11 @@ for (const {
for (const toolcacheVersion of [
// Test that we use the tools from the toolcache when `SAMPLE_DEFAULT_CLI_VERSION` is requested
// and `SAMPLE_DEFAULT_CLI_VERSION-` is in the toolcache.
SAMPLE_DEFAULT_CLI_VERSION.cliVersion,
`${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}-20230101`,
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
`${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion}-20230101`,
]) {
test.serial(
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.cliVersion} is requested and ` +
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion} is requested and ` +
`${toolcacheVersion} is installed`,
async (t) => {
const features = createFeatures([]);
@@ -308,11 +319,16 @@ for (const toolcacheVersion of [
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
);
t.is(result.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
t.is(
result.toolsVersion,
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
);
t.is(result.toolsSource, ToolsSource.Toolcache);
t.is(result.toolsDownloadStatusReport?.combinedDurationMs, undefined);
t.is(result.toolsDownloadStatusReport?.downloadDurationMs, undefined);
@@ -342,9 +358,15 @@ test.serial(
tmpDir,
util.GitHubVariant.GHES,
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
],
},
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -384,9 +406,15 @@ test.serial(
tmpDir,
util.GitHubVariant.GHES,
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
],
},
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -426,6 +454,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -467,6 +497,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -540,7 +572,7 @@ test.serial("getExtraOptions throws for bad content", (t) => {
});
// Test macro for ensuring different variants of injected augmented configurations
const injectedConfigMacro = test.macro({
const injectedConfigMacro = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
augmentationProperties: AugmentationProperties,
@@ -590,9 +622,8 @@ const injectedConfigMacro = test.macro({
`databaseInitCluster() injected config: ${providedTitle}`,
});
test.serial(
injectedConfigMacro.serial(
"basic",
injectedConfigMacro,
{
...defaultAugmentationProperties,
},
@@ -600,9 +631,8 @@ test.serial(
{},
);
test.serial(
injectedConfigMacro.serial(
"injected packs from input",
injectedConfigMacro,
{
...defaultAugmentationProperties,
packsInput: ["xxx", "yyy"],
@@ -613,9 +643,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected packs from input with existing packs combines",
injectedConfigMacro,
{
...defaultAugmentationProperties,
packsInputCombines: true,
@@ -635,9 +664,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected packs from input with existing packs overrides",
injectedConfigMacro,
{
...defaultAugmentationProperties,
packsInput: ["xxx", "yyy"],
@@ -655,9 +683,8 @@ test.serial(
);
// similar, but with queries
test.serial(
injectedConfigMacro.serial(
"injected queries from input",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInput: [{ uses: "xxx" }, { uses: "yyy" }],
@@ -675,9 +702,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries from input overrides",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInput: [{ uses: "xxx" }, { uses: "yyy" }],
@@ -699,9 +725,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries from input combines",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -727,9 +752,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries from input combines 2",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -749,9 +773,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries and packs, but empty",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -768,9 +791,8 @@ test.serial(
{},
);
test.serial(
injectedConfigMacro.serial(
"repo property queries have the highest precedence",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -790,9 +812,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"repo property queries combines with queries input",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: false,
@@ -817,9 +838,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"repo property queries combines everything else",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -1052,7 +1072,7 @@ test.serial(
);
test.serial(
"Avoids duplicating --overwrite flag if specified in CODEQL_ACTION_EXTRA_OPTIONS",
"Avoids duplicating --force-overwrite flag if specified in CODEQL_ACTION_EXTRA_OPTIONS",
async (t) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await stubCodeql();
@@ -1060,7 +1080,7 @@ test.serial(
sinon.stub(io, "which").resolves("");
process.env["CODEQL_ACTION_EXTRA_OPTIONS"] =
'{ "database": { "init": ["--overwrite"] } }';
'{ "database": { "init": ["--force-overwrite"] } }';
await codeqlObject.databaseInitCluster(
stubConfig,
@@ -1073,9 +1093,9 @@ test.serial(
t.true(runnerConstructorStub.calledOnce);
const args = runnerConstructorStub.firstCall.args[1] as string[];
t.is(
args.filter((option: string) => option === "--overwrite").length,
args.filter((option: string) => option === "--force-overwrite").length,
1,
"--overwrite should only be passed once",
"--force-overwrite should only be passed once",
);
// Clean up
+21 -20
View File
@@ -15,7 +15,7 @@ import * as api from "./api-client";
import { CliError, wrapCliConfigurationError } from "./cli-errors";
import { appendExtraQueryExclusions, type Config } from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { EnvVar, exportVariable } from "./environment";
import {
CodeQLDefaultVersionInfo,
Feature,
@@ -277,7 +277,7 @@ let cachedCodeQL: CodeQL | undefined = undefined;
* The version flags below can be used to conditionally enable certain features
* on versions newer than this.
*/
const CODEQL_MINIMUM_VERSION = "2.17.6";
const CODEQL_MINIMUM_VERSION = "2.19.4";
/**
* This version will shortly become the oldest version of CodeQL that the Action will run with.
@@ -305,6 +305,8 @@ const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
* @param tempDir
* @param variant
* @param defaultCliVersion
* @param rawLanguages Raw set of languages.
* @param useOverlayAwareDefaultCliVersion Whether to select an overlay-aware default CLI version.
* @param features Information about the features that are enabled.
* @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum
@@ -317,6 +319,8 @@ export async function setupCodeQL(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
useOverlayAwareDefaultCliVersion: boolean,
features: FeatureEnablement,
logger: Logger,
checkVersion: boolean,
@@ -340,6 +344,8 @@ export async function setupCodeQL(
tempDir,
variant,
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
);
@@ -586,13 +592,6 @@ async function getCodeQLForCmd(
extraArgs.push(`--qlconfig-file=${qlconfigFile}`);
}
const overwriteFlag = isSupportedToolsFeature(
await this.getVersion(),
ToolsFeature.ForceOverwrite,
)
? "--force-overwrite"
: "--overwrite";
const overlayDatabaseMode = config.overlayDatabaseMode;
if (overlayDatabaseMode === OverlayDatabaseMode.Overlay) {
const overlayChangesFile = await writeOverlayChangesFile(
@@ -619,7 +618,7 @@ async function getCodeQLForCmd(
"init",
...(overlayDatabaseMode === OverlayDatabaseMode.Overlay
? []
: [overwriteFlag]),
: ["--force-overwrite"]),
"--db-cluster",
config.dbLocation,
`--source-root=${sourceRoot}`,
@@ -630,7 +629,14 @@ async function getCodeQLForCmd(
// Some user configs specify `--no-calculate-baseline` as an additional
// argument to `codeql database init`. Therefore ignore the baseline file
// options here to avoid specifying the same argument twice and erroring.
ignoringOptions: ["--overwrite", ...baselineFilesOptions],
//
// Ignore `--overwrite` to avoid passing both `--force-overwrite` and `--overwrite` if
// the user has configured `--overwrite`.
ignoringOptions: [
"--force-overwrite",
"--overwrite",
...baselineFilesOptions,
],
}),
],
{ stdin: externalRepositoryToken },
@@ -847,7 +853,7 @@ async function getCodeQLForCmd(
"--sarif-group-rules-by-pack",
"--sarif-include-query-help=always",
"--sublanguage-file-coverage",
...(await getJobRunUuidSarifOptions(this)),
...(await getJobRunUuidSarifOptions()),
...getExtraOptionsFromEnv(["database", "interpret-results"]),
];
if (sarifRunPropertyFlag !== undefined) {
@@ -1090,7 +1096,7 @@ async function getCodeQLForCmd(
}' by 'github/codeql-action/*@v${getActionVersion()}' in your code scanning workflow to ` +
"continue using this version of the CodeQL Action.",
);
core.exportVariable(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING, "true");
exportVariable(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING, "true");
}
return codeql;
}
@@ -1277,13 +1283,8 @@ function applyAutobuildAzurePipelinesTimeoutFix() {
].join(" ");
}
async function getJobRunUuidSarifOptions(codeql: CodeQL) {
async function getJobRunUuidSarifOptions() {
const jobRunUuid = process.env[EnvVar.JOB_RUN_UUID];
return jobRunUuid &&
(await codeql.supportsFeature(
ToolsFeature.DatabaseInterpretResultsSupportsSarifRunProperty,
))
? [`--sarif-run-property=jobRunUuid=${jobRunUuid}`]
: [];
return jobRunUuid ? [`--sarif-run-property=jobRunUuid=${jobRunUuid}`] : [];
}
+155 -127
View File
@@ -21,6 +21,7 @@ import { GitVersionInfo } from "./git-utils";
import { BuiltInLanguage, Language } from "./languages";
import { getRunnerLogger } from "./logging";
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
import * as overlayDiagnostics from "./overlay/diagnostics";
import { OverlayDisabledReason } from "./overlay/diagnostics";
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
import * as overlayStatus from "./overlay/status";
@@ -34,6 +35,7 @@ import {
LoggedMessage,
mockCodeQLVersion,
createTestConfig,
makeMacro,
} from "./testing-utils";
import {
GitHubVariant,
@@ -1034,10 +1036,9 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
repositoryProperties: {},
};
const checkOverlayEnablementMacro = test.macro({
const checkOverlayEnablementMacro = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
setupOverrides: Partial<OverlayDatabaseModeTestSetup>,
expected:
| {
@@ -1131,11 +1132,10 @@ const checkOverlayEnablementMacro = test.macro({
}
});
},
title: (_, title) => `checkOverlayEnablement: ${title}`,
title: (title) => `checkOverlayEnablement: ${title}`,
});
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override - Overlay",
{
overlayDatabaseEnvVar: "overlay",
@@ -1146,8 +1146,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override - OverlayBase",
{
overlayDatabaseEnvVar: "overlay-base",
@@ -1158,8 +1157,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override - None",
{
overlayDatabaseEnvVar: "none",
@@ -1169,8 +1167,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Ignore invalid environment variable",
{
overlayDatabaseEnvVar: "invalid-mode",
@@ -1180,8 +1177,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Ignore feature flag when analyzing non-default branch",
{
languages: [BuiltInLanguage.javascript],
@@ -1192,8 +1188,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch when feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1206,15 +1201,14 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch when feature enabled with custom analysis",
{
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1223,8 +1217,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch when code-scanning feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1240,8 +1233,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if runner disk space is too low",
{
languages: [BuiltInLanguage.javascript],
@@ -1260,8 +1252,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if we can't determine runner disk space",
{
languages: [BuiltInLanguage.javascript],
@@ -1277,8 +1268,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch if runner disk space is too low and skip resource checks flag is enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1299,8 +1289,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if runner disk space is below v2 limit and v2 resource checks enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1320,8 +1309,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch if runner disk space is between v2 and v1 limits and v2 resource checks enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1342,8 +1330,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if runner disk space is between v2 and v1 limits and v2 resource checks not enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1362,8 +1349,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if memory flag is too low",
{
languages: [BuiltInLanguage.javascript],
@@ -1379,8 +1365,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch if memory flag is too low but CodeQL >= 2.24.3",
{
languages: [BuiltInLanguage.javascript],
@@ -1398,8 +1383,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch if memory flag is too low and skip resource checks flag is enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1417,8 +1401,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when cached status indicates previous failure",
{
languages: [BuiltInLanguage.javascript],
@@ -1435,8 +1418,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when cached status indicates previous failure",
{
languages: [BuiltInLanguage.javascript],
@@ -1453,8 +1435,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with disable-default-queries",
{
languages: [BuiltInLanguage.javascript],
@@ -1464,7 +1445,7 @@ test.serial(
],
codeScanningConfig: {
"disable-default-queries": true,
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1472,8 +1453,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with packs",
{
languages: [BuiltInLanguage.javascript],
@@ -1483,7 +1463,7 @@ test.serial(
],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1491,8 +1471,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with queries",
{
languages: [BuiltInLanguage.javascript],
@@ -1502,7 +1481,7 @@ test.serial(
],
codeScanningConfig: {
queries: [{ uses: "some-query.ql" }],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1510,8 +1489,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with query-filters",
{
languages: [BuiltInLanguage.javascript],
@@ -1521,7 +1499,7 @@ test.serial(
],
codeScanningConfig: {
"query-filters": [{ include: { "security-severity": "high" } }],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1529,8 +1507,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when only language-specific feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1542,8 +1519,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when only code-scanning feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1555,8 +1531,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when language-specific feature disabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1568,8 +1543,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR when feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1582,15 +1556,14 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR when feature enabled with custom analysis",
{
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1599,8 +1572,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR when code-scanning feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1616,8 +1588,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR if runner disk space is too low",
{
languages: [BuiltInLanguage.javascript],
@@ -1636,8 +1607,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR if runner disk space is too low and skip resource checks flag is enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1658,8 +1628,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR if we can't determine runner disk space",
{
languages: [BuiltInLanguage.javascript],
@@ -1675,8 +1644,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR if memory flag is too low",
{
languages: [BuiltInLanguage.javascript],
@@ -1692,8 +1660,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR if memory flag is too low but CodeQL >= 2.24.3",
{
languages: [BuiltInLanguage.javascript],
@@ -1711,8 +1678,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR if memory flag is too low and skip resource checks flag is enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1730,8 +1696,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with disable-default-queries",
{
languages: [BuiltInLanguage.javascript],
@@ -1741,7 +1706,7 @@ test.serial(
],
codeScanningConfig: {
"disable-default-queries": true,
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1749,8 +1714,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with packs",
{
languages: [BuiltInLanguage.javascript],
@@ -1760,7 +1724,7 @@ test.serial(
],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1768,8 +1732,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with queries",
{
languages: [BuiltInLanguage.javascript],
@@ -1779,7 +1742,7 @@ test.serial(
],
codeScanningConfig: {
queries: [{ uses: "some-query.ql" }],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1787,8 +1750,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with query-filters",
{
languages: [BuiltInLanguage.javascript],
@@ -1798,7 +1760,7 @@ test.serial(
],
codeScanningConfig: {
"query-filters": [{ include: { "security-severity": "high" } }],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1806,8 +1768,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when only language-specific feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1819,8 +1780,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when only code-scanning feature enabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1832,8 +1792,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when language-specific feature disabled",
{
languages: [BuiltInLanguage.javascript],
@@ -1845,8 +1804,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay PR analysis by env",
{
overlayDatabaseEnvVar: "overlay",
@@ -1857,8 +1815,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay PR analysis by env on a runner with low disk space",
{
overlayDatabaseEnvVar: "overlay",
@@ -1870,8 +1827,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay PR analysis by feature flag",
{
languages: [BuiltInLanguage.javascript],
@@ -1884,8 +1840,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to autobuild with traced language",
{
overlayDatabaseEnvVar: "overlay",
@@ -1897,8 +1852,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to no build mode with traced language",
{
overlayDatabaseEnvVar: "overlay",
@@ -1910,8 +1864,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to old CodeQL version",
{
overlayDatabaseEnvVar: "overlay",
@@ -1922,8 +1875,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to missing git root",
{
overlayDatabaseEnvVar: "overlay",
@@ -1934,8 +1886,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to old git version with submodules",
{
overlayDatabaseEnvVar: "overlay",
@@ -1947,8 +1898,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback when git version cannot be determined and repo has submodules",
{
overlayDatabaseEnvVar: "overlay",
@@ -1960,8 +1910,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay enabled when git version cannot be determined and repo has no submodules",
{
overlayDatabaseEnvVar: "overlay",
@@ -1974,8 +1923,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay when disabled via repository property",
{
languages: [BuiltInLanguage.javascript],
@@ -1990,8 +1938,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay not disabled when repository property is false",
{
languages: [BuiltInLanguage.javascript],
@@ -2007,8 +1954,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override takes precedence over repository property",
{
overlayDatabaseEnvVar: "overlay",
@@ -2024,8 +1970,7 @@ test.serial(
// Exercise language-specific overlay analysis features code paths
for (const language in BuiltInLanguage) {
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
`Check default overlay analysis feature for ${language}`,
{
languages: [language],
@@ -2042,8 +1987,7 @@ for (const language in BuiltInLanguage) {
// overlay analysis enabled, even when the base overlay feature flag is on.
// Using swift here as it doesn't currently have overlay support — update this if
// swift gains overlay support.
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis for language without per-language overlay feature flag",
{
languages: [BuiltInLanguage.swift],
@@ -2200,3 +2144,87 @@ test.serial(
});
},
);
test("applyIncrementalAnalysisSettings: no-op when mode is not Overlay and diff ranges are unavailable", async (t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const codeql = createStubCodeQL({});
const logger = getRunnerLogger(true);
await configUtils.applyIncrementalAnalysisSettings(
config,
false,
codeql,
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, []);
});
test("applyIncrementalAnalysisSettings: keeps overlay mode and adds exclusions when diff ranges are available", async (t) => {
const config = createTestConfig({
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
});
const codeql = createStubCodeQL({});
const logger = getRunnerLogger(true);
await configUtils.applyIncrementalAnalysisSettings(
config,
true,
codeql,
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.Overlay);
t.deepEqual(config.extraQueryExclusions, [
{ exclude: { tags: "exclude-from-incremental" } },
]);
});
test("applyIncrementalAnalysisSettings: disables overlay analysis when diff ranges are unavailable", async (t) => {
const config = createTestConfig({
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
});
config.useOverlayDatabaseCaching = true;
const codeql = createStubCodeQL({});
const logger = getRunnerLogger(true);
const addDiagnosticsStub = sinon
.stub(overlayDiagnostics, "addOverlayDisablementDiagnostics")
.resolves();
await configUtils.applyIncrementalAnalysisSettings(
config,
false,
codeql,
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.is(config.useOverlayDatabaseCaching, false);
t.deepEqual(config.extraQueryExclusions, []);
t.true(addDiagnosticsStub.calledOnce);
t.is(
addDiagnosticsStub.firstCall.args[2],
OverlayDisabledReason.DiffInformedAnalysisNotEnabled,
);
});
test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only runs", async (t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const codeql = createStubCodeQL({});
const logger = getRunnerLogger(true);
await configUtils.applyIncrementalAnalysisSettings(
config,
true,
codeql,
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, [
{ exclude: { tags: "exclude-from-incremental" } },
]);
});
+59 -17
View File
@@ -2,7 +2,6 @@ import * as fs from "fs";
import * as path from "path";
import { performance } from "perf_hooks";
import * as core from "@actions/core";
import * as yaml from "js-yaml";
import {
@@ -31,8 +30,8 @@ import {
addNoLanguageDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { prepareDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import { EnvVar, exportVariable } from "./environment";
import * as errorMessages from "./error-messages";
import { Feature, FeatureEnablement } from "./feature-flags";
import {
@@ -407,6 +406,7 @@ export async function getLanguages(
return languages;
}
/** Splits the `languages` input into a list of raw languages without checking if they are supported by CodeQL. */
export function getRawLanguagesNoAutodetect(
languagesInput: string | undefined,
): string[] {
@@ -1044,10 +1044,10 @@ async function setCppTrapCachingEnvironmentVariables(
);
} else if (config.trapCaches[BuiltInLanguage.cpp]) {
logger.info("Enabling TRAP caching for C/C++.");
core.exportVariable(envVar, "true");
exportVariable(envVar, "true");
} else {
logger.debug(`Disabling TRAP caching for C/C++.`);
core.exportVariable(envVar, "false");
exportVariable(envVar, "false");
}
}
}
@@ -1076,6 +1076,48 @@ function hasQueryCustomisation(userConfig: UserConfig): boolean {
);
}
/**
* Finalize the incremental-analysis configuration for this run.
*
* Overlay analysis has only been validated in combination with diff-informed
* analysis, so if `Overlay` mode was selected for a pull request but the diff
* ranges could not be computed, fall back to a full non-overlay analysis.
*
* Query exclusions for incremental-only queries are then applied whenever the
* diff ranges are available — which, after the fallback above, is exactly the
* set of runs where any kind of incremental analysis (overlay or
* diff-informed) is in effect.
*/
export async function applyIncrementalAnalysisSettings(
config: Config,
hasDiffRanges: boolean,
codeql: CodeQL,
logger: Logger,
): Promise<void> {
if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay &&
!hasDiffRanges
) {
logger.info(
`Reverting overlay database mode to ${OverlayDatabaseMode.None} ` +
"because the PR diff ranges could not be computed.",
);
config.overlayDatabaseMode = OverlayDatabaseMode.None;
config.useOverlayDatabaseCaching = false;
await addOverlayDisablementDiagnostics(
config,
codeql,
OverlayDisabledReason.DiffInformedAnalysisNotEnabled,
);
}
if (hasDiffRanges) {
config.extraQueryExclusions.push({
exclude: { tags: "exclude-from-incremental" },
});
}
}
/**
* Load and return the config.
*
@@ -1230,18 +1272,18 @@ export async function initConfig(
);
}
if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
(await shouldPerformDiffInformedAnalysis(
inputs.codeql,
inputs.features,
logger,
))
) {
config.extraQueryExclusions.push({
exclude: { tags: "exclude-from-incremental" },
});
}
const hasDiffRanges = await prepareDiffInformedAnalysis(
inputs.codeql,
inputs.features,
logger,
);
await applyIncrementalAnalysisSettings(
config,
hasDiffRanges,
inputs.codeql,
logger,
);
if (await isTrapCachingEnabled(features, config.overlayDatabaseMode)) {
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
+48 -65
View File
@@ -7,6 +7,7 @@ import {
checkExpectedLogMessages,
getRecordingLogger,
LoggedMessage,
makeMacro,
} from "../testing-utils";
import { ConfigurationError, prettyPrintPack } from "../util";
@@ -15,7 +16,7 @@ import * as dbConfig from "./db-config";
/**
* Test macro for ensuring the packs block is valid
*/
const parsePacksMacro = test.macro({
const parsePacksMacro = makeMacro({
exec: (
t: ExecutionContext<unknown>,
packsInput: string,
@@ -33,7 +34,7 @@ const parsePacksMacro = test.macro({
/**
* Test macro for testing when the packs block is invalid
*/
const parsePacksErrorMacro = test.macro({
const parsePacksErrorMacro = makeMacro({
exec: (
t: ExecutionContext<unknown>,
packsInput: string,
@@ -49,34 +50,32 @@ const parsePacksErrorMacro = test.macro({
/**
* Test macro for testing when the packs block is invalid
*/
const invalidPackNameMacro = test.macro({
exec: (t: ExecutionContext, name: string) =>
parsePacksErrorMacro.exec(
const invalidPackNameMacro = makeMacro({
exec: (t: ExecutionContext, arg: string) =>
parsePacksErrorMacro.fn(
t,
name,
arg,
[BuiltInLanguage.cpp],
new RegExp(`^"${name}" is not a valid pack$`),
new RegExp(`^"${arg}" is not a valid pack$`),
),
title: (_providedTitle: string | undefined, arg: string | undefined) =>
`Invalid pack string: ${arg}`,
});
test("no packs", parsePacksMacro, "", [], undefined);
test("two packs", parsePacksMacro, "a/b,c/d@1.2.3", [BuiltInLanguage.cpp], {
parsePacksMacro("no packs", "", [], undefined);
parsePacksMacro("two packs", "a/b,c/d@1.2.3", [BuiltInLanguage.cpp], {
[BuiltInLanguage.cpp]: ["a/b", "c/d@1.2.3"],
});
test(
parsePacksMacro(
"two packs with spaces",
parsePacksMacro,
" a/b , c/d@1.2.3 ",
[BuiltInLanguage.cpp],
{
[BuiltInLanguage.cpp]: ["a/b", "c/d@1.2.3"],
},
);
test(
parsePacksErrorMacro(
"two packs with language",
parsePacksErrorMacro,
"a/b,c/d@1.2.3",
[BuiltInLanguage.cpp, BuiltInLanguage.java],
new RegExp(
@@ -85,9 +84,8 @@ test(
),
);
test(
parsePacksMacro(
"packs with other valid names",
parsePacksMacro,
[
// ranges are ok
"c/d@1.0",
@@ -123,23 +121,23 @@ test(
},
);
test(invalidPackNameMacro, "c"); // all packs require at least a scope and a name
test(invalidPackNameMacro, "c-/d");
test(invalidPackNameMacro, "-c/d");
test(invalidPackNameMacro, "c/d_d");
test(invalidPackNameMacro, "c/d@@");
test(invalidPackNameMacro, "c/d@1.0.0:");
test(invalidPackNameMacro, "c/d:");
test(invalidPackNameMacro, "c/d:/a");
test(invalidPackNameMacro, "@1.0.0:a");
test(invalidPackNameMacro, "c/d@../a");
test(invalidPackNameMacro, "c/d@b/../a");
test(invalidPackNameMacro, "c/d:z@1");
invalidPackNameMacro.test("c"); // all packs require at least a scope and a name
invalidPackNameMacro.test("c-/d");
invalidPackNameMacro.test("-c/d");
invalidPackNameMacro.test("c/d_d");
invalidPackNameMacro.test("c/d@@");
invalidPackNameMacro.test("c/d@1.0.0:");
invalidPackNameMacro.test("c/d:");
invalidPackNameMacro.test("c/d:/a");
invalidPackNameMacro.test("@1.0.0:a");
invalidPackNameMacro.test("c/d@../a");
invalidPackNameMacro.test("c/d@b/../a");
invalidPackNameMacro.test("c/d:z@1");
/**
* Test macro for pretty printing pack specs
*/
const packSpecPrettyPrintingMacro = test.macro({
const packSpecPrettyPrintingMacro = makeMacro({
exec: (t: ExecutionContext, packStr: string, packObj: dbConfig.Pack) => {
const parsed = dbConfig.parsePacksSpecification(packStr);
t.deepEqual(parsed, packObj, "parsed pack spec is correct");
@@ -163,36 +161,35 @@ const packSpecPrettyPrintingMacro = test.macro({
) => `Prettyprint pack spec: '${packStr}'`,
});
test(packSpecPrettyPrintingMacro, "a/b", {
packSpecPrettyPrintingMacro.test("a/b", {
name: "a/b",
version: undefined,
path: undefined,
});
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3", {
packSpecPrettyPrintingMacro.test("a/b@~1.2.3", {
name: "a/b",
version: "~1.2.3",
path: undefined,
});
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3:abc/def", {
packSpecPrettyPrintingMacro.test("a/b@~1.2.3:abc/def", {
name: "a/b",
version: "~1.2.3",
path: "abc/def",
});
test(packSpecPrettyPrintingMacro, "a/b:abc/def", {
packSpecPrettyPrintingMacro.test("a/b:abc/def", {
name: "a/b",
version: undefined,
path: "abc/def",
});
test(packSpecPrettyPrintingMacro, " a/b:abc/def ", {
packSpecPrettyPrintingMacro.test(" a/b:abc/def ", {
name: "a/b",
version: undefined,
path: "abc/def",
});
const calculateAugmentationMacro = test.macro({
const calculateAugmentationMacro = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
rawPacksInput: string | undefined,
rawQueriesInput: string | undefined,
languages: Language[],
@@ -207,11 +204,10 @@ const calculateAugmentationMacro = test.macro({
);
t.deepEqual(actualAugmentationProperties, expectedAugmentationProperties);
},
title: (_, title) => `Calculate Augmentation: ${title}`,
title: (title) => `Calculate Augmentation: ${title}`,
});
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"All empty",
undefined,
undefined,
@@ -222,8 +218,7 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With queries",
undefined,
" a, b , c, d",
@@ -235,8 +230,7 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With queries combining",
undefined,
" + a, b , c, d ",
@@ -249,8 +243,7 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With packs",
" codeql/a , codeql/b , codeql/c , codeql/d ",
undefined,
@@ -262,8 +255,7 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With packs combining",
" + codeql/a, codeql/b, codeql/c, codeql/d",
undefined,
@@ -276,8 +268,7 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With repo property queries",
undefined,
undefined,
@@ -294,8 +285,7 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With repo property queries combining",
undefined,
undefined,
@@ -312,10 +302,9 @@ test(
},
);
const calculateAugmentationErrorMacro = test.macro({
const calculateAugmentationErrorMacro = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
rawPacksInput: string | undefined,
rawQueriesInput: string | undefined,
languages: Language[],
@@ -333,11 +322,10 @@ const calculateAugmentationErrorMacro = test.macro({
{ message: expectedError },
);
},
title: (_, title) => `Calculate Augmentation Error: ${title}`,
title: (title) => `Calculate Augmentation Error: ${title}`,
});
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Plus (+) with nothing else (queries)",
undefined,
" + ",
@@ -346,8 +334,7 @@ test(
/The workflow property "queries" is invalid/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Plus (+) with nothing else (packs)",
" + ",
undefined,
@@ -356,8 +343,7 @@ test(
/The workflow property "packs" is invalid/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Plus (+) with nothing else (repo property queries)",
undefined,
undefined,
@@ -368,8 +354,7 @@ test(
/The repository property "github-codeql-extra-queries" is invalid/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Packs input with multiple languages",
" + a/b, c/d ",
undefined,
@@ -378,8 +363,7 @@ test(
/Cannot specify a 'packs' input in a multi-language analysis/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Packs input with no languages",
" + a/b, c/d ",
undefined,
@@ -388,8 +372,7 @@ test(
/No languages specified/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Invalid packs",
" a-pack-without-a-scope ",
undefined,
+3 -3
View File
@@ -11,7 +11,7 @@ import { dbIsFinalized } from "./analyze";
import { scanArtifactsForTokens } from "./artifact-scanner";
import { type CodeQL } from "./codeql";
import { Config } from "./config-utils";
import { EnvVar } from "./environment";
import { EnvVar, exportVariable } from "./environment";
import * as json from "./json";
import { Language } from "./languages";
import { Logger, withGroup } from "./logging";
@@ -263,7 +263,7 @@ export function getArtifactSuffix(matrix: string | undefined): string {
try {
const matrixObject = JSON.parse(matrix);
if (json.isObject(matrixObject)) {
for (const matrixKey of Object.keys(matrixObject as object).sort())
for (const matrixKey of Object.keys(matrixObject).sort())
suffix += `-${matrixObject[matrixKey]}`;
} else {
core.warning("User-specified `matrix` input is not an object.");
@@ -330,7 +330,7 @@ export async function uploadArtifacts(
// some issues early.
if (isInTestMode()) {
await scanArtifactsForTokens(toUpload, logger);
core.exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true");
exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true");
}
const suffix = getArtifactSuffix(getOptionalInput("matrix"));
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.25.3",
"cliVersion": "2.25.3",
"priorBundleVersion": "codeql-bundle-v2.25.2",
"priorCliVersion": "2.25.2"
"bundleVersion": "codeql-bundle-v2.25.5",
"cliVersion": "2.25.5",
"priorBundleVersion": "codeql-bundle-v2.25.4",
"priorCliVersion": "2.25.4"
}
+149 -30
View File
@@ -5,17 +5,20 @@ import * as actionsUtil from "./actions-util";
import type { PullRequestBranches } from "./actions-util";
import * as apiClient from "./api-client";
import {
shouldPerformDiffInformedAnalysis,
getDiffInformedAnalysisBranches,
prepareDiffInformedAnalysis,
exportedForTesting,
} from "./diff-informed-analysis-utils";
import { Feature, initFeatures } from "./feature-flags";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import {
setupTests,
createFeatures,
mockCodeQLVersion,
mockFeatureFlagApiEndpoint,
setupActionsVars,
makeMacro,
} from "./testing-utils";
import { GitHubVariant, withTmpDir } from "./util";
import type { GitHubVersion } from "./util";
@@ -42,10 +45,9 @@ const defaultTestCase: DiffInformedAnalysisTestCase = {
codeQLVersion: "2.21.0",
};
const testShouldPerformDiffInformedAnalysis = test.macro({
const testShouldPerformDiffInformedAnalysis = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
partialTestCase: Partial<DiffInformedAnalysisTestCase>,
expectedResult: boolean,
) => {
@@ -73,39 +75,34 @@ const testShouldPerformDiffInformedAnalysis = test.macro({
[Feature.DiffInformedQueries]: testCase.featureEnabled,
});
const getGitHubVersionStub = sinon
sinon
.stub(apiClient, "getGitHubVersion")
.resolves(testCase.gitHubVersion);
const getPullRequestBranchesStub = sinon
sinon
.stub(actionsUtil, "getPullRequestBranches")
.returns(testCase.pullRequestBranches);
const result = await shouldPerformDiffInformedAnalysis(
const branches = await getDiffInformedAnalysisBranches(
codeql,
features,
logger,
);
t.is(result, expectedResult);
t.is(branches !== undefined, expectedResult);
delete process.env.CODEQL_ACTION_DIFF_INFORMED_QUERIES;
getGitHubVersionStub.restore();
getPullRequestBranchesStub.restore();
});
},
title: (_, title) => `shouldPerformDiffInformedAnalysis: ${title}`,
title: (title) => `getDiffInformedAnalysisBranches: ${title}`,
});
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns true in the default test case",
{},
true,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false when feature flag is disabled from the API",
{
featureEnabled: false,
@@ -113,8 +110,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false when CODEQL_ACTION_DIFF_INFORMED_QUERIES is set to false",
{
featureEnabled: true,
@@ -123,8 +119,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns true when CODEQL_ACTION_DIFF_INFORMED_QUERIES is set to true",
{
featureEnabled: false,
@@ -133,8 +128,7 @@ test.serial(
true,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false for CodeQL version 2.20.0",
{
codeQLVersion: "2.20.0",
@@ -142,8 +136,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false for invalid GHES version",
{
gitHubVersion: {
@@ -154,8 +147,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false for GHES version 3.18.5",
{
gitHubVersion: {
@@ -166,8 +158,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns true for GHES version 3.19.0",
{
gitHubVersion: {
@@ -178,8 +169,7 @@ test.serial(
true,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false when not a pull request",
{
pullRequestBranches: undefined,
@@ -187,6 +177,135 @@ test.serial(
false,
);
test.serial(
"prepareDiffInformedAnalysis: returns false when not a pull request",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
const features = createFeatures([Feature.DiffInformedQueries]);
sinon.stub(actionsUtil, "getPullRequestBranches").returns(undefined);
sinon
.stub(apiClient, "getGitHubVersion")
.resolves({ type: GitHubVariant.DOTCOM });
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.false(result);
});
},
);
test.serial(
"prepareDiffInformedAnalysis: returns false when applicability check throws",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
// A features implementation whose getValue rejects, simulating an
// unexpected failure when determining whether diff-informed analysis
// should run.
const features: FeatureEnablement = {
getEnabledDefaultCliVersions: async () => {
throw new Error("not implemented");
},
getValue: async () => {
throw new Error("feature flag lookup failed");
},
};
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.false(result);
});
},
);
test.serial(
"prepareDiffInformedAnalysis: returns true when the diff is fetched successfully",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
const features = createFeatures([Feature.DiffInformedQueries]);
sinon
.stub(actionsUtil, "getPullRequestBranches")
.returns({ base: "main", head: "feature" });
sinon
.stub(apiClient, "getGitHubVersion")
.resolves({ type: GitHubVariant.DOTCOM });
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
sinon.stub(apiClient, "getApiClient").returns({
rest: {
repos: {
compareCommitsWithBasehead: sinon
.stub()
.resolves({ data: { files: [] } }),
},
},
} as any);
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.true(result);
});
},
);
test.serial(
"prepareDiffInformedAnalysis: returns false when the diff API call fails",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
const features = createFeatures([Feature.DiffInformedQueries]);
sinon
.stub(actionsUtil, "getPullRequestBranches")
.returns({ base: "main", head: "feature" });
sinon
.stub(apiClient, "getGitHubVersion")
.resolves({ type: GitHubVariant.DOTCOM });
const notFoundError: any = new Error("Not Found");
notFoundError.status = 404;
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
sinon.stub(apiClient, "getApiClient").returns({
rest: {
repos: {
compareCommitsWithBasehead: sinon.stub().rejects(notFoundError),
},
},
} as any);
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.false(result);
});
},
);
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
return exportedForTesting.getDiffRanges(
{
+69 -16
View File
@@ -5,9 +5,9 @@ import type { PullRequestBranches } from "./actions-util";
import { getApiClient, getGitHubVersion } from "./api-client";
import type { CodeQL } from "./codeql";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Logger } from "./logging";
import { Logger, withGroupAsync } from "./logging";
import { getRepositoryNwoFromEnv } from "./repository";
import { GitHubVariant, satisfiesGHESVersion } from "./util";
import { getErrorMessage, GitHubVariant, satisfiesGHESVersion } from "./util";
/**
* This interface is an abbreviated version of the file diff object returned by
@@ -21,20 +21,6 @@ interface FileDiff {
patch?: string | undefined;
}
/**
* Check if the action should perform diff-informed analysis.
*/
export async function shouldPerformDiffInformedAnalysis(
codeql: CodeQL,
features: FeatureEnablement,
logger: Logger,
): Promise<boolean> {
return (
(await getDiffInformedAnalysisBranches(codeql, features, logger)) !==
undefined
);
}
/**
* Get the branches to use for diff-informed analysis.
*
@@ -69,6 +55,46 @@ export async function getDiffInformedAnalysisBranches(
return branches;
}
/**
* Prepares the diff ranges needed for diff-informed analysis for the current
* run.
*
* @returns `true` if the diff ranges were successfully computed and persisted
* and are therefore available for use, `false` otherwise.
*/
export async function prepareDiffInformedAnalysis(
codeql: CodeQL,
features: FeatureEnablement,
logger: Logger,
): Promise<boolean> {
let branches: PullRequestBranches | undefined;
try {
branches = await getDiffInformedAnalysisBranches(codeql, features, logger);
} catch (e) {
// If we cannot determine whether diff-informed analysis applies (for
// example, because a feature-flag lookup failed), treat it as not
// applicable rather than triggering the overlay fallback.
logger.warning(
`Failed to determine branch information for diff-informed analysis: ${getErrorMessage(e)}`,
);
return false;
}
if (!branches) {
return false;
}
return await withGroupAsync("Computing PR diff ranges", async () => {
try {
return await computeAndPersistDiffRanges(branches, logger);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
);
return false;
}
});
}
export interface DiffThunkRange {
/** Relative path from the repository root, using forward slashes as separators. */
path: string;
@@ -151,6 +177,33 @@ export async function getPullRequestEditedDiffRanges(
return results;
}
/**
* Compute and persist the diff ranges for a pull request. This fetches the
* diff from the GitHub API and writes it to the diff ranges JSON file so that
* CodeQL can use it for diff-informed analysis.
*
* @param branches The base and head branches of the pull request, as returned
* by `getDiffInformedAnalysisBranches`.
* @param logger
* @returns `true` if the diff ranges were successfully computed and persisted,
* otherwise `false`.
*/
export async function computeAndPersistDiffRanges(
branches: PullRequestBranches,
logger: Logger,
): Promise<boolean> {
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
if (ranges === undefined) {
return false;
}
writeDiffRangesJsonFile(logger, ranges);
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
logger.info(
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`,
);
return true;
}
async function getFileDiffsWithBasehead(
branches: PullRequestBranches,
logger: Logger,
+3
View File
@@ -0,0 +1,3 @@
export async function run__ACTION__() {
return await __ACTION__.runWrapper();
}
+28
View File
@@ -1,3 +1,5 @@
import * as core from "@actions/core";
/**
* Environment variables used by the CodeQL Action.
*
@@ -154,3 +156,29 @@ export enum EnvVar {
/** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */
RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID",
}
/**
* Returns whether we are in test mode. This is used by CodeQL Action PR checks.
*
* In test mode, we skip several uploads (SARIF results, status reports, DBs, ...).
*/
export function isInTestMode(): boolean {
return process.env[EnvVar.TEST_MODE] === "true";
}
/**
* Wrapper around `core.exportVariable` which does not call `core.exportVariable`
* when running unit tests. This is important, because otherwise `core.exportVariable`
* sets environment variables for other steps in a workflow when we run unit tests in CI.
*/
export function exportVariable(name: string, val: any): void {
if (process.env["NODE_ENV"] === "test") {
// Setting the environment variable for the current process is OK since we reset
// those at the end of each test. This allows tests to pass that rely on that
// part of the `core.exportVariable` behaviour.
process.env[name] = val;
} else {
// Call `core.exportVariable` whenever we are not in a test environment.
core.exportVariable(name, val);
}
}
+27 -12
View File
@@ -451,12 +451,16 @@ test.serial(`selects CLI from defaults.json on GHES`, async (t) => {
await withTmpDir(async (tmpDir) => {
const features = setUpFeatureFlagTests(tmpDir);
const defaultCliVersion = await features.getDefaultCliVersion(
const defaultCliVersion = await features.getEnabledDefaultCliVersions(
GitHubVariant.GHES,
);
t.deepEqual(defaultCliVersion, {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
],
});
});
});
@@ -482,10 +486,13 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
false;
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
const defaultCliVersion = await features.getDefaultCliVersion(variant);
const defaultCliVersion =
await features.getEnabledDefaultCliVersions(variant);
t.deepEqual(defaultCliVersion, {
cliVersion: "2.20.1",
tagName: "codeql-bundle-v2.20.1",
enabledVersions: [
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
],
toolsFeatureFlagsValid: true,
});
});
@@ -500,10 +507,15 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
const expectedFeatureEnablement = initializeFeatures(true);
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
const defaultCliVersion = await features.getDefaultCliVersion(variant);
const defaultCliVersion =
await features.getEnabledDefaultCliVersions(variant);
t.deepEqual(defaultCliVersion, {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
],
toolsFeatureFlagsValid: false,
});
});
@@ -529,10 +541,13 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
] = true;
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
const defaultCliVersion = await features.getDefaultCliVersion(variant);
const defaultCliVersion =
await features.getEnabledDefaultCliVersions(variant);
t.deepEqual(defaultCliVersion, {
cliVersion: "2.20.1",
tagName: "codeql-bundle-v2.20.1",
enabledVersions: [
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
],
toolsFeatureFlagsValid: true,
});
+85 -24
View File
@@ -26,12 +26,38 @@ const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
/**
* The first version of the CodeQL Bundle that shipped with zstd-compressed bundles.
*
* This is now below the minimum version of CodeQL, but we keep this around because we currently set
* up CodeQL before checking that the version is new enough.
*/
export const CODEQL_VERSION_ZSTD_BUNDLE = "2.19.0";
export interface CodeQLDefaultVersionInfo {
const LINKED_CODEQL_VERSION: CodeQLVersionInfo = {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
};
export interface CodeQLVersionInfo {
/** The version number of the CodeQL CLI, e.g. `2.19.0`. */
cliVersion: string;
/**
* The tag name of the CodeQL Bundle associated with this version, e.g. `codeql-bundle-v2.19.0`.
*/
tagName: string;
}
export interface CodeQLDefaultVersionInfo {
/**
* CodeQL CLI versions that are enabled as defaults, sorted from highest to lowest.
*
* Guaranteed to be non-empty. When feature flags are unavailable, this falls back to a single
* entry containing the version pinned in `defaults.json`.
*/
enabledVersions: CodeQLVersionInfo[];
/**
* If accessed, whether the tools feature flags are valid, i.e. contain at least one enabled
* version.
*/
toolsFeatureFlagsValid?: boolean;
}
@@ -44,6 +70,8 @@ export interface CodeQLDefaultVersionInfo {
* Legacy features should end with `_enabled`.
*/
export enum Feature {
/** Controls whether we allow multiple values for the `analysis-kinds` input. */
AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds",
AllowToolcacheInput = "allow_toolcache_input",
CleanupTrapCaches = "cleanup_trap_caches",
CppDependencyInstallation = "cpp_dependency_installation_enabled",
@@ -72,6 +100,19 @@ export enum Feature {
OverlayAnalysisGo = "overlay_analysis_go",
OverlayAnalysisJava = "overlay_analysis_java",
OverlayAnalysisJavascript = "overlay_analysis_javascript",
/**
* When set, chooses the default CodeQL CLI version as the highest version that is both enabled by
* feature flags and present as an overlay-base database in the Actions cache for the configured
* languages. Falls back to the highest feature flagged version if no intersecting overlay-base
* database exists in the cache.
*/
OverlayAnalysisMatchCodeqlVersion = "overlay_analysis_match_codeql_version",
/**
* Like `OverlayAnalysisMatchCodeqlVersion`, but only logs a diagnostic with the version that
* would have been chosen instead of actually changing the default CodeQL CLI version.
* `OverlayAnalysisMatchCodeqlVersion` overrides this flag.
*/
OverlayAnalysisMatchCodeqlVersionDryRun = "overlay_analysis_match_codeql_version_dry_run",
OverlayAnalysisPython = "overlay_analysis_python",
/**
* Controls whether lower disk space requirements are used for overlay hardware checks.
@@ -124,6 +165,11 @@ export type FeatureConfig = {
};
export const featureConfig = {
[Feature.AllowMultipleAnalysisKinds]: {
defaultValue: false,
envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS",
minimumVersion: undefined,
},
[Feature.AllowToolcacheInput]: {
defaultValue: false,
envVar: "CODEQL_ACTION_ALLOW_TOOLCACHE_INPUT",
@@ -277,6 +323,16 @@ export const featureConfig = {
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_DISABLE_TRAP_CACHING",
minimumVersion: undefined,
},
[Feature.OverlayAnalysisMatchCodeqlVersion]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION",
minimumVersion: undefined,
},
[Feature.OverlayAnalysisMatchCodeqlVersionDryRun]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_MATCH_CODEQL_VERSION_DRY_RUN",
minimumVersion: undefined,
},
[Feature.OverlayAnalysisResourceChecksV2]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RESOURCE_CHECKS_V2",
@@ -346,8 +402,12 @@ export type FeatureWithoutCLI = {
}[keyof typeof featureConfig];
export interface FeatureEnablement {
/** Gets the default version of the CodeQL tools. */
getDefaultCliVersion(
/**
* Returns the set of default CodeQL CLI versions to consider, sorted from
* highest to lowest. The first entry is the version that the CodeQL Action
* will use by default. The list is always non-empty.
*/
getEnabledDefaultCliVersions(
variant: util.GitHubVariant,
): Promise<CodeQLDefaultVersionInfo>;
getValue(feature: FeatureWithoutCLI): Promise<boolean>;
@@ -371,12 +431,11 @@ export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
class OfflineFeatures implements FeatureEnablement {
constructor(protected readonly logger: Logger) {}
async getDefaultCliVersion(
async getEnabledDefaultCliVersions(
_variant: util.GitHubVariant,
): Promise<CodeQLDefaultVersionInfo> {
return {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [LINKED_CODEQL_VERSION],
};
}
@@ -386,7 +445,7 @@ class OfflineFeatures implements FeatureEnablement {
getFeatureConfig(feature: Feature): FeatureConfig {
// Narrow the type to FeatureConfig to avoid type errors. To avoid unsafe use of `as`, we
// check that the required properties exist using `satisfies`.
return featureConfig[feature] satisfies FeatureConfig as FeatureConfig;
return featureConfig[feature] satisfies FeatureConfig;
}
/**
@@ -518,13 +577,13 @@ class Features extends OfflineFeatures {
);
}
async getDefaultCliVersion(
async getEnabledDefaultCliVersions(
variant: util.GitHubVariant,
): Promise<CodeQLDefaultVersionInfo> {
if (supportsFeatureFlags(variant)) {
return await this.gitHubFeatureFlags.getDefaultCliVersionFromFlags();
return await this.gitHubFeatureFlags.getEnabledDefaultCliVersionsFromFlags();
}
return super.getDefaultCliVersion(variant);
return super.getEnabledDefaultCliVersions(variant);
}
/**
@@ -600,16 +659,22 @@ class GitHubFeatureFlags {
return version;
}
async getDefaultCliVersionFromFlags(): Promise<CodeQLDefaultVersionInfo> {
/**
* Returns CLI versions enabled by `default_codeql_version_*_enabled` feature
* flags, sorted from highest to lowest. Falls back to the version pinned in
* `defaults.json` if no such flags are enabled.
*/
async getEnabledDefaultCliVersionsFromFlags(): Promise<CodeQLDefaultVersionInfo> {
const response = await this.getAllFeatures();
const enabledFeatureFlagCliVersions = Object.entries(response)
const sortedCliVersions = Object.entries(response)
.map(([f, isEnabled]) =>
isEnabled ? this.getCliVersionFromFeatureFlag(f) : undefined,
)
.filter((f): f is string => f !== undefined);
.filter((f): f is string => f !== undefined)
.sort(semver.rcompare);
if (enabledFeatureFlagCliVersions.length === 0) {
if (sortedCliVersions.length === 0) {
// We expect at least one default CLI version to be enabled on Dotcom at any time. However if
// the feature flags are misconfigured, rather than crashing, we fall back to the CLI version
// shipped with the Action in defaults.json. This has the effect of immediately rolling out
@@ -625,8 +690,7 @@ class GitHubFeatureFlags {
`shipped with the Action. This is ${defaults.cliVersion}.`,
);
const result: CodeQLDefaultVersionInfo = {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [LINKED_CODEQL_VERSION],
};
if (this.hasAccessedRemoteFeatureFlags) {
result.toolsFeatureFlagsValid = false;
@@ -634,17 +698,14 @@ class GitHubFeatureFlags {
return result;
}
const maxCliVersion = enabledFeatureFlagCliVersions.reduce(
(maxVersion, currentVersion) =>
currentVersion > maxVersion ? currentVersion : maxVersion,
enabledFeatureFlagCliVersions[0],
);
this.logger.debug(
`Derived default CLI version of ${maxCliVersion} from feature flags.`,
`Derived default CLI version of ${sortedCliVersions[0]} from feature flags.`,
);
return {
cliVersion: maxCliVersion,
tagName: `codeql-bundle-v${maxCliVersion}`,
enabledVersions: sortedCliVersions.map((cliVersion) => ({
cliVersion,
tagName: `codeql-bundle-v${cliVersion}`,
})),
toolsFeatureFlagsValid: true,
};
}
+77 -14
View File
@@ -33,7 +33,6 @@ test.serial(
const actualRef = await gitUtils.getRef();
t.deepEqual(actualRef, expectedRef);
callback.restore();
});
},
);
@@ -54,7 +53,6 @@ test.serial(
const actualRef = await gitUtils.getRef();
t.deepEqual(actualRef, expectedRef);
callback.restore();
});
},
);
@@ -73,7 +71,6 @@ test.serial(
const actualRef = await gitUtils.getRef();
t.deepEqual(actualRef, "refs/pull/1/head");
callback.restore();
});
},
);
@@ -100,8 +97,6 @@ test.serial(
const actualRef = await gitUtils.getRef();
t.deepEqual(actualRef, "refs/pull/2/merge");
callback.restore();
getAdditionalInputStub.restore();
});
},
);
@@ -161,7 +156,6 @@ test.serial(
"Both 'ref' and 'sha' are required if one of them is provided.",
},
);
getAdditionalInputStub.restore();
});
},
);
@@ -188,7 +182,6 @@ test.serial(
"Both 'ref' and 'sha' are required if one of them is provided.",
},
);
getAdditionalInputStub.restore();
});
},
);
@@ -242,7 +235,6 @@ test.serial("isAnalyzingDefaultBranch()", async (t) => {
process.env["GITHUB_EVENT_NAME"] = "schedule";
process.env["GITHUB_REF"] = "refs/heads/main";
t.deepEqual(await gitUtils.isAnalyzingDefaultBranch(), false);
getAdditionalInputStub.restore();
});
});
@@ -254,8 +246,6 @@ test.serial("determineBaseBranchHeadCommitOid non-pullrequest", async (t) => {
const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname);
t.deepEqual(result, undefined);
t.deepEqual(0, infoStub.callCount);
infoStub.restore();
});
test.serial(
@@ -276,8 +266,6 @@ test.serial(
"git call failed. Will calculate the base branch SHA on the server. Error: " +
"The checkout path provided to the action does not appear to be a git repository.",
);
infoStub.restore();
},
);
@@ -301,10 +289,27 @@ test.serial("determineBaseBranchHeadCommitOid other error", async (t) => {
"The checkout path provided to the action does not appear to be a git repository.",
),
);
infoStub.restore();
});
test.serial(
"determineBaseBranchHeadCommitOid accepts SHA-256 OIDs",
async (t) => {
const mergeSha = "a".repeat(64);
const baseOid = "b".repeat(64);
const headOid = "c".repeat(64);
process.env["GITHUB_EVENT_NAME"] = "pull_request";
process.env["GITHUB_SHA"] = mergeSha;
sinon
.stub(gitUtils as any, "runGitCommand")
.resolves(`commit ${mergeSha}\nparent ${baseOid}\nparent ${headOid}\n`);
const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname);
t.deepEqual(result, baseOid);
},
);
test.serial("decodeGitFilePath unquoted strings", async (t) => {
t.deepEqual(gitUtils.decodeGitFilePath("foo"), "foo");
t.deepEqual(gitUtils.decodeGitFilePath("foo bar"), "foo bar");
@@ -436,6 +441,64 @@ test.serial("getFileOidsUnderPath handles quoted paths", async (t) => {
});
});
test.serial("getFileOidsUnderPath handles SHA-256 OIDs", async (t) => {
await withTmpDir(async (tmpDir) => {
const sha256OidA =
"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2c0d4b7e8f9a1234567890ab";
const sha256OidB =
"aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899";
sinon
.stub(gitUtils as any, "runGitCommand")
.callsFake(async (_cwd: any, args: any) => {
if (args[0] === "rev-parse") {
return `${tmpDir}\n`;
}
return (
`100644 ${sha256OidA} 0\tlib/sha256-file-a.js\n` +
`100644 ${sha256OidB} 0\tsrc/sha256-file-b.ts`
);
});
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
t.deepEqual(result, {
"lib/sha256-file-a.js": sha256OidA,
"src/sha256-file-b.ts": sha256OidB,
});
});
});
test.serial(
"getFileOidsUnderPath rejects OIDs of unsupported length",
async (t) => {
await withTmpDir(async (tmpDir) => {
// 50-char OID: not a valid SHA-1 (40) or SHA-256 (64) length. The regex
// must not accept this even though every character is a valid hex digit.
const invalidLine =
"100644 30d998ded095371488be3a729eb61d86ed721a1830d998ded0 0\tlib/bad.js";
sinon
.stub(gitUtils as any, "runGitCommand")
.callsFake(async (_cwd: any, args: any) => {
if (args[0] === "rev-parse") {
return `${tmpDir}\n`;
}
return invalidLine;
});
await t.throwsAsync(
async () => {
await gitUtils.getFileOidsUnderPath("/fake/path");
},
{
instanceOf: Error,
message: `Unexpected "git ls-files" output: ${invalidLine}`,
},
);
});
},
);
test.serial("getFileOidsUnderPath handles empty output", async (t) => {
await withTmpDir(async (tmpDir) => {
sinon
+6 -4
View File
@@ -163,11 +163,12 @@ export const determineBaseBranchHeadCommitOid = async function (
}
}
// Let's confirm our assumptions: We had a merge commit and the parsed parent data looks correct
// Let's confirm our assumptions: We had a merge commit and the parsed parent
// data looks correct. OIDs are either 40 (SHA-1) or 64 (SHA-256) hex characters.
if (
commitOid === mergeSha &&
headOid.length === 40 &&
baseOid.length === 40
(headOid.length === 40 || headOid.length === 64) &&
(baseOid.length === 40 || baseOid.length === 64)
) {
return baseOid;
}
@@ -296,7 +297,8 @@ export const getFileOidsUnderPath = async function (
// 100644 4c51bc1d9e86cd86e01b0f340cb8ce095c33b283 0\tsrc/git-utils.test.ts
// 100644 6b792ea543ce75d7a8a03df591e3c85311ecb64f 0\tsrc/git-utils.ts
// The fields are: <mode> <oid> <stage>\t<path>
const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/;
// The OID is either 40 (SHA-1) or 64 (SHA-256) hex characters.
const regex = /^[0-9]+ ([0-9a-f]{40}|[0-9a-f]{64}) [0-9]+\t(.+)$/;
for (const line of stdout.split("\n")) {
if (line) {
const match = line.match(regex);

Some files were not shown because too many files have changed in this diff Show More