Compare commits

..

47 Commits

Author SHA1 Message Date
Michael B. Gale
0cf61911ea Generate UserConfig from schema ... ish 2025-10-17 16:19:12 +01:00
Michael B. Gale
816fc30181 Add command for turning JSON schemas into TypeScript typings 2025-10-17 16:12:28 +01:00
Michael B. Gale
9ce56a247f Make schema for QueryFilter less strict 2025-10-17 15:11:16 +01:00
Michael B. Gale
2c8f4891d1 Add FF for config validation 2025-10-17 15:11:13 +01:00
Michael B. Gale
d7a8ae5fdd Include first 10 errors in exception message 2025-10-17 15:09:05 +01:00
Michael B. Gale
0822fb12e7 Log validation errors 2025-10-17 15:09:04 +01:00
Michael B. Gale
913cd47984 Add checkExpectedLogMessages function to testing-utils 2025-10-17 15:09:04 +01:00
Michael B. Gale
4f14649ced Add additional regex to CliConfigErrorCategory.PackCannotBeFound 2025-10-17 15:09:03 +01:00
Michael B. Gale
ac922ab562 Add and validate UserConfig schema 2025-10-17 15:09:01 +01:00
Michael B. Gale
66df0bc515 Add and use parseUserConfig
- Throws a `ConfigurationError` if parsing the YAML fails
- Add a couple of tests for it
2025-10-17 15:08:59 +01:00
Michael B. Gale
70205d3d12 Merge pull request #3211 from github/mbg/init/starting-partial-config
Make analysis kinds available for `starting` status report
2025-10-17 14:46:51 +01:00
Michael B. Gale
697c209bfc Merge remote-tracking branch 'origin/main' into mbg/init/starting-partial-config 2025-10-17 14:21:44 +01:00
Henry Mercer
1bd53ba38c Merge pull request #3205 from github/update-bundle/codeql-bundle-v2.23.3
Update default bundle to 2.23.3
2025-10-17 14:18:19 +01:00
github-actions[bot]
cac4df0c79 Rebuild 2025-10-17 12:59:18 +00:00
Henry Mercer
77e5c0d0a2 Merge branch 'main' into update-bundle/codeql-bundle-v2.23.3 2025-10-17 13:53:02 +01:00
Michael B. Gale
97a4f751be Merge pull request #3204 from github/mbg/setup-codeql
Add `setup-codeql` action
2025-10-17 13:47:42 +01:00
Michael B. Gale
2d5512b361 Merge remote-tracking branch 'origin/main' into mbg/init/starting-partial-config 2025-10-17 13:44:28 +01:00
Michael B. Gale
fa7bdf0559 Call getAnalysisKinds a second time, and ignore exceptions thrown during the first call 2025-10-17 13:40:18 +01:00
Michael B. Gale
57c7b0a884 Rename initAnalysisKinds to getAnalysisKinds and cache results 2025-10-17 13:33:55 +01:00
Michael B. Gale
4874f90a8d Merge branch 'main' into mbg/setup-codeql 2025-10-17 13:32:40 +01:00
Michael B. Gale
5a9e92afca Merge pull request #3212 from github/mbg/ci/pin-python
Install Python 3.13 to fix failing PR checks with older CLI versions
2025-10-17 13:31:26 +01:00
Michael B. Gale
9bd9b03572 Remove now unused qualityQueriesInput from InitConfigInputs 2025-10-17 13:22:41 +01:00
Michael B. Gale
3569065d7e Install Python 3.13, except for nightly-latest 2025-10-17 12:51:50 +01:00
Michael B. Gale
c0e8887d5a Throw a ConfigurationError if setup-codeql has run before init 2025-10-17 12:17:47 +01:00
Michael B. Gale
3c8d00aea0 Initialise analysis kinds before starting status report 2025-10-17 11:46:35 +01:00
Michael B. Gale
bc93b04b0c Add initAnalysisKinds for analysis-kinds enablement logic 2025-10-17 11:43:00 +01:00
Michael B. Gale
adf39dd33f Add function for starting status report 2025-10-17 11:16:00 +01:00
Michael B. Gale
000295122d Use failure instead of aborted 2025-10-16 19:05:03 +01:00
Michael B. Gale
2611d033d7 De-duplicate InitToolsDownloadFIelds definition 2025-10-16 19:03:46 +01:00
Michael B. Gale
ee753b4724 Merge pull request #3209 from github/mbg/code-quality/skip-failed-upload
Skip failed SARIF upload if Code Quality is the only analysis kind
2025-10-16 15:22:01 +01:00
Michael B. Gale
db6938a4d0 Change check to be restrictive by default 2025-10-16 15:06:19 +01:00
Michael B. Gale
d02f50ee62 Update changelog for setup-codeql 2025-10-16 14:50:16 +01:00
Michael B. Gale
f4237b7e76 Add setup-codeql to README 2025-10-16 14:48:35 +01:00
Michael B. Gale
302fc5e00d Update docs 2025-10-16 14:46:35 +01:00
Michael B. Gale
c77b3fb96e Skip failed SARIF upload if analysis-kinds: code-quality 2025-10-16 14:27:17 +01:00
Michael B. Gale
2a54ab5016 Fix init-action-post-helper tests using broken Configs 2025-10-16 14:18:51 +01:00
github-actions[bot]
a60e5ce8ec Add changelog note 2025-10-14 12:53:29 +00:00
github-actions[bot]
8d0251c1f7 Update default bundle to codeql-bundle-v2.23.3 2025-10-14 12:53:17 +00:00
Michael B. Gale
80220dcd46 Use setup-codeql action in bundle-from-toolcache check 2025-10-12 14:14:07 +01:00
Michael B. Gale
e72fd9acb1 Add initial setup-codeql action 2025-10-12 14:14:06 +01:00
Michael B. Gale
17783bfb99 Merge pull request #3199 from github/mergeback/v4.30.8-to-main-f443b600
Mergeback v4.30.8 refs/heads/releases/v4 into main
2025-10-10 18:16:14 +01:00
Henry Mercer
3c764cd93a Only create GitHub release if it doesn't already exist 2025-10-10 17:54:08 +01:00
Henry Mercer
e1968324ff Merge branch 'releases/v4' into mergeback/v4.30.8-to-main-f443b600 2025-10-10 17:53:24 +01:00
Henry Mercer
2a6736cca7 Merge pull request #3200 from github/henrymercer/backport-hotfix
Revert "Rebuild" commit rather than "Update dependencies"
2025-10-10 17:47:40 +01:00
Henry Mercer
c8765c966b Revert "Rebuild" commit rather than "Update dependencies" 2025-10-10 17:23:02 +01:00
github-actions[bot]
61789e2fdb Rebuild 2025-10-10 15:59:22 +00:00
github-actions[bot]
5cd2d139cb Update changelog and version after v4.30.8 2025-10-10 15:55:20 +00:00
63 changed files with 102444 additions and 2950 deletions

View File

@@ -371,10 +371,10 @@ def main():
# releases.
run_git('revert', vOlder_update_commits[0], '--no-edit')
# Also revert the "Update checked-in dependencies" commit created by Actions.
update_dependencies_commit = run_git('log', '--grep', '^Update checked-in dependencies', '--format=%H').split()[0]
print(f' Reverting {update_dependencies_commit}')
run_git('revert', update_dependencies_commit, '--no-edit')
# Also revert the "Rebuild" commit created by Actions.
rebuild_commit = run_git('log', '--grep', '^Rebuild$', '--format=%H').split()[0]
print(f' Reverting {rebuild_commit}')
run_git('revert', rebuild_commit, '--no-edit')
else:
print(' Nothing to revert.')

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -70,6 +80,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- uses: ./../action/init
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}

View File

@@ -67,10 +67,9 @@ jobs:
if (allCodeqlVersions.length === 0) {
throw new Error(`CodeQL could not be found in the toolcache`);
}
- id: init
uses: ./../action/init
- id: setup-codeql
uses: ./../action/setup-codeql
with:
languages: javascript
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Check CodeQL is installed within the toolcache
uses: actions/github-script@v8

15
.github/workflows/__local-bundle.yml generated vendored
View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -70,6 +80,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- name: Fetch latest CodeQL bundle
run: |
wget https://github.com/github/codeql-action/releases/latest/download/codeql-bundle-linux64.tar.zst

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -104,6 +114,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- name: Use Xcode 16
if: runner.os == 'macOS' && matrix.version != 'nightly-latest'
run: sudo xcode-select -s "/Applications/Xcode_16.app"

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -81,6 +91,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- uses: ./../action/init
with:
config-file: .github/codeql/codeql-config-packaging3.yml

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -72,6 +82,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- uses: ./../action/init
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -72,6 +82,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- uses: ./../action/init
id: init
with:

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -70,6 +80,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- uses: ./../action/init
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}

15
.github/workflows/__upload-sarif.yml generated vendored
View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -77,6 +87,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- uses: ./../action/init
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}

View File

@@ -27,6 +27,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
workflow_call:
inputs:
go-version:
@@ -34,6 +39,11 @@ on:
description: The version of Go to install
required: false
default: '>=1.21.0'
python-version:
type: string
description: The version of Python to install
required: false
default: '3.13'
defaults:
run:
shell: bash
@@ -70,6 +80,11 @@ jobs:
with:
go-version: ${{ inputs.go-version || '>=1.21.0' }}
cache: false
- name: Install Python
if: matrix.version != 'nightly-latest'
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version || '3.13' }}
- name: Delete original checkout
run: |
# delete the original checkout so we don't accidentally use it.

View File

@@ -146,6 +146,7 @@ jobs:
private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}
- name: Create the GitHub release
if: steps.check.outputs.exists != 'true'
env:
PARTIAL_CHANGELOG: "${{ runner.temp }}/partial_changelog.md"
VERSION: "${{ steps.getVersion.outputs.version }}"

View File

@@ -2,6 +2,11 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## [UNRELEASED]
- Update default CodeQL bundle version to 2.23.3. [#3205](https://github.com/github/codeql-action/pull/3205)
- Experimental: A new `setup-codeql` action has been added which is similar to `init`, except it only installs the CodeQL CLI and does not initialize a database. Do not use this in production as it is part of an internal experiment and subject to change at any time. [#3204](https://github.com/github/codeql-action/pull/3204)
## 4.30.8 - 10 Oct 2025
No user facing changes.

View File

@@ -34,6 +34,7 @@ Actions with special purposes and unlikely to be used directly:
- `autobuild`: Attempts to automatically build the code. Only used for analyzing languages that require a build. Use the `build-mode: autobuild` input in the `init` action instead. For information about input parameters, see the [autobuild action definition](https://github.com/github/codeql-action/blob/main/autobuild/action.yml).
- `resolve-environment`: [Experimental] Attempts to infer a build environment suitable for automatic builds. For information about input parameters, see the [resolve-environment action definition](https://github.com/github/codeql-action/blob/main/resolve-environment/action.yml).
- `start-proxy`: [Experimental] Start the HTTP proxy server. Internal use only and will change without notice. For information about input parameters, see the [start-proxy action definition](https://github.com/github/codeql-action/blob/main/start-proxy/action.yml).
- `setup-codeql`: [Experimental] Similar to `init`, except it only installs the CodeQL CLI and does not initialize a database.
### Workflow Permissions

29
json-schemas.mjs Normal file
View File

@@ -0,0 +1,29 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { globSync } from "glob";
import { compileFromFile } from 'json-schema-to-typescript';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const SRC_DIR = path.join(__dirname, "schemas");
const OUT_DIR = path.join(__dirname, "src");
async function generateTypings() {
const schemas = globSync(`${SRC_DIR}/*.json`);
for (const schema of schemas) {
const outPath = path.join(
OUT_DIR,
`${path.basename(schema, ".json")}.d.ts`,
);
const ts = await compileFromFile(schema, {
bannerComment:
"/* This file was automatically generated by `npm run generate:schemas`. Do not edit by hand. */",
});
fs.writeFileSync(outPath, ts, "utf-8");
}
}
await generateTypings();

File diff suppressed because it is too large Load Diff

2635
lib/analyze-action.js generated

File diff suppressed because it is too large Load Diff

1334
lib/autobuild-action.js generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.23.2",
"cliVersion": "2.23.2",
"priorBundleVersion": "codeql-bundle-v2.23.1",
"priorCliVersion": "2.23.1"
"bundleVersion": "codeql-bundle-v2.23.3",
"cliVersion": "2.23.3",
"priorBundleVersion": "codeql-bundle-v2.23.2",
"priorCliVersion": "2.23.2"
}

2657
lib/init-action-post.js generated

File diff suppressed because it is too large Load Diff

1688
lib/init-action.js generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

88987
lib/setup-codeql-action.js generated Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

1331
lib/start-proxy-action.js generated

File diff suppressed because it is too large Load Diff

67
lib/upload-lib.js generated
View File

@@ -20885,19 +20885,19 @@ var require_validator = __commonJS({
var SchemaError = helpers.SchemaError;
var SchemaContext = helpers.SchemaContext;
var anonymousBase = "/";
var Validator2 = function Validator3() {
this.customFormats = Object.create(Validator3.prototype.customFormats);
var Validator3 = function Validator4() {
this.customFormats = Object.create(Validator4.prototype.customFormats);
this.schemas = {};
this.unresolvedRefs = [];
this.types = Object.create(types);
this.attributes = Object.create(attribute.validators);
};
Validator2.prototype.customFormats = {};
Validator2.prototype.schemas = null;
Validator2.prototype.types = null;
Validator2.prototype.attributes = null;
Validator2.prototype.unresolvedRefs = null;
Validator2.prototype.addSchema = function addSchema(schema2, base) {
Validator3.prototype.customFormats = {};
Validator3.prototype.schemas = null;
Validator3.prototype.types = null;
Validator3.prototype.attributes = null;
Validator3.prototype.unresolvedRefs = null;
Validator3.prototype.addSchema = function addSchema(schema2, base) {
var self2 = this;
if (!schema2) {
return null;
@@ -20915,25 +20915,25 @@ var require_validator = __commonJS({
});
return this.schemas[ourUri];
};
Validator2.prototype.addSubSchemaArray = function addSubSchemaArray(baseuri, schemas) {
Validator3.prototype.addSubSchemaArray = function addSubSchemaArray(baseuri, schemas) {
if (!Array.isArray(schemas)) return;
for (var i = 0; i < schemas.length; i++) {
this.addSubSchema(baseuri, schemas[i]);
}
};
Validator2.prototype.addSubSchemaObject = function addSubSchemaArray(baseuri, schemas) {
Validator3.prototype.addSubSchemaObject = function addSubSchemaArray(baseuri, schemas) {
if (!schemas || typeof schemas != "object") return;
for (var p in schemas) {
this.addSubSchema(baseuri, schemas[p]);
}
};
Validator2.prototype.setSchemas = function setSchemas(schemas) {
Validator3.prototype.setSchemas = function setSchemas(schemas) {
this.schemas = schemas;
};
Validator2.prototype.getSchema = function getSchema(urn) {
Validator3.prototype.getSchema = function getSchema(urn) {
return this.schemas[urn];
};
Validator2.prototype.validate = function validate(instance, schema2, options, ctx) {
Validator3.prototype.validate = function validate(instance, schema2, options, ctx) {
if (typeof schema2 !== "boolean" && typeof schema2 !== "object" || schema2 === null) {
throw new SchemaError("Expected `schema` to be an object or boolean");
}
@@ -20971,7 +20971,7 @@ var require_validator = __commonJS({
if (typeof ref == "string") return ref;
return false;
}
Validator2.prototype.validateSchema = function validateSchema(instance, schema2, options, ctx) {
Validator3.prototype.validateSchema = function validateSchema(instance, schema2, options, ctx) {
var result = new ValidatorResult(instance, schema2, options, ctx);
if (typeof schema2 === "boolean") {
if (schema2 === true) {
@@ -21021,17 +21021,17 @@ var require_validator = __commonJS({
}
return result;
};
Validator2.prototype.schemaTraverser = function schemaTraverser(schemaobj, s) {
Validator3.prototype.schemaTraverser = function schemaTraverser(schemaobj, s) {
schemaobj.schema = helpers.deepMerge(schemaobj.schema, this.superResolve(s, schemaobj.ctx));
};
Validator2.prototype.superResolve = function superResolve(schema2, ctx) {
Validator3.prototype.superResolve = function superResolve(schema2, ctx) {
var ref = shouldResolve(schema2);
if (ref) {
return this.resolve(schema2, ref, ctx).subschema;
}
return schema2;
};
Validator2.prototype.resolve = function resolve6(schema2, switchSchema, ctx) {
Validator3.prototype.resolve = function resolve6(schema2, switchSchema, ctx) {
switchSchema = ctx.resolve(switchSchema);
if (ctx.schemas[switchSchema]) {
return { subschema: ctx.schemas[switchSchema], switchSchema };
@@ -21048,7 +21048,7 @@ var require_validator = __commonJS({
}
return { subschema, switchSchema };
};
Validator2.prototype.testType = function validateType(instance, schema2, options, ctx, type2) {
Validator3.prototype.testType = function validateType(instance, schema2, options, ctx, type2) {
if (type2 === void 0) {
return;
} else if (type2 === null) {
@@ -21063,7 +21063,7 @@ var require_validator = __commonJS({
}
return true;
};
var types = Validator2.prototype.types = {};
var types = Validator3.prototype.types = {};
types.string = function testString(instance) {
return typeof instance == "string";
};
@@ -21091,7 +21091,7 @@ var require_validator = __commonJS({
types.object = function testObject(instance) {
return instance && typeof instance === "object" && !Array.isArray(instance) && !(instance instanceof Date);
};
module2.exports = Validator2;
module2.exports = Validator3;
}
});
@@ -21099,7 +21099,7 @@ var require_validator = __commonJS({
var require_lib2 = __commonJS({
"node_modules/jsonschema/lib/index.js"(exports2, module2) {
"use strict";
var Validator2 = module2.exports.Validator = require_validator();
var Validator3 = module2.exports.Validator = require_validator();
module2.exports.ValidatorResult = require_helpers().ValidatorResult;
module2.exports.ValidatorResultError = require_helpers().ValidatorResultError;
module2.exports.ValidationError = require_helpers().ValidationError;
@@ -21107,7 +21107,7 @@ var require_lib2 = __commonJS({
module2.exports.SchemaScanResult = require_scan().SchemaScanResult;
module2.exports.scan = require_scan().scan;
module2.exports.validate = function(instance, schema2, options) {
var v = new Validator2();
var v = new Validator3();
return v.validate(instance, schema2, options);
};
}
@@ -33606,7 +33606,7 @@ var require_package = __commonJS({
"package.json"(exports2, module2) {
module2.exports = {
name: "codeql",
version: "4.30.8",
version: "4.30.9",
private: true,
description: "CodeQL action",
scripts: {
@@ -33618,7 +33618,8 @@ var require_package = __commonJS({
ava: "npm run transpile && ava --serial --verbose",
test: "npm run ava -- src/",
"test-debug": "npm run test -- --timeout=20m",
transpile: "tsc --build --verbose"
transpile: "npm run generate:schemas && tsc --build --verbose",
"generate:schemas": "node json-schemas.mjs"
},
ava: {
typescript: {
@@ -33684,6 +33685,7 @@ var require_package = __commonJS({
"eslint-plugin-import": "2.29.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.0.3",
"json-schema-to-typescript": "^15.0.4",
nock: "^14.0.10",
sinon: "^21.0.0",
typescript: "^5.9.3"
@@ -84865,7 +84867,7 @@ var path14 = __toESM(require("path"));
var url = __toESM(require("url"));
var import_zlib = __toESM(require("zlib"));
var core11 = __toESM(require_core());
var jsonschema = __toESM(require_lib2());
var jsonschema2 = __toESM(require_lib2());
// src/actions-util.ts
var fs4 = __toESM(require("fs"));
@@ -88971,6 +88973,9 @@ var cliErrorsConfig = {
cliErrorMessageCandidates: [
new RegExp(
"Query pack .* cannot be found\\. Check the spelling of the pack\\."
),
new RegExp(
"is not a .ql file, .qls file, a directory, or a query pack specification."
)
]
},
@@ -89049,6 +89054,7 @@ var path9 = __toESM(require("path"));
var core6 = __toESM(require_core());
// src/config/db-config.ts
var jsonschema = __toESM(require_lib2());
var semver2 = __toESM(require_semver2());
var PACK_IDENTIFIER_PATTERN = (function() {
const alphaNumeric = "[a-z0-9]";
@@ -89065,8 +89071,8 @@ var path8 = __toESM(require("path"));
var semver4 = __toESM(require_semver2());
// src/defaults.json
var bundleVersion = "codeql-bundle-v2.23.2";
var cliVersion = "2.23.2";
var bundleVersion = "codeql-bundle-v2.23.3";
var cliVersion = "2.23.3";
// src/overlay-database-utils.ts
var fs5 = __toESM(require("fs"));
@@ -89530,6 +89536,11 @@ var featureConfig = {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
},
["validate_db_config" /* ValidateDbConfig */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_VALIDATE_DB_CONFIG",
minimumVersion: void 0
}
};
@@ -92651,7 +92662,7 @@ function validateSarifFileSchema(sarif, sarifFilePath, logger) {
}
logger.info(`Validating ${sarifFilePath}`);
const schema2 = require_sarif_schema_2_1_0();
const result = new jsonschema.Validator().validate(sarif, schema2);
const result = new jsonschema2.Validator().validate(sarif, schema2);
const warningAttributes = ["uri-reference", "uri"];
const errors = (result.errors ?? []).filter(
(err) => !(err.name === "format" && typeof err.argument === "string" && warningAttributes.includes(err.argument))

File diff suppressed because it is too large Load Diff

View File

@@ -32309,7 +32309,7 @@ var require_package = __commonJS({
"package.json"(exports2, module2) {
module2.exports = {
name: "codeql",
version: "4.30.8",
version: "4.30.9",
private: true,
description: "CodeQL action",
scripts: {
@@ -32321,7 +32321,8 @@ var require_package = __commonJS({
ava: "npm run transpile && ava --serial --verbose",
test: "npm run ava -- src/",
"test-debug": "npm run test -- --timeout=20m",
transpile: "tsc --build --verbose"
transpile: "npm run generate:schemas && tsc --build --verbose",
"generate:schemas": "node json-schemas.mjs"
},
ava: {
typescript: {
@@ -32387,6 +32388,7 @@ var require_package = __commonJS({
"eslint-plugin-import": "2.29.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.0.3",
"json-schema-to-typescript": "^15.0.4",
nock: "^14.0.10",
sinon: "^21.0.0",
typescript: "^5.9.3"
@@ -80374,19 +80376,19 @@ var require_validator2 = __commonJS({
var SchemaError = helpers.SchemaError;
var SchemaContext = helpers.SchemaContext;
var anonymousBase = "/";
var Validator2 = function Validator3() {
this.customFormats = Object.create(Validator3.prototype.customFormats);
var Validator3 = function Validator4() {
this.customFormats = Object.create(Validator4.prototype.customFormats);
this.schemas = {};
this.unresolvedRefs = [];
this.types = Object.create(types);
this.attributes = Object.create(attribute.validators);
};
Validator2.prototype.customFormats = {};
Validator2.prototype.schemas = null;
Validator2.prototype.types = null;
Validator2.prototype.attributes = null;
Validator2.prototype.unresolvedRefs = null;
Validator2.prototype.addSchema = function addSchema(schema2, base) {
Validator3.prototype.customFormats = {};
Validator3.prototype.schemas = null;
Validator3.prototype.types = null;
Validator3.prototype.attributes = null;
Validator3.prototype.unresolvedRefs = null;
Validator3.prototype.addSchema = function addSchema(schema2, base) {
var self2 = this;
if (!schema2) {
return null;
@@ -80404,25 +80406,25 @@ var require_validator2 = __commonJS({
});
return this.schemas[ourUri];
};
Validator2.prototype.addSubSchemaArray = function addSubSchemaArray(baseuri, schemas) {
Validator3.prototype.addSubSchemaArray = function addSubSchemaArray(baseuri, schemas) {
if (!Array.isArray(schemas)) return;
for (var i = 0; i < schemas.length; i++) {
this.addSubSchema(baseuri, schemas[i]);
}
};
Validator2.prototype.addSubSchemaObject = function addSubSchemaArray(baseuri, schemas) {
Validator3.prototype.addSubSchemaObject = function addSubSchemaArray(baseuri, schemas) {
if (!schemas || typeof schemas != "object") return;
for (var p in schemas) {
this.addSubSchema(baseuri, schemas[p]);
}
};
Validator2.prototype.setSchemas = function setSchemas(schemas) {
Validator3.prototype.setSchemas = function setSchemas(schemas) {
this.schemas = schemas;
};
Validator2.prototype.getSchema = function getSchema(urn) {
Validator3.prototype.getSchema = function getSchema(urn) {
return this.schemas[urn];
};
Validator2.prototype.validate = function validate(instance, schema2, options, ctx) {
Validator3.prototype.validate = function validate(instance, schema2, options, ctx) {
if (typeof schema2 !== "boolean" && typeof schema2 !== "object" || schema2 === null) {
throw new SchemaError("Expected `schema` to be an object or boolean");
}
@@ -80460,7 +80462,7 @@ var require_validator2 = __commonJS({
if (typeof ref == "string") return ref;
return false;
}
Validator2.prototype.validateSchema = function validateSchema(instance, schema2, options, ctx) {
Validator3.prototype.validateSchema = function validateSchema(instance, schema2, options, ctx) {
var result = new ValidatorResult(instance, schema2, options, ctx);
if (typeof schema2 === "boolean") {
if (schema2 === true) {
@@ -80510,17 +80512,17 @@ var require_validator2 = __commonJS({
}
return result;
};
Validator2.prototype.schemaTraverser = function schemaTraverser(schemaobj, s) {
Validator3.prototype.schemaTraverser = function schemaTraverser(schemaobj, s) {
schemaobj.schema = helpers.deepMerge(schemaobj.schema, this.superResolve(s, schemaobj.ctx));
};
Validator2.prototype.superResolve = function superResolve(schema2, ctx) {
Validator3.prototype.superResolve = function superResolve(schema2, ctx) {
var ref = shouldResolve(schema2);
if (ref) {
return this.resolve(schema2, ref, ctx).subschema;
}
return schema2;
};
Validator2.prototype.resolve = function resolve6(schema2, switchSchema, ctx) {
Validator3.prototype.resolve = function resolve6(schema2, switchSchema, ctx) {
switchSchema = ctx.resolve(switchSchema);
if (ctx.schemas[switchSchema]) {
return { subschema: ctx.schemas[switchSchema], switchSchema };
@@ -80537,7 +80539,7 @@ var require_validator2 = __commonJS({
}
return { subschema, switchSchema };
};
Validator2.prototype.testType = function validateType(instance, schema2, options, ctx, type2) {
Validator3.prototype.testType = function validateType(instance, schema2, options, ctx, type2) {
if (type2 === void 0) {
return;
} else if (type2 === null) {
@@ -80552,7 +80554,7 @@ var require_validator2 = __commonJS({
}
return true;
};
var types = Validator2.prototype.types = {};
var types = Validator3.prototype.types = {};
types.string = function testString(instance) {
return typeof instance == "string";
};
@@ -80580,7 +80582,7 @@ var require_validator2 = __commonJS({
types.object = function testObject(instance) {
return instance && typeof instance === "object" && !Array.isArray(instance) && !(instance instanceof Date);
};
module2.exports = Validator2;
module2.exports = Validator3;
}
});
@@ -80588,7 +80590,7 @@ var require_validator2 = __commonJS({
var require_lib2 = __commonJS({
"node_modules/jsonschema/lib/index.js"(exports2, module2) {
"use strict";
var Validator2 = module2.exports.Validator = require_validator2();
var Validator3 = module2.exports.Validator = require_validator2();
module2.exports.ValidatorResult = require_helpers3().ValidatorResult;
module2.exports.ValidatorResultError = require_helpers3().ValidatorResultError;
module2.exports.ValidationError = require_helpers3().ValidationError;
@@ -80596,7 +80598,7 @@ var require_lib2 = __commonJS({
module2.exports.SchemaScanResult = require_scan2().SchemaScanResult;
module2.exports.scan = require_scan2().scan;
module2.exports.validate = function(instance, schema2, options) {
var v = new Validator2();
var v = new Validator3();
return v.validate(instance, schema2, options);
};
}
@@ -88976,8 +88978,8 @@ var path8 = __toESM(require("path"));
var semver3 = __toESM(require_semver2());
// src/defaults.json
var bundleVersion = "codeql-bundle-v2.23.2";
var cliVersion = "2.23.2";
var bundleVersion = "codeql-bundle-v2.23.3";
var cliVersion = "2.23.3";
// src/overlay-database-utils.ts
var fs5 = __toESM(require("fs"));
@@ -89446,6 +89448,11 @@ var featureConfig = {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
},
["validate_db_config" /* ValidateDbConfig */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_VALIDATE_DB_CONFIG",
minimumVersion: void 0
}
};
var FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
@@ -89725,6 +89732,7 @@ var path10 = __toESM(require("path"));
var core8 = __toESM(require_core());
// src/config/db-config.ts
var jsonschema = __toESM(require_lib2());
var semver4 = __toESM(require_semver2());
var PACK_IDENTIFIER_PATTERN = (function() {
const alphaNumeric = "[a-z0-9]";
@@ -90009,7 +90017,7 @@ var path15 = __toESM(require("path"));
var url = __toESM(require("url"));
var import_zlib = __toESM(require("zlib"));
var core12 = __toESM(require_core());
var jsonschema = __toESM(require_lib2());
var jsonschema2 = __toESM(require_lib2());
// src/codeql.ts
var fs12 = __toESM(require("fs"));
@@ -90223,6 +90231,9 @@ var cliErrorsConfig = {
cliErrorMessageCandidates: [
new RegExp(
"Query pack .* cannot be found\\. Check the spelling of the pack\\."
),
new RegExp(
"is not a .ql file, .qls file, a directory, or a query pack specification."
)
]
},
@@ -93305,7 +93316,7 @@ function validateSarifFileSchema(sarif, sarifFilePath, logger) {
}
logger.info(`Validating ${sarifFilePath}`);
const schema2 = require_sarif_schema_2_1_0();
const result = new jsonschema.Validator().validate(sarif, schema2);
const result = new jsonschema2.Validator().validate(sarif, schema2);
const warningAttributes = ["uri-reference", "uri"];
const errors = (result.errors ?? []).filter(
(err) => !(err.name === "format" && typeof err.argument === "string" && warningAttributes.includes(err.argument))

61
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "codeql",
"version": "4.30.8",
"version": "4.30.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.30.8",
"version": "4.30.9",
"license": "MIT",
"dependencies": {
"@actions/artifact": "^2.3.1",
@@ -63,6 +63,7 @@
"eslint-plugin-import": "2.29.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.0.3",
"json-schema-to-typescript": "^15.0.4",
"nock": "^14.0.10",
"sinon": "^21.0.0",
"typescript": "^5.9.3"
@@ -429,6 +430,24 @@
"semver": "^6.1.0"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "11.9.3",
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz",
"integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.15",
"js-yaml": "^4.1.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/philsturgeon"
}
},
"node_modules/@ava/typescript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-6.0.0.tgz",
@@ -1525,6 +1544,13 @@
"node": ">=18.0.0"
}
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"dev": true,
"license": "MIT"
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz",
@@ -2644,6 +2670,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz",
@@ -6952,6 +6985,30 @@
"node": ">=0.1.90"
}
},
"node_modules/json-schema-to-typescript": {
"version": "15.0.4",
"resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-15.0.4.tgz",
"integrity": "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^11.5.5",
"@types/json-schema": "^7.0.15",
"@types/lodash": "^4.17.7",
"is-glob": "^4.0.3",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"minimist": "^1.2.8",
"prettier": "^3.2.5",
"tinyglobby": "^0.2.9"
},
"bin": {
"json2ts": "dist/src/cli.js"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"dev": true,

View File

@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.30.8",
"version": "4.30.9",
"private": true,
"description": "CodeQL action",
"scripts": {
@@ -12,7 +12,8 @@
"ava": "npm run transpile && ava --serial --verbose",
"test": "npm run ava -- src/",
"test-debug": "npm run test -- --timeout=20m",
"transpile": "tsc --build --verbose"
"transpile": "npm run generate:schemas && tsc --build --verbose",
"generate:schemas": "node json-schemas.mjs"
},
"ava": {
"typescript": {
@@ -78,6 +79,7 @@
"eslint-plugin-import": "2.29.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.0.3",
"json-schema-to-typescript": "^15.0.4",
"nock": "^14.0.10",
"sinon": "^21.0.0",
"typescript": "^5.9.3"

View File

@@ -2,6 +2,7 @@ name: "Analyze: 'ref' and 'sha' from inputs"
description: "Checks that specifying 'ref' and 'sha' as inputs works"
versions: ["default"]
installGo: true
installPython: true
steps:
- uses: ./../action/init
with:

View File

@@ -15,10 +15,9 @@ steps:
if (allCodeqlVersions.length === 0) {
throw new Error(`CodeQL could not be found in the toolcache`);
}
- id: init
uses: ./../action/init
- id: setup-codeql
uses: ./../action/setup-codeql
with:
languages: javascript
tools: ${{ steps.prepare-test.outputs.tools-url }}
- name: Check CodeQL is installed within the toolcache
uses: actions/github-script@v8

View File

@@ -2,6 +2,7 @@ name: "Local CodeQL bundle"
description: "Tests using a CodeQL bundle from a local file rather than a URL"
versions: ["linked"]
installGo: true
installPython: true
steps:
- name: Fetch latest CodeQL bundle
run: |

View File

@@ -4,6 +4,7 @@ operatingSystems: ["macos", "ubuntu"]
env:
CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true
installGo: true
installPython: true
steps:
- name: Use Xcode 16
if: runner.os == 'macOS' && matrix.version != 'nightly-latest'

View File

@@ -3,6 +3,7 @@ description: "Checks that specifying packages using a combination of a config fi
versions: ["linked", "default", "nightly-latest"] # This feature is not compatible with old CLIs
installGo: true
installNode: true
installPython: true
steps:
- uses: ./../action/init
with:

View File

@@ -6,6 +6,7 @@ versions:
- linked
- nightly-latest
installGo: true
installPython: true
steps:
- uses: ./../action/init
with:

View File

@@ -6,6 +6,7 @@ versions:
- linked
- nightly-latest
installGo: true
installPython: true
steps:
- uses: ./../action/init
id: init

View File

@@ -2,6 +2,7 @@ name: "Upload-sarif: 'ref' and 'sha' from inputs"
description: "Checks that specifying 'ref' and 'sha' as inputs works"
versions: ["default"]
installGo: true
installPython: true
steps:
- uses: ./../action/init
with:

View File

@@ -3,6 +3,7 @@ description: "Checks that uploading SARIFs to the code quality endpoint works"
versions: ["default"]
analysisKinds: ["code-scanning", "code-quality", "code-scanning,code-quality"]
installGo: true
installPython: true
steps:
- uses: ./../action/init
with:

View File

@@ -2,6 +2,7 @@ name: "Use a custom `checkout_path`"
description: "Checks that a custom `checkout_path` will find the proper commit_oid"
versions: ["linked"]
installGo: true
installPython: true
steps:
# This ensures we don't accidentally use the original checkout for any part of the test.
- name: Delete original checkout

View File

@@ -184,6 +184,26 @@ for file in sorted((this_dir / 'checks').glob('*.yml')):
}
})
installPython = is_truthy(checkSpecification.get('installPython', ''))
if installPython:
basePythonVersionExpr = '3.13'
workflowInputs['python-version'] = {
'type': 'string',
'description': 'The version of Python to install',
'required': False,
'default': basePythonVersionExpr,
}
steps.append({
'name': 'Install Python',
'if': 'matrix.version != \'nightly-latest\'',
'uses': 'actions/setup-python@v6',
'with': {
'python-version': '${{ inputs.python-version || \'' + basePythonVersionExpr + '\' }}'
}
})
# If container initialisation steps are present in the check specification,
# make sure to execute them first.
if 'container' in checkSpecification and 'container-init-steps' in checkSpecification:

View File

@@ -0,0 +1,145 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "UserConfig",
"description": "Format of the config file supplied by the user for CodeQL analysis",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the configuration"
},
"disable-default-queries": {
"type": "boolean",
"description": "Whether to disable default queries"
},
"queries": {
"type": "array",
"description": "List of additional queries to run",
"items": {
"$ref": "#/definitions/QuerySpec"
}
},
"paths-ignore": {
"type": "array",
"description": "Paths to ignore during analysis",
"items": {
"type": "string"
}
},
"paths": {
"type": "array",
"description": "Paths to include in analysis",
"items": {
"type": "string"
}
},
"packs": {
"description": "Query packs to include. Can be a simple array for single-language analysis or an object with language-specific arrays for multi-language analysis",
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
]
},
"query-filters": {
"type": "array",
"description": "Set of query filters to include and exclude extra queries based on CodeQL query suite include and exclude properties",
"items": {
"$ref": "#/definitions/QueryFilter"
}
}
},
"additionalProperties": true,
"definitions": {
"QuerySpec": {
"type": "object",
"description": "Detailed query specification object",
"properties": {
"name": {
"type": "string",
"description": "Optional name for the query"
},
"uses": {
"type": "string",
"description": "The query or query suite to use"
}
},
"required": ["uses"],
"additionalProperties": false
},
"QueryFilter": {
"description": "Query filter that can either include or exclude queries",
"oneOf": [
{
"$ref": "#/definitions/ExcludeQueryFilter"
},
{
"$ref": "#/definitions/IncludeQueryFilter"
},
{}
]
},
"ExcludeQueryFilter": {
"type": "object",
"description": "Filter to exclude queries",
"properties": {
"exclude": {
"type": "object",
"description": "Queries to exclude",
"additionalProperties": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "string"
}
]
}
}
},
"required": ["exclude"],
"additionalProperties": false
},
"IncludeQueryFilter": {
"type": "object",
"description": "Filter to include queries",
"properties": {
"include": {
"type": "object",
"description": "Queries to include",
"additionalProperties": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "string"
}
]
}
}
},
"required": ["include"],
"additionalProperties": false
}
}
}

39
setup-codeql/action.yml Normal file
View File

@@ -0,0 +1,39 @@
name: 'CodeQL: Setup'
description: 'Installs the CodeQL CLI'
author: 'GitHub'
inputs:
tools:
description: >-
By default, the Action will use the recommended version of the CodeQL
Bundle to analyze your project. You can override this choice using this
input. One of:
- A local path to a CodeQL Bundle tarball, or
- The URL of a CodeQL Bundle tarball GitHub release asset, or
- A special value `linked` which uses the version of the CodeQL tools
that the Action has been bundled with.
- A special value `nightly` which uses the latest nightly version of the
CodeQL tools. Note that this is unstable and not recommended for
production use.
If not specified, the Action will check in several places until it finds
the CodeQL tools.
required: false
token:
description: GitHub token to use for authenticating with this instance of GitHub.
default: ${{ github.token }}
required: false
matrix:
default: ${{ toJson(matrix) }}
required: false
external-repository-token:
description: A token for fetching additional files from private repositories in the same GitHub instance that is running this action.
required: false
outputs:
codeql-path:
description: The path of the CodeQL binary that was installed.
codeql-version:
description: The version of the CodeQL binary that was installed.
runs:
using: node24
main: '../lib/setup-codeql-action.js'

View File

@@ -1,12 +1,19 @@
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import {
AnalysisKind,
getAnalysisKinds,
parseAnalysisKinds,
supportedAnalysisKinds,
} from "./analyses";
import { getRunnerLogger } from "./logging";
import { setupTests } from "./testing-utils";
import { ConfigurationError } from "./util";
setupTests(test);
test("All known analysis kinds can be parsed successfully", async (t) => {
for (const analysisKind of supportedAnalysisKinds) {
t.deepEqual(await parseAnalysisKinds(analysisKind), [analysisKind]);
@@ -34,3 +41,29 @@ test("Parsing analysis kinds requires at least one analysis kind", async (t) =>
instanceOf: ConfigurationError,
});
});
test("getAnalysisKinds - returns expected analysis kinds for `analysis-kinds` input", async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns("code-scanning,code-quality");
const result = await getAnalysisKinds(getRunnerLogger(true), true);
t.assert(result.includes(AnalysisKind.CodeScanning));
t.assert(result.includes(AnalysisKind.CodeQuality));
});
test("getAnalysisKinds - includes `code-quality` when deprecated `quality-queries` input is used", async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("analysis-kinds").returns("code-scanning");
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
optionalInputStub.withArgs("quality-queries").returns("code-quality");
const result = await getAnalysisKinds(getRunnerLogger(true), true);
t.assert(result.includes(AnalysisKind.CodeScanning));
t.assert(result.includes(AnalysisKind.CodeQuality));
});
test("getAnalysisKinds - throws if `analysis-kinds` input is invalid", async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing");
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true));
});

View File

@@ -1,4 +1,8 @@
import { fixCodeQualityCategory } from "./actions-util";
import {
fixCodeQualityCategory,
getOptionalInput,
getRequiredInput,
} from "./actions-util";
import { Logger } from "./logging";
import { ConfigurationError } from "./util";
@@ -41,6 +45,55 @@ export async function parseAnalysisKinds(
);
}
// Used to avoid re-parsing the input after we have done it once.
let cachedAnalysisKinds: AnalysisKind[] | undefined;
/**
* Initialises the analysis kinds for the analysis based on the `analysis-kinds` input.
* This function will also use the deprecated `quality-queries` input as an indicator to enable `code-quality`.
* If the `analysis-kinds` input cannot be parsed, a `ConfigurationError` is thrown.
*
* @param logger The logger to use.
* @param skipCache For testing, whether to ignore the cached values (default: false).
*
* @returns The array of enabled analysis kinds.
* @throws A `ConfigurationError` if the `analysis-kinds` input cannot be parsed.
*/
export async function getAnalysisKinds(
logger: Logger,
skipCache: boolean = false,
): Promise<AnalysisKind[]> {
if (!skipCache && cachedAnalysisKinds !== undefined) {
return cachedAnalysisKinds;
}
cachedAnalysisKinds = await parseAnalysisKinds(
getRequiredInput("analysis-kinds"),
);
// Warn that `quality-queries` is deprecated if there is an argument for it.
const qualityQueriesInput = getOptionalInput("quality-queries");
if (qualityQueriesInput !== undefined) {
logger.warning(
"The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. " +
"Use the `analysis-kinds` input to configure different analysis kinds instead.",
);
}
// For backwards compatibility, add Code Quality to the enabled analysis kinds
// if an input to `quality-queries` was specified. We should remove this once
// `quality-queries` is no longer used.
if (
!cachedAnalysisKinds.includes(AnalysisKind.CodeQuality) &&
qualityQueriesInput !== undefined
) {
cachedAnalysisKinds.push(AnalysisKind.CodeQuality);
}
return cachedAnalysisKinds;
}
/** The queries to use for Code Quality analyses. */
export const codeQualityQueries: string[] = ["code-quality"];

View File

@@ -310,6 +310,20 @@ test("wrapCliConfigurationError - pack cannot be found", (t) => {
t.true(wrappedError instanceof ConfigurationError);
});
test("wrapCliConfigurationError - unknown query file", (t) => {
const commandError = new CommandInvocationError(
"codeql",
["database", "init"],
2,
"my-query-file is not a .ql file, .qls file, a directory, or a query pack specification. See the logs for more details.",
);
const cliError = new CliError(commandError);
const wrappedError = wrapCliConfigurationError(cliError);
t.true(wrappedError instanceof ConfigurationError);
});
test("wrapCliConfigurationError - pack missing auth", (t) => {
const commandError = new CommandInvocationError(
"codeql",

View File

@@ -264,6 +264,9 @@ export const cliErrorsConfig: Record<
new RegExp(
"Query pack .* cannot be found\\. Check the spelling of the pack\\.",
),
new RegExp(
"is not a .ql file, .qls file, a directory, or a query pack specification.",
),
],
},
[CliConfigErrorCategory.PackMissingAuth]: {

View File

@@ -49,10 +49,9 @@ function createTestInitConfigInputs(
return Object.assign(
{},
{
analysisKindsInput: "code-scanning",
analysisKinds: [AnalysisKind.CodeScanning],
languagesInput: undefined,
queriesInput: undefined,
qualityQueriesInput: undefined,
packsInput: undefined,
configFile: undefined,
dbLocation: undefined,
@@ -149,6 +148,7 @@ test("load empty config", async (t) => {
});
const config = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput: languages,
repository: { owner: "github", repo: "example" },
@@ -188,8 +188,9 @@ test("load code quality config", async (t) => {
});
const config = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
analysisKindsInput: "code-quality",
analysisKinds: [AnalysisKind.CodeQuality],
languagesInput: languages,
repository: { owner: "github", repo: "example" },
tempDir,
@@ -272,8 +273,9 @@ test("initActionState doesn't throw if there are queries configured in the repos
await t.notThrowsAsync(async () => {
const config = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
analysisKindsInput: "code-quality",
analysisKinds: [AnalysisKind.CodeQuality],
languagesInput: languages,
repository: { owner: "github", repo: "example" },
tempDir,
@@ -310,6 +312,7 @@ test("loading a saved config produces the same config", async (t) => {
t.deepEqual(await configUtils.getConfig(tempDir, logger), undefined);
const config1 = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput: "javascript,python",
tempDir,
@@ -361,6 +364,7 @@ test("loading config with version mismatch throws", async (t) => {
.returns("does-not-exist");
const config = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput: "javascript,python",
tempDir,
@@ -389,6 +393,7 @@ test("load input outside of workspace", async (t) => {
return await withTmpDir(async (tempDir) => {
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
configFile: "../input",
tempDir,
@@ -416,6 +421,7 @@ test("load non-local input with invalid repo syntax", async (t) => {
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
configFile,
tempDir,
@@ -444,6 +450,7 @@ test("load non-existent input", async (t) => {
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput,
configFile,
@@ -527,6 +534,7 @@ test("load non-empty input", async (t) => {
const configFilePath = createConfigFile(inputFileContents, tempDir);
const actualConfig = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput,
buildModeInput: "none",
@@ -583,6 +591,7 @@ test("Using config input and file together, config input should be used.", async
const languagesInput = "javascript";
const config = await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput,
configFile: configFilePath,
@@ -633,6 +642,7 @@ test("API client used when reading remote config", async (t) => {
const languagesInput = "javascript";
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput,
configFile,
@@ -653,6 +663,7 @@ test("Remote config handles the case where a directory is provided", async (t) =
const repoReference = "octo-org/codeql-config/config.yaml@main";
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
configFile: repoReference,
tempDir,
@@ -681,6 +692,7 @@ test("Invalid format of remote config handled correctly", async (t) => {
const repoReference = "octo-org/codeql-config/config.yaml@main";
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
configFile: repoReference,
tempDir,
@@ -710,6 +722,7 @@ test("No detected languages", async (t) => {
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
tempDir,
codeql,
@@ -732,6 +745,7 @@ test("Unknown languages", async (t) => {
try {
await configUtils.initConfig(
createFeatures([]),
createTestInitConfigInputs({
languagesInput,
tempDir,

View File

@@ -11,7 +11,6 @@ import {
CodeQuality,
codeQualityQueries,
CodeScanning,
parseAnalysisKinds,
} from "./analyses";
import * as api from "./api-client";
import { CachingKind, getCachingKind } from "./caching-utils";
@@ -20,6 +19,7 @@ import {
calculateAugmentation,
ExcludeQueryFilter,
generateCodeScanningConfig,
parseUserConfig,
UserConfig,
} from "./config/db-config";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
@@ -373,10 +373,8 @@ export async function getRawLanguages(
/** Inputs required to initialize a configuration. */
export interface InitConfigInputs {
analysisKindsInput: string;
languagesInput: string | undefined;
queriesInput: string | undefined;
qualityQueriesInput: string | undefined;
packsInput: string | undefined;
configFile: string | undefined;
dbLocation: string | undefined;
@@ -396,6 +394,7 @@ export interface InitConfigInputs {
apiDetails: api.GitHubApiCombinedDetails;
features: FeatureEnablement;
repositoryProperties: RepositoryProperties;
analysisKinds: AnalysisKind[];
logger: Logger;
}
@@ -405,10 +404,8 @@ export interface InitConfigInputs {
*/
export async function initActionState(
{
analysisKindsInput,
languagesInput,
queriesInput,
qualityQueriesInput,
packsInput,
buildModeInput,
dbLocation,
@@ -424,22 +421,11 @@ export async function initActionState(
githubVersion,
features,
repositoryProperties,
analysisKinds,
logger,
}: InitConfigInputs,
userConfig: UserConfig,
): Promise<Config> {
const analysisKinds = await parseAnalysisKinds(analysisKindsInput);
// For backwards compatibility, add Code Quality to the enabled analysis kinds
// if an input to `quality-queries` was specified. We should remove this once
// `quality-queries` is no longer used.
if (
!analysisKinds.includes(AnalysisKind.CodeQuality) &&
qualityQueriesInput !== undefined
) {
analysisKinds.push(AnalysisKind.CodeQuality);
}
const languages = await getLanguages(
codeql,
languagesInput,
@@ -540,10 +526,12 @@ async function downloadCacheWithTime(
}
async function loadUserConfig(
logger: Logger,
configFile: string,
workspacePath: string,
apiDetails: api.GitHubApiCombinedDetails,
tempDir: string,
validateConfig: boolean,
): Promise<UserConfig> {
if (isLocal(configFile)) {
if (configFile !== userConfigFromActionPath(tempDir)) {
@@ -556,9 +544,14 @@ async function loadUserConfig(
);
}
}
return getLocalConfig(configFile);
return getLocalConfig(logger, configFile, validateConfig);
} else {
return await getRemoteConfig(configFile, apiDetails);
return await getRemoteConfig(
logger,
configFile,
apiDetails,
validateConfig,
);
}
}
@@ -794,7 +787,10 @@ function hasQueryCustomisation(userConfig: UserConfig): boolean {
* This will parse the config from the user input if present, or generate
* a default config. The parsed config is then stored to a known location.
*/
export async function initConfig(inputs: InitConfigInputs): Promise<Config> {
export async function initConfig(
features: FeatureEnablement,
inputs: InitConfigInputs,
): Promise<Config> {
const { logger, tempDir } = inputs;
// if configInput is set, it takes precedence over configFile
@@ -814,11 +810,14 @@ export async function initConfig(inputs: InitConfigInputs): Promise<Config> {
logger.debug("No configuration file was provided");
} else {
logger.debug(`Using configuration file: ${inputs.configFile}`);
const validateConfig = await features.getValue(Feature.ValidateDbConfig);
userConfig = await loadUserConfig(
logger,
inputs.configFile,
inputs.workspacePath,
inputs.apiDetails,
tempDir,
validateConfig,
);
}
@@ -912,7 +911,11 @@ function isLocal(configPath: string): boolean {
return configPath.indexOf("@") === -1;
}
function getLocalConfig(configFile: string): UserConfig {
function getLocalConfig(
logger: Logger,
configFile: string,
validateConfig: boolean,
): UserConfig {
// Error if the file does not exist
if (!fs.existsSync(configFile)) {
throw new ConfigurationError(
@@ -920,12 +923,19 @@ function getLocalConfig(configFile: string): UserConfig {
);
}
return yaml.load(fs.readFileSync(configFile, "utf8")) as UserConfig;
return parseUserConfig(
logger,
configFile,
fs.readFileSync(configFile, "utf-8"),
validateConfig,
);
}
async function getRemoteConfig(
logger: Logger,
configFile: string,
apiDetails: api.GitHubApiCombinedDetails,
validateConfig: boolean,
): Promise<UserConfig> {
// retrieve the various parts of the config location, and ensure they're present
const format = new RegExp(
@@ -961,9 +971,12 @@ async function getRemoteConfig(
);
}
return yaml.load(
return parseUserConfig(
logger,
configFile,
Buffer.from(fileContents, "base64").toString("binary"),
) as UserConfig;
validateConfig,
);
}
/**

View File

@@ -2,7 +2,13 @@ import test, { ExecutionContext } from "ava";
import { RepositoryProperties } from "../feature-flags/properties";
import { KnownLanguage, Language } from "../languages";
import { prettyPrintPack } from "../util";
import { getRunnerLogger } from "../logging";
import {
checkExpectedLogMessages,
getRecordingLogger,
LoggedMessage,
} from "../testing-utils";
import { ConfigurationError, prettyPrintPack } from "../util";
import * as dbConfig from "./db-config";
@@ -391,3 +397,111 @@ test(
{},
/"a-pack-without-a-scope" is not a valid pack/,
);
test("parseUserConfig - successfully parses valid YAML", (t) => {
const result = dbConfig.parseUserConfig(
getRunnerLogger(true),
"test",
`
paths-ignore:
- "some/path"
queries:
- uses: foo
some-unknown-option: true
`,
true,
);
t.truthy(result);
if (t.truthy(result["paths-ignore"])) {
t.is(result["paths-ignore"].length, 1);
t.is(result["paths-ignore"][0], "some/path");
}
if (t.truthy(result["queries"])) {
t.is(result["queries"].length, 1);
t.deepEqual(result["queries"][0], { uses: "foo" });
}
});
test("parseUserConfig - throws a ConfigurationError if the file is not valid YAML", (t) => {
t.throws(
() =>
dbConfig.parseUserConfig(
getRunnerLogger(true),
"test",
`
paths-ignore:
- "some/path"
queries:
- foo
`,
true,
),
{
instanceOf: ConfigurationError,
},
);
});
test("parseUserConfig - validation isn't picky about `query-filters`", (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
t.notThrows(() =>
dbConfig.parseUserConfig(
logger,
"test",
`
query-filters:
- something
- include: foo
- exclude: bar
`,
true,
),
);
});
test("parseUserConfig - throws a ConfigurationError if validation fails", (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
t.throws(
() =>
dbConfig.parseUserConfig(
logger,
"test",
`
paths-ignore:
- "some/path"
queries: true
`,
true,
),
{
instanceOf: ConfigurationError,
message:
'The configuration file "test" is invalid: instance.queries is not of a type(s) array.',
},
);
const expectedMessages = ["instance.queries is not of a type(s) array"];
checkExpectedLogMessages(t, loggedMessages, expectedMessages);
});
test("parseUserConfig - throws no ConfigurationError if validation should fail, but feature is disabled", (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
t.notThrows(() =>
dbConfig.parseUserConfig(
logger,
"test",
`
paths-ignore:
- "some/path"
queries: true
`,
false,
),
);
});

View File

@@ -1,7 +1,10 @@
import * as path from "path";
import * as yaml from "js-yaml";
import * as jsonschema from "jsonschema";
import * as semver from "semver";
import type { UserConfig as DbConfig, QuerySpec } from "../db-config-schema";
import * as errorMessages from "../error-messages";
import {
RepositoryProperties,
@@ -11,6 +14,8 @@ import { Language } from "../languages";
import { Logger } from "../logging";
import { cloneObject, ConfigurationError, prettyPrintPack } from "../util";
export type { QuerySpec } from "../db-config-schema";
export interface ExcludeQueryFilter {
exclude: Record<string, string[] | string>;
}
@@ -21,30 +26,14 @@ export interface IncludeQueryFilter {
export type QueryFilter = ExcludeQueryFilter | IncludeQueryFilter;
export interface QuerySpec {
name?: string;
uses: string;
}
/**
* Format of the config file supplied by the user.
*/
export interface UserConfig {
name?: string;
"disable-default-queries"?: boolean;
queries?: QuerySpec[];
"paths-ignore"?: string[];
paths?: string[];
// If this is a multi-language analysis, then the packages must be split by
// language. If this is a single language analysis, then no split by
// language is necessary.
packs?: Record<string, string[]> | string[];
export type UserConfig = DbConfig & {
// Set of query filters to include and exclude extra queries based on
// codeql query suite `include` and `exclude` properties
"query-filters"?: QueryFilter[];
}
};
/**
* Represents additional configuration data from a source other than
@@ -474,3 +463,53 @@ export function generateCodeScanningConfig(
return augmentedConfig;
}
/**
* Attempts to parse `contents` into a `UserConfig` value.
*
* @param logger The logger to use.
* @param pathInput The path to the file where `contents` was obtained from, for use in error messages.
* @param contents The string contents of a YAML file to try and parse as a `UserConfig`.
* @param validateConfig Whether to validate the configuration file against the schema.
* @returns The `UserConfig` corresponding to `contents`, if parsing was successful.
* @throws A `ConfigurationError` if parsing failed.
*/
export function parseUserConfig(
logger: Logger,
pathInput: string,
contents: string,
validateConfig: boolean,
): UserConfig {
try {
const schema =
// eslint-disable-next-line @typescript-eslint/no-require-imports
require("../../schemas/db-config-schema.json") as jsonschema.Schema;
const doc = yaml.load(contents);
if (validateConfig) {
const result = new jsonschema.Validator().validate(doc, schema);
if (result.errors.length > 0) {
for (const error of result.errors) {
logger.error(error.stack);
}
throw new ConfigurationError(
errorMessages.getInvalidConfigFileMessage(
pathInput,
result.errors.map((e) => e.stack),
),
);
}
}
return doc as UserConfig;
} catch (error) {
if (error instanceof yaml.YAMLException) {
throw new ConfigurationError(
errorMessages.getConfigFileParseErrorMessage(pathInput, error.message),
);
}
throw error;
}
}

53
src/db-config-schema.d.ts vendored Normal file
View File

@@ -0,0 +1,53 @@
/* This file was automatically generated by `npm run generate:schemas`. Do not edit by hand. */
/**
* Format of the config file supplied by the user for CodeQL analysis
*/
export interface UserConfig {
/**
* Name of the configuration
*/
name?: string;
/**
* Whether to disable default queries
*/
"disable-default-queries"?: boolean;
/**
* List of additional queries to run
*/
queries?: QuerySpec[];
/**
* Paths to ignore during analysis
*/
"paths-ignore"?: string[];
/**
* Paths to include in analysis
*/
paths?: string[];
/**
* Query packs to include. Can be a simple array for single-language analysis or an object with language-specific arrays for multi-language analysis
*/
packs?:
| string[]
| {
[k: string]: string[];
};
/**
* Set of query filters to include and exclude extra queries based on CodeQL query suite include and exclude properties
*/
"query-filters"?: unknown[];
[k: string]: unknown;
}
/**
* Detailed query specification object
*/
export interface QuerySpec {
/**
* Optional name for the query
*/
name?: string;
/**
* The query or query suite to use
*/
uses: string;
}

View File

@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.23.2",
"cliVersion": "2.23.2",
"priorBundleVersion": "codeql-bundle-v2.23.1",
"priorCliVersion": "2.23.1"
"bundleVersion": "codeql-bundle-v2.23.3",
"cliVersion": "2.23.3",
"priorBundleVersion": "codeql-bundle-v2.23.2",
"priorCliVersion": "2.23.2"
}

View File

@@ -47,6 +47,9 @@ export enum EnvVar {
/** Whether the CodeQL Action has already warned the user about low disk space. */
HAS_WARNED_ABOUT_DISK_SPACE = "CODEQL_ACTION_HAS_WARNED_ABOUT_DISK_SPACE",
/** Whether the `setup-codeql` action has been run. */
SETUP_CODEQL_ACTION_HAS_RUN = "CODEQL_ACTION_SETUP_CODEQL_HAS_RUN",
/** Whether the init action has been run. */
INIT_ACTION_HAS_RUN = "CODEQL_ACTION_INIT_HAS_RUN",

View File

@@ -14,6 +14,22 @@ export function getConfigFileDoesNotExistErrorMessage(
return `The configuration file "${configFile}" does not exist`;
}
export function getConfigFileParseErrorMessage(
configFile: string,
message: string,
): string {
return `Cannot parse "${configFile}": ${message}`;
}
export function getInvalidConfigFileMessage(
configFile: string,
messages: string[],
): string {
const andMore =
messages.length > 10 ? `, and ${messages.length - 10} more.` : ".";
return `The configuration file "${configFile}" is invalid: ${messages.slice(0, 10).join(", ")}${andMore}`;
}
export function getConfigFileRepoFormatInvalidMessage(
configFile: string,
): string {

View File

@@ -77,6 +77,7 @@ export enum Feature {
QaTelemetryEnabled = "qa_telemetry_enabled",
ResolveSupportedLanguagesUsingCli = "resolve_supported_languages_using_cli",
UseRepositoryProperties = "use_repository_properties",
ValidateDbConfig = "validate_db_config",
}
export const featureConfig: Record<
@@ -287,6 +288,11 @@ export const featureConfig: Record<
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0",
},
[Feature.ValidateDbConfig]: {
defaultValue: false,
envVar: "CODEQL_ACTION_VALIDATE_DB_CONFIG",
minimumVersion: undefined,
},
};
/**

View File

@@ -2,6 +2,7 @@ import test, { ExecutionContext } from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { AnalysisKind } from "./analyses";
import * as codeql from "./codeql";
import * as configUtils from "./config-utils";
import { Feature } from "./feature-flags";
@@ -28,12 +29,13 @@ test("post: init action with debug mode off", async (t) => {
const gitHubVersion: util.GitHubVersion = {
type: util.GitHubVariant.DOTCOM,
};
sinon.stub(configUtils, "getConfig").resolves({
debugMode: false,
gitHubVersion,
languages: [],
packs: [],
} as unknown as configUtils.Config);
sinon.stub(configUtils, "getConfig").resolves(
createTestConfig({
debugMode: false,
gitHubVersion,
languages: [],
}),
);
const uploadAllAvailableDebugArtifactsSpy = sinon.spy();
const printDebugLogsSpy = sinon.spy();
@@ -295,6 +297,17 @@ test("uploading failed SARIF run fails when workflow does not reference github/c
t.truthy(result.upload_failed_run_stack_trace);
});
test("not uploading failed SARIF when `code-scanning` is not an enabled analysis kind", async (t) => {
const result = await testFailedSarifUpload(t, createTestWorkflow([]), {
analysisKinds: [AnalysisKind.CodeQuality],
expectUpload: false,
});
t.is(
result.upload_failed_run_skipped_because,
"Code Scanning is not enabled.",
);
});
function createTestWorkflow(
steps: workflow.WorkflowJobStep[],
): workflow.Workflow {
@@ -327,20 +340,22 @@ async function testFailedSarifUpload(
expectUpload = true,
exportDiagnosticsEnabled = false,
matrix = {},
analysisKinds = [AnalysisKind.CodeScanning],
}: {
category?: string;
databaseExists?: boolean;
expectUpload?: boolean;
exportDiagnosticsEnabled?: boolean;
matrix?: { [key: string]: string };
analysisKinds?: AnalysisKind[];
} = {},
): Promise<initActionPostHelper.UploadFailedSarifResult> {
const config = {
const config = createTestConfig({
analysisKinds,
codeQLCmd: "codeql",
debugMode: true,
languages: [],
packs: [],
} as unknown as configUtils.Config;
});
if (databaseExists) {
config.dbLocation = "path/to/database";
}

View File

@@ -7,7 +7,7 @@ import * as actionsUtil from "./actions-util";
import { CodeScanning } from "./analyses";
import { getApiClient } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql";
import { Config } from "./config-utils";
import { Config, isCodeScanningEnabled } from "./config-utils";
import * as dependencyCaching from "./dependency-caching";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement } from "./feature-flags";
@@ -139,6 +139,15 @@ export async function tryUploadSarifIfRunFailed(
EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigErrorStatus,
);
// If the only enabled analysis kind is `code-quality`, then we shouldn't
// upload the failed SARIF to Code Scanning.
if (!isCodeScanningEnabled(config)) {
return {
upload_failed_run_skipped_because: "Code Scanning is not enabled.",
};
}
try {
return await maybeUploadFailedSarif(
config,

View File

@@ -15,6 +15,7 @@ import {
getTemporaryDirectory,
persistInputs,
} from "./actions-util";
import { AnalysisKind, getAnalysisKinds } from "./analyses";
import { getGitHubVersion } from "./api-client";
import {
getDependencyCachingEnabled,
@@ -56,6 +57,7 @@ import { ToolsSource } from "./setup-codeql";
import {
ActionName,
InitStatusReport,
InitToolsDownloadFields,
InitWithConfigStatusReport,
createInitWithConfigStatusReport,
createStatusReportBase,
@@ -86,14 +88,29 @@ import {
} from "./util";
import { validateWorkflow } from "./workflow";
/** Fields of the init status report populated when the tools source is `download`. */
interface InitToolsDownloadFields {
/** Time taken to download the bundle, in milliseconds. */
tools_download_duration_ms?: number;
/**
* Whether the relevant tools dotcom feature flags have been misconfigured.
* Only populated if we attempt to determine the default version based on the dotcom feature flags. */
tools_feature_flags_valid?: boolean;
/**
* Sends a status report indicating that the `init` Action is starting.
*
* @param startedAt
* @param config
* @param logger
*/
async function sendStartingStatusReport(
startedAt: Date,
config: Partial<configUtils.Config> | undefined,
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.Init,
"starting",
startedAt,
config,
await checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
}
async function sendCompletedStatusReport(
@@ -210,6 +227,7 @@ async function run() {
? await loadPropertiesFromApi(gitHubVersion, logger, repositoryNwo)
: {};
// Create a unique identifier for this run.
const jobRunUuid = uuidV4();
logger.info(`Job run UUID is ${jobRunUuid}.`);
core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
@@ -227,17 +245,30 @@ async function run() {
);
try {
const statusReportBase = await createStatusReportBase(
ActionName.Init,
"starting",
startedAt,
config,
await checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
// Parsing the `analysis-kinds` input may throw a `ConfigurationError`, which we don't want before
// we have called `sendStartingStatusReport` below. However, we want the analysis kinds for that status
// report. To work around this, we ignore exceptions that are thrown here and then call `getAnalysisKinds`
// a second time later. The second call will then throw the exception again. If `getAnalysisKinds` is
// successful, the results are cached so that we don't duplicate the work in normal runs.
let analysisKinds: AnalysisKind[] | undefined;
try {
analysisKinds = await getAnalysisKinds(logger);
} catch (err) {
logger.debug(
`Failed to parse analysis kinds for 'starting' status report: ${getErrorMessage(err)}`,
);
}
// Send a status report indicating that an analysis is starting.
await sendStartingStatusReport(startedAt, { analysisKinds }, logger);
// Throw a `ConfigurationError` if the `setup-codeql` action has been run.
if (process.env[EnvVar.SETUP_CODEQL_ACTION_HAS_RUN] === "true") {
throw new ConfigurationError(
`The 'init' action should not be run in the same workflow as 'setup-codeql'.`,
);
}
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);
@@ -293,21 +324,11 @@ async function run() {
}
}
// Warn that `quality-queries` is deprecated if there is an argument for it.
const qualityQueriesInput = getOptionalInput("quality-queries");
if (qualityQueriesInput !== undefined) {
logger.warning(
"The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. " +
"Use the `analysis-kinds` input to configure different analysis kinds instead.",
);
}
config = await initConfig({
analysisKindsInput: getRequiredInput("analysis-kinds"),
analysisKinds = await getAnalysisKinds(logger);
config = await initConfig(features, {
analysisKinds,
languagesInput: getOptionalInput("languages"),
queriesInput: getOptionalInput("queries"),
qualityQueriesInput,
packsInput: getOptionalInput("packs"),
buildModeInput: getOptionalInput("build-mode"),
configFile,

View File

@@ -61,10 +61,11 @@ export async function initCodeQL(
}
export async function initConfig(
features: FeatureEnablement,
inputs: configUtils.InitConfigInputs,
): Promise<configUtils.Config> {
return await withGroupAsync("Load language configuration", async () => {
return await configUtils.initConfig(inputs);
return await configUtils.initConfig(features, inputs);
});
}

196
src/setup-codeql-action.ts Normal file
View File

@@ -0,0 +1,196 @@
import * as core from "@actions/core";
import { v4 as uuidV4 } from "uuid";
import {
getActionVersion,
getOptionalInput,
getRequiredInput,
getTemporaryDirectory,
} from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { CodeQL } from "./codeql";
import { EnvVar } from "./environment";
import { Features } from "./feature-flags";
import { initCodeQL } from "./init";
import { getActionsLogger, Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import { ToolsSource } from "./setup-codeql";
import {
ActionName,
InitStatusReport,
InitToolsDownloadFields,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
} from "./status-report";
import { ToolsDownloadStatusReport } from "./tools-download";
import {
checkDiskUsage,
checkForTimeout,
checkGitHubVersionInRange,
getRequiredEnvParam,
initializeEnvironment,
ConfigurationError,
wrapError,
checkActionVersion,
getErrorMessage,
} from "./util";
/**
* Helper function to send a full status report for this action.
*/
async function sendCompletedStatusReport(
startedAt: Date,
toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined,
toolsFeatureFlagsValid: boolean | undefined,
toolsSource: ToolsSource,
toolsVersion: string,
logger: Logger,
error?: Error,
): Promise<void> {
const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL,
getActionsStatus(error),
startedAt,
undefined,
await checkDiskUsage(logger),
logger,
error?.message,
error?.stack,
);
if (statusReportBase === undefined) {
return;
}
const initStatusReport: InitStatusReport = {
...statusReportBase,
tools_input: getOptionalInput("tools") || "",
tools_resolved_version: toolsVersion,
tools_source: toolsSource || ToolsSource.Unknown,
workflow_languages: "",
};
const initToolsDownloadFields: InitToolsDownloadFields = {};
if (toolsDownloadStatusReport?.downloadDurationMs !== undefined) {
initToolsDownloadFields.tools_download_duration_ms =
toolsDownloadStatusReport.downloadDurationMs;
}
if (toolsFeatureFlagsValid !== undefined) {
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
}
await sendStatusReport({ ...initStatusReport, ...initToolsDownloadFields });
}
/** The main behaviour of this action. */
async function run(): Promise<void> {
const startedAt = new Date();
const logger = getActionsLogger();
initializeEnvironment(getActionVersion());
let codeql: CodeQL;
let toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined;
let toolsFeatureFlagsValid: boolean | undefined;
let toolsSource: ToolsSource;
let toolsVersion: string;
const apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: getRequiredEnvParam("GITHUB_SERVER_URL"),
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
};
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
checkActionVersion(getActionVersion(), gitHubVersion);
const repositoryNwo = getRepositoryNwo();
const features = new Features(
gitHubVersion,
repositoryNwo,
getTemporaryDirectory(),
logger,
);
const jobRunUuid = uuidV4();
logger.info(`Job run UUID is ${jobRunUuid}.`);
core.exportVariable(EnvVar.JOB_RUN_UUID, jobRunUuid);
try {
const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL,
"starting",
startedAt,
undefined,
await checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
const initCodeQLResult = await initCodeQL(
getOptionalInput("tools"),
apiDetails,
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
features,
logger,
);
codeql = initCodeQLResult.codeql;
toolsDownloadStatusReport = initCodeQLResult.toolsDownloadStatusReport;
toolsVersion = initCodeQLResult.toolsVersion;
toolsSource = initCodeQLResult.toolsSource;
core.setOutput("codeql-path", codeql.getPath());
core.setOutput("codeql-version", (await codeql.getVersion()).version);
core.exportVariable(EnvVar.SETUP_CODEQL_ACTION_HAS_RUN, "true");
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
core.setFailed(error.message);
const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL,
error instanceof ConfigurationError ? "user-error" : "failure",
startedAt,
undefined,
await checkDiskUsage(logger),
logger,
error.message,
error.stack,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
return;
}
await sendCompletedStatusReport(
startedAt,
toolsDownloadStatusReport,
toolsFeatureFlagsValid,
toolsSource,
toolsVersion,
logger,
);
}
/** Run the action and catch any unhandled errors. */
async function runWrapper(): Promise<void> {
try {
await run();
} catch (error) {
core.setFailed(`setup-codeql action failed: ${getErrorMessage(error)}`);
}
await checkForTimeout();
}
void runWrapper();

View File

@@ -41,6 +41,7 @@ export enum ActionName {
Init = "init",
InitPost = "init-post",
ResolveEnvironment = "resolve-environment",
SetupCodeQL = "setup-codeql",
StartProxy = "start-proxy",
UploadSarif = "upload-sarif",
}
@@ -516,6 +517,16 @@ export interface InitWithConfigStatusReport extends InitStatusReport {
config_file: string;
}
/** Fields of the init status report populated when the tools source is `download`. */
export interface InitToolsDownloadFields {
/** Time taken to download the bundle, in milliseconds. */
tools_download_duration_ms?: number;
/**
* Whether the relevant tools dotcom feature flags have been misconfigured.
* Only populated if we attempt to determine the default version based on the dotcom feature flags. */
tools_feature_flags_valid?: boolean;
}
/**
* Composes a `InitWithConfigStatusReport` from the given values.
*

View File

@@ -2,7 +2,7 @@ import { TextDecoder } from "node:util";
import path from "path";
import * as github from "@actions/github";
import { TestFn } from "ava";
import { ExecutionContext, TestFn } from "ava";
import nock from "nock";
import * as sinon from "sinon";
@@ -180,6 +180,23 @@ export function getRecordingLogger(messages: LoggedMessage[]): Logger {
};
}
export function checkExpectedLogMessages(
t: ExecutionContext<any>,
messages: LoggedMessage[],
expectedMessages: string[],
) {
for (const expectedMessage of expectedMessages) {
t.assert(
messages.some(
(msg) =>
typeof msg.message === "string" &&
msg.message.includes(expectedMessage),
),
`Expected '${expectedMessage}' in the logger output, but didn't find it in:\n ${messages.map((m) => ` - '${m.message}'`).join("\n")}`,
);
}
}
/** Mock the HTTP request to the feature flags enablement API endpoint. */
export function mockFeatureFlagApiEndpoint(
responseStatusCode: number,