Compare commits

..

43 Commits

Author SHA1 Message Date
Michael B. Gale
c252c71195 Move initializeEnvironment out of util.ts 2026-10-16 22:15:06 +01:00
Michael B. Gale
a234b846e2 Remove unused type parameters from Env classes 2026-10-16 22:04:24 +01:00
Michael B. Gale
dc2ed81cc7 Remove InstanceURL from util.ts 2026-10-16 22:04:05 +01:00
Michael B. Gale
02283e9542 Move more GitHub-related definitions from util.ts to github.ts 2026-10-16 21:54:02 +01:00
Michael B. Gale
86bc4448d3 Mark additional imports in util.ts as type-only 2026-10-16 21:52:19 +01:00
Michael B. Gale
9c72af5b2b Move codeQlVersionAtLeast out of util.ts and into CodeQL 2026-10-16 21:50:31 +01:00
Michael B. Gale
929f5c0db4 Move isDiskConfigurationError out of codeql.ts 2026-10-16 21:50:09 +01:00
Michael B. Gale
a76419a632 Move wrapError and getErrorMessage out of util.ts 2026-10-16 21:46:16 +01:00
Michael B. Gale
071544a67a Move HTTPError out of util.ts 2026-10-16 21:40:48 +01:00
Michael B. Gale
8bdea824e2 Move ConfigurationError out of util.ts 2026-10-16 21:39:54 +01:00
Michael B. Gale
83e1837c6d Merge pull request #4247 from github/mbg/copilot/clarify-feature-flags
Add Copilot instructions for FFs, and update documentation more generally
2026-10-16 15:20:34 +00:00
Michael B. Gale
5ecf3b32e9 Suggest "Minor" prefix 2026-10-16 16:03:32 +01:00
Michael B. Gale
e461913b38 Drop "requests" again 2026-10-16 16:03:00 +01:00
Michael B. Gale
6992155901 Elaborate GHES details 2026-10-16 16:02:01 +01:00
Michael B. Gale
f3c93e3106 Add some guidelines for change notes 2026-10-16 15:32:50 +01:00
Michael B. Gale
2441e6e56e Update "Review instructions" based on recent reviews 2026-10-16 15:31:56 +01:00
Michael B. Gale
0d2baf6822 Update copilot-instructions.md to mention feature flags 2026-10-16 15:11:17 +01:00
Henry Mercer
ae098fa9fe Merge pull request #4242 from github/henrymercer/graduate-toolcache-cleanup
Enable cleaning up toolcache bundles by default
2026-10-16 12:43:00 +00:00
Michael B. Gale
0a458f10ac Merge pull request #4230 from github/mbg/auth/allow-unauthed
Make `auth` in `GitHubApiDetails` optional
2026-10-16 11:05:53 +00:00
Henry Mercer
4ebc48bf25 Remove the toolcache cleanup test override
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9bf883d4-ecd7-4428-970f-86895f760571
2026-10-15 14:54:30 +01:00
Henry Mercer
1ec6b03cf4 Enable toolcache bundle cleanup by default
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9bf883d4-ecd7-4428-970f-86895f760571
2026-10-15 14:54:30 +01:00
Michael B. Gale
7a6e2f798f Merge pull request #4240 from github/mergeback/v4.39.0-to-main-6f2ac81f
Mergeback v4.39.0 refs/heads/releases/v4 into main
2026-10-15 11:25:10 +00:00
github-actions[bot]
46d3daa879 Rebuild 2026-10-15 10:57:15 +00:00
github-actions[bot]
2edace40f1 Update changelog and version after v4.39.0 2026-10-15 10:57:06 +00:00
Michael B. Gale
e5b217aeb5 Merge pull request #4238 from github/dependabot/github_actions/dot-github/workflows/actions-minor-c673fd7efc
Bump the actions-minor group across 1 directory with 3 updates
2026-10-15 10:26:50 +00:00
Michael B. Gale
3ec43abae3 Merge branch 'main' into dependabot/github_actions/dot-github/workflows/actions-minor-c673fd7efc 2026-10-15 11:01:21 +01:00
github-actions[bot]
3afe9c01db Rebuild 2026-10-14 21:26:52 +00:00
dependabot[bot]
8844fb33e0 Bump the actions-minor group across 1 directory with 3 updates
Bumps the actions-minor group with 3 updates in the /.github/workflows directory: [actions/upload-artifact](https://github.com/actions/upload-artifact), [actions/setup-node](https://github.com/actions/setup-node) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](043fb46d1a...cf430e030d)

Updates `actions/setup-node` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](8207627860...949feb2413)

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](3e5f45b2cf...9000827ccb)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-14 21:24:28 +00:00
Michael B. Gale
c1641847bb Remove explicit authorization header from uploadBundledDatabase 2026-10-14 20:03:19 +01:00
Michael B. Gale
047c751d92 Add a test for uploadBundledDatabase 2026-10-14 20:02:27 +01:00
Michael B. Gale
f6e8dce502 Pass apiDetails to getApiClient in uploadBundledDatabase 2026-10-14 18:28:21 +01:00
Michael B. Gale
a08b587b61 Allow getApiClient to accept GitHubApiDetails 2026-10-14 18:27:15 +01:00
Michael B. Gale
bfe264b559 Add very basic getApiClientWithExternalAuth test 2026-10-14 18:19:52 +01:00
Michael B. Gale
f9b4185b99 Check token value in uploadBundledDatabase 2026-10-14 18:19:52 +01:00
Michael B. Gale
66c96fdc35 Check auth value in getAuthorizationHeaderFor
Also add some tests for it
2026-10-14 18:19:52 +01:00
Michael B. Gale
1d30a842cd Guard against blank auth value
This shouldn't be possible, but was previously handled by the library
2026-10-14 18:19:51 +01:00
Michael B. Gale
1b0a617641 Make externalRepoAuth check more consistent 2026-10-14 18:19:51 +01:00
Michael B. Gale
96501a33ac Use getCurrentInstance() 2026-10-14 18:19:51 +01:00
Michael B. Gale
a2438139ce Make auth property on GitHubApiDetails optional 2026-10-14 18:19:50 +01:00
Michael B. Gale
e0625309c3 Move GitHubApiDetails to github.ts 2026-10-14 18:19:50 +01:00
Michael B. Gale
61bfcffab9 Add functions to get the current and dotcom instance 2026-10-14 18:19:50 +01:00
Michael B. Gale
102e46e9d1 Move some definitions out of util.ts into a new github.ts module 2026-10-14 18:19:46 +01:00
Michael B. Gale
0735d7910f Improve GitHubApiDetails types and documentation 2026-10-14 18:14:48 +01:00
63 changed files with 954 additions and 619 deletions

View File

@@ -11,9 +11,12 @@ The CodeQL Action is used in GitHub Actions workflows to run CodeQL scans using
## Review instructions
- When wording review comments, be helpful and friendly. Assume that the PR author has written the code with the best of intentions. Word your comments constructively as suggestions for improvements. Do not word suggestions as commands.
- Prioritise identifying critical issues, such as logic bugs, over comments on documentation or typos.
- When wording review comments, be helpful and friendly. Assume that the PR author has written the code with the best of intentions. Never word suggestions as commands or using the imperative. Examples of what you should never write: "Preserve that non-fatal behaviour here" or "Please preserve that non-fatal behaviour here".
- When writing feedback, assume that the PR author is competent and is able to understand whether the point you are making is valid or not, why you are raising it, and how to best address it. While you can suggest how to address a point you are making, don't phrase it as a requirement or assume that it is the only solution.
- Always word your comments constructively as suggestions for improvements, for example: "It would make sense to ..." or "Could you add ...?" or "Would it make sense to ...?"
- If you want to comment on a change that you believe will fail a CI check, do not present the CI failure you expect as a fact. Instead, write that you think a change "may" lead to a failure in CI. Suggest that, if such a failure manifests, the changes you are commenting on may be the place responsible for the failure and are worth looking at.
- If a suggestion you make is suitable for a follow-up, such as a refactoring that doesn't change the behaviour or fixing a typo in a comment, mention that it can be addressed in a later PR rather than blocking this one.
- If a suggestion you make is suitable for a follow-up, such as a refactoring that doesn't change the behaviour or fixing a typo in a comment, mention that it can be addressed in a later PR rather than blocking this one or prefix your comment with "Minor: ".
- If a change is a net improvement, for example because it improves on an existing limitation of existing code, do not complain about pre-existing problems that remain. You may comment on them, but you should make it clear that the thing you are commenting on is not new by writing e.g. "Not new in this PR, but [..]" followed by your description of the issue and a suggestion that it could be improved at the same time with e.g. "Consider whether this is worth addressing as part of this PR as well."
## Generated code
@@ -22,6 +25,16 @@ The main codebase of the CodeQL Action is written in TypeScript and compiled to
GitHub Actions workflows in the `.github/workflows` directory whose filenames start with two underscores (e.g. `__all-platform-bundle.yml`) are automatically generated using the `pr-checks/sync.sh` script from template files in the `pr-checks/checks` directory. Therefore, you do not need to review files in the `.github/workflows` directory that start with two underscores. However, you should review changes to the `pr-checks` directory as well as workflows in the `.github/workflows` directory that do not start with underscores.
## Feature flags
Some functionality in the code may be gated behind feature flags, which are controlled server-side. If the functionality that is gated behind them is user-facing, then this should be documented in a change note at the time that the gated functionality is added. See `CHANGELOG.md` for existing notes that mention "experimental" for examples. These typically include information about when users may expect to see the feature rolled out.
When a feature flag that gates some functionality is removed, this does not need to be documented in a change note since the feature is already rolled out and the notes for the release which introduced the feature already describe it. The only exception is functionality which will start having an effect on GHES as a result of the feature flag removal:
- Most functionality gated behind feature flags does not affect GHES.
- We can make functionality available on GHES by setting `defaultValue` to `true` first and waiting for this to have had an effect on GHES. No change note is needed if a feature flag is removed where `defaultValue` was `true`.
- When removing a feature flag where `defaultValue` was `false` and the functionality starts to affect GHES, a change note may be appropriate.
## Release process
Changes that are merged to the `main` branch are not used in production straight away. Instead, changes from `main` must first be released using the release process, which has to be manually triggered by a staff member. Therefore, it is possible for further changes to be made to the code after some changes have been merged to `main` but before they are released. Once the release process automation is started, a series of PRs are automatically generated:

View File

@@ -87,7 +87,7 @@ jobs:
post-processed-sarif-path: '${{ runner.temp }}/post-processed'
- name: Upload SARIF files
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: |
analysis-kinds-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}
@@ -95,7 +95,7 @@ jobs:
retention-days: 7
- name: Upload post-processed SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: |
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}

View File

@@ -65,7 +65,7 @@ jobs:
output: '${{ runner.temp }}/results'
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: '${{ runner.temp }}/results/javascript.sarif'

View File

@@ -47,7 +47,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 20.x
cache: npm

View File

@@ -76,7 +76,7 @@ jobs:
output: '${{ runner.temp }}/results'
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: '${{ runner.temp }}/results/javascript.sarif'

View File

@@ -97,7 +97,7 @@ jobs:
with:
output: '${{ runner.temp }}/results'
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: '${{ runner.temp }}/results/javascript.sarif'

View File

@@ -62,7 +62,7 @@ jobs:
with:
output: '${{ runner.temp }}/results'
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: '${{ runner.temp }}/results/javascript.sarif'

View File

@@ -192,6 +192,5 @@ jobs:
exit 1
fi
env:
CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES: true
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
CODEQL_ACTION_TEST_MODE: true

View File

@@ -80,7 +80,7 @@ jobs:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 20.x
cache: npm

View File

@@ -80,7 +80,7 @@ jobs:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 20.x
cache: npm

View File

@@ -80,7 +80,7 @@ jobs:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 20.x
cache: npm

View File

@@ -80,7 +80,7 @@ jobs:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 20.x
cache: npm

View File

@@ -57,7 +57,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: 'npm'

View File

@@ -89,7 +89,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- name: Check expected artifacts exist
run: |
LANGUAGES="cpp csharp go java javascript python"

View File

@@ -82,7 +82,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- name: Check expected artifacts exist
run: |
VERSIONS="stable-v2.20.7 default linked nightly-latest"

View File

@@ -47,7 +47,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # ensure we have all tags and can push commits
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: 'npm'

View File

@@ -42,7 +42,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
@@ -110,7 +110,7 @@ jobs:
fi
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: "npm"
@@ -175,7 +175,7 @@ jobs:
- name: Upload repo size comment
if: steps.fetch-base.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: repo-size-comment
path: ${{ runner.temp }}/repo-size/
@@ -223,7 +223,7 @@ jobs:
steps:
- name: Download repo size comment
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: repo-size-comment
path: repo-size-comment

View File

@@ -33,7 +33,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: npm

View File

@@ -30,7 +30,7 @@ jobs:
ref: ${{ env.HEAD_REF }}
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: 'npm'

View File

@@ -41,7 +41,7 @@ jobs:
git config --global user.name "github-actions[bot]"
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: 'npm'

View File

@@ -26,7 +26,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: 'npm'

View File

@@ -2,6 +2,10 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## [UNRELEASED]
No user facing changes.
## 4.39.0 - 15 Oct 2026
- _Breaking change_: Bump the minimum required CodeQL bundle version to 2.20.7. [#4205](https://github.com/github/codeql-action/pull/4205)

356
lib/entry-points.js generated
View File

@@ -26401,7 +26401,7 @@ var require_utils4 = __commonJS({
})();
Object.defineProperty(exports2, "__esModule", { value: true });
exports2.GitHub = exports2.defaults = exports2.context = void 0;
exports2.getOctokitOptions = getOctokitOptions2;
exports2.getOctokitOptions = getOctokitOptions;
var Context = __importStar2(require_context());
var Utils = __importStar2(require_utils3());
var core_1 = (init_dist_src2(), __toCommonJS(dist_src_exports));
@@ -26417,7 +26417,7 @@ var require_utils4 = __commonJS({
}
};
exports2.GitHub = core_1.Octokit.plugin(plugin_rest_endpoint_methods_1.restEndpointMethods, plugin_paginate_rest_1.paginateRest).defaults(exports2.defaults);
function getOctokitOptions2(token, options) {
function getOctokitOptions(token, options) {
const opts = Object.assign({}, options || {});
const auth2 = Utils.getAuthString(token, opts);
if (auth2) {
@@ -34719,7 +34719,7 @@ var require_cacheUtils = __commonJS({
var crypto3 = __importStar2(require("crypto"));
var fs32 = __importStar2(require("fs"));
var path30 = __importStar2(require("path"));
var semver11 = __importStar2(require_semver3());
var semver12 = __importStar2(require_semver3());
var util4 = __importStar2(require("util"));
var constants_1 = require_constants7();
var versionSalt = "1.0";
@@ -34812,7 +34812,7 @@ var require_cacheUtils = __commonJS({
function getCompressionMethod() {
return __awaiter2(this, void 0, void 0, function* () {
const versionOutput = yield getVersion("zstd", ["--quiet"]);
const version = semver11.clean(versionOutput);
const version = semver12.clean(versionOutput);
core29.debug(`zstd version: ${version}`);
if (versionOutput === "") {
return constants_1.CompressionMethod.Gzip;
@@ -42967,11 +42967,11 @@ var require_tracingPolicy = __commonJS({
var import_log = require_log3();
var import_core_util = require_commonjs4();
var import_restError = require_restError3();
var import_util59 = require_internal3();
var import_util58 = require_internal3();
var tracingPolicyName2 = "tracingPolicy";
function tracingPolicy2(options = {}) {
const userAgentPromise = (0, import_userAgent.getUserAgentValue)(options.userAgentPrefix);
const sanitizer = new import_util59.Sanitizer({
const sanitizer = new import_util58.Sanitizer({
additionalAllowedQueryParameters: options.additionalAllowedQueryParameters
});
const tracingClient = tryCreateTracingClient();
@@ -46597,11 +46597,11 @@ var require_response2 = __commonJS({
});
module2.exports = __toCommonJS2(response_exports);
var import_core_rest_pipeline = require_commonjs6();
var import_util59 = require_util9();
var import_util58 = require_util9();
var originalResponse = /* @__PURE__ */ Symbol("Original FullOperationResponse");
function toCompatResponse2(response, options) {
let request3 = (0, import_util59.toWebResourceLike)(response.request);
let headers = (0, import_util59.toHttpHeadersLike)(response.headers);
let request3 = (0, import_util58.toWebResourceLike)(response.request);
let headers = (0, import_util58.toHttpHeadersLike)(response.headers);
if (options?.createProxy) {
return new Proxy(response, {
get(target, prop, receiver) {
@@ -46642,7 +46642,7 @@ var require_response2 = __commonJS({
return {
...compatResponse,
headers,
request: (0, import_util59.toPipelineRequest)(compatResponse.request)
request: (0, import_util58.toPipelineRequest)(compatResponse.request)
};
}
}
@@ -46749,7 +46749,7 @@ var require_requestPolicyFactoryPolicy = __commonJS({
requestPolicyFactoryPolicyName: () => requestPolicyFactoryPolicyName2
});
module2.exports = __toCommonJS2(requestPolicyFactoryPolicy_exports);
var import_util59 = require_util9();
var import_util58 = require_util9();
var import_response = require_response2();
var HttpPipelineLogLevel2 = /* @__PURE__ */ ((HttpPipelineLogLevel22) => {
HttpPipelineLogLevel22[HttpPipelineLogLevel22["ERROR"] = 1] = "ERROR";
@@ -46773,14 +46773,14 @@ var require_requestPolicyFactoryPolicy = __commonJS({
async sendRequest(request3, next) {
let httpPipeline = {
async sendRequest(httpRequest) {
const response2 = await next((0, import_util59.toPipelineRequest)(httpRequest));
const response2 = await next((0, import_util58.toPipelineRequest)(httpRequest));
return (0, import_response.toCompatResponse)(response2, { createProxy: true });
}
};
for (const factory of orderedFactories) {
httpPipeline = factory.create(httpPipeline, mockRequestPolicyOptions);
}
const webResourceLike = (0, import_util59.toWebResourceLike)(request3, { createProxy: true });
const webResourceLike = (0, import_util58.toWebResourceLike)(request3, { createProxy: true });
const response = await httpPipeline.sendRequest(webResourceLike);
return (0, import_response.toPipelineResponse)(response);
}
@@ -46815,12 +46815,12 @@ var require_httpClientAdapter = __commonJS({
});
module2.exports = __toCommonJS2(httpClientAdapter_exports);
var import_response = require_response2();
var import_util59 = require_util9();
var import_util58 = require_util9();
function convertHttpClient2(requestPolicyClient) {
return {
sendRequest: async (request3) => {
const response = await requestPolicyClient.sendRequest(
(0, import_util59.toWebResourceLike)(request3, { createProxy: true })
(0, import_util58.toWebResourceLike)(request3, { createProxy: true })
);
return (0, import_response.toPipelineResponse)(response);
}
@@ -46858,7 +46858,7 @@ var require_commonjs9 = __commonJS({
disableKeepAlivePolicyName: () => import_disableKeepAlivePolicy.disableKeepAlivePolicyName,
requestPolicyFactoryPolicyName: () => import_requestPolicyFactoryPolicy.requestPolicyFactoryPolicyName,
toCompatResponse: () => import_response.toCompatResponse,
toHttpHeadersLike: () => import_util59.toHttpHeadersLike
toHttpHeadersLike: () => import_util58.toHttpHeadersLike
});
module2.exports = __toCommonJS2(src_exports);
var import_extendedClient = require_extendedClient();
@@ -46866,7 +46866,7 @@ var require_commonjs9 = __commonJS({
var import_requestPolicyFactoryPolicy = require_requestPolicyFactoryPolicy();
var import_disableKeepAlivePolicy = require_disableKeepAlivePolicy();
var import_httpClientAdapter = require_httpClientAdapter();
var import_util59 = require_util9();
var import_util58 = require_util9();
}
});
@@ -106361,7 +106361,7 @@ var require_manifest = __commonJS({
exports2._findMatch = _findMatch;
exports2._getOsVersion = _getOsVersion;
exports2._readLinuxVersionFile = _readLinuxVersionFile;
var semver11 = __importStar2(require_semver2());
var semver12 = __importStar2(require_semver2());
var core_1 = require_core();
var os7 = require("os");
var cp = require("child_process");
@@ -106375,7 +106375,7 @@ var require_manifest = __commonJS({
for (const candidate of candidates) {
const version = candidate.version;
(0, core_1.debug)(`check ${version} satisfies ${versionSpec}`);
if (semver11.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) {
if (semver12.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) {
file = candidate.files.find((item) => {
(0, core_1.debug)(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`);
let chk = item.arch === archFilter && item.platform === platFilter;
@@ -106384,7 +106384,7 @@ var require_manifest = __commonJS({
if (osVersion === item.platform_version) {
chk = true;
} else {
chk = semver11.satisfies(osVersion, item.platform_version);
chk = semver12.satisfies(osVersion, item.platform_version);
}
}
return chk;
@@ -106644,7 +106644,7 @@ var require_tool_cache = __commonJS({
var os7 = __importStar2(require("os"));
var path30 = __importStar2(require("path"));
var httpm = __importStar2(require_lib());
var semver11 = __importStar2(require_semver2());
var semver12 = __importStar2(require_semver2());
var stream2 = __importStar2(require("stream"));
var util4 = __importStar2(require("util"));
var assert_1 = require("assert");
@@ -106917,7 +106917,7 @@ var require_tool_cache = __commonJS({
}
function cacheDir2(sourceDir, tool, version, arch2) {
return __awaiter2(this, void 0, void 0, function* () {
version = semver11.clean(version) || version;
version = semver12.clean(version) || version;
arch2 = arch2 || os7.arch();
core29.debug(`Caching tool ${tool} ${version} ${arch2}`);
core29.debug(`source dir: ${sourceDir}`);
@@ -106935,7 +106935,7 @@ var require_tool_cache = __commonJS({
}
function cacheFile(sourceFile, targetFile, tool, version, arch2) {
return __awaiter2(this, void 0, void 0, function* () {
version = semver11.clean(version) || version;
version = semver12.clean(version) || version;
arch2 = arch2 || os7.arch();
core29.debug(`Caching tool ${tool} ${version} ${arch2}`);
core29.debug(`source file: ${sourceFile}`);
@@ -106965,7 +106965,7 @@ var require_tool_cache = __commonJS({
}
let toolPath = "";
if (versionSpec) {
versionSpec = semver11.clean(versionSpec) || "";
versionSpec = semver12.clean(versionSpec) || "";
const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2);
core29.debug(`checking cache: ${cachePath}`);
if (fs32.existsSync(cachePath) && fs32.existsSync(`${cachePath}.complete`)) {
@@ -107045,7 +107045,7 @@ var require_tool_cache = __commonJS({
}
function _createToolPath(tool, version, arch2) {
return __awaiter2(this, void 0, void 0, function* () {
const folderPath = path30.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || "");
const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || "");
core29.debug(`destination ${folderPath}`);
const markerPath = `${folderPath}.complete`;
yield io9.rmRF(folderPath);
@@ -107055,15 +107055,15 @@ var require_tool_cache = __commonJS({
});
}
function _completeToolPath(tool, version, arch2) {
const folderPath = path30.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || "");
const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || "");
const markerPath = `${folderPath}.complete`;
fs32.writeFileSync(markerPath, "");
core29.debug("finished caching tool");
}
function isExplicitVersion(versionSpec) {
const c = semver11.clean(versionSpec) || "";
const c = semver12.clean(versionSpec) || "";
core29.debug(`isExplicit: ${c}`);
const valid4 = semver11.valid(c) != null;
const valid4 = semver12.valid(c) != null;
core29.debug(`explicit? ${valid4}`);
return valid4;
}
@@ -107071,14 +107071,14 @@ var require_tool_cache = __commonJS({
let version = "";
core29.debug(`evaluating ${versions.length} versions`);
versions = versions.sort((a, b) => {
if (semver11.gt(a, b)) {
if (semver12.gt(a, b)) {
return 1;
}
return -1;
});
for (let i = versions.length - 1; i >= 0; i--) {
const potential = versions[i];
const satisfied = semver11.satisfies(potential, versionSpec);
const satisfied = semver12.satisfies(potential, versionSpec);
if (satisfied) {
version = potential;
break;
@@ -140010,7 +140010,7 @@ var require_utils10 = __commonJS({
}
};
exports2.GitHub = core_1.Octokit.plugin(plugin_rest_endpoint_methods_1.restEndpointMethods, plugin_paginate_rest_1.paginateRest).defaults(exports2.defaults);
function getOctokitOptions2(token, options) {
function getOctokitOptions(token, options) {
const opts = Object.assign({}, options || {});
const auth2 = Utils.getAuthString(token, opts);
if (auth2) {
@@ -140018,7 +140018,7 @@ var require_utils10 = __commonJS({
}
return opts;
}
exports2.getOctokitOptions = getOctokitOptions2;
exports2.getOctokitOptions = getOctokitOptions;
}
});
@@ -166182,6 +166182,17 @@ var Env = class extends ReadOnlyEnv {
core.exportVariable(name, val);
}
}
/**
* Set some initial environment variables that we can set even without
* knowing what version of CodeQL we're running.
*/
initialise(version) {
this.export("CODEQL_ACTION_FEATURE_MULTI_LANGUAGE" /* FEATURE_MULTI_LANGUAGE */, "false");
this.export("CODEQL_ACTION_FEATURE_SANDWICH" /* FEATURE_SANDWICH */, "false");
this.export("CODEQL_ACTION_FEATURE_SARIF_COMBINE" /* FEATURE_SARIF_COMBINE */, "true");
this.export("CODEQL_ACTION_FEATURE_WILL_UPLOAD" /* FEATURE_WILL_UPLOAD */, "true");
this.export("CODEQL_ACTION_VERSION" /* VERSION */, version);
}
/** Gets a value indicating whether `set` was called at least once. */
hasChanged() {
return this.changed;
@@ -169555,10 +169566,85 @@ function checkSchema(schema, obj, options = {}, path30 = "") {
return result;
}
// src/errors/index.ts
function wrapError(error3) {
return error3 instanceof Error ? error3 : new Error(String(error3));
}
function getErrorMessage(error3) {
return error3 instanceof Error ? error3.message : String(error3);
}
function isDiskConfigurationError(e) {
if (!(e instanceof Error)) {
return false;
}
return (
// out of disk space
e.message.includes("ENOSPC") || // access denied
e.message.includes("EACCES")
);
}
var ConfigurationError = class extends Error {
};
var HTTPError = class extends Error {
status;
constructor(message, status) {
super(message);
this.status = status;
}
};
function asHTTPError(arg) {
if (!isObject2(arg) || !isString(arg.message)) {
return void 0;
}
if (Number.isInteger(arg.status)) {
return new HTTPError(arg.message, arg.status);
}
if (Number.isInteger(arg.httpStatusCode)) {
return new HTTPError(arg.message, arg.httpStatusCode);
}
return void 0;
}
// src/github.ts
var GITHUB_DOTCOM_URL = "https://github.com";
function parseGitHubUrl(inputUrl) {
const originalUrl = inputUrl;
if (inputUrl.indexOf("://") === -1) {
inputUrl = `https://${inputUrl}`;
}
if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) {
throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`);
}
let url2;
try {
url2 = new URL(inputUrl);
} catch {
throw new ConfigurationError(`"${originalUrl}" is not a valid URL`);
}
if (url2.hostname === "github.com" || url2.hostname === "api.github.com") {
return GITHUB_DOTCOM_URL;
}
if (url2.pathname.indexOf("/api/v3") !== -1) {
url2.pathname = url2.pathname.substring(0, url2.pathname.indexOf("/api/v3"));
}
if (url2.hostname.startsWith("api.")) {
url2.hostname = url2.hostname.substring(4);
}
if (!url2.pathname.endsWith("/")) {
url2.pathname = `${url2.pathname}/`;
}
return url2.toString();
}
function getCurrentInstance(env) {
return {
url: env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */),
apiURL: env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */)
};
}
// src/util.ts
var BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json";
var BROKEN_VERSIONS = ["0.0.0-20211207"];
var GITHUB_DOTCOM_URL = "https://github.com";
var DEFAULT_DEBUG_ARTIFACT_NAME = "debug-artifacts";
var DEFAULT_DEBUG_DATABASE_NAME = "db";
var DEFAULT_RESERVED_RAM_SCALING_FACTOR = 0.05;
@@ -169774,34 +169860,6 @@ function getGeneratedSuitePath(config, language) {
"config-queries.qls"
);
}
function parseGitHubUrl(inputUrl) {
const originalUrl = inputUrl;
if (inputUrl.indexOf("://") === -1) {
inputUrl = `https://${inputUrl}`;
}
if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) {
throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`);
}
let url2;
try {
url2 = new URL(inputUrl);
} catch {
throw new ConfigurationError(`"${originalUrl}" is not a valid URL`);
}
if (url2.hostname === "github.com" || url2.hostname === "api.github.com") {
return GITHUB_DOTCOM_URL;
}
if (url2.pathname.indexOf("/api/v3") !== -1) {
url2.pathname = url2.pathname.substring(0, url2.pathname.indexOf("/api/v3"));
}
if (url2.hostname.startsWith("api.")) {
url2.hostname = url2.hostname.substring(4);
}
if (!url2.pathname.endsWith("/")) {
url2.pathname = `${url2.pathname}/`;
}
return url2.toString();
}
var CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION";
var hasBeenWarnedAboutVersion = false;
function checkGitHubVersionInRange(version, logger2) {
@@ -169845,37 +169903,6 @@ var ExhaustivityCheckingError = class extends Error {
function assertNever(value) {
throw new ExhaustivityCheckingError(value);
}
function initializeEnvironment(version, env = getEnv()) {
env.export("CODEQL_ACTION_FEATURE_MULTI_LANGUAGE" /* FEATURE_MULTI_LANGUAGE */, "false");
env.export("CODEQL_ACTION_FEATURE_SANDWICH" /* FEATURE_SANDWICH */, "false");
env.export("CODEQL_ACTION_FEATURE_SARIF_COMBINE" /* FEATURE_SARIF_COMBINE */, "true");
env.export("CODEQL_ACTION_FEATURE_WILL_UPLOAD" /* FEATURE_WILL_UPLOAD */, "true");
env.export("CODEQL_ACTION_VERSION" /* VERSION */, version);
}
var HTTPError = class extends Error {
status;
constructor(message, status) {
super(message);
this.status = status;
}
};
var ConfigurationError = class extends Error {
};
function asHTTPError(arg) {
if (!isObject2(arg) || !isString(arg.message)) {
return void 0;
}
if (Number.isInteger(arg.status)) {
return new HTTPError(arg.message, arg.status);
}
if (Number.isInteger(arg.httpStatusCode)) {
return new HTTPError(arg.message, arg.httpStatusCode);
}
return void 0;
}
async function codeQlVersionAtLeast(codeql, requiredVersion) {
return semver.gte((await codeql.getVersion()).version, requiredVersion);
}
function getBaseDatabaseOidsFilePath(config) {
return path.join(config.dbLocation, BASE_DATABASE_OIDS_FILE_NAME);
}
@@ -170010,12 +170037,6 @@ function parseMatrixInput(matrixInput) {
);
}
}
function wrapError(error3) {
return error3 instanceof Error ? error3 : new Error(String(error3));
}
function getErrorMessage(error3) {
return error3 instanceof Error ? error3.message : String(error3);
}
function prettyPrintPack(pack) {
return `${pack.name}${pack.version ? `@${pack.version}` : ""}${pack.path ? `:${pack.path}` : ""}`;
}
@@ -170183,7 +170204,7 @@ function getDiffRangesJsonFilePath(env = getEnv()) {
return path2.join(getTemporaryDirectory(env), PR_DIFF_RANGE_JSON_FILENAME);
}
function getActionVersion() {
return "4.39.0";
return "4.39.1";
}
function getWorkflowEventName(env = getEnv()) {
return env.getRequired("GITHUB_EVENT_NAME" /* GITHUB_EVENT_NAME */);
@@ -170724,43 +170745,46 @@ function makeProxyRequestOptions(dispatcher) {
};
}
function createApiClientWithDetails(apiDetails, { allowExternal = false, proxy = void 0 } = {}) {
const auth2 = allowExternal && apiDetails.externalRepoAuth || apiDetails.auth;
const auth2 = allowExternal && apiDetails.externalRepoAuth !== void 0 ? apiDetails.externalRepoAuth : apiDetails.auth;
const retryingOctokit = githubUtils.GitHub.plugin(retry);
const requestOptions = makeProxyRequestOptions(proxy);
return new retryingOctokit(
githubUtils.getOctokitOptions(auth2, {
baseUrl: apiDetails.apiURL,
userAgent: `CodeQL-Action/${getActionVersion()}`,
log: {
debug: core6.debug,
info: core6.info,
warn: core6.warning,
error: core6.error
},
request: requestOptions,
retry: {
doNotRetry: DO_NOT_RETRY_STATUSES
}
})
);
const opts = {
auth: auth2 !== void 0 && auth2.trim() !== "" ? `token ${auth2}` : void 0,
baseUrl: apiDetails.apiURL,
userAgent: `CodeQL-Action/${getActionVersion()}`,
log: {
debug: core6.debug,
info: core6.info,
warn: core6.warning,
error: core6.error
},
request: requestOptions,
retry: {
doNotRetry: DO_NOT_RETRY_STATUSES
}
};
return new retryingOctokit(opts);
}
function getApiDetails(env = getEnv()) {
return {
auth: getRequiredInput("token"),
url: env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */),
apiURL: env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */)
...getCurrentInstance(env)
};
}
function getApiClient(env = getEnv()) {
return createApiClientWithDetails(getApiDetails(env));
function getApiClient(env = getEnv(), apiDetails) {
return createApiClientWithDetails(apiDetails ?? getApiDetails(env));
}
function getApiClientWithExternalAuth(apiDetails, proxy) {
return createApiClientWithDetails(apiDetails, { allowExternal: true, proxy });
}
function getAuthorizationHeaderFor(logger2, apiDetails, url2) {
const token = apiDetails.auth?.trim();
if (token === void 0 || token === "") {
return void 0;
}
if (url2.startsWith(`${apiDetails.url}/`) || apiDetails.apiURL && url2.startsWith(`${apiDetails.apiURL}/`)) {
logger2.debug(`Providing an authorization token.`);
return `token ${apiDetails.auth}`;
return `token ${token}`;
}
logger2.debug(`Not using an authorization token.`);
return void 0;
@@ -171921,11 +171945,6 @@ var featureConfig = {
envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS",
minimumVersion: void 0
},
["cleanup_toolcache_bundles" /* CleanupToolcacheBundles */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES",
minimumVersion: void 0
},
["cleanup_trap_caches" /* CleanupTrapCaches */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_CLEANUP_TRAP_CACHES",
@@ -172244,7 +172263,7 @@ var OfflineFeatures = class {
}
const minimumVersion2 = config.minimumVersion;
if (codeql && minimumVersion2) {
if (!await codeQlVersionAtLeast(codeql, minimumVersion2)) {
if (!await codeql.isVersionAtLeast(minimumVersion2)) {
this.logger.debug(
`Feature ${feature} is disabled because the CodeQL CLI version is older than the minimum version ${minimumVersion2}.`
);
@@ -172667,6 +172686,7 @@ var fs16 = __toESM(require("fs"));
var path15 = __toESM(require("path"));
var core12 = __toESM(require_core());
var toolrunner3 = __toESM(require_toolrunner());
var semver10 = __toESM(require_semver2());
// src/cli-errors.ts
var SUPPORTED_PLATFORMS = [
@@ -174655,10 +174675,7 @@ function runnerHasSufficientDiskSpace(diskUsage, logger2, minimumDiskSpaceMb) {
return true;
}
async function runnerHasSufficientMemory(codeql, ramInput, logger2) {
if (await codeQlVersionAtLeast(
codeql,
CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE
)) {
if (await codeql.isVersionAtLeast(CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE)) {
logger2.debug(
`Skipping memory check for overlay analysis because CodeQL version is at least ${CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE}.`
);
@@ -174796,7 +174813,7 @@ async function validateOverlayDatabaseMode(overlayDatabaseMode, useOverlayDataba
);
return new Failure("incompatible-build-mode" /* IncompatibleBuildMode */);
}
if (!await codeQlVersionAtLeast(codeql, CODEQL_OVERLAY_MINIMUM_VERSION)) {
if (!await codeql.isVersionAtLeast(CODEQL_OVERLAY_MINIMUM_VERSION)) {
logger2.warning(
`Cannot build an ${overlayDatabaseMode} database because the CodeQL CLI is older than ${CODEQL_OVERLAY_MINIMUM_VERSION}. Falling back to creating a normal full database instead.`
);
@@ -176718,7 +176735,6 @@ function getToolcacheDestination({ logger: logger2 }, source) {
}
async function tryDeleteToolcacheBundles({
env,
features,
logger: logger2
}) {
if (env.getOptional("CODEQL_ACTION_HAS_SET_UP_CODEQL" /* HAS_SET_UP_CODEQL */) !== void 0) {
@@ -176727,7 +176743,7 @@ async function tryDeleteToolcacheBundles({
);
return;
}
if (!isGitHubHostedRunner() || !isToolcacheOnWorkspaceFilesystem(logger2) || !await features.getValue("cleanup_toolcache_bundles" /* CleanupToolcacheBundles */)) {
if (!isGitHubHostedRunner(env) || !isToolcacheOnWorkspaceFilesystem(logger2)) {
return;
}
const result = await deleteToolcacheBundles({ env, logger: logger2 });
@@ -176787,7 +176803,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau
break;
case "download": {
const result = await downloadCodeQLBundle(
{ env: getEnv(), features, logger: logger2 },
{ env: getEnv(), logger: logger2 },
source,
apiDetails,
zstdAvailability.version,
@@ -176997,16 +177013,6 @@ var CODEQL_NEXT_MINIMUM_VERSION = "2.21.3";
var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.17";
var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-09-24";
var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
function isDiskConfigurationError(e) {
if (!(e instanceof Error)) {
return false;
}
return (
// out of disk space
e.message.includes("ENOSPC") || // access denied
e.message.includes("EACCES")
);
}
async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2, checkVersion) {
try {
const {
@@ -177081,6 +177087,9 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion) {
}
return result;
},
async isVersionAtLeast(requiredVersion) {
return semver10.gte((await this.getVersion()).version, requiredVersion);
},
async printVersion() {
core12.info(JSON.stringify(await this.getVersion(), null, 2));
},
@@ -177469,11 +177478,11 @@ Output was: ${extractorPath}`
await runCli(cmd, args);
}
};
if (checkVersion && !await codeQlVersionAtLeast(codeql, CODEQL_MINIMUM_VERSION)) {
if (checkVersion && !await codeql.isVersionAtLeast(CODEQL_MINIMUM_VERSION)) {
throw new ConfigurationError(
`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${(await codeql.getVersion()).version}`
);
} else if (checkVersion && process.env["CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING" /* SUPPRESS_DEPRECATED_SOON_WARNING */] !== "true" && !await codeQlVersionAtLeast(codeql, CODEQL_NEXT_MINIMUM_VERSION)) {
} else if (checkVersion && process.env["CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING" /* SUPPRESS_DEPRECATED_SOON_WARNING */] !== "true" && !await codeql.isVersionAtLeast(CODEQL_NEXT_MINIMUM_VERSION)) {
const result = await codeql.getVersion();
core12.warning(
`CodeQL CLI version ${result.version} was discontinued on ${GHES_MOST_RECENT_DEPRECATION_DATE} alongside GitHub Enterprise Server ${GHES_VERSION_MOST_RECENTLY_DEPRECATED} and will not be supported by the next minor release of the CodeQL Action. Please update to CodeQL CLI version ${CODEQL_NEXT_MINIMUM_VERSION} or later. For instance, if you have specified a custom version of the CLI using the 'tools' input to the 'init' Action, you can remove this input to use the default version.
@@ -178491,7 +178500,7 @@ async function recordClearCleanupSizes(codeql, config, reports, logger2) {
}
}
async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundledDb, bundledDbSize, apiDetails) {
const client = getApiClient();
const client = getApiClient(getEnv(), apiDetails);
const uploadsUrl = new URL(parseGitHubUrl(apiDetails.url));
uploadsUrl.hostname = `uploads.${uploadsUrl.hostname}`;
let uploadsBaseUrl = uploadsUrl.toString();
@@ -178512,7 +178521,6 @@ async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundled
commit_oid: commitOid,
data: bundledDbReadStream,
headers: {
authorization: `token ${apiDetails.auth}`,
"Content-Type": "application/zip",
"Content-Length": bundledDbSize
},
@@ -180011,6 +180019,7 @@ async function shouldDisableCombineSarifFiles(sarifObjects, githubVersion) {
return true;
}
async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, logger2) {
const env = getEnv();
logger2.info("Combining SARIF files using the CodeQL CLI");
const sarifObjects = sarifFiles.map(readSarifFile);
const deprecationWarningMessage = gitHubVersion.type === "GitHub Enterprise Server" /* GHES */ ? "and will be removed in GitHub Enterprise Server 3.18" : "and will be removed in July 2025";
@@ -180043,8 +180052,7 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo
externalRepoAuth: getOptionalInput(
"external-repository-token"
),
url: getRequiredEnvParam("GITHUB_SERVER_URL"),
apiURL: getRequiredEnvParam("GITHUB_API_URL")
...getCurrentInstance(env)
};
const codeQLDefaultVersionInfo = await features.getEnabledDefaultCliVersions(gitHubVersion.type);
const initCodeQLResult = await initCodeQL(
@@ -180755,7 +180763,7 @@ async function run(action) {
let dependencyCacheResults;
let databaseUploadResults = [];
try {
initializeEnvironment(getActionVersion());
action.env.initialise(getActionVersion());
persistInputs();
const statusReportBase = await createStatusReportBase(
"finish" /* Analyze */,
@@ -183086,7 +183094,7 @@ var import_async = __toESM(require_async(), 1);
var import_path7 = require("path");
// node_modules/archiver/lib/error.js
var import_util37 = __toESM(require("util"), 1);
var import_util36 = __toESM(require("util"), 1);
var ERROR_CODES = {
ABORTED: "archive was aborted",
DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value",
@@ -183111,7 +183119,7 @@ function ArchiverError(code, data) {
this.code = code;
this.data = data;
}
import_util37.default.inherits(ArchiverError, Error);
import_util36.default.inherits(ArchiverError, Error);
// node_modules/archiver/lib/core.js
var import_readable_stream2 = __toESM(require_ours(), 1);
@@ -185934,8 +185942,8 @@ async function runWrapper2() {
// src/autobuild-action.ts
var core17 = __toESM(require_core());
async function sendCompletedStatusReport(config, logger2, startedAt, allLanguages, failingLanguage, cause) {
initializeEnvironment(getActionVersion());
async function sendCompletedStatusReport(env, config, logger2, startedAt, allLanguages, failingLanguage, cause) {
env.initialise(getActionVersion());
const status = getActionsStatus(cause, failingLanguage);
const statusReportBase = await createStatusReportBase(
"autobuild" /* Autobuild */,
@@ -186007,6 +186015,7 @@ async function run2({
`We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. ${error3.message}`
);
await sendCompletedStatusReport(
env,
config,
logger2,
startedAt,
@@ -186017,7 +186026,13 @@ async function run2({
return;
}
env.export("CODEQL_ACTION_AUTOBUILD_DID_COMPLETE_SUCCESSFULLY" /* AUTOBUILD_DID_COMPLETE_SUCCESSFULLY */, "true");
await sendCompletedStatusReport(config, logger2, startedAt, languages ?? []);
await sendCompletedStatusReport(
env,
config,
logger2,
startedAt,
languages ?? []
);
}
var autobuild = {
name: "autobuild" /* Autobuild */,
@@ -186032,7 +186047,7 @@ var fs29 = __toESM(require("fs"));
var path25 = __toESM(require("path"));
var core19 = __toESM(require_core());
var io7 = __toESM(require_io());
var semver10 = __toESM(require_semver2());
var semver11 = __toESM(require_semver2());
// src/config/inputs.ts
async function getToolsInput(action, repositoryProperties) {
@@ -186445,13 +186460,12 @@ async function run3(actionState) {
let toolsSource;
let toolsVersion;
try {
initializeEnvironment(getActionVersion());
actionState.env.initialise(getActionVersion());
persistInputs();
apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: actionState.env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */),
apiURL: actionState.env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */)
...getCurrentInstance(actionState.env)
};
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger2);
@@ -186543,7 +186557,7 @@ async function run3(actionState) {
) {
const publicPreview = "2.22.1";
const actualVer = (await codeql.getVersion()).version;
if (semver10.lt(actualVer, publicPreview)) {
if (semver11.lt(actualVer, publicPreview)) {
actionState.env.export("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true");
logger2.info("Experimental Rust analysis enabled");
}
@@ -186776,7 +186790,7 @@ exec ${goBinaryPath} "$@"`
logger2.debug(
`${"CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */} is already set to '${process.env["CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */]}', so the Action will not override it.`
);
} else if (await codeQlVersionAtLeast(codeql, CODEQL_VERSION_JAR_MINIMIZATION) && config.dependencyCachingEnabled && config.buildMode === "none" /* None */ && config.languages.includes("java" /* java */)) {
} else if (await codeql.isVersionAtLeast(CODEQL_VERSION_JAR_MINIMIZATION) && config.dependencyCachingEnabled && config.buildMode === "none" /* None */ && config.languages.includes("java" /* java */)) {
actionState.env.export(
"CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */,
"true"
@@ -187489,12 +187503,11 @@ async function run6(actionState) {
let toolsSource;
let toolsVersion;
try {
initializeEnvironment(getActionVersion());
actionState.env.initialise(getActionVersion());
const apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: actionState.env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */),
apiURL: actionState.env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */)
...getCurrentInstance(actionState.env)
};
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger2);
@@ -188442,9 +188455,10 @@ async function sendSuccessStatusReport2(startedAt, uploadStats, logger2) {
await sendStatusReport(statusReport);
}
}
async function run8({ startedAt, logger: logger2 }) {
async function run8(action) {
const logger2 = action.logger;
try {
initializeEnvironment(getActionVersion());
action.env.initialise(getActionVersion());
const gitHubVersion = await getGitHubVersion();
checkActionVersion(getActionVersion(), gitHubVersion);
persistInputs();
@@ -188458,7 +188472,7 @@ async function run8({ startedAt, logger: logger2 }) {
const startingStatusReportBase = await createStatusReportBase(
"upload-sarif" /* UploadSarif */,
"starting",
startedAt,
action.startedAt,
void 0,
await checkDiskUsage(logger2),
logger2
@@ -188501,7 +188515,7 @@ async function run8({ startedAt, logger: logger2 }) {
}
}
await sendSuccessStatusReport2(
startedAt,
action.startedAt,
codeScanningResult?.statusReport || {},
logger2
);
@@ -188512,7 +188526,7 @@ async function run8({ startedAt, logger: logger2 }) {
const errorStatusReportBase = await createStatusReportBase(
"upload-sarif" /* UploadSarif */,
getActionsStatus(error3),
startedAt,
action.startedAt,
void 0,
await checkDiskUsage(logger2),
logger2,

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "codeql",
"version": "4.39.0",
"version": "4.39.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.39.0",
"version": "4.39.1",
"license": "MIT",
"workspaces": [
"pr-checks"

View File

@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.39.0",
"version": "4.39.1",
"private": true,
"description": "CodeQL action",
"scripts": {

View File

@@ -46,7 +46,7 @@ steps:
post-processed-sarif-path: "${{ runner.temp }}/post-processed"
- name: Upload SARIF files
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: |
analysis-kinds-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}
@@ -54,7 +54,7 @@ steps:
retention-days: 7
- name: Upload post-processed SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: |
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}

View File

@@ -14,7 +14,7 @@ steps:
output: "${{ runner.temp }}/results"
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"

View File

@@ -27,7 +27,7 @@ steps:
output: "${{ runner.temp }}/results"
upload-database: false
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"

View File

@@ -27,7 +27,7 @@ steps:
with:
output: "${{ runner.temp }}/results"
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"

View File

@@ -12,7 +12,7 @@ steps:
with:
output: "${{ runner.temp }}/results"
- name: Upload SARIF
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"

View File

@@ -14,7 +14,6 @@ operatingSystems:
- stable-v2.21.4
- stable-v2.22.4
env:
CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES: true
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
installGo: true
installDotNet: true

View File

@@ -215,8 +215,8 @@ const languageSetups: LanguageSetups = {
name: "Install Node.js",
uses: pinnedUses(
"actions/setup-node",
"820762786026740c76f36085b0efc47a31fe5020",
"v7.0.0",
"949feb2413d6458794dcd2491c4babbbce0c15c1",
"v7.1.0",
),
with: {
"node-version": defaultLanguageVersions.javascript,

View File

@@ -12,8 +12,8 @@ import {
import { computeAutomationID } from "./api-client";
import { EnvVar } from "./environment";
import { getRunnerLogger } from "./logging";
import { RecordingLogger, setupTests } from "./testing-utils";
import { initializeEnvironment, withTmpDir } from "./util";
import { getTestEnv, RecordingLogger, setupTests } from "./testing-utils";
import { withTmpDir } from "./util";
setupTests(test);
@@ -228,8 +228,9 @@ test.serial(
);
test.serial("initializeEnvironment", (t) => {
initializeEnvironment("1.2.3");
t.deepEqual(process.env[EnvVar.VERSION], "1.2.3");
const env = getTestEnv();
env.initialise("1.2.3");
t.deepEqual(env.getOptional(EnvVar.VERSION), "1.2.3");
});
test.serial("fixCodeQualityCategory", (t) => {

View File

@@ -238,7 +238,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
let databaseUploadResults: DatabaseUploadResult[] = [];
try {
util.initializeEnvironment(actionsUtil.getActionVersion());
action.env.initialise(actionsUtil.getActionVersion());
// Make inputs accessible in the `post` step, details at
// https://github.com/github/codeql-action/issues/2553

View File

@@ -7,14 +7,21 @@ import { ProxyAgent } from "undici";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { DO_NOT_RETRY_STATUSES } from "./api-client";
import { ActionsEnvVars, RegistryProxyVars } from "./environment";
import { callee, getTestEnv, setupTests } from "./testing-utils";
import { ActionsEnvVars, RegistryProxyVars, getEnv } from "./environment";
import { GITHUB_DOTCOM_API_URL, GITHUB_DOTCOM_URL } from "./github";
import {
callee,
getTestEnv,
RecordingLogger,
SAMPLE_DOTCOM_API_DETAILS,
setupTests,
} from "./testing-utils";
import * as util from "./util";
setupTests(test);
test.beforeEach(() => {
util.initializeEnvironment(actionsUtil.getActionVersion());
getEnv().initialise(actionsUtil.getActionVersion());
});
test.serial("getApiClient", async (t) => {
@@ -284,3 +291,87 @@ test("makeProxyRequestOptions - returns fetch with custom proxy", async (t) => {
githubUtils.defaults.request,
);
});
test.serial(
"getApiClientWithExternalAuth constructs clients with or without tokens",
(t) => {
t.notThrows(() => {
api.getApiClientWithExternalAuth(SAMPLE_DOTCOM_API_DETAILS);
api.getApiClientWithExternalAuth({
...SAMPLE_DOTCOM_API_DETAILS,
auth: undefined,
});
});
},
);
test("getAuthorizationHeaderFor - returns undefined if there's no token", (t) => {
const logger = new RecordingLogger();
t.is(
api.getAuthorizationHeaderFor(
logger,
{ ...SAMPLE_DOTCOM_API_DETAILS, auth: undefined },
`${GITHUB_DOTCOM_URL}/github`,
),
undefined,
);
t.is(
api.getAuthorizationHeaderFor(
logger,
{ ...SAMPLE_DOTCOM_API_DETAILS, auth: "" },
`${GITHUB_DOTCOM_URL}/github`,
),
undefined,
);
t.is(
api.getAuthorizationHeaderFor(
logger,
{ ...SAMPLE_DOTCOM_API_DETAILS, auth: " " },
`${GITHUB_DOTCOM_URL}/github`,
),
undefined,
);
});
test("getAuthorizationHeaderFor - returns header value with token if url is allowed", (t) => {
const logger = new RecordingLogger();
t.is(
api.getAuthorizationHeaderFor(
logger,
SAMPLE_DOTCOM_API_DETAILS,
`${GITHUB_DOTCOM_URL}/github`,
),
`token ${SAMPLE_DOTCOM_API_DETAILS.auth}`,
);
t.is(
api.getAuthorizationHeaderFor(
logger,
SAMPLE_DOTCOM_API_DETAILS,
`${GITHUB_DOTCOM_API_URL}/github`,
),
`token ${SAMPLE_DOTCOM_API_DETAILS.auth}`,
);
});
test("getAuthorizationHeaderFor - returns undefined if url is not allowed", (t) => {
const logger = new RecordingLogger();
t.is(
api.getAuthorizationHeaderFor(
logger,
SAMPLE_DOTCOM_API_DETAILS,
`https://localhost/github`,
),
undefined,
);
t.is(
api.getAuthorizationHeaderFor(
logger,
SAMPLE_DOTCOM_API_DETAILS,
`https://api.localhost/github`,
),
undefined,
);
});

View File

@@ -1,5 +1,6 @@
import * as core from "@actions/core";
import * as githubUtils from "@actions/github/lib/utils";
import type { OctokitOptions } from "@octokit/core";
import * as retry from "@octokit/plugin-retry";
import { RequestRequestOptions } from "@octokit/types";
import {
@@ -12,26 +13,32 @@ import {
import type { ActionState } from "./action-common";
import { getActionVersion, getRequiredInput } from "./actions-util";
import {
ActionsEnvVars,
EnvVar,
ReadOnlyEnv,
RegistryProxyVars,
getEnv,
exportEnvVar,
} from "./environment";
import {
getCurrentInstance,
GITHUB_DOTCOM_URL,
parseGitHubUrl,
type GitHubApiCombinedDetails,
type GitHubApiDetails,
} from "./github";
import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
import {
asHTTPError,
ConfigurationError,
getRequiredEnvParam,
GITHUB_DOTCOM_URL,
GitHubVariant,
GitHubVersion,
parseGitHubUrl,
parseMatrixInput,
} from "./util";
export type { GitHubApiDetails, GitHubApiCombinedDetails } from "./github";
const GITHUB_ENTERPRISE_VERSION_HEADER = "x-github-enterprise-version";
/**
@@ -43,21 +50,6 @@ const GITHUB_ENTERPRISE_VERSION_HEADER = "x-github-enterprise-version";
*/
export const DO_NOT_RETRY_STATUSES = [400, 410, 422, 451];
export type GitHubApiCombinedDetails = GitHubApiDetails &
GitHubApiExternalRepoDetails;
export interface GitHubApiDetails {
auth: string;
url: string;
apiURL: string | undefined;
}
export interface GitHubApiExternalRepoDetails {
externalRepoAuth?: string;
url: string;
apiURL: string | undefined;
}
/**
* Gets the configuration for the private registry authentication proxy,
* if it is available in the environment.
@@ -139,37 +131,60 @@ function createApiClientWithDetails(
{ allowExternal = false, proxy = undefined }: CreateApiClientOptions = {},
): ApiClient {
const auth =
(allowExternal && apiDetails.externalRepoAuth) || apiDetails.auth;
allowExternal && apiDetails.externalRepoAuth !== undefined
? apiDetails.externalRepoAuth
: apiDetails.auth;
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry);
const requestOptions = makeProxyRequestOptions(proxy);
return new retryingOctokit(
githubUtils.getOctokitOptions(auth, {
baseUrl: apiDetails.apiURL,
userAgent: `CodeQL-Action/${getActionVersion()}`,
log: {
debug: core.debug,
info: core.info,
warn: core.warning,
error: core.error,
},
request: requestOptions,
retry: {
doNotRetry: DO_NOT_RETRY_STATUSES,
},
}),
);
const opts: OctokitOptions = {
auth:
auth !== undefined && auth.trim() !== "" ? `token ${auth}` : undefined,
baseUrl: apiDetails.apiURL,
userAgent: `CodeQL-Action/${getActionVersion()}`,
log: {
debug: core.debug,
info: core.info,
warn: core.warning,
error: core.error,
},
request: requestOptions,
retry: {
doNotRetry: DO_NOT_RETRY_STATUSES,
},
};
return new retryingOctokit(opts);
}
/**
* Gets a `GitHubApiDetails` object describing the GitHub instance we are currently running on.
*
* @param env The environment variables to read.
*/
export function getApiDetails(env: ReadOnlyEnv = getEnv()): GitHubApiDetails {
return {
auth: getRequiredInput("token"),
url: env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: env.getRequired(ActionsEnvVars.GITHUB_API_URL),
...getCurrentInstance(env),
};
}
export function getApiClient(env: ReadOnlyEnv = getEnv()) {
return createApiClientWithDetails(getApiDetails(env));
/**
* Constructs an {@link ApiClient} using {@link apiDetails}.
*
* If {@link apiDetails} is not specified, then the GitHub instance URLs and auth token
* are taken from {@link env}.
*
* If {@link env} is not specified, then the process environment is used.
*
* @param env The environment to obtain the GitHub instance URLs from.
* @param apiDetails The {@link GitHubApiDetails} to construct the {@link ApiClient} for.
*/
export function getApiClient(
env: ReadOnlyEnv = getEnv(),
apiDetails?: GitHubApiDetails,
) {
return createApiClientWithDetails(apiDetails ?? getApiDetails(env));
}
export function getApiClientWithExternalAuth(
@@ -194,6 +209,12 @@ export function getAuthorizationHeaderFor(
apiDetails: GitHubApiDetails,
url: string,
): string | undefined {
// We don't have to check the `url` if we don't have a token to begin with.
const token = apiDetails.auth?.trim();
if (token === undefined || token === "") {
return undefined;
}
// We only want to provide an authorization header if we are downloading
// from the same GitHub instance the Action is running on.
// This avoids leaking Enterprise tokens to dotcom.
@@ -202,7 +223,7 @@ export function getAuthorizationHeaderFor(
(apiDetails.apiURL && url.startsWith(`${apiDetails.apiURL}/`))
) {
logger.debug(`Providing an authorization token.`);
return `token ${apiDetails.auth}`;
return `token ${token}`;
}
logger.debug(`Not using an authorization token.`);

View File

@@ -10,7 +10,7 @@ import { getGitHubVersion } from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
import { EnvVar } from "./environment";
import { EnvVar, type Env } from "./environment";
import { Language } from "./languages";
import { Logger } from "./logging";
import {
@@ -26,7 +26,6 @@ import {
checkDiskUsage,
checkGitHubVersionInRange,
ConfigurationError,
initializeEnvironment,
wrapError,
} from "./util";
@@ -38,6 +37,7 @@ interface AutobuildStatusReport extends StatusReportBase {
}
async function sendCompletedStatusReport(
env: Env,
config: Config | undefined,
logger: Logger,
startedAt: Date,
@@ -45,7 +45,7 @@ async function sendCompletedStatusReport(
failingLanguage?: string,
cause?: Error,
) {
initializeEnvironment(getActionVersion());
env.initialise(getActionVersion());
const status = getActionsStatus(cause, failingLanguage);
const statusReportBase = await createStatusReportBase(
@@ -129,6 +129,7 @@ async function run({
`We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. ${error.message}`,
);
await sendCompletedStatusReport(
env,
config,
logger,
startedAt,
@@ -141,7 +142,13 @@ async function run({
env.export(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true");
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
await sendCompletedStatusReport(
env,
config,
logger,
startedAt,
languages ?? [],
);
}
/** Defines the `autobuild` Action. */

View File

@@ -21,6 +21,7 @@ import {
import type { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { DocUrl } from "./doc-url";
import { getEnv } from "./environment";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import { ToolsSource } from "./setup-codeql";
@@ -37,14 +38,13 @@ import {
} from "./testing-utils";
import { ToolsDownloadStatusReport } from "./tools-download";
import * as util from "./util";
import { initializeEnvironment } from "./util";
setupTests(test);
let stubConfig: Config;
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
stubConfig = createTestConfig({
languages: [BuiltInLanguage.cpp],
@@ -126,7 +126,7 @@ function mockReleaseApi({
});
}
function mockApiDetails(apiDetails: GitHubApiDetails) {
function mockApiDetails(apiDetails: Required<GitHubApiDetails>) {
// This is a workaround to mock `api.getApiDetails()` since it doesn't seem to be possible to
// mock this directly. The difficulty is that `getApiDetails()` is called locally in
// `api-client.ts`, but `sinon.stub(api, "getApiDetails")` only affects calls to

View File

@@ -4,6 +4,7 @@ import * as path from "path";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as yaml from "js-yaml";
import * as semver from "semver";
import {
CommandInvocationError,
@@ -19,6 +20,7 @@ import type { ActionConfig } from "./config/action-config";
import { appendExtraQueryExclusions, type Config } from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar, getEnv, exportEnvVar } from "./environment";
import { getErrorMessage, isDiskConfigurationError } from "./errors";
import {
CodeQLDefaultVersionInfo,
Feature,
@@ -34,7 +36,9 @@ import { ToolsDownloadStatusReport } from "./tools-download";
import { ToolsFeature, isSupportedToolsFeature } from "./tools-features";
import { shouldEnableIndirectTracing } from "./tracer-config";
import * as util from "./util";
import { BuildMode, CleanupLevel, getErrorMessage } from "./util";
import { BuildMode, CleanupLevel } from "./util";
export { isDiskConfigurationError } from "./errors";
type Options = Array<string | number | boolean>;
@@ -70,6 +74,11 @@ export interface CodeQL {
* Get a string containing the semver version of the CodeQL executable.
*/
getVersion(): Promise<VersionInfo>;
/**
* Determines whether the CLI installation represented by this {@link CodeQL} instance
* has at least the {@link requiredVersion}.
*/
isVersionAtLeast(requiredVersion: string): Promise<boolean>;
/**
* Print version information about CodeQL.
*/
@@ -274,26 +283,6 @@ const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-09-24";
/** The CLI verbosity level to use for extraction in debug mode. */
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
/**
* Decides whether `e` is a disk-related error outside of our control
* that should be classified as a `ConfigurationError`.
*
* @param e The error to check.
* @returns True if the error should be treated as a `ConfigurationError` or false if not.
*/
export function isDiskConfigurationError(e: unknown): boolean {
if (!(e instanceof Error)) {
return false;
}
return (
// out of disk space
e.message.includes("ENOSPC") ||
// access denied
e.message.includes("EACCES")
);
}
/**
* Set up CodeQL CLI access.
*
@@ -431,6 +420,19 @@ export function createStubCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
getVersion: resolveFunction(partialCodeql, "getVersion", async () => ({
version: "1.0.0",
})),
isVersionAtLeast: resolveFunction(
partialCodeql,
"codeQlVersionAtLeast",
async (requiredVersion) => {
return (
!!partialCodeql.getVersion &&
semver.gte(
(await partialCodeql.getVersion()).version,
requiredVersion,
)
);
},
),
printVersion: resolveFunction(partialCodeql, "printVersion"),
supportsFeature: resolveFunction(
partialCodeql,
@@ -534,6 +536,9 @@ async function getCodeQLForCmd(
}
return result;
},
async isVersionAtLeast(requiredVersion: string) {
return semver.gte((await this.getVersion()).version, requiredVersion);
},
async printVersion() {
// Reuse the cached version information rather than invoking the CLI again.
core.info(JSON.stringify(await this.getVersion(), null, 2));
@@ -1028,7 +1033,7 @@ async function getCodeQLForCmd(
// CodeQL object is created.
if (
checkVersion &&
!(await util.codeQlVersionAtLeast(codeql, CODEQL_MINIMUM_VERSION))
!(await codeql.isVersionAtLeast(CODEQL_MINIMUM_VERSION))
) {
throw new util.ConfigurationError(
`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${
@@ -1038,7 +1043,7 @@ async function getCodeQLForCmd(
} else if (
checkVersion &&
process.env[EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING] !== "true" &&
!(await util.codeQlVersionAtLeast(codeql, CODEQL_NEXT_MINIMUM_VERSION))
!(await codeql.isVersionAtLeast(CODEQL_NEXT_MINIMUM_VERSION))
) {
const result = await codeql.getVersion();
core.warning(

View File

@@ -82,7 +82,6 @@ import {
GitHubVersion,
ConfigurationError,
BuildMode,
codeQlVersionAtLeast,
cloneObject,
isDefined,
checkDiskUsage,
@@ -652,10 +651,7 @@ async function runnerHasSufficientMemory(
logger: Logger,
): Promise<boolean> {
if (
await codeQlVersionAtLeast(
codeql,
CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE,
)
await codeql.isVersionAtLeast(CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE)
) {
logger.debug(
`Skipping memory check for overlay analysis because CodeQL version is at least ${CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE}.`,
@@ -899,7 +895,7 @@ async function validateOverlayDatabaseMode(
);
return new Failure(OverlayDisabledReason.IncompatibleBuildMode);
}
if (!(await codeQlVersionAtLeast(codeql, CODEQL_OVERLAY_MINIMUM_VERSION))) {
if (!(await codeql.isVersionAtLeast(CODEQL_OVERLAY_MINIMUM_VERSION))) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because ` +
`the CodeQL CLI is older than ${CODEQL_OVERLAY_MINIMUM_VERSION}. ` +

View File

@@ -1,4 +1,5 @@
import * as fs from "fs";
import fs from "fs";
import path from "path";
import * as github from "@actions/github";
import test from "ava";
@@ -10,7 +11,11 @@ import { GitHubApiDetails } from "./api-client";
import * as apiClient from "./api-client";
import { createStubCodeQL } from "./codeql";
import { Config } from "./config-utils";
import { cleanupAndUploadDatabases } from "./database-upload";
import {
cleanupAndUploadDatabases,
uploadBundledDatabase,
} from "./database-upload";
import { getEnv } from "./environment";
import { Feature } from "./feature-flags";
import * as gitUtils from "./git-utils";
import { BuiltInLanguage } from "./languages";
@@ -23,21 +28,16 @@ import {
getTestEnv,
initAllState,
RecordingLogger,
SAMPLE_DOTCOM_API_DETAILS,
setupActionsVars,
setupTests,
} from "./testing-utils";
import {
CleanupLevel,
GitHubVariant,
HTTPError,
initializeEnvironment,
withTmpDir,
} from "./util";
import { CleanupLevel, GitHubVariant, HTTPError, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
const testRepoName: RepositoryNwo = { owner: "github", repo: "example" };
@@ -550,3 +550,86 @@ test.serial(
});
},
);
test.serial(
"uploadBundledDatabase provides expected request arguments",
async (t) =>
withTmpDir(async (tmpDir) => {
// Write some file that `uploadBundledDatabase` can use for its `fs.createReadStream(bundledDb)` call
// so we don't have to stub that or create a fake `ReadStream`.
const dbPath = path.join(tmpDir, "db");
const content = JSON.stringify({});
fs.writeFileSync(dbPath, content);
// A simple stub for `fetch` that satisfies Octokit when it inspects the response.
const fetchStub = sinon.stub().resolves({
status: 200,
headers: new Headers(),
text: async () => "",
});
// Construct a real `ApiClient`.
const client = apiClient.getApiClient(
getTestEnv(),
SAMPLE_DOTCOM_API_DETAILS,
);
// Then wrap the real `request` method in a stub that calls it with our stubbed `fetch`.
// This ensures that we have a real `ApiClient` with a mostly real `request`, but which
// does not perform any actual requests.
const request = client.request;
sinon.stub(client, "request").callsFake(async (route, options) =>
request(route, {
...options,
request: { ...options?.request, fetch: fetchStub },
}),
);
// Stub `getApiClient` to return our modified `ApiClient`.
const getApiClient = sinon
.stub(apiClient, "getApiClient")
.returns(client);
// Calling `uploadBundledDatabase` shouldn't throw.
const nwo: RepositoryNwo = { owner: "test", repo: "foo" };
const language: BuiltInLanguage = BuiltInLanguage.java;
await t.notThrowsAsync(async () => {
await uploadBundledDatabase(
nwo,
language,
"123",
dbPath,
content.length,
SAMPLE_DOTCOM_API_DETAILS,
);
});
// Check that our stubs were called and that they received the expected arguments.
t.is(getApiClient.callCount, 1);
t.is(fetchStub.callCount, 1);
const args = fetchStub.args[0];
t.is(args.length, 2);
const url = args[0];
t.is(
url,
`https://uploads.github.com/repos/${nwo.owner}/${nwo.repo}/code-scanning/codeql/databases/${language}?name=${language}-database&commit_oid=123`,
);
const options = args[1];
t.like(options, {
// Going via `new Headers` and `Object.fromEntries` ensures we get the same cases
// for the header keys as we are given.
headers: Object.fromEntries(
new Headers({
authorization: `token ${SAMPLE_DOTCOM_API_DETAILS.auth}`,
"Content-Type": "application/zip",
"Content-Length": content.length.toString(),
}).entries(),
),
method: "POST",
} satisfies RequestInit);
}),
);

View File

@@ -12,11 +12,12 @@ import { type CodeQL } from "./codeql";
import { Config } from "./config-utils";
import { Feature } from "./feature-flags";
import * as gitUtils from "./git-utils";
import { parseGitHubUrl } from "./github";
import { Logger, withGroupAsync } from "./logging";
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
import { RepositoryNwo } from "./repository";
import * as util from "./util";
import { asHTTPError, bundleDb, CleanupLevel, parseGitHubUrl } from "./util";
import { asHTTPError, bundleDb, CleanupLevel } from "./util";
/** Information about a database upload. */
export interface DatabaseUploadResult {
@@ -258,7 +259,7 @@ async function recordClearCleanupSizes(
*
* @returns the duration of the upload in milliseconds
*/
async function uploadBundledDatabase(
export async function uploadBundledDatabase(
repositoryNwo: RepositoryNwo,
language: string,
commitOid: string,
@@ -266,7 +267,10 @@ async function uploadBundledDatabase(
bundledDbSize: number,
apiDetails: GitHubApiDetails,
): Promise<number> {
const client = getApiClient();
// Ensure that the `apiDetails` that `getApiClient` uses match the ones we were given here,
// because we use `apiDetails.auth` below which must be used with the same instance that
// `apiDetails` refers to.
const client = getApiClient(util.getEnv(), apiDetails);
const uploadsUrl = new URL(parseGitHubUrl(apiDetails.url));
uploadsUrl.hostname = `uploads.${uploadsUrl.hostname}`;
@@ -292,7 +296,6 @@ async function uploadBundledDatabase(
commit_oid: commitOid,
data: bundledDbReadStream,
headers: {
authorization: `token ${apiDetails.auth}`,
"Content-Type": "application/zip",
"Content-Length": bundledDbSize,
},

View File

@@ -281,8 +281,8 @@ export function getOptionalEnvVar(paramName: string): string | undefined {
* in tests, while clearly signalling in regular code that the consumer of the `ReadOnlyEnv` instance
* will only read from it.
*/
export class ReadOnlyEnv<T extends string | undefined = string | undefined> {
constructor(protected readonly vars: Record<string, T>) {}
export class ReadOnlyEnv {
constructor(protected readonly vars: Record<string, string | undefined>) {}
/** Clones the object while detaching the underlying environment from the original. */
public clone(): this {
@@ -290,7 +290,7 @@ export class ReadOnlyEnv<T extends string | undefined = string | undefined> {
}
/** Gets a copy of the underlying environment. */
public get(): Record<string, T> {
public get(): Record<string, string | undefined> {
return { ...this.vars };
}
@@ -305,7 +305,7 @@ export class ReadOnlyEnv<T extends string | undefined = string | undefined> {
}
/** Gets the entries of the underlying `ProcessEnv`. */
public entries(): Array<[string, T]> {
public entries(): Array<[string, string | undefined]> {
return Object.entries(this.vars);
}
@@ -335,19 +335,17 @@ export class ReadOnlyEnv<T extends string | undefined = string | undefined> {
* Use `ReadOnlyEnv` instead if you only plan to read from the environment.
* This type allows writing to the environment.
*/
export class Env<
T extends string | undefined = string | undefined,
> extends ReadOnlyEnv<T> {
export class Env extends ReadOnlyEnv {
private changed: boolean = false;
/** Sets an environment variable. */
public set(name: string, value: T): void {
public set(name: string, value: string): void {
this.vars[name] = value;
this.changed = true;
}
/** Sets all environment variables given by `vars`. */
public setAll(vars: Record<string, T>): void {
public setAll(vars: Record<string, string>): void {
for (const [key, val] of Object.entries(vars)) {
this.set(key, val);
}
@@ -361,7 +359,7 @@ export class Env<
* @param name The name of the environment variable to set and export.
* @param val The value to set and export for the environment variable.
*/
public export(name: string, val: T): void {
public export(name: string, val: string): void {
// Setting the environment variable for this instance is always OK, including
// in tests, since we use fresh `Env` instances whenever needed. This allows
// tests to pass that rely on that part of the `core.exportVariable` behaviour.
@@ -373,6 +371,18 @@ export class Env<
}
}
/**
* Set some initial environment variables that we can set even without
* knowing what version of CodeQL we're running.
*/
public initialise(version: string) {
this.export(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
this.export(EnvVar.FEATURE_SANDWICH, "false");
this.export(EnvVar.FEATURE_SARIF_COMBINE, "true");
this.export(EnvVar.FEATURE_WILL_UPLOAD, "true");
this.export(EnvVar.VERSION, version);
}
/** Gets a value indicating whether `set` was called at least once. */
public hasChanged(): boolean {
return this.changed;

73
src/errors/index.ts Normal file
View File

@@ -0,0 +1,73 @@
import * as json from "../json";
/**
* If {@link error} is not already an instance of {@link Error}, then this function constructs
* an {@link Error} with a string representation of {@link error} as the message.
*/
export function wrapError(error: unknown): Error {
return error instanceof Error ? error : new Error(String(error));
}
/**
* Returns an appropriate message for the error.
*
* If the error is an {@link Error} instance, this returns the error message without
* an `Error: ` prefix.
*/
export function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
/**
* Decides whether `e` is a disk-related error outside of our control
* that should be classified as a `ConfigurationError`.
*
* @param e The error to check.
* @returns True if the error should be treated as a `ConfigurationError` or false if not.
*/
export function isDiskConfigurationError(e: unknown): boolean {
if (!(e instanceof Error)) {
return false;
}
return (
// out of disk space
e.message.includes("ENOSPC") ||
// access denied
e.message.includes("EACCES")
);
}
/**
* An Error class that indicates an error that occurred due to
* a misconfiguration of the action or the CodeQL CLI.
*/
export class ConfigurationError extends Error {}
/** Minimal representation of errors resulting from HTTP requests. */
export class HTTPError extends Error {
public status: number;
constructor(message: string, status: number) {
super(message);
this.status = status;
}
}
/**
* Tries to convert {@link arg} to a {@link HTTPError}, but returns `undefined` if
* {@link arg} is not like a {@link HTTPError}.
*/
export function asHTTPError(arg: any): HTTPError | undefined {
if (!json.isObject<any>(arg) || !json.isString(arg.message)) {
return undefined;
}
if (Number.isInteger(arg.status)) {
return new HTTPError(arg.message, arg.status as number);
}
// See https://github.com/actions/toolkit/blob/acb230b99a46ed33a3f04a758cd68b47b9a82908/packages/tool-cache/src/tool-cache.ts#L19
if (Number.isInteger(arg.httpStatusCode)) {
return new HTTPError(arg.message, arg.httpStatusCode as number);
}
return undefined;
}

View File

@@ -4,6 +4,7 @@ import * as path from "path";
import test from "ava";
import * as defaults from "./defaults.json";
import { getEnv } from "./environment";
import {
Feature,
featureConfig,
@@ -26,12 +27,12 @@ import {
setupTests,
stubFeatureFlagApiEndpoint,
} from "./testing-utils";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
import { GitHubVariant, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
test.serial(

View File

@@ -74,11 +74,6 @@ export enum Feature {
AllowMergeConfigFiles = "allow_merge_config_files",
/** Controls whether we allow multiple values for the `analysis-kinds` input. */
AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds",
/**
* Controls whether we delete CodeQL bundles that we are not going to use from the toolcache
* before downloading a different bundle, in order to reclaim disk space.
*/
CleanupToolcacheBundles = "cleanup_toolcache_bundles",
CleanupTrapCaches = "cleanup_trap_caches",
/** Whether to allow the `config-file` input to be specified via a repository property. */
ConfigFileRepositoryProperty = "config_file_repository_property",
@@ -221,11 +216,6 @@ export const featureConfig = {
envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS",
minimumVersion: undefined,
},
[Feature.CleanupToolcacheBundles]: {
defaultValue: false,
envVar: "CODEQL_ACTION_CLEANUP_TOOLCACHE_BUNDLES",
minimumVersion: undefined,
},
[Feature.CleanupTrapCaches]: {
defaultValue: false,
envVar: "CODEQL_ACTION_CLEANUP_TRAP_CACHES",
@@ -599,7 +589,7 @@ class OfflineFeatures implements FeatureEnablement {
// Never use this feature if the CLI version explicitly can't support it.
const minimumVersion = config.minimumVersion;
if (codeql && minimumVersion) {
if (!(await util.codeQlVersionAtLeast(codeql, minimumVersion))) {
if (!(await codeql.isVersionAtLeast(minimumVersion))) {
this.logger.debug(
`Feature ${feature} is disabled because the CodeQL CLI version is older than the minimum ` +
`version ${minimumVersion}.`,

View File

@@ -8,7 +8,7 @@ import {
LoggedMessage,
setupTests,
} from "../testing-utils";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "../util";
import { getEnv, GitHubVariant, withTmpDir } from "../util";
import {
assertAllFeaturesHaveDefaultValues,
@@ -18,7 +18,7 @@ import {
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
test("OfflineFeatures makes no API requests", async (t) => {

111
src/github.test.ts Normal file
View File

@@ -0,0 +1,111 @@
import test from "ava";
import { ActionsEnvVars } from "./environment";
import * as github from "./github";
import { getTestEnv, setupTests } from "./testing-utils";
setupTests(test);
test("parseGitHubUrl", (t) => {
t.deepEqual(github.parseGitHubUrl("github.com"), "https://github.com");
t.deepEqual(
github.parseGitHubUrl("https://github.com"),
"https://github.com",
);
t.deepEqual(
github.parseGitHubUrl("https://api.github.com"),
"https://github.com",
);
t.deepEqual(
github.parseGitHubUrl("https://github.com/foo/bar"),
"https://github.com",
);
t.deepEqual(
github.parseGitHubUrl("github.example.com"),
"https://github.example.com/",
);
t.deepEqual(
github.parseGitHubUrl("https://github.example.com"),
"https://github.example.com/",
);
t.deepEqual(
github.parseGitHubUrl("https://api.github.example.com"),
"https://github.example.com/",
);
t.deepEqual(
github.parseGitHubUrl("https://github.example.com/api/v3"),
"https://github.example.com/",
);
t.deepEqual(
github.parseGitHubUrl("https://github.example.com:1234"),
"https://github.example.com:1234/",
);
t.deepEqual(
github.parseGitHubUrl("https://api.github.example.com:1234"),
"https://github.example.com:1234/",
);
t.deepEqual(
github.parseGitHubUrl("https://github.example.com:1234/api/v3"),
"https://github.example.com:1234/",
);
t.deepEqual(
github.parseGitHubUrl("https://github.example.com/base/path"),
"https://github.example.com/base/path/",
);
t.deepEqual(
github.parseGitHubUrl("https://github.example.com/base/path/api/v3"),
"https://github.example.com/base/path/",
);
t.throws(() => github.parseGitHubUrl(""), {
message: '"" is not a valid URL',
});
t.throws(() => github.parseGitHubUrl("ssh://github.com"), {
message: '"ssh://github.com" is not a http or https URL',
});
t.throws(() => github.parseGitHubUrl("http:///::::433"), {
message: '"http:///::::433" is not a valid URL',
});
});
test("getCurrentInstance throws if required env vars aren't set", (t) => {
t.throws(() => github.getCurrentInstance(getTestEnv()));
t.throws(() => {
const env = getTestEnv({
[ActionsEnvVars.GITHUB_SERVER_URL]: "https://localhost",
});
github.getCurrentInstance(env);
});
t.throws(() => {
const env = getTestEnv({
[ActionsEnvVars.GITHUB_API_URL]: "https://api.localhost",
});
github.getCurrentInstance(env);
});
});
test("getCurrentInstance constructs instance from environment", (t) => {
const url = "https://localhost";
const apiUrl = "https://api.localhost";
const env = getTestEnv({
[ActionsEnvVars.GITHUB_SERVER_URL]: url,
[ActionsEnvVars.GITHUB_API_URL]: apiUrl,
});
const instance = github.getCurrentInstance(env);
t.is(instance.url, url);
t.is(instance.apiURL, apiUrl);
});
test("getCurrentInstance constructs dotcom instance with dotcom env vars", (t) => {
const env = getTestEnv({
[ActionsEnvVars.GITHUB_SERVER_URL]: github.GITHUB_DOTCOM_URL,
[ActionsEnvVars.GITHUB_API_URL]: github.GITHUB_DOTCOM_API_URL,
});
const instance = github.getCurrentInstance(env);
const dotcomInstance = github.getDotcomInstance();
t.deepEqual(dotcomInstance, instance);
});

124
src/github.ts Normal file
View File

@@ -0,0 +1,124 @@
import { ActionsEnvVars, type ReadOnlyEnv } from "./environment";
import { ConfigurationError } from "./errors";
export enum GitHubVariant {
/** [GitHub.com](https://github.com) */
DOTCOM = "GitHub.com",
/** [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server) */
GHES = "GitHub Enterprise Server",
/** [GitHub Enterprise Cloud with data residency](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency) */
GHEC_DR = "GitHub Enterprise Cloud with data residency",
}
export type GitHubVersion =
| { type: GitHubVariant.DOTCOM }
| { type: GitHubVariant.GHEC_DR }
| { type: GitHubVariant.GHES; version: string };
/** A string representing a GitHub instance address. */
export type InstanceURL = string;
/**
* The URL for github.com.
*/
export const GITHUB_DOTCOM_URL: InstanceURL = "https://github.com";
/** A string representing a GitHub API instance address. */
export type InstanceApiURL = string;
/**
* The URL for api.github.com.
*/
export const GITHUB_DOTCOM_API_URL: InstanceApiURL = "https://api.github.com";
/**
* Represents information about a GitHub instance, how to authenticate to it,
* and how to authenticate to other repos on it.
*/
export type GitHubApiCombinedDetails = GitHubApiDetails &
GitHubApiExternalRepoDetails;
/** Represents information about a GitHub instance. */
export type GitHubInstance = {
/** The base URL of the GitHub instance, such as `https://github.com`. */
url: string;
/**
* The API base URL of the GitHub instance, such as `https://api.github.com`.
* Typically this is the `api.` subdomain of the `url` domain.
*/
apiURL: string | undefined;
};
/** Represents a {@link GitHubInstance} with a token for accessing the API. */
export type GitHubApiDetails = GitHubInstance & {
/** The token to use for accessing the API of the GitHub instance. */
auth?: string;
};
/**
* Represents information for accessing a repository on the same GitHub instance as the Action
* is currently running on.
*/
export type GitHubApiExternalRepoDetails = {
/** An optional token for accessing repo(s) on the same GitHub instance. */
externalRepoAuth?: string;
};
/**
* Parses user input of a github.com or GHES URL to a canonical form.
* Removes any API prefix or suffix if one is present.
*/
export function parseGitHubUrl(inputUrl: string): string {
const originalUrl = inputUrl;
if (inputUrl.indexOf("://") === -1) {
inputUrl = `https://${inputUrl}`;
}
if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) {
throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`);
}
let url: URL;
try {
url = new URL(inputUrl);
} catch {
throw new ConfigurationError(`"${originalUrl}" is not a valid URL`);
}
// If we detect this is trying to be to github.com
// then return with a fixed canonical URL.
if (url.hostname === "github.com" || url.hostname === "api.github.com") {
return GITHUB_DOTCOM_URL;
}
// Remove the API prefix if it's present
if (url.pathname.indexOf("/api/v3") !== -1) {
url.pathname = url.pathname.substring(0, url.pathname.indexOf("/api/v3"));
}
// Also consider subdomain isolation on GHES
if (url.hostname.startsWith("api.")) {
url.hostname = url.hostname.substring(4);
}
// Normalise path to having a trailing slash for consistency
if (!url.pathname.endsWith("/")) {
url.pathname = `${url.pathname}/`;
}
return url.toString();
}
/** Retrieves a {@link GitHubInstance} describing the instance we are running on. */
export function getCurrentInstance(env: ReadOnlyEnv): GitHubInstance {
return {
url: env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: env.getRequired(ActionsEnvVars.GITHUB_API_URL),
};
}
/** Retrieves a {@link GitHubInstance} describing the GitHub.com instance. */
export function getDotcomInstance(): GitHubInstance {
return {
url: GITHUB_DOTCOM_URL,
apiURL: GITHUB_DOTCOM_API_URL,
};
}

View File

@@ -50,6 +50,7 @@ import {
loadRepositoryProperties,
RepositoryPropertyName,
} from "./feature-flags/properties";
import * as github from "./github";
import {
checkInstallPython311,
checkPacksForOverlayCompatibility,
@@ -87,12 +88,10 @@ import {
checkDiskUsage,
checkForTimeout,
checkGitHubVersionInRange,
codeQlVersionAtLeast,
DEFAULT_DEBUG_ARTIFACT_NAME,
DEFAULT_DEBUG_DATABASE_NAME,
getCodeQLMemoryLimit,
getThreadsFlagValue,
initializeEnvironment,
ConfigurationError,
wrapError,
checkActionVersion,
@@ -225,7 +224,7 @@ async function run(
let toolsVersion: string;
try {
initializeEnvironment(getActionVersion());
actionState.env.initialise(getActionVersion());
// Make inputs accessible in the `post` step.
persistInputs();
@@ -233,8 +232,7 @@ async function run(
apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: actionState.env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: actionState.env.getRequired(ActionsEnvVars.GITHUB_API_URL),
...github.getCurrentInstance(actionState.env),
};
const gitHubVersion = await getGitHubVersion();
@@ -686,7 +684,7 @@ async function run(
`${EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS} is already set to '${process.env[EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS]}', so the Action will not override it.`,
);
} else if (
(await codeQlVersionAtLeast(codeql, CODEQL_VERSION_JAR_MINIMIZATION)) &&
(await codeql.isVersionAtLeast(CODEQL_VERSION_JAR_MINIMIZATION)) &&
config.dependencyCachingEnabled &&
config.buildMode === BuildMode.None &&
config.languages.includes(BuiltInLanguage.java)

View File

@@ -17,9 +17,10 @@ import { getGitHubVersion } from "./api-client";
import { CodeQL } from "./codeql";
import { ComputedInput, getToolsInput } from "./config/inputs";
import { getRawLanguagesNoAutodetect } from "./config-utils";
import { ActionsEnvVars, EnvVar } from "./environment";
import { EnvVar } from "./environment";
import { initFeatures } from "./feature-flags";
import { loadRepositoryProperties } from "./feature-flags/properties";
import * as github from "./github";
import { initCodeQL } from "./init";
import { Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
@@ -37,7 +38,6 @@ import {
checkDiskUsage,
checkForTimeout,
checkGitHubVersionInRange,
initializeEnvironment,
ConfigurationError,
wrapError,
checkActionVersion,
@@ -107,13 +107,12 @@ async function run(
let toolsVersion: string;
try {
initializeEnvironment(getActionVersion());
actionState.env.initialise(getActionVersion());
const apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: actionState.env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: actionState.env.getRequired(ActionsEnvVars.GITHUB_API_URL),
...github.getCurrentInstance(actionState.env),
};
const gitHubVersion = await getGitHubVersion();

View File

@@ -11,7 +11,13 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import * as diagnostics from "./diagnostics";
import { ActionsEnvVars, Env, EnvVar, ReadOnlyEnv } from "./environment";
import {
ActionsEnvVars,
Env,
EnvVar,
getEnv,
ReadOnlyEnv,
} from "./environment";
import { Feature } from "./feature-flags";
import { BuiltInLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
@@ -37,18 +43,12 @@ import {
setupTests,
} from "./testing-utils";
import * as toolsDownload from "./tools-download";
import {
getErrorMessage,
GitHubVariant,
HTTPError,
initializeEnvironment,
withTmpDir,
} from "./util";
import { getErrorMessage, GitHubVariant, HTTPError, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
function stubDownloadAndExtract() {
@@ -596,7 +596,7 @@ test.serial(
undefined, // rawLanguages
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.CleanupToolcacheBundles]),
createFeatures([]),
getRunnerLogger(true),
);
@@ -1375,10 +1375,7 @@ for (const bundle of ["per-language", "combined", "fallback"] as const) {
};
});
const addDiagnostic = sinon.stub(diagnostics, "addNoLanguageDiagnostic");
const features = createFeatures([
Feature.PerLanguageBundles,
Feature.CleanupToolcacheBundles,
]);
const features = createFeatures([Feature.PerLanguageBundles]);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -1826,11 +1823,15 @@ function createToolcacheEntry(
*/
async function runDownloadCodeQL(
toolcacheRoot: string,
features: Feature[],
bundleVersion: string | undefined,
env = getTestEnv({
[ActionsEnvVars.RUNNER_TOOL_CACHE]: toolcacheRoot,
}),
{
env = getTestEnv({
[ActionsEnvVars.RUNNER_ENVIRONMENT]: "github-hosted",
[ActionsEnvVars.RUNNER_TOOL_CACHE]: toolcacheRoot,
}),
}: {
env?: Env;
} = {},
): Promise<{
codeqlFolder: string;
cleanupDiagnostic: toolsDownload.ToolcacheCleanupResult | undefined;
@@ -1841,7 +1842,6 @@ async function runDownloadCodeQL(
const { codeqlFolder } = await setupCodeql.downloadCodeQLBundle(
initAllState({
env,
features: createFeatures(features),
}),
{
bundle: {
@@ -1879,11 +1879,9 @@ async function runDownloadCodeQL(
async function testToolcacheCleanup(
t: ExecutionContext<unknown>,
{
features,
runnerEnvironment,
setUp,
}: {
features: Feature[];
runnerEnvironment: string | undefined;
setUp?: (env: Env) => void;
},
@@ -1895,12 +1893,8 @@ async function testToolcacheCleanup(
) {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
if (runnerEnvironment === undefined) {
delete process.env[ActionsEnvVars.RUNNER_ENVIRONMENT];
} else {
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = runnerEnvironment;
}
const env = getTestEnv({
[ActionsEnvVars.RUNNER_ENVIRONMENT]: runnerEnvironment,
[ActionsEnvVars.RUNNER_TOOL_CACHE]: tmpDir,
});
setUp?.(env);
@@ -1920,9 +1914,8 @@ async function testToolcacheCleanup(
const { cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
features,
CLEANUP_BUNDLE_VERSION,
env,
{ env },
);
t.true(
@@ -1934,28 +1927,12 @@ async function testToolcacheCleanup(
});
}
test.serial(
"downloadCodeQLBundle does not clean up the toolcache when the feature flag is disabled",
async (t) => {
await testToolcacheCleanup(
t,
{ features: [], runnerEnvironment: "github-hosted" },
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
t.true(fs.existsSync(staleDirectory));
t.true(fs.existsSync(destinationDirectory));
t.is(cleanupDiagnostic, undefined);
},
);
},
);
test.serial(
"downloadCodeQLBundle does not clean up the toolcache when the runner is not GitHub-hosted",
async (t) => {
await testToolcacheCleanup(
t,
{
features: [Feature.CleanupToolcacheBundles],
runnerEnvironment: "self-hosted",
},
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
@@ -1975,7 +1952,6 @@ test.serial(
await testToolcacheCleanup(
t,
{
features: [Feature.CleanupToolcacheBundles],
runnerEnvironment: undefined,
},
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
@@ -1988,12 +1964,11 @@ test.serial(
);
test.serial(
"downloadCodeQLBundle deletes other CodeQL bundles from the toolcache when enabled on a GitHub-hosted runner",
"downloadCodeQLBundle deletes other CodeQL bundles from the toolcache on a GitHub-hosted runner",
async (t) => {
await testToolcacheCleanup(
t,
{
features: [Feature.CleanupToolcacheBundles],
runnerEnvironment: "github-hosted",
},
({ cleanupDiagnostic, destinationDirectory, staleDirectory }) => {
@@ -2027,7 +2002,6 @@ test.serial(
const { cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
);
@@ -2057,7 +2031,6 @@ test.serial(
const { cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
);
@@ -2189,7 +2162,6 @@ test.serial(
const { cleanupDiagnostic } = await runDownloadCodeQL(
toolcacheRoot,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
);
@@ -2212,7 +2184,6 @@ test.serial(
await testToolcacheCleanup(
t,
{
features: [Feature.CleanupToolcacheBundles],
runnerEnvironment: "github-hosted",
setUp: (env) => {
env.set(EnvVar.HAS_SET_UP_CODEQL, "true");
@@ -2242,9 +2213,13 @@ test.serial(
);
const { codeqlFolder, cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
getTestEnv({ [EnvVar.HAS_SET_UP_CODEQL]: "true" }),
{
env: getTestEnv({
[ActionsEnvVars.RUNNER_ENVIRONMENT]: "github-hosted",
[EnvVar.HAS_SET_UP_CODEQL]: "true",
}),
},
);
t.true(fs.existsSync(staleDirectory));
@@ -2312,7 +2287,7 @@ test.serial(
undefined, // rawLanguages
undefined, // otherLanguagePacksReason
false, // useOverlayAwareDefaultCliVersion
createFeatures([Feature.CleanupToolcacheBundles]),
createFeatures([]),
getRunnerLogger(true),
),
{ is: error },
@@ -2347,7 +2322,6 @@ test.serial(
const { codeqlFolder, cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
[Feature.CleanupToolcacheBundles],
undefined, // bundleVersion
);
@@ -2381,7 +2355,6 @@ test.serial(
const { cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
);
@@ -2417,7 +2390,6 @@ test.serial(
const { cleanupDiagnostic } = await runDownloadCodeQL(
tmpDir,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
);
@@ -2467,7 +2439,6 @@ test.serial(
const { cleanupDiagnostic } = await runDownloadCodeQL(
toolcacheRoot,
[Feature.CleanupToolcacheBundles],
CLEANUP_BUNDLE_VERSION,
);

View File

@@ -839,7 +839,7 @@ function getToolcacheDestination(
}
/**
* Reclaims disk space by deleting the CodeQL tools from the toolcache, if enabled.
* Reclaims disk space by deleting the CodeQL tools from the toolcache on GitHub-hosted runners.
*
* On GitHub-hosted runners the toolcache shares a filesystem with the workspace, so tools left in
* the toolcache take up space that the analysis could use instead. This holds wherever we extract
@@ -847,9 +847,8 @@ function getToolcacheDestination(
*/
async function tryDeleteToolcacheBundles({
env,
features,
logger,
}: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>): Promise<void> {
}: ActionState<["Logger", "ReadOnlyEnv"]>): Promise<void> {
// A step that has already set up CodeQL may hand out a path into the toolcache that a later step
// runs, so only the first step to set it up can know that nothing else relies on the toolcache.
if (env.getOptional(EnvVar.HAS_SET_UP_CODEQL) !== undefined) {
@@ -860,11 +859,7 @@ async function tryDeleteToolcacheBundles({
return;
}
if (
!isGitHubHostedRunner() ||
!isToolcacheOnWorkspaceFilesystem(logger) ||
!(await features.getValue(Feature.CleanupToolcacheBundles))
) {
if (!isGitHubHostedRunner(env) || !isToolcacheOnWorkspaceFilesystem(logger)) {
return;
}
@@ -983,7 +978,7 @@ export async function setupCodeQLBundle(
break;
case "download": {
const result = await downloadCodeQLBundle(
{ env: getEnv(), features, logger },
{ env: getEnv(), logger },
source,
apiDetails,
zstdAvailability.version,
@@ -1019,7 +1014,7 @@ export async function setupCodeQLBundle(
* @returns The extraction directory and download timings.
*/
export async function downloadCodeQLBundle(
action: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>,
action: ActionState<["Logger", "ReadOnlyEnv"]>,
source: CodeQLDownloadSource,
apiDetails: api.GitHubApiDetails,
tarVersion: tar.TarVersion | undefined,

View File

@@ -6,7 +6,6 @@ import * as sinon from "sinon";
import * as actionsUtil from "../actions-util";
import { ActionsEnvVars } from "../environment";
import { callee, getTestEnv, setupTests } from "../testing-utils";
import { initializeEnvironment } from "../util";
import { getCodeQLActionRepository } from "./repository";
@@ -16,7 +15,7 @@ test.serial("getCodeQLActionRepository", async (t) => {
const initialEnv = getTestEnv({
[ActionsEnvVars.RUNNER_TEMP]: path.dirname(__dirname),
});
initializeEnvironment("1.2.3", initialEnv);
initialEnv.initialise("1.2.3");
const target = callee(getCodeQLActionRepository)
.withArgs()

View File

@@ -2,12 +2,12 @@ import { ActionState } from "../action-common";
import { isRunningLocalAction } from "../actions-util";
import * as api from "../api-client";
import { ActionsEnvVars } from "../environment";
import { GITHUB_DOTCOM_URL, type InstanceURL } from "../github";
import {
isRepositoryNwoStr,
parseRepositoryNwo,
type RepositoryNwoStr,
} from "../repository";
import { GITHUB_DOTCOM_URL, type InstanceURL } from "../util";
/** The NWO of the standard CodeQL Action repository. */
export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";

View File

@@ -31,6 +31,7 @@ import {
exportEnvVar,
} from "./environment";
import { getRef } from "./git-utils";
import { GITHUB_DOTCOM_URL } from "./github";
import * as json from "./json";
import type { Logger } from "./logging";
import type { OverlayBaseDatabaseDownloadStats } from "./overlay/caching";
@@ -41,7 +42,6 @@ import {
ConfigurationError,
getRequiredEnvParam,
isInTestMode,
GITHUB_DOTCOM_URL,
DiskUsage,
assertNever,
BuildMode,

View File

@@ -10,6 +10,7 @@ import { https } from "follow-redirects";
import nock from "nock";
import * as sinon from "sinon";
import * as errors from "./errors";
import { getRunnerLogger } from "./logging";
import * as tar from "./tar";
import { setupTests } from "./testing-utils";
@@ -164,7 +165,7 @@ test.serial(
test.serial(
"downloadAndExtract handles an unknown status as a non-HTTP error",
async (t) => {
const asHTTPError = sinon.spy(util, "asHTTPError");
const asHTTPError = sinon.spy(errors, "asHTTPError");
await util.withTmpDir(async (tmpDir) => {
sinon.stub(process, "platform").value("linux");
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");

View File

@@ -10,17 +10,18 @@ import * as analyses from "./analyses";
import { AnalysisKind, CodeQuality, CodeScanning } from "./analyses";
import * as api from "./api-client";
import * as diffUtils from "./diff-informed-analysis-utils";
import { getEnv } from "./environment";
import { getRunnerLogger, Logger } from "./logging";
import * as sarif from "./sarif";
import { setupTests } from "./testing-utils";
import * as uploadLib from "./upload-lib";
import { UploadPayload } from "./upload-lib/types";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
import { GitHubVariant, withTmpDir } from "./util";
setupTests(test);
test.beforeEach(() => {
initializeEnvironment("1.2.3");
getEnv().initialise("1.2.3");
});
test.serial("validateSarifFileSchema - valid", (t) => {

View File

@@ -18,6 +18,7 @@ import { ActionsEnvVars, EnvVar, exportEnvVar } from "./environment";
import { FeatureEnablement } from "./feature-flags";
import * as fingerprints from "./fingerprints";
import * as gitUtils from "./git-utils";
import * as github from "./github";
import { initCodeQL } from "./init";
import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
@@ -33,7 +34,6 @@ import * as util from "./util";
import {
ConfigurationError,
getErrorMessage,
getRequiredEnvParam,
GitHubVariant,
GitHubVersion,
satisfiesGHESVersion,
@@ -104,6 +104,8 @@ async function combineSarifFilesUsingCLI(
features: FeatureEnablement,
logger: Logger,
): Promise<Partial<sarif.Log>> {
const env = util.getEnv();
logger.info("Combining SARIF files using the CodeQL CLI");
const sarifObjects = sarifFiles.map(sarif.readSarifFile);
@@ -152,8 +154,7 @@ async function combineSarifFilesUsingCLI(
externalRepoAuth: actionsUtil.getOptionalInput(
"external-repository-token",
),
url: getRequiredEnvParam("GITHUB_SERVER_URL"),
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
...github.getCurrentInstance(env),
};
const codeQLDefaultVersionInfo =

View File

@@ -23,7 +23,6 @@ import {
ConfigurationError,
checkActionVersion,
checkDiskUsage,
initializeEnvironment,
shouldSkipSarifUpload,
wrapError,
} from "./util";
@@ -54,11 +53,13 @@ async function sendSuccessStatusReport(
}
}
async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
async function run(action: ActionState<["Base", "Env", "Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
const logger = action.logger;
try {
initializeEnvironment(getActionVersion());
action.env.initialise(getActionVersion());
const gitHubVersion = await getGitHubVersion();
checkActionVersion(getActionVersion(), gitHubVersion);
@@ -77,7 +78,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
const startingStatusReportBase = await createStatusReportBase(
ActionName.UploadSarif,
"starting",
startedAt,
action.startedAt,
undefined,
await checkDiskUsage(logger),
logger,
@@ -131,7 +132,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
// so we can't wait for that here.
}
await sendSuccessStatusReport(
startedAt,
action.startedAt,
codeScanningResult?.statusReport || {},
logger,
);
@@ -147,7 +148,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
const errorStatusReportBase = await createStatusReportBase(
ActionName.UploadSarif,
getActionsStatus(error),
startedAt,
action.startedAt,
undefined,
await checkDiskUsage(logger),
logger,

View File

@@ -164,66 +164,6 @@ test.serial("getExtraOptionsEnvParam() fails on invalid JSON", (t) => {
process.env.CODEQL_ACTION_EXTRA_OPTIONS = origExtraOptions;
});
test("parseGitHubUrl", (t) => {
t.deepEqual(util.parseGitHubUrl("github.com"), "https://github.com");
t.deepEqual(util.parseGitHubUrl("https://github.com"), "https://github.com");
t.deepEqual(
util.parseGitHubUrl("https://api.github.com"),
"https://github.com",
);
t.deepEqual(
util.parseGitHubUrl("https://github.com/foo/bar"),
"https://github.com",
);
t.deepEqual(
util.parseGitHubUrl("github.example.com"),
"https://github.example.com/",
);
t.deepEqual(
util.parseGitHubUrl("https://github.example.com"),
"https://github.example.com/",
);
t.deepEqual(
util.parseGitHubUrl("https://api.github.example.com"),
"https://github.example.com/",
);
t.deepEqual(
util.parseGitHubUrl("https://github.example.com/api/v3"),
"https://github.example.com/",
);
t.deepEqual(
util.parseGitHubUrl("https://github.example.com:1234"),
"https://github.example.com:1234/",
);
t.deepEqual(
util.parseGitHubUrl("https://api.github.example.com:1234"),
"https://github.example.com:1234/",
);
t.deepEqual(
util.parseGitHubUrl("https://github.example.com:1234/api/v3"),
"https://github.example.com:1234/",
);
t.deepEqual(
util.parseGitHubUrl("https://github.example.com/base/path"),
"https://github.example.com/base/path/",
);
t.deepEqual(
util.parseGitHubUrl("https://github.example.com/base/path/api/v3"),
"https://github.example.com/base/path/",
);
t.throws(() => util.parseGitHubUrl(""), {
message: '"" is not a valid URL',
});
t.throws(() => util.parseGitHubUrl("ssh://github.com"), {
message: '"ssh://github.com" is not a http or https URL',
});
t.throws(() => util.parseGitHubUrl("http:///::::433"), {
message: '"http:///::::433" is not a valid URL',
});
});
test("allowed API versions", async (t) => {
t.is(util.apiVersionInRange("1.33.0", "1.33", "2.0"), undefined);
t.is(util.apiVersionInRange("1.33.1", "1.33", "2.0"), undefined);

View File

@@ -20,20 +20,21 @@ import {
getRequiredEnvParam,
isInTestMode,
exportEnvVar,
getEnv,
type Env,
} from "./environment";
import * as json from "./json";
import { Language } from "./languages";
import { Logger } from "./logging";
import { ConfigurationError, getErrorMessage, wrapError } from "./errors";
import { GitHubVariant, type GitHubVersion } from "./github";
import type { Language } from "./languages";
import type { Logger } from "./logging";
// Re-export for backwards compatibility to avoid updating a lot of imports elsewhere.
export * from "./errors";
export {
getRequiredEnvParam,
getOptionalEnvVar,
getEnv,
isInTestMode,
} from "./environment";
export { GitHubVariant, type GitHubVersion } from "./github";
/**
* The name of the file containing the base database OIDs, as stored in the
@@ -47,14 +48,6 @@ const BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json";
*/
const BROKEN_VERSIONS = ["0.0.0-20211207"];
/** A string representing a GitHub instance. */
export type InstanceURL = string;
/**
* The URL for github.com.
*/
export const GITHUB_DOTCOM_URL: InstanceURL = "https://github.com";
/**
* Default name of the debugging artifact.
*/
@@ -470,68 +463,11 @@ export function getGeneratedSuitePath(config: Config, language: Language) {
);
}
/**
* Parses user input of a github.com or GHES URL to a canonical form.
* Removes any API prefix or suffix if one is present.
*/
export function parseGitHubUrl(inputUrl: string): string {
const originalUrl = inputUrl;
if (inputUrl.indexOf("://") === -1) {
inputUrl = `https://${inputUrl}`;
}
if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) {
throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`);
}
let url: URL;
try {
url = new URL(inputUrl);
} catch {
throw new ConfigurationError(`"${originalUrl}" is not a valid URL`);
}
// If we detect this is trying to be to github.com
// then return with a fixed canonical URL.
if (url.hostname === "github.com" || url.hostname === "api.github.com") {
return GITHUB_DOTCOM_URL;
}
// Remove the API prefix if it's present
if (url.pathname.indexOf("/api/v3") !== -1) {
url.pathname = url.pathname.substring(0, url.pathname.indexOf("/api/v3"));
}
// Also consider subdomain isolation on GHES
if (url.hostname.startsWith("api.")) {
url.hostname = url.hostname.substring(4);
}
// Normalise path to having a trailing slash for consistency
if (!url.pathname.endsWith("/")) {
url.pathname = `${url.pathname}/`;
}
return url.toString();
}
const CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR =
"CODEQL_ACTION_WARNED_ABOUT_VERSION";
let hasBeenWarnedAboutVersion = false;
export enum GitHubVariant {
/** [GitHub.com](https://github.com) */
DOTCOM = "GitHub.com",
/** [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server) */
GHES = "GitHub Enterprise Server",
/** [GitHub Enterprise Cloud with data residency](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency) */
GHEC_DR = "GitHub Enterprise Cloud with data residency",
}
export type GitHubVersion =
| { type: GitHubVariant.DOTCOM }
| { type: GitHubVariant.GHEC_DR }
| { type: GitHubVariant.GHES; version: string };
export function checkGitHubVersionInRange(
version: GitHubVersion,
logger: Logger,
@@ -600,54 +536,6 @@ export function assertNever(value: never): never {
throw new ExhaustivityCheckingError(value);
}
/**
* Set some initial environment variables that we can set even without
* knowing what version of CodeQL we're running.
*/
export function initializeEnvironment(version: string, env: Env = getEnv()) {
env.export(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
env.export(EnvVar.FEATURE_SANDWICH, "false");
env.export(EnvVar.FEATURE_SARIF_COMBINE, "true");
env.export(EnvVar.FEATURE_WILL_UPLOAD, "true");
env.export(EnvVar.VERSION, version);
}
export class HTTPError extends Error {
public status: number;
constructor(message: string, status: number) {
super(message);
this.status = status;
}
}
/**
* An Error class that indicates an error that occurred due to
* a misconfiguration of the action or the CodeQL CLI.
*/
export class ConfigurationError extends Error {}
export function asHTTPError(arg: any): HTTPError | undefined {
if (!json.isObject<any>(arg) || !json.isString(arg.message)) {
return undefined;
}
if (Number.isInteger(arg.status)) {
return new HTTPError(arg.message, arg.status as number);
}
// See https://github.com/actions/toolkit/blob/acb230b99a46ed33a3f04a758cd68b47b9a82908/packages/tool-cache/src/tool-cache.ts#L19
if (Number.isInteger(arg.httpStatusCode)) {
return new HTTPError(arg.message, arg.httpStatusCode as number);
}
return undefined;
}
export async function codeQlVersionAtLeast(
codeql: CodeQL,
requiredVersion: string,
): Promise<boolean> {
return semver.gte((await codeql.getVersion()).version, requiredVersion);
}
export function getBaseDatabaseOidsFilePath(
config: ActionConfig<"dbLocation">,
): string {
@@ -915,20 +803,6 @@ export function parseMatrixInput(
}
}
export function wrapError(error: unknown): Error {
return error instanceof Error ? error : new Error(String(error));
}
/**
* Returns an appropriate message for the error.
*
* If the error is an `Error` instance, this returns the error message without
* an `Error: ` prefix.
*/
export function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
export function prettyPrintPack(pack: Pack) {
return `${pack.name}${pack.version ? `@${pack.version}` : ""}${
pack.path ? `:${pack.path}` : ""

View File

@@ -2,6 +2,16 @@
Change-notes are Markdown files used to document user-facing changes. When making a change that affects users, create a Markdown file here that describes the change. During the next release, the change-note files in `unreleased-change-notes/` will automatically be added to `CHANGELOG.md`.
### What do document
Some guidelines to help you decide whether a change needs a change note:
- New functionality that is gated behind a feature flag should be documented when it is added, highlighting that the feature will only be rolled out later. Ideally, this should also include an estimate for when the feature is expected to become available.
- When a feature flag is removed and the gated functionality becomes the default behaviour, this does not need to be documented.
- Changes that may require users to make changes to their workflows must be documented.
If in doubt, see `CHANGELOG.md` for what we have included previously.
### Change-note file format
Change-note files must follow a certain format so that they can be automatically validated and processed. Failure to follow the format will result in a failed PR check.