Skip to content

Use the combined bundle when queries may need other languages' library packs - #4184

Open
henrymercer wants to merge 20 commits into
mainfrom
henrymercer/per-language-pr-check-failures
Open

henrymercer wants to merge 20 commits into
mainfrom
henrymercer/per-language-pr-check-failures

Conversation

@henrymercer

@henrymercer henrymercer commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

We support custom configuration files that include packs for other languages, for example:

name: Use custom queries
disable-default-queries: true
queries:
  ...
  - name: Go queries
    uses: codeql-testing/go-querypack@master
  - name: Cpp queries
    uses: codeql-testing/cpp-querypack@second-branch
  - name: JavaScript queries
    uses: codeql-testing/javascript-querypack/show_ifs2.ql@master
  - name: Python queries
    uses: codeql-testing/python-querypack/show_ifs2.ql@second-branch

as taken from our PR checks.

If these custom queries live in compiled packs, then the packs ship their own dependencies. However if the custom query is just a path to a QL file, then the dependencies are resolved from a bundle.

This creates an issue with per-language bundles: CodeQL resolves every configured query before selecting the ones for the analyzed language, so a single-language analysis with a configuration like the above will fail. This is evidenced by failures in the "Go: Custom queries" and "Start proxy" PR checks when using per-language bundles.

This PR only selects a per-language bundle when the query configuration known before CodeQL is set up can't reference such queries: there's no configuration file, the config input is unset or only sets threat models and model packs like default setup's does, and the queries input and github-codeql-extra-queries repository property only name built-in query suites. This avoids loading configuration files before setting up CodeQL, at the cost of using the combined bundle for configuration files that only use built-in queries. The config input is parsed once, before setting up CodeQL, and used both for this check and to configure the analysis. As a result, an invalid config input now fails before CodeQL is downloaded, with an error that names the config input rather than a temporary file.

This PR also modifies setup-codeql to always use the combined bundle, since it can't tell whether the queries that a workflow runs with the CLI will need library packs for other languages. A separate commit corrects the docs for its languages and analysis-kinds inputs, which said to also pass them to init, even though init fails if setup-codeql has run in the same job.

It also disables per-language bundles in the "Export file baseline information" PR check, since a per-language bundle only reports file baseline information for its own language. The second commit moves defaultSuites so that per-language-bundles.ts can use it without an import cycle.

Finally, it moves per-language bundles to a new per_language_bundles_v2 feature flag, so that we can roll them out to Action versions that include this fix without also enabling them for earlier versions.

Risk assessment

Low risk: The change only affects bundle selection when the per_language_bundles or per_language_bundles_v2 feature flag is enabled.

Which use cases does this change impact?

Workflow types:

  • Advanced setup - Single-language analyses with a configuration file, a config input, or queries that aren't built-in query suites, and workflows that pass a single language to setup-codeql.
  • Managed - Default setup analyses that get a configuration file, or queries that aren't built-in query suites, from repository properties.

Products:

  • Code Scanning
  • Code Quality

Environments:

  • Dotcom - Per-language bundles are only selected on GitHub.com.

How did/will you validate this change?

  • Unit tests - Each source of query configuration, and bundle selection for releases and nightlies. The setup-codeql Action's entry point has no unit tests, so its reason is only covered indirectly.
  • End-to-end tests - "Go: Custom queries" and "Start proxy" use configuration files that reference other languages' queries, so they cover this change whenever they download a bundle with per-language bundles enabled.

If something goes wrong after this change is released, what are the mitigation and rollback strategies?

  • Feature flags - Disable per-language bundle selection with per_language_bundles_v2.

How will you know if something goes wrong after this change is released?

  • Telemetry - Monitor init failures in analyses that use a per-language bundle.

Are there any special considerations for merging or releasing this change?

  • No special considerations - This change can be merged at any time.

Merge / deployment checklist

  • Confirm this change is backwards compatible with existing workflows.
  • Consider adding a changelog entry for this change.
  • Confirm the readme and docs have been updated if necessary.

@github-actions github-actions Bot added the size/L May be hard to review label Sep 30, 2026
@henrymercer
henrymercer requested a balanced review from Copilot October 1, 2026 09:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Copilot review overview

🟢 Approval recommended

Bundle eligibility is consistently propagated and covered by focused tests without unresolved correctness issues.

Review effort: Balanced
Findings: None

What changed in this PR

Updates bundle selection to use combined bundles whenever configured queries may depend on other languages’ library packs.

Changes:

  • Detects query configurations requiring combined bundles.
  • Propagates bundle-selection reasoning through CodeQL setup.
  • Updates tests, documentation, and PR-check configuration.
File Description
src/​per-language-bundles.ts Adds query eligibility logic.
src/​per-language-bundles.test.ts Tests eligibility and explanations.
src/​setup-codeql.ts Applies eligibility to release and nightly bundles.
src/​setup-codeql.test.ts Tests combined-bundle selection.
src/​setup-codeql-action.ts Forces combined bundles for standalone setup.
src/​init-action.ts Collects query configuration inputs.
src/​init.ts Propagates bundle-selection reasoning.
src/​codeql.ts Propagates setup options.
src/​codeql.test.ts Updates setup calls.
src/​upload-lib.ts Updates initialization call.
src/​config/​db-config.ts Centralizes built-in suite names.
src/​analyze.ts Uses centralized suite names.
src/​analyze.test.ts Updates suite import.
setup-codeql/​action.yml Corrects input documentation.
pr-checks/​checks/​export-file-baseline-information.yml Disables per-language bundles for the baseline check.
lib/​entry-points.js Generated output; excluded from review.
.github/​workflows/​__export-file-baseline-information.yml Generated workflow; excluded from review.
Files excluded by content exclusion policy (2)
  • .github/workflows/__export-file-baseline-information.yml
  • lib/entry-points.js

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@henrymercer
henrymercer marked this pull request as ready for review October 1, 2026 11:32
@henrymercer
henrymercer requested a review from a team as a code owner October 1, 2026 11:32

@mbg mbg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for taking care of this! I think this approach makes sense to work around the issue. Query customisation is reasonably rare so that this still allows most users to benefit from per-language bundles, while avoiding any issues like we saw in CI for those that do customise them.

I left a few comments, with one or two points about long-term maintainability and otherwise minor comments. So, generally this looks pretty good already and it shouldn't be far off from being ready to merge.

Comment thread pr-checks/checks/export-file-baseline-information.yml
Comment thread src/setup-codeql-action.ts Outdated
Comment thread src/per-language-bundles.ts
Comment thread src/per-language-bundles.ts Outdated
Comment thread src/per-language-bundles.ts Outdated
Comment thread src/per-language-bundles.ts Outdated
Comment thread src/per-language-bundles.ts Outdated
Comment thread src/init-action.ts Outdated

@mbg mbg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing the comments from my previous review! I've had some more comments on the changes here. The main ones are again related to the maintainability of duplicated logic and whether we can reduce that or move some of the work to a common place before any of the consumers.

Comment thread src/config/db-config.ts Outdated
Comment on lines +464 to +466
* Parses the `queries` input, a comma-separated list of queries that's combined with the queries
* from the configuration if it starts with '+'. The `input` of the result is `undefined` if the
* value is unset or empty. Entries aren't validated, so an empty entry becomes `{ uses: "" }`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: This comment is largely about how this function is used, not what it does. I.e. this function is completely disconnected from the queries input and doesn't control how the returned combines value is interpreted by the caller.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1c0814d

Comment thread src/config/db-config.ts Outdated
Comment on lines +481 to +483
* Parses the `github-codeql-extra-queries` repository property, which has the same format as the
* `queries` input. The `input` of the result is `undefined` if the value is unset or empty. Entries
* aren't validated, so an empty entry becomes `{ uses: "" }`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Same issue with this comment as with the one for parseQueriesInput.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1c0814d

Comment thread src/config/db-config.ts Outdated
Comment on lines +487 to +489
export function parseExtraQueriesProperty(
value: string | undefined,
): Augmentation<QuerySpec[]> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is essentially the same as parseQueriesInput, except that it provides the additional argument to parseQueriesFromInput. When I added the repo property, I added the extra, optional parameter to parseQueriesFromInput so that the function could be shared between the two uses.

Consider whether parseQueriesInput could have a second, optional parameter for the error like parseQueriesFromInput does to avoid having a mostly duplicate parseExtraQueriesProperty or whether we could move the shouldCombine call into parseQueriesFromInput.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1c0814d. Since we're calling parseQueriesFromInput in two places, I removed the error parameter from parseQueriesFromInput as part of these changes so that we don't need to create the error message in two places.

Comment on lines +87 to +89
const query = findNonBuiltInQuery(
parseQueriesInput(inputs.queriesInput).input,
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for changing this to better reuse the existing implementation!

Another concern here, which I don't think is blocking for this PR, is that getOtherLanguagePacksReason is more conservative than it needs to be. Specifically, we ignore the combines property and so are ignoring the precedence / combination rules that are implemented in combineQueries. Since that may discard some of the configured queries if others take precedence / don't allow combining, not all of the configured queries may actually end up getting passed to the CLI. If that is the case, then we might disable per-language bundles here even though we may not need to. Since this is more conservative than necessary, it is fine as-is, but we could look into reusing more of the combineQueries logic here to ensure consistency and to allow per-language bundles in more cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we are more conservative than necessary, which is fine, and that it would make sense to look into refactoring this as future work.

Comment on lines +64 to +66
* @throws A `ConfigurationError` if the `queries` input or the `github-codeql-extra-queries`
* repository property is a '+' with no queries after it, unless an input that's checked earlier
* already gives a reason.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Observation: getOtherLanguagePacksReason will now throw if parseQueriesInput or parseExtraQueriesProperty throws. Previously, getOtherLanguagePacksReason would never have thrown an error.

It's probably fine because the circumstances under which an error is thrown here would lead to an error later on in any case, but it moves that error to an earlier point in the execution.

I don't think that's an issue because I don't think there's anything happening in-between the two throwing sites that would particularly matter, but if we wanted to be more conservative then we could catch the exceptions in getOtherLanguagePacksReason and treat them as equivalent to no custom queries being configured in the respective case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably fine because the circumstances under which an error is thrown here would lead to an error later on in any case, but it moves that error to an earlier point in the execution.

Exactly this — we're going to fail later anyway after downloading the CodeQL CLI, so if anything it is better to fail earlier on. Note that with the follow up changes I've made, this reasoning now applies to invalid YAML in the config input too.

Comment thread src/per-language-bundles.ts Outdated
// queries. For example, default setup only uses it for threat models and model packs.
// The `config` input can configure queries in the same way as a configuration file. We assume
// that dynamic workflows, which GitHub manages, don't use it to add queries. For example, default
// setup only uses it for threat models and model packs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super minor: instead of the example at the end, we could mention the DefaultSetupConfig type here (from db-config.ts) to look at for what assumptions we do make about the configuration provided by Default Setup. Since that affects the behaviour of the Action, we are more likely to update it if things change than the example in this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think DEFAULT_SETUP_CONFIG_SCHEMA is what we want, since we're interested in the UserConfig properties set by default setup, rather than the eventual augmented user configuration. I've added a comment in f6a7f00.

Comment thread src/config/db-config.ts Outdated
* uses, which are threat models and model packs, to valid values. Returns `false` otherwise,
* including if `contents` isn't valid YAML.
*/
export function matchesDefaultSetupConfigSchema(contents: string): boolean {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matchesDefaultSetupConfigSchema implements similar logic to mergeDefaultSetupAndUserConfigs with some minor differences (mergeDefaultSetupAndUserConfigs expects to already be given a UserConfig object and doesn't parse the YAML itself; both handle the outcomes of checkSchema differently).

Two concerns with this:

  1. We may end up parsing and validating the config twice. That seems a bit unnecessary.
  2. If we change one for any reason, we should also change the other.

Alongside the similar points for getOtherLanguagePacksReason, I am starting to think that maybe we should refactor this more so that we perform the input processing once before getOtherLanguagePacksReason and initConfig are called, and then pass the results of the input processing to both. I appreciate that it's more work to do that than we'd like for this though, and I haven't checked if any of the relevant input processing in initConfig has a dependency on the CLI.

Could we at least modify the flow so that the config input is parsed as YAML earlier and the resulting object can be used here as well as in mergeDefaultSetupAndUserConfigs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we at least modify the flow so that the config input is parsed as YAML earlier and the resulting object can be used here as well as in mergeDefaultSetupAndUserConfigs?

I've done this, so we only parse config as YAML once now. I'd prefer to leave the rest to future work, since this PR is already changing enough functionality.

Comment thread src/config/db-config.ts Outdated
Comment on lines +106 to +111
} catch (error) {
if (error instanceof yaml.YAMLException) {
return false;
}
throw error;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are keeping the YAML processing here, I think it would be good to add an inline comment here before the if (error instanceof ...) check to explain why we are happy to treat a YAMLException as indicative that the configuration wasn't provided by Default Setup. (Maybe also add a logger.debug call to log that case.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer relevant given the refactoring to only parse YAML once. If we have invalid YAML we'll now fail init before we download the CLI.

@henrymercer henrymercer added Rebuild Re-transpile JS & re-generate workflows and removed Rebuild Re-transpile JS & re-generate workflows labels Oct 2, 2026
@henrymercer

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

…age-pr-check-failures

# Conflicts:
#	lib/entry-points.js

Co-authored-by: henrymercer <[email protected]>

Copilot AI commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merged main in a47cc9f. The only conflict was in the generated lib/entry-points.js, because main bumped @actions/cache to 5.3.0. I resolved it by installing the merged dependencies and regenerating lib with rm -rf lib && npm run-script build.

  • The PR's diff against main is unchanged: 21 files, +732/−191. The only lines in the rebuilt lib/entry-points.js that differ from main are this PR's changes. Some identifiers come out renamed (logger → logger2, isObject → isObject2), which matches how main's build already names them.
  • check-js.sh reports the JS is up to date, all 1162 unit tests pass, ESLint passes on the changed files, and pr-checks/sync.ts doesn't change any generated workflows.

npm ci can't run in my sandbox: 31 resolved URLs in package-lock.json point to ms-feed-*.pkgs.visualstudio.com, which I can't reach, and those entries are on main too. To install, I pointed those entries at registry.npmjs.org in a temporary copy of the lockfile and kept the integrity hashes. I put the original lockfile back before committing, so the packages are identical and the committed lockfile is unchanged.

@henrymercer

Copy link
Copy Markdown
Contributor Author

npm ci can't run in my sandbox: 31 resolved URLs in package-lock.json point to ms-feed-*.pkgs.visualstudio.com, which I can't reach, and those entries are on main too. To install, I pointed those entries at registry.npmjs.org in a temporary copy of the lockfile and kept the integrity hashes. I put the original lockfile back before committing, so the packages are identical and the committed lockfile is unchanged.

@mbg is preparing a fix for this.

@github-actions github-actions Bot added size/XL May be very hard to review and removed size/L May be hard to review labels Oct 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL May be very hard to review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants