Compare commits

..

28 Commits

Author SHA1 Message Date
Michael B. Gale 8041026692 Add NODE_ENV as safe environment variable 2026-05-22 15:14:24 +01:00
Michael B. Gale d3b3ffb888 Add basic eslint enforcement 2026-05-22 15:03:52 +01:00
Michael B. Gale dc5f2b964a Add wrapper around core.exportVariable 2026-05-22 15:03:52 +01:00
Michael B. Gale ffebdc8cf8 Move isInTestMode to environment.ts 2026-05-22 13:45:07 +01:00
Óscar San José 0fb8a6672b Merge pull request #3928 from github/mergeback/v4.36.0-to-main-7211b7c8
Mergeback v4.36.0 refs/heads/releases/v4 into main
2026-05-22 11:28:10 +00:00
github-actions[bot] 80795fb0d4 Rebuild 2026-05-22 11:08:00 +00:00
github-actions[bot] 0cd24d8654 Update changelog and version after v4.36.0 2026-05-22 11:07:48 +00:00
Óscar San José 7211b7c807 Merge pull request #3927 from github/update-v4.36.0-ebc2d9e2b
Merge main into releases/v4
2026-05-22 13:06:23 +02:00
github-actions[bot] 7740f2fb21 Update changelog for v4.36.0 2026-05-22 10:49:45 +00:00
Óscar San José ebc2d9e2bc Merge pull request #3926 from github/update-bundle/codeql-bundle-v2.25.5
Update default bundle to 2.25.5
2026-05-22 10:32:55 +00:00
github-actions[bot] d1f74b777c Add changelog note 2026-05-22 10:18:49 +00:00
github-actions[bot] 2dc40cec39 Update default bundle to codeql-bundle-v2.25.5 2026-05-22 10:18:43 +00:00
Henry Mercer 84498526a0 Merge pull request #3910 from github/henrymercer/repo-size-diff-check
Action size: Add a PR check that comments on significant repo size changes
2026-05-21 10:29:33 +00:00
Henry Mercer 72ac23c6d1 Update excluded required check list 2026-05-21 10:16:47 +01:00
Henry Mercer c5297a28a2 Merge pull request #3919 from github/henrymercer/workflow-concurrency
CI: Automatically cancel non-generated workflows
2026-05-20 16:26:50 +00:00
Henry Mercer 8ffeae7d05 CI: Automatically cancel non-generated workflows
Specify concurrency groups for non-generated workflows so we can cancel in-progress runs when new commits are pushed to a PR.
2026-05-20 16:39:16 +01:00
Henry Mercer f3f52bf568 Revert getErrorMessage import
To avoid requiring additional dependencies
2026-05-20 15:55:41 +01:00
Henry Mercer a14f75e3ac Address review comments 2026-05-20 15:39:14 +01:00
Henry Mercer 164c32a61e Merge pull request #3918 from github/henrymercer/upgrade-brace-expansion
Bump `brace-expansion`
2026-05-20 14:31:25 +00:00
Henry Mercer a134948b87 Bump brace-expansion
Address https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-jxxr-4gwj-5jf2
2026-05-20 15:17:16 +01:00
Henry Mercer 2c8faa5e9f Pass comment body file directly 2026-05-18 20:28:53 +01:00
Henry Mercer 15a712bbc2 Address review comments 2026-05-18 20:08:43 +01:00
Henry Mercer 9b6438e936 Tweak workflow 2026-05-18 18:25:26 +01:00
Henry Mercer b5b50d62f1 Merge branch 'main' into henrymercer/repo-size-diff-check 2026-05-18 18:20:16 +01:00
Henry Mercer 5a80681bb6 Address review comments 2026-05-18 17:53:50 +01:00
Henry Mercer bcffb2b658 Unify checks into a single job 2026-05-18 17:33:45 +01:00
Henry Mercer 6f8805e224 Default setup env vars: Restrict results to src 2026-05-18 17:15:30 +01:00
Henry Mercer 4fc0f3e51b Add a PR check that comments on significant repo size changes 2026-05-18 16:36:58 +01:00
39 changed files with 1244 additions and 679 deletions
@@ -9,6 +9,10 @@ on:
# by other workflows. # by other workflows.
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -24,6 +24,10 @@ on:
- cron: '0 5 * * *' - cron: '0 5 * * *'
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -20,6 +20,10 @@ on:
- cron: '0 5 * * *' - cron: '0 5 * * *'
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -19,6 +19,10 @@ on:
- cron: '0 5 * * *' - cron: '0 5 * * *'
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
+121 -46
View File
@@ -10,6 +10,10 @@ on:
types: [checks_requested] types: [checks_requested]
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -29,6 +33,10 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 45 timeout-minutes: 45
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: pr-checks-unit-tests-${{ github.ref }}-${{ github.event_name }}-${{ matrix.os }}-node${{ matrix['node-version'] }}
steps: steps:
- name: Prepare git (Windows) - name: Prepare git (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
@@ -67,22 +75,21 @@ jobs:
sarif_file: eslint.sarif sarif_file: eslint.sarif
category: eslint category: eslint
# Verifying the PR checks are up-to-date requires Node 24. The PR checks are not dependent # These checks do not need to be run as part of the same matrix that we use for the `unit-tests`
# on the main codebase and therefore do not need to be run as part of the same matrix that # job.
# we use for the `unit-tests` job. other-checks:
verify-pr-checks: name: Other checks
name: Verify PR checks
if: github.triggering_actor != 'dependabot[bot]' if: github.triggering_actor != 'dependabot[bot]'
permissions: permissions:
contents: read contents: read
runs-on: ubuntu-slim runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: concurrency:
- name: Prepare git (Windows) cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
if: runner.os == 'Windows' group: pr-checks-pr-checks-${{ github.ref }}-${{ github.event_name }}
run: git config --global core.autocrlf false
steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
@@ -93,66 +100,134 @@ jobs:
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
id: install-deps
run: npm ci run: npm ci
- name: Verify PR checks up to date - name: Verify PR checks up to date
if: always() if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
run: .github/workflows/script/verify-pr-checks.sh run: .github/workflows/script/verify-pr-checks.sh
- name: Run pr-checks tests - name: Run pr-checks tests
if: always() if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
working-directory: pr-checks working-directory: pr-checks
run: npx tsx --test run: npx tsx --test
check-node-version: - name: Verify all Actions use the same Node version
if: github.triggering_actor != 'dependabot[bot]' && startsWith(github.head_ref, 'backport-') id: head-version
name: Check Action Node versions for Backport
runs-on: ubuntu-latest
timeout-minutes: 5
env:
BASE_REF: ${{ github.base_ref }}
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- id: head-version
name: Determine Node version for HEAD
run: | run: |
if [[ ! -f ".nvmrc" ]]; then NODE_VERSION=$(find . -path "*/node_modules" -prune -o -name "action.yml" -exec yq -o=json '.runs.using' {} \; | jq -rs '[.[] | select(. != null and startswith("node"))] | unique | .[]')
echo "::error::Cannot find .nvmrc in the HEAD commit." echo "NODE_VERSION: ${NODE_VERSION}"
if [[ $(echo "$NODE_VERSION" | wc -l) -gt 1 ]]; then
echo "::error::More than one node version used in 'action.yml' files."
exit 1 exit 1
fi fi
NODE_VERSION=$(cat .nvmrc)
echo "NODE_VERSION: ${NODE_VERSION}"
echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT
- id: checkout-base - name: Fetch base commit
name: 'Backport: Check out base ref' id: fetch-base
# Forks and Dependabot PRs don't have permission to write comments, so skip the repo size
# check in those cases.
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Compare against the merge base so the size delta reflects only the commits actually
# added by this PR, ignoring any changes that have landed on the base branch since the
# PR branched off.
merge_base=$(gh api "repos/$GITHUB_REPOSITORY/compare/$BASE_SHA...$HEAD_SHA" --jq '.merge_base_commit.sha')
echo "merge_base=$merge_base" >> "$GITHUB_OUTPUT"
git fetch --no-tags --depth=1 origin "$merge_base" "$HEAD_SHA"
- name: Check repo size
if: steps.fetch-base.outcome == 'success'
working-directory: pr-checks
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
BASE_SHA: ${{ steps.fetch-base.outputs.merge_base }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: npx tsx check-repo-size.ts --output-dir "$RUNNER_TEMP/repo-size"
- name: Upload repo size comment
if: steps.fetch-base.outcome == 'success'
uses: actions/upload-artifact@v7
with:
name: repo-size-comment
path: ${{ runner.temp }}/repo-size/
if-no-files-found: error
- name: 'Backport: Check out base ref'
id: checkout-base
if: ${{ startsWith(github.head_ref, 'backport-') }}
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
ref: ${{ env.BASE_REF }} ref: ${{ github.base_ref }}
fetch-depth: 1
- name: 'Backport: Verify Node versions unchanged' - name: 'Backport: Verify Node versions unchanged'
if: steps.checkout-base.outcome == 'success'
env: env:
HEAD_VERSION: ${{ steps.head-version.outputs.node_version }} HEAD_VERSION: ${{ steps.head-version.outputs.node_version }}
run: | run: |
if [[ ! -f ".nvmrc" ]]; then BASE_VERSION=$(find . -path "*/node_modules" -prune -o -name "action.yml" -exec yq -o=json '.runs.using' {} \; | jq -rs '[.[] | select(. != null and startswith("node"))] | unique | .[]')
echo "::error::Cannot find .nvmrc in the base commit."
exit 1
fi
BASE_VERSION=$(cat .nvmrc)
echo "HEAD_VERSION: ${HEAD_VERSION}" echo "HEAD_VERSION: ${HEAD_VERSION}"
echo "BASE_VERSION: ${BASE_VERSION}" echo "BASE_VERSION: ${BASE_VERSION}"
if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then
echo "::error::Cannot change the Node version of an Action in a backport PR." echo "::error::Cannot change the Node version of an Action in a backport PR."
exit 1 exit 1
fi fi
post-repo-size-comment:
name: Post repo size comment
needs: other-checks
# Keep write permissions isolated from the job that checks out and tests PR code. This job only
# posts the candidate comment body produced by the read-only `pr-checks` job.
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]' &&
needs.other-checks.result == 'success'
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-slim
timeout-minutes: 10
concurrency:
cancel-in-progress: true
group: check-repo-size-${{ github.event.pull_request.number }}
steps:
- name: Download repo size comment
uses: actions/download-artifact@v8
with:
name: repo-size-comment
path: repo-size-comment
- name: Post repo size comment
env:
COMMENT_MARKER: "<!-- repo-size-diff-bot -->"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
significant=$(jq -r '.significant' repo-size-comment/metadata.json)
comment_id=$(
gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \
--paginate \
--jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" \
| head -n 1
)
if [[ -n "$comment_id" ]]; then
echo "Updating existing comment $comment_id."
gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" --field body=@repo-size-comment/body.md
elif [[ "$significant" == "true" ]]; then
echo "Creating new repo size comment."
gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" --field body=@repo-size-comment/body.md
else
echo "Skipping repo size comment because the delta is below the threshold and no sticky comment exists."
fi
+4
View File
@@ -14,6 +14,10 @@ on:
- cron: '0 0 * * 1' - cron: '0 0 * * 1'
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
+4
View File
@@ -17,6 +17,10 @@ on:
- cron: '0 5 * * *' - cron: '0 5 * * *'
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -18,6 +18,11 @@ on:
schedule: schedule:
- cron: '0 5 * * *' - cron: '0 5 * * *'
workflow_dispatch: workflow_dispatch:
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: ${{ github.workflow }}-${{ github.ref }}
defaults: defaults:
run: run:
shell: bash shell: bash
-1
View File
@@ -1 +0,0 @@
24
+5
View File
@@ -4,8 +4,13 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
## [UNRELEASED] ## [UNRELEASED]
No user facing changes.
## 4.36.0 - 22 May 2026
- _Breaking change_: Bump the minimum required CodeQL bundle version to 2.19.4. [#3894](https://github.com/github/codeql-action/pull/3894) - _Breaking change_: Bump the minimum required CodeQL bundle version to 2.19.4. [#3894](https://github.com/github/codeql-action/pull/3894)
- Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893) - Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893)
- Update default CodeQL bundle version to [2.25.5](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.5). [#3926](https://github.com/github/codeql-action/pull/3926)
## 4.35.5 - 15 May 2026 ## 4.35.5 - 15 May 2026
+2 -73
View File
@@ -4,7 +4,6 @@ import { fileURLToPath } from "node:url";
import * as esbuild from "esbuild"; import * as esbuild from "esbuild";
import { globSync } from "glob"; import { globSync } from "glob";
import * as yaml from "js-yaml";
import pkg from "./package.json" with { type: "json" }; import pkg from "./package.json" with { type: "json" };
@@ -28,70 +27,6 @@ const cleanPlugin = {
}, },
}; };
/** A plugin that checks that the Node versions in all `action.yml` files are the same. */
const checkNodeVersionsPlugin = {
name: "check-node-versions",
setup(build) {
build.onStart(async () => {
// Find all the `action.yml` files. We don't care about the stub in the repository root,
// since that is a `composite` action.
const actionSpecifications = globSync("*/action.yml");
// Track the Node versions we find for each file.
const nodeVersions = {};
// We will store the first Node version we find and use it to compare against the others.
// If there's any disagreement, we set `versionMismatch` to `true` and throw an error
// that includes all the discovered Node versions at the end.
let nodeVersion = undefined;
let versionMismatch = false;
for (const actionSpecification of actionSpecifications) {
// Read the contents of the action.yml file.
const contents = await readFile(actionSpecification, "utf-8");
const specification = yaml.load(contents);
// Find the `runs.using` value in the specification.
const using = specification.runs.using;
if (using === undefined || using === null) {
throw new Error(
`Couldn't find 'runs.using' in ${actionSpecification}`,
);
}
if (typeof using !== "string" || !using.startsWith("node")) {
throw new Error(
`Expected 'runs.using' to be a string starting with 'node' in ${actionSpecification}`,
);
}
if (nodeVersion === undefined) {
// First one we found: set it as the baseline.
nodeVersion = using;
} else if (nodeVersion !== using) {
// Disagreement: set `versionMismatch` to indicate that we should throw an error later.
versionMismatch = true;
}
nodeVersions[actionSpecification] = using;
}
// Throw an error if there was a version mismatch.
if (versionMismatch) {
throw new Error(
`More than one node version used in 'action.yml' files: ${JSON.stringify(nodeVersions)}`,
);
}
// Write the node version to `.nvmrc`.
await writeFile(
join(__dirname, ".nvmrc"),
nodeVersion.substring("node".length) + "\n",
"utf-8",
);
});
},
};
/** /**
* Copy defaults.json to the output directory since other projects depend on it. * Copy defaults.json to the output directory since other projects depend on it.
* *
@@ -143,7 +78,7 @@ const UPLOAD_LIB_SRC = "./src/upload-lib";
* *
* The virtual module additionally re-exports `upload-lib` under the `uploadLib` namespace so that * The virtual module additionally re-exports `upload-lib` under the `uploadLib` namespace so that
* external consumers can access it via the small `lib/upload-lib.js` stub emitted below. * external consumers can access it via the small `lib/upload-lib.js` stub emitted below.
* *
* A tiny stub file is emitted for each Action entrypoint, and one for `upload-lib`. Each stub * A tiny stub file is emitted for each Action entrypoint, and one for `upload-lib`. Each stub
* imports the shared bundle and calls/re-exports from the respective entry point. * imports the shared bundle and calls/re-exports from the respective entry point.
* *
@@ -273,13 +208,7 @@ const context = await esbuild.context({
outdir: OUT_DIR, outdir: OUT_DIR,
platform: "node", platform: "node",
external: ["./entry-points"], external: ["./entry-points"],
plugins: [ plugins: [cleanPlugin, copyDefaultsPlugin, entryPointsPlugin, onEndPlugin],
cleanPlugin,
checkNodeVersionsPlugin,
copyDefaultsPlugin,
entryPointsPlugin,
onEndPlugin,
],
target: ["node20"], target: ["node20"],
define: { define: {
__CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version), __CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version),
+21
View File
@@ -140,6 +140,18 @@ export default [
"no-async-foreach/no-async-foreach": "error", "no-async-foreach/no-async-foreach": "error",
"no-sequences": "error", "no-sequences": "error",
"no-shadow": "off", "no-shadow": "off",
// A basic check that we don't use `exportVariable` from `@actions/core`. This rule depends on
// the module being imported as `core`, but that is a good enough check for us.
"no-restricted-syntax": [
"error",
{
selector:
"MemberExpression[object.name='core'][property.name='exportVariable']",
message: "Use `exportVariable` from `environment.ts` instead.",
},
],
// This is overly restrictive with unsetting `EnvVar`s // This is overly restrictive with unsetting `EnvVar`s
"@typescript-eslint/no-dynamic-delete": "off", "@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/no-shadow": "error", "@typescript-eslint/no-shadow": "error",
@@ -157,6 +169,15 @@ export default [
], ],
}, },
}, },
{
files: ["src/environment.ts"],
// We allow `exportVariable` from `@actions/core` to be used in this file
// since it defines the wrapper around it that other modules use.
rules: {
"no-restricted-syntax": "off",
},
},
{ {
files: ["**/*.ts", "**/*.js"], files: ["**/*.ts", "**/*.js"],
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"bundleVersion": "codeql-bundle-v2.25.4", "bundleVersion": "codeql-bundle-v2.25.5",
"cliVersion": "2.25.4", "cliVersion": "2.25.5",
"priorBundleVersion": "codeql-bundle-v2.25.3", "priorBundleVersion": "codeql-bundle-v2.25.4",
"priorCliVersion": "2.25.3" "priorCliVersion": "2.25.4"
} }
+450 -446
View File
File diff suppressed because it is too large Load Diff
+11 -11
View File
@@ -1,12 +1,12 @@
{ {
"name": "codeql", "name": "codeql",
"version": "4.36.0", "version": "4.36.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "codeql", "name": "codeql",
"version": "4.36.0", "version": "4.36.1",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"pr-checks" "pr-checks"
@@ -3795,9 +3795,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.13", "version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
@@ -5122,9 +5122,9 @@
} }
}, },
"node_modules/eslint-plugin-import-x/node_modules/brace-expansion": { "node_modules/eslint-plugin-import-x/node_modules/brace-expansion": {
"version": "5.0.5", "version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -6078,9 +6078,9 @@
} }
}, },
"node_modules/glob/node_modules/brace-expansion": { "node_modules/glob/node_modules/brace-expansion": {
"version": "5.0.5", "version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^4.0.2" "balanced-match": "^4.0.2"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "codeql", "name": "codeql",
"version": "4.36.0", "version": "4.36.1",
"private": true, "private": true,
"description": "CodeQL action", "description": "CodeQL action",
"scripts": { "scripts": {
+259
View File
@@ -0,0 +1,259 @@
#!/usr/bin/env npx tsx
/*
Tests for check-repo-size.ts.
*/
import * as assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { randomBytes } from "node:crypto";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { afterEach, beforeEach, describe, it } from "node:test";
import {
COMMENT_MARKER,
DEFAULT_BASE_REF,
buildCommentBody,
formatBytes,
formatPercent,
isDeltaSignificant,
measureArchiveSize,
readArgs,
} from "./check-repo-size";
describe("formatBytes", async () => {
const cases: Array<[number, boolean, string]> = [
// Unsigned values, including sub-KiB amounts which round to 0.00.
[0, false, "0.00 KiB"],
[512, false, "0.50 KiB"],
[1024, false, "1.00 KiB"],
[1024 * 1024, false, "1024.00 KiB"],
[2 * 1024 * 1024, false, "2048.00 KiB"],
// Negative values always use a leading minus.
[-2 * 1024 * 1024, false, "-2048.00 KiB"],
// signed=true prepends a + to non-negative values.
[0, true, "+0.00 KiB"],
[2 * 1024 * 1024, true, "+2048.00 KiB"],
[-2 * 1024 * 1024, true, "-2048.00 KiB"],
];
for (const [bytes, signed, expected] of cases) {
await it(`formats ${bytes} (signed=${signed}) as ${expected}`, () => {
assert.equal(formatBytes(bytes, signed), expected);
});
}
});
describe("formatPercent", async () => {
await it("formats positive fractions with a leading +", () => {
assert.equal(formatPercent(0.1), "+10.00%");
assert.equal(formatPercent(0.0123), "+1.23%");
});
await it("formats negative fractions with a leading -", () => {
assert.equal(formatPercent(-0.1), "-10.00%");
});
await it("formats zero without a sign", () => {
assert.equal(formatPercent(0), "0.00%");
});
});
describe("isDeltaSignificant", async () => {
const cases: Array<[number, number, number, boolean]> = [
// At and above threshold (both signs).
[100, 1000, 0.1, true],
[101, 1000, 0.1, true],
[-100, 1000, 0.1, true],
// Below threshold (both signs, plus exact zero).
[99, 1000, 0.1, false],
[-99, 1000, 0.1, false],
[0, 1000, 0.1, false],
];
for (const [delta, base, fraction, expected] of cases) {
await it(`returns ${expected} for delta=${delta}, base=${base}, fraction=${fraction}`, () => {
assert.equal(isDeltaSignificant(delta, base, fraction), expected);
});
}
});
describe("buildCommentBody", async () => {
await it("includes the marker, the base/PR/delta rows, and the run URL", () => {
const body = buildCommentBody({
baseRef: "main",
baseSize: 2_000_000,
prSize: 2_300_000,
runUrl: "https://example.test/run",
});
assert.match(body, new RegExp(`^${escapeRegExp(COMMENT_MARKER)}`));
assert.match(body, /Base \(`main`\) \| 1953\.13 KiB \(2000000 bytes\)/);
assert.match(body, /This PR \| 2246\.09 KiB \(2300000 bytes\)/);
assert.match(
body,
/\*\*Delta\*\* \| \*\*\+292\.97 KiB \(\+300000 bytes, \+15\.00%\)\*\*/,
);
assert.match(body, /\[workflow run\]\(https:\/\/example\.test\/run\)/);
});
await it("formats negative deltas with a leading minus and omits the run URL when missing", () => {
const body = buildCommentBody({
baseRef: "main",
baseSize: 2_000_000,
prSize: 1_800_000,
});
assert.match(
body,
/\*\*Delta\*\* \| \*\*-195\.31 KiB \(-200000 bytes, -10\.00%\)\*\*/,
);
assert.doesNotMatch(body, /workflow run/);
});
});
describe("readArgs", async () => {
await it("defaults the base ref and head commit for local runs", () => {
const originalEnv = process.env;
const originalArgv = process.argv;
try {
process.env = {};
process.argv = ["node", "check-repo-size.ts", "--output-dir", "/tmp/out"];
const args = readArgs();
assert.equal(args.baseRef, DEFAULT_BASE_REF);
assert.equal(args.baseCommitish, `origin/${DEFAULT_BASE_REF}`);
assert.equal(args.headCommitish, "HEAD");
assert.equal(args.outputDir, "/tmp/out");
assert.equal(args.runUrl, undefined);
} finally {
process.env = originalEnv;
process.argv = originalArgv;
}
});
await it("uses the base and head SHAs when provided by the workflow", () => {
const originalEnv = process.env;
const originalArgv = process.argv;
try {
process.env = {
BASE_REF: "main",
BASE_SHA: "abc123",
HEAD_SHA: "def456",
RUN_URL: "https://example.test/run",
};
process.argv = ["node", "check-repo-size.ts", "--output-dir", "/tmp/out"];
const args = readArgs();
assert.equal(args.baseRef, "main");
assert.equal(args.baseCommitish, "abc123");
assert.equal(args.headCommitish, "def456");
assert.equal(args.outputDir, "/tmp/out");
assert.equal(args.runUrl, "https://example.test/run");
} finally {
process.env = originalEnv;
process.argv = originalArgv;
}
});
await it("throws when --output-dir is missing", () => {
const originalEnv = process.env;
const originalArgv = process.argv;
try {
process.env = {};
process.argv = ["node", "check-repo-size.ts"];
assert.throws(() => readArgs(), /--output-dir is required/);
} finally {
process.env = originalEnv;
process.argv = originalArgv;
}
});
});
let repoDir: string;
beforeEach(() => {
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), "check-repo-size-test-"));
execFileSync("git", ["init", "--initial-branch=main", "-q"], {
cwd: repoDir,
});
execFileSync("git", ["config", "user.email", "test@example.test"], {
cwd: repoDir,
});
execFileSync("git", ["config", "user.name", "Test"], { cwd: repoDir });
execFileSync("git", ["config", "commit.gpgsign", "false"], { cwd: repoDir });
});
afterEach(() => {
fs.rmSync(repoDir, { recursive: true, force: true });
});
function commit(name: string, content: string, message: string) {
fs.writeFileSync(path.join(repoDir, name), content);
execFileSync("git", ["add", name], { cwd: repoDir });
execFileSync("git", ["commit", "-q", "-m", message], { cwd: repoDir });
}
describe("measureArchiveSize", async () => {
await it("returns a positive byte count for a non-empty repo", async () => {
commit("a.txt", "hello world\n", "first");
const size = await measureArchiveSize("HEAD", repoDir);
assert.ok(size > 0, `expected size > 0, got ${size}`);
});
await it("returns the same size on repeated runs (deterministic)", async () => {
commit("a.txt", "hello world\n", "first");
const a = await measureArchiveSize("HEAD", repoDir);
const b = await measureArchiveSize("HEAD", repoDir);
assert.equal(a, b);
});
await it("returns a larger size when more content is added", async () => {
commit("a.txt", "hello world\n", "first");
const small = await measureArchiveSize("HEAD", repoDir);
// Use random bytes so the new content is incompressible and the archive
// is guaranteed to grow even after gzip.
commit("b.bin", randomBytes(8192).toString("base64"), "second");
const big = await measureArchiveSize("HEAD", repoDir);
assert.ok(
big > small,
`expected ${big} > ${small} after adding more content`,
);
});
await it("ignores untracked files (e.g. node_modules)", async () => {
commit("a.txt", "hello\n", "first");
commit(".gitignore", "node_modules/\n", "ignore node_modules");
const sizeBefore = await measureArchiveSize("HEAD", repoDir);
fs.mkdirSync(path.join(repoDir, "node_modules"));
fs.writeFileSync(
path.join(repoDir, "node_modules", "huge.bin"),
"x".repeat(1_000_000),
);
const sizeAfter = await measureArchiveSize("HEAD", repoDir);
assert.equal(
sizeAfter,
sizeBefore,
"untracked node_modules should not affect the archive size",
);
});
await it("rejects when the ref does not exist", async () => {
commit("a.txt", "hello\n", "first");
await assert.rejects(
() => measureArchiveSize("does-not-exist", repoDir),
/git archive does-not-exist exited with code/,
);
});
});
function escapeRegExp(s: string): string {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
+223
View File
@@ -0,0 +1,223 @@
#!/usr/bin/env npx tsx
/*
Measures the difference in the `.tar.gz`'d checkout size of the repo between the PR head and the PR
base. This size is relevant because it corresponds to the duration of the "Download action
repository" step that happens at the start of every job that uses this Action.
Writes the candidate sticky-comment body and a small metadata file to `--output-dir`. A separate
workflow job consumes those artifacts and decides whether to create or update a PR comment.
*/
import { spawn } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import { parseArgs } from "node:util";
import { REPO_ROOT } from "./config";
/** Hidden marker used to find the existing sticky comment on a PR. */
export const COMMENT_MARKER = "<!-- repo-size-diff-bot -->";
export const DEFAULT_BASE_REF = "main";
/**
* Fraction of the base archive size at which a delta is considered significant enough to warrant
* a new sticky comment. We always update an existing comment regardless, so the comment stays in
* sync as the diff evolves.
*/
export const SIGNIFICANT_DELTA_FRACTION = 0.1;
/**
* Stream `git archive --format=tar.gz <ref>` and count the compressed bytes.
*
* `git archive` only includes tracked files, so untracked directories like `node_modules` and
* `build` aren't counted in the size downloaded when starting up a CodeQL job.
*/
export async function measureArchiveSize(
ref: string,
cwd: string,
): Promise<number> {
const git = spawn("git", ["archive", "--format=tar.gz", ref], { cwd });
let stderr = "";
git.stderr.on("data", (chunk: Buffer) => {
stderr += chunk.toString();
});
let size = 0;
git.stdout.on("data", (chunk: Buffer) => {
size += chunk.length;
});
const exitCode = await new Promise<number>((resolve, reject) => {
git.on("error", reject);
git.on("close", resolve);
});
if (exitCode !== 0) {
throw new Error(
`git archive ${ref} exited with code ${exitCode}: ${stderr.trim()}`,
);
}
return size;
}
/**
* Format a byte count as KiB. If `signed` is true, a leading `+` is prepended for non-negative
* values so gains and losses are visually distinct.
*/
export function formatBytes(bytes: number, signed = false): string {
const sign = bytes < 0 ? "-" : signed ? "+" : "";
const kib = Math.abs(bytes) / 1024;
return `${sign}${kib.toFixed(2)} KiB`;
}
/** Format a fraction as a signed percentage with 2 decimal places. */
export function formatPercent(fraction: number): string {
const pct = fraction * 100;
const sign = pct > 0 ? "+" : "";
return `${sign}${pct.toFixed(2)}%`;
}
export interface CommentBodyOptions {
baseRef: string;
baseSize: number;
prSize: number;
/** Optional URL of the workflow run, included in the comment footer. */
runUrl?: string;
}
export function buildCommentBody(opts: CommentBodyOptions): string {
const { baseRef, baseSize, prSize, runUrl } = opts;
const delta = prSize - baseSize;
const signedDelta = delta >= 0 ? `+${delta}` : `${delta}`;
const runUrlLine = runUrl
? ` See the [workflow run](${runUrl}) for details.`
: "";
return [
COMMENT_MARKER,
"### Repository checkout size",
"",
"| | Compressed archive size |",
"|---|---|",
`| Base (\`${baseRef}\`) | ${formatBytes(baseSize)} (${baseSize} bytes) |`,
`| This PR | ${formatBytes(prSize)} (${prSize} bytes) |`,
`| **Delta** | **${formatBytes(delta, true)} (${signedDelta} bytes, ${formatPercent(delta / baseSize)})** |`,
"",
"Sizes are measured by streaming `git archive --format=tar.gz <ref>`, " +
"which includes tracked files and excludes untracked files such as " +
"`node_modules`. The compressed checkout is " +
"downloaded by every consumer of this Action, so changes here directly " +
`affect Action download time.${runUrlLine}`,
].join("\n");
}
/**
* Returns true when the absolute delta is at least `fraction` of the base size. Both increases and
* decreases are considered significant, so we report wins as well as losses.
*/
export function isDeltaSignificant(
delta: number,
baseSize: number,
fraction: number,
): boolean {
return Math.abs(delta) >= baseSize * fraction;
}
interface MainArgs {
/** Base ref of the PR. Defaults to `main`. Used as the label in the PR comment. */
baseRef: string;
/** Base commit-ish to archive. Defaults to `origin/<baseRef>` for local runs. */
baseCommitish: string;
/** Head commit-ish to archive. Defaults to `HEAD` for local runs. */
headCommitish: string;
/** Optional URL of the workflow run, surfaced in the comment footer. */
runUrl?: string;
/** Directory where `body.md` and `metadata.json` are written. */
outputDir: string;
}
export function readArgs(): MainArgs {
const { values } = parseArgs({
options: {
"output-dir": { type: "string" },
},
strict: true,
});
const outputDir = values["output-dir"];
if (!outputDir) {
throw new Error("--output-dir is required");
}
const baseRef = process.env.BASE_REF ?? DEFAULT_BASE_REF;
const baseCommitish = process.env.BASE_SHA ?? `origin/${baseRef}`;
const headCommitish = process.env.HEAD_SHA ?? "HEAD";
return {
baseRef,
baseCommitish,
headCommitish,
runUrl: process.env.RUN_URL,
outputDir,
};
}
async function main(): Promise<number> {
const args = readArgs();
console.log(`Measuring base archive size for ${args.baseCommitish}...`);
const baseSize = await measureArchiveSize(args.baseCommitish, REPO_ROOT);
console.log(` ${baseSize} bytes`);
console.log(`Measuring PR archive size for ${args.headCommitish}...`);
const prSize = await measureArchiveSize(args.headCommitish, REPO_ROOT);
console.log(` ${prSize} bytes`);
const delta = prSize - baseSize;
const significant = isDeltaSignificant(
delta,
baseSize,
SIGNIFICANT_DELTA_FRACTION,
);
console.log(
`Delta: ${delta} bytes (significant=${significant}, threshold=${(
SIGNIFICANT_DELTA_FRACTION * 100
).toFixed(2)}%)`,
);
const body = buildCommentBody({
baseRef: args.baseRef,
baseSize,
prSize,
runUrl: args.runUrl,
});
fs.mkdirSync(args.outputDir, { recursive: true });
fs.writeFileSync(path.join(args.outputDir, "body.md"), body);
fs.writeFileSync(
path.join(args.outputDir, "metadata.json"),
`${JSON.stringify(
{ significant, baseRef: args.baseRef, baseSize, prSize, delta },
null,
2,
)}\n`,
);
console.log(`Wrote body.md and metadata.json to ${args.outputDir}.`);
return 0;
}
async function run(): Promise<void> {
try {
process.exit(await main());
} catch (err) {
console.error(err instanceof Error ? err.message : String(err));
process.exit(1);
}
}
if (require.main === module) {
void run();
}
+5 -2
View File
@@ -6,14 +6,17 @@ export const OLDEST_SUPPORTED_MAJOR_VERSION = 3;
/** The `pr-checks` directory. */ /** The `pr-checks` directory. */
export const PR_CHECKS_DIR = __dirname; export const PR_CHECKS_DIR = __dirname;
/** The repository root. */
export const REPO_ROOT = path.join(PR_CHECKS_DIR, "..");
/** The path of the file configuring which checks shouldn't be required. */ /** The path of the file configuring which checks shouldn't be required. */
export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml"); export const PR_CHECK_EXCLUDED_FILE = path.join(PR_CHECKS_DIR, "excluded.yml");
/** The path to the esbuild metadata file. */ /** The path to the esbuild metadata file. */
export const BUNDLE_METADATA_FILE = path.join(PR_CHECKS_DIR, "..", "meta.json"); export const BUNDLE_METADATA_FILE = path.join(REPO_ROOT, "meta.json");
/** The `src` directory. */ /** The `src` directory. */
const SOURCE_ROOT = path.join(PR_CHECKS_DIR, "..", "src"); const SOURCE_ROOT = path.join(REPO_ROOT, "src");
/** The path to the built-in languages file. */ /** The path to the built-in languages file. */
export const BUILTIN_LANGUAGES_FILE = path.join( export const BUILTIN_LANGUAGES_FILE = path.join(
+8 -7
View File
@@ -1,16 +1,17 @@
# PR checks to exclude from required checks # PR checks to exclude from required checks
contains: contains:
- "https://"
- "Update"
- "ESLint" - "ESLint"
- "update" - "https://"
- "test-setup-python-scripts" - "test-setup-python-scripts"
- "update"
- "Update"
is: is:
- "Agent"
- "check-expected-release-files"
- "Cleanup artifacts"
- "CodeQL" - "CodeQL"
- "Dependabot" - "Dependabot"
- "check-expected-release-files" - "Label PR with size"
- "Agent" - "Post repo size comment"
- "Cleanup artifacts"
- "Prepare" - "Prepare"
- "Upload results" - "Upload results"
- "Label PR with size"
@@ -23,7 +23,8 @@ predicate isSafeForDefaultSetup(string envVar) {
"GITHUB_BASE_REF", "GITHUB_EVENT_NAME", "GITHUB_JOB", "GITHUB_RUN_ATTEMPT", "GITHUB_RUN_ID", "GITHUB_BASE_REF", "GITHUB_EVENT_NAME", "GITHUB_JOB", "GITHUB_RUN_ATTEMPT", "GITHUB_RUN_ID",
"GITHUB_SHA", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "GITHUB_TOKEN", "GITHUB_WORKFLOW", "GITHUB_SHA", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "GITHUB_TOKEN", "GITHUB_WORKFLOW",
"GITHUB_WORKSPACE", "GOFLAGS", "ImageVersion", "JAVA_TOOL_OPTIONS", "RUNNER_ARCH", "GITHUB_WORKSPACE", "GOFLAGS", "ImageVersion", "JAVA_TOOL_OPTIONS", "RUNNER_ARCH",
"RUNNER_ENVIRONMENT", "RUNNER_NAME", "RUNNER_OS", "RUNNER_TEMP", "RUNNER_TOOL_CACHE" "RUNNER_ENVIRONMENT", "RUNNER_NAME", "RUNNER_OS", "RUNNER_TEMP", "RUNNER_TOOL_CACHE",
"NODE_ENV"
] ]
} }
@@ -43,6 +44,7 @@ predicate envVarRead(DataFlow::Node node, string envVar) {
from DataFlow::Node read, string envVar from DataFlow::Node read, string envVar
where where
envVarRead(read, envVar) and envVarRead(read, envVar) and
read.getFile().getRelativePath().matches("src/%") and
not read.getFile().getBaseName().matches("%.test.ts") and not read.getFile().getBaseName().matches("%.test.ts") and
not isSafeForDefaultSetup(envVar) not isSafeForDefaultSetup(envVar)
select read, select read,
+3 -3
View File
@@ -28,7 +28,7 @@ import {
DependencyCacheUploadStatusReport, DependencyCacheUploadStatusReport,
uploadDependencyCaches, uploadDependencyCaches,
} from "./dependency-caching"; } from "./dependency-caching";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { initFeatures } from "./feature-flags"; import { initFeatures } from "./feature-flags";
import { BuiltInLanguage } from "./languages"; import { BuiltInLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging"; import { getActionsLogger, Logger } from "./logging";
@@ -284,7 +284,7 @@ async function run(startedAt: Date) {
const apiDetails = getApiDetails(); const apiDetails = getApiDetails();
const outputDir = actionsUtil.getRequiredInput("output"); const outputDir = actionsUtil.getRequiredInput("output");
core.exportVariable(EnvVar.SARIF_RESULTS_OUTPUT_DIR, outputDir); exportVariable(EnvVar.SARIF_RESULTS_OUTPUT_DIR, outputDir);
const threads = util.getThreadsFlag( const threads = util.getThreadsFlag(
actionsUtil.getOptionalInput("threads") || process.env["CODEQL_THREADS"], actionsUtil.getOptionalInput("threads") || process.env["CODEQL_THREADS"],
logger, logger,
@@ -444,7 +444,7 @@ async function run(startedAt: Date) {
`expect-error input was set to true but no error was thrown.`, `expect-error input was set to true but no error was thrown.`,
); );
} }
core.exportVariable(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY, "true"); exportVariable(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY, "true");
} catch (unwrappedError) { } catch (unwrappedError) {
const error = util.wrapError(unwrappedError); const error = util.wrapError(unwrappedError);
if ( if (
+2 -2
View File
@@ -3,7 +3,7 @@ import * as githubUtils from "@actions/github/lib/utils";
import * as retry from "@octokit/plugin-retry"; import * as retry from "@octokit/plugin-retry";
import { getActionVersion, getRequiredInput } from "./actions-util"; import { getActionVersion, getRequiredInput } from "./actions-util";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { Logger } from "./logging"; import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository"; import { getRepositoryNwo, RepositoryNwo } from "./repository";
import { import {
@@ -216,7 +216,7 @@ export async function getAnalysisKey(): Promise<string> {
const jobName = getRequiredEnvParam("GITHUB_JOB"); const jobName = getRequiredEnvParam("GITHUB_JOB");
analysisKey = `${workflowPath}:${jobName}`; analysisKey = `${workflowPath}:${jobName}`;
core.exportVariable(EnvVar.ANALYSIS_KEY, analysisKey); exportVariable(EnvVar.ANALYSIS_KEY, analysisKey);
return analysisKey; return analysisKey;
} }
+2 -2
View File
@@ -9,7 +9,7 @@ import { getGitHubVersion } from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild"; import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import { getCodeQL } from "./codeql"; import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils"; import { Config, getConfig } from "./config-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { Language } from "./languages"; import { Language } from "./languages";
import { Logger, getActionsLogger } from "./logging"; import { Logger, getActionsLogger } from "./logging";
import { import {
@@ -137,7 +137,7 @@ async function run(startedAt: Date) {
return; return;
} }
core.exportVariable(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true"); exportVariable(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true");
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []); await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
} }
+5 -7
View File
@@ -1,11 +1,9 @@
import * as core from "@actions/core";
import { getTemporaryDirectory, getWorkflowEventName } from "./actions-util"; import { getTemporaryDirectory, getWorkflowEventName } from "./actions-util";
import { getGitHubVersion } from "./api-client"; import { getGitHubVersion } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql"; import { CodeQL, getCodeQL } from "./codeql";
import * as configUtils from "./config-utils"; import * as configUtils from "./config-utils";
import { DocUrl } from "./doc-url"; import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { Feature, featureConfig, initFeatures } from "./feature-flags"; import { Feature, featureConfig, initFeatures } from "./feature-flags";
import { BuiltInLanguage, Language } from "./languages"; import { BuiltInLanguage, Language } from "./languages";
import { Logger } from "./logging"; import { Logger } from "./logging";
@@ -136,16 +134,16 @@ export async function setupCppAutobuild(codeql: CodeQL, logger: Logger) {
: "" : ""
}`, }`,
); );
core.exportVariable(envVar, "false"); exportVariable(envVar, "false");
} else { } else {
logger.info( logger.info(
`Enabling ${featureName}. This can be disabled by setting the ${envVar} environment variable to 'false'. See ${DocUrl.DEFINE_ENV_VARIABLES} for more information.`, `Enabling ${featureName}. This can be disabled by setting the ${envVar} environment variable to 'false'. See ${DocUrl.DEFINE_ENV_VARIABLES} for more information.`,
); );
core.exportVariable(envVar, "true"); exportVariable(envVar, "true");
} }
} else { } else {
logger.info(`Disabling ${featureName}.`); logger.info(`Disabling ${featureName}.`);
core.exportVariable(envVar, "false"); exportVariable(envVar, "false");
} }
} }
@@ -165,7 +163,7 @@ export async function runAutobuild(
await codeQL.runAutobuild(config, language); await codeQL.runAutobuild(config, language);
} }
if (language === BuiltInLanguage.go) { if (language === BuiltInLanguage.go) {
core.exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true"); exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true");
} }
logger.endGroup(); logger.endGroup();
} }
+2 -2
View File
@@ -15,7 +15,7 @@ import * as api from "./api-client";
import { CliError, wrapCliConfigurationError } from "./cli-errors"; import { CliError, wrapCliConfigurationError } from "./cli-errors";
import { appendExtraQueryExclusions, type Config } from "./config-utils"; import { appendExtraQueryExclusions, type Config } from "./config-utils";
import { DocUrl } from "./doc-url"; import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { import {
CodeQLDefaultVersionInfo, CodeQLDefaultVersionInfo,
Feature, Feature,
@@ -1096,7 +1096,7 @@ async function getCodeQLForCmd(
}' by 'github/codeql-action/*@v${getActionVersion()}' in your code scanning workflow to ` + }' by 'github/codeql-action/*@v${getActionVersion()}' in your code scanning workflow to ` +
"continue using this version of the CodeQL Action.", "continue using this version of the CodeQL Action.",
); );
core.exportVariable(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING, "true"); exportVariable(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING, "true");
} }
return codeql; return codeql;
} }
+3 -4
View File
@@ -2,7 +2,6 @@ import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import { performance } from "perf_hooks"; import { performance } from "perf_hooks";
import * as core from "@actions/core";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import { import {
@@ -32,7 +31,7 @@ import {
makeTelemetryDiagnostic, makeTelemetryDiagnostic,
} from "./diagnostics"; } from "./diagnostics";
import { prepareDiffInformedAnalysis } from "./diff-informed-analysis-utils"; import { prepareDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import * as errorMessages from "./error-messages"; import * as errorMessages from "./error-messages";
import { Feature, FeatureEnablement } from "./feature-flags"; import { Feature, FeatureEnablement } from "./feature-flags";
import { import {
@@ -1045,10 +1044,10 @@ async function setCppTrapCachingEnvironmentVariables(
); );
} else if (config.trapCaches[BuiltInLanguage.cpp]) { } else if (config.trapCaches[BuiltInLanguage.cpp]) {
logger.info("Enabling TRAP caching for C/C++."); logger.info("Enabling TRAP caching for C/C++.");
core.exportVariable(envVar, "true"); exportVariable(envVar, "true");
} else { } else {
logger.debug(`Disabling TRAP caching for C/C++.`); logger.debug(`Disabling TRAP caching for C/C++.`);
core.exportVariable(envVar, "false"); exportVariable(envVar, "false");
} }
} }
} }
+2 -2
View File
@@ -11,7 +11,7 @@ import { dbIsFinalized } from "./analyze";
import { scanArtifactsForTokens } from "./artifact-scanner"; import { scanArtifactsForTokens } from "./artifact-scanner";
import { type CodeQL } from "./codeql"; import { type CodeQL } from "./codeql";
import { Config } from "./config-utils"; import { Config } from "./config-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import * as json from "./json"; import * as json from "./json";
import { Language } from "./languages"; import { Language } from "./languages";
import { Logger, withGroup } from "./logging"; import { Logger, withGroup } from "./logging";
@@ -330,7 +330,7 @@ export async function uploadArtifacts(
// some issues early. // some issues early.
if (isInTestMode()) { if (isInTestMode()) {
await scanArtifactsForTokens(toUpload, logger); await scanArtifactsForTokens(toUpload, logger);
core.exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true"); exportVariable("CODEQL_ACTION_ARTIFACT_SCAN_FINISHED", "true");
} }
const suffix = getArtifactSuffix(getOptionalInput("matrix")); const suffix = getArtifactSuffix(getOptionalInput("matrix"));
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"bundleVersion": "codeql-bundle-v2.25.4", "bundleVersion": "codeql-bundle-v2.25.5",
"cliVersion": "2.25.4", "cliVersion": "2.25.5",
"priorBundleVersion": "codeql-bundle-v2.25.3", "priorBundleVersion": "codeql-bundle-v2.25.4",
"priorCliVersion": "2.25.3" "priorCliVersion": "2.25.4"
} }
+28
View File
@@ -1,3 +1,5 @@
import * as core from "@actions/core";
/** /**
* Environment variables used by the CodeQL Action. * Environment variables used by the CodeQL Action.
* *
@@ -154,3 +156,29 @@ export enum EnvVar {
/** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */ /** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */
RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID", RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID",
} }
/**
* Returns whether we are in test mode. This is used by CodeQL Action PR checks.
*
* In test mode, we skip several uploads (SARIF results, status reports, DBs, ...).
*/
export function isInTestMode(): boolean {
return process.env[EnvVar.TEST_MODE] === "true";
}
/**
* Wrapper around `core.exportVariable` which does not call `core.exportVariable`
* when running unit tests. This is important, because otherwise `core.exportVariable`
* sets environment variables for other steps in a workflow when we run unit tests in CI.
*/
export function exportVariable(name: string, val: any): void {
if (process.env["NODE_ENV"] === "test") {
// Setting the environment variable for the current process is OK since we reset
// those at the end of each test. This allows tests to pass that rely on that
// part of the `core.exportVariable` behaviour.
process.env[name] = val;
} else {
// Call `core.exportVariable` whenever we are not in a test environment.
core.exportVariable(name, val);
}
}
+3 -3
View File
@@ -20,7 +20,7 @@ import {
DependencyCachingUsageReport, DependencyCachingUsageReport,
getDependencyCacheUsage, getDependencyCacheUsage,
} from "./dependency-caching"; } from "./dependency-caching";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { initFeatures } from "./feature-flags"; import { initFeatures } from "./feature-flags";
import * as gitUtils from "./git-utils"; import * as gitUtils from "./git-utils";
import * as initActionPostHelper from "./init-action-post-helper"; import * as initActionPostHelper from "./init-action-post-helper";
@@ -157,7 +157,7 @@ function getFinalJobStatus(config: Config | undefined): JobStatus {
let jobStatus: JobStatus; let jobStatus: JobStatus;
if (process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] === "true") { if (process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] === "true") {
core.exportVariable(EnvVar.JOB_STATUS, JobStatus.SuccessStatus); exportVariable(EnvVar.JOB_STATUS, JobStatus.SuccessStatus);
jobStatus = JobStatus.SuccessStatus; jobStatus = JobStatus.SuccessStatus;
} else if (config !== undefined) { } else if (config !== undefined) {
// - We have computed a CodeQL config // - We have computed a CodeQL config
@@ -182,7 +182,7 @@ function getFinalJobStatus(config: Config | undefined): JobStatus {
// This shouldn't be necessary, but in the odd case that we run more than one // This shouldn't be necessary, but in the odd case that we run more than one
// `init` post step, ensure the job status is consistent between them. // `init` post step, ensure the job status is consistent between them.
core.exportVariable(EnvVar.JOB_STATUS, jobStatus); exportVariable(EnvVar.JOB_STATUS, jobStatus);
return jobStatus; return jobStatus;
} }
+14 -17
View File
@@ -37,7 +37,7 @@ import {
makeDiagnostic, makeDiagnostic,
makeTelemetryDiagnostic, makeTelemetryDiagnostic,
} from "./diagnostics"; } from "./diagnostics";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags"; import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import { import {
loadPropertiesFromApi, loadPropertiesFromApi,
@@ -255,9 +255,9 @@ async function run(startedAt: Date) {
// Create a unique identifier for this run. // Create a unique identifier for this run.
const jobRunUuid = uuidV4(); const jobRunUuid = uuidV4();
logger.info(`Job run UUID is ${jobRunUuid}.`); logger.info(`Job run UUID is ${jobRunUuid}.`);
core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid); exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true"); exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true");
configFile = getOptionalInput("config-file"); configFile = getOptionalInput("config-file");
@@ -343,7 +343,7 @@ async function run(startedAt: Date) {
); );
} }
if (semver.lt(actualVer, publicPreview)) { if (semver.lt(actualVer, publicPreview)) {
core.exportVariable(EnvVar.EXPERIMENTAL_FEATURES, "true"); exportVariable(EnvVar.EXPERIMENTAL_FEATURES, "true");
logger.info("Experimental Rust analysis enabled"); logger.info("Experimental Rust analysis enabled");
} }
} }
@@ -508,7 +508,7 @@ async function run(startedAt: Date) {
// Forward Go flags // Forward Go flags
const goFlags = process.env["GOFLAGS"]; const goFlags = process.env["GOFLAGS"];
if (goFlags) { if (goFlags) {
core.exportVariable("GOFLAGS", goFlags); exportVariable("GOFLAGS", goFlags);
core.warning( core.warning(
"Passing the GOFLAGS env parameter to the init action is deprecated. Please move this to the analyze action.", "Passing the GOFLAGS env parameter to the init action is deprecated. Please move this to the analyze action.",
); );
@@ -554,7 +554,7 @@ async function run(startedAt: Date) {
// Store the original location of our wrapper script somewhere where we can // Store the original location of our wrapper script somewhere where we can
// later retrieve it from and cross-check that it hasn't been changed. // later retrieve it from and cross-check that it hasn't been changed.
core.exportVariable(EnvVar.GO_BINARY_LOCATION, goWrapperPath); exportVariable(EnvVar.GO_BINARY_LOCATION, goWrapperPath);
} catch (e) { } catch (e) {
logger.warning( logger.warning(
`Analyzing Go on Linux, but failed to install wrapper script. Tracing custom builds may fail: ${e}`, `Analyzing Go on Linux, but failed to install wrapper script. Tracing custom builds may fail: ${e}`,
@@ -563,7 +563,7 @@ async function run(startedAt: Date) {
} else { } else {
// Store the location of the original Go binary, so we can check that no setup tasks were performed after the // Store the location of the original Go binary, so we can check that no setup tasks were performed after the
// `init` Action ran. // `init` Action ran.
core.exportVariable(EnvVar.GO_BINARY_LOCATION, goBinaryPath); exportVariable(EnvVar.GO_BINARY_LOCATION, goBinaryPath);
} }
} catch (e) { } catch (e) {
logger.warning( logger.warning(
@@ -598,12 +598,12 @@ async function run(startedAt: Date) {
// threads it would ask extractors to use. See help text for the "--ram" and "--threads" // threads it would ask extractors to use. See help text for the "--ram" and "--threads"
// options at https://codeql.github.com/docs/codeql-cli/manual/database-trace-command/ // options at https://codeql.github.com/docs/codeql-cli/manual/database-trace-command/
// for details. // for details.
core.exportVariable( exportVariable(
"CODEQL_RAM", "CODEQL_RAM",
process.env["CODEQL_RAM"] || process.env["CODEQL_RAM"] ||
getCodeQLMemoryLimit(getOptionalInput("ram"), logger).toString(), getCodeQLMemoryLimit(getOptionalInput("ram"), logger).toString(),
); );
core.exportVariable( exportVariable(
"CODEQL_THREADS", "CODEQL_THREADS",
process.env["CODEQL_THREADS"] || process.env["CODEQL_THREADS"] ||
getThreadsFlagValue(getOptionalInput("threads"), logger).toString(), getThreadsFlagValue(getOptionalInput("threads"), logger).toString(),
@@ -611,7 +611,7 @@ async function run(startedAt: Date) {
// Disable Kotlin extractor if feature flag set // Disable Kotlin extractor if feature flag set
if (await features.getValue(Feature.DisableKotlinAnalysisEnabled)) { if (await features.getValue(Feature.DisableKotlinAnalysisEnabled)) {
core.exportVariable("CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN", "true"); exportVariable("CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN", "true");
} }
const kotlinLimitVar = const kotlinLimitVar =
@@ -620,7 +620,7 @@ async function run(startedAt: Date) {
(await codeQlVersionAtLeast(codeql, "2.20.3")) && (await codeQlVersionAtLeast(codeql, "2.20.3")) &&
!(await codeQlVersionAtLeast(codeql, "2.20.4")) !(await codeQlVersionAtLeast(codeql, "2.20.4"))
) { ) {
core.exportVariable(kotlinLimitVar, "2.1.20"); exportVariable(kotlinLimitVar, "2.1.20");
} }
// Restore dependency cache(s), if they exist. // Restore dependency cache(s), if they exist.
@@ -669,10 +669,7 @@ async function run(startedAt: Date) {
config.buildMode === BuildMode.None && config.buildMode === BuildMode.None &&
config.languages.includes(BuiltInLanguage.java) config.languages.includes(BuiltInLanguage.java)
) { ) {
core.exportVariable( exportVariable(EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS, "true");
EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS,
"true",
);
} }
const { registriesAuthTokens, qlconfigFile } = const { registriesAuthTokens, qlconfigFile } =
@@ -729,7 +726,7 @@ async function run(startedAt: Date) {
const tracerConfig = await getCombinedTracerConfig(codeql, config); const tracerConfig = await getCombinedTracerConfig(codeql, config);
if (tracerConfig !== undefined) { if (tracerConfig !== undefined) {
for (const [key, value] of Object.entries(tracerConfig.env)) { for (const [key, value] of Object.entries(tracerConfig.env)) {
core.exportVariable(key, value); exportVariable(key, value);
} }
} }
@@ -740,7 +737,7 @@ async function run(startedAt: Date) {
getOptionalEnvVar(JavaEnvVars.JAVA_TOOL_OPTIONS) || ""; getOptionalEnvVar(JavaEnvVars.JAVA_TOOL_OPTIONS) || "";
// Add the network debugging options. // Add the network debugging options.
core.exportVariable( exportVariable(
JavaEnvVars.JAVA_TOOL_OPTIONS, JavaEnvVars.JAVA_TOOL_OPTIONS,
`${existingJavaToolOptions} -Djavax.net.debug=all`, `${existingJavaToolOptions} -Djavax.net.debug=all`,
); );
+8 -8
View File
@@ -1,13 +1,13 @@
import * as fs from "fs"; import * as fs from "fs";
import path from "path"; import path from "path";
import * as core from "@actions/core";
import * as github from "@actions/github"; import * as github from "@actions/github";
import test, { ExecutionContext } from "ava"; import test, { ExecutionContext } from "ava";
import * as sinon from "sinon"; import * as sinon from "sinon";
import * as actionsUtil from "./actions-util"; import * as actionsUtil from "./actions-util";
import { createStubCodeQL } from "./codeql"; import { createStubCodeQL } from "./codeql";
import * as environment from "./environment";
import { Feature } from "./feature-flags"; import { Feature } from "./feature-flags";
import { import {
checkPacksForOverlayCompatibility, checkPacksForOverlayCompatibility,
@@ -545,7 +545,7 @@ test.serial(
test.serial( test.serial(
"file coverage deprecation warning for org-owned repo with default setup recommends repo property", "file coverage deprecation warning for org-owned repo with default setup recommends repo property",
(t) => { (t) => {
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(true); sinon.stub(actionsUtil, "isDefaultSetup").returns(true);
github.context.payload = { github.context.payload = {
repository: { repository: {
@@ -572,7 +572,7 @@ test.serial(
test.serial( test.serial(
"file coverage deprecation warning for org-owned repo with advanced setup recommends env var and repo property", "file coverage deprecation warning for org-owned repo with advanced setup recommends env var and repo property",
(t) => { (t) => {
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(false); sinon.stub(actionsUtil, "isDefaultSetup").returns(false);
github.context.payload = { github.context.payload = {
repository: { repository: {
@@ -600,7 +600,7 @@ test.serial(
test.serial( test.serial(
"file coverage deprecation warning for user-owned repo with default setup recommends advanced setup", "file coverage deprecation warning for user-owned repo with default setup recommends advanced setup",
(t) => { (t) => {
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(true); sinon.stub(actionsUtil, "isDefaultSetup").returns(true);
github.context.payload = { github.context.payload = {
repository: { repository: {
@@ -626,7 +626,7 @@ test.serial(
test.serial( test.serial(
"file coverage deprecation warning for user-owned repo with advanced setup recommends env var", "file coverage deprecation warning for user-owned repo with advanced setup recommends env var",
(t) => { (t) => {
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(false); sinon.stub(actionsUtil, "isDefaultSetup").returns(false);
github.context.payload = { github.context.payload = {
repository: { repository: {
@@ -651,7 +651,7 @@ test.serial(
test.serial( test.serial(
"file coverage deprecation warning for unknown owner type with default setup recommends advanced setup", "file coverage deprecation warning for unknown owner type with default setup recommends advanced setup",
(t) => { (t) => {
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(true); sinon.stub(actionsUtil, "isDefaultSetup").returns(true);
github.context.payload = { repository: undefined }; github.context.payload = { repository: undefined };
const messages: LoggedMessage[] = []; const messages: LoggedMessage[] = [];
@@ -672,7 +672,7 @@ test.serial(
test.serial( test.serial(
"file coverage deprecation warning for unknown owner type with advanced setup recommends env var", "file coverage deprecation warning for unknown owner type with advanced setup recommends env var",
(t) => { (t) => {
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(false); sinon.stub(actionsUtil, "isDefaultSetup").returns(false);
github.context.payload = { repository: undefined }; github.context.payload = { repository: undefined };
const messages: LoggedMessage[] = []; const messages: LoggedMessage[] = [];
@@ -694,7 +694,7 @@ test.serial(
(t) => { (t) => {
process.env["CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION"] = process.env["CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION"] =
"true"; "true";
const exportVariableStub = sinon.stub(core, "exportVariable"); const exportVariableStub = sinon.stub(environment, "exportVariable");
const messages: LoggedMessage[] = []; const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages)); logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 0); t.is(messages.length, 0);
+2 -3
View File
@@ -1,7 +1,6 @@
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner"; import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as github from "@actions/github"; import * as github from "@actions/github";
import * as io from "@actions/io"; import * as io from "@actions/io";
@@ -16,7 +15,7 @@ import {
import { GitHubApiDetails } from "./api-client"; import { GitHubApiDetails } from "./api-client";
import { CodeQL, setupCodeQL } from "./codeql"; import { CodeQL, setupCodeQL } from "./codeql";
import * as configUtils from "./config-utils"; import * as configUtils from "./config-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { import {
CodeQLDefaultVersionInfo, CodeQLDefaultVersionInfo,
Feature, Feature,
@@ -418,5 +417,5 @@ export function logFileCoverageOnPrsDeprecationWarning(logger: Logger): void {
} }
logger.warning(message); logger.warning(message);
core.exportVariable(EnvVar.DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION, "true"); exportVariable(EnvVar.DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION, "true");
} }
+2 -1
View File
@@ -8,6 +8,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "../actions-util"; import * as actionsUtil from "../actions-util";
import * as apiClient from "../api-client"; import * as apiClient from "../api-client";
import type { ResolveDatabaseOutput } from "../codeql"; import type { ResolveDatabaseOutput } from "../codeql";
import * as environment from "../environment";
import * as gitUtils from "../git-utils"; import * as gitUtils from "../git-utils";
import { BuiltInLanguage } from "../languages"; import { BuiltInLanguage } from "../languages";
import { getRunnerLogger } from "../logging"; import { getRunnerLogger } from "../logging";
@@ -82,7 +83,7 @@ const testDownloadOverlayBaseDatabaseFromCache = makeMacro({
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/"); sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
sinon.stub(utils, "isInTestMode").returns(testCase.isInTestMode); sinon.stub(environment, "isInTestMode").returns(testCase.isInTestMode);
if (testCase.restoreCacheResult instanceof Error) { if (testCase.restoreCacheResult instanceof Error) {
sinon sinon
+3 -3
View File
@@ -11,7 +11,7 @@ import { AnalysisKind, getAnalysisKinds } from "./analyses";
import { getGitHubVersion } from "./api-client"; import { getGitHubVersion } from "./api-client";
import { CodeQL } from "./codeql"; import { CodeQL } from "./codeql";
import { getRawLanguagesNoAutodetect } from "./config-utils"; import { getRawLanguagesNoAutodetect } from "./config-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { initFeatures } from "./feature-flags"; import { initFeatures } from "./feature-flags";
import { initCodeQL } from "./init"; import { initCodeQL } from "./init";
import { getActionsLogger, Logger } from "./logging"; import { getActionsLogger, Logger } from "./logging";
@@ -125,7 +125,7 @@ async function run(startedAt: Date): Promise<void> {
const jobRunUuid = uuidV4(); const jobRunUuid = uuidV4();
logger.info(`Job run UUID is ${jobRunUuid}.`); logger.info(`Job run UUID is ${jobRunUuid}.`);
core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid); exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
const statusReportBase = await createStatusReportBase( const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL, ActionName.SetupCodeQL,
@@ -165,7 +165,7 @@ async function run(startedAt: Date): Promise<void> {
core.setOutput("codeql-path", codeql.getPath()); core.setOutput("codeql-path", codeql.getPath());
core.setOutput("codeql-version", (await codeql.getVersion()).version); core.setOutput("codeql-version", (await codeql.getVersion()).version);
core.exportVariable(EnvVar.SETUP_CODEQL_ACTION_HAS_RUN, "true"); exportVariable(EnvVar.SETUP_CODEQL_ACTION_HAS_RUN, "true");
} catch (unwrappedError) { } catch (unwrappedError) {
const error = wrapError(unwrappedError); const error = wrapError(unwrappedError);
core.setFailed(error.message); core.setFailed(error.message);
+5 -5
View File
@@ -15,7 +15,7 @@ import { getAnalysisKey, getApiClient } from "./api-client";
import { parseRegistriesWithoutCredentials, type Config } from "./config-utils"; import { parseRegistriesWithoutCredentials, type Config } from "./config-utils";
import { DependencyCacheRestoreStatusReport } from "./dependency-caching"; import { DependencyCacheRestoreStatusReport } from "./dependency-caching";
import { DocUrl } from "./doc-url"; import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { getRef } from "./git-utils"; import { getRef } from "./git-utils";
import { Logger } from "./logging"; import { Logger } from "./logging";
import { OverlayBaseDatabaseDownloadStats } from "./overlay/caching"; import { OverlayBaseDatabaseDownloadStats } from "./overlay/caching";
@@ -216,12 +216,12 @@ export function getJobStatusDisplayName(status: JobStatus): string {
*/ */
function setJobStatusIfUnsuccessful(actionStatus: ActionStatus) { function setJobStatusIfUnsuccessful(actionStatus: ActionStatus) {
if (actionStatus === "user-error") { if (actionStatus === "user-error") {
core.exportVariable( exportVariable(
EnvVar.JOB_STATUS, EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigErrorStatus, process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigErrorStatus,
); );
} else if (actionStatus === "failure" || actionStatus === "aborted") { } else if (actionStatus === "failure" || actionStatus === "aborted") {
core.exportVariable( exportVariable(
EnvVar.JOB_STATUS, EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.FailureStatus, process.env[EnvVar.JOB_STATUS] ?? JobStatus.FailureStatus,
); );
@@ -280,7 +280,7 @@ export async function createStatusReportBase(
let workflowStartedAt = process.env[EnvVar.WORKFLOW_STARTED_AT]; let workflowStartedAt = process.env[EnvVar.WORKFLOW_STARTED_AT];
if (workflowStartedAt === undefined) { if (workflowStartedAt === undefined) {
workflowStartedAt = actionStartedAt.toISOString(); workflowStartedAt = actionStartedAt.toISOString();
core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt); exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt);
} }
const runnerOs = getRequiredEnvParam("RUNNER_OS"); const runnerOs = getRequiredEnvParam("RUNNER_OS");
const codeQlCliVersion = getCachedCodeQlVersion(); const codeQlCliVersion = getCachedCodeQlVersion();
@@ -289,7 +289,7 @@ export async function createStatusReportBase(
// re-export the testing environment variable so that it is available to subsequent steps, // re-export the testing environment variable so that it is available to subsequent steps,
// even if it was only set for this step // even if it was only set for this step
if (testingEnvironment) { if (testingEnvironment) {
core.exportVariable(EnvVar.TESTING_ENVIRONMENT, testingEnvironment); exportVariable(EnvVar.TESTING_ENVIRONMENT, testingEnvironment);
} }
const isSteadyStateDefaultSetupRun = const isSteadyStateDefaultSetupRun =
process.env["CODE_SCANNING_IS_STEADY_STATE_DEFAULT_SETUP"] === "true"; process.env["CODE_SCANNING_IS_STEADY_STATE_DEFAULT_SETUP"] === "true";
+3 -3
View File
@@ -14,7 +14,7 @@ import { getGitHubVersion, wrapApiConfigurationError } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql"; import { CodeQL, getCodeQL } from "./codeql";
import { getConfig } from "./config-utils"; import { getConfig } from "./config-utils";
import { readDiffRangesJsonFile } from "./diff-informed-analysis-utils"; import { readDiffRangesJsonFile } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable } from "./environment";
import { FeatureEnablement } from "./feature-flags"; import { FeatureEnablement } from "./feature-flags";
import * as fingerprints from "./fingerprints"; import * as fingerprints from "./fingerprints";
import * as gitUtils from "./git-utils"; import * as gitUtils from "./git-utils";
@@ -126,7 +126,7 @@ async function combineSarifFilesUsingCLI(
logger.warning( logger.warning(
`Uploading multiple SARIF runs with the same category is deprecated ${deprecationWarningMessage}. Please update your workflow to upload a single run per category. ${deprecationMoreInformationMessage}`, `Uploading multiple SARIF runs with the same category is deprecated ${deprecationWarningMessage}. Please update your workflow to upload a single run per category. ${deprecationMoreInformationMessage}`,
); );
core.exportVariable("CODEQL_MERGE_SARIF_DEPRECATION_WARNING", "true"); exportVariable("CODEQL_MERGE_SARIF_DEPRECATION_WARNING", "true");
} }
// If not, use the naive method of combining the files. // If not, use the naive method of combining the files.
@@ -1023,7 +1023,7 @@ export function validateUniqueCategory(
`Category: (${id ? id : "none"}) Tool: (${tool ? tool : "none"})`, `Category: (${id ? id : "none"}) Tool: (${tool ? tool : "none"})`,
); );
} }
core.exportVariable(sentinelEnvVar, sentinelEnvVar); exportVariable(sentinelEnvVar, sentinelEnvVar);
} }
} }
+11 -18
View File
@@ -13,11 +13,13 @@ import * as apiCompatibility from "./api-compatibility.json";
import type { CodeQL, VersionInfo } from "./codeql"; import type { CodeQL, VersionInfo } from "./codeql";
import type { Pack } from "./config/db-config"; import type { Pack } from "./config/db-config";
import type { Config } from "./config-utils"; import type { Config } from "./config-utils";
import { EnvVar } from "./environment"; import { EnvVar, exportVariable, isInTestMode } from "./environment";
import * as json from "./json"; import * as json from "./json";
import { Language } from "./languages"; import { Language } from "./languages";
import { Logger } from "./logging"; import { Logger } from "./logging";
export { isInTestMode } from "./environment";
/** /**
* The name of the file containing the base database OIDs, as stored in the * The name of the file containing the base database OIDs, as stored in the
* root of the database location. * root of the database location.
@@ -515,7 +517,7 @@ export function checkGitHubVersionInRange(
); );
} }
hasBeenWarnedAboutVersion = true; hasBeenWarnedAboutVersion = true;
core.exportVariable(CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR, true); exportVariable(CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR, true);
} }
export enum DisallowedAPIVersionReason { export enum DisallowedAPIVersionReason {
@@ -559,11 +561,11 @@ export function assertNever(value: never): never {
* knowing what version of CodeQL we're running. * knowing what version of CodeQL we're running.
*/ */
export function initializeEnvironment(version: string) { export function initializeEnvironment(version: string) {
core.exportVariable(EnvVar.FEATURE_MULTI_LANGUAGE, "false"); exportVariable(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
core.exportVariable(EnvVar.FEATURE_SANDWICH, "false"); exportVariable(EnvVar.FEATURE_SANDWICH, "false");
core.exportVariable(EnvVar.FEATURE_SARIF_COMBINE, "true"); exportVariable(EnvVar.FEATURE_SARIF_COMBINE, "true");
core.exportVariable(EnvVar.FEATURE_WILL_UPLOAD, "true"); exportVariable(EnvVar.FEATURE_WILL_UPLOAD, "true");
core.exportVariable(EnvVar.VERSION, version); exportVariable(EnvVar.VERSION, version);
} }
/** /**
@@ -708,15 +710,6 @@ export function isGoodVersion(versionSpec: string) {
return !BROKEN_VERSIONS.includes(versionSpec); return !BROKEN_VERSIONS.includes(versionSpec);
} }
/**
* Returns whether we are in test mode. This is used by CodeQL Action PR checks.
*
* In test mode, we skip several uploads (SARIF results, status reports, DBs, ...).
*/
export function isInTestMode(): boolean {
return process.env[EnvVar.TEST_MODE] === "true";
}
/** /**
* Returns whether we specifically want to skip uploading SARIF files. * Returns whether we specifically want to skip uploading SARIF files.
*/ */
@@ -935,7 +928,7 @@ export async function checkDiskUsage(
} else { } else {
logger.debug(message); logger.debug(message);
} }
core.exportVariable(EnvVar.HAS_WARNED_ABOUT_DISK_SPACE, "true"); exportVariable(EnvVar.HAS_WARNED_ABOUT_DISK_SPACE, "true");
} }
return { return {
numAvailableBytes: diskUsage.bavail * blockSizeInBytes, numAvailableBytes: diskUsage.bavail * blockSizeInBytes,
@@ -984,7 +977,7 @@ export function checkActionVersion(
"https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/", "https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/",
); );
// 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"); exportVariable(EnvVar.LOG_VERSION_DEPRECATION, "true");
} }
} }
} }