mirror of
https://github.com/github/codeql-action.git
synced 2026-10-20 17:51:32 +00:00
Compare commits
48 Commits
v4
...
mbg/all-ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a6a2ca1d1 | ||
|
|
6c74506460 | ||
|
|
84bd5a897c | ||
|
|
938d1c591f | ||
|
|
9d9422aff0 | ||
|
|
2a0f9f0e08 | ||
|
|
dca467bb23 | ||
|
|
1907698e19 | ||
|
|
a4c1eb2b5f | ||
|
|
c5913260b1 | ||
|
|
3db7caf901 | ||
|
|
83e1837c6d | ||
|
|
5ecf3b32e9 | ||
|
|
e461913b38 | ||
|
|
6992155901 | ||
|
|
f3c93e3106 | ||
|
|
2441e6e56e | ||
|
|
0d2baf6822 | ||
|
|
ae098fa9fe | ||
|
|
0a458f10ac | ||
|
|
4ebc48bf25 | ||
|
|
1ec6b03cf4 | ||
|
|
7a6e2f798f | ||
|
|
46d3daa879 | ||
|
|
2edace40f1 | ||
|
|
e5b217aeb5 | ||
|
|
3ec43abae3 | ||
|
|
d63cb97549 | ||
|
|
3afe9c01db | ||
|
|
8844fb33e0 | ||
|
|
c1641847bb | ||
|
|
047c751d92 | ||
|
|
f6e8dce502 | ||
|
|
a08b587b61 | ||
|
|
bfe264b559 | ||
|
|
f9b4185b99 | ||
|
|
66c96fdc35 | ||
|
|
1d30a842cd | ||
|
|
1b0a617641 | ||
|
|
96501a33ac | ||
|
|
a2438139ce | ||
|
|
e0625309c3 | ||
|
|
61bfcffab9 | ||
|
|
102e46e9d1 | ||
|
|
0735d7910f | ||
|
|
a6ff2ad52a | ||
|
|
2883b5b5db | ||
|
|
ba79a4f0b9 |
17
.github/copilot-instructions.md
vendored
17
.github/copilot-instructions.md
vendored
@@ -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:
|
||||
|
||||
4
.github/workflows/__analysis-kinds.yml
generated
vendored
4
.github/workflows/__analysis-kinds.yml
generated
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/__config-export.yml
generated
vendored
2
.github/workflows/__config-export.yml
generated
vendored
@@ -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'
|
||||
|
||||
2
.github/workflows/__config-input.yml
generated
vendored
2
.github/workflows/__config-input.yml
generated
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/__diagnostics-export.yml
generated
vendored
2
.github/workflows/__diagnostics-export.yml
generated
vendored
@@ -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'
|
||||
|
||||
2
.github/workflows/__export-file-baseline-information.yml
generated
vendored
2
.github/workflows/__export-file-baseline-information.yml
generated
vendored
@@ -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'
|
||||
|
||||
2
.github/workflows/__job-run-uuid-sarif.yml
generated
vendored
2
.github/workflows/__job-run-uuid-sarif.yml
generated
vendored
@@ -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'
|
||||
|
||||
1
.github/workflows/__multi-language-autodetect.yml
generated
vendored
1
.github/workflows/__multi-language-autodetect.yml
generated
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/__packaging-codescanning-config-inputs-js.yml
generated
vendored
2
.github/workflows/__packaging-codescanning-config-inputs-js.yml
generated
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/__packaging-config-inputs-js.yml
generated
vendored
2
.github/workflows/__packaging-config-inputs-js.yml
generated
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/__packaging-config-js.yml
generated
vendored
2
.github/workflows/__packaging-config-js.yml
generated
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/__packaging-inputs-js.yml
generated
vendored
2
.github/workflows/__packaging-inputs-js.yml
generated
vendored
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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"
|
||||
|
||||
2
.github/workflows/debug-artifacts-safe.yml
vendored
2
.github/workflows/debug-artifacts-safe.yml
vendored
@@ -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"
|
||||
|
||||
2
.github/workflows/post-release-mergeback.yml
vendored
2
.github/workflows/post-release-mergeback.yml
vendored
@@ -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'
|
||||
|
||||
20
.github/workflows/pr-checks.yml
vendored
20
.github/workflows/pr-checks.yml
vendored
@@ -2,6 +2,9 @@ name: PR Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "releases/v*"
|
||||
pull_request:
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
@@ -42,7 +45,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 +113,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"
|
||||
@@ -145,12 +148,11 @@ jobs:
|
||||
|
||||
- name: Fetch base commit
|
||||
id: fetch-base
|
||||
# Forks and Dependabot PRs don't have permission to write comments, so skip the repo size
|
||||
# check in those cases.
|
||||
# Skip this (and subsequent steps) if the PR originates from a fork, since this workflow
|
||||
# won't have the necessary permissions to post comments on the PR.
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||||
github.event.pull_request.user.login != 'dependabot[bot]'
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@@ -175,7 +177,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/
|
||||
@@ -209,7 +211,7 @@ jobs:
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||||
github.event.pull_request.user.login != 'dependabot[bot]' &&
|
||||
github.actor != 'dependabot[bot]' &&
|
||||
needs.other-checks.result == 'success'
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -223,7 +225,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
|
||||
|
||||
2
.github/workflows/query-filters.yml
vendored
2
.github/workflows/query-filters.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/rebuild.yml
vendored
2
.github/workflows/rebuild.yml
vendored
@@ -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'
|
||||
|
||||
2
.github/workflows/update-bundle.yml
vendored
2
.github/workflows/update-bundle.yml
vendored
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
1038
lib/entry-points.js
generated
1038
lib/entry-points.js
generated
File diff suppressed because it is too large
Load Diff
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.39.0",
|
||||
"version": "4.39.1",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import { ActionsEnv, getActionsEnv } from "./actions-util";
|
||||
import { ActionsEnv, getActionsEnv, restoreInputs } from "./actions-util";
|
||||
import type { ApiClient } from "./api-client";
|
||||
import { Env, ReadOnlyEnv } from "./environment";
|
||||
import type { FeatureEnablement } from "./feature-flags";
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
} from "./status-report";
|
||||
import { getEnv, getErrorMessage, wrapError } from "./util";
|
||||
|
||||
export { ActionName } from "./status-report";
|
||||
|
||||
/** Base state that is available to an Action on startup. */
|
||||
export interface BaseState {
|
||||
/** The name of the Action. */
|
||||
@@ -160,6 +162,19 @@ export interface Action {
|
||||
transformTelemetryError?: (error: Error) => string;
|
||||
}
|
||||
|
||||
/** Decides if {@link name} refers to a post-action. */
|
||||
function isPostAction(name: ActionName) {
|
||||
switch (name) {
|
||||
case ActionName.AnalyzePost:
|
||||
case ActionName.InitPost:
|
||||
case ActionName.StartProxyPost:
|
||||
case ActionName.UploadSarifPost:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** A generic entry point that sets up the basic environment for the `action` and runs it. */
|
||||
export async function runInActions(action: Action) {
|
||||
const startedAt = new Date();
|
||||
@@ -178,6 +193,11 @@ export async function runInActions(action: Action) {
|
||||
actions: actionsEnv,
|
||||
};
|
||||
|
||||
// If this is a post-action, restore inputs from the main action.
|
||||
if (isPostAction(action.name)) {
|
||||
restoreInputs(logger);
|
||||
}
|
||||
|
||||
// Create a unique identifier for this run.
|
||||
getJobUUID(actionState);
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ declare const __CODEQL_ACTION_VERSION__: string;
|
||||
export interface ActionsEnv {
|
||||
getRequiredInput: (name: string) => string;
|
||||
getOptionalInput: (name: string) => string | undefined;
|
||||
setFailed: (message: string) => void;
|
||||
getState: (name: string) => string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,8 @@ export function getActionsEnv(): ActionsEnv {
|
||||
return {
|
||||
getRequiredInput,
|
||||
getOptionalInput,
|
||||
setFailed: core.setFailed,
|
||||
getState: core.getState,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
*/
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import {
|
||||
ActionName,
|
||||
runInActions,
|
||||
type Action,
|
||||
type ActionState,
|
||||
} from "./action-common";
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { getConfig } from "./config-utils";
|
||||
@@ -16,22 +20,24 @@ import {
|
||||
getJavaTempDependencyDir,
|
||||
} from "./dependency-caching";
|
||||
import { EnvVar } from "./environment";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
export async function runWrapper() {
|
||||
/**
|
||||
* The main functionality of the `analyze` post-action is implemented by this function.
|
||||
* This is called once by {@link runInActions} after it is called in {@link runWrapper}.
|
||||
*/
|
||||
export async function run(action: ActionState<["Logger", "Env", "Actions"]>) {
|
||||
// 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 {
|
||||
const logger = getActionsLogger();
|
||||
actionsUtil.restoreInputs(logger);
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
checkGitHubVersionInRange(gitHubVersion, logger);
|
||||
|
||||
// Upload SARIF artifacts if we determine that this is a first-party analysis run.
|
||||
// For third-party runs, this artifact will be uploaded in the `upload-sarif-post` step.
|
||||
if (process.env[EnvVar.INIT_ACTION_HAS_RUN] === "true") {
|
||||
if (action.env.getOptional(EnvVar.INIT_ACTION_HAS_RUN) === "true") {
|
||||
const config = await getConfig(
|
||||
actionsUtil.getTemporaryDirectory(),
|
||||
logger,
|
||||
@@ -63,8 +69,19 @@ export async function runWrapper() {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(
|
||||
action.actions.setFailed(
|
||||
`analyze post-action step failed: ${getErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines the `analyze` post-action. */
|
||||
const analyzePostAction: Action = {
|
||||
name: ActionName.AnalyzePost,
|
||||
run,
|
||||
};
|
||||
|
||||
/** The main entry point for the `analyze` post-action. */
|
||||
export async function runWrapper() {
|
||||
await runInActions(analyzePostAction);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,14 @@ 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 { 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);
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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.`);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,10 @@ 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 { Feature } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
@@ -23,6 +27,7 @@ import {
|
||||
getTestEnv,
|
||||
initAllState,
|
||||
RecordingLogger,
|
||||
SAMPLE_DOTCOM_API_DETAILS,
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
@@ -550,3 +555,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);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
111
src/github.test.ts
Normal file
111
src/github.test.ts
Normal 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);
|
||||
});
|
||||
110
src/github.ts
Normal file
110
src/github.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { ActionsEnvVars, type ReadOnlyEnv } from "./environment";
|
||||
import { ConfigurationError } from "./util";
|
||||
|
||||
/** 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,
|
||||
};
|
||||
}
|
||||
@@ -4,15 +4,8 @@
|
||||
* other `post:` hooks.
|
||||
*/
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import { runInActions, type Action, type ActionState } from "./action-common";
|
||||
import {
|
||||
restoreInputs,
|
||||
getOptionalInput,
|
||||
getTemporaryDirectory,
|
||||
printDebugLogs,
|
||||
} from "./actions-util";
|
||||
import { getTemporaryDirectory, printDebugLogs } from "./actions-util";
|
||||
import { getGitHubVersion, wrapApiConfigurationError } from "./api-client";
|
||||
import { CachingKind } from "./caching-utils";
|
||||
import { getCodeQL } from "./codeql";
|
||||
@@ -49,7 +42,11 @@ interface InitPostStatusReport
|
||||
initActionPostHelper.JobStatusReport,
|
||||
initActionPostHelper.DependencyCachingUsageReport {}
|
||||
|
||||
async function run(action: ActionState<["Base", "Logger", "Env"]>) {
|
||||
/**
|
||||
* The main functionality of the `init` post-action is implemented by this function.
|
||||
* This is called once by {@link runInActions} after it is called in {@link runWrapper}.
|
||||
*/
|
||||
async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
const logger = action.logger;
|
||||
@@ -64,10 +61,7 @@ async function run(action: ActionState<["Base", "Logger", "Env"]>) {
|
||||
// Read the job status before restoring inputs, since it is provided by the Actions runtime
|
||||
// environment for this step and would otherwise be overwritten by the value that the `init`
|
||||
// Action saw, which is always a success.
|
||||
const jobStatus = getOptionalInput("job-status");
|
||||
|
||||
// Restore inputs from `init` Action.
|
||||
restoreInputs(logger);
|
||||
const jobStatus = action.actions.getOptionalInput("job-status");
|
||||
|
||||
const tmpDir = getTemporaryDirectory();
|
||||
config = await getConfig(tmpDir, logger);
|
||||
@@ -86,7 +80,7 @@ async function run(action: ActionState<["Base", "Logger", "Env"]>) {
|
||||
const features = initFeatures(
|
||||
gitHubVersion,
|
||||
repositoryNwo,
|
||||
getTemporaryDirectory(),
|
||||
config?.tempDir ?? tmpDir,
|
||||
logger,
|
||||
);
|
||||
|
||||
@@ -126,7 +120,7 @@ async function run(action: ActionState<["Base", "Logger", "Env"]>) {
|
||||
}
|
||||
} catch (unwrappedError) {
|
||||
const error = wrapApiConfigurationError(wrapError(unwrappedError));
|
||||
core.setFailed(error.message);
|
||||
action.actions.setFailed(error.message);
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
action.name,
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
loadRepositoryProperties,
|
||||
RepositoryPropertyName,
|
||||
} from "./feature-flags/properties";
|
||||
import * as github from "./github";
|
||||
import {
|
||||
checkInstallPython311,
|
||||
checkPacksForOverlayCompatibility,
|
||||
@@ -233,8 +234,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();
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import {
|
||||
getActionVersion,
|
||||
getOptionalInput,
|
||||
getRequiredInput,
|
||||
getTemporaryDirectory,
|
||||
} from "./actions-util";
|
||||
import { runInActions, type Action, type ActionState } from "./action-common";
|
||||
import { getActionVersion, getTemporaryDirectory } from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { CliError } from "./cli-errors";
|
||||
import { Config, getConfig } from "./config-utils";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { runResolveBuildEnvironment } from "./resolve-environment";
|
||||
import {
|
||||
sendStatusReport,
|
||||
sendUnhandledErrorStatusReport,
|
||||
createStatusReportBase,
|
||||
getActionsStatus,
|
||||
ActionName,
|
||||
@@ -24,25 +18,28 @@ import {
|
||||
checkForTimeout,
|
||||
checkGitHubVersionInRange,
|
||||
ConfigurationError,
|
||||
getErrorMessage,
|
||||
wrapError,
|
||||
} from "./util";
|
||||
|
||||
const ENVIRONMENT_OUTPUT_NAME = "environment";
|
||||
|
||||
async function run(startedAt: Date) {
|
||||
/**
|
||||
* The main functionality of the `resolve-environment` action is implemented by this function.
|
||||
* This is called once by {@link runInActions} after it is called in {@link runWrapper}.
|
||||
*/
|
||||
async function run(action: ActionState<["Base", "Logger", "Actions"]>) {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
const logger = getActionsLogger();
|
||||
const logger = action.logger;
|
||||
|
||||
let config: Config | undefined;
|
||||
|
||||
try {
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.ResolveEnvironment,
|
||||
action.name,
|
||||
"starting",
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
@@ -62,12 +59,13 @@ async function run(startedAt: Date) {
|
||||
);
|
||||
}
|
||||
|
||||
const workingDirectory = getOptionalInput("working-directory");
|
||||
const workingDirectory =
|
||||
action.actions.getOptionalInput("working-directory");
|
||||
const result = await runResolveBuildEnvironment(
|
||||
config.codeQLCmd,
|
||||
logger,
|
||||
workingDirectory,
|
||||
getRequiredInput("language"),
|
||||
action.actions.getRequiredInput("language"),
|
||||
);
|
||||
core.setOutput(ENVIRONMENT_OUTPUT_NAME, result);
|
||||
} catch (unwrappedError) {
|
||||
@@ -82,14 +80,14 @@ async function run(startedAt: Date) {
|
||||
);
|
||||
} else {
|
||||
// For any other error types, something has more seriously gone wrong and we fail.
|
||||
core.setFailed(
|
||||
action.actions.setFailed(
|
||||
`Failed to resolve a build environment suitable for automatically building your code. ${error.message}`,
|
||||
);
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.ResolveEnvironment,
|
||||
action.name,
|
||||
getActionsStatus(error),
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
@@ -105,9 +103,9 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.ResolveEnvironment,
|
||||
action.name,
|
||||
"success",
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
@@ -117,23 +115,14 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines the `resolve-environment` action. */
|
||||
const resolveEnvironmentAction: Action = {
|
||||
name: ActionName.ResolveEnvironment,
|
||||
run,
|
||||
};
|
||||
|
||||
/** The main entry point for the `resolve-environment` action. */
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
await run(startedAt);
|
||||
} catch (error) {
|
||||
core.setFailed(
|
||||
`${ActionName.ResolveEnvironment} action failed: ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
);
|
||||
await sendUnhandledErrorStatusReport(
|
||||
ActionName.ResolveEnvironment,
|
||||
startedAt,
|
||||
error,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
await runInActions(resolveEnvironmentAction);
|
||||
await checkForTimeout();
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -112,8 +113,7 @@ async function run(
|
||||
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();
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,12 +2,13 @@ import { ActionState } from "../action-common";
|
||||
import { isRunningLocalAction } from "../actions-util";
|
||||
import * as api from "../api-client";
|
||||
import { ActionsEnvVars } from "../environment";
|
||||
import { GITHUB_DOTCOM_URL } from "../github";
|
||||
import {
|
||||
isRepositoryNwoStr,
|
||||
parseRepositoryNwo,
|
||||
type RepositoryNwoStr,
|
||||
} from "../repository";
|
||||
import { GITHUB_DOTCOM_URL, type InstanceURL } from "../util";
|
||||
import { type InstanceURL } from "../util";
|
||||
|
||||
/** The NWO of the standard CodeQL Action repository. */
|
||||
export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
|
||||
|
||||
@@ -3,38 +3,41 @@
|
||||
* It will run after the all steps in this job, in reverse order in relation to
|
||||
* other `post:` hooks.
|
||||
*/
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import {
|
||||
ActionName,
|
||||
runInActions,
|
||||
type Action,
|
||||
type ActionState,
|
||||
} from "./action-common";
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { uploadArtifacts } from "./debug-artifacts";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { getSafeErrorMessage } from "./start-proxy/status-report";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
export async function runWrapper() {
|
||||
/**
|
||||
* The main functionality of the `start-proxy` post-action is implemented by this function.
|
||||
* This is called once by {@link runInActions} after it is called in {@link runWrapper}.
|
||||
*/
|
||||
export async function run(action: ActionState<["Logger", "Actions"]>) {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
const logger = getActionsLogger();
|
||||
const logger = action.logger;
|
||||
|
||||
try {
|
||||
// Restore inputs from `start-proxy` Action.
|
||||
actionsUtil.restoreInputs(logger);
|
||||
|
||||
// Kill the running proxy
|
||||
const pid = core.getState("proxy-process-pid");
|
||||
const pid = action.actions.getState("proxy-process-pid");
|
||||
if (pid) {
|
||||
process.kill(Number(pid));
|
||||
}
|
||||
|
||||
const config = await configUtils.getConfig(
|
||||
actionsUtil.getTemporaryDirectory(),
|
||||
logger,
|
||||
);
|
||||
const tempDir = actionsUtil.getTemporaryDirectory();
|
||||
const config = await configUtils.getConfig(tempDir, logger);
|
||||
|
||||
if (config?.debugMode || core.isDebug()) {
|
||||
const logFilePath = core.getState("proxy-log-file");
|
||||
if (config?.debugMode || logger.isDebug()) {
|
||||
const logFilePath = action.actions.getState("proxy-log-file");
|
||||
logger.info(
|
||||
"Debug mode is on. Uploading proxy log as Actions debugging artifact...",
|
||||
);
|
||||
@@ -44,7 +47,7 @@ export async function runWrapper() {
|
||||
await uploadArtifacts(
|
||||
logger,
|
||||
[logFilePath],
|
||||
actionsUtil.getTemporaryDirectory(),
|
||||
tempDir,
|
||||
"proxy-log-file",
|
||||
gitHubVersion.type,
|
||||
);
|
||||
@@ -56,3 +59,15 @@ export async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines the `start-proxy` post-action. */
|
||||
const startProxyPostAction: Action = {
|
||||
name: ActionName.StartProxyPost,
|
||||
run,
|
||||
transformTelemetryError: getSafeErrorMessage,
|
||||
};
|
||||
|
||||
/** The main entry point for the `start-proxy` post-action. */
|
||||
export async function runWrapper() {
|
||||
await runInActions(startProxyPostAction);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -52,19 +52,25 @@ import {
|
||||
|
||||
export enum ActionName {
|
||||
Analyze = "finish",
|
||||
AnalyzePost = "analyze-post",
|
||||
Autobuild = "autobuild",
|
||||
Init = "init",
|
||||
InitPost = "init-post",
|
||||
ResolveEnvironment = "resolve-environment",
|
||||
SetupCodeQL = "setup-codeql",
|
||||
StartProxy = "start-proxy",
|
||||
StartProxyPost = "start-proxy-post",
|
||||
UploadSarif = "upload-sarif",
|
||||
UploadSarifPost = "upload-sarif-post",
|
||||
}
|
||||
|
||||
/** A partial mapping from `ActionName` to names for actions whose display name is not the `ActionName` value. */
|
||||
export const ACTION_DISPLAY_NAMES: Partial<Record<ActionName, string>> = {
|
||||
[ActionName.Analyze]: "analyze",
|
||||
[ActionName.AnalyzePost]: "analyze post-action",
|
||||
[ActionName.InitPost]: "init post",
|
||||
[ActionName.StartProxyPost]: "start-proxy post-action",
|
||||
[ActionName.UploadSarifPost]: "upload-sarif post-action",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -227,8 +227,13 @@ export function getTestEnv(testEnv: NodeJS.ProcessEnv = {}): Env {
|
||||
return getEnv(testEnv);
|
||||
}
|
||||
|
||||
/** An implementation of `ActionsEnv` for use in tests. */
|
||||
/**
|
||||
* An implementation of {@link ActionsEnv} for use in tests.
|
||||
* The methods of instances of this class are intended to be stubbed.
|
||||
*/
|
||||
class TestActionsEnv implements ActionsEnv {
|
||||
private failed: boolean = false;
|
||||
|
||||
constructor(private readonly env: Env) {}
|
||||
|
||||
public clone(env: Env): this {
|
||||
@@ -242,6 +247,18 @@ class TestActionsEnv implements ActionsEnv {
|
||||
public getOptionalInput(_name: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public setFailed(_message: string) {
|
||||
this.failed = true;
|
||||
}
|
||||
|
||||
get hasFailed() {
|
||||
return this.failed;
|
||||
}
|
||||
|
||||
public getState(name: string): string {
|
||||
throw new Error(`State '${name}' requested but not set.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -3,32 +3,36 @@
|
||||
* It will run after the all steps in this job, in reverse order in relation to
|
||||
* other `post:` hooks.
|
||||
*/
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import {
|
||||
ActionName,
|
||||
runInActions,
|
||||
type Action,
|
||||
type ActionState,
|
||||
} from "./action-common";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import * as debugArtifacts from "./debug-artifacts";
|
||||
import { EnvVar } from "./environment";
|
||||
import { getActionsLogger, withGroup } from "./logging";
|
||||
import { withGroup } from "./logging";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
export async function runWrapper() {
|
||||
/**
|
||||
* The main functionality of the `upload-sarif` post-action is implemented by this function.
|
||||
* This is called once by {@link runInActions} after it is called in {@link runWrapper}.
|
||||
*/
|
||||
export async function run(action: ActionState<["Logger", "Env", "Actions"]>) {
|
||||
// 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 {
|
||||
const logger = getActionsLogger();
|
||||
|
||||
// Restore inputs from `upload-sarif` Action.
|
||||
actionsUtil.restoreInputs(logger);
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
checkGitHubVersionInRange(gitHubVersion, logger);
|
||||
|
||||
// Upload SARIF artifacts if we determine that this is a third-party analysis run.
|
||||
// For first-party runs, this artifact will be uploaded in the `analyze-post` step.
|
||||
if (process.env[EnvVar.INIT_ACTION_HAS_RUN] !== "true") {
|
||||
if (action.env.getOptional(EnvVar.INIT_ACTION_HAS_RUN) !== "true") {
|
||||
if (gitHubVersion.type === undefined) {
|
||||
core.warning(
|
||||
logger.warning(
|
||||
`Did not upload debug artifacts because cannot determine the GitHub variant running.`,
|
||||
);
|
||||
return;
|
||||
@@ -38,8 +42,19 @@ export async function runWrapper() {
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(
|
||||
action.actions.setFailed(
|
||||
`upload-sarif post-action step failed: ${getErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines the `upload-sarif` post-action. */
|
||||
const uploadSarifPostAction: Action = {
|
||||
name: ActionName.UploadSarifPost,
|
||||
run,
|
||||
};
|
||||
|
||||
/** The main entry point for the `upload-sarif` post-action. */
|
||||
export async function runWrapper() {
|
||||
await runInActions(uploadSarifPostAction);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
48
src/util.ts
48
src/util.ts
@@ -50,11 +50,6 @@ 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,49 +465,6 @@ 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";
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user