mirror of
https://github.com/github/codeql-action.git
synced 2025-12-19 05:49:21 +08:00
Compare commits
2 Commits
mbg/csharp
...
henrymerce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d8b358273 | ||
|
|
f336c09493 |
@@ -16,9 +16,9 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.12'
|
python-version: 3.12
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/__bundle-zstd.yml
generated
vendored
2
.github/workflows/__bundle-zstd.yml
generated
vendored
@@ -79,7 +79,7 @@ jobs:
|
|||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.os }}-zstd-bundle.sarif
|
name: ${{ matrix.os }}-zstd-bundle.sarif
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
path: ${{ runner.temp }}/results/javascript.sarif
|
||||||
|
|||||||
2
.github/workflows/__config-export.yml
generated
vendored
2
.github/workflows/__config-export.yml
generated
vendored
@@ -67,7 +67,7 @@ jobs:
|
|||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
path: ${{ runner.temp }}/results/javascript.sarif
|
||||||
|
|||||||
2
.github/workflows/__diagnostics-export.yml
generated
vendored
2
.github/workflows/__diagnostics-export.yml
generated
vendored
@@ -78,7 +78,7 @@ jobs:
|
|||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
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
@@ -85,7 +85,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
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
@@ -64,7 +64,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
path: ${{ runner.temp }}/results/javascript.sarif
|
||||||
|
|||||||
40
.github/workflows/__multi-language-autodetect.yml
generated
vendored
40
.github/workflows/__multi-language-autodetect.yml
generated
vendored
@@ -9,9 +9,6 @@ env:
|
|||||||
GO111MODULE: auto
|
GO111MODULE: auto
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- releases/v*
|
|
||||||
pull_request:
|
pull_request:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
@@ -56,42 +53,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: macos-latest
|
|
||||||
version: stable-v2.17.6
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: stable-v2.17.6
|
|
||||||
- os: macos-latest
|
|
||||||
version: stable-v2.18.4
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: stable-v2.18.4
|
|
||||||
- os: macos-latest
|
|
||||||
version: stable-v2.19.4
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: stable-v2.19.4
|
|
||||||
- os: macos-latest
|
|
||||||
version: stable-v2.20.7
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: stable-v2.20.7
|
|
||||||
- os: macos-latest
|
|
||||||
version: stable-v2.21.4
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: stable-v2.21.4
|
|
||||||
- os: macos-latest
|
|
||||||
version: stable-v2.22.4
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: stable-v2.22.4
|
|
||||||
- os: macos-latest
|
|
||||||
version: default
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: default
|
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
version: linked
|
version: linked
|
||||||
- os: ubuntu-latest
|
|
||||||
version: linked
|
|
||||||
- os: macos-latest
|
|
||||||
version: nightly-latest
|
|
||||||
- os: ubuntu-latest
|
|
||||||
version: nightly-latest
|
|
||||||
name: Multi-language repository
|
name: Multi-language repository
|
||||||
if: github.triggering_actor != 'dependabot[bot]'
|
if: github.triggering_actor != 'dependabot[bot]'
|
||||||
permissions:
|
permissions:
|
||||||
@@ -185,6 +148,3 @@ jobs:
|
|||||||
echo "Did not create a database for Swift, or created it in the wrong location."
|
echo "Did not create a database for Swift, or created it in the wrong location."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
env:
|
|
||||||
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
|
|
||||||
CODEQL_ACTION_TEST_MODE: true
|
|
||||||
|
|||||||
6
.github/workflows/__quality-queries.yml
generated
vendored
6
.github/workflows/__quality-queries.yml
generated
vendored
@@ -83,7 +83,7 @@ jobs:
|
|||||||
post-processed-sarif-path: ${{ runner.temp }}/post-processed
|
post-processed-sarif-path: ${{ runner.temp }}/post-processed
|
||||||
- name: Upload security SARIF
|
- name: Upload security SARIF
|
||||||
if: contains(matrix.analysis-kinds, 'code-scanning')
|
if: contains(matrix.analysis-kinds, 'code-scanning')
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
||||||
@@ -91,14 +91,14 @@ jobs:
|
|||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Upload quality SARIF
|
- name: Upload quality SARIF
|
||||||
if: contains(matrix.analysis-kinds, 'code-quality')
|
if: contains(matrix.analysis-kinds, 'code-quality')
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
|
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
|
||||||
path: ${{ runner.temp }}/results/javascript.quality.sarif
|
path: ${{ runner.temp }}/results/javascript.quality.sarif
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Upload post-processed SARIF
|
- name: Upload post-processed SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
||||||
|
|||||||
2
.github/workflows/__rubocop-multi-language.yml
generated
vendored
2
.github/workflows/__rubocop-multi-language.yml
generated
vendored
@@ -56,7 +56,7 @@ jobs:
|
|||||||
use-all-platform-bundle: 'false'
|
use-all-platform-bundle: 'false'
|
||||||
setup-kotlin: 'true'
|
setup-kotlin: 'true'
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
|
uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.6
|
ruby-version: 2.6
|
||||||
- name: Install Code Scanning integration
|
- name: Install Code Scanning integration
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-expected-release-files:
|
check-expected-release-files:
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-14,macos-15]
|
os: [ubuntu-22.04,ubuntu-24.04,windows-2022,windows-2025,macos-13,macos-14,macos-15]
|
||||||
tools: ${{ fromJson(needs.check-codeql-versions.outputs.versions) }}
|
tools: ${{ fromJson(needs.check-codeql-versions.outputs.versions) }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v5
|
||||||
- name: Check expected artifacts exist
|
- name: Check expected artifacts exist
|
||||||
run: |
|
run: |
|
||||||
LANGUAGES="cpp csharp go java javascript python"
|
LANGUAGES="cpp csharp go java javascript python"
|
||||||
|
|||||||
2
.github/workflows/debug-artifacts-safe.yml
vendored
2
.github/workflows/debug-artifacts-safe.yml
vendored
@@ -73,7 +73,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v5
|
||||||
- name: Check expected artifacts exist
|
- name: Check expected artifacts exist
|
||||||
run: |
|
run: |
|
||||||
VERSIONS="stable-v2.20.3 default linked nightly-latest"
|
VERSIONS="stable-v2.20.3 default linked nightly-latest"
|
||||||
|
|||||||
2
.github/workflows/label-pr-size.yml
vendored
2
.github/workflows/label-pr-size.yml
vendored
@@ -16,7 +16,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
sizeup:
|
sizeup:
|
||||||
name: Label PR with size
|
name: Label PR with size
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Run sizeup
|
- name: Run sizeup
|
||||||
|
|||||||
5
.github/workflows/post-release-mergeback.yml
vendored
5
.github/workflows/post-release-mergeback.yml
vendored
@@ -24,7 +24,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
merge-back:
|
merge-back:
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
environment: Automation
|
environment: Automation
|
||||||
if: github.repository == 'github/codeql-action'
|
if: github.repository == 'github/codeql-action'
|
||||||
env:
|
env:
|
||||||
@@ -48,9 +48,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # ensure we have all tags and can push commits
|
fetch-depth: 0 # ensure we have all tags and can push commits
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
- uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Update git config
|
- name: Update git config
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/prepare-release.yml
vendored
2
.github/workflows/prepare-release.yml
vendored
@@ -29,7 +29,7 @@ defaults:
|
|||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
name: "Prepare release"
|
name: "Prepare release"
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'github/codeql-action'
|
if: github.repository == 'github/codeql-action'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
28
.github/workflows/publish-immutable-action.yml
vendored
28
.github/workflows/publish-immutable-action.yml
vendored
@@ -1,10 +1,8 @@
|
|||||||
name: 'Publish Immutable Action Version'
|
name: 'Publish Immutable Action Version'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
tags:
|
types: [published]
|
||||||
# Match version tags, but not the major version tags.
|
|
||||||
- 'v[0-9]+.**'
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -12,16 +10,30 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Check release name
|
||||||
|
id: check
|
||||||
|
env:
|
||||||
|
RELEASE_NAME: ${{ github.event.release.name }}
|
||||||
|
run: |
|
||||||
|
echo "Release name: ${{ github.event.release.name }}"
|
||||||
|
if [[ $RELEASE_NAME == v* ]]; then
|
||||||
|
echo "This is a CodeQL Action release. Create an Immutable Action"
|
||||||
|
echo "is-action-release=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "This is a CodeQL Bundle release. Do not create an Immutable Action"
|
||||||
|
echo "is-action-release=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
- name: Checking out
|
||||||
|
if: steps.check.outputs.is-action-release == 'true'
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
|
- name: Publish
|
||||||
- name: Publish immutable release
|
if: steps.check.outputs.is-action-release == 'true'
|
||||||
id: publish
|
id: publish
|
||||||
uses: actions/publish-immutable-action@v0.0.4
|
uses: actions/publish-immutable-action@v0.0.4
|
||||||
|
|||||||
18
.github/workflows/script/bundle_changelog.py
vendored
18
.github/workflows/script/bundle_changelog.py
vendored
@@ -1,18 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Get the PR number from the PR URL.
|
|
||||||
pr_number = os.environ['PR_URL'].split('/')[-1]
|
|
||||||
changelog_note = f"- Update default CodeQL bundle version to {os.environ['CLI_VERSION']}. [#{pr_number}]({os.environ['PR_URL']})"
|
|
||||||
|
|
||||||
# If the "[UNRELEASED]" section starts with "no user facing changes", remove that line.
|
|
||||||
with open('CHANGELOG.md', 'r') as f:
|
|
||||||
changelog = f.read()
|
|
||||||
|
|
||||||
changelog = changelog.replace('## [UNRELEASED]\n\nNo user facing changes.', '## [UNRELEASED]\n')
|
|
||||||
|
|
||||||
# Add the changelog note to the bottom of the "[UNRELEASED]" section.
|
|
||||||
changelog = re.sub(r'\n## (\d+\.\d+\.\d+)', f'{changelog_note}\n\n## \\1', changelog, count=1)
|
|
||||||
|
|
||||||
with open('CHANGELOG.md', 'w') as f:
|
|
||||||
f.write(changelog)
|
|
||||||
@@ -29,7 +29,7 @@ fi
|
|||||||
echo "Getting checks for $GITHUB_SHA"
|
echo "Getting checks for $GITHUB_SHA"
|
||||||
|
|
||||||
# Ignore any checks with "https://", CodeQL, LGTM, Update, and ESLint checks.
|
# Ignore any checks with "https://", CodeQL, LGTM, Update, and ESLint checks.
|
||||||
CHECKS="$(gh api repos/github/codeql-action/commits/"${GITHUB_SHA}"/check-runs --paginate | jq --slurp --compact-output --raw-output '[.[].check_runs.[] | select(.conclusion != "skipped") | .name | select(contains("https://") or . == "CodeQL" or . == "Dependabot" or . == "check-expected-release-files" or contains("Update") or contains("ESLint") or contains("update") or contains("test-setup-python-scripts") or . == "Agent" or . == "Cleanup artifacts" or . == "Prepare" or . == "Upload results" | not)] | unique | sort')"
|
CHECKS="$(gh api repos/github/codeql-action/commits/"${GITHUB_SHA}"/check-runs --paginate | jq --slurp --compact-output --raw-output '[.[].check_runs.[] | select(.conclusion != "skipped") | .name | select(contains("https://") or . == "CodeQL" or . == "Dependabot" or . == "check-expected-release-files" or contains("Update") or contains("ESLint") or contains("update") or contains("test-setup-python-scripts") | not)] | unique | sort')"
|
||||||
|
|
||||||
echo "$CHECKS" | jq
|
echo "$CHECKS" | jq
|
||||||
|
|
||||||
|
|||||||
29
.github/workflows/update-bundle.yml
vendored
29
.github/workflows/update-bundle.yml
vendored
@@ -20,7 +20,7 @@ defaults:
|
|||||||
jobs:
|
jobs:
|
||||||
update-bundle:
|
update-bundle:
|
||||||
if: github.event.release.prerelease && startsWith(github.event.release.tag_name, 'codeql-bundle-')
|
if: github.event.release.prerelease && startsWith(github.event.release.tag_name, 'codeql-bundle-')
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # needed to push commits
|
contents: write # needed to push commits
|
||||||
pull-requests: write # needed to create pull requests
|
pull-requests: write # needed to create pull requests
|
||||||
@@ -40,11 +40,6 @@ jobs:
|
|||||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name "github-actions[bot]"
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: '3.12'
|
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
@@ -83,8 +78,28 @@ jobs:
|
|||||||
echo "PR_URL=$pr_url" | tee -a "$GITHUB_ENV"
|
echo "PR_URL=$pr_url" | tee -a "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Create changelog note
|
- name: Create changelog note
|
||||||
|
shell: python
|
||||||
run: |
|
run: |
|
||||||
python .github/workflows/script/bundle_changelog.py
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Get the PR number from the PR URL.
|
||||||
|
pr_number = os.environ['PR_URL'].split('/')[-1]
|
||||||
|
changelog_note = f"- Update default CodeQL bundle version to {os.environ['CLI_VERSION']}. [#{pr_number}]({os.environ['PR_URL']})"
|
||||||
|
|
||||||
|
# If the "[UNRELEASED]" section starts with "no user facing changes", remove that line.
|
||||||
|
# Use perl to avoid having to escape the newline character.
|
||||||
|
|
||||||
|
with open('CHANGELOG.md', 'r') as f:
|
||||||
|
changelog = f.read()
|
||||||
|
|
||||||
|
changelog = changelog.replace('## [UNRELEASED]\n\nNo user facing changes.', '## [UNRELEASED]\n')
|
||||||
|
|
||||||
|
# Add the changelog note to the bottom of the "[UNRELEASED]" section.
|
||||||
|
changelog = re.sub(r'\n## (\d+\.\d+\.\d+)', f'{changelog_note}\n\n## \\1', changelog, count=1)
|
||||||
|
|
||||||
|
with open('CHANGELOG.md', 'w') as f:
|
||||||
|
f.write(changelog)
|
||||||
|
|
||||||
- name: Push changelog note
|
- name: Push changelog note
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.github/workflows/update-release-branch.yml
vendored
4
.github/workflows/update-release-branch.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
update:
|
update:
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
needs: [prepare]
|
needs: [prepare]
|
||||||
env:
|
env:
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
|
|
||||||
backport:
|
backport:
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
environment: Automation
|
environment: Automation
|
||||||
needs: [prepare]
|
needs: [prepare]
|
||||||
if: ${{ (github.event_name == 'push') && needs.prepare.outputs.backport_target_branches != '[]' }}
|
if: ${{ (github.event_name == 'push') && needs.prepare.outputs.backport_target_branches != '[]' }}
|
||||||
|
|||||||
@@ -4,18 +4,12 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: "0 0 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- .github/workflows/update-supported-enterprise-server-versions.yml
|
|
||||||
- .github/workflows/update-supported-enterprise-server-versions/update.py
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-supported-enterprise-server-versions:
|
update-supported-enterprise-server-versions:
|
||||||
name: Update Supported Enterprise Server Versions
|
name: Update Supported Enterprise Server Versions
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'github/codeql-action'
|
if: github.repository == 'github/codeql-action'
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # needed to push commits
|
contents: write # needed to push commits
|
||||||
@@ -34,7 +28,6 @@ jobs:
|
|||||||
repository: github/enterprise-releases
|
repository: github/enterprise-releases
|
||||||
token: ${{ secrets.ENTERPRISE_RELEASE_TOKEN }}
|
token: ${{ secrets.ENTERPRISE_RELEASE_TOKEN }}
|
||||||
path: ${{ github.workspace }}/enterprise-releases/
|
path: ${{ github.workspace }}/enterprise-releases/
|
||||||
sparse-checkout: releases.json
|
|
||||||
- name: Update Supported Enterprise Server Versions
|
- name: Update Supported Enterprise Server Versions
|
||||||
run: |
|
run: |
|
||||||
cd ./.github/workflows/update-supported-enterprise-server-versions/
|
cd ./.github/workflows/update-supported-enterprise-server-versions/
|
||||||
@@ -42,7 +35,6 @@ jobs:
|
|||||||
pipenv install
|
pipenv install
|
||||||
pipenv run ./update.py
|
pipenv run ./update.py
|
||||||
rm --recursive "$ENTERPRISE_RELEASES_PATH"
|
rm --recursive "$ENTERPRISE_RELEASES_PATH"
|
||||||
npm ci
|
|
||||||
npm run build
|
npm run build
|
||||||
env:
|
env:
|
||||||
ENTERPRISE_RELEASES_PATH: ${{ github.workspace }}/enterprise-releases/
|
ENTERPRISE_RELEASES_PATH: ${{ github.workspace }}/enterprise-releases/
|
||||||
@@ -52,33 +44,25 @@ jobs:
|
|||||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name "github-actions[bot]"
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes and open PR
|
||||||
id: prepare-commit
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
if [[ -z $(git status --porcelain) ]]; then
|
if [[ -z $(git status --porcelain) ]]; then
|
||||||
echo "No changes to commit"
|
echo "No changes to commit"
|
||||||
echo "committed=false" >> $GITHUB_OUTPUT
|
|
||||||
else
|
else
|
||||||
git checkout -b update-supported-enterprise-server-versions
|
git checkout -b update-supported-enterprise-server-versions
|
||||||
git add .
|
git add .
|
||||||
git commit --message "Update supported GitHub Enterprise Server versions"
|
git commit --message "Update supported GitHub Enterprise Server versions"
|
||||||
|
git push origin update-supported-enterprise-server-versions
|
||||||
|
|
||||||
echo "committed=true" >> $GITHUB_OUTPUT
|
body="This PR updates the list of supported GitHub Enterprise Server versions, either because a new "
|
||||||
|
body+="version is about to be feature frozen, or because an old release has been deprecated."
|
||||||
|
body+=$'\n\n'
|
||||||
|
body+="If an old release has been deprecated, please follow the instructions in CONTRIBUTING.md to "
|
||||||
|
body+="deprecate the corresponding version of CodeQL."
|
||||||
|
|
||||||
|
gh pr create --draft \
|
||||||
|
--title "Update supported GitHub Enterprise Server versions" \
|
||||||
|
--body "$body"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Open PR
|
|
||||||
if: github.event_name != 'pull_request' && steps.prepare-commit.outputs.committed == 'true'
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
git push origin update-supported-enterprise-server-versions
|
|
||||||
|
|
||||||
body="This PR updates the list of supported GitHub Enterprise Server versions, either because a new "
|
|
||||||
body+="version is about to be feature frozen, or because an old release has been deprecated."
|
|
||||||
body+=$'\n\n'
|
|
||||||
body+="If an old release has been deprecated, please follow the instructions in CONTRIBUTING.md to "
|
|
||||||
body+="deprecate the corresponding version of CodeQL."
|
|
||||||
|
|
||||||
gh pr create --draft \
|
|
||||||
--title "Update supported GitHub Enterprise Server versions" \
|
|
||||||
--body "$body"
|
|
||||||
|
|||||||
@@ -4,16 +4,8 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
|
|||||||
|
|
||||||
## [UNRELEASED]
|
## [UNRELEASED]
|
||||||
|
|
||||||
- CodeQL Action v3 will be deprecated in December 2026. The Action now logs a warning for customers who are running v3 but could be running v4. For more information, see [Upcoming deprecation of CodeQL Action v3](https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/).
|
|
||||||
|
|
||||||
## 4.31.2 - 30 Oct 2025
|
|
||||||
|
|
||||||
No user facing changes.
|
No user facing changes.
|
||||||
|
|
||||||
## 4.31.1 - 30 Oct 2025
|
|
||||||
|
|
||||||
- The `add-snippets` input has been removed from the `analyze` action. This input has been deprecated since CodeQL Action 3.26.4 in August 2024 when this removal was announced.
|
|
||||||
|
|
||||||
## 4.31.0 - 24 Oct 2025
|
## 4.31.0 - 24 Oct 2025
|
||||||
|
|
||||||
- Bump minimum CodeQL bundle version to 2.17.6. [#3223](https://github.com/github/codeql-action/pull/3223)
|
- Bump minimum CodeQL bundle version to 2.17.6. [#3223](https://github.com/github/codeql-action/pull/3223)
|
||||||
|
|||||||
@@ -32,10 +32,14 @@ inputs:
|
|||||||
and 13GB for macOS).
|
and 13GB for macOS).
|
||||||
required: false
|
required: false
|
||||||
add-snippets:
|
add-snippets:
|
||||||
description: Does not have any effect.
|
description: Specify whether or not to add code snippets to the output sarif file.
|
||||||
required: false
|
required: false
|
||||||
|
default: "false"
|
||||||
deprecationMessage: >-
|
deprecationMessage: >-
|
||||||
The input "add-snippets" has been removed and no longer has any effect.
|
The input "add-snippets" is deprecated and will be removed on the first release in August 2025.
|
||||||
|
When this input is set to true it is expected to add code snippets with an alert to the SARIF file.
|
||||||
|
However, since Code Scanning ignores code snippets provided as part of a SARIF file this is currently
|
||||||
|
a no operation. No alternative is available.
|
||||||
skip-queries:
|
skip-queries:
|
||||||
description: If this option is set, the CodeQL database will be built but no queries will be run on it. Thus, no results will be produced.
|
description: If this option is set, the CodeQL database will be built but no queries will be run on it. Thus, no results will be produced.
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import filenames from "eslint-plugin-filenames";
|
|||||||
import github from "eslint-plugin-github";
|
import github from "eslint-plugin-github";
|
||||||
import _import from "eslint-plugin-import";
|
import _import from "eslint-plugin-import";
|
||||||
import noAsyncForeach from "eslint-plugin-no-async-foreach";
|
import noAsyncForeach from "eslint-plugin-no-async-foreach";
|
||||||
import jsdoc from "eslint-plugin-jsdoc";
|
|
||||||
import globals from "globals";
|
import globals from "globals";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -53,7 +52,6 @@ export default [
|
|||||||
github: fixupPluginRules(github),
|
github: fixupPluginRules(github),
|
||||||
import: fixupPluginRules(_import),
|
import: fixupPluginRules(_import),
|
||||||
"no-async-foreach": noAsyncForeach,
|
"no-async-foreach": noAsyncForeach,
|
||||||
"jsdoc": jsdoc,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
@@ -135,16 +133,6 @@ export default [
|
|||||||
"@typescript-eslint/no-shadow": "error",
|
"@typescript-eslint/no-shadow": "error",
|
||||||
"@typescript-eslint/prefer-optional-chain": "error",
|
"@typescript-eslint/prefer-optional-chain": "error",
|
||||||
"one-var": ["error", "never"],
|
"one-var": ["error", "never"],
|
||||||
|
|
||||||
// Check param names to ensure that we don't have outdated JSDocs.
|
|
||||||
"jsdoc/check-param-names": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
// We don't currently require full JSDoc coverage, so this rule
|
|
||||||
// should not error on missing @param annotations.
|
|
||||||
disableMissingParamChecks: true,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
13759
lib/analyze-action-post.js
generated
13759
lib/analyze-action-post.js
generated
File diff suppressed because one or more lines are too long
13178
lib/analyze-action.js
generated
13178
lib/analyze-action.js
generated
File diff suppressed because it is too large
Load Diff
5497
lib/autobuild-action.js
generated
5497
lib/autobuild-action.js
generated
File diff suppressed because it is too large
Load Diff
22280
lib/init-action-post.js
generated
22280
lib/init-action-post.js
generated
File diff suppressed because one or more lines are too long
13268
lib/init-action.js
generated
13268
lib/init-action.js
generated
File diff suppressed because it is too large
Load Diff
5505
lib/resolve-environment-action.js
generated
5505
lib/resolve-environment-action.js
generated
File diff suppressed because it is too large
Load Diff
12709
lib/setup-codeql-action.js
generated
12709
lib/setup-codeql-action.js
generated
File diff suppressed because it is too large
Load Diff
13698
lib/start-proxy-action-post.js
generated
13698
lib/start-proxy-action-post.js
generated
File diff suppressed because one or more lines are too long
5221
lib/start-proxy-action.js
generated
5221
lib/start-proxy-action.js
generated
File diff suppressed because it is too large
Load Diff
12711
lib/upload-lib.js
generated
12711
lib/upload-lib.js
generated
File diff suppressed because it is too large
Load Diff
13728
lib/upload-sarif-action-post.js
generated
13728
lib/upload-sarif-action-post.js
generated
File diff suppressed because one or more lines are too long
12791
lib/upload-sarif-action.js
generated
12791
lib/upload-sarif-action.js
generated
File diff suppressed because it is too large
Load Diff
1603
package-lock.json
generated
1603
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "4.31.3",
|
"version": "4.31.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "CodeQL action",
|
"description": "CodeQL action",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -24,20 +24,22 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": "^4.0.0",
|
"@actions/artifact": "^2.3.1",
|
||||||
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
|
||||||
"@actions/cache": "^4.1.0",
|
"@actions/cache": "^4.1.0",
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^1.11.1",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^1.1.1",
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^6.0.0",
|
||||||
"@actions/glob": "^0.5.0",
|
"@actions/glob": "^0.5.0",
|
||||||
"@actions/http-client": "^3.0.0",
|
"@actions/http-client": "^2.2.3",
|
||||||
"@actions/io": "^2.0.0",
|
"@actions/io": "^1.1.3",
|
||||||
"@actions/tool-cache": "^2.0.2",
|
"@actions/tool-cache": "^2.0.2",
|
||||||
"@octokit/plugin-retry": "^6.0.0",
|
"@octokit/plugin-retry": "^6.0.0",
|
||||||
"@octokit/request-error": "^7.0.2",
|
"@octokit/request-error": "^7.0.1",
|
||||||
"@schemastore/package": "0.0.10",
|
"@schemastore/package": "0.0.10",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
|
"console-log-level": "^1.4.1",
|
||||||
|
"del": "^8.0.0",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"follow-redirects": "^1.15.11",
|
"follow-redirects": "^1.15.11",
|
||||||
"get-folder-size": "^5.0.0",
|
"get-folder-size": "^5.0.0",
|
||||||
@@ -45,34 +47,34 @@
|
|||||||
"jsonschema": "1.4.1",
|
"jsonschema": "1.4.1",
|
||||||
"long": "^5.3.2",
|
"long": "^5.3.2",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"octokit": "^5.0.5",
|
"octokit": "^5.0.4",
|
||||||
"semver": "^7.7.3",
|
"semver": "^7.7.3",
|
||||||
"uuid": "^13.0.0"
|
"uuid": "^13.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ava/typescript": "6.0.0",
|
"@ava/typescript": "6.0.0",
|
||||||
"@eslint/compat": "^1.4.1",
|
"@eslint/compat": "^1.4.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.38.0",
|
||||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||||
"@octokit/types": "^16.0.0",
|
"@octokit/types": "^15.0.0",
|
||||||
"@types/archiver": "^7.0.0",
|
"@types/archiver": "^6.0.3",
|
||||||
|
"@types/console-log-level": "^1.4.5",
|
||||||
"@types/follow-redirects": "^1.14.4",
|
"@types/follow-redirects": "^1.14.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "20.19.9",
|
"@types/node": "20.19.9",
|
||||||
"@types/node-forge": "^1.3.14",
|
"@types/node-forge": "^1.3.14",
|
||||||
"@types/semver": "^7.7.1",
|
"@types/semver": "^7.7.1",
|
||||||
"@types/sinon": "^17.0.4",
|
"@types/sinon": "^17.0.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
"@typescript-eslint/eslint-plugin": "^8.46.1",
|
||||||
"@typescript-eslint/parser": "^8.41.0",
|
"@typescript-eslint/parser": "^8.41.0",
|
||||||
"ava": "^6.4.1",
|
"ava": "^6.4.1",
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.25.11",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
"eslint-import-resolver-typescript": "^3.8.7",
|
"eslint-import-resolver-typescript": "^3.8.7",
|
||||||
"eslint-plugin-filenames": "^1.3.2",
|
"eslint-plugin-filenames": "^1.3.2",
|
||||||
"eslint-plugin-github": "^5.1.8",
|
"eslint-plugin-github": "^5.1.8",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-jsdoc": "^61.1.12",
|
|
||||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||||
"glob": "^11.0.3",
|
"glob": "^11.0.3",
|
||||||
"nock": "^14.0.10",
|
"nock": "^14.0.10",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ steps:
|
|||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.os }}-zstd-bundle.sarif
|
name: ${{ matrix.os }}-zstd-bundle.sarif
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
path: ${{ runner.temp }}/results/javascript.sarif
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ steps:
|
|||||||
output: "${{ runner.temp }}/results"
|
output: "${{ runner.temp }}/results"
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: "${{ runner.temp }}/results/javascript.sarif"
|
path: "${{ runner.temp }}/results/javascript.sarif"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ steps:
|
|||||||
output: "${{ runner.temp }}/results"
|
output: "${{ runner.temp }}/results"
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: diagnostics-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: "${{ runner.temp }}/results/javascript.sarif"
|
path: "${{ runner.temp }}/results/javascript.sarif"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ steps:
|
|||||||
with:
|
with:
|
||||||
output: "${{ runner.temp }}/results"
|
output: "${{ runner.temp }}/results"
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: with-baseline-information-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: "${{ runner.temp }}/results/javascript.sarif"
|
path: "${{ runner.temp }}/results/javascript.sarif"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ steps:
|
|||||||
with:
|
with:
|
||||||
output: "${{ runner.temp }}/results"
|
output: "${{ runner.temp }}/results"
|
||||||
- name: Upload SARIF
|
- name: Upload SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: ${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: "${{ runner.temp }}/results/javascript.sarif"
|
path: "${{ runner.temp }}/results/javascript.sarif"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ steps:
|
|||||||
post-processed-sarif-path: "${{ runner.temp }}/post-processed"
|
post-processed-sarif-path: "${{ runner.temp }}/post-processed"
|
||||||
- name: Upload security SARIF
|
- name: Upload security SARIF
|
||||||
if: contains(matrix.analysis-kinds, 'code-scanning')
|
if: contains(matrix.analysis-kinds, 'code-scanning')
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
||||||
@@ -47,14 +47,14 @@ steps:
|
|||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Upload quality SARIF
|
- name: Upload quality SARIF
|
||||||
if: contains(matrix.analysis-kinds, 'code-quality')
|
if: contains(matrix.analysis-kinds, 'code-quality')
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
|
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
|
||||||
path: "${{ runner.temp }}/results/javascript.quality.sarif"
|
path: "${{ runner.temp }}/results/javascript.quality.sarif"
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Upload post-processed SARIF
|
- name: Upload post-processed SARIF
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ description: "Tests using RuboCop to analyze a multi-language repository and the
|
|||||||
versions: ["default"]
|
versions: ["default"]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
|
uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.6
|
ruby-version: 2.6
|
||||||
- name: Install Code Scanning integration
|
- name: Install Code Scanning integration
|
||||||
|
|||||||
@@ -9,15 +9,9 @@ if [ "$GITHUB_ACTIONS" = "true" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if npm install is likely needed before proceeding
|
# Check if npm install is likely needed before proceeding
|
||||||
if [ ! -d node_modules ]; then
|
if [ ! -d node_modules ] || [ package-lock.json -nt node_modules/.package-lock.json ]; then
|
||||||
echo "Running 'npm install' because 'node_modules' directory is missing."
|
echo "Running 'npm install' because 'node_modules/.package-lock.json' appears to be outdated..."
|
||||||
npm install
|
|
||||||
elif [ package.json -nt package-lock.json ]; then
|
|
||||||
echo "Running 'npm install' because 'package-lock.json' appears to be outdated."
|
|
||||||
npm install
|
|
||||||
elif [ package-lock.json -nt node_modules/.package-lock.json ]; then
|
|
||||||
echo "Running 'npm install' because 'node_modules/.package-lock.json' appears to be outdated."
|
|
||||||
npm install
|
npm install
|
||||||
else
|
else
|
||||||
echo "Skipping 'npm install' because everything appears to be up-to-date."
|
echo "Skipping 'npm install' because 'node_modules/.package-lock.json' appears to be up-to-date."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ test("analyze action with RAM & threads from environment variables", async (t) =
|
|||||||
t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1");
|
t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1");
|
||||||
t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=4992");
|
t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=4992");
|
||||||
t.assert(runQueriesStub.calledOnce);
|
t.assert(runQueriesStub.calledOnce);
|
||||||
t.deepEqual(runQueriesStub.firstCall.args[2], "--threads=-1");
|
t.deepEqual(runQueriesStub.firstCall.args[3], "--threads=-1");
|
||||||
t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=4992");
|
t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=4992");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ test("analyze action with RAM & threads from action inputs", async (t) => {
|
|||||||
t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1");
|
t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1");
|
||||||
t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=3012");
|
t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=3012");
|
||||||
t.assert(runQueriesStub.calledOnce);
|
t.assert(runQueriesStub.calledOnce);
|
||||||
t.deepEqual(runQueriesStub.firstCall.args[2], "--threads=-1");
|
t.deepEqual(runQueriesStub.firstCall.args[3], "--threads=-1");
|
||||||
t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=3012");
|
t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=3012");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -324,16 +324,10 @@ async function run() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (actionsUtil.getRequiredInput("skip-queries") !== "true") {
|
if (actionsUtil.getRequiredInput("skip-queries") !== "true") {
|
||||||
// Warn if the removed `add-snippets` input is used.
|
|
||||||
if (actionsUtil.getOptionalInput("add-snippets") !== undefined) {
|
|
||||||
logger.warning(
|
|
||||||
"The `add-snippets` input has been removed and no longer has any effect.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
runStats = await runQueries(
|
runStats = await runQueries(
|
||||||
outputDir,
|
outputDir,
|
||||||
memory,
|
memory,
|
||||||
|
util.getAddSnippetsFlag(actionsUtil.getRequiredInput("add-snippets")),
|
||||||
threads,
|
threads,
|
||||||
diffRangePackDir,
|
diffRangePackDir,
|
||||||
actionsUtil.getOptionalInput("category"),
|
actionsUtil.getOptionalInput("category"),
|
||||||
@@ -438,11 +432,14 @@ async function run() {
|
|||||||
|
|
||||||
// Store dependency cache(s) if dependency caching is enabled.
|
// Store dependency cache(s) if dependency caching is enabled.
|
||||||
if (shouldStoreCache(config.dependencyCachingEnabled)) {
|
if (shouldStoreCache(config.dependencyCachingEnabled)) {
|
||||||
dependencyCacheResults = await uploadDependencyCaches(
|
const minimizeJavaJars = await features.getValue(
|
||||||
|
Feature.JavaMinimizeDependencyJars,
|
||||||
codeql,
|
codeql,
|
||||||
features,
|
);
|
||||||
|
dependencyCacheResults = await uploadDependencyCaches(
|
||||||
config,
|
config,
|
||||||
logger,
|
logger,
|
||||||
|
minimizeJavaJars,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ test("status report fields", async (t) => {
|
|||||||
setupActionsVars(tmpDir, tmpDir);
|
setupActionsVars(tmpDir, tmpDir);
|
||||||
|
|
||||||
const memoryFlag = "";
|
const memoryFlag = "";
|
||||||
|
const addSnippetsFlag = "";
|
||||||
const threadsFlag = "";
|
const threadsFlag = "";
|
||||||
sinon.stub(uploadLib, "validateSarifFileSchema");
|
sinon.stub(uploadLib, "validateSarifFileSchema");
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ test("status report fields", async (t) => {
|
|||||||
const statusReport = await runQueries(
|
const statusReport = await runQueries(
|
||||||
tmpDir,
|
tmpDir,
|
||||||
memoryFlag,
|
memoryFlag,
|
||||||
|
addSnippetsFlag,
|
||||||
threadsFlag,
|
threadsFlag,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import * as path from "path";
|
|||||||
import { performance } from "perf_hooks";
|
import { performance } from "perf_hooks";
|
||||||
|
|
||||||
import * as io from "@actions/io";
|
import * as io from "@actions/io";
|
||||||
|
import * as del from "del";
|
||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
|
|
||||||
import { getTemporaryDirectory, PullRequestBranches } from "./actions-util";
|
import { getTemporaryDirectory, PullRequestBranches } from "./actions-util";
|
||||||
@@ -38,26 +39,89 @@ export class CodeQLAnalysisError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type KnownLanguageKey = keyof typeof KnownLanguage;
|
export interface QueriesStatusReport {
|
||||||
|
|
||||||
type RunQueriesDurationStatusReport = {
|
|
||||||
/**
|
/**
|
||||||
* Time taken in ms to run queries for the language (or undefined if this language was not analyzed).
|
* Time taken in ms to run queries for actions (or undefined if this language was not analyzed).
|
||||||
*
|
*
|
||||||
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
* taken to run _all_ the queries.
|
* taken to run _all_ the queries.
|
||||||
*/
|
*/
|
||||||
[L in KnownLanguageKey as `analyze_builtin_queries_${L}_duration_ms`]?: number;
|
analyze_builtin_queries_actions_duration_ms?: number;
|
||||||
};
|
/**
|
||||||
|
* Time taken in ms to run queries for cpp (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_cpp_duration_ms?: number;
|
||||||
|
/**
|
||||||
|
* Time taken in ms to run queries for csharp (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_csharp_duration_ms?: number;
|
||||||
|
/**
|
||||||
|
* Time taken in ms to run queries for go (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_go_duration_ms?: number;
|
||||||
|
/**
|
||||||
|
* Time taken in ms to run queries for java (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_java_duration_ms?: number;
|
||||||
|
/**
|
||||||
|
* Time taken in ms to run queries for javascript (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_javascript_duration_ms?: number;
|
||||||
|
/**
|
||||||
|
* Time taken in ms to run queries for python (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_python_duration_ms?: number;
|
||||||
|
/**
|
||||||
|
* Time taken in ms to run queries for ruby (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_ruby_duration_ms?: number;
|
||||||
|
/** Time taken in ms to run queries for swift (or undefined if this language was not analyzed).
|
||||||
|
*
|
||||||
|
* The "builtin" designation is now outdated with the move to CLI config parsing: this is the time
|
||||||
|
* taken to run _all_ the queries.
|
||||||
|
*/
|
||||||
|
analyze_builtin_queries_swift_duration_ms?: number;
|
||||||
|
|
||||||
type InterpretResultsDurationStatusReport = {
|
/** Time taken in ms to interpret results for actions (or undefined if this language was not analyzed). */
|
||||||
/** Time taken in ms to interpret results for the language (or undefined if this language was not analyzed). */
|
interpret_results_actions_duration_ms?: number;
|
||||||
[L in KnownLanguageKey as `interpret_results_${L}_duration_ms`]?: number;
|
/** Time taken in ms to interpret results for cpp (or undefined if this language was not analyzed). */
|
||||||
};
|
interpret_results_cpp_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for csharp (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_csharp_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for go (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_go_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for java (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_java_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for javascript (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_javascript_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for python (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_python_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for ruby (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_ruby_duration_ms?: number;
|
||||||
|
/** Time taken in ms to interpret results for swift (or undefined if this language was not analyzed). */
|
||||||
|
interpret_results_swift_duration_ms?: number;
|
||||||
|
|
||||||
export interface QueriesStatusReport
|
|
||||||
extends RunQueriesDurationStatusReport,
|
|
||||||
InterpretResultsDurationStatusReport {
|
|
||||||
/**
|
/**
|
||||||
* Whether the analysis is diff-informed (in the sense that the action generates a diff-range data
|
* Whether the analysis is diff-informed (in the sense that the action generates a diff-range data
|
||||||
* extension for the analysis, regardless of whether the data extension is actually used by queries).
|
* extension for the analysis, regardless of whether the data extension is actually used by queries).
|
||||||
@@ -373,6 +437,7 @@ export function addSarifExtension(
|
|||||||
export async function runQueries(
|
export async function runQueries(
|
||||||
sarifFolder: string,
|
sarifFolder: string,
|
||||||
memoryFlag: string,
|
memoryFlag: string,
|
||||||
|
addSnippetsFlag: string,
|
||||||
threadsFlag: string,
|
threadsFlag: string,
|
||||||
diffRangePackDir: string | undefined,
|
diffRangePackDir: string | undefined,
|
||||||
automationDetailsId: string | undefined,
|
automationDetailsId: string | undefined,
|
||||||
@@ -562,6 +627,7 @@ export async function runQueries(
|
|||||||
databasePath,
|
databasePath,
|
||||||
queries,
|
queries,
|
||||||
sarifFile,
|
sarifFile,
|
||||||
|
addSnippetsFlag,
|
||||||
threadsFlag,
|
threadsFlag,
|
||||||
enableDebugLogging ? "-vv" : "-v",
|
enableDebugLogging ? "-vv" : "-v",
|
||||||
sarifRunPropertyFlag,
|
sarifRunPropertyFlag,
|
||||||
@@ -605,7 +671,7 @@ export async function runFinalize(
|
|||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<DatabaseCreationTimings> {
|
): Promise<DatabaseCreationTimings> {
|
||||||
try {
|
try {
|
||||||
await fs.promises.rm(outputDir, { force: true, recursive: true });
|
await del.deleteAsync(outputDir, { force: true });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error?.code !== "ENOENT") {
|
if (error?.code !== "ENOENT") {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -169,32 +169,4 @@ test("wrapApiConfigurationError correctly wraps specific configuration errors",
|
|||||||
res,
|
res,
|
||||||
new util.ConfigurationError("Resource not accessible by integration"),
|
new util.ConfigurationError("Resource not accessible by integration"),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Enablement errors.
|
|
||||||
const enablementErrorMessages = [
|
|
||||||
"Code Security must be enabled for this repository to use code scanning",
|
|
||||||
"Advanced Security must be enabled for this repository to use code scanning",
|
|
||||||
"Code Scanning is not enabled for this repository. Please enable code scanning in the repository settings.",
|
|
||||||
];
|
|
||||||
const transforms = [
|
|
||||||
(msg: string) => msg,
|
|
||||||
(msg: string) => msg.toLowerCase(),
|
|
||||||
(msg: string) => msg.toLocaleUpperCase(),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const enablementErrorMessage of enablementErrorMessages) {
|
|
||||||
for (const transform of transforms) {
|
|
||||||
const enablementError = new util.HTTPError(
|
|
||||||
transform(enablementErrorMessage),
|
|
||||||
403,
|
|
||||||
);
|
|
||||||
res = api.wrapApiConfigurationError(enablementError);
|
|
||||||
t.deepEqual(
|
|
||||||
res,
|
|
||||||
new util.ConfigurationError(
|
|
||||||
api.getFeatureEnablementError(enablementError.message),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import * as githubUtils from "@actions/github/lib/utils";
|
import * as githubUtils from "@actions/github/lib/utils";
|
||||||
import * as retry from "@octokit/plugin-retry";
|
import * as retry from "@octokit/plugin-retry";
|
||||||
|
import consoleLogLevel from "console-log-level";
|
||||||
|
|
||||||
import { getActionVersion, getRequiredInput } from "./actions-util";
|
import { getActionVersion, getRequiredInput } from "./actions-util";
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
@@ -49,12 +50,7 @@ function createApiClientWithDetails(
|
|||||||
githubUtils.getOctokitOptions(auth, {
|
githubUtils.getOctokitOptions(auth, {
|
||||||
baseUrl: apiDetails.apiURL,
|
baseUrl: apiDetails.apiURL,
|
||||||
userAgent: `CodeQL-Action/${getActionVersion()}`,
|
userAgent: `CodeQL-Action/${getActionVersion()}`,
|
||||||
log: {
|
log: consoleLogLevel({ level: "debug" }),
|
||||||
debug: core.debug,
|
|
||||||
info: core.info,
|
|
||||||
warn: core.warning,
|
|
||||||
error: core.error,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -283,20 +279,6 @@ export async function getRepositoryProperties(repositoryNwo: RepositoryNwo) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEnablementError(msg: string) {
|
|
||||||
return [
|
|
||||||
/Code Security must be enabled/i,
|
|
||||||
/Advanced Security must be enabled/i,
|
|
||||||
/Code Scanning is not enabled/i,
|
|
||||||
].some((pattern) => pattern.test(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Move to `error-messages.ts` after refactoring import order to avoid cycle
|
|
||||||
// since `error-messages.ts` currently depends on this file.
|
|
||||||
export function getFeatureEnablementError(message: string): string {
|
|
||||||
return `Please verify that the necessary features are enabled: ${message}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function wrapApiConfigurationError(e: unknown) {
|
export function wrapApiConfigurationError(e: unknown) {
|
||||||
const httpError = asHTTPError(e);
|
const httpError = asHTTPError(e);
|
||||||
if (httpError !== undefined) {
|
if (httpError !== undefined) {
|
||||||
@@ -318,11 +300,6 @@ export function wrapApiConfigurationError(e: unknown) {
|
|||||||
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
|
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (httpError.status === 403 && isEnablementError(httpError.message)) {
|
|
||||||
return new ConfigurationError(
|
|
||||||
getFeatureEnablementError(httpError.message),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (httpError.status === 429) {
|
if (httpError.status === 429) {
|
||||||
return new ConfigurationError("API rate limit exceeded");
|
return new ConfigurationError("API rate limit exceeded");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import * as crypto from "crypto";
|
|
||||||
|
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
|
|
||||||
import { getOptionalInput, isDefaultSetup } from "./actions-util";
|
import { getOptionalInput, isDefaultSetup } from "./actions-util";
|
||||||
@@ -73,33 +71,6 @@ export function getCachingKind(input: string | undefined): CachingKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The length to which `createCacheKeyHash` truncates hash strings.
|
|
||||||
export const cacheKeyHashLength = 16;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a SHA-256 hash of the cache key components to ensure uniqueness
|
|
||||||
* while keeping the cache key length manageable.
|
|
||||||
*
|
|
||||||
* @param components Object containing all components that should influence cache key uniqueness
|
|
||||||
* @returns A short SHA-256 hash (first 16 characters) of the components
|
|
||||||
*/
|
|
||||||
export function createCacheKeyHash(components: Record<string, any>): string {
|
|
||||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
|
|
||||||
//
|
|
||||||
// "Properties are visited using the same algorithm as Object.keys(), which
|
|
||||||
// has a well-defined order and is stable across implementations. For example,
|
|
||||||
// JSON.stringify on the same object will always produce the same string, and
|
|
||||||
// JSON.parse(JSON.stringify(obj)) would produce an object with the same key
|
|
||||||
// ordering as the original (assuming the object is completely
|
|
||||||
// JSON-serializable)."
|
|
||||||
const componentsJson = JSON.stringify(components);
|
|
||||||
return crypto
|
|
||||||
.createHash("sha256")
|
|
||||||
.update(componentsJson)
|
|
||||||
.digest("hex")
|
|
||||||
.substring(0, cacheKeyHashLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Determines whether dependency caching is enabled. */
|
/** Determines whether dependency caching is enabled. */
|
||||||
export function getDependencyCachingEnabled(): CachingKind {
|
export function getDependencyCachingEnabled(): CachingKind {
|
||||||
// If the workflow specified something always respect that
|
// If the workflow specified something always respect that
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as toolrunner from "@actions/exec/lib/toolrunner";
|
|||||||
import * as io from "@actions/io";
|
import * as io from "@actions/io";
|
||||||
import * as toolcache from "@actions/tool-cache";
|
import * as toolcache from "@actions/tool-cache";
|
||||||
import test, { ExecutionContext } from "ava";
|
import test, { ExecutionContext } from "ava";
|
||||||
|
import * as del from "del";
|
||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
import nock from "nock";
|
import nock from "nock";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
@@ -556,7 +557,7 @@ const injectedConfigMacro = test.macro({
|
|||||||
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
|
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
|
||||||
t.deepEqual(augmentedConfig, expectedConfig);
|
t.deepEqual(augmentedConfig, expectedConfig);
|
||||||
|
|
||||||
await fs.promises.rm(configFile, { force: true });
|
await del.deleteAsync(configFile, { force: true });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1045,7 +1046,7 @@ test("Avoids duplicating --overwrite flag if specified in CODEQL_ACTION_EXTRA_OP
|
|||||||
);
|
);
|
||||||
t.truthy(configArg, "Should have injected a codescanning config");
|
t.truthy(configArg, "Should have injected a codescanning config");
|
||||||
const configFile = configArg!.split("=")[1];
|
const configFile = configArg!.split("=")[1];
|
||||||
await fs.promises.rm(configFile, { force: true });
|
await del.deleteAsync(configFile, { force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
export function stubToolRunnerConstructor(
|
export function stubToolRunnerConstructor(
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ export interface CodeQL {
|
|||||||
databasePath: string,
|
databasePath: string,
|
||||||
querySuitePaths: string[] | undefined,
|
querySuitePaths: string[] | undefined,
|
||||||
sarifFile: string,
|
sarifFile: string,
|
||||||
|
addSnippetsFlag: string,
|
||||||
threadsFlag: string,
|
threadsFlag: string,
|
||||||
verbosityFlag: string | undefined,
|
verbosityFlag: string | undefined,
|
||||||
sarifRunPropertyFlag: string | undefined,
|
sarifRunPropertyFlag: string | undefined,
|
||||||
@@ -816,6 +817,7 @@ export async function getCodeQLForCmd(
|
|||||||
databasePath: string,
|
databasePath: string,
|
||||||
querySuitePaths: string[] | undefined,
|
querySuitePaths: string[] | undefined,
|
||||||
sarifFile: string,
|
sarifFile: string,
|
||||||
|
addSnippetsFlag: string,
|
||||||
threadsFlag: string,
|
threadsFlag: string,
|
||||||
verbosityFlag: string,
|
verbosityFlag: string,
|
||||||
sarifRunPropertyFlag: string | undefined,
|
sarifRunPropertyFlag: string | undefined,
|
||||||
@@ -834,6 +836,7 @@ export async function getCodeQLForCmd(
|
|||||||
"--format=sarif-latest",
|
"--format=sarif-latest",
|
||||||
verbosityFlag,
|
verbosityFlag,
|
||||||
`--output=${sarifFile}`,
|
`--output=${sarifFile}`,
|
||||||
|
addSnippetsFlag,
|
||||||
"--print-diagnostics-summary",
|
"--print-diagnostics-summary",
|
||||||
"--print-metrics-summary",
|
"--print-metrics-summary",
|
||||||
"--sarif-add-baseline-file-info",
|
"--sarif-add-baseline-file-info",
|
||||||
@@ -1071,11 +1074,8 @@ export async function getCodeQLForCmd(
|
|||||||
/**
|
/**
|
||||||
* Gets the options for `path` of `options` as an array of extra option strings.
|
* Gets the options for `path` of `options` as an array of extra option strings.
|
||||||
*
|
*
|
||||||
* @param paths The CLI command components to get extra options for.
|
* @param ignoringOptions Options that should be ignored, for example because they have already
|
||||||
* @param args Additional arguments for this function.
|
* been passed and it is an error to pass them more than once.
|
||||||
* @param args.ignoringOptions
|
|
||||||
* Options that should be ignored, for example because they have already
|
|
||||||
* been passed and it is an error to pass them more than once.
|
|
||||||
*/
|
*/
|
||||||
function getExtraOptionsFromEnv(
|
function getExtraOptionsFromEnv(
|
||||||
paths: string[],
|
paths: string[],
|
||||||
@@ -1157,9 +1157,8 @@ async function runCli(
|
|||||||
/**
|
/**
|
||||||
* Writes the code scanning configuration that is to be used by the CLI.
|
* Writes the code scanning configuration that is to be used by the CLI.
|
||||||
*
|
*
|
||||||
* @param config The CodeQL Action state to write.
|
* @param codeql The CodeQL object to use.
|
||||||
* @param logger The logger to use.
|
* @param config The CodeQL Action state to use.
|
||||||
*
|
|
||||||
* @returns The path to the generated user configuration file.
|
* @returns The path to the generated user configuration file.
|
||||||
*/
|
*/
|
||||||
async function writeCodeScanningConfigFile(
|
async function writeCodeScanningConfigFile(
|
||||||
|
|||||||
@@ -873,62 +873,71 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
|
|||||||
expectedLanguages: ["javascript"],
|
expectedLanguages: ["javascript"],
|
||||||
},
|
},
|
||||||
].forEach((args) => {
|
].forEach((args) => {
|
||||||
test(`getLanguages: ${args.name}`, async (t) => {
|
for (const resolveSupportedLanguagesUsingCli of [true, false]) {
|
||||||
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
test(`getLanguages${resolveSupportedLanguagesUsingCli ? " (supported languages via CLI)" : ""}: ${args.name}`, async (t) => {
|
||||||
const stubExtractorEntry = {
|
const features = createFeatures(
|
||||||
extractor_root: "",
|
resolveSupportedLanguagesUsingCli
|
||||||
};
|
? [Feature.ResolveSupportedLanguagesUsingCli]
|
||||||
const codeQL = createStubCodeQL({
|
: [],
|
||||||
betterResolveLanguages: (options) =>
|
);
|
||||||
Promise.resolve({
|
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
||||||
aliases: {
|
const stubExtractorEntry = {
|
||||||
"c#": KnownLanguage.csharp,
|
extractor_root: "",
|
||||||
c: KnownLanguage.cpp,
|
};
|
||||||
kotlin: KnownLanguage.java,
|
const codeQL = createStubCodeQL({
|
||||||
typescript: KnownLanguage.javascript,
|
betterResolveLanguages: (options) =>
|
||||||
},
|
Promise.resolve({
|
||||||
extractors: {
|
aliases: {
|
||||||
cpp: [stubExtractorEntry],
|
"c#": KnownLanguage.csharp,
|
||||||
csharp: [stubExtractorEntry],
|
c: KnownLanguage.cpp,
|
||||||
java: [stubExtractorEntry],
|
kotlin: KnownLanguage.java,
|
||||||
javascript: [stubExtractorEntry],
|
typescript: KnownLanguage.javascript,
|
||||||
python: [stubExtractorEntry],
|
},
|
||||||
...(options?.filterToLanguagesWithQueries
|
extractors: {
|
||||||
? {}
|
cpp: [stubExtractorEntry],
|
||||||
: {
|
csharp: [stubExtractorEntry],
|
||||||
html: [stubExtractorEntry],
|
java: [stubExtractorEntry],
|
||||||
}),
|
javascript: [stubExtractorEntry],
|
||||||
},
|
python: [stubExtractorEntry],
|
||||||
}),
|
...(options?.filterToLanguagesWithQueries
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
html: [stubExtractorEntry],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (args.expectedLanguages) {
|
||||||
|
// happy path
|
||||||
|
const actualLanguages = await configUtils.getLanguages(
|
||||||
|
codeQL,
|
||||||
|
args.languagesInput,
|
||||||
|
mockRepositoryNwo,
|
||||||
|
".",
|
||||||
|
features,
|
||||||
|
mockLogger,
|
||||||
|
);
|
||||||
|
|
||||||
|
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
|
||||||
|
} else {
|
||||||
|
// there is an error
|
||||||
|
await t.throwsAsync(
|
||||||
|
async () =>
|
||||||
|
await configUtils.getLanguages(
|
||||||
|
codeQL,
|
||||||
|
args.languagesInput,
|
||||||
|
mockRepositoryNwo,
|
||||||
|
".",
|
||||||
|
features,
|
||||||
|
mockLogger,
|
||||||
|
),
|
||||||
|
{ message: args.expectedError },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
t.deepEqual(mockRequest.called, args.expectedApiCall);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
if (args.expectedLanguages) {
|
|
||||||
// happy path
|
|
||||||
const actualLanguages = await configUtils.getLanguages(
|
|
||||||
codeQL,
|
|
||||||
args.languagesInput,
|
|
||||||
mockRepositoryNwo,
|
|
||||||
".",
|
|
||||||
mockLogger,
|
|
||||||
);
|
|
||||||
|
|
||||||
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
|
|
||||||
} else {
|
|
||||||
// there is an error
|
|
||||||
await t.throwsAsync(
|
|
||||||
async () =>
|
|
||||||
await configUtils.getLanguages(
|
|
||||||
codeQL,
|
|
||||||
args.languagesInput,
|
|
||||||
mockRepositoryNwo,
|
|
||||||
".",
|
|
||||||
mockLogger,
|
|
||||||
),
|
|
||||||
{ message: args.expectedError },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
t.deepEqual(mockRequest.called, args.expectedApiCall);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const { displayName, language, feature } of [
|
for (const { displayName, language, feature } of [
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import {
|
|||||||
OverlayDatabaseMode,
|
OverlayDatabaseMode,
|
||||||
} from "./overlay-database-utils";
|
} from "./overlay-database-utils";
|
||||||
import { RepositoryNwo } from "./repository";
|
import { RepositoryNwo } from "./repository";
|
||||||
import { ToolsFeature } from "./tools-features";
|
|
||||||
import { downloadTrapCaches } from "./trap-caching";
|
import { downloadTrapCaches } from "./trap-caching";
|
||||||
import {
|
import {
|
||||||
GitHubVersion,
|
GitHubVersion,
|
||||||
@@ -178,10 +177,12 @@ export interface Config {
|
|||||||
|
|
||||||
export async function getSupportedLanguageMap(
|
export async function getSupportedLanguageMap(
|
||||||
codeql: CodeQL,
|
codeql: CodeQL,
|
||||||
|
features: FeatureEnablement,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<Record<string, string>> {
|
): Promise<Record<string, string>> {
|
||||||
const resolveSupportedLanguagesUsingCli = await codeql.supportsFeature(
|
const resolveSupportedLanguagesUsingCli = await features.getValue(
|
||||||
ToolsFeature.BuiltinExtractorsSpecifyDefaultQueries,
|
Feature.ResolveSupportedLanguagesUsingCli,
|
||||||
|
codeql,
|
||||||
);
|
);
|
||||||
const resolveResult = await codeql.betterResolveLanguages({
|
const resolveResult = await codeql.betterResolveLanguages({
|
||||||
filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli,
|
filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli,
|
||||||
@@ -282,6 +283,7 @@ export async function getLanguages(
|
|||||||
languagesInput: string | undefined,
|
languagesInput: string | undefined,
|
||||||
repository: RepositoryNwo,
|
repository: RepositoryNwo,
|
||||||
sourceRoot: string,
|
sourceRoot: string,
|
||||||
|
features: FeatureEnablement,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<Language[]> {
|
): Promise<Language[]> {
|
||||||
// Obtain languages without filtering them.
|
// Obtain languages without filtering them.
|
||||||
@@ -292,7 +294,7 @@ export async function getLanguages(
|
|||||||
logger,
|
logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
const languageMap = await getSupportedLanguageMap(codeql, logger);
|
const languageMap = await getSupportedLanguageMap(codeql, features, logger);
|
||||||
const languagesSet = new Set<Language>();
|
const languagesSet = new Set<Language>();
|
||||||
const unknownLanguages: string[] = [];
|
const unknownLanguages: string[] = [];
|
||||||
|
|
||||||
@@ -429,6 +431,7 @@ export async function initActionState(
|
|||||||
languagesInput,
|
languagesInput,
|
||||||
repository,
|
repository,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
|
features,
|
||||||
logger,
|
logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1033,6 +1036,7 @@ export async function getConfig(
|
|||||||
* pack.
|
* pack.
|
||||||
*
|
*
|
||||||
* @param registriesInput The value of the `registries` input.
|
* @param registriesInput The value of the `registries` input.
|
||||||
|
* @param codeQL a codeQL object, used only for checking the version of CodeQL.
|
||||||
* @param tempDir a temporary directory to store the generated qlconfig.yml file.
|
* @param tempDir a temporary directory to store the generated qlconfig.yml file.
|
||||||
* @param logger a logger object.
|
* @param logger a logger object.
|
||||||
* @returns The path to the generated `qlconfig.yml` file and the auth tokens to
|
* @returns The path to the generated `qlconfig.yml` file and the auth tokens to
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ const PACK_IDENTIFIER_PATTERN = (function () {
|
|||||||
* Version and path are optional.
|
* Version and path are optional.
|
||||||
*
|
*
|
||||||
* @param packStr the package specification to verify.
|
* @param packStr the package specification to verify.
|
||||||
|
* @param configFile Config file to use for error reporting
|
||||||
*/
|
*/
|
||||||
export function parsePacksSpecification(packStr: string): Pack {
|
export function parsePacksSpecification(packStr: string): Pack {
|
||||||
if (typeof packStr !== "string") {
|
if (typeof packStr !== "string") {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as artifact from "@actions/artifact";
|
|||||||
import * as artifactLegacy from "@actions/artifact-legacy";
|
import * as artifactLegacy from "@actions/artifact-legacy";
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import archiver from "archiver";
|
import archiver from "archiver";
|
||||||
|
import * as del from "del";
|
||||||
|
|
||||||
import { getOptionalInput, getTemporaryDirectory } from "./actions-util";
|
import { getOptionalInput, getTemporaryDirectory } from "./actions-util";
|
||||||
import { dbIsFinalized } from "./analyze";
|
import { dbIsFinalized } from "./analyze";
|
||||||
@@ -344,7 +345,7 @@ async function createPartialDatabaseBundle(
|
|||||||
);
|
);
|
||||||
// See `bundleDb` for explanation behind deleting existing db bundle.
|
// See `bundleDb` for explanation behind deleting existing db bundle.
|
||||||
if (fs.existsSync(databaseBundlePath)) {
|
if (fs.existsSync(databaseBundlePath)) {
|
||||||
await fs.promises.rm(databaseBundlePath, { force: true });
|
await del.deleteAsync(databaseBundlePath, { force: true });
|
||||||
}
|
}
|
||||||
const output = fs.createWriteStream(databaseBundlePath);
|
const output = fs.createWriteStream(databaseBundlePath);
|
||||||
const zip = archiver("zip");
|
const zip = archiver("zip");
|
||||||
|
|||||||
@@ -1,389 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
import * as actionsCache from "@actions/cache";
|
|
||||||
import * as glob from "@actions/glob";
|
|
||||||
import test from "ava";
|
|
||||||
import * as sinon from "sinon";
|
|
||||||
|
|
||||||
import { cacheKeyHashLength } from "./caching-utils";
|
|
||||||
import { createStubCodeQL } from "./codeql";
|
|
||||||
import {
|
|
||||||
CacheConfig,
|
|
||||||
checkHashPatterns,
|
|
||||||
getCsharpHashPatterns,
|
|
||||||
getFeaturePrefix,
|
|
||||||
makePatternCheck,
|
|
||||||
internal,
|
|
||||||
CSHARP_BASE_PATTERNS,
|
|
||||||
CSHARP_EXTRA_PATTERNS,
|
|
||||||
downloadDependencyCaches,
|
|
||||||
CacheHitKind,
|
|
||||||
cacheKey,
|
|
||||||
} from "./dependency-caching";
|
|
||||||
import { Feature } from "./feature-flags";
|
|
||||||
import { KnownLanguage } from "./languages";
|
|
||||||
import {
|
|
||||||
setupTests,
|
|
||||||
createFeatures,
|
|
||||||
getRecordingLogger,
|
|
||||||
checkExpectedLogMessages,
|
|
||||||
LoggedMessage,
|
|
||||||
} from "./testing-utils";
|
|
||||||
import { withTmpDir } from "./util";
|
|
||||||
|
|
||||||
setupTests(test);
|
|
||||||
|
|
||||||
function makeAbsolutePatterns(tmpDir: string, patterns: string[]): string[] {
|
|
||||||
return patterns.map((pattern) => path.join(tmpDir, pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
test("makePatternCheck - returns undefined if no patterns match", async (t) => {
|
|
||||||
await withTmpDir(async (tmpDir) => {
|
|
||||||
fs.writeFileSync(path.join(tmpDir, "test.java"), "");
|
|
||||||
const result = await makePatternCheck(
|
|
||||||
makeAbsolutePatterns(tmpDir, ["**/*.cs"]),
|
|
||||||
);
|
|
||||||
t.is(result, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("makePatternCheck - returns all patterns if any pattern matches", async (t) => {
|
|
||||||
await withTmpDir(async (tmpDir) => {
|
|
||||||
fs.writeFileSync(path.join(tmpDir, "test.java"), "");
|
|
||||||
const patterns = makeAbsolutePatterns(tmpDir, ["**/*.cs", "**/*.java"]);
|
|
||||||
const result = await makePatternCheck(patterns);
|
|
||||||
t.deepEqual(result, patterns);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getCsharpHashPatterns - returns base patterns if any pattern matches", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([]);
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_BASE_PATTERNS)
|
|
||||||
.resolves(CSHARP_BASE_PATTERNS);
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).rejects();
|
|
||||||
|
|
||||||
await t.notThrowsAsync(async () => {
|
|
||||||
const result = await getCsharpHashPatterns(codeql, features);
|
|
||||||
t.deepEqual(result, CSHARP_BASE_PATTERNS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getCsharpHashPatterns - returns base patterns if any base pattern matches and CsharpNewCacheKey is enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_BASE_PATTERNS)
|
|
||||||
.resolves(CSHARP_BASE_PATTERNS);
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_EXTRA_PATTERNS)
|
|
||||||
.resolves(CSHARP_EXTRA_PATTERNS);
|
|
||||||
|
|
||||||
await t.notThrowsAsync(async () => {
|
|
||||||
const result = await getCsharpHashPatterns(codeql, features);
|
|
||||||
t.deepEqual(result, CSHARP_BASE_PATTERNS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getCsharpHashPatterns - returns extra patterns if any extra pattern matches and CsharpNewCacheKey is enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_BASE_PATTERNS).resolves(undefined);
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_EXTRA_PATTERNS)
|
|
||||||
.resolves(CSHARP_EXTRA_PATTERNS);
|
|
||||||
|
|
||||||
await t.notThrowsAsync(async () => {
|
|
||||||
const result = await getCsharpHashPatterns(codeql, features);
|
|
||||||
t.deepEqual(result, CSHARP_EXTRA_PATTERNS);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getCsharpHashPatterns - returns undefined if neither base nor extra patterns match", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_BASE_PATTERNS).resolves(undefined);
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).resolves(undefined);
|
|
||||||
|
|
||||||
await t.notThrowsAsync(async () => {
|
|
||||||
const result = await getCsharpHashPatterns(codeql, features);
|
|
||||||
t.deepEqual(result, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("checkHashPatterns - logs when no patterns match", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([]);
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const config: CacheConfig = {
|
|
||||||
getDependencyPaths: () => [],
|
|
||||||
getHashPatterns: async () => undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await checkHashPatterns(
|
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
KnownLanguage.csharp,
|
|
||||||
config,
|
|
||||||
"download",
|
|
||||||
getRecordingLogger(messages),
|
|
||||||
);
|
|
||||||
|
|
||||||
t.is(result, undefined);
|
|
||||||
checkExpectedLogMessages(t, messages, [
|
|
||||||
"Skipping download of dependency cache",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("checkHashPatterns - returns patterns when patterns match", async (t) => {
|
|
||||||
await withTmpDir(async (tmpDir) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([]);
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const patterns = makeAbsolutePatterns(tmpDir, ["**/*.cs", "**/*.java"]);
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(tmpDir, "test.java"), "");
|
|
||||||
|
|
||||||
const config: CacheConfig = {
|
|
||||||
getDependencyPaths: () => [],
|
|
||||||
getHashPatterns: async () => makePatternCheck(patterns),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await checkHashPatterns(
|
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
KnownLanguage.csharp,
|
|
||||||
config,
|
|
||||||
"upload",
|
|
||||||
getRecordingLogger(messages),
|
|
||||||
);
|
|
||||||
|
|
||||||
t.deepEqual(result, patterns);
|
|
||||||
t.deepEqual(messages, []);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
type RestoreCacheFunc = (
|
|
||||||
paths: string[],
|
|
||||||
primaryKey: string,
|
|
||||||
restoreKeys: string[] | undefined,
|
|
||||||
) => Promise<string | undefined>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a function that `actionsCache.restoreCache` can be stubbed with.
|
|
||||||
*
|
|
||||||
* @param mockCacheKeys The keys of caches that we want to exist in the Actions cache.
|
|
||||||
*
|
|
||||||
* @returns Returns a function that `actionsCache.restoreCache` can be stubbed with.
|
|
||||||
*/
|
|
||||||
function makeMockCacheCheck(mockCacheKeys: string[]): RestoreCacheFunc {
|
|
||||||
return async (
|
|
||||||
_paths: string[],
|
|
||||||
primaryKey: string,
|
|
||||||
restoreKeys: string[] | undefined,
|
|
||||||
) => {
|
|
||||||
// The behaviour here mirrors what the real `restoreCache` would do:
|
|
||||||
// - Starting with the primary restore key, check all caches for a match:
|
|
||||||
// even for the primary restore key, this only has to be a prefix match.
|
|
||||||
// - If the primary restore key doesn't prefix-match any cache, then proceed
|
|
||||||
// in the same way for each restore key in turn.
|
|
||||||
for (const restoreKey of [primaryKey, ...(restoreKeys || [])]) {
|
|
||||||
for (const mockCacheKey of mockCacheKeys) {
|
|
||||||
if (mockCacheKey.startsWith(restoreKey)) {
|
|
||||||
return mockCacheKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only if no restore key matches any cache key prefix, there is no matching
|
|
||||||
// cache and we return `undefined`.
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test("downloadDependencyCaches - does not restore caches with feature keys if no features are enabled", async (t) => {
|
|
||||||
process.env["RUNNER_OS"] = "Linux";
|
|
||||||
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const logger = getRecordingLogger(messages);
|
|
||||||
|
|
||||||
sinon.stub(glob, "hashFiles").resolves("abcdef");
|
|
||||||
|
|
||||||
const keyWithFeature = await cacheKey(
|
|
||||||
codeql,
|
|
||||||
createFeatures([Feature.CsharpNewCacheKey]),
|
|
||||||
KnownLanguage.csharp,
|
|
||||||
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const restoreCacheStub = sinon
|
|
||||||
.stub(actionsCache, "restoreCache")
|
|
||||||
.callsFake(makeMockCacheCheck([keyWithFeature]));
|
|
||||||
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_BASE_PATTERNS)
|
|
||||||
.resolves(CSHARP_BASE_PATTERNS);
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).resolves(undefined);
|
|
||||||
|
|
||||||
const results = await downloadDependencyCaches(
|
|
||||||
codeql,
|
|
||||||
createFeatures([]),
|
|
||||||
[KnownLanguage.csharp],
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
t.is(results.length, 1);
|
|
||||||
t.is(results[0].language, KnownLanguage.csharp);
|
|
||||||
t.is(results[0].hit_kind, CacheHitKind.Miss);
|
|
||||||
t.assert(restoreCacheStub.calledOnce);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("downloadDependencyCaches - restores caches with feature keys if features are enabled", async (t) => {
|
|
||||||
process.env["RUNNER_OS"] = "Linux";
|
|
||||||
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const logger = getRecordingLogger(messages);
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
|
|
||||||
sinon.stub(glob, "hashFiles").resolves("abcdef");
|
|
||||||
|
|
||||||
const keyWithFeature = await cacheKey(
|
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
KnownLanguage.csharp,
|
|
||||||
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const restoreCacheStub = sinon
|
|
||||||
.stub(actionsCache, "restoreCache")
|
|
||||||
.callsFake(makeMockCacheCheck([keyWithFeature]));
|
|
||||||
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_BASE_PATTERNS)
|
|
||||||
.resolves(CSHARP_BASE_PATTERNS);
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).resolves(undefined);
|
|
||||||
|
|
||||||
const results = await downloadDependencyCaches(
|
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
[KnownLanguage.csharp],
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
t.is(results.length, 1);
|
|
||||||
t.is(results[0].language, KnownLanguage.csharp);
|
|
||||||
t.is(results[0].hit_kind, CacheHitKind.Exact);
|
|
||||||
t.assert(restoreCacheStub.calledOnce);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("downloadDependencyCaches - restores caches with feature keys if features are enabled for partial matches", async (t) => {
|
|
||||||
process.env["RUNNER_OS"] = "Linux";
|
|
||||||
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const logger = getRecordingLogger(messages);
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
|
|
||||||
const hashFilesStub = sinon.stub(glob, "hashFiles");
|
|
||||||
hashFilesStub.onFirstCall().resolves("abcdef");
|
|
||||||
hashFilesStub.onSecondCall().resolves("123456");
|
|
||||||
|
|
||||||
const keyWithFeature = await cacheKey(
|
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
KnownLanguage.csharp,
|
|
||||||
// Patterns don't matter here because we have stubbed `hashFiles` to always return a specific hash above.
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const restoreCacheStub = sinon
|
|
||||||
.stub(actionsCache, "restoreCache")
|
|
||||||
.callsFake(makeMockCacheCheck([keyWithFeature]));
|
|
||||||
|
|
||||||
const makePatternCheckStub = sinon.stub(internal, "makePatternCheck");
|
|
||||||
makePatternCheckStub
|
|
||||||
.withArgs(CSHARP_BASE_PATTERNS)
|
|
||||||
.resolves(CSHARP_BASE_PATTERNS);
|
|
||||||
makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).resolves(undefined);
|
|
||||||
|
|
||||||
const results = await downloadDependencyCaches(
|
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
[KnownLanguage.csharp],
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
t.is(results.length, 1);
|
|
||||||
t.is(results[0].language, KnownLanguage.csharp);
|
|
||||||
t.is(results[0].hit_kind, CacheHitKind.Partial);
|
|
||||||
t.assert(restoreCacheStub.calledOnce);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getFeaturePrefix - returns empty string if no features are enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([]);
|
|
||||||
|
|
||||||
for (const knownLanguage of Object.values(KnownLanguage)) {
|
|
||||||
const result = await getFeaturePrefix(codeql, features, knownLanguage);
|
|
||||||
t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getFeaturePrefix - Java - returns 'minify-' if JavaMinimizeDependencyJars is enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.JavaMinimizeDependencyJars]);
|
|
||||||
|
|
||||||
const result = await getFeaturePrefix(codeql, features, KnownLanguage.java);
|
|
||||||
t.deepEqual(result, "minify-");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getFeaturePrefix - non-Java - returns '' if JavaMinimizeDependencyJars is enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.JavaMinimizeDependencyJars]);
|
|
||||||
|
|
||||||
for (const knownLanguage of Object.values(KnownLanguage)) {
|
|
||||||
// Skip Java since we expect a result for it, which is tested in the previous test.
|
|
||||||
if (knownLanguage === KnownLanguage.java) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const result = await getFeaturePrefix(codeql, features, knownLanguage);
|
|
||||||
t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getFeaturePrefix - C# - returns prefix if CsharpNewCacheKey is enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
|
|
||||||
const result = await getFeaturePrefix(codeql, features, KnownLanguage.csharp);
|
|
||||||
t.notDeepEqual(result, "");
|
|
||||||
t.assert(result.endsWith("-"));
|
|
||||||
// Check the length of the prefix, which should correspond to `cacheKeyHashLength` + 1 for the trailing `-`.
|
|
||||||
t.is(result.length, cacheKeyHashLength + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getFeaturePrefix - non-C# - returns '' if CsharpNewCacheKey is enabled", async (t) => {
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
const features = createFeatures([Feature.CsharpNewCacheKey]);
|
|
||||||
|
|
||||||
for (const knownLanguage of Object.values(KnownLanguage)) {
|
|
||||||
// Skip C# since we expect a result for it, which is tested in the previous test.
|
|
||||||
if (knownLanguage === KnownLanguage.csharp) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const result = await getFeaturePrefix(codeql, features, knownLanguage);
|
|
||||||
t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -6,11 +6,9 @@ import * as glob from "@actions/glob";
|
|||||||
|
|
||||||
import { getTemporaryDirectory } from "./actions-util";
|
import { getTemporaryDirectory } from "./actions-util";
|
||||||
import { listActionsCaches } from "./api-client";
|
import { listActionsCaches } from "./api-client";
|
||||||
import { createCacheKeyHash, getTotalCacheSize } from "./caching-utils";
|
import { getTotalCacheSize } from "./caching-utils";
|
||||||
import { CodeQL } from "./codeql";
|
|
||||||
import { Config } from "./config-utils";
|
import { Config } from "./config-utils";
|
||||||
import { EnvVar } from "./environment";
|
import { EnvVar } from "./environment";
|
||||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
|
||||||
import { KnownLanguage, Language } from "./languages";
|
import { KnownLanguage, Language } from "./languages";
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
import { getErrorMessage, getRequiredEnvParam } from "./util";
|
import { getErrorMessage, getRequiredEnvParam } from "./util";
|
||||||
@@ -18,21 +16,15 @@ import { getErrorMessage, getRequiredEnvParam } from "./util";
|
|||||||
/**
|
/**
|
||||||
* Caching configuration for a particular language.
|
* Caching configuration for a particular language.
|
||||||
*/
|
*/
|
||||||
export interface CacheConfig {
|
interface CacheConfig {
|
||||||
/** Gets the paths of directories on the runner that should be included in the cache. */
|
/** The paths of directories on the runner that should be included in the cache. */
|
||||||
getDependencyPaths: () => string[];
|
paths: string[];
|
||||||
/**
|
/**
|
||||||
* Gets an array of glob patterns for the paths of files whose contents affect which dependencies are used
|
* Patterns for the paths of files whose contents affect which dependencies are used
|
||||||
* by a project. This function also checks whether there are any matching files and returns
|
* by a project. We find all files which match these patterns, calculate a hash for
|
||||||
* `undefined` if no files match.
|
* their contents, and use that hash as part of the cache key.
|
||||||
*
|
|
||||||
* The glob patterns are intended to be used for cache keys, where we find all files which match these
|
|
||||||
* patterns, calculate a hash for their contents, and use that hash as part of the cache key.
|
|
||||||
*/
|
*/
|
||||||
getHashPatterns: (
|
hash: string[];
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
) => Promise<string[] | undefined>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CODEQL_DEPENDENCY_CACHE_PREFIX = "codeql-dependencies";
|
const CODEQL_DEPENDENCY_CACHE_PREFIX = "codeql-dependencies";
|
||||||
@@ -47,105 +39,21 @@ export function getJavaTempDependencyDir(): string {
|
|||||||
return join(getTemporaryDirectory(), "codeql_java", "repository");
|
return join(getTemporaryDirectory(), "codeql_java", "repository");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of paths of directories on the runner that should be included in a dependency cache
|
|
||||||
* for a Java analysis. It is important that this is a function, because we call `getTemporaryDirectory`
|
|
||||||
* which would otherwise fail in tests if we haven't had a chance to initialise `RUNNER_TEMP`.
|
|
||||||
*
|
|
||||||
* @returns The paths of directories on the runner that should be included in a dependency cache
|
|
||||||
* for a Java analysis.
|
|
||||||
*/
|
|
||||||
export function getJavaDependencyDirs(): string[] {
|
|
||||||
return [
|
|
||||||
// Maven
|
|
||||||
join(os.homedir(), ".m2", "repository"),
|
|
||||||
// Gradle
|
|
||||||
join(os.homedir(), ".gradle", "caches"),
|
|
||||||
// CodeQL Java build-mode: none
|
|
||||||
getJavaTempDependencyDir(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that there are files which match `patterns`. If there are matching files for any of the patterns,
|
|
||||||
* this function returns all `patterns`. Otherwise, `undefined` is returned.
|
|
||||||
*
|
|
||||||
* @param patterns The glob patterns to find matching files for.
|
|
||||||
* @returns The array of glob patterns if there are matching files, or `undefined` otherwise.
|
|
||||||
*/
|
|
||||||
export async function makePatternCheck(
|
|
||||||
patterns: string[],
|
|
||||||
): Promise<string[] | undefined> {
|
|
||||||
const globber = await makeGlobber(patterns);
|
|
||||||
|
|
||||||
if ((await globber.glob()).length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** These files contain accurate information about dependencies, including the exact versions
|
|
||||||
* that the relevant package manager has determined for the project. Using these gives us
|
|
||||||
* stable hashes unless the dependencies change.
|
|
||||||
*/
|
|
||||||
export const CSHARP_BASE_PATTERNS = [
|
|
||||||
// NuGet
|
|
||||||
"**/packages.lock.json",
|
|
||||||
// Paket
|
|
||||||
"**/paket.lock",
|
|
||||||
];
|
|
||||||
|
|
||||||
/** These are less accurate for use in cache key calculations, because they:
|
|
||||||
*
|
|
||||||
* - Don't contain the exact versions used. They may only contain version ranges or none at all.
|
|
||||||
* - They contain information unrelated to dependencies, which we don't care about.
|
|
||||||
*
|
|
||||||
* As a result, the hash we compute from these files may change, even if
|
|
||||||
* the dependencies haven't changed.
|
|
||||||
*/
|
|
||||||
export const CSHARP_EXTRA_PATTERNS = [
|
|
||||||
"**/*.csproj",
|
|
||||||
"**/packages.config",
|
|
||||||
"**/nuget.config",
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the list of glob patterns that should be used to calculate the cache key hash
|
|
||||||
* for a C# dependency cache. This will try to use `CSHARP_BASE_PATTERNS` whenever possible.
|
|
||||||
* As a fallback, it will also use `CSHARP_EXTRA_PATTERNS` if the corresponding FF is enabled.
|
|
||||||
*
|
|
||||||
* @param codeql The CodeQL instance to use.
|
|
||||||
* @param features Information about which FFs are enabled.
|
|
||||||
* @returns A list of glob patterns to use for hashing.
|
|
||||||
*/
|
|
||||||
export async function getCsharpHashPatterns(
|
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
): Promise<string[] | undefined> {
|
|
||||||
const basePatterns = await internal.makePatternCheck(CSHARP_BASE_PATTERNS);
|
|
||||||
|
|
||||||
if (basePatterns !== undefined) {
|
|
||||||
return basePatterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await features.getValue(Feature.CsharpNewCacheKey, codeql)) {
|
|
||||||
return internal.makePatternCheck(CSHARP_EXTRA_PATTERNS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get to this point, we didn't find any files with `CSHARP_BASE_PATTERNS`,
|
|
||||||
// and `Feature.CsharpNewCacheKey` is not enabled.
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default caching configurations per language.
|
* Default caching configurations per language.
|
||||||
*/
|
*/
|
||||||
const defaultCacheConfigs: { [language: string]: CacheConfig } = {
|
function getDefaultCacheConfig(): { [language: string]: CacheConfig } {
|
||||||
java: {
|
return {
|
||||||
getDependencyPaths: getJavaDependencyDirs,
|
java: {
|
||||||
getHashPatterns: async () =>
|
paths: [
|
||||||
internal.makePatternCheck([
|
// Maven
|
||||||
|
join(os.homedir(), ".m2", "repository"),
|
||||||
|
// Gradle
|
||||||
|
join(os.homedir(), ".gradle", "caches"),
|
||||||
|
// CodeQL Java build-mode: none
|
||||||
|
getJavaTempDependencyDir(),
|
||||||
|
],
|
||||||
|
hash: [
|
||||||
// Maven
|
// Maven
|
||||||
"**/pom.xml",
|
"**/pom.xml",
|
||||||
// Gradle
|
// Gradle
|
||||||
@@ -155,17 +63,23 @@ const defaultCacheConfigs: { [language: string]: CacheConfig } = {
|
|||||||
"buildSrc/**/Dependencies.kt",
|
"buildSrc/**/Dependencies.kt",
|
||||||
"gradle/*.versions.toml",
|
"gradle/*.versions.toml",
|
||||||
"**/versions.properties",
|
"**/versions.properties",
|
||||||
]),
|
],
|
||||||
},
|
},
|
||||||
csharp: {
|
csharp: {
|
||||||
getDependencyPaths: () => [join(os.homedir(), ".nuget", "packages")],
|
paths: [join(os.homedir(), ".nuget", "packages")],
|
||||||
getHashPatterns: getCsharpHashPatterns,
|
hash: [
|
||||||
},
|
// NuGet
|
||||||
go: {
|
"**/packages.lock.json",
|
||||||
getDependencyPaths: () => [join(os.homedir(), "go", "pkg", "mod")],
|
// Paket
|
||||||
getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]),
|
"**/paket.lock",
|
||||||
},
|
],
|
||||||
};
|
},
|
||||||
|
go: {
|
||||||
|
paths: [join(os.homedir(), "go", "pkg", "mod")],
|
||||||
|
hash: ["**/go.sum"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function makeGlobber(patterns: string[]): Promise<glob.Globber> {
|
async function makeGlobber(patterns: string[]): Promise<glob.Globber> {
|
||||||
return glob.create(patterns.join("\n"));
|
return glob.create(patterns.join("\n"));
|
||||||
@@ -193,57 +107,23 @@ export interface DependencyCacheRestoreStatus {
|
|||||||
/** An array of `DependencyCacheRestoreStatus` objects for each analysed language with a caching configuration. */
|
/** An array of `DependencyCacheRestoreStatus` objects for each analysed language with a caching configuration. */
|
||||||
export type DependencyCacheRestoreStatusReport = DependencyCacheRestoreStatus[];
|
export type DependencyCacheRestoreStatusReport = DependencyCacheRestoreStatus[];
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around `cacheConfig.getHashPatterns` which logs when there are no files to calculate
|
|
||||||
* a hash for the cache key from.
|
|
||||||
*
|
|
||||||
* @param codeql The CodeQL instance to use.
|
|
||||||
* @param features Information about which FFs are enabled.
|
|
||||||
* @param language The language the `CacheConfig` is for. For use in the log message.
|
|
||||||
* @param cacheConfig The caching configuration to call `getHashPatterns` on.
|
|
||||||
* @param checkType Whether we are checking the patterns for a download or upload.
|
|
||||||
* @param logger The logger to write the log message to if there is an error.
|
|
||||||
* @returns An array of glob patterns to use for hashing files, or `undefined` if there are no matching files.
|
|
||||||
*/
|
|
||||||
export async function checkHashPatterns(
|
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
language: Language,
|
|
||||||
cacheConfig: CacheConfig,
|
|
||||||
checkType: "download" | "upload",
|
|
||||||
logger: Logger,
|
|
||||||
): Promise<string[] | undefined> {
|
|
||||||
const patterns = await cacheConfig.getHashPatterns(codeql, features);
|
|
||||||
|
|
||||||
if (patterns === undefined) {
|
|
||||||
logger.info(
|
|
||||||
`Skipping ${checkType} of dependency cache for ${language} as we cannot calculate a hash for the cache key.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to restore dependency caches for the languages being analyzed.
|
* Attempts to restore dependency caches for the languages being analyzed.
|
||||||
*
|
*
|
||||||
* @param codeql The CodeQL instance to use.
|
|
||||||
* @param features Information about which FFs are enabled.
|
|
||||||
* @param languages The languages being analyzed.
|
* @param languages The languages being analyzed.
|
||||||
* @param logger A logger to record some informational messages to.
|
* @param logger A logger to record some informational messages to.
|
||||||
*
|
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
|
||||||
* @returns An array of `DependencyCacheRestoreStatus` objects for each analysed language with a caching configuration.
|
* @returns An array of `DependencyCacheRestoreStatus` objects for each analysed language with a caching configuration.
|
||||||
*/
|
*/
|
||||||
export async function downloadDependencyCaches(
|
export async function downloadDependencyCaches(
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
languages: Language[],
|
languages: Language[],
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
minimizeJavaJars: boolean,
|
||||||
): Promise<DependencyCacheRestoreStatusReport> {
|
): Promise<DependencyCacheRestoreStatusReport> {
|
||||||
const status: DependencyCacheRestoreStatusReport = [];
|
const status: DependencyCacheRestoreStatusReport = [];
|
||||||
|
|
||||||
for (const language of languages) {
|
for (const language of languages) {
|
||||||
const cacheConfig = defaultCacheConfigs[language];
|
const cacheConfig = getDefaultCacheConfig()[language];
|
||||||
|
|
||||||
if (cacheConfig === undefined) {
|
if (cacheConfig === undefined) {
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -254,22 +134,19 @@ export async function downloadDependencyCaches(
|
|||||||
|
|
||||||
// Check that we can find files to calculate the hash for the cache key from, so we don't end up
|
// Check that we can find files to calculate the hash for the cache key from, so we don't end up
|
||||||
// with an empty string.
|
// with an empty string.
|
||||||
const patterns = await checkHashPatterns(
|
const globber = await makeGlobber(cacheConfig.hash);
|
||||||
codeql,
|
|
||||||
features,
|
if ((await globber.glob()).length === 0) {
|
||||||
language,
|
|
||||||
cacheConfig,
|
|
||||||
"download",
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
if (patterns === undefined) {
|
|
||||||
status.push({ language, hit_kind: CacheHitKind.NoHash });
|
status.push({ language, hit_kind: CacheHitKind.NoHash });
|
||||||
|
logger.info(
|
||||||
|
`Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryKey = await cacheKey(codeql, features, language, patterns);
|
const primaryKey = await cacheKey(language, cacheConfig, minimizeJavaJars);
|
||||||
const restoreKeys: string[] = [
|
const restoreKeys: string[] = [
|
||||||
await cachePrefix(codeql, features, language),
|
await cachePrefix(language, minimizeJavaJars),
|
||||||
];
|
];
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -280,7 +157,7 @@ export async function downloadDependencyCaches(
|
|||||||
|
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
const hitKey = await actionsCache.restoreCache(
|
const hitKey = await actionsCache.restoreCache(
|
||||||
cacheConfig.getDependencyPaths(),
|
cacheConfig.paths,
|
||||||
primaryKey,
|
primaryKey,
|
||||||
restoreKeys,
|
restoreKeys,
|
||||||
);
|
);
|
||||||
@@ -326,22 +203,20 @@ export type DependencyCacheUploadStatusReport = DependencyCacheUploadStatus[];
|
|||||||
/**
|
/**
|
||||||
* Attempts to store caches for the languages that were analyzed.
|
* Attempts to store caches for the languages that were analyzed.
|
||||||
*
|
*
|
||||||
* @param codeql The CodeQL instance to use.
|
|
||||||
* @param features Information about which FFs are enabled.
|
|
||||||
* @param config The configuration for this workflow.
|
* @param config The configuration for this workflow.
|
||||||
* @param logger A logger to record some informational messages to.
|
* @param logger A logger to record some informational messages to.
|
||||||
|
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
|
||||||
*
|
*
|
||||||
* @returns An array of `DependencyCacheUploadStatus` objects for each analysed language with a caching configuration.
|
* @returns An array of `DependencyCacheUploadStatus` objects for each analysed language with a caching configuration.
|
||||||
*/
|
*/
|
||||||
export async function uploadDependencyCaches(
|
export async function uploadDependencyCaches(
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
config: Config,
|
config: Config,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
minimizeJavaJars: boolean,
|
||||||
): Promise<DependencyCacheUploadStatusReport> {
|
): Promise<DependencyCacheUploadStatusReport> {
|
||||||
const status: DependencyCacheUploadStatusReport = [];
|
const status: DependencyCacheUploadStatusReport = [];
|
||||||
for (const language of config.languages) {
|
for (const language of config.languages) {
|
||||||
const cacheConfig = defaultCacheConfigs[language];
|
const cacheConfig = getDefaultCacheConfig()[language];
|
||||||
|
|
||||||
if (cacheConfig === undefined) {
|
if (cacheConfig === undefined) {
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -352,16 +227,13 @@ export async function uploadDependencyCaches(
|
|||||||
|
|
||||||
// Check that we can find files to calculate the hash for the cache key from, so we don't end up
|
// Check that we can find files to calculate the hash for the cache key from, so we don't end up
|
||||||
// with an empty string.
|
// with an empty string.
|
||||||
const patterns = await checkHashPatterns(
|
const globber = await makeGlobber(cacheConfig.hash);
|
||||||
codeql,
|
|
||||||
features,
|
if ((await globber.glob()).length === 0) {
|
||||||
language,
|
|
||||||
cacheConfig,
|
|
||||||
"upload",
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
if (patterns === undefined) {
|
|
||||||
status.push({ language, result: CacheStoreResult.NoHash });
|
status.push({ language, result: CacheStoreResult.NoHash });
|
||||||
|
logger.info(
|
||||||
|
`Skipping upload of dependency cache for ${language} as we cannot calculate a hash for the cache key.`,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,11 +247,7 @@ export async function uploadDependencyCaches(
|
|||||||
// use the cache quota that we compete with. In that case, we do not wish to use up all of the quota
|
// use the cache quota that we compete with. In that case, we do not wish to use up all of the quota
|
||||||
// with the dependency caches. For this, we could use the Cache API to check whether other workflows
|
// with the dependency caches. For this, we could use the Cache API to check whether other workflows
|
||||||
// are using the quota and how full it is.
|
// are using the quota and how full it is.
|
||||||
const size = await getTotalCacheSize(
|
const size = await getTotalCacheSize(cacheConfig.paths, logger, true);
|
||||||
cacheConfig.getDependencyPaths(),
|
|
||||||
logger,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Skip uploading an empty cache.
|
// Skip uploading an empty cache.
|
||||||
if (size === 0) {
|
if (size === 0) {
|
||||||
@@ -390,7 +258,7 @@ export async function uploadDependencyCaches(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = await cacheKey(codeql, features, language, patterns);
|
const key = await cacheKey(language, cacheConfig, minimizeJavaJars);
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`Uploading cache of size ${size} for ${language} with key ${key}...`,
|
`Uploading cache of size ${size} for ${language} with key ${key}...`,
|
||||||
@@ -398,7 +266,7 @@ export async function uploadDependencyCaches(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
await actionsCache.saveCache(cacheConfig.getDependencyPaths(), key);
|
await actionsCache.saveCache(cacheConfig.paths, key);
|
||||||
const upload_duration_ms = Math.round(performance.now() - start);
|
const upload_duration_ms = Math.round(performance.now() - start);
|
||||||
|
|
||||||
status.push({
|
status.push({
|
||||||
@@ -431,86 +299,31 @@ export async function uploadDependencyCaches(
|
|||||||
/**
|
/**
|
||||||
* Computes a cache key for the specified language.
|
* Computes a cache key for the specified language.
|
||||||
*
|
*
|
||||||
* @param codeql The CodeQL instance to use.
|
|
||||||
* @param features Information about which FFs are enabled.
|
|
||||||
* @param language The language being analyzed.
|
* @param language The language being analyzed.
|
||||||
* @param patterns The file patterns to hash.
|
* @param cacheConfig The cache configuration for the language.
|
||||||
*
|
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
|
||||||
* @returns A cache key capturing information about the project(s) being analyzed in the specified language.
|
* @returns A cache key capturing information about the project(s) being analyzed in the specified language.
|
||||||
*/
|
*/
|
||||||
export async function cacheKey(
|
async function cacheKey(
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
language: Language,
|
language: Language,
|
||||||
patterns: string[],
|
cacheConfig: CacheConfig,
|
||||||
|
minimizeJavaJars: boolean = false,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const hash = await glob.hashFiles(patterns.join("\n"));
|
const hash = await glob.hashFiles(cacheConfig.hash.join("\n"));
|
||||||
return `${await cachePrefix(codeql, features, language)}${hash}`;
|
return `${await cachePrefix(language, minimizeJavaJars)}${hash}`;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If experimental features which the cache contents depend on are enabled for the current language,
|
|
||||||
* this function returns a prefix that uniquely identifies the set of enabled features. The purpose of
|
|
||||||
* this is to avoid restoring caches whose contents depended on experimental features, if those
|
|
||||||
* experimental features are later disabled.
|
|
||||||
*
|
|
||||||
* @param codeql The CodeQL instance.
|
|
||||||
* @param features Information about enabled features.
|
|
||||||
* @param language The language we are creating the key for.
|
|
||||||
*
|
|
||||||
* @returns A cache key prefix identifying the enabled, experimental features that the cache depends on.
|
|
||||||
*/
|
|
||||||
export async function getFeaturePrefix(
|
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
language: Language,
|
|
||||||
): Promise<string> {
|
|
||||||
const enabledFeatures: Feature[] = [];
|
|
||||||
|
|
||||||
const addFeatureIfEnabled = async (feature: Feature) => {
|
|
||||||
if (await features.getValue(feature, codeql)) {
|
|
||||||
enabledFeatures.push(feature);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (language === KnownLanguage.java) {
|
|
||||||
// To ensure a safe rollout of JAR minimization, we change the key when the feature is enabled.
|
|
||||||
const minimizeJavaJars = await features.getValue(
|
|
||||||
Feature.JavaMinimizeDependencyJars,
|
|
||||||
codeql,
|
|
||||||
);
|
|
||||||
|
|
||||||
// To maintain backwards compatibility with this, we return "minify-" instead of a hash.
|
|
||||||
if (minimizeJavaJars) {
|
|
||||||
return "minify-";
|
|
||||||
}
|
|
||||||
} else if (language === KnownLanguage.csharp) {
|
|
||||||
await addFeatureIfEnabled(Feature.CsharpNewCacheKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any features that affect the cache are enabled, return a feature prefix by
|
|
||||||
// computing a hash of the feature array.
|
|
||||||
if (enabledFeatures.length > 0) {
|
|
||||||
return `${createCacheKeyHash(enabledFeatures)}-`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No feature prefix.
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a prefix for the cache key, comprised of a CodeQL-specific prefix, a version number that
|
* Constructs a prefix for the cache key, comprised of a CodeQL-specific prefix, a version number that
|
||||||
* can be changed to invalidate old caches, the runner's operating system, and the specified language name.
|
* can be changed to invalidate old caches, the runner's operating system, and the specified language name.
|
||||||
*
|
*
|
||||||
* @param codeql The CodeQL instance to use.
|
|
||||||
* @param features Information about which FFs are enabled.
|
|
||||||
* @param language The language being analyzed.
|
* @param language The language being analyzed.
|
||||||
|
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
|
||||||
* @returns The prefix that identifies what a cache is for.
|
* @returns The prefix that identifies what a cache is for.
|
||||||
*/
|
*/
|
||||||
async function cachePrefix(
|
async function cachePrefix(
|
||||||
codeql: CodeQL,
|
|
||||||
features: FeatureEnablement,
|
|
||||||
language: Language,
|
language: Language,
|
||||||
|
minimizeJavaJars: boolean,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const runnerOs = getRequiredEnvParam("RUNNER_OS");
|
const runnerOs = getRequiredEnvParam("RUNNER_OS");
|
||||||
const customPrefix = process.env[EnvVar.DEPENDENCY_CACHING_PREFIX];
|
const customPrefix = process.env[EnvVar.DEPENDENCY_CACHING_PREFIX];
|
||||||
@@ -520,18 +333,12 @@ async function cachePrefix(
|
|||||||
prefix = `${prefix}-${customPrefix}`;
|
prefix = `${prefix}-${customPrefix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the feature prefix for the cache, if any. This is a hash that identifies
|
// To ensure a safe rollout of JAR minimization, we change the key when the feature is enabled.
|
||||||
// experimental features that affect the cache contents.
|
if (language === KnownLanguage.java && minimizeJavaJars) {
|
||||||
const featurePrefix = await getFeaturePrefix(codeql, features, language);
|
prefix = `minify-${prefix}`;
|
||||||
|
|
||||||
// Assemble the cache key. For backwards compatibility with the JAR minification experiment's existing
|
|
||||||
// feature prefix usage, we add that feature prefix at the start. Other feature prefixes are inserted
|
|
||||||
// after the general CodeQL dependency cache prefix.
|
|
||||||
if (featurePrefix === "minify-") {
|
|
||||||
return `${featurePrefix}${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
|
|
||||||
} else {
|
|
||||||
return `${prefix}-${featurePrefix}${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return `${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents information about our overall cache usage for CodeQL dependency caches. */
|
/** Represents information about our overall cache usage for CodeQL dependency caches. */
|
||||||
@@ -564,7 +371,3 @@ export async function getDependencyCacheUsage(
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const internal = {
|
|
||||||
makePatternCheck,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -137,10 +137,4 @@ export enum EnvVar {
|
|||||||
* This setting is more specific than `CODEQL_ACTION_TEST_MODE`, which implies this option.
|
* This setting is more specific than `CODEQL_ACTION_TEST_MODE`, which implies this option.
|
||||||
*/
|
*/
|
||||||
SKIP_SARIF_UPLOAD = "CODEQL_ACTION_SKIP_SARIF_UPLOAD",
|
SKIP_SARIF_UPLOAD = "CODEQL_ACTION_SKIP_SARIF_UPLOAD",
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to skip workflow validation. Intended for internal use, where we know that
|
|
||||||
* the workflow is valid and validation is not necessary.
|
|
||||||
*/
|
|
||||||
SKIP_WORKFLOW_VALIDATION = "CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION",
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ export enum Feature {
|
|||||||
AnalyzeUseNewUpload = "analyze_use_new_upload",
|
AnalyzeUseNewUpload = "analyze_use_new_upload",
|
||||||
CleanupTrapCaches = "cleanup_trap_caches",
|
CleanupTrapCaches = "cleanup_trap_caches",
|
||||||
CppDependencyInstallation = "cpp_dependency_installation_enabled",
|
CppDependencyInstallation = "cpp_dependency_installation_enabled",
|
||||||
CsharpNewCacheKey = "csharp_new_cache_key",
|
|
||||||
DiffInformedQueries = "diff_informed_queries",
|
DiffInformedQueries = "diff_informed_queries",
|
||||||
DisableCsharpBuildless = "disable_csharp_buildless",
|
DisableCsharpBuildless = "disable_csharp_buildless",
|
||||||
DisableJavaBuildlessEnabled = "disable_java_buildless_enabled",
|
DisableJavaBuildlessEnabled = "disable_java_buildless_enabled",
|
||||||
@@ -77,6 +76,7 @@ export enum Feature {
|
|||||||
OverlayAnalysisSwift = "overlay_analysis_swift",
|
OverlayAnalysisSwift = "overlay_analysis_swift",
|
||||||
PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib",
|
PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib",
|
||||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||||
|
ResolveSupportedLanguagesUsingCli = "resolve_supported_languages_using_cli",
|
||||||
UseRepositoryProperties = "use_repository_properties",
|
UseRepositoryProperties = "use_repository_properties",
|
||||||
ValidateDbConfig = "validate_db_config",
|
ValidateDbConfig = "validate_db_config",
|
||||||
}
|
}
|
||||||
@@ -133,11 +133,6 @@ export const featureConfig: Record<
|
|||||||
legacyApi: true,
|
legacyApi: true,
|
||||||
minimumVersion: "2.15.0",
|
minimumVersion: "2.15.0",
|
||||||
},
|
},
|
||||||
[Feature.CsharpNewCacheKey]: {
|
|
||||||
defaultValue: false,
|
|
||||||
envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY",
|
|
||||||
minimumVersion: undefined,
|
|
||||||
},
|
|
||||||
[Feature.DiffInformedQueries]: {
|
[Feature.DiffInformedQueries]: {
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
envVar: "CODEQL_ACTION_DIFF_INFORMED_QUERIES",
|
envVar: "CODEQL_ACTION_DIFF_INFORMED_QUERIES",
|
||||||
@@ -166,6 +161,12 @@ export const featureConfig: Record<
|
|||||||
legacyApi: true,
|
legacyApi: true,
|
||||||
minimumVersion: undefined,
|
minimumVersion: undefined,
|
||||||
},
|
},
|
||||||
|
[Feature.ResolveSupportedLanguagesUsingCli]: {
|
||||||
|
defaultValue: false,
|
||||||
|
envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI",
|
||||||
|
minimumVersion: undefined,
|
||||||
|
toolsFeature: ToolsFeature.BuiltinExtractorsSpecifyDefaultQueries,
|
||||||
|
},
|
||||||
[Feature.OverlayAnalysis]: {
|
[Feature.OverlayAnalysis]: {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS",
|
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS",
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ import {
|
|||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
BuildMode,
|
BuildMode,
|
||||||
} from "./util";
|
} from "./util";
|
||||||
import { checkWorkflow } from "./workflow";
|
import { validateWorkflow } from "./workflow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a status report indicating that the `init` Action is starting.
|
* Sends a status report indicating that the `init` Action is starting.
|
||||||
@@ -288,9 +288,16 @@ async function run() {
|
|||||||
toolsSource = initCodeQLResult.toolsSource;
|
toolsSource = initCodeQLResult.toolsSource;
|
||||||
zstdAvailability = initCodeQLResult.zstdAvailability;
|
zstdAvailability = initCodeQLResult.zstdAvailability;
|
||||||
|
|
||||||
// Check the workflow for problems. If there are any problems, they are reported
|
core.startGroup("Validating workflow");
|
||||||
// to the workflow log. No exceptions are thrown.
|
const validateWorkflowResult = await validateWorkflow(codeql, logger);
|
||||||
await checkWorkflow(logger, codeql);
|
if (validateWorkflowResult === undefined) {
|
||||||
|
logger.info("Detected no issues with the code scanning workflow.");
|
||||||
|
} else {
|
||||||
|
logger.warning(
|
||||||
|
`Unable to validate code scanning workflow: ${validateWorkflowResult}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
core.endGroup();
|
||||||
|
|
||||||
// Set CODEQL_ENABLE_EXPERIMENTAL_FEATURES for Rust if between 2.19.3 (included) and 2.22.1 (excluded)
|
// Set CODEQL_ENABLE_EXPERIMENTAL_FEATURES for Rust if between 2.19.3 (included) and 2.22.1 (excluded)
|
||||||
// We need to set this environment variable before initializing the config, otherwise Rust
|
// We need to set this environment variable before initializing the config, otherwise Rust
|
||||||
@@ -578,12 +585,15 @@ async function run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore dependency cache(s), if they exist.
|
// Restore dependency cache(s), if they exist.
|
||||||
|
const minimizeJavaJars = await features.getValue(
|
||||||
|
Feature.JavaMinimizeDependencyJars,
|
||||||
|
codeql,
|
||||||
|
);
|
||||||
if (shouldRestoreCache(config.dependencyCachingEnabled)) {
|
if (shouldRestoreCache(config.dependencyCachingEnabled)) {
|
||||||
dependencyCachingResults = await downloadDependencyCaches(
|
dependencyCachingResults = await downloadDependencyCaches(
|
||||||
codeql,
|
|
||||||
features,
|
|
||||||
config.languages,
|
config.languages,
|
||||||
logger,
|
logger,
|
||||||
|
minimizeJavaJars,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,7 +655,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.`,
|
`${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 (
|
} else if (
|
||||||
(await features.getValue(Feature.JavaMinimizeDependencyJars, codeql)) &&
|
minimizeJavaJars &&
|
||||||
config.dependencyCachingEnabled &&
|
config.dependencyCachingEnabled &&
|
||||||
config.buildMode === BuildMode.None &&
|
config.buildMode === BuildMode.None &&
|
||||||
config.languages.includes(KnownLanguage.java)
|
config.languages.includes(KnownLanguage.java)
|
||||||
|
|||||||
@@ -13,15 +13,7 @@ export interface Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getActionsLogger(): Logger {
|
export function getActionsLogger(): Logger {
|
||||||
return {
|
return core;
|
||||||
debug: core.debug,
|
|
||||||
info: core.info,
|
|
||||||
warning: core.warning,
|
|
||||||
error: core.error,
|
|
||||||
isDebug: core.isDebug,
|
|
||||||
startGroup: core.startGroup,
|
|
||||||
endGroup: core.endGroup,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRunnerLogger(debugMode: boolean): Logger {
|
export function getRunnerLogger(debugMode: boolean): Logger {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as crypto from "crypto";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
@@ -10,7 +11,6 @@ import {
|
|||||||
getWorkflowRunID,
|
getWorkflowRunID,
|
||||||
} from "./actions-util";
|
} from "./actions-util";
|
||||||
import { getAutomationID } from "./api-client";
|
import { getAutomationID } from "./api-client";
|
||||||
import { createCacheKeyHash } from "./caching-utils";
|
|
||||||
import { type CodeQL } from "./codeql";
|
import { type CodeQL } from "./codeql";
|
||||||
import { type Config } from "./config-utils";
|
import { type Config } from "./config-utils";
|
||||||
import { getCommitOid, getFileOidsUnderPath } from "./git-utils";
|
import { getCommitOid, getFileOidsUnderPath } from "./git-utils";
|
||||||
@@ -514,3 +514,27 @@ export async function getCacheRestoreKeyPrefix(
|
|||||||
// easier to debug and understand the cache key structure.
|
// easier to debug and understand the cache key structure.
|
||||||
return `${CACHE_PREFIX}-${CACHE_VERSION}-${componentsHash}-${languages}-${codeQlVersion}-`;
|
return `${CACHE_PREFIX}-${CACHE_VERSION}-${componentsHash}-${languages}-${codeQlVersion}-`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SHA-256 hash of the cache key components to ensure uniqueness
|
||||||
|
* while keeping the cache key length manageable.
|
||||||
|
*
|
||||||
|
* @param components Object containing all components that should influence cache key uniqueness
|
||||||
|
* @returns A short SHA-256 hash (first 16 characters) of the components
|
||||||
|
*/
|
||||||
|
function createCacheKeyHash(components: Record<string, any>): string {
|
||||||
|
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
|
||||||
|
//
|
||||||
|
// "Properties are visited using the same algorithm as Object.keys(), which
|
||||||
|
// has a well-defined order and is stable across implementations. For example,
|
||||||
|
// JSON.stringify on the same object will always produce the same string, and
|
||||||
|
// JSON.parse(JSON.stringify(obj)) would produce an object with the same key
|
||||||
|
// ordering as the original (assuming the object is completely
|
||||||
|
// JSON-serializable)."
|
||||||
|
const componentsJson = JSON.stringify(components);
|
||||||
|
return crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(componentsJson)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 16);
|
||||||
|
}
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ export interface EventReport {
|
|||||||
*
|
*
|
||||||
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
|
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
|
||||||
* @param status The status. Must be 'success', 'failure', or 'starting'
|
* @param status The status. Must be 'success', 'failure', or 'starting'
|
||||||
* @param actionStartedAt The time this action started executing.
|
* @param startedAt The time this action started executing.
|
||||||
* @param cause Cause of failure (only supply if status is 'failure')
|
* @param cause Cause of failure (only supply if status is 'failure')
|
||||||
* @param exception Exception (only supply if status is 'failure')
|
* @param exception Exception (only supply if status is 'failure')
|
||||||
* @returns undefined if an exception was thrown.
|
* @returns undefined if an exception was thrown.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as semver from "semver";
|
|||||||
|
|
||||||
import { CommandInvocationError } from "./actions-util";
|
import { CommandInvocationError } from "./actions-util";
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
import { assertNever, cleanUpPath, isBinaryAccessible } from "./util";
|
import { assertNever, cleanUpGlob, isBinaryAccessible } from "./util";
|
||||||
|
|
||||||
const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
|
const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
|
||||||
const MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
const MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
||||||
@@ -217,7 +217,7 @@ export async function extractTarZst(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await cleanUpPath(dest, "extraction destination directory", logger);
|
await cleanUpGlob(dest, "extraction destination directory", logger);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import * as semver from "semver";
|
|||||||
|
|
||||||
import { formatDuration, Logger } from "./logging";
|
import { formatDuration, Logger } from "./logging";
|
||||||
import * as tar from "./tar";
|
import * as tar from "./tar";
|
||||||
import { cleanUpPath, getErrorMessage, getRequiredEnvParam } from "./util";
|
import { cleanUpGlob, getErrorMessage, getRequiredEnvParam } from "./util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* High watermark to use when streaming the download and extraction of the CodeQL tools.
|
* High watermark to use when streaming the download and extraction of the CodeQL tools.
|
||||||
@@ -130,7 +130,7 @@ export async function downloadAndExtract(
|
|||||||
|
|
||||||
// If we failed during processing, we want to clean up the destination directory
|
// If we failed during processing, we want to clean up the destination directory
|
||||||
// before we try again.
|
// before we try again.
|
||||||
await cleanUpPath(dest, "CodeQL bundle", logger);
|
await cleanUpGlob(dest, "CodeQL bundle", logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolsDownloadStart = performance.now();
|
const toolsDownloadStart = performance.now();
|
||||||
@@ -167,7 +167,7 @@ export async function downloadAndExtract(
|
|||||||
)}).`,
|
)}).`,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
await cleanUpPath(archivedBundlePath, "CodeQL bundle archive", logger);
|
await cleanUpGlob(archivedBundlePath, "CodeQL bundle archive", logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -738,7 +738,7 @@ export async function postProcessSarifFiles(
|
|||||||
* @param logger The logger to use.
|
* @param logger The logger to use.
|
||||||
* @param pathInput The input provided for `post-processed-sarif-path`.
|
* @param pathInput The input provided for `post-processed-sarif-path`.
|
||||||
* @param uploadTarget The upload target.
|
* @param uploadTarget The upload target.
|
||||||
* @param postProcessingResults The results of post-processing SARIF files.
|
* @param processingResults The results of post-processing SARIF files.
|
||||||
*/
|
*/
|
||||||
export async function writePostProcessedFiles(
|
export async function writePostProcessedFiles(
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
|||||||
@@ -101,6 +101,16 @@ test("getMemoryFlag() throws if the ram input is < 0 or NaN", async (t) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getAddSnippetsFlag() should return the correct flag", (t) => {
|
||||||
|
t.deepEqual(util.getAddSnippetsFlag(true), "--sarif-add-snippets");
|
||||||
|
t.deepEqual(util.getAddSnippetsFlag("true"), "--sarif-add-snippets");
|
||||||
|
|
||||||
|
t.deepEqual(util.getAddSnippetsFlag(false), "--no-sarif-add-snippets");
|
||||||
|
t.deepEqual(util.getAddSnippetsFlag(undefined), "--no-sarif-add-snippets");
|
||||||
|
t.deepEqual(util.getAddSnippetsFlag("false"), "--no-sarif-add-snippets");
|
||||||
|
t.deepEqual(util.getAddSnippetsFlag("foo bar"), "--no-sarif-add-snippets");
|
||||||
|
});
|
||||||
|
|
||||||
test("getThreadsFlag() should return the correct --threads flag", (t) => {
|
test("getThreadsFlag() should return the correct --threads flag", (t) => {
|
||||||
const numCpus = os.cpus().length;
|
const numCpus = os.cpus().length;
|
||||||
|
|
||||||
@@ -447,21 +457,13 @@ const CHECK_ACTION_VERSION_TESTS: Array<[string, util.GitHubVersion, boolean]> =
|
|||||||
["2.2.1", { type: util.GitHubVariant.DOTCOM }, true],
|
["2.2.1", { type: util.GitHubVariant.DOTCOM }, true],
|
||||||
["2.2.1", { type: util.GitHubVariant.GHE_DOTCOM }, true],
|
["2.2.1", { type: util.GitHubVariant.GHE_DOTCOM }, true],
|
||||||
["2.2.1", { type: util.GitHubVariant.GHES, version: "3.10" }, false],
|
["2.2.1", { type: util.GitHubVariant.GHES, version: "3.10" }, false],
|
||||||
["2.2.1", { type: util.GitHubVariant.GHES, version: "3.11" }, false],
|
["2.2.1", { type: util.GitHubVariant.GHES, version: "3.11" }, true],
|
||||||
["2.2.1", { type: util.GitHubVariant.GHES, version: "3.12" }, false],
|
["2.2.1", { type: util.GitHubVariant.GHES, version: "3.12" }, true],
|
||||||
["3.2.1", { type: util.GitHubVariant.DOTCOM }, true],
|
["3.2.1", { type: util.GitHubVariant.DOTCOM }, false],
|
||||||
["3.2.1", { type: util.GitHubVariant.GHE_DOTCOM }, true],
|
["3.2.1", { type: util.GitHubVariant.GHE_DOTCOM }, false],
|
||||||
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.10" }, false],
|
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.10" }, false],
|
||||||
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.11" }, false],
|
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.11" }, false],
|
||||||
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.12" }, false],
|
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.12" }, false],
|
||||||
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.19" }, false],
|
|
||||||
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.20" }, true],
|
|
||||||
["3.2.1", { type: util.GitHubVariant.GHES, version: "3.21" }, true],
|
|
||||||
["4.2.1", { type: util.GitHubVariant.DOTCOM }, false],
|
|
||||||
["4.2.1", { type: util.GitHubVariant.GHE_DOTCOM }, false],
|
|
||||||
["4.2.1", { type: util.GitHubVariant.GHES, version: "3.19" }, false],
|
|
||||||
["4.2.1", { type: util.GitHubVariant.GHES, version: "3.20" }, false],
|
|
||||||
["4.2.1", { type: util.GitHubVariant.GHES, version: "3.21" }, false],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const [
|
for (const [
|
||||||
@@ -488,7 +490,9 @@ for (const [
|
|||||||
if (shouldReportError) {
|
if (shouldReportError) {
|
||||||
t.true(
|
t.true(
|
||||||
warningSpy.calledOnceWithExactly(
|
warningSpy.calledOnceWithExactly(
|
||||||
sinon.match("CodeQL Action v3 will be deprecated in December 2026."),
|
sinon.match(
|
||||||
|
"CodeQL Action major versions v1 and v2 have been deprecated.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -530,12 +534,3 @@ test("getCgroupCpuCountFromCpus returns undefined if the CPU file exists but is
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("checkDiskUsage succeeds and produces positive numbers", async (t) => {
|
|
||||||
process.env["GITHUB_WORKSPACE"] = os.tmpdir();
|
|
||||||
const diskUsage = await util.checkDiskUsage(getRunnerLogger(true));
|
|
||||||
if (t.truthy(diskUsage)) {
|
|
||||||
t.true(diskUsage.numAvailableBytes > 0);
|
|
||||||
t.true(diskUsage.numTotalBytes > 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
60
src/util.ts
60
src/util.ts
@@ -6,6 +6,7 @@ import * as path from "path";
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import * as exec from "@actions/exec/lib/exec";
|
import * as exec from "@actions/exec/lib/exec";
|
||||||
import * as io from "@actions/io";
|
import * as io from "@actions/io";
|
||||||
|
import * as del from "del";
|
||||||
import getFolderSize from "get-folder-size";
|
import getFolderSize from "get-folder-size";
|
||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
import * as semver from "semver";
|
import * as semver from "semver";
|
||||||
@@ -166,7 +167,7 @@ export async function withTmpDir<T>(
|
|||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeql-action-"));
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "codeql-action-"));
|
||||||
const result = await body(tmpDir);
|
const result = await body(tmpDir);
|
||||||
await fs.promises.rm(tmpDir, { force: true, recursive: true });
|
await del.deleteAsync(tmpDir, { force: true });
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +343,21 @@ export function getMemoryFlag(
|
|||||||
return `--ram=${megabytes}`;
|
return `--ram=${megabytes}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the codeql flag to specify whether to add code snippets to the sarif file.
|
||||||
|
*
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
export function getAddSnippetsFlag(
|
||||||
|
userInput: string | boolean | undefined,
|
||||||
|
): string {
|
||||||
|
if (typeof userInput === "string") {
|
||||||
|
// have to process specifically because any non-empty string is truthy
|
||||||
|
userInput = userInput.toLowerCase() === "true";
|
||||||
|
}
|
||||||
|
return userInput ? "--sarif-add-snippets" : "--no-sarif-add-snippets";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of the codeql `--threads` flag specified for the `threads`
|
* Get the value of the codeql `--threads` flag specified for the `threads`
|
||||||
* input. If no value was specified, all available threads will be used.
|
* input. If no value was specified, all available threads will be used.
|
||||||
@@ -740,7 +756,7 @@ export async function bundleDb(
|
|||||||
// from somewhere else or someone trying to make the action upload a
|
// from somewhere else or someone trying to make the action upload a
|
||||||
// non-database file.
|
// non-database file.
|
||||||
if (fs.existsSync(databaseBundlePath)) {
|
if (fs.existsSync(databaseBundlePath)) {
|
||||||
await fs.promises.rm(databaseBundlePath, { force: true });
|
await del.deleteAsync(databaseBundlePath, { force: true });
|
||||||
}
|
}
|
||||||
await codeql.databaseBundle(databasePath, databaseBundlePath, dbName);
|
await codeql.databaseBundle(databasePath, databaseBundlePath, dbName);
|
||||||
return databaseBundlePath;
|
return databaseBundlePath;
|
||||||
@@ -1114,38 +1130,38 @@ export async function checkDiskUsage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt the customer to upgrade to CodeQL Action v4, if appropriate.
|
* Prompt the customer to upgrade to CodeQL Action v3, if appropriate.
|
||||||
*
|
*
|
||||||
* Check whether a customer is running v3. If they are, and we can determine that the GitHub
|
* Check whether a customer is running v1 or v2. If they are, and we can determine that the GitHub
|
||||||
* instance supports v4, then log an error prompting the customer to upgrade to v4.
|
* instance supports v3, then log an error prompting the customer to upgrade to v3.
|
||||||
*/
|
*/
|
||||||
export function checkActionVersion(
|
export function checkActionVersion(
|
||||||
version: string,
|
version: string,
|
||||||
githubVersion: GitHubVersion,
|
githubVersion: GitHubVersion,
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
!semver.satisfies(version, ">=4") && // do not log error if the customer is already running v4
|
!semver.satisfies(version, ">=3") && // do not log error if the customer is already running v3
|
||||||
!process.env[EnvVar.LOG_VERSION_DEPRECATION] // do not log error if we have already
|
!process.env[EnvVar.LOG_VERSION_DEPRECATION] // do not log error if we have already
|
||||||
) {
|
) {
|
||||||
// Only error for versions of GHES that are compatible with CodeQL Action version 4.
|
// Only error for versions of GHES that are compatible with CodeQL Action version 3.
|
||||||
//
|
//
|
||||||
// GHES 3.20 is the first version to ship with the v4 tag and this warning message code.
|
// GHES 3.11 shipped without the v3 tag, but it also shipped without this warning message code.
|
||||||
// Therefore, users who are seeing this warning message code are running on GHES 3.20 or newer,
|
// Therefore users who are seeing this warning message code have pulled in a new version of the
|
||||||
// and should update to CodeQL Action v4.
|
// Action, and with it the v3 tag.
|
||||||
if (
|
if (
|
||||||
githubVersion.type === GitHubVariant.DOTCOM ||
|
githubVersion.type === GitHubVariant.DOTCOM ||
|
||||||
githubVersion.type === GitHubVariant.GHE_DOTCOM ||
|
githubVersion.type === GitHubVariant.GHE_DOTCOM ||
|
||||||
(githubVersion.type === GitHubVariant.GHES &&
|
(githubVersion.type === GitHubVariant.GHES &&
|
||||||
semver.satisfies(
|
semver.satisfies(
|
||||||
semver.coerce(githubVersion.version) ?? "0.0.0",
|
semver.coerce(githubVersion.version) ?? "0.0.0",
|
||||||
">=3.20",
|
">=3.11",
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
core.error(
|
core.error(
|
||||||
"CodeQL Action v3 will be deprecated in December 2026. " +
|
"CodeQL Action major versions v1 and v2 have been deprecated. " +
|
||||||
"Please update all occurrences of the CodeQL Action in your workflow files to v4. " +
|
"Please update all occurrences of the CodeQL Action in your workflow files to v3. " +
|
||||||
"For more information, see " +
|
"For more information, see " +
|
||||||
"https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/",
|
"https://github.blog/changelog/2025-01-10-code-scanning-codeql-action-v2-is-now-deprecated/",
|
||||||
);
|
);
|
||||||
// set LOG_VERSION_DEPRECATION env var to prevent the warning from being logged multiple times
|
// set LOG_VERSION_DEPRECATION env var to prevent the warning from being logged multiple times
|
||||||
core.exportVariable(EnvVar.LOG_VERSION_DEPRECATION, "true");
|
core.exportVariable(EnvVar.LOG_VERSION_DEPRECATION, "true");
|
||||||
@@ -1240,13 +1256,19 @@ export async function checkSipEnablement(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cleanUpPath(file: string, name: string, logger: Logger) {
|
export async function cleanUpGlob(glob: string, name: string, logger: Logger) {
|
||||||
logger.debug(`Cleaning up ${name}.`);
|
logger.debug(`Cleaning up ${name}.`);
|
||||||
try {
|
try {
|
||||||
await fs.promises.rm(file, {
|
const deletedPaths = await del.deleteAsync(glob, { force: true });
|
||||||
force: true,
|
if (deletedPaths.length === 0) {
|
||||||
recursive: true,
|
logger.warning(
|
||||||
});
|
`Failed to clean up ${name}: no files found matching ${glob}.`,
|
||||||
|
);
|
||||||
|
} else if (deletedPaths.length === 1) {
|
||||||
|
logger.debug(`Cleaned up ${name}.`);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Cleaned up ${name} (${deletedPaths.length} files).`);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warning(`Failed to clean up ${name}: ${e}.`);
|
logger.warning(`Failed to clean up ${name}: ${e}.`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,9 @@ import test, { ExecutionContext } from "ava";
|
|||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
|
|
||||||
import * as actionsUtil from "./actions-util";
|
import { getCodeQLForTesting } from "./codeql";
|
||||||
import { createStubCodeQL, getCodeQLForTesting } from "./codeql";
|
import { setupTests } from "./testing-utils";
|
||||||
import { EnvVar } from "./environment";
|
|
||||||
import {
|
import {
|
||||||
checkExpectedLogMessages,
|
|
||||||
getRecordingLogger,
|
|
||||||
LoggedMessage,
|
|
||||||
setupTests,
|
|
||||||
} from "./testing-utils";
|
|
||||||
import {
|
|
||||||
checkWorkflow,
|
|
||||||
CodedError,
|
CodedError,
|
||||||
formatWorkflowCause,
|
formatWorkflowCause,
|
||||||
formatWorkflowErrors,
|
formatWorkflowErrors,
|
||||||
@@ -21,7 +13,6 @@ import {
|
|||||||
Workflow,
|
Workflow,
|
||||||
WorkflowErrors,
|
WorkflowErrors,
|
||||||
} from "./workflow";
|
} from "./workflow";
|
||||||
import * as workflow from "./workflow";
|
|
||||||
|
|
||||||
function errorCodes(
|
function errorCodes(
|
||||||
actual: CodedError[],
|
actual: CodedError[],
|
||||||
@@ -879,78 +870,3 @@ test("getCategoryInputOrThrow throws error for workflow with multiple calls to a
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("checkWorkflow - validates workflow if `SKIP_WORKFLOW_VALIDATION` is not set", async (t) => {
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
|
|
||||||
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
|
|
||||||
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
|
|
||||||
validateWorkflow.resolves(undefined);
|
|
||||||
|
|
||||||
await checkWorkflow(getRecordingLogger(messages), codeql);
|
|
||||||
|
|
||||||
t.assert(
|
|
||||||
validateWorkflow.calledOnce,
|
|
||||||
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
|
|
||||||
);
|
|
||||||
checkExpectedLogMessages(t, messages, [
|
|
||||||
"Detected no issues with the code scanning workflow.",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("checkWorkflow - logs problems with workflow validation", async (t) => {
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
|
|
||||||
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
|
|
||||||
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
|
|
||||||
validateWorkflow.resolves("problem");
|
|
||||||
|
|
||||||
await checkWorkflow(getRecordingLogger(messages), codeql);
|
|
||||||
|
|
||||||
t.assert(
|
|
||||||
validateWorkflow.calledOnce,
|
|
||||||
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
|
|
||||||
);
|
|
||||||
checkExpectedLogMessages(t, messages, [
|
|
||||||
"Unable to validate code scanning workflow: problem",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("checkWorkflow - skips validation if `SKIP_WORKFLOW_VALIDATION` is `true`", async (t) => {
|
|
||||||
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] = "true";
|
|
||||||
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
|
|
||||||
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
|
|
||||||
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
|
|
||||||
|
|
||||||
await checkWorkflow(getRecordingLogger(messages), codeql);
|
|
||||||
|
|
||||||
t.assert(
|
|
||||||
validateWorkflow.notCalled,
|
|
||||||
"`checkWorkflow` called `validateWorkflow` unexpectedly",
|
|
||||||
);
|
|
||||||
t.is(messages.length, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("checkWorkflow - skips validation for `dynamic` workflows", async (t) => {
|
|
||||||
const messages: LoggedMessage[] = [];
|
|
||||||
const codeql = createStubCodeQL({});
|
|
||||||
|
|
||||||
const isDynamicWorkflow = sinon
|
|
||||||
.stub(actionsUtil, "isDynamicWorkflow")
|
|
||||||
.returns(true);
|
|
||||||
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
|
|
||||||
|
|
||||||
await checkWorkflow(getRecordingLogger(messages), codeql);
|
|
||||||
|
|
||||||
t.assert(isDynamicWorkflow.calledOnce);
|
|
||||||
t.assert(
|
|
||||||
validateWorkflow.notCalled,
|
|
||||||
"`checkWorkflow` called `validateWorkflow` unexpectedly",
|
|
||||||
);
|
|
||||||
t.is(messages.length, 0);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import zlib from "zlib";
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import * as yaml from "js-yaml";
|
import * as yaml from "js-yaml";
|
||||||
|
|
||||||
import { isDynamicWorkflow } from "./actions-util";
|
|
||||||
import * as api from "./api-client";
|
import * as api from "./api-client";
|
||||||
import { CodeQL } from "./codeql";
|
import { CodeQL } from "./codeql";
|
||||||
import { EnvVar } from "./environment";
|
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
import {
|
import {
|
||||||
getRequiredEnvParam,
|
getRequiredEnvParam,
|
||||||
@@ -218,7 +216,7 @@ function hasWorkflowTrigger(triggerName: string, doc: Workflow): boolean {
|
|||||||
return Object.prototype.hasOwnProperty.call(doc.on, triggerName);
|
return Object.prototype.hasOwnProperty.call(doc.on, triggerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateWorkflow(
|
export async function validateWorkflow(
|
||||||
codeql: CodeQL,
|
codeql: CodeQL,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<undefined | string> {
|
): Promise<undefined | string> {
|
||||||
@@ -464,36 +462,3 @@ export function getCheckoutPathInputOrThrow(
|
|||||||
) || getRequiredEnvParam("GITHUB_WORKSPACE") // if unspecified, checkout_path defaults to ${{ github.workspace }}
|
) || getRequiredEnvParam("GITHUB_WORKSPACE") // if unspecified, checkout_path defaults to ${{ github.workspace }}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around `validateWorkflow` which reports the outcome.
|
|
||||||
*
|
|
||||||
* @param logger The logger to use.
|
|
||||||
* @param codeql The CodeQL instance.
|
|
||||||
*/
|
|
||||||
export async function checkWorkflow(logger: Logger, codeql: CodeQL) {
|
|
||||||
// Check the workflow for problems, unless `SKIP_WORKFLOW_VALIDATION` is `true`
|
|
||||||
// or the workflow trigger is `dynamic`.
|
|
||||||
if (
|
|
||||||
!isDynamicWorkflow() &&
|
|
||||||
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] !== "true"
|
|
||||||
) {
|
|
||||||
core.startGroup("Validating workflow");
|
|
||||||
const validateWorkflowResult = await internal.validateWorkflow(
|
|
||||||
codeql,
|
|
||||||
logger,
|
|
||||||
);
|
|
||||||
if (validateWorkflowResult === undefined) {
|
|
||||||
logger.info("Detected no issues with the code scanning workflow.");
|
|
||||||
} else {
|
|
||||||
logger.debug(
|
|
||||||
`Unable to validate code scanning workflow: ${validateWorkflowResult}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
core.endGroup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const internal = {
|
|
||||||
validateWorkflow,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"sdk": {
|
|
||||||
"version": "9.0.307",
|
|
||||||
"rollForward": "latestFeature"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user