Compare commits

..

151 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 95e58e9a2c Merge pull request #3824 from github/update-v4.35.2-d2e135a73
Merge main into releases/v4
2026-04-15 12:22:51 +01:00
github-actions[bot] 6f31bfe060 Update changelog for v4.35.2 2026-04-15 10:56:23 +00:00
Henry Mercer d2e135a73a Merge pull request #3823 from github/update-bundle/codeql-bundle-v2.25.2
Update default bundle to 2.25.2
2026-04-15 10:06:23 +00:00
github-actions[bot] 60abb65df0 Add changelog note 2026-04-15 09:39:31 +00:00
github-actions[bot] 5a0a562209 Update default bundle to codeql-bundle-v2.25.2 2026-04-15 09:39:24 +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 23883 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}`,",
+17
View File
@@ -4,10 +4,27 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
## [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)
- The Git version 2.36.0 requirement for improved incremental analysis now only applies to repositories that contain submodules. [#3789](https://github.com/github/codeql-action/pull/3789)
- Python analysis on GHES no longer extracts the standard library, relying instead on models of the standard library. This should result in significantly faster extraction and analysis times, while the effect on alerts should be minimal. [#3794](https://github.com/github/codeql-action/pull/3794)
- Fixed a bug in the validation of OIDC configurations for private registries that was added in CodeQL Action 4.33.0 / 3.33.0. [#3807](https://github.com/github/codeql-action/pull/3807)
- Update default CodeQL bundle version to [2.25.2](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.2). [#3823](https://github.com/github/codeql-action/pull/3823)
## 4.35.1 - 27 Mar 2026
+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.1",
"cliVersion": "2.25.1",
"priorBundleVersion": "codeql-bundle-v2.24.3",
"priorCliVersion": "2.24.3"
"bundleVersion": "codeql-bundle-v2.25.4",
"cliVersion": "2.25.4",
"priorBundleVersion": "codeql-bundle-v2.25.3",
"priorCliVersion": "2.25.3"
}
+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.1",
"cliVersion": "2.25.1",
"priorBundleVersion": "codeql-bundle-v2.24.3",
"priorCliVersion": "2.24.3"
"bundleVersion": "codeql-bundle-v2.25.4",
"cliVersion": "2.25.4",
"priorBundleVersion": "codeql-bundle-v2.25.3",
"priorCliVersion": "2.25.3"
}
+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-c++": "cpp",
"c-cpp": "cpp",
"c#": "csharp",
"c++": "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