Compare commits

..

146 Commits

Author SHA1 Message Date
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
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
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
Michael B. Gale e46ed2cbd0 Merge pull request #3867 from github/update-v4.35.3-8c6e48dbe
Merge main into releases/v4
2026-05-01 15:05:28 +01:00
Michael B. Gale b73d1d1634 Add changelog entry for #3853 2026-05-01 14:09:58 +01:00
Michael B. Gale 24e0bb00a9 Reorder changelog entries 2026-05-01 14:07:12 +01:00
github-actions[bot] ec298daba7 Update changelog for v4.35.3 2026-05-01 12:57:50 +00:00
Henry Mercer 8c6e48dbe0 Merge pull request #3865 from github/update-bundle/codeql-bundle-v2.25.3
Update default bundle to 2.25.3
2026-04-30 16:07:18 +00:00
github-actions[bot] 719098349e Add changelog note 2026-04-30 15:31:49 +00:00
github-actions[bot] 2bb209555a Update default bundle to codeql-bundle-v2.25.3 2026-04-30 15:31:40 +00:00
Michael B. Gale 7851e55dc3 Merge pull request #3850 from github/mbg/private-registry/cloudsmith-gcp
Private registries: Add support for Cloudsmith and GCP OIDC configurations
2026-04-30 13:33:44 +00:00
Michael B. Gale 262a15f6cf Add generic non-printable chars test for OIDC configs 2026-04-30 14:10:36 +01:00
Michael B. Gale a6109b1c07 Merge pull request #3853 from github/mbg/start-proxy/improved-checks
Improve connection tests
2026-04-30 12:48:34 +00:00
Michael B. Gale 022ff3c73f Merge remote-tracking branch 'origin/main' into mbg/private-registry/cloudsmith-gcp 2026-04-30 13:43:29 +01:00
Michael B. Gale 0a4d574ac4 Add changelog entry 2026-04-30 13:42:29 +01:00
Michael B. Gale d1edf2e4de Improve replaces-base validation and add tests 2026-04-30 13:41:13 +01:00
Henry Mercer facd53f789 Merge pull request #3859 from github/dependabot/npm_and_yarn/ava/typescript-7.0.0
Bump @ava/typescript from 6.0.0 to 7.0.0
2026-04-30 12:30:35 +00:00
Michael B. Gale b77983290b Fix permutations comment 2026-04-30 13:28:42 +01:00
Henry Mercer fcf29e3d86 Merge pull request #3862 from github/dependabot/github_actions/dot-github/workflows/actions-minor-933f87fbf1
Bump ruby/setup-ruby from 1.301.0 to 1.305.0 in /.github/workflows in the actions-minor group across 1 directory
2026-04-30 12:17:13 +00:00
Henry Mercer 1fed3e9ba8 Merge branch 'main' into dependabot/npm_and_yarn/ava/typescript-7.0.0 2026-04-30 13:10:19 +01:00
Michael B. Gale 549683cee5 Make it clearer what the expectations for isUsernamePassword are 2026-04-30 12:49:49 +01:00
Michael B. Gale 7a6ed56219 Modify FromSchema so that optional properties are actually optional 2026-04-30 11:54:21 +01:00
Michael B. Gale 91fbc51606 Improve validateSchema comment 2026-04-30 11:46:01 +01:00
Michael B. Gale 35715ef8fe Improve typing of cloneCredential 2026-04-30 11:43:54 +01:00
Michael B. Gale bac7fdaf42 Fix linter error 2026-04-30 11:26:12 +01:00
Henry Mercer 1517969c90 Merge pull request #3837 from github/update-supported-enterprise-server-versions
Update supported GitHub Enterprise Server versions
2026-04-30 10:16:37 +00:00
github-actions[bot] f073360456 Rebuild 2026-04-29 18:02:23 +00:00
dependabot[bot] 5145c112e7 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.301.0 to 1.305.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/4c56a21280b36d862b5fc31348f463d60bdc55d5...0cb964fd540e0a24c900370abf38a33466142735)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-29 18:00:14 +00:00
dependabot[bot] 7108503ac6 Bump @ava/typescript from 6.0.0 to 7.0.0
Bumps [@ava/typescript](https://github.com/avajs/typescript) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/avajs/typescript/releases)
- [Commits](https://github.com/avajs/typescript/compare/v6.0.0...v7.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-29 17:59:33 +00:00
Henry Mercer 4fe9b1e243 Merge pull request #3856 from github/henrymercer/overlay-add-log-group
Add log group for downloading overlay-base DB
2026-04-29 10:51:09 +00:00
Henry Mercer 56733fb5ae Add log group for downloading overlay-base DB 2026-04-28 19:00:28 +01:00
Henry Mercer 0a636086c9 Add GHES 3.21 to supported versions table 2026-04-28 15:32:55 +01:00
Henry Mercer 97be3af35a Deprecate CodeQL versions 2.19.3 and earlier 2026-04-28 15:32:55 +01:00
github-actions[bot] de303a9db5 Update supported GitHub Enterprise Server versions 2026-04-28 15:24:46 +01:00
Michael B. Gale 7a818e6977 Log disclaimer about connection tests, with link to docs 2026-04-28 13:45:53 +01:00
Michael B. Gale 30e0f4391d Use /v3/index.json for NuGet feed check 2026-04-28 13:45:52 +01:00
Henry Mercer 7c5585e5cf Merge pull request #3852 from github/henrymercer/avoid-diagnostic-collisions
Add random suffix when writing diagnostics to avoid filename collisions
2026-04-28 12:04:59 +00:00
Henry Mercer 245f6828c4 Use a counter instead of Math.random for diagnostic filename suffix 2026-04-28 12:42:42 +01:00
Henry Mercer c109008fac Add changelog note 2026-04-28 11:40:03 +01:00
Henry Mercer e73c940c9b Defensively sanitize timestamp 2026-04-28 11:40:02 +01:00
Henry Mercer cdb655d6d4 Add random suffix when writing diagnostics to avoid filename collisions 2026-04-28 11:39:40 +01:00
Michael B. Gale 6153577cab Switch from HEAD to GET requests
Not all registry implementations support `HEAD` correctly.
2026-04-28 10:42:27 +01:00
Óscar San José 8f02cfa11d Update from main and Rebuild 2026-04-27 19:30:21 +02:00
Michael B. Gale 0ed734b61b Ignore test files 2026-04-25 18:36:22 +01:00
Michael B. Gale efdcb31f11 Accept replaces-base option 2026-04-25 18:36:22 +01:00
Michael B. Gale 4d2c7c6e10 Validate GCP OIDC configurations 2026-04-25 18:36:22 +01:00
Michael B. Gale 70b2658d23 Validate Cloudsmith OIDC configurations 2026-04-25 18:36:21 +01:00
Michael B. Gale 530fcb3bbf Group OIDC schemas into an array 2026-04-25 18:36:19 +01:00
Michael B. Gale 2acf81942b Add tests for getAuthConfig 2026-04-25 18:34:00 +01:00
Michael B. Gale d2a54a4507 Add schemas for basic credential types 2026-04-25 18:33:01 +01:00
Michael B. Gale bc4097bbe1 Simplify credential cloning in getAuthConfig 2026-04-25 18:23:11 +01:00
Michael B. Gale c8e26e209a Move getAuthConfig out of start-proxy.ts 2026-04-25 16:49:05 +01:00
Michael B. Gale 0752451507 Use schema/validation for existing OIDC config types 2026-04-25 16:49:05 +01:00
Michael B. Gale 243c274daf Add simple JSON schema / validation helpers 2026-04-25 15:35:50 +01:00
Henry Mercer 19b3a84f58 Merge pull request #3849 from github/henrymercer/simplify-diff-range-interface
Simplify `writeDiffRangeDataExtensionPack` interface
2026-04-23 20:29:05 +00:00
Henry Mercer 858a6149c1 Simplify writeDiffRangeDataExtensionPack interface 2026-04-23 16:47:15 +01:00
Henry Mercer c60c75576d Merge pull request #3848 from github/dependabot/npm_and_yarn/fast-xml-parser-5.7.1
Bump fast-xml-parser from 5.5.7 to 5.7.1
2026-04-22 23:03:27 +00:00
Henry Mercer 59aede2113 Merge pull request #3847 from github/dependabot/npm_and_yarn/uuid-14.0.0
Bump uuid from 13.0.0 to 14.0.0
2026-04-22 23:02:16 +00:00
github-actions[bot] 6c35f8607b Rebuild 2026-04-22 21:54:06 +00:00
github-actions[bot] c486cacf49 Rebuild 2026-04-22 21:53:49 +00:00
dependabot[bot] 365478cc5b Bump fast-xml-parser from 5.5.7 to 5.7.1
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.5.7 to 5.7.1.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.5.7...v5.7.1)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.7.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-22 21:52:05 +00:00
dependabot[bot] f0e6490756 Bump uuid from 13.0.0 to 14.0.0
Bumps [uuid](https://github.com/uuidjs/uuid) from 13.0.0 to 14.0.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v13.0.0...v14.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-22 21:51:48 +00:00
Henry Mercer 860353f245 Merge pull request #3840 from github/dependabot/npm_and_yarn/npm-minor-580efa6e3b
Bump the npm-minor group across 1 directory with 3 updates
2026-04-22 20:59:20 +00:00
Henry Mercer 4fb8483ef0 Merge pull request #3835 from github/dependabot/npm_and_yarn/eslint-import-resolver-typescript-4.4.4
Bump eslint-import-resolver-typescript from 3.8.7 to 4.4.4
2026-04-22 20:33:35 +00:00
dependabot[bot] c2574efbee 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), [sinon](https://github.com/sinonjs/sinon) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


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

Updates `sinon` from 21.0.3 to 21.1.2
- [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.0.3...v21.1.2)

Updates `typescript-eslint` from 8.58.1 to 8.58.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.58.2/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: sinon
  dependency-version: 21.1.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: typescript-eslint
  dependency-version: 8.58.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-22 17:58:53 +00:00
Henry Mercer 4cbe7bef85 Merge pull request #3839 from github/henrymercer/workflow-run-triggers
Escape "+"s in `on.workflow_run.workflows`
2026-04-22 10:44:53 +00:00
Henry Mercer f6a5638305 Escape "+"s in on.workflow_run.workflows 2026-04-22 11:14:07 +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
Henry Mercer 1dcdb940d5 Merge pull request #3830 from github/henrymercer/deflake
Add workflow to rerun potentially transient failures
2026-04-21 10:57:19 +00:00
Henry Mercer 0b7b740d4c Merge pull request #3831 from github/dependabot/npm_and_yarn/npm-minor-f46f1f14d7
Bump the npm-minor group across 1 directory with 2 updates
2026-04-16 11:08:29 +00:00
Henry Mercer 0ac85966ba Merge branch 'main' into dependabot/npm_and_yarn/npm-minor-f46f1f14d7 2026-04-16 11:49:39 +01:00
dependabot[bot] 5019ed041c Bump eslint-import-resolver-typescript from 3.8.7 to 4.4.4
Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 3.8.7 to 4.4.4.
- [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases)
- [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v3.8.7...v4.4.4)

---
updated-dependencies:
- dependency-name: eslint-import-resolver-typescript
  dependency-version: 4.4.4
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 17:58:58 +00:00
dependabot[bot] d64d81d41f Bump the npm-minor group across 1 directory with 2 updates
Bumps the npm-minor group with 2 updates in the / directory: [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@eslint/compat` from 2.0.4 to 2.0.5
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v2.0.5/packages/compat)

Updates `typescript-eslint` from 8.58.0 to 8.58.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.58.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 2.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: typescript-eslint
  dependency-version: 8.58.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 17:58:27 +00:00
Henry Mercer 6777c894e9 Merge pull request #3811 from github/henrymercer/record-all-builtin-languages
Store all built-in languages
2026-04-15 17:57:19 +00:00
Henry Mercer 79f9c0517c Merge remote-tracking branch 'origin/main' into henrymercer/record-all-builtin-languages
# Conflicts:
#	lib/start-proxy-action.js
#	src/known-language-aliases.json
2026-04-15 18:36:47 +01:00
Henry Mercer 3b3a77544b Rename job
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-15 18:34:13 +01:00
Henry Mercer 9f95de42d6 Add workflow to rerun potentially transient failures 2026-04-15 18:28:17 +01:00
Henry Mercer e2d518d895 Merge pull request #3827 from github/dependabot/npm_and_yarn/follow-redirects-1.16.0
Bump follow-redirects from 1.15.11 to 1.16.0
2026-04-15 12:47:52 +00:00
github-actions[bot] 9df9e9176e Rebuild 2026-04-15 12:20:46 +00:00
dependabot[bot] 6847a42aa8 Bump follow-redirects from 1.15.11 to 1.16.0
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 12:18:36 +00:00
Henry Mercer f820c80d4d Merge pull request #3825 from github/mergeback/v4.35.2-to-main-95e58e9a
Mergeback v4.35.2 refs/heads/releases/v4 into main
2026-04-15 11:56:45 +00:00
github-actions[bot] ca7d6d3b79 Rebuild 2026-04-15 11:27:36 +00:00
github-actions[bot] 8d9c36a0ce Update changelog and version after v4.35.2 2026-04-15 11:24:19 +00:00
Henry Mercer f8b62132ab Include experimental languages 2026-04-14 17:38:26 +01:00
Henry Mercer 90d7616015 Merge branch 'main' into henrymercer/record-all-builtin-languages 2026-04-13 18:00:09 +01:00
Henry Mercer 1aef4ed505 Exclude new TypeScript code from package tests
Avoid new source code changing expected output
2026-04-13 17:37:29 +01:00
Henry Mercer cb52ba6486 Refactoring: Split up script 2026-04-13 17:03:20 +01:00
Henry Mercer 7c9e131894 Add constant for builtin languages file path 2026-04-13 16:57:47 +01:00
Henry Mercer 130ab2d721 Improve JSDoc 2026-04-13 16:54:06 +01:00
Henry Mercer 8cf2dc52f9 Fix casing mismatch 2026-04-13 16:49:31 +01:00
Henry Mercer 97bcdd8c1e Move script to pr-checks directory 2026-04-13 16:49:10 +01:00
Henry Mercer e6c21da23c Refactoring: Rename KnownLanguage to BuiltInLanguage 2026-04-10 19:09:47 +01:00
Henry Mercer bad0a744dd Store all built-in languages
While we want the CodeQL Action to work with third-party language support, having a list of all built-in languages can help us create better type-level checks to ensure that we don't miss things that we want to customize for each of our built-in languages.
2026-04-10 19:09:46 +01:00
Michael B. Gale 4ea3a4b4af Bump undici to at least 6.24.0 2026-03-27 17:32:08 +00:00
89 changed files with 23882 additions and 302586 deletions
+3 -1
View File
@@ -1,5 +1,5 @@
name: "CodeQL config"
queries:
queries:
- name: Run custom queries
uses: ./queries
# Run all extra query suites, both because we want to
@@ -13,3 +13,5 @@ queries:
paths-ignore:
- lib
- tests
- "**/*.test.ts"
- "**/testing-util.ts"
+1 -1
View File
@@ -59,7 +59,7 @@ jobs:
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- name: Set up Ruby
uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1.301.0
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+106
View File
@@ -0,0 +1,106 @@
# Workflow runs on main, on a release branch, and that were triggered as part of a merge group have
# already passed CI before being merged. Therefore if they fail, we should make sure that there
# wasn't a transient failure by rerunning the failed jobs once before investigating further.
name: Deflake
on:
workflow_run:
types: [completed]
# Exclude workflows that have significant side effects, like publishing releases. It's OK to
# retry CodeQL analysis.
workflows:
- Check Expected Release Files
- Code-Scanning config CLI tests
- CodeQL action
- Manual Check - go
- "PR Check - All-platform bundle"
- "PR Check - Analysis kinds"
- "PR Check - Analyze: 'ref' and 'sha' from inputs"
- "PR Check - autobuild-action"
- "PR Check - Autobuild direct tracing (custom working directory)"
- "PR Check - Autobuild working directory"
- "PR Check - Build mode autobuild"
- "PR Check - Build mode manual"
- "PR Check - Build mode none"
- "PR Check - Build mode rollback"
- "PR Check - Bundle: Caching checks"
- "PR Check - Bundle: From nightly"
- "PR Check - Bundle: From toolcache"
- "PR Check - Bundle: Zstandard checks"
- "PR Check - C/C\\+\\+: autoinstalling dependencies (Linux)"
- "PR Check - C/C\\+\\+: autoinstalling dependencies is skipped (macOS)"
- "PR Check - C/C\\+\\+: disabling autoinstalling dependencies (Linux)"
- "PR Check - Clean up database cluster directory"
- "PR Check - CodeQL Bundle All"
- "PR Check - Config export"
- "PR Check - Config input"
- "PR Check - Custom source root"
- "PR Check - Debug artifact upload"
- "PR Check - Debug artifacts after failure"
- "PR Check - Diagnostic export"
- "PR Check - Export file baseline information"
- "PR Check - Extractor ram and threads options test"
- "PR Check - Go: Custom queries"
- "PR Check - Go: diagnostic when Go is changed after init step"
- "PR Check - Go: diagnostic when `file` is not installed"
- "PR Check - Go: tracing with autobuilder step"
- "PR Check - Go: tracing with custom build steps"
- "PR Check - Go: tracing with legacy workflow"
- "PR Check - Go: workaround for indirect tracing"
- "PR Check - Job run UUID added to SARIF"
- "PR Check - Language aliases"
- "PR Check - Local CodeQL bundle"
- "PR Check - Multi-language repository"
- "PR Check - Overlay database init fallback"
- "PR Check - Packaging: Action input"
- "PR Check - Packaging: Config and input"
- "PR Check - Packaging: Config and input passed to the CLI"
- "PR Check - Packaging: Config file"
- "PR Check - Packaging: Download using registries"
- "PR Check - Proxy test"
- "PR Check - Remote config file"
- "PR Check - Resolve environment"
- "PR Check - RuboCop multi-language"
- "PR Check - Ruby analysis"
- "PR Check - Rust analysis"
- "PR Check - Split workflow"
- "PR Check - Start proxy"
- "PR Check - Submit SARIF after failure"
- "PR Check - Swift analysis using a custom build command"
- "PR Check - Swift analysis using autobuild"
- "PR Check - Test different uses of `upload-sarif`"
- "PR Check - Test unsetting environment variables"
- "PR Check - Upload-sarif: ref and sha from inputs"
- "PR Check - Use a custom `checkout_path`"
- PR Checks
- Query filters tests
- Test that the workaround for python 3.12 on windows works
jobs:
rerun-on-failure:
name: Rerun failed jobs
if: >-
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt == 1 &&
(
github.event.workflow_run.head_branch == 'main' ||
startsWith(github.event.workflow_run.head_branch, 'releases/') ||
github.event.workflow_run.event == 'merge_group'
)
runs-on: ubuntu-slim
permissions:
actions: write
steps:
- name: Rerun failed jobs in ${{ github.event.workflow_run.name }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RUN_ID: ${{ github.event.workflow_run.id }}
RUN_NAME: ${{ github.event.workflow_run.name }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
run: |
echo "Rerunning failed jobs for workflow run ${RUN_ID}"
gh run rerun "${RUN_ID}" --failed
echo "### Reran failed jobs :recycle:" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Workflow: [${RUN_NAME}](${RUN_URL})" >> "$GITHUB_STEP_SUMMARY"
+2 -5
View File
@@ -63,13 +63,10 @@ jobs:
with:
tools: https://github.com/github/codeql-action/releases/download/${{ github.event.release.tag_name }}/codeql-bundle-linux64.tar.gz
- name: Update language aliases
- name: Update built-in languages
run: npx tsx pr-checks/update-builtin-languages.ts "$CODEQL_PATH"
env:
CODEQL_PATH: ${{ steps.setup-codeql.outputs.codeql-path }}
run: |
"$CODEQL_PATH" resolve languages --format=betterjson --extractor-include-aliases \
| jq -S '.aliases // {}' \
> src/known-language-aliases.json
- name: Bump Action minor version if new CodeQL minor version series
id: bump-action-version
+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}`,",
+16
View File
@@ -2,6 +2,22 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## [UNRELEASED]
- Added an experimental change which, when running a Code Scanning analysis for a PR with [improved incremental analysis](https://github.com/github/roadmap/issues/1158) enabled, prefers CodeQL CLI versions that have a cached overlay-base database for the configured languages. This speeds up analysis for a repository when there is not yet a cached overlay-base database for the latest CLI version. We expect to roll this change out to everyone in May. [#3880](https://github.com/github/codeql-action/pull/3880)
## 4.35.4 - 07 May 2026
- Update default CodeQL bundle version to [2.25.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.4). [#3881](https://github.com/github/codeql-action/pull/3881)
## 4.35.3 - 01 May 2026
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.19.3 and earlier. These versions of CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise Server 3.15, and will be unsupported by the next minor release of the CodeQL Action. [#3837](https://github.com/github/codeql-action/pull/3837)
- Configurations for private registries that use Cloudsmith or GCP OIDC are now accepted. [#3850](https://github.com/github/codeql-action/pull/3850)
- Best-effort connection tests for private registries now use `GET` requests instead of `HEAD` for better compatibility with various registry implementations. For NuGet feeds, the test is now always performed against the service index. [#3853](https://github.com/github/codeql-action/pull/3853)
- Fixed a bug where two diagnostics produced within the same millisecond could overwrite each other on disk, causing one of them to be lost. [#3852](https://github.com/github/codeql-action/pull/3852)
- Update default CodeQL bundle version to [2.25.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.3). [#3865](https://github.com/github/codeql-action/pull/3865)
## 4.35.2 - 15 Apr 2026
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable is deprecated and will be removed in May 2026. If you are affected by this, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#3795](https://github.com/github/codeql-action/pull/3795)
+1
View File
@@ -72,6 +72,7 @@ We typically release new minor versions of the CodeQL Action and Bundle when a n
| Minimum CodeQL Action | Minimum CodeQL Bundle Version | GitHub Environment | Notes |
|-----------------------|-------------------------------|--------------------|-------|
| `v4.33.0` | `2.24.3` | Enterprise Server 3.21 | |
| `v4.31.10` | `2.23.9` | Enterprise Server 3.20 | |
| `v3.29.11` | `2.22.4` | Enterprise Server 3.19 | |
| `v3.28.21` | `2.21.3` | Enterprise Server 3.18 | |
+1512 -36149
View File
File diff suppressed because one or more lines are too long
+1822 -19519
View File
File diff suppressed because one or more lines are too long
+1421 -19297
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.25.2",
"cliVersion": "2.25.2",
"priorBundleVersion": "codeql-bundle-v2.25.1",
"priorCliVersion": "2.25.1"
"bundleVersion": "codeql-bundle-v2.25.4",
"cliVersion": "2.25.4",
"priorBundleVersion": "codeql-bundle-v2.25.3",
"priorCliVersion": "2.25.3"
}
+1831 -36255
View File
File diff suppressed because one or more lines are too long
+1835 -19544
View File
File diff suppressed because one or more lines are too long
+1395 -19276
View File
File diff suppressed because one or more lines are too long
+1973 -19553
View File
File diff suppressed because one or more lines are too long
+1509 -36146
View File
File diff suppressed because one or more lines are too long
+2713 -20563
View File
File diff suppressed because one or more lines are too long
+1690 -19351
View File
File diff suppressed because one or more lines are too long
+1509 -36146
View File
File diff suppressed because one or more lines are too long
+1714 -19370
View File
File diff suppressed because one or more lines are too long
+207 -245
View File
@@ -1,12 +1,12 @@
{
"name": "codeql",
"version": "4.35.2",
"version": "4.35.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.35.2",
"version": "4.35.5",
"license": "MIT",
"workspaces": [
"pr-checks"
@@ -25,7 +25,7 @@
"@octokit/plugin-retry": "^8.1.0",
"archiver": "^7.0.1",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.15.11",
"follow-redirects": "^1.16.0",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^4.1.1",
@@ -33,35 +33,35 @@
"long": "^5.3.2",
"node-forge": "^1.4.0",
"semver": "^7.7.4",
"uuid": "^13.0.0"
"uuid": "^14.0.0"
},
"devDependencies": {
"@ava/typescript": "6.0.0",
"@eslint/compat": "^2.0.4",
"@ava/typescript": "7.0.0",
"@eslint/compat": "^2.0.5",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@octokit/types": "^16.0.0",
"@types/archiver": "^7.0.0",
"@types/follow-redirects": "^1.14.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.19.9",
"@types/node": "^20.19.39",
"@types/node-forge": "^1.3.14",
"@types/sarif": "^2.1.7",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.1",
"ava": "^7.0.0",
"esbuild": "^0.28.0",
"eslint": "^9.39.2",
"eslint-import-resolver-typescript": "^3.8.7",
"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.4.0",
"globals": "^17.5.0",
"nock": "^14.0.12",
"sinon": "^21.0.3",
"typescript": "^6.0.2",
"typescript-eslint": "^8.58.0"
"sinon": "^21.1.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.1"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -410,15 +410,6 @@
"undici": "^6.23.0"
}
},
"node_modules/@actions/github/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/@actions/glob": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.1.tgz",
@@ -439,15 +430,6 @@
"undici": "^6.23.0"
}
},
"node_modules/@actions/http-client/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/@actions/io": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz",
@@ -468,16 +450,17 @@
}
},
"node_modules/@ava/typescript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-6.0.0.tgz",
"integrity": "sha512-+8oDYc4J5cCaWZh1VUbyc+cegGplJO9FqHpqR4LVAVx8fRLVRaYlC4yyA6cqHJ1vWP23Ff/ECS5U68Zz6OLZlg==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-7.0.0.tgz",
"integrity": "sha512-0ktzq4/9ya2QoAuVWzl3McpLV9W//Tj+oMonQ4ucgm5l6tQ46aaju/rJL9kzeY5MkG6wzXvFt/MmaLqf9uNC9w==",
"dev": true,
"license": "MIT",
"dependencies": {
"escape-string-regexp": "^5.0.0",
"execa": "^9.6.0"
"execa": "^9.6.1"
},
"engines": {
"node": "^20.8 || ^22 || >=24"
"node": "^22.20 || ^24.12 || >=25"
}
},
"node_modules/@ava/typescript/node_modules/escape-string-regexp": {
@@ -1333,13 +1316,13 @@
}
},
"node_modules/@eslint/compat": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.4.tgz",
"integrity": "sha512-o598tCGstJv9Kk4XapwP+oDij9HD9Qr3V37ABzTfdzVvbFciV+sfg9zSW6olj6G/IXj7p89SwSzPnZ+JUEPIPg==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.5.tgz",
"integrity": "sha512-IbHDbHJfkVNv6xjlET8AIVo/K1NQt7YT4Rp6ok/clyBGcpRx1l6gv0Rq3vBvYfPJIZt6ODf66Zq08FJNDpnzgg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^1.2.0"
"@eslint/core": "^1.2.1"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -1354,15 +1337,15 @@
}
},
"node_modules/@eslint/config-array": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
"integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
"minimatch": "^3.1.5"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1408,20 +1391,20 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
"integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.1",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
"engines": {
@@ -1444,9 +1427,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
"integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1493,14 +1476,6 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@fastify/busboy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"engines": {
"node": ">=14"
}
},
"node_modules/@github/browserslist-config": {
"version": "1.0.0",
"dev": true,
@@ -1988,6 +1963,18 @@
"@tybys/wasm-util": "^0.10.0"
}
},
"node_modules/@nodable/entities": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz",
"integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/nodable"
}
],
"license": "MIT"
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
@@ -2020,15 +2007,6 @@
"node": ">= 8"
}
},
"node_modules/@nolyfill/is-core-module": {
"version": "1.0.39",
"resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
"integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
"dev": true,
"engines": {
"node": ">=12.4.0"
}
},
"node_modules/@octokit/auth-token": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
@@ -2362,7 +2340,8 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
"integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
"dev": true
"dev": true,
"license": "MIT"
},
"node_modules/@sindresorhus/base62": {
"version": "1.0.0",
@@ -2400,9 +2379,9 @@
}
},
"node_modules/@sinonjs/fake-timers": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz",
"integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==",
"version": "15.3.2",
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz",
"integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -2410,9 +2389,9 @@
}
},
"node_modules/@sinonjs/samsam": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-9.0.3.tgz",
"integrity": "sha512-ZgYY7Dc2RW+OUdnZ1DEHg00lhRt+9BjymPKHog4PRFzr1U3MbK57+djmscWyKxzO1qfunHqs4N45WWyKIFKpiQ==",
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz",
"integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -2490,9 +2469,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz",
"integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==",
"version": "20.19.39",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
"integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2549,17 +2528,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz",
"integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz",
"integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.58.0",
"@typescript-eslint/type-utils": "8.58.0",
"@typescript-eslint/utils": "8.58.0",
"@typescript-eslint/visitor-keys": "8.58.0",
"@typescript-eslint/scope-manager": "8.59.1",
"@typescript-eslint/type-utils": "8.59.1",
"@typescript-eslint/utils": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -2572,7 +2551,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.58.0",
"@typescript-eslint/parser": "^8.59.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
@@ -2588,16 +2567,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz",
"integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz",
"integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.58.0",
"@typescript-eslint/types": "8.58.0",
"@typescript-eslint/typescript-estree": "8.58.0",
"@typescript-eslint/visitor-keys": "8.58.0",
"@typescript-eslint/scope-manager": "8.59.1",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1",
"debug": "^4.4.3"
},
"engines": {
@@ -2631,14 +2610,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz",
"integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz",
"integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.58.0",
"@typescript-eslint/types": "^8.58.0",
"@typescript-eslint/tsconfig-utils": "^8.59.1",
"@typescript-eslint/types": "^8.59.1",
"debug": "^4.4.3"
},
"engines": {
@@ -2671,14 +2650,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz",
"integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz",
"integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.58.0",
"@typescript-eslint/visitor-keys": "8.58.0"
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2689,9 +2668,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz",
"integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz",
"integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2706,15 +2685,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz",
"integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz",
"integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.58.0",
"@typescript-eslint/typescript-estree": "8.58.0",
"@typescript-eslint/utils": "8.58.0",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1",
"@typescript-eslint/utils": "8.59.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -2749,9 +2728,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
"integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz",
"integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2763,16 +2742,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
"integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz",
"integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.58.0",
"@typescript-eslint/tsconfig-utils": "8.58.0",
"@typescript-eslint/types": "8.58.0",
"@typescript-eslint/visitor-keys": "8.58.0",
"@typescript-eslint/project-service": "8.59.1",
"@typescript-eslint/tsconfig-utils": "8.59.1",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -2848,16 +2827,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz",
"integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz",
"integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.58.0",
"@typescript-eslint/types": "8.58.0",
"@typescript-eslint/typescript-estree": "8.58.0"
"@typescript-eslint/scope-manager": "8.59.1",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2872,13 +2851,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
"integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz",
"integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.58.0",
"@typescript-eslint/types": "8.59.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -3292,7 +3271,9 @@
}
},
"node_modules/ajv": {
"version": "6.12.6",
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4477,9 +4458,9 @@
}
},
"node_modules/diff": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
"integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
"integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -4540,19 +4521,6 @@
"version": "8.0.0",
"license": "MIT"
},
"node_modules/enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/es-abstract": {
"version": "1.24.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
@@ -4759,25 +4727,25 @@
}
},
"node_modules/eslint": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-array": "^0.21.2",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.39.2",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "9.39.4",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
@@ -4796,7 +4764,7 @@
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -4873,24 +4841,25 @@
}
},
"node_modules/eslint-import-resolver-typescript": {
"version": "3.8.7",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.7.tgz",
"integrity": "sha512-U7k84gOzrfl09c33qrIbD3TkWTWu3nt3dK5sDajHSekfoLlYGusIwSdPlPzVeA6TFpi0Wpj+ZdBD8hX4hxPoww==",
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz",
"integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==",
"dev": true,
"license": "ISC",
"dependencies": {
"@nolyfill/is-core-module": "1.0.39",
"debug": "^4.3.7",
"enhanced-resolve": "^5.15.0",
"get-tsconfig": "^4.10.0",
"is-bun-module": "^1.0.2",
"stable-hash": "^0.0.4",
"tinyglobby": "^0.2.12"
"debug": "^4.4.1",
"eslint-import-context": "^0.1.8",
"get-tsconfig": "^4.10.1",
"is-bun-module": "^2.0.0",
"stable-hash-x": "^0.2.0",
"tinyglobby": "^0.2.14",
"unrs-resolver": "^1.7.11"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
"node": "^16.17.0 || >=18.6.0"
},
"funding": {
"url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
"url": "https://opencollective.com/eslint-import-resolver-typescript"
},
"peerDependencies": {
"eslint": "*",
@@ -5616,10 +5585,11 @@
}
},
"node_modules/execa": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz",
"integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==",
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz",
"integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/merge-streams": "^4.0.0",
"cross-spawn": "^7.0.6",
@@ -5700,9 +5670,9 @@
"license": "MIT"
},
"node_modules/fast-xml-builder": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
"integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz",
"integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==",
"funding": [
{
"type": "github",
@@ -5715,9 +5685,9 @@
}
},
"node_modules/fast-xml-parser": {
"version": "5.5.7",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz",
"integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==",
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.1.tgz",
"integrity": "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==",
"funding": [
{
"type": "github",
@@ -5726,9 +5696,10 @@
],
"license": "MIT",
"dependencies": {
"fast-xml-builder": "^1.1.4",
"path-expression-matcher": "^1.1.3",
"strnum": "^2.2.0"
"@nodable/entities": "^2.1.0",
"fast-xml-builder": "^1.1.5",
"path-expression-matcher": "^1.5.0",
"strnum": "^2.2.3"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -5841,9 +5812,9 @@
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
@@ -6033,6 +6004,7 @@
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
"integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sec-ant/readable-stream": "^0.4.1",
"is-stream": "^4.0.1"
@@ -6148,9 +6120,9 @@
}
},
"node_modules/globals": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
"integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
"version": "17.5.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz",
"integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6393,6 +6365,7 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
"integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
@@ -6581,12 +6554,13 @@
}
},
"node_modules/is-bun-module": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.1.0.tgz",
"integrity": "sha512-4mTAVPlrXpaN3jtF0lsnPCMGnq4+qZjVIKq0HCpfcqf8OC1SM5oATCIAPM5V5FN05qp2NNnFndphmdZS9CV3hA==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
"integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.6.3"
"semver": "^7.7.1"
}
},
"node_modules/is-callable": {
@@ -6789,6 +6763,7 @@
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -6863,6 +6838,7 @@
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
"integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
@@ -7571,6 +7547,7 @@
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^4.0.0",
"unicorn-magic": "^0.3.0"
@@ -7587,6 +7564,7 @@
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -7860,9 +7838,9 @@
}
},
"node_modules/path-expression-matcher": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz",
"integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
"integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==",
"funding": [
{
"type": "github",
@@ -8532,17 +8510,16 @@
}
},
"node_modules/sinon": {
"version": "21.0.3",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.3.tgz",
"integrity": "sha512-0x8TQFr8EjADhSME01u1ZK31yv2+bd6Z5NrBCHVM+n4qL1wFqbxftmeyi3bwlr49FbbzRfrqSFOpyHCOh/YmYA==",
"version": "21.1.2",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-21.1.2.tgz",
"integrity": "sha512-FS6mN+/bx7e2ajpXkEmOcWB6xBzWiuNoAQT18/+a20SS4U7FSYl8Ms7N6VTUxN/1JAjkx7aXp+THMC8xdpp0gA==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@sinonjs/commons": "^3.0.1",
"@sinonjs/fake-timers": "^15.1.1",
"@sinonjs/samsam": "^9.0.3",
"diff": "^8.0.3",
"supports-color": "^7.2.0"
"@sinonjs/fake-timers": "^15.3.2",
"@sinonjs/samsam": "^10.0.2",
"diff": "^8.0.4"
},
"funding": {
"type": "opencollective",
@@ -8626,13 +8603,6 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
"node_modules/stable-hash": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
"integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==",
"dev": true,
"license": "MIT"
},
"node_modules/stable-hash-x": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
@@ -8844,6 +8814,7 @@
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
"integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
@@ -8863,9 +8834,9 @@
}
},
"node_modules/strnum": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz",
"integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz",
"integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==",
"funding": [
{
"type": "github",
@@ -8988,15 +8959,6 @@
"url": "https://opencollective.com/unts"
}
},
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/tar": {
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz",
@@ -9811,9 +9773,9 @@
}
},
"node_modules/typescript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -9825,16 +9787,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.58.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz",
"integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.1.tgz",
"integrity": "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.58.0",
"@typescript-eslint/parser": "8.58.0",
"@typescript-eslint/typescript-estree": "8.58.0",
"@typescript-eslint/utils": "8.58.0"
"@typescript-eslint/eslint-plugin": "8.59.1",
"@typescript-eslint/parser": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1",
"@typescript-eslint/utils": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9868,14 +9830,12 @@
}
},
"node_modules/undici": {
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz",
"integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==",
"license": "MIT",
"engines": {
"node": ">=14.0"
"node": ">=18.17"
}
},
"node_modules/undici-types": {
@@ -9890,6 +9850,7 @@
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
@@ -9999,9 +9960,9 @@
"license": "MIT"
},
"node_modules/uuid": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz",
"integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@@ -10393,10 +10354,11 @@
}
},
"node_modules/yoctocolors": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz",
"integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
"integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
@@ -10428,7 +10390,7 @@
"yaml": "^2.8.3"
},
"devDependencies": {
"@types/node": "^20.19.9",
"@types/node": "^20.19.39",
"tsx": "^4.21.0"
}
}
+16 -15
View File
@@ -1,18 +1,18 @@
{
"name": "codeql",
"version": "4.35.2",
"version": "4.35.5",
"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"
"transpile": "tsc --build --verbose tsconfig.json"
},
"license": "MIT",
"workspaces": [
@@ -32,7 +32,7 @@
"@octokit/plugin-retry": "^8.1.0",
"archiver": "^7.0.1",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.15.11",
"follow-redirects": "^1.16.0",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^4.1.1",
@@ -40,35 +40,35 @@
"long": "^5.3.2",
"node-forge": "^1.4.0",
"semver": "^7.7.4",
"uuid": "^13.0.0"
"uuid": "^14.0.0"
},
"devDependencies": {
"@ava/typescript": "6.0.0",
"@eslint/compat": "^2.0.4",
"@ava/typescript": "7.0.0",
"@eslint/compat": "^2.0.5",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@octokit/types": "^16.0.0",
"@types/archiver": "^7.0.0",
"@types/follow-redirects": "^1.14.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.19.9",
"@types/node": "^20.19.39",
"@types/node-forge": "^1.3.14",
"@types/sarif": "^2.1.7",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.1",
"ava": "^7.0.0",
"esbuild": "^0.28.0",
"eslint": "^9.39.2",
"eslint-import-resolver-typescript": "^3.8.7",
"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.4.0",
"globals": "^17.5.0",
"nock": "^14.0.12",
"sinon": "^21.0.3",
"typescript": "^6.0.2",
"typescript-eslint": "^8.58.0"
"sinon": "^21.1.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.1"
},
"overrides": {
"@actions/tool-cache": {
@@ -90,6 +90,7 @@
"semver": ">=6.3.1"
},
"brace-expansion@2.0.1": "2.0.2",
"glob": "^11.1.0"
"glob": "^11.1.0",
"undici": "^6.24.0"
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ versions:
- default
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1.301.0
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+10
View File
@@ -11,3 +11,13 @@ export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml");
/** The path to the esbuild metadata file. */
export const BUNDLE_METADATA_FILE = path.join(PR_CHECKS_DIR, "..", "meta.json");
/** The `src` directory. */
const SOURCE_ROOT = path.join(PR_CHECKS_DIR, "..", "src");
/** The path to the built-in languages file. */
export const BUILTIN_LANGUAGES_FILE = path.join(
SOURCE_ROOT,
"languages",
"builtin.json",
);
+1 -1
View File
@@ -10,7 +10,7 @@
"yaml": "^2.8.3"
},
"devDependencies": {
"@types/node": "^20.19.9",
"@types/node": "^20.19.39",
"tsx": "^4.21.0"
}
}
+5 -5
View File
@@ -5,7 +5,7 @@ import * as path from "path";
import * as yaml from "yaml";
import { KnownLanguage } from "../src/languages";
import { BuiltInLanguage } from "../src/languages";
/** Known workflow input names. */
enum KnownInputName {
@@ -91,8 +91,8 @@ interface LanguageSetup {
steps: Step[];
}
/** Describes partial mappings from known languages to their specific setup information. */
type LanguageSetups = Partial<Record<KnownLanguage, LanguageSetup>>;
/** Describes partial mappings from built-in languages to their specific setup information. */
type LanguageSetups = Partial<Record<BuiltInLanguage, LanguageSetup>>;
// The default set of CodeQL Bundle versions to use for the PR checks.
const defaultTestVersions = [
@@ -125,7 +125,7 @@ const defaultLanguageVersions = {
java: "17",
python: "3.13",
csharp: "9.x",
} as const satisfies Partial<Record<KnownLanguage, string>>;
} as const satisfies Partial<Record<BuiltInLanguage, string>>;
/** A mapping from known input names to their specifications. */
const inputSpecs: WorkflowInputs = {
@@ -364,7 +364,7 @@ function getSetupSteps(checkSpecification: JobSpecification): {
const inputs: Array<Set<KnownInputName>> = [];
const steps: Step[] = [];
for (const language of Object.values(KnownLanguage).sort()) {
for (const language of Object.values(BuiltInLanguage).sort()) {
const setupSpec = languageSetups[language];
if (
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env npx tsx
/*
* Updates src/languages/builtin.json by querying the CodeQL CLI for:
* - Languages that have default queries (via codeql-extractor.yml)
* - Language aliases (via `codeql resolve languages --format=betterjson --extractor-include-aliases`)
*
* Usage:
* npx tsx pr-checks/update-builtin-languages.ts [path-to-codeql]
*
* If no path is given, falls back to "codeql".
*/
import { execFileSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import * as yaml from "yaml";
import { EnvVar } from "../src/environment";
import { BUILTIN_LANGUAGES_FILE } from "./config";
/** Resolve all known language extractor directories. */
function resolveLanguages(codeqlPath: string): Record<string, string[]> {
return JSON.parse(
execFileSync(codeqlPath, ["resolve", "languages", "--format=json"], {
encoding: "utf8",
env: {
...process.env,
[EnvVar.EXPERIMENTAL_FEATURES]: "true", // include experimental languages
},
}),
) as Record<string, string[]>;
}
/**
* Return the sorted list of languages whose extractors ship default queries.
*
* @param extractorDirs - Map from language to list of extractor directories
*/
function findLanguagesWithDefaultQueries(
extractorDirs: Record<string, string[]>,
): string[] {
const languages: string[] = [];
for (const [language, dirs] of Object.entries(extractorDirs)) {
if (dirs.length !== 1) {
throw new Error(
`Expected exactly one extractor directory for language '${language}', but found ${dirs.length}: ${dirs.join(
", ",
)}`,
);
}
const extractorYmlPath = path.join(dirs[0], "codeql-extractor.yml");
if (!fs.existsSync(extractorYmlPath)) {
throw new Error(
`Extractor YAML not found for language '${language}' at expected path: ${extractorYmlPath}`,
);
}
const extractorYml = yaml.parse(fs.readFileSync(extractorYmlPath, "utf8"));
const defaultQueries: unknown[] | undefined = extractorYml.default_queries;
if (Array.isArray(defaultQueries) && defaultQueries.length > 0) {
console.log(
`${language}: included (default queries: ${JSON.stringify(defaultQueries)})`,
);
languages.push(language);
} else {
console.log(`${language}: excluded (no default queries)`);
}
}
return languages.sort();
}
/**
* Resolve language aliases from the CodeQL CLI, keeping only those whose
* target is in the given set of included languages.
*/
function resolveAliases(
codeqlPath: string,
includedLanguages: Set<string>,
): Record<string, string> {
const betterjsonOutput = JSON.parse(
execFileSync(
codeqlPath,
[
"resolve",
"languages",
"--format=betterjson",
"--extractor-include-aliases",
],
{ encoding: "utf8" },
),
);
return Object.fromEntries(
Object.entries((betterjsonOutput.aliases ?? {}) as Record<string, string>)
.filter(([, target]) => includedLanguages.has(target))
.sort(([a], [b]) => a.localeCompare(b)),
);
}
/** Write the built-in languages data to disk. */
function writeBuiltinLanguages(
languages: string[],
aliases: Record<string, string>,
): void {
const content = `${JSON.stringify({ languages, aliases }, null, 2)}\n`;
fs.mkdirSync(path.dirname(BUILTIN_LANGUAGES_FILE), { recursive: true });
fs.writeFileSync(BUILTIN_LANGUAGES_FILE, content);
console.log(`\nWrote ${BUILTIN_LANGUAGES_FILE}`);
console.log(` Languages: ${languages.join(", ")}`);
console.log(` Aliases: ${Object.keys(aliases).join(", ")}`);
}
function main(): void {
const codeqlPath = process.argv[2] || "codeql";
const extractorDirs = resolveLanguages(codeqlPath);
const languages = findLanguagesWithDefaultQueries(extractorDirs);
const aliases = resolveAliases(codeqlPath, new Set(languages));
writeBuiltinLanguages(languages, aliases);
}
main();
+19
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 }}
+10 -6
View File
@@ -30,7 +30,7 @@ import {
} from "./dependency-caching";
import { EnvVar } from "./environment";
import { initFeatures } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { cleanupAndUploadOverlayBaseDatabaseToCache } from "./overlay/caching";
import { getRepositoryNwo } from "./repository";
@@ -135,9 +135,13 @@ function hasBadExpectErrorInput(): boolean {
function doesGoExtractionOutputExist(config: Config): boolean {
const golangDbDirectory = util.getCodeQLDatabasePath(
config,
KnownLanguage.go,
BuiltInLanguage.go,
);
const trapDirectory = path.join(
golangDbDirectory,
"trap",
BuiltInLanguage.go,
);
const trapDirectory = path.join(golangDbDirectory, "trap", KnownLanguage.go);
return (
fs.existsSync(trapDirectory) &&
fs
@@ -169,7 +173,7 @@ function doesGoExtractionOutputExist(config: Config): boolean {
* whether any extraction output already exists for Go.
*/
async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
if (!config.languages.includes(KnownLanguage.go)) {
if (!config.languages.includes(BuiltInLanguage.go)) {
return;
}
if (config.buildMode) {
@@ -182,7 +186,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
logger.debug("Won't run Go autobuild since it has already been run.");
return;
}
if (dbIsFinalized(config, KnownLanguage.go, logger)) {
if (dbIsFinalized(config, BuiltInLanguage.go, logger)) {
logger.debug(
"Won't run Go autobuild since there is already a finalized database for Go.",
);
@@ -205,7 +209,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
logger.debug(
"Running Go autobuild because extraction output (TRAP files) for Go code has not been found.",
);
await runAutobuild(config, KnownLanguage.go, logger);
await runAutobuild(config, BuiltInLanguage.go, logger);
}
async function run(startedAt: Date) {
+6 -6
View File
@@ -14,7 +14,7 @@ import {
} from "./analyze";
import { createStubCodeQL } from "./codeql";
import { Feature } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import {
setupTests,
@@ -41,7 +41,7 @@ test.serial("status report fields", async (t) => {
const threadsFlag = "";
sinon.stub(uploadLib, "validateSarifFileSchema");
for (const language of Object.values(KnownLanguage)) {
for (const language of Object.values(BuiltInLanguage)) {
const codeql = createStubCodeQL({
databaseRunQueries: async () => {},
databaseInterpretResults: async (
@@ -130,13 +130,13 @@ test.serial("status report fields", async (t) => {
test("resolveQuerySuiteAlias", (t) => {
// default query suite names should resolve to something language-specific ending in `.qls`.
for (const suite of defaultSuites) {
const resolved = resolveQuerySuiteAlias(KnownLanguage.go, suite);
const resolved = resolveQuerySuiteAlias(BuiltInLanguage.go, suite);
t.assert(
path.extname(resolved) === ".qls",
"Resolved default suite doesn't end in .qls",
);
t.assert(
resolved.indexOf(KnownLanguage.go) >= 0,
resolved.indexOf(BuiltInLanguage.go) >= 0,
"Resolved default suite doesn't contain language name",
);
}
@@ -145,12 +145,12 @@ test("resolveQuerySuiteAlias", (t) => {
const names = ["foo", "bar", "codeql/go-queries@1.0"];
for (const name of names) {
t.deepEqual(resolveQuerySuiteAlias(KnownLanguage.go, name), name);
t.deepEqual(resolveQuerySuiteAlias(BuiltInLanguage.go, name), name);
}
});
test("addSarifExtension", (t) => {
for (const language of Object.values(KnownLanguage)) {
for (const language of Object.values(BuiltInLanguage)) {
t.deepEqual(addSarifExtension(CodeScanning, language), `${language}.sarif`);
t.deepEqual(
addSarifExtension(CodeQuality, language),
+15 -27
View File
@@ -21,7 +21,7 @@ import {
} from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { FeatureEnablement, Feature } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
import { BuiltInLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
import type * as sarif from "./sarif";
@@ -41,7 +41,7 @@ export class CodeQLAnalysisError extends Error {
}
}
type KnownLanguageKey = keyof typeof KnownLanguage;
type BuiltInLanguageKey = keyof typeof BuiltInLanguage;
type RunQueriesDurationStatusReport = {
/**
@@ -50,12 +50,12 @@ type RunQueriesDurationStatusReport = {
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
* taken to run _all_ the queries.
*/
[L in KnownLanguageKey as `analyze_builtin_queries_${L}_duration_ms`]?: number;
[L in BuiltInLanguageKey as `analyze_builtin_queries_${L}_duration_ms`]?: number;
};
type InterpretResultsDurationStatusReport = {
/** Time taken in ms to interpret results for the language (or undefined if this language was not analyzed). */
[L in KnownLanguageKey as `interpret_results_${L}_duration_ms`]?: number;
[L in BuiltInLanguageKey as `interpret_results_${L}_duration_ms`]?: number;
};
export interface QueriesStatusReport
@@ -115,12 +115,12 @@ export async function runExtraction(
if (await shouldExtractLanguage(codeql, config, language)) {
logger.startGroup(`Extracting ${language}`);
if (language === KnownLanguage.python) {
if (language === BuiltInLanguage.python) {
await setupPythonExtractor(logger);
}
if (config.buildMode) {
if (
language === KnownLanguage.cpp &&
language === BuiltInLanguage.cpp &&
config.buildMode === BuildMode.Autobuild
) {
await setupCppAutobuild(codeql, logger);
@@ -131,14 +131,14 @@ export async function runExtraction(
// a stable path that caches can be restored into and that we can cache at the
// end of the workflow (i.e. that does not get removed when the scratch directory is).
if (
language === KnownLanguage.java &&
language === BuiltInLanguage.java &&
config.buildMode === BuildMode.None
) {
process.env["CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_DEPENDENCY_DIR"] =
getJavaTempDependencyDir();
}
if (
language === KnownLanguage.csharp &&
language === BuiltInLanguage.csharp &&
config.buildMode === BuildMode.None &&
(await features.getValue(Feature.CsharpCacheBuildModeNone))
) {
@@ -251,16 +251,9 @@ export async function setupDiffInformedQueryRun(
diffRanges,
checkoutPath,
);
if (packDir === undefined) {
logger.warning(
"Cannot create diff range extension pack for diff-informed queries; " +
"reverting to performing full analysis.",
);
} else {
logger.info(
`Successfully created diff range extension pack at ${packDir}.`,
);
}
logger.info(
`Successfully created diff range extension pack at ${packDir}.`,
);
return packDir;
},
);
@@ -314,18 +307,13 @@ extensions:
* @param ranges The file line ranges, as returned by
* `getPullRequestEditedDiffRanges`.
* @param checkoutPath The path at which the repository was checked out.
* @returns The absolute path of the directory containing the extension pack, or
* `undefined` if no extension pack was created.
* @returns The absolute path of the directory containing the extension pack.
*/
function writeDiffRangeDataExtensionPack(
logger: Logger,
ranges: DiffThunkRange[] | undefined,
ranges: DiffThunkRange[],
checkoutPath: string,
): string | undefined {
if (ranges === undefined) {
return undefined;
}
): string {
if (ranges.length === 0) {
// An empty diff range means that there are no added or modified lines in
// the pull request. But the `restrictAlertsTo` extensible predicate
@@ -698,7 +686,7 @@ export async function warnIfGoInstalledAfterInit(
addDiagnostic(
config,
KnownLanguage.go,
BuiltInLanguage.go,
makeDiagnostic(
"go/workflow/go-installed-after-codeql-init",
"Go was installed after the `codeql-action/init` Action was run",
+22 -5
View File
@@ -128,6 +128,8 @@ export async function getGitHubVersionFromApi(
// Doesn't strictly have to be the meta endpoint as we're only
// using the response headers which are available on every request.
//
// See https://docs.github.com/en/rest/meta/meta#get-github-meta-information.
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const response = await apiClient.rest.meta.get();
@@ -164,6 +166,9 @@ export async function getGitHubVersion(): Promise<GitHubVersion> {
/**
* Get the path of the currently executing workflow relative to the repository root.
*
* See https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run
* and https://docs.github.com/en/rest/actions/workflows#get-a-workflow.
*/
export async function getWorkflowRelativePath(): Promise<string> {
const repo_nwo = getRepositoryNwo();
@@ -252,9 +257,13 @@ export interface ActionsCacheItem {
size_in_bytes?: number;
}
/** List all Actions cache entries matching the provided key and ref. */
/**
* List all Actions cache entries starting with the provided key prefix and matching the provided ref.
*
* See https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository.
*/
export async function listActionsCaches(
key: string,
keyPrefix: string,
ref?: string,
): Promise<ActionsCacheItem[]> {
const repositoryNwo = getRepositoryNwo();
@@ -264,13 +273,17 @@ export async function listActionsCaches(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
key,
key: keyPrefix,
ref,
},
);
}
/** Delete an Actions cache item by its ID. */
/**
* Delete an Actions cache item by its ID.
*
* See https://docs.github.com/en/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id.
*/
export async function deleteActionsCache(id: number) {
const repositoryNwo = getRepositoryNwo();
@@ -281,7 +294,11 @@ export async function deleteActionsCache(id: number) {
});
}
/** Retrieve all custom repository properties. */
/**
* Retrieve all custom repository properties.
*
* See https://docs.github.com/en/rest/repos/custom-properties#get-all-custom-property-values-for-a-repository.
*/
export async function getRepositoryProperties(repositoryNwo: RepositoryNwo) {
return getApiClient().request("GET /repos/:owner/:repo/properties/values", {
owner: repositoryNwo.owner,
+1 -1
View File
@@ -1 +1 @@
{"maximumVersion": "3.21", "minimumVersion": "3.14"}
{"maximumVersion": "3.21", "minimumVersion": "3.16"}
+6 -1
View File
@@ -141,7 +141,12 @@ test("scanArtifactsForTokens handles files without tokens", async (t) => {
}
});
if (os.platform() !== "win32") {
// This test is slow (extracts and scans a zip artifact), so by default we only run it in CI. Set
// RUN_SLOW_TESTS=1 to run it locally.
if (
os.platform() !== "win32" &&
(process.env.CI === "true" || process.env.RUN_SLOW_TESTS === "1")
) {
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
t.timeout(15000); // 15 seconds
const messages: LoggedMessage[] = [];
+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: [],
+5 -5
View File
@@ -7,7 +7,7 @@ import * as configUtils from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { Feature, featureConfig, initFeatures } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
import { BuiltInLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import { asyncFilter, BuildMode } from "./util";
@@ -72,7 +72,7 @@ export async function determineAutobuildLanguages(
* version of the CodeQL Action.
*/
const autobuildLanguagesWithoutGo = autobuildLanguages.filter(
(l) => l !== KnownLanguage.go,
(l) => l !== BuiltInLanguage.go,
);
const languages: Language[] = [];
@@ -84,7 +84,7 @@ export async function determineAutobuildLanguages(
// If Go is requested, run the Go autobuilder last to ensure it doesn't
// interfere with the other autobuilder.
if (autobuildLanguages.length !== autobuildLanguagesWithoutGo.length) {
languages.push(KnownLanguage.go);
languages.push(BuiltInLanguage.go);
}
logger.debug(`Will autobuild ${languages.join(" and ")}.`);
@@ -156,7 +156,7 @@ export async function runAutobuild(
) {
logger.startGroup(`Attempting to automatically build ${language} code`);
const codeQL = await getCodeQL(config.codeQLCmd);
if (language === KnownLanguage.cpp) {
if (language === BuiltInLanguage.cpp) {
await setupCppAutobuild(codeQL, logger);
}
if (config.buildMode) {
@@ -164,7 +164,7 @@ export async function runAutobuild(
} else {
await codeQL.runAutobuild(config, language);
}
if (language === KnownLanguage.go) {
if (language === BuiltInLanguage.go) {
core.exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true");
}
logger.endGroup();
+61 -39
View File
@@ -21,7 +21,7 @@ import {
import type { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { DocUrl } from "./doc-url";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import { ToolsSource } from "./setup-codeql";
import {
@@ -33,6 +33,7 @@ import {
mockBundleDownloadApi,
makeVersionInfo,
createTestConfig,
makeMacro,
} from "./testing-utils";
import { ToolsDownloadStatusReport } from "./tools-download";
import * as util from "./util";
@@ -46,7 +47,7 @@ test.beforeEach(() => {
initializeEnvironment("1.2.3");
stubConfig = createTestConfig({
languages: [KnownLanguage.cpp],
languages: [BuiltInLanguage.cpp],
});
});
@@ -70,8 +71,10 @@ async function installIntoToolcache({
tmpDir,
util.GitHubVariant.GHES,
cliVersion !== undefined
? { cliVersion, tagName }
? { enabledVersions: [{ cliVersion, tagName }] }
: SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
createFeatures([]),
getRunnerLogger(true),
false,
@@ -115,7 +118,7 @@ async function stubCodeql(): Promise<codeql.CodeQL> {
sinon.stub(codeqlObject, "getVersion").resolves(makeVersionInfo("2.17.6"));
sinon
.stub(codeqlObject, "isTracedLanguage")
.withArgs(KnownLanguage.cpp)
.withArgs(BuiltInLanguage.cpp)
.resolves(true);
return codeqlObject;
}
@@ -143,6 +146,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -175,6 +180,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -214,6 +221,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -264,6 +273,8 @@ for (const {
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -284,11 +295,11 @@ for (const {
for (const toolcacheVersion of [
// Test that we use the tools from the toolcache when `SAMPLE_DEFAULT_CLI_VERSION` is requested
// and `SAMPLE_DEFAULT_CLI_VERSION-` is in the toolcache.
SAMPLE_DEFAULT_CLI_VERSION.cliVersion,
`${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}-20230101`,
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
`${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion}-20230101`,
]) {
test.serial(
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.cliVersion} is requested and ` +
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion} is requested and ` +
`${toolcacheVersion} is installed`,
async (t) => {
const features = createFeatures([]);
@@ -308,11 +319,16 @@ for (const toolcacheVersion of [
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
);
t.is(result.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
t.is(
result.toolsVersion,
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
);
t.is(result.toolsSource, ToolsSource.Toolcache);
t.is(result.toolsDownloadStatusReport?.combinedDurationMs, undefined);
t.is(result.toolsDownloadStatusReport?.downloadDurationMs, undefined);
@@ -342,9 +358,15 @@ test.serial(
tmpDir,
util.GitHubVariant.GHES,
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
],
},
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -384,9 +406,15 @@ test.serial(
tmpDir,
util.GitHubVariant.GHES,
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
enabledVersions: [
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
],
},
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -426,6 +454,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -467,6 +497,8 @@ test.serial(
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
getRunnerLogger(true),
false,
@@ -540,7 +572,7 @@ test.serial("getExtraOptions throws for bad content", (t) => {
});
// Test macro for ensuring different variants of injected augmented configurations
const injectedConfigMacro = test.macro({
const injectedConfigMacro = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
augmentationProperties: AugmentationProperties,
@@ -590,9 +622,8 @@ const injectedConfigMacro = test.macro({
`databaseInitCluster() injected config: ${providedTitle}`,
});
test.serial(
injectedConfigMacro.serial(
"basic",
injectedConfigMacro,
{
...defaultAugmentationProperties,
},
@@ -600,9 +631,8 @@ test.serial(
{},
);
test.serial(
injectedConfigMacro.serial(
"injected packs from input",
injectedConfigMacro,
{
...defaultAugmentationProperties,
packsInput: ["xxx", "yyy"],
@@ -613,9 +643,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected packs from input with existing packs combines",
injectedConfigMacro,
{
...defaultAugmentationProperties,
packsInputCombines: true,
@@ -635,9 +664,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected packs from input with existing packs overrides",
injectedConfigMacro,
{
...defaultAugmentationProperties,
packsInput: ["xxx", "yyy"],
@@ -655,9 +683,8 @@ test.serial(
);
// similar, but with queries
test.serial(
injectedConfigMacro.serial(
"injected queries from input",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInput: [{ uses: "xxx" }, { uses: "yyy" }],
@@ -675,9 +702,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries from input overrides",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInput: [{ uses: "xxx" }, { uses: "yyy" }],
@@ -699,9 +725,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries from input combines",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -727,9 +752,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries from input combines 2",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -749,9 +773,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"injected queries and packs, but empty",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -768,9 +791,8 @@ test.serial(
{},
);
test.serial(
injectedConfigMacro.serial(
"repo property queries have the highest precedence",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -790,9 +812,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"repo property queries combines with queries input",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: false,
@@ -817,9 +838,8 @@ test.serial(
},
);
test.serial(
injectedConfigMacro.serial(
"repo property queries combines everything else",
injectedConfigMacro,
{
...defaultAugmentationProperties,
queriesInputCombines: true,
@@ -956,7 +976,8 @@ test.serial("runTool summarizes autobuilder errors", async (t) => {
sinon.stub(io, "which").resolves("");
await t.throwsAsync(
async () => await codeqlObject.runAutobuild(stubConfig, KnownLanguage.java),
async () =>
await codeqlObject.runAutobuild(stubConfig, BuiltInLanguage.java),
{
instanceOf: util.ConfigurationError,
message:
@@ -982,7 +1003,8 @@ test.serial("runTool truncates long autobuilder errors", async (t) => {
sinon.stub(io, "which").resolves("");
await t.throwsAsync(
async () => await codeqlObject.runAutobuild(stubConfig, KnownLanguage.java),
async () =>
await codeqlObject.runAutobuild(stubConfig, BuiltInLanguage.java),
{
instanceOf: util.ConfigurationError,
message:
+9 -3
View File
@@ -282,17 +282,17 @@ const CODEQL_MINIMUM_VERSION = "2.17.6";
/**
* This version will shortly become the oldest version of CodeQL that the Action will run with.
*/
const CODEQL_NEXT_MINIMUM_VERSION = "2.17.6";
const CODEQL_NEXT_MINIMUM_VERSION = "2.19.4";
/**
* This is the version of GHES that was most recently deprecated.
*/
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13";
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15";
/**
* This is the deprecation date for the version of GHES that was most recently deprecated.
*/
const GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19";
const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09";
/** The CLI verbosity level to use for extraction in debug mode. */
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
@@ -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,
);
+128 -185
View File
@@ -18,7 +18,7 @@ import { Feature } from "./feature-flags";
import { RepositoryProperties } from "./feature-flags/properties";
import * as gitUtils from "./git-utils";
import { GitVersionInfo } from "./git-utils";
import { KnownLanguage, Language } from "./languages";
import { BuiltInLanguage, Language } from "./languages";
import { getRunnerLogger } from "./logging";
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
import { OverlayDisabledReason } from "./overlay/diagnostics";
@@ -34,6 +34,7 @@ import {
LoggedMessage,
mockCodeQLVersion,
createTestConfig,
makeMacro,
} from "./testing-utils";
import {
GitHubVariant,
@@ -215,7 +216,7 @@ test.serial("load code quality config", async (t) => {
// And the config we expect it to result in
const expectedConfig = createTestConfig({
analysisKinds: [AnalysisKind.CodeQuality],
languages: [KnownLanguage.actions],
languages: [BuiltInLanguage.actions],
// This gets set because we only have `AnalysisKind.CodeQuality`
computedConfig: {
"disable-default-queries": true,
@@ -268,7 +269,7 @@ test.serial(
const expectedConfig = createTestConfig({
analysisKinds: [AnalysisKind.CodeQuality],
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
codeQLCmd: codeql.getPath(),
computedConfig,
dbLocation: path.resolve(tempDir, "codeql_databases"),
@@ -518,7 +519,7 @@ test.serial("load non-empty input", async (t) => {
// And the config we expect it to parse to
const expectedConfig = createTestConfig({
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
buildMode: BuildMode.None,
originalUserInput: userConfig,
computedConfig: userConfig,
@@ -892,10 +893,10 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
betterResolveLanguages: (options) =>
Promise.resolve({
aliases: {
"c#": KnownLanguage.csharp,
c: KnownLanguage.cpp,
kotlin: KnownLanguage.java,
typescript: KnownLanguage.javascript,
"c#": BuiltInLanguage.csharp,
c: BuiltInLanguage.cpp,
kotlin: BuiltInLanguage.java,
typescript: BuiltInLanguage.javascript,
},
extractors: {
cpp: [stubExtractorEntry],
@@ -944,12 +945,12 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
for (const { displayName, language, feature } of [
{
displayName: "Java",
language: KnownLanguage.java,
language: BuiltInLanguage.java,
feature: Feature.DisableJavaBuildlessEnabled,
},
{
displayName: "C#",
language: KnownLanguage.csharp,
language: BuiltInLanguage.csharp,
feature: Feature.DisableCsharpBuildless,
},
]) {
@@ -969,7 +970,7 @@ for (const { displayName, language, feature } of [
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[KnownLanguage.python],
[BuiltInLanguage.python],
createFeatures([feature]),
getRecordingLogger(messages),
);
@@ -1019,7 +1020,7 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
isPullRequest: false,
isDefaultBranch: false,
buildMode: BuildMode.None,
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
codeqlVersion: CODEQL_OVERLAY_MINIMUM_VERSION,
gitRoot: "/some/git/root",
gitVersion: new GitVersionInfo("2.39.0", "2.39.0"),
@@ -1034,10 +1035,9 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
repositoryProperties: {},
};
const checkOverlayEnablementMacro = test.macro({
const checkOverlayEnablementMacro = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
setupOverrides: Partial<OverlayDatabaseModeTestSetup>,
expected:
| {
@@ -1091,7 +1091,7 @@ const checkOverlayEnablementMacro = test.macro({
sinon
.stub(codeql, "isTracedLanguage")
.callsFake(async (lang: Language) => {
return [KnownLanguage.java].includes(lang as KnownLanguage);
return lang === BuiltInLanguage.java;
});
// Mock git root detection
@@ -1131,11 +1131,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 +1145,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override - OverlayBase",
{
overlayDatabaseEnvVar: "overlay-base",
@@ -1158,8 +1156,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override - None",
{
overlayDatabaseEnvVar: "none",
@@ -1169,8 +1166,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Ignore invalid environment variable",
{
overlayDatabaseEnvVar: "invalid-mode",
@@ -1180,11 +1176,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Ignore feature flag when analyzing non-default branch",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
},
{
@@ -1192,11 +1187,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch when feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
isDefaultBranch: true,
},
@@ -1206,15 +1200,14 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch when feature enabled with custom analysis",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1223,11 +1216,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay-base database on default branch when code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1240,11 +1232,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if runner disk space is too low",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1260,11 +1251,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if we can't determine runner disk space",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1277,11 +1267,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1299,11 +1288,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1320,11 +1308,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1342,11 +1329,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1362,11 +1348,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch if memory flag is too low",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1379,11 +1364,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1398,11 +1382,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1417,11 +1400,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when cached status indicates previous failure",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisJavascript,
@@ -1435,11 +1417,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when cached status indicates previous failure",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisJavascript,
@@ -1453,18 +1434,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with disable-default-queries",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
"disable-default-queries": true,
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1472,18 +1452,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with packs",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1491,18 +1470,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with queries",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
queries: [{ uses: "some-query.ql" }],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1510,18 +1488,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when code-scanning feature enabled with query-filters",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
"query-filters": [{ include: { "security-severity": "high" } }],
} as UserConfig,
},
isDefaultBranch: true,
},
{
@@ -1529,11 +1506,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when only language-specific feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysisJavascript],
isDefaultBranch: true,
},
@@ -1542,11 +1518,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when only code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysisCodeScanningJavascript],
isDefaultBranch: true,
},
@@ -1555,11 +1530,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay-base database on default branch when language-specific feature disabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis],
isDefaultBranch: true,
},
@@ -1568,11 +1542,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR when feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
isPullRequest: true,
},
@@ -1582,15 +1555,14 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR when feature enabled with custom analysis",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1599,11 +1571,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR when code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1616,11 +1587,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR if runner disk space is too low",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1636,11 +1606,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1658,11 +1627,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR if we can't determine runner disk space",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1675,11 +1643,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR if memory flag is too low",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1692,11 +1659,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay analysis on PR if memory flag is too low but CodeQL >= 2.24.3",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1711,11 +1677,10 @@ 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: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
@@ -1730,18 +1695,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with disable-default-queries",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
"disable-default-queries": true,
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1749,18 +1713,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with packs",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
packs: ["some-custom-pack@1.0.0"],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1768,18 +1731,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with queries",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
queries: [{ uses: "some-query.ql" }],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1787,18 +1749,17 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when code-scanning feature enabled with query-filters",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [
Feature.OverlayAnalysis,
Feature.OverlayAnalysisCodeScanningJavascript,
],
codeScanningConfig: {
"query-filters": [{ include: { "security-severity": "high" } }],
} as UserConfig,
},
isPullRequest: true,
},
{
@@ -1806,11 +1767,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when only language-specific feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysisJavascript],
isPullRequest: true,
},
@@ -1819,11 +1779,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when only code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysisCodeScanningJavascript],
isPullRequest: true,
},
@@ -1832,11 +1791,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay analysis on PR when language-specific feature disabled",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis],
isPullRequest: true,
},
@@ -1845,8 +1803,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay PR analysis by env",
{
overlayDatabaseEnvVar: "overlay",
@@ -1857,8 +1814,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay PR analysis by env on a runner with low disk space",
{
overlayDatabaseEnvVar: "overlay",
@@ -1870,11 +1826,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay PR analysis by feature flag",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
isPullRequest: true,
},
@@ -1884,34 +1839,31 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to autobuild with traced language",
{
overlayDatabaseEnvVar: "overlay",
buildMode: BuildMode.Autobuild,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
},
{
disabledReason: OverlayDisabledReason.IncompatibleBuildMode,
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to no build mode with traced language",
{
overlayDatabaseEnvVar: "overlay",
buildMode: undefined,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
},
{
disabledReason: OverlayDisabledReason.IncompatibleBuildMode,
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to old CodeQL version",
{
overlayDatabaseEnvVar: "overlay",
@@ -1922,8 +1874,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to missing git root",
{
overlayDatabaseEnvVar: "overlay",
@@ -1934,8 +1885,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback due to old git version with submodules",
{
overlayDatabaseEnvVar: "overlay",
@@ -1947,8 +1897,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Fallback when git version cannot be determined and repo has submodules",
{
overlayDatabaseEnvVar: "overlay",
@@ -1960,8 +1909,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay enabled when git version cannot be determined and repo has no submodules",
{
overlayDatabaseEnvVar: "overlay",
@@ -1974,11 +1922,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"No overlay when disabled via repository property",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
isPullRequest: true,
repositoryProperties: {
@@ -1990,11 +1937,10 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Overlay not disabled when repository property is false",
{
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
isPullRequest: true,
repositoryProperties: {
@@ -2007,8 +1953,7 @@ test.serial(
},
);
test.serial(
checkOverlayEnablementMacro,
checkOverlayEnablementMacro.serial(
"Environment variable override takes precedence over repository property",
{
overlayDatabaseEnvVar: "overlay",
@@ -2023,9 +1968,8 @@ test.serial(
);
// Exercise language-specific overlay analysis features code paths
for (const language in KnownLanguage) {
test.serial(
checkOverlayEnablementMacro,
for (const language in BuiltInLanguage) {
checkOverlayEnablementMacro.serial(
`Check default overlay analysis feature for ${language}`,
{
languages: [language],
@@ -2042,11 +1986,10 @@ for (const language in KnownLanguage) {
// 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: [KnownLanguage.swift],
languages: [BuiltInLanguage.swift],
features: [Feature.OverlayAnalysis],
isPullRequest: true,
},
+9 -8
View File
@@ -48,7 +48,7 @@ import {
hasSubmodules,
isAnalyzingDefaultBranch,
} from "./git-utils";
import { KnownLanguage, Language } from "./languages";
import { BuiltInLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
import {
@@ -274,10 +274,10 @@ async function getSupportedLanguageMap(
for (const extractor of Object.keys(resolveResult.extractors)) {
// If the CLI supports resolving languages with default queries, use these
// as the set of supported languages. Otherwise, require the language to be
// a known language.
// a built-in language.
if (
resolveSupportedLanguagesUsingCli ||
KnownLanguage[extractor] !== undefined
BuiltInLanguage[extractor] !== undefined
) {
supportedLanguages[extractor] = extractor;
}
@@ -407,6 +407,7 @@ export async function getLanguages(
return languages;
}
/** Parses the `languages` input into a list of languages without checking if they are supported by CodeQL. */
export function getRawLanguagesNoAutodetect(
languagesInput: string | undefined,
): string[] {
@@ -947,7 +948,7 @@ async function validateOverlayDatabaseMode(
await Promise.all(
languages.map(
async (l) =>
l !== KnownLanguage.go && // Workaround to allow overlay analysis for Go with any build
l !== BuiltInLanguage.go && // Workaround to allow overlay analysis for Go with any build
// mode, since it does not yet support BMN. The Go autobuilder and/or extractor will
// ensure that overlay-base databases are only created for supported Go build setups,
// and that we'll fall back to full databases in other cases.
@@ -1036,13 +1037,13 @@ async function setCppTrapCachingEnvironmentVariables(
config: Config,
logger: Logger,
): Promise<void> {
if (config.languages.includes(KnownLanguage.cpp)) {
if (config.languages.includes(BuiltInLanguage.cpp)) {
const envVar = "CODEQL_EXTRACTOR_CPP_TRAP_CACHING";
if (process.env[envVar]) {
logger.info(
`Environment variable ${envVar} already set, leaving it unchanged.`,
);
} else if (config.trapCaches[KnownLanguage.cpp]) {
} else if (config.trapCaches[BuiltInLanguage.cpp]) {
logger.info("Enabling TRAP caching for C/C++.");
core.exportVariable(envVar, "true");
} else {
@@ -1539,7 +1540,7 @@ export async function parseBuildModeInput(
}
if (
languages.includes(KnownLanguage.csharp) &&
languages.includes(BuiltInLanguage.csharp) &&
(await features.getValue(Feature.DisableCsharpBuildless))
) {
logger.warning(
@@ -1549,7 +1550,7 @@ export async function parseBuildModeInput(
}
if (
languages.includes(KnownLanguage.java) &&
languages.includes(BuiltInLanguage.java) &&
(await features.getValue(Feature.DisableJavaBuildlessEnabled))
) {
logger.warning(
+68 -85
View File
@@ -1,12 +1,13 @@
import test, { ExecutionContext } from "ava";
import { RepositoryProperties } from "../feature-flags/properties";
import { KnownLanguage, Language } from "../languages";
import { BuiltInLanguage, Language } from "../languages";
import { getRunnerLogger } from "../logging";
import {
checkExpectedLogMessages,
getRecordingLogger,
LoggedMessage,
makeMacro,
} from "../testing-utils";
import { ConfigurationError, prettyPrintPack } from "../util";
@@ -15,7 +16,7 @@ import * as dbConfig from "./db-config";
/**
* Test macro for ensuring the packs block is valid
*/
const parsePacksMacro = test.macro({
const parsePacksMacro = makeMacro({
exec: (
t: ExecutionContext<unknown>,
packsInput: string,
@@ -33,7 +34,7 @@ const parsePacksMacro = test.macro({
/**
* Test macro for testing when the packs block is invalid
*/
const parsePacksErrorMacro = test.macro({
const parsePacksErrorMacro = makeMacro({
exec: (
t: ExecutionContext<unknown>,
packsInput: string,
@@ -49,45 +50,42 @@ const parsePacksErrorMacro = test.macro({
/**
* Test macro for testing when the packs block is invalid
*/
const invalidPackNameMacro = test.macro({
exec: (t: ExecutionContext, name: string) =>
parsePacksErrorMacro.exec(
const invalidPackNameMacro = makeMacro({
exec: (t: ExecutionContext, arg: string) =>
parsePacksErrorMacro.fn(
t,
name,
[KnownLanguage.cpp],
new RegExp(`^"${name}" is not a valid pack$`),
arg,
[BuiltInLanguage.cpp],
new RegExp(`^"${arg}" is not a valid pack$`),
),
title: (_providedTitle: string | undefined, arg: string | undefined) =>
`Invalid pack string: ${arg}`,
});
test("no packs", parsePacksMacro, "", [], undefined);
test("two packs", parsePacksMacro, "a/b,c/d@1.2.3", [KnownLanguage.cpp], {
[KnownLanguage.cpp]: ["a/b", "c/d@1.2.3"],
parsePacksMacro("no packs", "", [], undefined);
parsePacksMacro("two packs", "a/b,c/d@1.2.3", [BuiltInLanguage.cpp], {
[BuiltInLanguage.cpp]: ["a/b", "c/d@1.2.3"],
});
test(
parsePacksMacro(
"two packs with spaces",
parsePacksMacro,
" a/b , c/d@1.2.3 ",
[KnownLanguage.cpp],
[BuiltInLanguage.cpp],
{
[KnownLanguage.cpp]: ["a/b", "c/d@1.2.3"],
[BuiltInLanguage.cpp]: ["a/b", "c/d@1.2.3"],
},
);
test(
parsePacksErrorMacro(
"two packs with language",
parsePacksErrorMacro,
"a/b,c/d@1.2.3",
[KnownLanguage.cpp, KnownLanguage.java],
[BuiltInLanguage.cpp, BuiltInLanguage.java],
new RegExp(
"Cannot specify a 'packs' input in a multi-language analysis. " +
"Use a codeql-config.yml file instead and specify packs by language.",
),
);
test(
parsePacksMacro(
"packs with other valid names",
parsePacksMacro,
[
// ranges are ok
"c/d@1.0",
@@ -106,9 +104,9 @@ test(
// (globbing is not done)
"c/d@1.2.3:+*)_(",
].join(","),
[KnownLanguage.cpp],
[BuiltInLanguage.cpp],
{
[KnownLanguage.cpp]: [
[BuiltInLanguage.cpp]: [
"c/d@1.0",
"c/d@~1.0.0",
"c/d@~1.0.0:a/b",
@@ -123,23 +121,23 @@ test(
},
);
test(invalidPackNameMacro, "c"); // all packs require at least a scope and a name
test(invalidPackNameMacro, "c-/d");
test(invalidPackNameMacro, "-c/d");
test(invalidPackNameMacro, "c/d_d");
test(invalidPackNameMacro, "c/d@@");
test(invalidPackNameMacro, "c/d@1.0.0:");
test(invalidPackNameMacro, "c/d:");
test(invalidPackNameMacro, "c/d:/a");
test(invalidPackNameMacro, "@1.0.0:a");
test(invalidPackNameMacro, "c/d@../a");
test(invalidPackNameMacro, "c/d@b/../a");
test(invalidPackNameMacro, "c/d:z@1");
invalidPackNameMacro.test("c"); // all packs require at least a scope and a name
invalidPackNameMacro.test("c-/d");
invalidPackNameMacro.test("-c/d");
invalidPackNameMacro.test("c/d_d");
invalidPackNameMacro.test("c/d@@");
invalidPackNameMacro.test("c/d@1.0.0:");
invalidPackNameMacro.test("c/d:");
invalidPackNameMacro.test("c/d:/a");
invalidPackNameMacro.test("@1.0.0:a");
invalidPackNameMacro.test("c/d@../a");
invalidPackNameMacro.test("c/d@b/../a");
invalidPackNameMacro.test("c/d:z@1");
/**
* Test macro for pretty printing pack specs
*/
const packSpecPrettyPrintingMacro = test.macro({
const packSpecPrettyPrintingMacro = makeMacro({
exec: (t: ExecutionContext, packStr: string, packObj: dbConfig.Pack) => {
const parsed = dbConfig.parsePacksSpecification(packStr);
t.deepEqual(parsed, packObj, "parsed pack spec is correct");
@@ -163,36 +161,35 @@ const packSpecPrettyPrintingMacro = test.macro({
) => `Prettyprint pack spec: '${packStr}'`,
});
test(packSpecPrettyPrintingMacro, "a/b", {
packSpecPrettyPrintingMacro.test("a/b", {
name: "a/b",
version: undefined,
path: undefined,
});
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3", {
packSpecPrettyPrintingMacro.test("a/b@~1.2.3", {
name: "a/b",
version: "~1.2.3",
path: undefined,
});
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3:abc/def", {
packSpecPrettyPrintingMacro.test("a/b@~1.2.3:abc/def", {
name: "a/b",
version: "~1.2.3",
path: "abc/def",
});
test(packSpecPrettyPrintingMacro, "a/b:abc/def", {
packSpecPrettyPrintingMacro.test("a/b:abc/def", {
name: "a/b",
version: undefined,
path: "abc/def",
});
test(packSpecPrettyPrintingMacro, " a/b:abc/def ", {
packSpecPrettyPrintingMacro.test(" a/b:abc/def ", {
name: "a/b",
version: undefined,
path: "abc/def",
});
const calculateAugmentationMacro = test.macro({
const calculateAugmentationMacro = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
rawPacksInput: string | undefined,
rawQueriesInput: string | undefined,
languages: Language[],
@@ -207,27 +204,25 @@ const calculateAugmentationMacro = test.macro({
);
t.deepEqual(actualAugmentationProperties, expectedAugmentationProperties);
},
title: (_, title) => `Calculate Augmentation: ${title}`,
title: (title) => `Calculate Augmentation: ${title}`,
});
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"All empty",
undefined,
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
{
...dbConfig.defaultAugmentationProperties,
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With queries",
undefined,
" a, b , c, d",
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
{
...dbConfig.defaultAugmentationProperties,
@@ -235,12 +230,11 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With queries combining",
undefined,
" + a, b , c, d ",
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
{
...dbConfig.defaultAugmentationProperties,
@@ -249,12 +243,11 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With packs",
" codeql/a , codeql/b , codeql/c , codeql/d ",
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
{
...dbConfig.defaultAugmentationProperties,
@@ -262,12 +255,11 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With packs combining",
" + codeql/a, codeql/b, codeql/c, codeql/d",
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
{
...dbConfig.defaultAugmentationProperties,
@@ -276,12 +268,11 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With repo property queries",
undefined,
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{
"github-codeql-extra-queries": "a, b, c, d",
},
@@ -294,12 +285,11 @@ test(
},
);
test(
calculateAugmentationMacro,
calculateAugmentationMacro(
"With repo property queries combining",
undefined,
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{
"github-codeql-extra-queries": "+ a, b, c, d",
},
@@ -312,10 +302,9 @@ test(
},
);
const calculateAugmentationErrorMacro = test.macro({
const calculateAugmentationErrorMacro = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
rawPacksInput: string | undefined,
rawQueriesInput: string | undefined,
languages: Language[],
@@ -333,53 +322,48 @@ const calculateAugmentationErrorMacro = test.macro({
{ message: expectedError },
);
},
title: (_, title) => `Calculate Augmentation Error: ${title}`,
title: (title) => `Calculate Augmentation Error: ${title}`,
});
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Plus (+) with nothing else (queries)",
undefined,
" + ",
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
/The workflow property "queries" is invalid/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Plus (+) with nothing else (packs)",
" + ",
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
/The workflow property "packs" is invalid/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Plus (+) with nothing else (repo property queries)",
undefined,
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{
"github-codeql-extra-queries": " + ",
},
/The repository property "github-codeql-extra-queries" is invalid/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Packs input with multiple languages",
" + a/b, c/d ",
undefined,
[KnownLanguage.javascript, KnownLanguage.java],
[BuiltInLanguage.javascript, BuiltInLanguage.java],
{},
/Cannot specify a 'packs' input in a multi-language analysis/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Packs input with no languages",
" + a/b, c/d ",
undefined,
@@ -388,12 +372,11 @@ test(
/No languages specified/,
);
test(
calculateAugmentationErrorMacro,
calculateAugmentationErrorMacro(
"Invalid packs",
" a-pack-without-a-scope ",
undefined,
[KnownLanguage.javascript],
[BuiltInLanguage.javascript],
{},
/"a-pack-without-a-scope" is not a valid pack/,
);
+2 -2
View File
@@ -12,7 +12,7 @@ import { createStubCodeQL } from "./codeql";
import { Config } from "./config-utils";
import { cleanupAndUploadDatabases } from "./database-upload";
import * as gitUtils from "./git-utils";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { RepositoryNwo } from "./repository";
import {
checkExpectedLogMessages,
@@ -45,7 +45,7 @@ const testApiDetails: GitHubApiDetails = {
function getTestConfig(tmpDir: string): Config {
return createTestConfig({
languages: [KnownLanguage.javascript],
languages: [BuiltInLanguage.javascript],
dbLocation: tmpDir,
});
}
+1 -1
View File
@@ -263,7 +263,7 @@ export function getArtifactSuffix(matrix: string | undefined): string {
try {
const matrixObject = JSON.parse(matrix);
if (json.isObject(matrixObject)) {
for (const matrixKey of Object.keys(matrixObject as object).sort())
for (const matrixKey of Object.keys(matrixObject).sort())
suffix += `-${matrixObject[matrixKey]}`;
} else {
core.warning("User-specified `matrix` input is not an object.");
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.25.2",
"cliVersion": "2.25.2",
"priorBundleVersion": "codeql-bundle-v2.25.1",
"priorCliVersion": "2.25.1"
"bundleVersion": "codeql-bundle-v2.25.4",
"cliVersion": "2.25.4",
"priorBundleVersion": "codeql-bundle-v2.25.3",
"priorCliVersion": "2.25.3"
}
+40 -32
View File
@@ -27,7 +27,7 @@ import {
CacheStoreResult,
} from "./dependency-caching";
import { Feature } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import {
setupTests,
createFeatures,
@@ -179,7 +179,7 @@ test("checkHashPatterns - logs when no patterns match", async (t) => {
const result = await checkHashPatterns(
codeql,
features,
KnownLanguage.csharp,
BuiltInLanguage.csharp,
config,
"download",
getRecordingLogger(messages),
@@ -208,7 +208,7 @@ test("checkHashPatterns - returns patterns when patterns match", async (t) => {
const result = await checkHashPatterns(
codeql,
features,
KnownLanguage.csharp,
BuiltInLanguage.csharp,
config,
"upload",
getRecordingLogger(messages),
@@ -270,7 +270,7 @@ test.serial(
const keyWithFeature = await cacheKey(
codeql,
createFeatures([Feature.CsharpNewCacheKey]),
KnownLanguage.csharp,
BuiltInLanguage.csharp,
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
[],
);
@@ -288,12 +288,12 @@ test.serial(
const result = await downloadDependencyCaches(
codeql,
createFeatures([]),
[KnownLanguage.csharp],
[BuiltInLanguage.csharp],
logger,
);
const statusReport = result.statusReport;
t.is(statusReport.length, 1);
t.is(statusReport[0].language, KnownLanguage.csharp);
t.is(statusReport[0].language, BuiltInLanguage.csharp);
t.is(statusReport[0].hit_kind, CacheHitKind.Miss);
t.deepEqual(result.restoredKeys, []);
t.assert(restoreCacheStub.calledOnce);
@@ -316,7 +316,7 @@ test.serial(
const keyWithFeature = await cacheKey(
codeql,
features,
KnownLanguage.csharp,
BuiltInLanguage.csharp,
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
[],
);
@@ -334,14 +334,14 @@ test.serial(
const result = await downloadDependencyCaches(
codeql,
features,
[KnownLanguage.csharp],
[BuiltInLanguage.csharp],
logger,
);
// Check that the status report for telemetry indicates that one cache was restored with an exact match.
const statusReport = result.statusReport;
t.is(statusReport.length, 1);
t.is(statusReport[0].language, KnownLanguage.csharp);
t.is(statusReport[0].language, BuiltInLanguage.csharp);
t.is(statusReport[0].hit_kind, CacheHitKind.Exact);
// Check that the restored key has been returned.
@@ -380,7 +380,7 @@ test.serial(
const keyWithFeature = await cacheKey(
codeql,
features,
KnownLanguage.csharp,
BuiltInLanguage.csharp,
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
[],
);
@@ -398,14 +398,14 @@ test.serial(
const result = await downloadDependencyCaches(
codeql,
features,
[KnownLanguage.csharp],
[BuiltInLanguage.csharp],
logger,
);
// Check that the status report for telemetry indicates that one cache was restored with a partial match.
const statusReport = result.statusReport;
t.is(statusReport.length, 1);
t.is(statusReport[0].language, KnownLanguage.csharp);
t.is(statusReport[0].language, BuiltInLanguage.csharp);
t.is(statusReport[0].hit_kind, CacheHitKind.Partial);
// Check that the restored key has been returned.
@@ -426,7 +426,7 @@ test("uploadDependencyCaches - skips upload for a language with no cache config"
const logger = getRecordingLogger(messages);
const features = createFeatures([]);
const config = createTestConfig({
languages: [KnownLanguage.actions],
languages: [BuiltInLanguage.actions],
});
const result = await uploadDependencyCaches(codeql, features, config, logger);
@@ -444,7 +444,7 @@ test.serial(
const logger = getRecordingLogger(messages);
const features = createFeatures([]);
const config = createTestConfig({
languages: [KnownLanguage.go],
languages: [BuiltInLanguage.go],
});
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
@@ -457,7 +457,7 @@ test.serial(
logger,
);
t.is(result.length, 1);
t.is(result[0].language, KnownLanguage.go);
t.is(result[0].language, BuiltInLanguage.go);
t.is(result[0].result, CacheStoreResult.NoHash);
},
);
@@ -483,12 +483,12 @@ test.serial(
const primaryCacheKey = await cacheKey(
codeql,
features,
KnownLanguage.csharp,
BuiltInLanguage.csharp,
CSHARP_BASE_PATTERNS,
);
const config = createTestConfig({
languages: [KnownLanguage.csharp],
languages: [BuiltInLanguage.csharp],
dependencyCachingRestoredKeys: [primaryCacheKey],
});
@@ -499,7 +499,7 @@ test.serial(
logger,
);
t.is(result.length, 1);
t.is(result[0].language, KnownLanguage.csharp);
t.is(result[0].language, BuiltInLanguage.csharp);
t.is(result[0].result, CacheStoreResult.Duplicate);
},
);
@@ -525,7 +525,7 @@ test.serial(
sinon.stub(cachingUtils, "getTotalCacheSize").resolves(0);
const config = createTestConfig({
languages: [KnownLanguage.csharp],
languages: [BuiltInLanguage.csharp],
});
const result = await uploadDependencyCaches(
@@ -535,7 +535,7 @@ test.serial(
logger,
);
t.is(result.length, 1);
t.is(result[0].language, KnownLanguage.csharp);
t.is(result[0].language, BuiltInLanguage.csharp);
t.is(result[0].result, CacheStoreResult.Empty);
checkExpectedLogMessages(t, messages, [
@@ -566,7 +566,7 @@ test.serial(
sinon.stub(actionsCache, "saveCache").resolves();
const config = createTestConfig({
languages: [KnownLanguage.csharp],
languages: [BuiltInLanguage.csharp],
});
const result = await uploadDependencyCaches(
@@ -576,7 +576,7 @@ test.serial(
logger,
);
t.is(result.length, 1);
t.is(result[0].language, KnownLanguage.csharp);
t.is(result[0].language, BuiltInLanguage.csharp);
t.is(result[0].result, CacheStoreResult.Stored);
t.is(result[0].upload_size_bytes, 1024);
@@ -608,7 +608,7 @@ test.serial(
.throws(new actionsCache.ReserveCacheError("Already in use"));
const config = createTestConfig({
languages: [KnownLanguage.csharp],
languages: [BuiltInLanguage.csharp],
});
await t.notThrowsAsync(async () => {
@@ -619,7 +619,7 @@ test.serial(
logger,
);
t.is(result.length, 1);
t.is(result[0].language, KnownLanguage.csharp);
t.is(result[0].language, BuiltInLanguage.csharp);
t.is(result[0].result, CacheStoreResult.Duplicate);
checkExpectedLogMessages(t, messages, ["Not uploading cache for"]);
@@ -647,7 +647,7 @@ test.serial("uploadDependencyCaches - throws other exceptions", async (t) => {
sinon.stub(actionsCache, "saveCache").throws();
const config = createTestConfig({
languages: [KnownLanguage.csharp],
languages: [BuiltInLanguage.csharp],
});
await t.throwsAsync(async () => {
@@ -659,7 +659,7 @@ test("getFeaturePrefix - returns empty string if no features are enabled", async
const codeql = createStubCodeQL({});
const features = createFeatures([]);
for (const knownLanguage of Object.values(KnownLanguage)) {
for (const knownLanguage of Object.values(BuiltInLanguage)) {
const result = await getFeaturePrefix(codeql, features, knownLanguage);
t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`);
}
@@ -669,7 +669,11 @@ test("getFeaturePrefix - C# - returns prefix if CsharpNewCacheKey is enabled", a
const codeql = createStubCodeQL({});
const features = createFeatures([Feature.CsharpNewCacheKey]);
const result = await getFeaturePrefix(codeql, features, KnownLanguage.csharp);
const result = await getFeaturePrefix(
codeql,
features,
BuiltInLanguage.csharp,
);
t.notDeepEqual(result, "");
t.assert(result.endsWith("-"));
// Check the length of the prefix, which should correspond to `cacheKeyHashLength` + 1 for the trailing `-`.
@@ -680,9 +684,9 @@ test("getFeaturePrefix - non-C# - returns '' if CsharpNewCacheKey is enabled", a
const codeql = createStubCodeQL({});
const features = createFeatures([Feature.CsharpNewCacheKey]);
for (const knownLanguage of Object.values(KnownLanguage)) {
for (const knownLanguage of Object.values(BuiltInLanguage)) {
// Skip C# since we expect a result for it, which is tested in the previous test.
if (knownLanguage === KnownLanguage.csharp) {
if (knownLanguage === BuiltInLanguage.csharp) {
continue;
}
const result = await getFeaturePrefix(codeql, features, knownLanguage);
@@ -694,7 +698,11 @@ test("getFeaturePrefix - C# - returns prefix if CsharpCacheBuildModeNone is enab
const codeql = createStubCodeQL({});
const features = createFeatures([Feature.CsharpCacheBuildModeNone]);
const result = await getFeaturePrefix(codeql, features, KnownLanguage.csharp);
const result = await getFeaturePrefix(
codeql,
features,
BuiltInLanguage.csharp,
);
t.notDeepEqual(result, "");
t.assert(result.endsWith("-"));
// Check the length of the prefix, which should correspond to `cacheKeyHashLength` + 1 for the trailing `-`.
@@ -705,9 +713,9 @@ test("getFeaturePrefix - non-C# - returns '' if CsharpCacheBuildModeNone is enab
const codeql = createStubCodeQL({});
const features = createFeatures([Feature.CsharpCacheBuildModeNone]);
for (const knownLanguage of Object.values(KnownLanguage)) {
for (const knownLanguage of Object.values(BuiltInLanguage)) {
// Skip C# since we expect a result for it, which is tested in the previous test.
if (knownLanguage === KnownLanguage.csharp) {
if (knownLanguage === BuiltInLanguage.csharp) {
continue;
}
const result = await getFeaturePrefix(codeql, features, knownLanguage);
+2 -2
View File
@@ -11,7 +11,7 @@ import { CodeQL } from "./codeql";
import { Config } from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
import { BuiltInLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { getErrorMessage, getRequiredEnvParam } from "./util";
@@ -541,7 +541,7 @@ export async function getFeaturePrefix(
}
};
if (language === KnownLanguage.csharp) {
if (language === BuiltInLanguage.csharp) {
await addFeatureIfEnabled(Feature.CsharpNewCacheKey);
await addFeatureIfEnabled(Feature.CsharpCacheBuildModeNone);
}
+17 -2
View File
@@ -72,6 +72,13 @@ let unwrittenDiagnostics: UnwrittenDiagnostic[] = [];
*/
let unwrittenDefaultLanguageDiagnostics: DiagnosticMessage[] = [];
/**
* Counter used to generate a unique suffix for each diagnostic filename, so that
* two diagnostics produced within the same millisecond do not overwrite each
* other on disk.
*/
let diagnosticCounter = 0;
/**
* Constructs a new diagnostic message with the specified id and name, as well as optional additional data.
*
@@ -167,10 +174,18 @@ function writeDiagnostic(
// Create the directory if it doesn't exist yet.
mkdirSync(diagnosticsPath, { recursive: true });
// Include a monotonically increasing suffix to avoid filename collisions
// between diagnostics produced within the same millisecond.
const uniqueSuffix = (diagnosticCounter++).toString();
// We should only need to remove colons, but to be defensive, only allow a restricted set of
// characters.
const sanitizedTimestamp = diagnostic.timestamp.replace(
/[^a-zA-Z0-9.-]/g,
"",
);
const jsonPath = path.resolve(
diagnosticsPath,
// Remove colons from the timestamp as these are not allowed in Windows filenames.
`codeql-action-${diagnostic.timestamp.replaceAll(":", "")}.json`,
`codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`,
);
writeFileSync(jsonPath, JSON.stringify(diagnostic));
+12 -21
View File
@@ -16,6 +16,7 @@ import {
mockCodeQLVersion,
mockFeatureFlagApiEndpoint,
setupActionsVars,
makeMacro,
} from "./testing-utils";
import { GitHubVariant, withTmpDir } from "./util";
import type { GitHubVersion } from "./util";
@@ -42,10 +43,9 @@ const defaultTestCase: DiffInformedAnalysisTestCase = {
codeQLVersion: "2.21.0",
};
const testShouldPerformDiffInformedAnalysis = test.macro({
const testShouldPerformDiffInformedAnalysis = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
partialTestCase: Partial<DiffInformedAnalysisTestCase>,
expectedResult: boolean,
) => {
@@ -94,18 +94,16 @@ const testShouldPerformDiffInformedAnalysis = test.macro({
getPullRequestBranchesStub.restore();
});
},
title: (_, title) => `shouldPerformDiffInformedAnalysis: ${title}`,
title: (title) => `shouldPerformDiffInformedAnalysis: ${title}`,
});
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns true in the default test case",
{},
true,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false when feature flag is disabled from the API",
{
featureEnabled: false,
@@ -113,8 +111,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false when CODEQL_ACTION_DIFF_INFORMED_QUERIES is set to false",
{
featureEnabled: true,
@@ -123,8 +120,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns true when CODEQL_ACTION_DIFF_INFORMED_QUERIES is set to true",
{
featureEnabled: false,
@@ -133,8 +129,7 @@ test.serial(
true,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false for CodeQL version 2.20.0",
{
codeQLVersion: "2.20.0",
@@ -142,8 +137,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false for invalid GHES version",
{
gitHubVersion: {
@@ -154,8 +148,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false for GHES version 3.18.5",
{
gitHubVersion: {
@@ -166,8 +159,7 @@ test.serial(
false,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns true for GHES version 3.19.0",
{
gitHubVersion: {
@@ -178,8 +170,7 @@ test.serial(
true,
);
test.serial(
testShouldPerformDiffInformedAnalysis,
testShouldPerformDiffInformedAnalysis.serial(
"returns false when not a pull request",
{
pullRequestBranches: undefined,
+1
View File
@@ -8,6 +8,7 @@ export enum DocUrl {
CODEQL_BUILD_MODES = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#codeql-build-modes",
DEFINE_ENV_VARIABLES = "https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow",
DELETE_ACTIONS_CACHE_ENTRIES = "https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manage-caches#deleting-cache-entries",
PRIVATE_REGISTRY_LOGS = "https://docs.github.com/en/code-security/reference/code-scanning/code-scanning-logs#diagnostic-information-for-private-package-registries",
SCANNING_ON_PUSH = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#scanning-on-push",
SPECIFY_BUILD_STEPS_MANUALLY = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#about-specifying-build-steps-manually",
SYSTEM_REQUIREMENTS = "https://codeql.github.com/docs/codeql-overview/system-requirements/",
+27 -12
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,
});
+75 -24
View File
@@ -29,9 +29,32 @@ const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
*/
export const CODEQL_VERSION_ZSTD_BUNDLE = "2.19.0";
export interface CodeQLDefaultVersionInfo {
const LINKED_CODEQL_VERSION: CodeQLVersionInfo = {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
};
export interface CodeQLVersionInfo {
/** The version number of the CodeQL CLI, e.g. `2.19.0`. */
cliVersion: string;
/**
* The tag name of the CodeQL Bundle associated with this version, e.g. `codeql-bundle-v2.19.0`.
*/
tagName: string;
}
export interface CodeQLDefaultVersionInfo {
/**
* CodeQL CLI versions that are enabled as defaults, sorted from highest to lowest.
*
* Guaranteed to be non-empty. When feature flags are unavailable, this falls back to a single
* entry containing the version pinned in `defaults.json`.
*/
enabledVersions: CodeQLVersionInfo[];
/**
* If accessed, whether the tools feature flags are valid, i.e. contain at least one enabled
* version.
*/
toolsFeatureFlagsValid?: boolean;
}
@@ -72,6 +95,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.
@@ -277,6 +313,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 +392,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 +421,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 +435,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 +567,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 +649,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 +680,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 +688,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,
};
}
+6 -8
View File
@@ -19,6 +19,7 @@ import {
createFeatures,
createTestConfig,
DEFAULT_ACTIONS_VARS,
makeMacro,
makeVersionInfo,
RecordingLogger,
setupActionsVars,
@@ -601,7 +602,7 @@ async function testFailedSarifUpload(
uploadFiles.resolves({
sarifID: "42",
statusReport: { raw_upload_size_bytes: 20, zipped_upload_size_bytes: 10 },
} as uploadLib.UploadResult);
});
const waitForProcessing = sinon.stub(uploadLib, "waitForProcessing");
const features = [] as Feature[];
@@ -796,7 +797,7 @@ test.serial(
},
);
const skippedUploadTest = test.macro({
const skippedUploadTest = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
config: Partial<configUtils.Config>,
@@ -823,9 +824,8 @@ const skippedUploadTest = test.macro({
`tryUploadSarifIfRunFailed - skips upload ${providedTitle}`,
});
test.serial(
skippedUploadTest.serial(
"without CodeQL command",
skippedUploadTest,
// No codeQLCmd
{
analysisKinds: [AnalysisKind.RiskAssessment],
@@ -834,9 +834,8 @@ test.serial(
"CodeQL command not found",
);
test.serial(
skippedUploadTest.serial(
"if no language is configured",
skippedUploadTest,
// No explicit language configuration
{
analysisKinds: [AnalysisKind.RiskAssessment],
@@ -845,9 +844,8 @@ test.serial(
"Unexpectedly, the configuration is not for a single language.",
);
test.serial(
skippedUploadTest.serial(
"if multiple languages is configured",
skippedUploadTest,
// Multiple explicit languages configured
{
analysisKinds: [AnalysisKind.RiskAssessment],
+32 -20
View File
@@ -58,7 +58,7 @@ import {
initConfig,
runDatabaseInitCluster,
} from "./init";
import { JavaEnvVars, KnownLanguage } from "./languages";
import { JavaEnvVars, BuiltInLanguage } from "./languages";
import { getActionsLogger, Logger, withGroupAsync } from "./logging";
import {
downloadOverlayBaseDatabaseFromCache,
@@ -298,16 +298,23 @@ async function run(startedAt: Date) {
);
}
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);
const codeQLDefaultVersionInfo =
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
const rawLanguages = configUtils.getRawLanguagesNoAutodetect(
getOptionalInput("languages"),
);
const useOverlayAwareDefaultCliVersion = !!analysisKinds?.includes(
AnalysisKind.CodeScanning,
);
const initCodeQLResult = await initCodeQL(
getOptionalInput("tools"),
apiDetails,
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
);
@@ -330,7 +337,7 @@ async function run(startedAt: Date) {
// requested rust - don't enable it via language autodetection.
configUtils
.getRawLanguagesNoAutodetect(getOptionalInput("languages"))
.includes(KnownLanguage.rust)
.includes(BuiltInLanguage.rust)
) {
const experimental = "2.19.3";
const publicPreview = "2.22.1";
@@ -390,7 +397,7 @@ async function run(startedAt: Date) {
});
if (
config.languages.includes(KnownLanguage.swift) &&
config.languages.includes(BuiltInLanguage.swift) &&
process.platform !== "darwin"
) {
throw new ConfigurationError(
@@ -465,18 +472,23 @@ async function run(startedAt: Date) {
// necessary preparations. So, in that mode, we would assume that
// everything is in order and let the analysis fail if that turns out not
// to be the case.
overlayBaseDatabaseStats = await downloadOverlayBaseDatabaseFromCache(
codeql,
config,
logger,
await withGroupAsync(
"Checking cache for overlay-base database",
async () => {
overlayBaseDatabaseStats = await downloadOverlayBaseDatabaseFromCache(
codeql,
config,
logger,
);
if (!overlayBaseDatabaseStats) {
config.overlayDatabaseMode = OverlayDatabaseMode.None;
logger.info(
"No overlay-base database found in cache, " +
`reverting overlay database mode to ${OverlayDatabaseMode.None}.`,
);
}
},
);
if (!overlayBaseDatabaseStats) {
config.overlayDatabaseMode = OverlayDatabaseMode.None;
logger.info(
"No overlay-base database found in cache, " +
`reverting overlay database mode to ${OverlayDatabaseMode.None}.`,
);
}
}
if (config.overlayDatabaseMode !== OverlayDatabaseMode.Overlay) {
@@ -509,7 +521,7 @@ async function run(startedAt: Date) {
}
if (
config.languages.includes(KnownLanguage.go) &&
config.languages.includes(BuiltInLanguage.go) &&
process.platform === "linux"
) {
try {
@@ -567,7 +579,7 @@ async function run(startedAt: Date) {
if (e instanceof FileCmdNotFoundError) {
addDiagnostic(
config,
KnownLanguage.go,
BuiltInLanguage.go,
makeDiagnostic(
"go/workflow/file-program-unavailable",
"The `file` program is required on Linux, but does not appear to be installed",
@@ -661,7 +673,7 @@ async function run(startedAt: Date) {
(await codeQlVersionAtLeast(codeql, CODEQL_VERSION_JAR_MINIMIZATION)) &&
config.dependencyCachingEnabled &&
config.buildMode === BuildMode.None &&
config.languages.includes(KnownLanguage.java)
config.languages.includes(BuiltInLanguage.java)
) {
core.exportVariable(
EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS,
+49 -61
View File
@@ -15,13 +15,14 @@ import {
getFileCoverageInformationEnabled,
logFileCoverageOnPrsDeprecationWarning,
} from "./init";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import {
createFeatures,
LoggedMessage,
createTestConfig,
getRecordingLogger,
setupTests,
makeMacro,
} from "./testing-utils";
import { ConfigurationError, withTmpDir } from "./util";
@@ -152,16 +153,15 @@ test("cleanupDatabaseClusterDirectory can disable warning with options", async (
});
type PackInfo = {
language: KnownLanguage;
language: BuiltInLanguage;
packinfoContents: string | undefined;
sourceOnlyPack?: boolean;
qlpackFileName?: string;
};
const testCheckPacksForOverlayCompatibility = test.macro({
const testCheckPacksForOverlayCompatibility = makeMacro({
exec: async (
t: ExecutionContext,
_title: string,
{
cliOverlayVersion,
languages,
@@ -169,13 +169,13 @@ const testCheckPacksForOverlayCompatibility = test.macro({
expectedResult,
}: {
cliOverlayVersion: number | undefined;
languages: KnownLanguage[];
languages: BuiltInLanguage[];
packs: Record<string, PackInfo>;
expectedResult: boolean;
},
) => {
await withTmpDir(async (tmpDir) => {
const packDirsByLanguage = new Map<KnownLanguage, string[]>();
const packDirsByLanguage = new Map<BuiltInLanguage, string[]>();
for (const [packName, packInfo] of Object.entries(packs)) {
const packPath = path.join(tmpDir, packName);
@@ -234,18 +234,17 @@ const testCheckPacksForOverlayCompatibility = test.macro({
);
});
},
title: (_, title) => `checkPacksForOverlayCompatibility: ${title}`,
title: (title) => `checkPacksForOverlayCompatibility: ${title}`,
});
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns false when CLI does not support overlay",
{
cliOverlayVersion: undefined,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
},
@@ -253,26 +252,24 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns true when there are no query packs",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {},
expectedResult: true,
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns true when query pack has not been compiled",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: undefined,
sourceOnlyPack: true,
},
@@ -281,15 +278,14 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns true when query pack has expected overlay version",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
},
@@ -297,19 +293,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns true when query packs for all languages to analyze are compatible",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.cpp, KnownLanguage.java],
languages: [BuiltInLanguage.cpp, BuiltInLanguage.java],
packs: {
"codeql/cpp-queries": {
language: KnownLanguage.cpp,
language: BuiltInLanguage.cpp,
packinfoContents: '{"overlayVersion":2}',
},
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
},
@@ -317,19 +312,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns true when query pack for a language not analyzed is incompatible",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/cpp-queries": {
language: KnownLanguage.cpp,
language: BuiltInLanguage.cpp,
packinfoContents: undefined,
},
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
},
@@ -337,19 +331,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns false when query pack for a language to analyze is incompatible",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.cpp, KnownLanguage.java],
languages: [BuiltInLanguage.cpp, BuiltInLanguage.java],
packs: {
"codeql/cpp-queries": {
language: KnownLanguage.cpp,
language: BuiltInLanguage.cpp,
packinfoContents: '{"overlayVersion":1}',
},
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
},
@@ -357,19 +350,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns false when query pack is missing .packinfo",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
"custom/queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: undefined,
},
},
@@ -377,19 +369,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns false when query pack has different overlay version",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
"custom/queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":1}',
},
},
@@ -397,19 +388,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns false when query pack is missing overlayVersion in .packinfo",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
"custom/queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: "{}",
},
},
@@ -417,19 +407,18 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns false when .packinfo is not valid JSON",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
},
"custom/queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: "this_is_not_valid_json",
},
},
@@ -437,15 +426,14 @@ test(
},
);
test(
testCheckPacksForOverlayCompatibility,
testCheckPacksForOverlayCompatibility(
"returns true when query pack uses codeql-pack.yml filename",
{
cliOverlayVersion: 2,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
packs: {
"codeql/java-queries": {
language: KnownLanguage.java,
language: BuiltInLanguage.java,
packinfoContents: '{"overlayVersion":2}',
qlpackFileName: "codeql-pack.yml",
},
+6 -2
View File
@@ -26,7 +26,7 @@ import {
RepositoryProperties,
RepositoryPropertyName,
} from "./feature-flags/properties";
import { KnownLanguage, Language } from "./languages";
import { BuiltInLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
import { ToolsSource } from "./setup-codeql";
import { ZstdAvailability } from "./tar";
@@ -39,6 +39,8 @@ export async function initCodeQL(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
useOverlayAwareDefaultCliVersion: boolean,
features: FeatureEnablement,
logger: Logger,
): Promise<{
@@ -61,6 +63,8 @@ export async function initCodeQL(
tempDir,
variant,
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
true,
@@ -235,7 +239,7 @@ export async function checkInstallPython311(
codeql: CodeQL,
) {
if (
languages.includes(KnownLanguage.python) &&
languages.includes(BuiltInLanguage.python) &&
process.platform === "win32" &&
!(await codeql.getVersion()).features?.supportsPython312
) {
+46
View File
@@ -0,0 +1,46 @@
import test from "ava";
import { setupTests } from "../testing-utils";
import * as json from ".";
setupTests(test);
const testSchema = {
requiredKey: json.string,
};
const optionalSchema = {
optionalKey: json.optional(json.string),
};
test("validateSchema - required properties are required", async (t) => {
t.false(json.validateSchema(testSchema, {}));
t.false(json.validateSchema(testSchema, { requiredKey: undefined }));
t.false(json.validateSchema(testSchema, { requiredKey: null }));
t.false(json.validateSchema(testSchema, { requiredKey: 0 }));
t.false(json.validateSchema(testSchema, { requiredKey: 123 }));
t.false(json.validateSchema(testSchema, { requiredKey: false }));
t.false(json.validateSchema(testSchema, { requiredKey: true }));
t.false(json.validateSchema(testSchema, { requiredKey: [] }));
t.false(json.validateSchema(testSchema, { requiredKey: {} }));
t.true(json.validateSchema(testSchema, { requiredKey: "" }));
t.true(json.validateSchema(testSchema, { requiredKey: "foo" }));
});
test("validateSchema - optional properties are optional", async (t) => {
// Optional fields may be absent
t.true(json.validateSchema(optionalSchema, {}));
t.true(json.validateSchema(optionalSchema, { optionalKey: undefined }));
t.true(json.validateSchema(optionalSchema, { optionalKey: null }));
// But, if present, should have the expected type
t.false(json.validateSchema(optionalSchema, { optionalKey: 0 }));
t.false(json.validateSchema(optionalSchema, { optionalKey: 123 }));
t.false(json.validateSchema(optionalSchema, { optionalKey: false }));
t.false(json.validateSchema(optionalSchema, { optionalKey: true }));
t.false(json.validateSchema(optionalSchema, { optionalKey: [] }));
t.false(json.validateSchema(optionalSchema, { optionalKey: {} }));
t.true(json.validateSchema(optionalSchema, { optionalKey: "" }));
t.true(json.validateSchema(optionalSchema, { optionalKey: "foo" }));
});
+79
View File
@@ -36,3 +36,82 @@ export function isStringOrUndefined(
): value is string | undefined {
return value === undefined || isString(value);
}
/**
* Represents a field of type `T` in a schema.
* Carries a validation function and flag indicating whether the field is required or not.
*/
export type Validator<T> = {
validate: (val: unknown) => val is T;
required: boolean;
};
/** Extracts `T` from `Validator<T>`. */
export type UnwrapValidator<V> = V extends Validator<infer A> ? A : never;
/** A validator for string fields in schemas. */
export const string = {
validate: isString,
required: true,
} as const satisfies Validator<string>;
/** Transforms a validator to be optional. */
export function optional<T>(validator: Validator<T>) {
return {
validate: (val: unknown) => {
return val === undefined || val === null || validator.validate(val);
},
required: false,
} as const satisfies Validator<T | undefined | null>;
}
/** Represents an arbitrary object schema. */
export type Schema = Record<string, Validator<any>>;
/** Extracts the required keys from `S`. */
export type RequiredKeys<S extends Schema> = {
[K in keyof S]: S[K]["required"] extends true ? K : never;
}[keyof S];
/** Extracts optional keys from `S`. */
export type OptionalKeys<S extends Schema> = {
[K in keyof S]: S[K]["required"] extends true ? never : K;
}[keyof S];
/** Constructs an object type corresponding to a schema. */
export type FromSchema<S extends Schema> = {
[K in RequiredKeys<S>]: UnwrapValidator<S[K]>;
} & { [K in OptionalKeys<S>]?: UnwrapValidator<S[K]> };
/**
* Validates that `obj` satisfies at least `schema`. Additional keys are accepted.
*
* @param schema The schema to validate against.
* @param obj The object to validate.
* @returns Asserts that `obj` is of the `schema`'s type if validation is successful.
*/
export function validateSchema<S extends Schema>(
schema: S,
obj: UnvalidatedObject<any>,
): obj is FromSchema<S> {
for (const [key, validator] of Object.entries(schema)) {
const hasKey = key in obj;
// If the property is required, but absent, fail.
if (validator.required && !hasKey) {
return false;
}
// If the property is required, but undefined or null, fail.
if (validator.required && (obj[key] === undefined || obj[key] === null)) {
return false;
}
// If the property is present, validate it.
if (hasKey && !validator.validate(obj[key])) {
return false;
}
}
return true;
}
+106
View File
@@ -0,0 +1,106 @@
import { ExecutionContext } from "ava";
import * as json from ".";
/**
* Constructs an object based on `schema` for unit tests.
* Assumes that all keys in `schema` have string values.
*
* @param includeOptional Whether to include optional properties.
* @param schema The schema to base the object on.
* @returns An object that satisfies `schema`.
*/
export function makeFromSchema<S extends json.Schema>(
includeOptional: boolean,
schema: S,
): json.FromSchema<S> {
const result = {};
for (const [key, validator] of Object.entries(schema)) {
if (!validator.required && !includeOptional) {
continue;
}
result[key] = `value-for-${key}`;
}
return result as json.FromSchema<S>;
}
/** Options for `withSchemaMatrix`. */
export interface SchemaMatrixOptions {
/** Whether cases where the properties are entirely absent should be excluded. */
excludeAbsent?: boolean;
}
/**
* Constructs a test matrix of possible objects for `schema`: all required properties
* plus all permutations of possible states for the optional properties.
*
* @param schema The schema to construct a test matrix for.
* @param body The test body to call with each value from the test matrix.
*/
export function withSchemaMatrix<S extends json.Schema>(
t: ExecutionContext<any>,
schema: S,
opts: SchemaMatrixOptions,
body: (value: json.FromSchema<S>) => void,
): void {
// Construct a base object that includes all required properties.
const required = makeFromSchema(false, schema);
// Identify optional properties.
const optionalKeys: Array<keyof S> = [];
for (const [key, validator] of Object.entries(schema)) {
if (!validator.required) {
optionalKeys.push(key);
}
}
const optionalValues = (key: keyof S) => [
null,
undefined,
`value-for-${String(key)}`,
];
// Constructs an array of test objects, starting with `required` and combining it with all
// possible states of each optional property. For example, with default settings:
//
// For { requiredKey: string }, we get: `[{ requiredKey: "some-string-value" }]`
//
// For { requiredKey: string, optionalKey?: string }, we get:
// [ { requiredKey: "some-string-value" },
// { requiredKey: "some-string-value", optionalKey: undefined },
// { requiredKey: "some-string-value", optionalKey: null },
// { requiredKey: "some-string-value", optionalKey: "some-value" },
// ]
const permutations = (keys: Array<keyof S>) => {
if (keys.length === 0) return [required];
const bases = permutations(keys.slice(1));
const result: Array<json.FromSchema<S>> = [];
const optionalKey = keys[0];
for (const base of bases) {
if (!opts.excludeAbsent) {
// Optional keys can be absent entirely.
result.push(base);
}
// Or be present and have one of the `optionalValues`.
for (const optionalValue of optionalValues(optionalKey)) {
result.push({ ...base, [optionalKey]: optionalValue });
}
}
return result;
};
// Call `body` for all test cases.
const testCases = permutations(optionalKeys);
for (const testCase of testCases) {
try {
body(testCase);
} catch (err) {
t.log(testCase);
throw err;
}
}
}
-11
View File
@@ -1,11 +0,0 @@
{
"c": "cpp",
"c#": "csharp",
"c++": "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
"kotlin": "java",
"typescript": "javascript"
}
-29
View File
@@ -1,29 +0,0 @@
/** A language to analyze with CodeQL. */
export type Language = string;
/**
* A language supported by CodeQL that is treated specially by the Action.
*
* This is not an exhaustive list of languages supported by CodeQL and new
* languages do not need to be added here.
*/
export enum KnownLanguage {
actions = "actions",
cpp = "cpp",
csharp = "csharp",
go = "go",
java = "java",
javascript = "javascript",
python = "python",
ruby = "ruby",
rust = "rust",
swift = "swift",
}
/** Java-specific environment variable names that we may care about. */
export enum JavaEnvVars {
JAVA_HOME = "JAVA_HOME",
JAVA_TOOL_OPTIONS = "JAVA_TOOL_OPTIONS",
JDK_JAVA_OPTIONS = "JDK_JAVA_OPTIONS",
_JAVA_OPTIONS = "_JAVA_OPTIONS",
}
+25
View File
@@ -0,0 +1,25 @@
{
"languages": [
"actions",
"cpp",
"csharp",
"go",
"java",
"javascript",
"python",
"ruby",
"rust",
"swift"
],
"aliases": {
"c": "cpp",
"c-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "cpp",
"java-kotlin": "java",
"javascript-typescript": "javascript",
"kotlin": "java",
"typescript": "javascript"
}
}
+46
View File
@@ -0,0 +1,46 @@
import test from "ava";
import { setupTests } from "../testing-utils";
import knownLanguagesData from "./builtin.json";
import { isBuiltInLanguage, BuiltInLanguage, parseBuiltInLanguage } from ".";
setupTests(test);
test("parseBuiltInLanguage", (t) => {
// Exact matches
t.is(parseBuiltInLanguage("csharp"), BuiltInLanguage.csharp);
t.is(parseBuiltInLanguage("cpp"), BuiltInLanguage.cpp);
t.is(parseBuiltInLanguage("go"), BuiltInLanguage.go);
t.is(parseBuiltInLanguage("java"), BuiltInLanguage.java);
t.is(parseBuiltInLanguage("javascript"), BuiltInLanguage.javascript);
t.is(parseBuiltInLanguage("python"), BuiltInLanguage.python);
t.is(parseBuiltInLanguage("rust"), BuiltInLanguage.rust);
// Aliases
t.is(parseBuiltInLanguage(" \t\nCsHaRp\t\t"), BuiltInLanguage.csharp);
t.is(parseBuiltInLanguage("c"), BuiltInLanguage.cpp);
t.is(parseBuiltInLanguage("c++"), BuiltInLanguage.cpp);
t.is(parseBuiltInLanguage("kotlin"), BuiltInLanguage.java);
t.is(parseBuiltInLanguage("typescript"), BuiltInLanguage.javascript);
// spaces and case-insensitivity
t.is(parseBuiltInLanguage(" \t\nkOtLin\t\t"), BuiltInLanguage.java);
// Not matches
t.is(parseBuiltInLanguage(BuiltInLanguage.python), BuiltInLanguage.python);
t.is(parseBuiltInLanguage("foo"), undefined);
t.is(parseBuiltInLanguage(" "), undefined);
t.is(parseBuiltInLanguage(""), undefined);
});
test("isBuiltInLanguage matches the curated built-in language set", (t) => {
t.true(isBuiltInLanguage(BuiltInLanguage.actions));
t.true(isBuiltInLanguage(BuiltInLanguage.swift));
t.false(isBuiltInLanguage("typescript"));
});
test("BuiltInLanguage enum matches builtin.json", (t) => {
t.deepEqual(Object.values(BuiltInLanguage), knownLanguagesData.languages);
});
+57
View File
@@ -0,0 +1,57 @@
import knownLanguagesData from "./builtin.json";
/** A language to analyze with CodeQL. */
export type Language = string;
/** A language built into the `defaults.json` CodeQL distribution. */
export enum BuiltInLanguage {
actions = "actions",
cpp = "cpp",
csharp = "csharp",
go = "go",
java = "java",
javascript = "javascript",
python = "python",
ruby = "ruby",
rust = "rust",
swift = "swift",
}
/** Java-specific environment variable names that we may care about. */
export enum JavaEnvVars {
JAVA_HOME = "JAVA_HOME",
JAVA_TOOL_OPTIONS = "JAVA_TOOL_OPTIONS",
JDK_JAVA_OPTIONS = "JDK_JAVA_OPTIONS",
_JAVA_OPTIONS = "_JAVA_OPTIONS",
}
const builtInLanguageSet = new Set<string>(knownLanguagesData.languages);
export function isBuiltInLanguage(
language: string,
): language is BuiltInLanguage {
return builtInLanguageSet.has(language);
}
/**
* Parse a language input corresponding to a built-in language into its canonical CodeQL language
* name.
*
* This uses the language aliases shipped with the Action and will not be able to resolve aliases
* added by third-party CodeQL language support or versions of the CodeQL CLI newer than the one
* mentioned in `defaults.json`. Therefore, this function should only be used when the CodeQL CLI is
* not available.
*/
export function parseBuiltInLanguage(
language: string,
): BuiltInLanguage | undefined {
language = language.trim().toLowerCase();
language =
knownLanguagesData.aliases[
language as keyof typeof knownLanguagesData.aliases
] ?? language;
if (isBuiltInLanguage(language)) {
return language;
}
return undefined;
}
+175 -28
View File
@@ -7,12 +7,13 @@ import * as sinon from "sinon";
import * as actionsUtil from "../actions-util";
import * as apiClient from "../api-client";
import { ResolveDatabaseOutput } from "../codeql";
import type { ResolveDatabaseOutput } from "../codeql";
import * as gitUtils from "../git-utils";
import { KnownLanguage } from "../languages";
import { BuiltInLanguage } from "../languages";
import { getRunnerLogger } from "../logging";
import {
createTestConfig,
makeMacro,
mockCodeQLVersion,
setupTests,
} from "../testing-utils";
@@ -23,6 +24,7 @@ import {
downloadOverlayBaseDatabaseFromCache,
getCacheRestoreKeyPrefix,
getCacheSaveKey,
getCodeQlVersionsForOverlayBaseDatabases,
} from "./caching";
import { OverlayDatabaseMode } from "./overlay-database-mode";
@@ -50,10 +52,9 @@ const defaultDownloadTestCase: DownloadOverlayBaseDatabaseTestCase = {
resolveDatabaseOutput: { overlayBaseSpecifier: "20250626:XXX" },
};
const testDownloadOverlayBaseDatabaseFromCache = test.macro({
const testDownloadOverlayBaseDatabaseFromCache = makeMacro({
exec: async (
t,
_title: string,
partialTestCase: Partial<DownloadOverlayBaseDatabaseTestCase>,
expectDownloadSuccess: boolean,
) => {
@@ -65,7 +66,7 @@ const testDownloadOverlayBaseDatabaseFromCache = test.macro({
const testCase = { ...defaultDownloadTestCase, ...partialTestCase };
const config = createTestConfig({
dbLocation,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
});
config.overlayDatabaseMode = testCase.overlayDatabaseMode;
@@ -141,18 +142,16 @@ const testDownloadOverlayBaseDatabaseFromCache = test.macro({
}
});
},
title: (_, title) => `downloadOverlayBaseDatabaseFromCache: ${title}`,
title: (title) => `downloadOverlayBaseDatabaseFromCache: ${title}`,
});
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns stats when successful",
{},
true,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when mode is OverlayDatabaseMode.OverlayBase",
{
overlayDatabaseMode: OverlayDatabaseMode.OverlayBase,
@@ -160,8 +159,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when mode is OverlayDatabaseMode.None",
{
overlayDatabaseMode: OverlayDatabaseMode.None,
@@ -169,8 +167,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when caching is disabled",
{
useOverlayDatabaseCaching: false,
@@ -178,8 +175,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined in test mode",
{
isInTestMode: true,
@@ -187,8 +183,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when cache miss",
{
restoreCacheResult: undefined,
@@ -196,8 +191,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when download fails",
{
restoreCacheResult: new Error("Download failed"),
@@ -205,8 +199,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when downloaded database is invalid",
{
hasBaseDatabaseOidsFile: false,
@@ -214,8 +207,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when downloaded database doesn't have an overlayBaseSpecifier",
{
resolveDatabaseOutput: {},
@@ -223,8 +215,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when resolving database metadata fails",
{
resolveDatabaseOutput: new Error("Failed to resolve database metadata"),
@@ -232,8 +223,7 @@ test.serial(
false,
);
test.serial(
testDownloadOverlayBaseDatabaseFromCache,
testDownloadOverlayBaseDatabaseFromCache.serial(
"returns undefined when filesystem error occurs",
{
tryGetFolderBytesSucceeds: false,
@@ -285,3 +275,160 @@ test.serial("overlay-base database cache keys remain stable", async (t) => {
`Expected save key "${saveKey}" to start with restore key prefix "${restoreKeyPrefix}"`,
);
});
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases returns unique versions sorted latest first",
async (t) => {
const logger = getRunnerLogger(true);
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(apiClient, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.23.0-abc123-1-1",
},
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.24.1-def456-2-1",
},
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.23.0-ghi789-3-1",
},
]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["javascript", "python"],
logger,
);
t.deepEqual(result, ["2.24.1", "2.23.0"]);
},
);
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases returns empty list when no caches exist",
async (t) => {
const logger = getRunnerLogger(true);
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(apiClient, "listActionsCaches").resolves([]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["python"],
logger,
);
t.deepEqual(result, []);
},
);
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases returns empty list when cache keys are unparseable",
async (t) => {
const logger = getRunnerLogger(true);
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(apiClient, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-malformed",
},
{ key: undefined },
]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["python"],
logger,
);
t.deepEqual(result, []);
},
);
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases returns the single version when only one cache exists",
async (t) => {
const logger = getRunnerLogger(true);
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(apiClient, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-cpp-2.25.0-abc123-1-1",
},
]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["cpp"],
logger,
);
t.deepEqual(result, ["2.25.0"]);
},
);
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases resolves language aliases",
async (t) => {
const logger = getRunnerLogger(true);
// The alias `c++` should be resolved to "cpp" and match cache entries keyed with "cpp"
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(apiClient, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-cpp-2.25.0-abc123-1-1",
},
]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["c++"],
logger,
);
t.deepEqual(result, ["2.25.0"]);
},
);
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases de-duplicates resolved language aliases",
async (t) => {
const logger = getRunnerLogger(true);
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
const listActionsCachesStub = sinon
.stub(apiClient, "listActionsCaches")
.resolves([
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.25.0-abc123-1-1",
},
]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["javascript", "typescript", "Python", "python"],
logger,
);
t.deepEqual(result, ["2.25.0"]);
sinon.assert.calledOnceWithExactly(
listActionsCachesStub,
"codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-",
);
},
);
test.serial(
"getCodeQlVersionsForOverlayBaseDatabases ignores nightly versions with build metadata",
async (t) => {
const logger = getRunnerLogger(true);
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(apiClient, "listActionsCaches").resolves([
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-2.25.0-abc123-1-1",
},
{
// Nightly release with semver build metadata; should be ignored.
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-2.26.0+202604211234-def456-2-1",
},
{
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-2.24.0-ghi789-3-1",
},
]);
const result = await getCodeQlVersionsForOverlayBaseDatabases(
["python"],
logger,
);
t.deepEqual(result, ["2.25.0", "2.24.0"]);
},
);
+105 -12
View File
@@ -1,18 +1,20 @@
import * as fs from "fs";
import * as actionsCache from "@actions/cache";
import * as semver from "semver";
import {
getRequiredInput,
getWorkflowRunAttempt,
getWorkflowRunID,
} from "../actions-util";
import { getAutomationID } from "../api-client";
import { getAutomationID, listActionsCaches } from "../api-client";
import { createCacheKeyHash } from "../caching-utils";
import { type CodeQL } from "../codeql";
import { type Config } from "../config-utils";
import { getCommitOid } from "../git-utils";
import { Logger, withGroupAsync } from "../logging";
import { type Language, parseBuiltInLanguage } from "../languages";
import { type Logger, withGroupAsync } from "../logging";
import {
CleanupLevel,
getBaseDatabaseOidsFilePath,
@@ -404,7 +406,17 @@ export async function getCacheRestoreKeyPrefix(
config: Config,
codeQlVersion: string,
): Promise<string> {
const languages = [...config.languages].sort().join("_");
return `${await getCacheKeyPrefixBase(config.languages)}${codeQlVersion}-`;
}
/**
* Computes the cache key prefix for overlay-base databases, excluding the
* CodeQL version.
*/
async function getCacheKeyPrefixBase(
parsedLanguages: Language[],
): Promise<string> {
const languagesComponent = [...parsedLanguages].sort().join("_");
const cacheKeyComponents = {
automationID: await getAutomationID(),
@@ -412,17 +424,98 @@ export async function getCacheRestoreKeyPrefix(
};
const componentsHash = createCacheKeyHash(cacheKeyComponents);
// For a cached overlay-base database to be considered compatible for overlay
// analysis, all components in the cache restore key must match:
//
// CACHE_PREFIX: distinguishes overlay-base databases from other cache objects
// CACHE_VERSION: cache format version
// componentsHash: hash of additional components (see above for details)
// languages: the languages included in the overlay-base database
// codeQlVersion: CodeQL bundle version
// languagesComponent: the languages included in the overlay-base database
//
// Technically we can also include languages and codeQlVersion in the
// componentsHash, but including them explicitly in the cache key makes it
// easier to debug and understand the cache key structure.
return `${CACHE_PREFIX}-${CACHE_VERSION}-${componentsHash}-${languages}-${codeQlVersion}-`;
// Technically we can also include languages in the componentsHash, but
// including them explicitly in the cache key makes it easier to debug and
// understand the cache key structure.
return `${CACHE_PREFIX}-${CACHE_VERSION}-${componentsHash}-${languagesComponent}-`;
}
/**
* Searches the GitHub Actions cache for overlay-base databases matching the given languages, and
* returns all stable CodeQL versions found across matching cache entries.
*
* Note that we do not guarantee that the cache entry for these versions of CodeQL will still be
* present by the time we attempt to restore the cache. We could achieve that with a download retry
* loop, but we expect that if there is sufficient Actions cache contention that an overlay-base
* cache entry for a particular CodeQL version is evicted before we can use it, then it is likely
* that the same thing will happen to other overlay-base cache entries, and therefore we will not be
* able to use overlay.
*
* @returns Unique stable CodeQL versions found in cached overlay-base databases, sorted from latest to
* earliest, or undefined if one of the languages is not a built-in language.
*/
export async function getCodeQlVersionsForOverlayBaseDatabases(
rawLanguages: string[],
logger: Logger,
): Promise<string[] | undefined> {
const languages = rawLanguages.map(parseBuiltInLanguage);
if (languages.includes(undefined)) {
logger.warning(
"One or more provided languages are not recognized as built-in languages. " +
"Skipping searching for overlay-base databases in cache.",
);
return undefined;
}
const dedupedLanguages = [
...new Set(languages.filter((l) => l !== undefined)),
];
const cacheKeyPrefix = await getCacheKeyPrefixBase(dedupedLanguages);
logger.debug(
`Searching for overlay-base databases in Actions cache with ` +
`prefix ${cacheKeyPrefix}`,
);
const caches = await listActionsCaches(cacheKeyPrefix);
if (caches.length === 0) {
logger.info("No overlay-base databases found in Actions cache.");
return [];
}
logger.info(
`Found ${caches.length} overlay-base ` +
`${caches.length === 1 ? "database" : "databases"} in the Actions cache.`,
);
// Parse CodeQL versions from cache keys, matching only stable releases.
//
// After the prefix, the remaining key format starts with `${codeQlVersion}-`. Nightlies will have
// a suffix like `+202604201548` that will break the match.
//
// Caveat: this relies on the fact that we haven't released any CodeQL bundles with the
// `x.y.z-<pre-release>` semver format which does not interact well with the current overlay base
// DB cache key format.
const versionRegex = /^([\d.]+)-/;
const versionSet = new Set<string>();
for (const cache of caches) {
if (!cache.key) continue;
const suffix = cache.key.substring(cacheKeyPrefix.length);
const match = suffix.match(versionRegex);
if (match && semver.valid(match[1])) {
versionSet.add(match[1]);
}
}
if (versionSet.size === 0) {
logger.info(
"Could not parse any CodeQL versions from overlay-base database " +
"cache keys.",
);
return [];
}
const versions = [...versionSet].sort(semver.rcompare);
logger.info(
`Found overlay databases for the following CodeQL versions in the Actions cache: ${versions.join(", ")}`,
);
return versions;
}
+10 -3
View File
@@ -7,8 +7,10 @@ import {
getRequiredInput,
getTemporaryDirectory,
} from "./actions-util";
import { AnalysisKind, getAnalysisKinds } from "./analyses";
import { getGitHubVersion } from "./api-client";
import { CodeQL } from "./codeql";
import { getRawLanguagesNoAutodetect } from "./config-utils";
import { EnvVar } from "./environment";
import { initFeatures } from "./feature-flags";
import { initCodeQL } from "./init";
@@ -136,16 +138,21 @@ async function run(startedAt: Date): Promise<void> {
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);
const codeQLDefaultVersionInfo =
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
const rawLanguages = getRawLanguagesNoAutodetect(
getOptionalInput("languages"),
);
const analysisKinds = await getAnalysisKinds(logger);
const initCodeQLResult = await initCodeQL(
getOptionalInput("tools"),
apiDetails,
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
rawLanguages,
analysisKinds.includes(AnalysisKind.CodeScanning),
features,
logger,
);
+315 -18
View File
@@ -7,8 +7,9 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Feature } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
import * as setupCodeql from "./setup-codeql";
import * as tar from "./tar";
import {
@@ -18,8 +19,9 @@ import {
SAMPLE_DOTCOM_API_DETAILS,
checkExpectedLogMessages,
createFeatures,
createTestConfig,
getRecordingLogger,
initializeFeatures,
makeMacro,
mockBundleDownloadApi,
setupActionsVars,
setupTests,
@@ -33,14 +35,6 @@ import {
setupTests(test);
// TODO: Remove when when we no longer need to pass in features (https://github.com/github/codeql-action/issues/2600)
const expectedFeatureEnablement: FeatureEnablement = initializeFeatures(
true,
) as FeatureEnablement;
expectedFeatureEnablement.getValue = function (feature: Feature) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return expectedFeatureEnablement[feature];
};
test.beforeEach(() => {
initializeEnvironment("1.2.3");
});
@@ -107,6 +101,8 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
`https://github.com/github/codeql-action/releases/download/${tagName}/codeql-bundle-linux64.tar.gz`,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -130,6 +126,8 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"linked",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -155,6 +153,8 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"latest",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -211,6 +211,8 @@ test.serial(
"tmp/codeql_action_test/",
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
logger,
);
@@ -266,6 +268,8 @@ test.serial(
"tmp/codeql_action_test/",
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
features,
logger,
);
@@ -317,6 +321,8 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"nightly",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -378,6 +384,8 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
undefined,
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -432,6 +440,8 @@ test.serial(
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -473,7 +483,7 @@ test.serial(
},
);
const toolcacheInputFallbackMacro = test.macro({
const toolcacheInputFallbackMacro = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
featureList: Feature[],
@@ -499,6 +509,8 @@ const toolcacheInputFallbackMacro = test.macro({
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
undefined, // rawLanguages
false, // useOverlayAwareDefaultCliVersion
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
@@ -514,7 +526,10 @@ const toolcacheInputFallbackMacro = test.macro({
// Check that `sourceType` and `toolsVersion` match expectations.
t.is(source.sourceType, "download");
t.is(source.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
t.is(
source.toolsVersion,
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
);
// Check that key messages we would expect to find in the log are present.
for (const expectedMessage of expectedMessages) {
@@ -533,9 +548,8 @@ const toolcacheInputFallbackMacro = test.macro({
`getCodeQLSource falls back to downloading the CLI if ${providedTitle}`,
});
test.serial(
toolcacheInputFallbackMacro.serial(
"the toolcache doesn't have a CodeQL CLI when tools == toolcache",
toolcacheInputFallbackMacro,
[Feature.AllowToolcacheInput],
{ GITHUB_EVENT_NAME: "dynamic" },
[],
@@ -545,9 +559,8 @@ test.serial(
],
);
test.serial(
toolcacheInputFallbackMacro.serial(
"the workflow trigger is not `dynamic`",
toolcacheInputFallbackMacro,
[Feature.AllowToolcacheInput],
{ GITHUB_EVENT_NAME: "pull_request" },
[],
@@ -556,9 +569,8 @@ test.serial(
],
);
test.serial(
toolcacheInputFallbackMacro.serial(
"the feature flag is not enabled",
toolcacheInputFallbackMacro,
[],
{ GITHUB_EVENT_NAME: "dynamic" },
[],
@@ -598,3 +610,288 @@ test.serial(
t.is(setupCodeql.getLatestToolcacheVersion(getRunnerLogger(true)), "3.2.1");
},
);
const overlayMatchEnabledVersions = {
enabledVersions: [
{ cliVersion: "2.20.2", tagName: "codeql-bundle-v2.20.2" },
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
],
toolsFeatureFlagsValid: true,
};
async function fakeOverlayBaseCacheKey(
language: string,
cliVersion: string,
suffix: string,
): Promise<string> {
const prefix = await getCacheRestoreKeyPrefix(
createTestConfig({ languages: [language] }),
cliVersion,
);
return `${prefix}${suffix}`;
}
test.serial(
"getCodeQLSource uses overlay-aware default version when requested for a PR",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env["CODE_SCANNING_REF"] = "refs/heads/feature-branch";
process.env["CODE_SCANNING_BASE_BRANCH"] = "main";
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
sinon
.stub(toolcache, "find")
.withArgs("CodeQL", "2.20.1")
.returns("/path/to/codeql-2.20.1");
const source = await setupCodeql.getCodeQLSource(
undefined,
overlayMatchEnabledVersions,
["javascript"],
true,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.assert(listStub.calledOnce);
t.is(source.sourceType, "toolcache");
t.is(source.toolsVersion, "2.20.1");
});
},
);
test.serial(
"getCodeQLSource skips overlay-aware default version when not requested",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env["CODE_SCANNING_REF"] = "refs/heads/feature-branch";
process.env["CODE_SCANNING_BASE_BRANCH"] = "main";
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
sinon
.stub(toolcache, "find")
.withArgs("CodeQL", "2.20.2")
.returns("/path/to/codeql-2.20.2");
const source = await setupCodeql.getCodeQLSource(
undefined,
overlayMatchEnabledVersions,
["javascript"],
false,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.assert(listStub.notCalled);
t.is(source.sourceType, "toolcache");
t.is(source.toolsVersion, "2.20.2");
});
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns flag-enabled versions present in cache, sorted desc",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
// Flag-enabled versions present in the cache, listed in non-descending
// order so the test exercises the sort.
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.0", "ghi-3-1"),
},
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "def-2-1"),
},
// Newer than any flag-enabled version: should be filtered out.
{
key: await fakeOverlayBaseCacheKey("javascript", "2.21.0", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.deepEqual(result, [
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
]);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns empty when no cached version is flag-enabled",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.19.0", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.deepEqual(result, []);
},
);
const noLanguagesMacro = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
rawLanguages: string[] | undefined,
) => {
const listStub = sinon.stub(api, "listActionsCaches").resolves([]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
rawLanguages,
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.deepEqual(result, []);
t.assert(
listStub.notCalled,
"Should not list Actions caches without any rawLanguages.",
);
},
title: (providedTitle = "") =>
`getEnabledVersionsWithOverlayBaseDatabases does not list caches when rawLanguages is ${providedTitle}`,
});
noLanguagesMacro.serial("undefined", undefined);
noLanguagesMacro.serial("an empty array", []);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns empty when listing caches throws",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").rejects(new Error("listing failed"));
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.deepEqual(result, []);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases returns versions present in the cache",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.2", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersion]),
getRunnerLogger(true),
);
t.deepEqual(result, [
{ cliVersion: "2.20.2", tagName: "codeql-bundle-v2.20.2" },
]);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases does not list caches when both gates are off",
async (t) => {
const listStub = sinon.stub(api, "listActionsCaches").resolves([]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([]),
getRunnerLogger(true),
);
t.deepEqual(result, []);
t.assert(
listStub.notCalled,
"Should not list Actions caches when both gating feature flags are off.",
);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases dry-run returns empty but lists caches",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([Feature.OverlayAnalysisMatchCodeqlVersionDryRun]),
getRunnerLogger(true),
);
t.deepEqual(
result,
[],
"Dry-run should return an empty list so the caller falls back.",
);
t.assert(
listStub.calledOnce,
"Dry-run should still list Actions caches to populate the diagnostic.",
);
},
);
test.serial(
"getEnabledVersionsWithOverlayBaseDatabases match flag wins over dry-run",
async (t) => {
sinon.stub(api, "getAutomationID").resolves("test/");
sinon.stub(api, "listActionsCaches").resolves([
{
key: await fakeOverlayBaseCacheKey("javascript", "2.20.1", "abc-1-1"),
},
]);
const result = await setupCodeql.getEnabledVersionsWithOverlayBaseDatabases(
overlayMatchEnabledVersions,
["javascript"],
createFeatures([
Feature.OverlayAnalysisMatchCodeqlVersion,
Feature.OverlayAnalysisMatchCodeqlVersionDryRun,
]),
getRunnerLogger(true),
);
t.deepEqual(result, [
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
]);
},
);
+155 -7
View File
@@ -7,17 +7,27 @@ import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";
import { isDynamicWorkflow, isRunningLocalAction } from "./actions-util";
import {
isAnalyzingPullRequest,
isDynamicWorkflow,
isRunningLocalAction,
} from "./actions-util";
import * as api from "./api-client";
import * as defaults from "./defaults.json";
import { addNoLanguageDiagnostic, makeDiagnostic } from "./diagnostics";
import {
addNoLanguageDiagnostic,
makeDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import {
CODEQL_VERSION_ZSTD_BUNDLE,
CodeQLDefaultVersionInfo,
CodeQLVersionInfo,
Feature,
FeatureEnablement,
} from "./feature-flags";
import { Logger } from "./logging";
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
import * as tar from "./tar";
import {
downloadAndExtract,
@@ -264,12 +274,131 @@ async function findOverridingToolsInCache(
return undefined;
}
/**
* Returns the sorted set of enabled versions that have cached overlay-base databases for the
* given languages, or an empty list if neither the `OverlayAnalysisMatchCodeqlVersion` nor the
* `OverlayAnalysisMatchCodeqlVersionDryRun` feature flag is enabled. When only the dry-run flag
* is enabled, this performs the lookup and emits a telemetry diagnostic with the version that
* would have been chosen, but still returns an empty list so the caller falls back.
*/
export async function getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLVersionInfo[]> {
if (rawLanguages === undefined || rawLanguages.length === 0) {
return [];
}
const isEnabled = await features.getValue(
Feature.OverlayAnalysisMatchCodeqlVersion,
);
const isDryRun =
!isEnabled &&
(await features.getValue(Feature.OverlayAnalysisMatchCodeqlVersionDryRun));
if (!isEnabled && !isDryRun) {
return [];
}
let cachedVersions: string[] | undefined;
try {
cachedVersions = await getCodeQlVersionsForOverlayBaseDatabases(
rawLanguages,
logger,
);
} catch (e) {
logger.warning(
"Could not list overlay-base databases in the Actions cache while choosing a default " +
`CodeQL CLI version, falling back to the highest enabled version. Details: ${util.getErrorMessage(e)}`,
);
return [];
}
if (cachedVersions === undefined || cachedVersions.length === 0) {
return [];
}
const cachedVersionsSet = new Set(cachedVersions);
const overlayVersions = defaultCliVersion.enabledVersions.filter((v) =>
cachedVersionsSet.has(v.cliVersion),
);
if (overlayVersions.length === 0) {
return [];
}
const isCachedVersionDifferent =
overlayVersions[0].cliVersion !==
defaultCliVersion.enabledVersions[0].cliVersion;
if (isCachedVersionDifferent) {
addNoLanguageDiagnostic(
undefined,
makeTelemetryDiagnostic(
"codeql-action/overlay-aware-default-codeql-version",
"Overlay-aware default CodeQL version selection",
{
cachedVersions,
enabledVersions: defaultCliVersion.enabledVersions.map(
(v) => v.cliVersion,
),
isDryRun,
overlayAwareVersion: overlayVersions[0].cliVersion,
},
),
);
}
if (isDryRun) {
logger.debug(
`Overlay-aware default CodeQL version selection is running in dry-run mode. Would have used version ${overlayVersions[0].cliVersion}.`,
);
return [];
}
return overlayVersions;
}
/**
* Resolves the newest enabled default CLI version that has a cached overlay-base database for the
* relevant languages, if running a Code Scanning analysis for a pull request and one exists.
* Otherwise, falls back to the newest enabled default CLI version.
*/
async function resolveDefaultCliVersion(
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
useOverlayAwareDefaultCliVersion: boolean,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLVersionInfo> {
if (!useOverlayAwareDefaultCliVersion || !isAnalyzingPullRequest()) {
return defaultCliVersion.enabledVersions[0];
}
const overlayVersions = await getEnabledVersionsWithOverlayBaseDatabases(
defaultCliVersion,
rawLanguages,
features,
logger,
);
if (overlayVersions.length > 0) {
logger.info(
`Using CodeQL version ${overlayVersions[0].cliVersion} since this is the ` +
`highest enabled version that has a cached overlay-base database.`,
);
return overlayVersions[0];
}
return defaultCliVersion.enabledVersions[0];
}
/**
* Determines where the CodeQL CLI we want to use comes from. This can be from a local file,
* the Actions toolcache, or a download.
*
* @param toolsInput The argument provided for the `tools` input, if any.
* @param defaultCliVersion The default CLI version that's linked to the CodeQL Action.
* @param rawLanguages Raw set of languages.
* @param useOverlayAwareDefaultCliVersion Whether to select an overlay-aware default CLI version.
* @param apiDetails Information about the GitHub API.
* @param variant The GitHub variant we are running on.
* @param tarSupportsZstd Whether zstd is supported by `tar`.
@@ -281,6 +410,8 @@ async function findOverridingToolsInCache(
export async function getCodeQLSource(
toolsInput: string | undefined,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
useOverlayAwareDefaultCliVersion: boolean,
apiDetails: api.GitHubApiDetails,
variant: util.GitHubVariant,
tarSupportsZstd: boolean,
@@ -438,8 +569,15 @@ export async function getCodeQLSource(
}
}
cliVersion = defaultCliVersion.cliVersion;
tagName = defaultCliVersion.tagName;
const version = await resolveDefaultCliVersion(
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
);
cliVersion = version.cliVersion;
tagName = version.tagName;
}
} else if (toolsInput !== undefined) {
// If a tools URL was provided, then use that.
@@ -454,9 +592,15 @@ export async function getCodeQLSource(
}
}
} else {
// Otherwise, use the default CLI version passed in.
cliVersion = defaultCliVersion.cliVersion;
tagName = defaultCliVersion.tagName;
const version = await resolveDefaultCliVersion(
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
features,
logger,
);
cliVersion = version.cliVersion;
tagName = version.tagName;
}
const bundleVersion =
@@ -791,6 +935,8 @@ export async function setupCodeQLBundle(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
rawLanguages: string[] | undefined,
useOverlayAwareDefaultCliVersion: boolean,
features: FeatureEnablement,
logger: Logger,
): Promise<SetupCodeQLResult> {
@@ -804,6 +950,8 @@ export async function setupCodeQLBundle(
const source = await getCodeQLSource(
toolsInput,
defaultCliVersion,
rawLanguages,
useOverlayAwareDefaultCliVersion,
apiDetails,
variant,
zstdAvailability.available,
+6 -6
View File
@@ -6,7 +6,7 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import {
@@ -14,7 +14,6 @@ import {
getCredentials,
getProxyBinaryPath,
getSafeErrorMessage,
parseLanguage,
ProxyInfo,
sendFailedStatusReport,
sendSuccessStatusReport,
@@ -33,7 +32,7 @@ async function run(startedAt: Date) {
const logger = getActionsLogger();
let features: FeatureEnablement | undefined;
let language: KnownLanguage | undefined;
let language: BuiltInLanguage | undefined;
try {
// Make inputs accessible in the `post` step.
@@ -56,7 +55,7 @@ async function run(startedAt: Date) {
// Get the language input.
const languageInput = actionsUtil.getOptionalInput("language");
language = languageInput ? parseLanguage(languageInput) : undefined;
language = languageInput ? parseBuiltInLanguage(languageInput) : undefined;
// Query the FF for whether we should use the reduced registry mapping.
const skipUnusedRegistries = await features.getValue(
@@ -112,14 +111,14 @@ async function run(startedAt: Date) {
logger,
);
// Check that the private registries are reachable.
// Perform best-effort checks that the private registries are reachable.
await checkConnections(logger, proxyInfo);
// Report success if we have reached this point.
await sendSuccessStatusReport(
startedAt,
{
languages: language && [language],
languages: language === undefined ? undefined : [language],
},
proxyConfig.all_credentials.map((c) => c.type),
logger,
@@ -199,6 +198,7 @@ async function startProxy(
.map((credential) => ({
type: credential.type,
url: credential.url,
"replaces-base": credential["replaces-base"],
}));
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
+148 -188
View File
@@ -8,15 +8,17 @@ import sinon from "sinon";
import * as apiClient from "./api-client";
import * as defaults from "./defaults.json";
import { setUpFeatureFlagTests } from "./feature-flags/testing-util";
import { KnownLanguage } from "./languages";
import { UnvalidatedObject, validateSchema } from "./json";
import { makeFromSchema } from "./json/testing-util";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger, Logger } from "./logging";
import * as startProxyExports from "./start-proxy";
import { parseLanguage } from "./start-proxy";
import * as statusReport from "./status-report";
import {
assertNotLogged,
checkExpectedLogMessages,
createFeatures,
makeMacro,
makeTestToken,
RecordingLogger,
setupTests,
@@ -31,7 +33,7 @@ import {
setupTests(test);
const sendFailedStatusReportTest = test.macro({
const sendFailedStatusReportTest = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
err: Error,
@@ -87,16 +89,14 @@ const sendFailedStatusReportTest = test.macro({
title: (providedTitle = "") => `sendFailedStatusReport - ${providedTitle}`,
});
test.serial(
sendFailedStatusReportTest.serial(
"reports generic error message for non-StartProxyError error",
sendFailedStatusReportTest,
new Error("Something went wrong today"),
"Error from start-proxy Action omitted (Error).",
);
test.serial(
sendFailedStatusReportTest.serial(
"reports generic error message for non-StartProxyError error with safe error message",
sendFailedStatusReportTest,
new Error(
startProxyExports.getStartProxyErrorMessage(
startProxyExports.StartProxyErrorType.DownloadFailed,
@@ -105,9 +105,8 @@ test.serial(
"Error from start-proxy Action omitted (Error).",
);
test.serial(
sendFailedStatusReportTest.serial(
"reports generic error message for ConfigurationError error",
sendFailedStatusReportTest,
new ConfigurationError("Something went wrong today"),
"Error from start-proxy Action omitted (ConfigurationError).",
"user-error",
@@ -232,7 +231,7 @@ test("getCredentials filters by language when specified", async (t) => {
getRunnerLogger(true),
undefined,
toEncodedJSON(mixedCredentials),
KnownLanguage.java,
BuiltInLanguage.java,
);
t.is(credentials.length, 1);
t.is(credentials[0].type, "maven_repository");
@@ -243,7 +242,7 @@ test("getCredentials returns all for a language when specified", async (t) => {
getRunnerLogger(true),
undefined,
toEncodedJSON(mixedCredentials),
KnownLanguage.go,
BuiltInLanguage.go,
);
t.is(credentials.length, 2);
@@ -263,7 +262,7 @@ test("getCredentials returns all goproxy_servers for Go when specified", async (
getRunnerLogger(true),
undefined,
toEncodedJSON(multipleGoproxyServers),
KnownLanguage.go,
BuiltInLanguage.go,
);
t.is(credentials.length, 3);
@@ -292,7 +291,7 @@ test("getCredentials returns all maven_repositories for Java when specified", as
getRunnerLogger(true),
undefined,
toEncodedJSON(multipleMavenRepositories),
KnownLanguage.java,
BuiltInLanguage.java,
);
t.is(credentials.length, 2);
@@ -350,147 +349,70 @@ test("getCredentials throws an error when non-printable characters are used", as
}
});
const validAzureCredential: startProxyExports.AzureConfig = {
"tenant-id": "12345678-1234-1234-1234-123456789012",
"client-id": "abcdef01-2345-6789-abcd-ef0123456789",
};
for (const oidcSchemaInfo of startProxyExports.oidcSchemas) {
test(`getCredentials throws when non-printable characters are used (${oidcSchemaInfo.name} OIDC)`, (t) => {
const validCredential = makeFromSchema(true, oidcSchemaInfo.schema);
for (const key of Object.keys(validCredential)) {
const invalidAuthConfig = {
...validCredential,
[key]: "123\x00",
};
const invalidCredential: startProxyExports.RawCredential = {
type: "nuget_feed",
host: `${key}.nuget.pkg.github.com`,
...invalidAuthConfig,
};
const credentialsInput = toEncodedJSON([invalidCredential]);
const validAwsCredential: startProxyExports.AWSConfig = {
"aws-region": "us-east-1",
"account-id": "123456789012",
"role-name": "MY_ROLE",
domain: "MY_DOMAIN",
"domain-owner": "987654321098",
audience: "custom-audience",
};
const validJFrogCredential: startProxyExports.JFrogConfig = {
"jfrog-oidc-provider-name": "MY_PROVIDER",
audience: "jfrog-audience",
"identity-mapping-name": "my-mapping",
};
test("getCredentials throws an error when non-printable characters are used for Azure OIDC", (t) => {
for (const key of Object.keys(validAzureCredential)) {
const invalidAzureCredential = {
...validAzureCredential,
[key]: "123\x00",
};
const invalidCredential: startProxyExports.RawCredential = {
type: "nuget_feed",
host: `${key}.nuget.pkg.github.com`,
...invalidAzureCredential,
};
const credentialsInput = toEncodedJSON([invalidCredential]);
t.throws(
() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
credentialsInput,
undefined,
),
{
message:
"Invalid credentials - fields must contain only printable characters",
},
);
}
});
test("getCredentials throws an error when non-printable characters are used for AWS OIDC", (t) => {
for (const key of Object.keys(validAwsCredential)) {
const invalidAwsCredential = {
...validAwsCredential,
[key]: "123\x00",
};
const invalidCredential: startProxyExports.RawCredential = {
type: "nuget_feed",
host: `${key}.nuget.pkg.github.com`,
...invalidAwsCredential,
};
const credentialsInput = toEncodedJSON([invalidCredential]);
t.throws(
() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
credentialsInput,
undefined,
),
{
message:
"Invalid credentials - fields must contain only printable characters",
},
);
}
});
test("getCredentials throws an error when non-printable characters are used for JFrog OIDC", (t) => {
for (const key of Object.keys(validJFrogCredential)) {
const invalidJFrogCredential = {
...validJFrogCredential,
[key]: "123\x00",
};
const invalidCredential: startProxyExports.RawCredential = {
type: "nuget_feed",
host: `${key}.nuget.pkg.github.com`,
...invalidJFrogCredential,
};
const credentialsInput = toEncodedJSON([invalidCredential]);
t.throws(
() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
credentialsInput,
undefined,
),
{
message:
"Invalid credentials - fields must contain only printable characters",
},
);
}
});
t.throws(
() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
credentialsInput,
undefined,
),
{
message:
"Invalid credentials - fields must contain only printable characters",
},
);
}
});
}
test("getCredentials accepts OIDC configurations", (t) => {
const oidcConfigurations = [
{
const oidcConfigurations = startProxyExports.oidcSchemas.map(
(schemaInfo) => ({
type: "nuget_feed",
host: "azure.pkg.github.com",
...validAzureCredential,
},
{
type: "nuget_feed",
host: "aws.pkg.github.com",
...validAwsCredential,
},
{
type: "nuget_feed",
host: "jfrog.pkg.github.com",
...validJFrogCredential,
},
];
host: `${schemaInfo.name.toLowerCase()}.pkg.github.com`,
...makeFromSchema(true, schemaInfo.schema),
}),
);
const credentials = startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
toEncodedJSON(oidcConfigurations),
KnownLanguage.csharp,
BuiltInLanguage.csharp,
);
t.is(credentials.length, 3);
t.is(credentials.length, startProxyExports.oidcSchemas.length);
t.assert(credentials.every((c) => c.type === "nuget_feed"));
t.assert(credentials.some((c) => startProxyExports.isAzureConfig(c)));
t.assert(credentials.some((c) => startProxyExports.isAWSConfig(c)));
t.assert(credentials.some((c) => startProxyExports.isJFrogConfig(c)));
for (const oidcSchemaInfo of startProxyExports.oidcSchemas) {
t.assert(
credentials.some((c) =>
validateSchema(
oidcSchemaInfo.schema,
c as unknown as UnvalidatedObject<any>,
),
),
);
}
});
const getCredentialsMacro = test.macro({
const getCredentialsMacro = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
credentials: startProxyExports.RawCredential[],
@@ -516,9 +438,8 @@ const getCredentialsMacro = test.macro({
title: (providedTitle = "") => `getCredentials - ${providedTitle}`,
});
test(
getCredentialsMacro(
"warns for PAT-like password without a username",
getCredentialsMacro,
[
{
type: "git_server",
@@ -533,7 +454,7 @@ test(
t.is(results[0].type, "git_server");
t.is(results[0].host, "https://github.com/");
if (startProxyExports.isUsernamePassword(results[0])) {
if (startProxyExports.hasUsernameAndPassword(results[0])) {
t.assert(results[0].password?.startsWith("ghp_"));
} else {
t.fail("Expected a `UsernamePassword`-based credential.");
@@ -546,9 +467,8 @@ test(
},
);
test(
getCredentialsMacro(
"no warning for PAT-like password with a username",
getCredentialsMacro,
[
{
type: "git_server",
@@ -564,7 +484,7 @@ test(
t.is(results[0].type, "git_server");
t.is(results[0].host, "https://github.com/");
if (startProxyExports.isUsernamePassword(results[0])) {
if (startProxyExports.hasUsernameAndPassword(results[0])) {
t.assert(results[0].password?.startsWith("ghp_"));
} else {
t.fail("Expected a `UsernamePassword`-based credential.");
@@ -578,9 +498,8 @@ test(
},
);
test(
getCredentialsMacro(
"warns for PAT-like token without a username",
getCredentialsMacro,
[
{
type: "git_server",
@@ -608,9 +527,8 @@ test(
},
);
test(
getCredentialsMacro(
"no warning for PAT-like token with a username",
getCredentialsMacro,
[
{
type: "git_server",
@@ -640,6 +558,76 @@ test(
},
);
test("getCredentials validates 'replaces-base' correctly", async (t) => {
// Valid cases.
const credentialsInput = toEncodedJSON([
{
type: "maven_repository",
host: "maven1.pkg.github.com",
token: "abc",
"replaces-base": false,
},
{
type: "maven_repository",
host: "maven2.pkg.github.com",
token: "def",
"replaces-base": true,
},
{
type: "maven_repository",
host: "maven3.pkg.github.com",
token: "ghi",
},
]);
const credentials = startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
credentialsInput,
BuiltInLanguage.java,
false,
);
t.is(credentials.length, 3);
t.true(credentials.some((c) => c["replaces-base"] === true));
t.true(credentials.some((c) => c["replaces-base"] === false));
t.true(credentials.some((c) => c["replaces-base"] === undefined));
// Invalid cases.
const baseInvalid = {
type: "maven_repository",
host: "maven4.pkg.github.com",
token: "jkl",
};
t.throws(() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
toEncodedJSON([{ ...baseInvalid, "replaces-base": null }]),
BuiltInLanguage.actions,
false,
),
);
t.throws(() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
toEncodedJSON([{ ...baseInvalid, "replaces-base": 123 }]),
BuiltInLanguage.actions,
false,
),
);
t.throws(() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
toEncodedJSON([{ ...baseInvalid, "replaces-base": "true" }]),
BuiltInLanguage.actions,
false,
),
);
});
test("getCredentials returns all credentials for Actions when using LANGUAGE_TO_REGISTRY_TYPE", async (t) => {
const credentialsInput = toEncodedJSON(mixedCredentials);
@@ -647,7 +635,7 @@ test("getCredentials returns all credentials for Actions when using LANGUAGE_TO_
getRunnerLogger(true),
undefined,
credentialsInput,
KnownLanguage.actions,
BuiltInLanguage.actions,
false,
);
t.is(credentials.length, mixedCredentials.length);
@@ -660,39 +648,12 @@ test("getCredentials returns no credentials for Actions when using NEW_LANGUAGE_
getRunnerLogger(true),
undefined,
credentialsInput,
KnownLanguage.actions,
BuiltInLanguage.actions,
true,
);
t.deepEqual(credentials, []);
});
test("parseLanguage", async (t) => {
// Exact matches
t.deepEqual(parseLanguage("csharp"), KnownLanguage.csharp);
t.deepEqual(parseLanguage("cpp"), KnownLanguage.cpp);
t.deepEqual(parseLanguage("go"), KnownLanguage.go);
t.deepEqual(parseLanguage("java"), KnownLanguage.java);
t.deepEqual(parseLanguage("javascript"), KnownLanguage.javascript);
t.deepEqual(parseLanguage("python"), KnownLanguage.python);
t.deepEqual(parseLanguage("rust"), KnownLanguage.rust);
// Aliases
t.deepEqual(parseLanguage("c"), KnownLanguage.cpp);
t.deepEqual(parseLanguage("c++"), KnownLanguage.cpp);
t.deepEqual(parseLanguage("c#"), KnownLanguage.csharp);
t.deepEqual(parseLanguage("kotlin"), KnownLanguage.java);
t.deepEqual(parseLanguage("typescript"), KnownLanguage.javascript);
// spaces and case-insensitivity
t.deepEqual(parseLanguage(" \t\nCsHaRp\t\t"), KnownLanguage.csharp);
t.deepEqual(parseLanguage(" \t\nkOtLin\t\t"), KnownLanguage.java);
// Not matches
t.deepEqual(parseLanguage("foo"), undefined);
t.deepEqual(parseLanguage(" "), undefined);
t.deepEqual(parseLanguage(""), undefined);
});
function mockGetApiClient(endpoints: any) {
return (
sinon
@@ -829,7 +790,7 @@ test.serial(
},
);
const wrapFailureTest = test.macro({
const wrapFailureTest = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
setup: () => void,
@@ -860,9 +821,8 @@ test.serial("downloadProxy - returns file path on success", async (t) => {
});
});
test.serial(
wrapFailureTest.serial(
"downloadProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "downloadTool").throws();
},
@@ -881,9 +841,8 @@ test.serial("extractProxy - returns file path on success", async (t) => {
});
});
test.serial(
wrapFailureTest.serial(
"extractProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "extractTar").throws();
},
@@ -907,9 +866,8 @@ test.serial("cacheProxy - returns file path on success", async (t) => {
});
});
test.serial(
wrapFailureTest.serial(
"cacheProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "cacheDir").throws();
},
@@ -1052,8 +1010,10 @@ test.serial(
return true;
});
const getDefaultCliVersion = sinon
.stub(features, "getDefaultCliVersion")
.resolves({ cliVersion: "2.20.1", tagName: expectedTag });
.stub(features, "getEnabledDefaultCliVersions")
.resolves({
enabledVersions: [{ cliVersion: "2.20.1", tagName: expectedTag }],
});
const path = await startProxyExports.getProxyBinaryPath(logger, features);
t.assert(getDefaultCliVersion.calledOnce);
+30 -120
View File
@@ -18,27 +18,18 @@ import {
FeatureEnablement,
} from "./feature-flags";
import * as json from "./json";
import * as knownLanguageAliases from "./known-language-aliases.json";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { Logger } from "./logging";
import {
Address,
Registry,
Credential,
AuthConfig,
isToken,
isAzureConfig,
Token,
UsernamePassword,
AzureConfig,
isAWSConfig,
AWSConfig,
isJFrogConfig,
JFrogConfig,
isUsernamePassword,
hasToken,
hasUsernameAndPassword,
hasUsername,
RawCredential,
} from "./start-proxy/types";
import { getAuthConfig } from "./start-proxy/validation";
import {
ActionName,
createStatusReportBase,
@@ -157,7 +148,7 @@ export function getSafeErrorMessage(error: Error): string {
export async function sendFailedStatusReport(
logger: Logger,
startedAt: Date,
language: KnownLanguage | undefined,
language: BuiltInLanguage | undefined,
unwrappedError: unknown,
) {
const error = util.wrapError(unwrappedError);
@@ -173,7 +164,7 @@ export async function sendFailedStatusReport(
getActionsStatus(error),
startedAt,
{
languages: language && [language],
languages: language === undefined ? undefined : [language],
},
await util.checkDiskUsage(logger),
logger,
@@ -189,35 +180,6 @@ export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
const UPDATEJOB_PROXY_URL_PREFIX =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.22.0/";
/**
* Parse the start-proxy language input into its canonical CodeQL language name.
*
* This uses the language aliases shipped with the Action and will not be able to resolve aliases
* added by versions of the CodeQL CLI newer than the one mentioned in `defaults.json`. However,
* this is sufficient for the start-proxy Action since we are already specifying proxy
* configurations on a per-language basis.
*/
export function parseLanguage(language: string): KnownLanguage | undefined {
// Normalize to lower case
language = language.trim().toLowerCase();
// See if it's an exact match
if (Object.hasOwn(KnownLanguage, language)) {
return language as KnownLanguage;
}
// Check language aliases
if (Object.hasOwn(knownLanguageAliases, language)) {
language =
knownLanguageAliases[language as keyof typeof knownLanguageAliases];
if (Object.hasOwn(KnownLanguage, language)) {
return language as KnownLanguage;
}
}
return undefined;
}
function isPAT(value: string) {
return artifactScanner.isAuthToken(value, [
artifactScanner.GITHUB_PAT_CLASSIC_PATTERN,
@@ -225,7 +187,7 @@ function isPAT(value: string) {
]);
}
type RegistryMapping = Partial<Record<KnownLanguage, string[]>>;
type RegistryMapping = Partial<Record<BuiltInLanguage, string[]>>;
const LANGUAGE_TO_REGISTRY_TYPE: RegistryMapping = {
java: ["maven_repository"],
@@ -281,75 +243,6 @@ function getRegistryAddress(
}
}
/** Extracts an `AuthConfig` value from `config`. */
export function getAuthConfig(
config: json.UnvalidatedObject<AuthConfig>,
): AuthConfig {
// Start by checking for the OIDC configurations, since they have required properties
// which we can use to identify them.
if (isAzureConfig(config)) {
return {
"tenant-id": config["tenant-id"],
"client-id": config["client-id"],
} satisfies AzureConfig;
} else if (isAWSConfig(config)) {
return {
"aws-region": config["aws-region"],
"account-id": config["account-id"],
"role-name": config["role-name"],
domain: config.domain,
"domain-owner": config["domain-owner"],
audience: config.audience,
} satisfies AWSConfig;
} else if (isJFrogConfig(config)) {
return {
"jfrog-oidc-provider-name": config["jfrog-oidc-provider-name"],
"identity-mapping-name": config["identity-mapping-name"],
audience: config.audience,
} satisfies JFrogConfig;
} else if (isToken(config)) {
// There are three scenarios for non-OIDC authentication based on the registry type:
//
// 1. `username`+`token`
// 2. A `token` that combines the username and actual token, separated by ':'.
// 3. `username`+`password`
//
// In all three cases, all fields are optional. If the `token` field is present,
// we accept the configuration as a `Token` typed configuration, with the `token`
// value and an optional `username`. Otherwise, we accept the configuration
// typed as `UsernamePassword` (in the `else` clause below) with optional
// username and password. I.e. a private registry type that uses 1. or 2.,
// but has no `token` configured, will get accepted as `UsernamePassword` here.
if (isDefined(config.token)) {
// Mask token to reduce chance of accidental leakage in logs, if we have one.
core.setSecret(config.token);
}
return { username: config.username, token: config.token } satisfies Token;
} else {
let username: string | undefined = undefined;
let password: string | undefined = undefined;
// Both "username" and "password" are optional. If we have reached this point, we need
// to validate which of them are present and that they have the correct type if so.
if ("password" in config && json.isString(config.password)) {
// Mask password to reduce chance of accidental leakage in logs, if we have one.
core.setSecret(config.password);
password = config.password;
}
if ("username" in config && json.isString(config.username)) {
username = config.username;
}
// Return the `UsernamePassword` object. Both username and password may be undefined.
return {
username,
password,
} satisfies UsernamePassword;
}
}
// getCredentials returns registry credentials from action inputs.
// It prefers `registries_credentials` over `registry_secrets`.
// If neither is set, it returns an empty array.
@@ -357,7 +250,7 @@ export function getCredentials(
logger: Logger,
registrySecrets: string | undefined,
registriesCredentials: string | undefined,
language: KnownLanguage | undefined,
language: BuiltInLanguage | undefined,
skipUnusedRegistries: boolean = false,
): Credential[] {
const registryMapping = skipUnusedRegistries
@@ -438,11 +331,11 @@ export function getCredentials(
const noUsername =
!hasUsername(authConfig) || !isDefined(authConfig.username);
const passwordIsPAT =
isUsernamePassword(authConfig) &&
hasUsernameAndPassword(authConfig) &&
isDefined(authConfig.password) &&
isPAT(authConfig.password);
const tokenIsPAT =
isToken(authConfig) &&
hasToken(authConfig) &&
isDefined(authConfig.token) &&
isPAT(authConfig.token);
@@ -454,8 +347,25 @@ export function getCredentials(
);
}
// Construct the base credential object.
const baseCredential: Omit<Registry, keyof Address> = { type: e.type };
// If "replaces-base" is present, it must be a boolean.
if ("replaces-base" in e) {
if (
isDefined(e["replaces-base"]) &&
typeof e["replaces-base"] === "boolean"
) {
baseCredential["replaces-base"] = e["replaces-base"];
} else {
throw new ConfigurationError(
"Invalid credentials - 'replaces-base' must be a boolean",
);
}
}
out.push({
type: e.type,
...baseCredential,
...authConfig,
...address,
});
@@ -505,7 +415,7 @@ async function getCliVersionFromFeatures(
features: FeatureEnablement,
): Promise<CodeQLDefaultVersionInfo> {
const gitHubVersion = await getGitHubVersion();
return await features.getDefaultCliVersion(gitHubVersion.type);
return await features.getEnabledDefaultCliVersions(gitHubVersion.type);
}
/**
@@ -530,7 +440,7 @@ export async function getDownloadUrl(
// Retrieve information about the CLI version we should use. This will be either the linked
// version, or the one enabled by FFs.
const versionInfo = useFeaturesToDetermineCLI
? await getCliVersionFromFeatures(features)
? (await getCliVersionFromFeatures(features)).enabledVersions[0]
: {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
+4 -4
View File
@@ -7,7 +7,7 @@ import * as io from "@actions/io";
import test, { ExecutionContext } from "ava";
import sinon from "sinon";
import { JavaEnvVars, KnownLanguage } from "../languages";
import { JavaEnvVars, BuiltInLanguage } from "../languages";
import {
checkExpectedLogMessages,
getRecordingLogger,
@@ -182,11 +182,11 @@ test.serial("checkProxyEnvVars - credentials are removed from URLs", (t) => {
});
test.serial(
"checkProxyEnvironment - includes base checks for all known languages",
"checkProxyEnvironment - includes base checks for all built-in languages",
async (t) => {
stubToolrunner();
for (const language of Object.values(KnownLanguage)) {
for (const language of Object.values(BuiltInLanguage)) {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
@@ -204,7 +204,7 @@ test.serial(
stubToolrunner();
await checkProxyEnvironment(logger, KnownLanguage.java);
await checkProxyEnvironment(logger, BuiltInLanguage.java);
assertEnvVarLogMessages(t, Object.keys(ProxyEnvVars), messages, false);
assertEnvVarLogMessages(t, JAVA_PROXY_ENV_VARS, messages, false);
},
+2 -2
View File
@@ -4,7 +4,7 @@ import * as path from "path";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io";
import { JavaEnvVars, KnownLanguage, Language } from "../languages";
import { JavaEnvVars, BuiltInLanguage, Language } from "../languages";
import { Logger } from "../logging";
import { getErrorMessage, isDefined } from "../util";
@@ -196,7 +196,7 @@ export async function checkProxyEnvironment(
// Check language-specific configurations. If we don't know the language,
// then we perform all checks.
if (language === undefined || language === KnownLanguage.java) {
if (language === undefined || language === BuiltInLanguage.java) {
checkJavaEnvVars(logger);
await showJavaSettings(logger);
+32
View File
@@ -8,6 +8,7 @@ import {
} from "./../testing-utils";
import {
checkConnections,
connectionTestConfig,
ReachabilityBackend,
ReachabilityError,
} from "./reachability";
@@ -118,3 +119,34 @@ test("checkConnections - handles invalid URLs", async (t) => {
`Finished testing connections`,
]);
});
test("checkConnections - appends extra paths", async (t) => {
const backend = new MockReachabilityBackend();
const checkConnection = sinon.stub(backend, "checkConnection").resolves(200);
const messages = await withRecordingLoggerAsync(async (logger) => {
await checkConnections(
logger,
{
...proxyInfo,
registries: [{ ...nugetFeed, url: "https://api.nuget.org/" }],
},
backend,
);
});
checkExpectedLogMessages(t, messages, [
`Testing connection to https://api.nuget.org/`,
`Successfully tested connection to https://api.nuget.org/`,
`Finished testing connections`,
]);
t.true(
checkConnection.calledWith(
sinon.match(
new URL(
`https://api.nuget.org/${connectionTestConfig["nuget_feed"]?.path}`,
),
),
),
);
});
+43 -2
View File
@@ -2,11 +2,41 @@ import * as https from "https";
import { HttpsProxyAgent } from "https-proxy-agent";
import { DocUrl } from "../doc-url";
import { Logger } from "../logging";
import { getErrorMessage } from "../util";
import { getAddressString, ProxyInfo, Registry } from "./types";
/** Represents registry-specific connection test configurations. */
export interface ConnectionTestConfig {
/** An optional path to append to the end of the base url. */
path?: string;
}
/** A partial mapping of registry types to extra connection test configurations. */
export const connectionTestConfig: Partial<
Record<string, ConnectionTestConfig>
> = {
nuget_feed: { path: "v3/index.json" },
};
/**
* Applies the registry-specific check configuration to the base URL, if any and applicable.
*/
export function makeTestUrl(
config: ConnectionTestConfig | undefined,
base: URL,
): URL {
if (config?.path === undefined) {
return base;
}
if (base.pathname.endsWith(config.path)) {
return base;
}
return new URL(config.path, base);
}
export class ReachabilityError extends Error {
constructor(public readonly statusCode?: number | undefined) {
super();
@@ -41,7 +71,7 @@ class NetworkReachabilityBackend implements ReachabilityBackend {
url,
{
agent: this.agent,
method: "HEAD",
method: "GET",
ca: this.proxy.cert,
timeout: 5 * 1000, // 5 seconds
},
@@ -85,6 +115,13 @@ export async function checkConnections(
// Don't do anything if there are no registries.
if (proxy.registries.length === 0) return result;
// Start a log group and print a message with a disclaimer with a link to the
// relevant documentation that these checks are a best-effort process.
logger.startGroup("Testing connections via the proxy");
logger.info(
`The connection tests performed here are best-effort only and failures here may not affect the subsequent analysis. See ${DocUrl.PRIVATE_REGISTRY_LOGS} for more information.`,
);
try {
// Initialise a networking backend if no backend was provided.
if (backend === undefined) {
@@ -92,6 +129,7 @@ export async function checkConnections(
}
for (const registry of proxy.registries) {
const config = connectionTestConfig[registry.type];
const address = getAddressString(registry);
const url = URL.parse(address);
@@ -102,9 +140,11 @@ export async function checkConnections(
continue;
}
const testUrl = makeTestUrl(config, url);
try {
logger.debug(`Testing connection to ${url}...`);
const statusCode = await backend.checkConnection(url);
const statusCode = await backend.checkConnection(testUrl);
logger.info(`Successfully tested connection to ${url} (${statusCode})`);
result.add(registry);
@@ -126,5 +166,6 @@ export async function checkConnections(
);
}
logger.endGroup();
return result;
}
+68 -2
View File
@@ -1,5 +1,6 @@
import test from "ava";
import { makeFromSchema, withSchemaMatrix } from "../json/testing-util";
import { setupTests } from "../testing-utils";
import * as types from "./types";
@@ -26,6 +27,38 @@ const validJFrogCredential: types.JFrogConfig = {
"identity-mapping-name": "my-mapping",
};
test("hasUsername", (t) => {
// Reject the case where `username` is missing.
t.false(types.hasUsername({}));
// Test all cases where `username` is present.
withSchemaMatrix(
t,
types.usernameSchema,
{ excludeAbsent: true },
(value) => {
t.true(types.hasUsername(value));
},
);
});
test("hasUsernameAndPassword", (t) => {
// Reject cases where `username` or `password` are missing.
t.false(types.hasUsernameAndPassword({}));
t.false(types.hasUsernameAndPassword({ username: "foo" }));
t.false(types.hasUsernameAndPassword({ password: "foo" }));
// Test all cases where both `username` and `password` are present.
withSchemaMatrix(
t,
types.usernamePasswordSchema,
{ excludeAbsent: true },
(value) => {
t.true(types.hasUsernameAndPassword(value));
},
);
});
test("credentialToStr - pretty-prints valid username+password configurations", (t) => {
const secret = "password123";
const credential: types.Credential = {
@@ -107,13 +140,46 @@ test("credentialToStr - pretty-prints valid JFrog OIDC configurations", (t) => {
);
});
test("credentialToStr - pretty-prints valid Cloudsmith OIDC configurations", (t) => {
const credential: types.Credential = {
type: "maven_credential",
url: "https://localhost",
...(makeFromSchema(
true,
types.cloudsmithConfigSchema,
) as types.CloudsmithConfig),
};
const str = types.credentialToStr(credential);
t.is(
"Type: maven_credential; Url: https://localhost; Cloudsmith Namespace: value-for-namespace; Cloudsmith Service Slug: value-for-service-slug; Cloudsmith API Host: value-for-api-host;",
str,
);
});
test("credentialToStr - pretty-prints valid GCP OIDC configurations", (t) => {
const credential: types.Credential = {
type: "maven_credential",
url: "https://localhost",
...(makeFromSchema(true, types.gcpConfigSchema) as types.GCPConfig),
};
const str = types.credentialToStr(credential);
t.is(
"Type: maven_credential; Url: https://localhost; GCP Workload Identity Provider: value-for-workload-identity-provider; GCP Service Account: value-for-service-account; GCP Audience: value-for-audience;",
str,
);
});
test("credentialToStr - hides passwords", (t) => {
const secret = "password123";
const credential = {
type: "maven_credential",
password: secret,
url: "https://localhost",
};
} satisfies types.Credential;
const str = types.credentialToStr(credential);
@@ -127,7 +193,7 @@ test("credentialToStr - hides tokens", (t) => {
type: "maven_credential",
token: secret,
url: "https://localhost",
};
} satisfies types.Credential;
const str = types.credentialToStr(credential);
+134 -88
View File
@@ -9,144 +9,177 @@ import { isDefined } from "../util";
*/
export type RawCredential = UnvalidatedObject<Credential>;
/** Usernames may be present for both authentication with tokens or passwords. */
export type Username = {
/** A schema for credential objects with a username. */
export const usernameSchema = {
/** The username needed to authenticate to the package registry, if any. */
username?: string;
};
username: json.optional(json.string),
} as const satisfies json.Schema;
/** Decides whether `config` has a username. */
/** Usernames may be present for both authentication with tokens or passwords. */
export type Username = json.FromSchema<typeof usernameSchema>;
/**
* Narrows `config` to `Username` if `config` has a `username` property.
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
*/
export function hasUsername(config: AuthConfig): config is Username {
return "username" in config;
}
/** A schema for credential objects with a username and password. */
export const usernamePasswordSchema = {
/** The password needed to authenticate to the package registry, if any. */
password: json.optional(json.string),
...usernameSchema,
} as const satisfies json.Schema;
/**
* Fields expected for authentication based on a username and password.
* Both username and password are optional.
*/
export type UsernamePassword = {
/** The password needed to authenticate to the package registry, if any. */
password?: string;
} & Username;
export type UsernamePassword = json.FromSchema<typeof usernamePasswordSchema>;
/** Decides whether `config` is based on a username and password. */
export function isUsernamePassword(
/**
* Narrows `config` to `UsernamePassword` if it has a `username` and `password` property.
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
*/
export function hasUsernameAndPassword(
config: AuthConfig,
): config is UsernamePassword {
return hasUsername(config) && "password" in config;
}
/** A schema for credential objects for token-based authentication. */
export const tokenSchema = {
/** The token needed to authenticate to the package registry, if any. */
token: json.optional(json.string),
...usernameSchema,
} as const satisfies json.Schema;
/**
* Fields expected for token-based authentication.
* Both username and token are optional.
*/
export type Token = {
/** The token needed to authenticate to the package registry, if any. */
token?: string;
} & Username;
export type Token = json.FromSchema<typeof tokenSchema>;
/**
* Narrows `config` to `Token` if it has a `token` property.
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
*/
export function hasToken(config: AuthConfig): config is Token {
return "token" in config;
}
/** Decides whether `config` is token-based. */
export function isToken(
config: UnvalidatedObject<AuthConfig>,
): config is Token {
// The "username" field is optional, but should be a string if present.
if ("username" in config && !json.isStringOrUndefined(config.username)) {
return false;
}
// The "token" field is required, and must be a string or undefined.
return "token" in config && json.isStringOrUndefined(config.token);
return "token" in config && json.validateSchema(tokenSchema, config);
}
/** A schema for Azure OIDC configurations. */
export const azureConfigSchema = {
"tenant-id": json.string,
"client-id": json.string,
} as const satisfies json.Schema;
/** Configuration for Azure OIDC. */
export type AzureConfig = { "tenant-id": string; "client-id": string };
export type AzureConfig = json.FromSchema<typeof azureConfigSchema>;
/** Decides whether `config` is an Azure OIDC configuration. */
export function isAzureConfig(
config: UnvalidatedObject<AuthConfig>,
): config is AzureConfig {
return (
"tenant-id" in config &&
"client-id" in config &&
isDefined(config["tenant-id"]) &&
isDefined(config["client-id"]) &&
json.isString(config["tenant-id"]) &&
json.isString(config["client-id"])
);
return json.validateSchema(azureConfigSchema, config);
}
/** A schema for AWS OIDC configurations. */
export const awsConfigSchema = {
"aws-region": json.string,
"account-id": json.string,
"role-name": json.string,
domain: json.string,
"domain-owner": json.string,
audience: json.optional(json.string),
} as const satisfies json.Schema;
/** Configuration for AWS OIDC. */
export type AWSConfig = {
"aws-region": string;
"account-id": string;
"role-name": string;
domain: string;
"domain-owner": string;
audience?: string;
};
export type AWSConfig = json.FromSchema<typeof awsConfigSchema>;
/** Decides whether `config` is an AWS OIDC configuration. */
export function isAWSConfig(
config: UnvalidatedObject<AuthConfig>,
): config is AWSConfig {
// All of these properties are required.
const requiredProperties = [
"aws-region",
"account-id",
"role-name",
"domain",
"domain-owner",
];
for (const property of requiredProperties) {
if (
!(property in config) ||
!isDefined(config[property]) ||
!json.isString(config[property])
) {
return false;
}
}
// The "audience" field is optional, but should be a string if present.
if ("audience" in config && !json.isStringOrUndefined(config.audience)) {
return false;
}
return true;
return json.validateSchema(awsConfigSchema, config);
}
/** A schema for JFrog OIDC configurations. */
export const jfrogConfigSchema = {
"jfrog-oidc-provider-name": json.string,
audience: json.optional(json.string),
"identity-mapping-name": json.optional(json.string),
} as const satisfies json.Schema;
/** Configuration for JFrog OIDC. */
export type JFrogConfig = {
"jfrog-oidc-provider-name": string;
audience?: string;
"identity-mapping-name"?: string;
};
export type JFrogConfig = json.FromSchema<typeof jfrogConfigSchema>;
/** Decides whether `config` is a JFrog OIDC configuration. */
export function isJFrogConfig(
config: UnvalidatedObject<AuthConfig>,
): config is JFrogConfig {
// The "audience" and "identity-mapping-name" fields are optional, but should be strings if present.
if ("audience" in config && !json.isStringOrUndefined(config.audience)) {
return false;
}
if (
"identity-mapping-name" in config &&
!json.isStringOrUndefined(config["identity-mapping-name"])
) {
return false;
}
return (
"jfrog-oidc-provider-name" in config &&
isDefined(config["jfrog-oidc-provider-name"]) &&
json.isString(config["jfrog-oidc-provider-name"])
);
return json.validateSchema(jfrogConfigSchema, config);
}
/** A schema for Cloudsmith OIDC configurations. */
export const cloudsmithConfigSchema = {
namespace: json.string,
"service-slug": json.string,
"api-host": json.string,
} as const satisfies json.Schema;
/** Configuration for Cloudsmith OIDC. */
export type CloudsmithConfig = json.FromSchema<typeof cloudsmithConfigSchema>;
/** Decides whether `config` is a Cloudsmith OIDC configuration. */
export function isCloudsmithConfig(
config: UnvalidatedObject<AuthConfig>,
): config is CloudsmithConfig {
return json.validateSchema(cloudsmithConfigSchema, config);
}
/** A schema for GCP OIDC configurations. */
export const gcpConfigSchema = {
"workload-identity-provider": json.string,
"service-account": json.optional(json.string),
audience: json.optional(json.string),
} as const satisfies json.Schema;
/** Configuration for GCP OIDC. */
export type GCPConfig = json.FromSchema<typeof gcpConfigSchema>;
/** Decides whether `config` is a GCP OIDC configuration. */
export function isGCPConfig(
config: UnvalidatedObject<AuthConfig>,
): config is GCPConfig {
return json.validateSchema(gcpConfigSchema, config);
}
/** An array of all OIDC configuration schemas along with output-friendly names. */
export const oidcSchemas = [
{ schema: azureConfigSchema, name: "Azure" },
{ schema: awsConfigSchema, name: "AWS" },
{ schema: jfrogConfigSchema, name: "JFrog" },
{ schema: cloudsmithConfigSchema, name: "Cloudsmith" },
{ schema: gcpConfigSchema, name: "GCP" },
];
/** Represents all supported OIDC configurations. */
export type OIDC = AzureConfig | AWSConfig | JFrogConfig;
export type OIDC =
| AzureConfig
| AWSConfig
| JFrogConfig
| CloudsmithConfig
| GCPConfig;
/** All authentication-related fields. */
export type AuthConfig = UsernamePassword | Token | OIDC;
@@ -165,7 +198,7 @@ export type Credential = AuthConfig & Registry;
export function credentialToStr(credential: Credential): string {
let result: string = `Type: ${credential.type};`;
const appendIfDefined = (name: string, val: string | undefined) => {
const appendIfDefined = (name: string, val: string | undefined | null) => {
if (isDefined(val)) {
result += ` ${name}: ${val};`;
}
@@ -184,7 +217,7 @@ export function credentialToStr(credential: Credential): string {
isDefined(credential.password) ? "***" : undefined,
);
}
if (isToken(credential)) {
if (hasToken(credential)) {
appendIfDefined("Token", isDefined(credential.token) ? "***" : undefined);
}
@@ -205,6 +238,17 @@ export function credentialToStr(credential: Credential): string {
credential["identity-mapping-name"],
);
appendIfDefined("JFrog Audience", credential.audience);
} else if (isCloudsmithConfig(credential)) {
appendIfDefined("Cloudsmith Namespace", credential.namespace);
appendIfDefined("Cloudsmith Service Slug", credential["service-slug"]);
appendIfDefined("Cloudsmith API Host", credential["api-host"]);
} else if (isGCPConfig(credential)) {
appendIfDefined(
"GCP Workload Identity Provider",
credential["workload-identity-provider"],
);
appendIfDefined("GCP Service Account", credential["service-account"]);
appendIfDefined("GCP Audience", credential.audience);
}
return result;
@@ -214,6 +258,8 @@ export function credentialToStr(credential: Credential): string {
export type Registry = {
/** The type of the package registry. */
type: string;
/** Whether the registry replaces the base registry for the ecosystem. */
"replaces-base"?: boolean;
} & Address;
// If a registry has an `url`, then that takes precedence over the `host` which may or may
+69
View File
@@ -0,0 +1,69 @@
import test from "ava";
import * as json from "../json";
import { makeFromSchema } from "../json/testing-util";
import { setupTests } from "../testing-utils";
import * as types from "./types";
import { getAuthConfig } from "./validation";
setupTests(test);
for (const schemaTest of types.oidcSchemas) {
for (const includeOptional of [true, false]) {
const minimalName = includeOptional ? "full" : "minimal";
test(`getAuthConfig - ${schemaTest.name} - ${minimalName}`, async (t) => {
const config = makeFromSchema(includeOptional, schemaTest.schema);
t.deepEqual(
getAuthConfig({
...config,
unexpected: "unexpected-value",
} as unknown as json.UnvalidatedObject<types.AuthConfig>),
config,
);
});
}
}
test("getAuthConfig - token", async (t) => {
const config = makeFromSchema(true, types.tokenSchema);
t.deepEqual(
getAuthConfig({
...config,
unexpected: "unexpected-value",
} as json.UnvalidatedObject<types.AuthConfig>),
config,
);
});
test("getAuthConfig - username and password", async (t) => {
const config = makeFromSchema(true, types.usernamePasswordSchema);
t.deepEqual(
getAuthConfig({
...config,
unexpected: "unexpected-value",
} as json.UnvalidatedObject<types.AuthConfig>),
config,
);
});
test("getAuthConfig - empty", async (t) => {
const config = makeFromSchema(false, types.usernamePasswordSchema);
// Since the purpose of constructing the `AuthConfig` values is for
// serialisation to JSON so that they can be passed to the proxy as configuration,
// we only care that the stringified JSON representations are the same.
t.deepEqual(
JSON.stringify(
getAuthConfig({
...config,
unexpected: "unexpected-value",
} as json.UnvalidatedObject<types.AuthConfig>),
),
JSON.stringify({}),
);
});
+81
View File
@@ -0,0 +1,81 @@
import * as core from "@actions/core";
import * as json from "../json";
import { isDefined } from "../util";
import type { AuthConfig, UsernamePassword } from "./types";
import * as types from "./types";
/** Constructs a new object from `obj` with only keys that exist in `schema`. */
export function cloneCredential<S extends json.Schema>(
schema: S,
obj: json.FromSchema<S>,
): json.FromSchema<S> {
const result = {};
for (const key of Object.keys(schema)) {
// Skip keys that don't exist or don't have a value.
if (!isDefined(obj[key])) {
continue;
}
result[key] = obj[key];
}
return result as json.FromSchema<S>;
}
/** Extracts an `AuthConfig` value from `config`. */
export function getAuthConfig(
config: json.UnvalidatedObject<AuthConfig>,
): AuthConfig {
// Start by checking for the OIDC configurations, since they have required properties
// which we can use to identify them.
for (const oidcSchema of types.oidcSchemas) {
if (json.validateSchema(oidcSchema.schema, config)) {
return cloneCredential(oidcSchema.schema, config);
}
}
// Otherwise, try the basic configuration types.
if (types.isToken(config)) {
// There are three scenarios for non-OIDC authentication based on the registry type:
//
// 1. `username`+`token`
// 2. A `token` that combines the username and actual token, separated by ':'.
// 3. `username`+`password`
//
// In all three cases, all fields are optional. If the `token` field is present,
// we accept the configuration as a `Token` typed configuration, with the `token`
// value and an optional `username`. Otherwise, we accept the configuration
// typed as `UsernamePassword` (in the `else` clause below) with optional
// username and password. I.e. a private registry type that uses 1. or 2.,
// but has no `token` configured, will get accepted as `UsernamePassword` here.
if (isDefined(config.token)) {
// Mask token to reduce chance of accidental leakage in logs, if we have one.
core.setSecret(config.token);
}
return cloneCredential(types.tokenSchema, config);
} else {
let username: string | undefined = undefined;
let password: string | undefined = undefined;
// Both "username" and "password" are optional. If we have reached this point, we need
// to validate which of them are present and that they have the correct type if so.
if ("password" in config && json.isString(config.password)) {
// Mask password to reduce chance of accidental leakage in logs, if we have one.
core.setSecret(config.password);
password = config.password;
}
if ("username" in config && json.isString(config.username)) {
username = config.username;
}
// Return the `UsernamePassword` object. Both username and password may be undefined.
return {
username,
password,
} satisfies UsernamePassword;
}
}
+11 -14
View File
@@ -4,7 +4,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { Config } from "./config-utils";
import { EnvVar } from "./environment";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import { ToolsSource } from "./setup-codeql";
import {
@@ -19,6 +19,7 @@ import {
setupTests,
setupActionsVars,
createTestConfig,
makeMacro,
} from "./testing-utils";
import { BuildMode, ConfigurationError, withTmpDir, wrapError } from "./util";
@@ -48,7 +49,7 @@ test.serial("createStatusReportBase", async (t) => {
new Date("May 19, 2023 05:19:00"),
createTestConfig({
buildMode: BuildMode.None,
languages: [KnownLanguage.java, KnownLanguage.swift],
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
}),
{ numAvailableBytes: 100, numTotalBytes: 500 },
getRunnerLogger(false),
@@ -291,10 +292,9 @@ test.serial(
},
);
const testCreateInitWithConfigStatusReport = test.macro({
const testCreateInitWithConfigStatusReport = makeMacro({
exec: async (
t,
_title: string,
config: Config,
expectedReportProperties: Partial<InitWithConfigStatusReport>,
) => {
@@ -337,15 +337,14 @@ const testCreateInitWithConfigStatusReport = test.macro({
}
});
},
title: (_, title) => `createInitWithConfigStatusReport: ${title}`,
title: (title) => `createInitWithConfigStatusReport: ${title}`,
});
test.serial(
testCreateInitWithConfigStatusReport,
testCreateInitWithConfigStatusReport.serial(
"returns a value",
createTestConfig({
buildMode: BuildMode.None,
languages: [KnownLanguage.java, KnownLanguage.swift],
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
}),
{
trap_cache_download_size_bytes: 1024,
@@ -355,12 +354,11 @@ test.serial(
},
);
test.serial(
testCreateInitWithConfigStatusReport,
testCreateInitWithConfigStatusReport.serial(
"includes packs for a single language",
createTestConfig({
buildMode: BuildMode.None,
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
computedConfig: {
packs: ["foo", "bar"],
},
@@ -372,12 +370,11 @@ test.serial(
},
);
test.serial(
testCreateInitWithConfigStatusReport,
testCreateInitWithConfigStatusReport.serial(
"includes packs for multiple languages",
createTestConfig({
buildMode: BuildMode.None,
languages: [KnownLanguage.java, KnownLanguage.swift],
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
computedConfig: {
packs: { java: ["java-foo", "java-bar"], swift: ["swift-bar"] },
},
+37 -9
View File
@@ -2,7 +2,11 @@ import { TextDecoder } from "node:util";
import path from "path";
import * as github from "@actions/github";
import { ExecutionContext, TestFn } from "ava";
import test, {
type ExecutionContext,
type MacroDeclarationOptions,
type TestFn,
} from "ava";
import nock from "nock";
import * as sinon from "sinon";
@@ -36,16 +40,20 @@ export const SAMPLE_DOTCOM_API_DETAILS = {
apiURL: "https://api.github.com",
};
export const SAMPLE_DEFAULT_CLI_VERSION: CodeQLDefaultVersionInfo = {
cliVersion: "2.20.0",
tagName: "codeql-bundle-v2.20.0",
};
export const LINKED_CLI_VERSION = {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
};
export const SAMPLE_DEFAULT_CLI_VERSION: CodeQLDefaultVersionInfo = {
enabledVersions: [
{
cliVersion: "2.20.0",
tagName: "codeql-bundle-v2.20.0",
},
],
};
type TestContext = {
stdoutWrite: any;
stderrWrite: any;
@@ -85,8 +93,8 @@ function wrapOutput(context: TestContext) {
};
}
export function setupTests(test: TestFn<any>) {
const typedTest = test as TestFn<TestContext>;
export function setupTests(testFn: TestFn<any>) {
const typedTest = testFn as TestFn<TestContext>;
typedTest.beforeEach((t) => {
// Set an empty CodeQL object so that all method calls will fail
@@ -139,6 +147,26 @@ export function setupTests(test: TestFn<any>) {
});
}
/**
* Declare a reusable test implementation, with better type safety than `test.macro`.
*/
export function makeMacro<Args extends unknown[]>(
decl: MacroDeclarationOptions<Args, unknown>,
) {
const m = test.macro<Args>(decl);
const wrapper = (name: string, ...args: Args) => test(name, m, ...args);
wrapper.test = (...args: Args) => test(m, ...args);
wrapper.serial = (name: string, ...args: Args) =>
test.serial(name, m, ...args);
// Make the implementation available as `fn`. We don't call it `exec` so
// that results from this function are not valid arguments to `test`
// or `test.serial`.
wrapper.fn = decl.exec;
return wrapper;
}
/**
* Default values for environment variables typically set in an Actions
* environment. Tests can override individual variables by passing them in the
@@ -442,7 +470,7 @@ export function mockCodeQLVersion(
*/
export function createFeatures(enabledFeatures: Feature[]): FeatureEnablement {
return {
getDefaultCliVersion: async () => {
getEnabledDefaultCliVersions: async () => {
throw new Error("not implemented");
},
getValue: async (feature) => {
+4 -4
View File
@@ -6,7 +6,7 @@ import * as sinon from "sinon";
import { CodeQL, getCodeQLForTesting } from "./codeql";
import * as configUtils from "./config-utils";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { createTestConfig, makeVersionInfo, setupTests } from "./testing-utils";
import { ToolsFeature } from "./tools-features";
import { getCombinedTracerConfig } from "./tracer-config";
@@ -16,7 +16,7 @@ setupTests(test);
function getTestConfig(tempDir: string): configUtils.Config {
return createTestConfig({
languages: [KnownLanguage.java],
languages: [BuiltInLanguage.java],
tempDir,
dbLocation: path.resolve(tempDir, "codeql_databases"),
});
@@ -36,7 +36,7 @@ async function stubCodeql(
);
sinon
.stub(codeqlObject, "isTracedLanguage")
.withArgs(KnownLanguage.java)
.withArgs(BuiltInLanguage.java)
.resolves(true);
return codeqlObject;
}
@@ -45,7 +45,7 @@ test("getCombinedTracerConfig - return undefined when no languages are traced la
await util.withTmpDir(async (tmpDir) => {
const config = getTestConfig(tmpDir);
// No traced languages
config.languages = [KnownLanguage.javascript, KnownLanguage.python];
config.languages = [BuiltInLanguage.javascript, BuiltInLanguage.python];
t.deepEqual(
await getCombinedTracerConfig(await stubCodeql(), config),
undefined,
+9 -9
View File
@@ -15,7 +15,7 @@ import {
import * as configUtils from "./config-utils";
import { Feature } from "./feature-flags";
import * as gitUtils from "./git-utils";
import { KnownLanguage } from "./languages";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import {
createFeatures,
@@ -41,7 +41,7 @@ const stubCodeql = createStubCodeQL({
async betterResolveLanguages() {
return {
extractors: {
[KnownLanguage.javascript]: [
[BuiltInLanguage.javascript]: [
{
extractor_root: "some_root",
extractor_options: {
@@ -65,7 +65,7 @@ const stubCodeql = createStubCodeQL({
},
},
],
[KnownLanguage.cpp]: [
[BuiltInLanguage.cpp]: [
{
extractor_root: "other_root",
},
@@ -76,7 +76,7 @@ const stubCodeql = createStubCodeQL({
});
const testConfigWithoutTmpDir = createTestConfig({
languages: [KnownLanguage.javascript, KnownLanguage.cpp],
languages: [BuiltInLanguage.javascript, BuiltInLanguage.cpp],
trapCaches: {
javascript: "/some/cache/dir",
},
@@ -84,7 +84,7 @@ const testConfigWithoutTmpDir = createTestConfig({
function getTestConfigWithTempDir(tempDir: string): configUtils.Config {
return createTestConfig({
languages: [KnownLanguage.javascript, KnownLanguage.ruby],
languages: [BuiltInLanguage.javascript, BuiltInLanguage.ruby],
tempDir,
dbLocation: path.resolve(tempDir, "codeql_databases"),
trapCaches: {
@@ -100,7 +100,7 @@ test.serial("check flags for JS, analyzing default branch", async (t) => {
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
const result = await getTrapCachingExtractorConfigArgsForLang(
config,
KnownLanguage.javascript,
BuiltInLanguage.javascript,
);
t.deepEqual(result, [
`-O=javascript.trap.cache.dir=${path.resolve(tmpDir, "jsCache")}`,
@@ -131,10 +131,10 @@ test("get languages that support TRAP caching", async (t) => {
const logger = getRecordingLogger(loggedMessages);
const languagesSupportingCaching = await getLanguagesSupportingCaching(
stubCodeql,
[KnownLanguage.javascript, KnownLanguage.cpp],
[BuiltInLanguage.javascript, BuiltInLanguage.cpp],
logger,
);
t.deepEqual(languagesSupportingCaching, [KnownLanguage.javascript]);
t.deepEqual(languagesSupportingCaching, [BuiltInLanguage.javascript]);
});
test.serial("upload cache key contains right fields", async (t) => {
@@ -180,7 +180,7 @@ test.serial(
);
await downloadTrapCaches(
stubCodeql,
[KnownLanguage.javascript, KnownLanguage.cpp],
[BuiltInLanguage.javascript, BuiltInLanguage.cpp],
logger,
);
t.assert(
+4 -3
View File
@@ -156,9 +156,8 @@ async function combineSarifFilesUsingCLI(
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
};
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);
const codeQLDefaultVersionInfo =
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
const initCodeQLResult = await initCodeQL(
undefined, // There is no tools input on the upload action
@@ -166,6 +165,8 @@ async function combineSarifFilesUsingCLI(
tempDir,
gitHubVersion.type,
codeQLDefaultVersionInfo,
undefined, // rawLanguages: upload-lib does not run analysis
false, // useOverlayAwareDefaultCliVersion: upload-lib does not run analysis
features,
logger,
);
+8 -13
View File
@@ -6,7 +6,7 @@ import * as sinon from "sinon";
import { AnalysisKind, getAnalysisConfig } from "./analyses";
import { getRunnerLogger } from "./logging";
import { createFeatures, setupTests } from "./testing-utils";
import { createFeatures, makeMacro, setupTests } from "./testing-utils";
import { UploadResult } from "./upload-lib";
import * as uploadLib from "./upload-lib";
import { postProcessAndUploadSarif } from "./upload-sarif";
@@ -43,7 +43,7 @@ function mockPostProcessSarifFiles() {
return postProcessSarifFiles;
}
const postProcessAndUploadSarifMacro = test.macro({
const postProcessAndUploadSarifMacro = makeMacro({
exec: async (
t: ExecutionContext<unknown>,
sarifFiles: string[],
@@ -67,7 +67,7 @@ const postProcessAndUploadSarifMacro = test.macro({
const analysisConfig = getAnalysisConfig(analysisKind);
uploadPostProcessedFiles
.withArgs(logger, sinon.match.any, analysisConfig, sinon.match.any)
.resolves(expectedResult[analysisKind as AnalysisKind]?.uploadResult);
.resolves(expectedResult[analysisKind]?.uploadResult);
}
const fullSarifPaths = sarifFiles.map(toFullPath);
@@ -123,9 +123,8 @@ const postProcessAndUploadSarifMacro = test.macro({
title: (providedTitle = "") => `processAndUploadSarif - ${providedTitle}`,
});
test.serial(
postProcessAndUploadSarifMacro.serial(
"SARIF file",
postProcessAndUploadSarifMacro,
["test.sarif"],
(tempDir) => path.join(tempDir, "test.sarif"),
{
@@ -138,9 +137,8 @@ test.serial(
},
);
test.serial(
postProcessAndUploadSarifMacro.serial(
"JSON file",
postProcessAndUploadSarifMacro,
["test.json"],
(tempDir) => path.join(tempDir, "test.json"),
{
@@ -153,9 +151,8 @@ test.serial(
},
);
test.serial(
postProcessAndUploadSarifMacro.serial(
"Code Scanning files",
postProcessAndUploadSarifMacro,
["test.json", "test.sarif"],
undefined,
{
@@ -169,9 +166,8 @@ test.serial(
},
);
test.serial(
postProcessAndUploadSarifMacro.serial(
"Code Quality file",
postProcessAndUploadSarifMacro,
["test.quality.sarif"],
(tempDir) => path.join(tempDir, "test.quality.sarif"),
{
@@ -184,9 +180,8 @@ test.serial(
},
);
test.serial(
postProcessAndUploadSarifMacro.serial(
"Mixed files",
postProcessAndUploadSarifMacro,
["test.sarif", "test.quality.sarif"],
undefined,
{
+1 -1
View File
@@ -422,7 +422,7 @@ async function testLanguageAliases(
],
},
},
} as Workflow,
},
codeql,
);
@@ -8,5 +8,6 @@ packs:
- codeql-testing/codeql-pack3:other-query.ql
paths-ignore:
- tests
- lib
- pr-checks
- tests
@@ -2,5 +2,6 @@ name: Pack testing in the CodeQL Action
disable-default-queries: true
paths-ignore:
- tests
- lib
- pr-checks
- tests
@@ -6,5 +6,6 @@ packs:
- codeql-testing/codeql-pack2
- codeql-testing/codeql-pack3:other-query.ql
paths-ignore:
- tests
- lib
- pr-checks
- tests