If your CI pipeline installs the CodeQL CLI directly, a change from an all-platform bundle to platform-specific bundles is more than a simple file replacement.
The pipeline needs to know which operating system and CPU architecture it is running on, select the correct CodeQL package, install it consistently, and verify that the existing security analysis still works.
This matters most for teams using self-hosted runners, custom installation scripts, containers, or internal CI infrastructure.
A migration is successful only when the complete CodeQL workflow continues to work after the package change.
Understand How Your CI Currently Installs CodeQL
Before changing anything, identify how CodeQL enters your build environment.
There are several common approaches:
GitHub CodeQL Action
|
+-- Managed tooling
Direct CLI Download
|
+-- Custom installation script
Container Image
|
+-- CodeQL preinstalled
Self-Hosted Runner
|
+-- Persistent CodeQL installationThese approaches have different migration requirements.
If your workflow uses the CodeQL Action and does not manually install the CLI, the change may be largely handled by the action itself.
If your organization downloads the CodeQL CLI directly, you need to review the installation process.
Find Every CodeQL Installation
Start by searching your repositories and CI infrastructure.
Look for terms such as:
codeql
codeql-cli
codeql-bundle
CODEQL_VERSION
CODEQL_URLFor a repository using GitHub Actions, you might search:
grep -Rni "codeql" .github/On Windows PowerShell:
Get-ChildItem -Recurse .github | Select-String "codeql"Do not limit the search to workflow files.
CodeQL installation may also exist in:
scripts/
build/
tools/
Dockerfile
Makefile
bootstrap scripts
runner configurationDetermine Whether You Use the CLI Directly
A workflow such as:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: csharpis different from a workflow that downloads the CLI itself.
For example:
- name: Install CodeQL
run: ./scripts/install-codeql.sh
- name: Run CodeQL
run: codeql database analyze ...The second workflow has an explicit CLI dependency that your team owns.
That is where platform-specific bundle changes become particularly important.
Inventory Your Runner Platforms
Make a list of every environment that runs CodeQL.
For example:
Runner | Operating System | Architecture | Installation |
|---|---|---|---|
Runner A | Linux | x64 | Script |
Runner B | Windows | x64 | Script |
Runner C | macOS | ARM64 | Container/Script |
Runner D | Linux | ARM64 | Script |
Do not assume that all runners use the same environment.
A single repository may execute on multiple operating systems.
Operating System Detection Matters
A platform-specific package must match the operating system of the runner.
A simple shell-based installation process might begin with:
case "$(uname -s)" in
Linux)
echo "Linux runner"
;;
Darwin)
echo "macOS runner"
;;
*)
echo "Unsupported operating system"
exit 1
;;
esacThe actual CodeQL package-selection logic should follow the supported distribution method for your environment.
The important principle is that the installation should explicitly identify its target platform.
CPU Architecture Matters Too
Operating system detection alone may not be enough.
For example:
uname -mcan help identify the machine architecture on Unix-like systems.
A simplified decision process might look like:
Operating System
+
Architecture
|
v
CodeQL PackagePossible combinations can include:
Linux + x64
Linux + ARM64
macOS + x64
macOS + ARM64
Windows + x64Only select combinations supported by the CodeQL distribution you are using.
Avoid Hard-Coded Platform Assumptions
A fragile script might effectively assume:
Every runner = Linux x64That may work until a new runner is introduced.
A better installation process makes the environment explicit:
Detect Platform
|
v
Detect Architecture
|
v
Select Supported Package
|
v
Download
|
v
Verify
|
v
InstallThis makes failures easier to diagnose.
Review Version Pinning
Check whether your organization pins the CodeQL version.
For example:
CODEQL_VERSION="X.Y.Z"Version pinning can make CI behavior more predictable.
During the migration, record:
Current version
Target version
Operating system
Architecture
Package source
Installation directoryDo not combine a packaging migration with an unnecessary version upgrade unless there is a specific reason to do so.
Keeping the number of variables small makes troubleshooting easier.
Verify the Package Source
The package should come from an approved and trusted source.
Your installation process should make it clear:
Where does CodeQL come from?
Which version is downloaded?
Which package is selected?
How is its integrity verified?If your organization already uses checksums or other artifact verification controls, preserve them during the migration.
For example, a generic verification step might look like:
sha256sum codeql-package.tar.gzThe expected checksum should come from a trusted source.
Do not invent or hard-code a checksum without verifying it against the authoritative release information.
Review Custom Installation Scripts
A common migration problem is an old script containing assumptions about the previous package format.
For example:
curl -L "$CODEQL_URL" -o codeql.zip
unzip codeql.zipSearch for:
Download URLs
File names
Archive names
Extraction paths
PATH settings
Version variablesThe script may work perfectly today and fail after the old package is no longer available.
Check the PATH Configuration
A successful installation does not necessarily mean the CLI is usable.
For example:
codeql versionshould resolve to the expected installation.
On Linux or macOS:
which codeqlOn Windows PowerShell:
Get-Command codeqlCheck that the returned path points to the intended installation.
This is particularly important on persistent self-hosted runners, where an older CodeQL installation may still be present.
Self-Hosted Runners Need Extra Attention
GitHub-hosted runners are generally recreated or maintained according to the hosted environment's lifecycle.
Self-hosted runners can be persistent.
That creates a possible problem:
Old CodeQL
+
New CodeQL
|
v
Which one does CI execute?For example:
/usr/local/bin/codeql
/opt/codeql/codeqlmay both exist.
If the PATH points to the older installation, your migration script may appear to succeed while the pipeline continues using the old CLI.
Always verify the executable path and version.
Review Container Images
If CodeQL is installed inside a container, inspect the container definition.
For example:
FROM ubuntu:latest
COPY tools/codeql /opt/codeql
ENV PATH="/opt/codeql:${PATH}"The installation may not appear in the GitHub Actions workflow at all.
Search:
Dockerfile
Containerfile
image build scripts
base images
internal CI imagesAfter updating the package, rebuild the image rather than assuming the existing cached image contains the new installation.
Watch for Container Cache Problems
Suppose your workflow uses:
container:
image: company/security-tools:latestEven if the image tag stays the same, your CI environment may continue using a cached version depending on your infrastructure.
After changing CodeQL inside the image:
Build the updated image.
Push it to the appropriate registry.
Confirm the runner pulls the intended version.
Run
codeql version.Execute a complete analysis.
This prevents an old cached installation from hiding migration problems.
Check CodeQL Database Creation
Do not stop after:
codeql versionThe next test should be database creation.
A simplified workflow looks like:
Source Code
|
v
CodeQL Database Creation
|
v
CodeQL Database
|
v
Query AnalysisFor example, a C# repository might use a workflow conceptually similar to:
codeql database create codeql-db \
--language=csharp \
--command="dotnet build"Use the exact command structure appropriate for your CodeQL version and project.
The important test is that the new CLI can successfully create the database.
Test the Actual Analysis
After database creation, run the analysis stage.
The migration should preserve the existing:
Queries
Query Packs
Language Configuration
Database Configuration
Output FormatA successful version check is not proof that the security analysis still works.
Verify SARIF Results
CodeQL findings are commonly represented in SARIF format for security-result processing.
Your pipeline should continue producing valid results after the migration.
A simplified flow is:
CodeQL Analysis
|
v
SARIF Results
|
v
Security ResultsAfter migration, verify:
Analysis completes
SARIF is generated
Results contain expected data
Upload succeeds
Security findings appear where expected
Test Custom Queries
Some organizations use custom CodeQL queries.
For example:
queries/
|
+-- security/
| +-- insecure-api.ql
|
+-- performance/
+-- expensive-call.qlIf your pipeline depends on custom queries, run them during migration testing.
A CLI installation can be successful while query resolution or execution fails later.
Check Query Packs
If the pipeline uses query packs, verify that they are still available and compatible with the selected CodeQL setup.
A useful test sequence is:
CLI Installation
|
v
Database Creation
|
v
Query Pack Resolution
|
v
Analysis
|
v
SARIFTesting the complete path is more useful than validating each component independently.
Review CI Permissions
A packaging migration should not require unnecessarily broad permissions.
For a GitHub Actions workflow, permissions should be intentionally configured.
For example:
permissions:
contents: read
security-events: write
actions: readThe exact permissions required depend on the workflow and GitHub configuration.
Do not add broad permissions simply because the migration is failing.
First identify the actual permission problem.
Do Not Mix Too Many Changes
Suppose you are moving to platform-specific bundles.
Avoid doing all of these simultaneously:
CodeQL package migration
+
New CodeQL version
+
New runner OS
+
New container
+
New query packs
+
New CI permissionsIf the pipeline fails, identifying the cause becomes difficult.
A better sequence is:
Package Migration
|
v
Validation
|
v
Version Upgrade
|
v
ValidationSmall changes are easier to test and roll back.
Common Migration Problems
The Wrong CodeQL Version Is Running
Check:
codeql versionThen verify the executable:
which codeqlor:
Get-Command codeqlThe Package Does Not Run
Check:
Operating system
CPU architecture
Package compatibility
Extraction
Execution permissions
The CLI Works but Database Creation Fails
Review:
Build command
Language configuration
Compiler availability
Project dependencies
Runner environment
Analysis Works but Upload Fails
Review:
SARIF generation
GitHub Actions permissions
Repository security configuration
Upload step
Custom Queries Fail
Check:
Query paths
Query packs
Version compatibility
File permissions
CI Still Uses the Old Bundle
Check:
PATH
Runner cache
Container image
Preinstalled tools
Installation orderA Safer Migration Process
A practical migration can follow these steps.
Step 1 - Inventory
Find all CodeQL installations and workflows.
Step 2 - Identify Platforms
Document operating systems and architectures.
Step 3 - Record Versions
Capture the current CodeQL version and installation method.
Step 4 - Review Installation Logic
Find hard-coded URLs, archive names, and paths.
Step 5 - Select the Correct Package
Use the platform and architecture supported by your environment.
Step 6 - Verify Installation
Run:
codeql versionStep 7 - Run Database Creation
Use the normal build and CodeQL database process.
Step 8 - Run Analysis
Test the same queries used in production.
Step 9 - Verify SARIF
Confirm results are generated and uploaded correctly.
Step 10 - Roll Out Gradually
Move from test runners to production runners after successful validation.
Best Practices
Document the Installation
Make it clear where CodeQL comes from and how it is installed.
Pin Versions Where Appropriate
Avoid unexpected tool changes in critical security pipelines.
Detect the Platform Explicitly
Do not assume all runners use the same operating system or architecture.
Verify the Executable
Check both the version and the executable path.
Test the Complete Workflow
Validate database creation, analysis, queries, and result upload.
Keep Security Controls Independent
Do not weaken repository or CI permissions to compensate for an installation problem.
Maintain a Rollback Plan
Know how to return to the previous working configuration if the migration causes an unexpected failure.
Advantages of a Well-Planned Migration
A structured migration provides:
More predictable CI behavior
Better understanding of runner dependencies
Easier troubleshooting
Cleaner installation logic
Reduced risk of hidden old installations
Better documentation of security tooling
Trade-Offs
The migration can also require additional work.
Teams may need to:
Maintain platform-specific logic
Test multiple runner types
Update container images
Review self-hosted environments
Maintain installation documentation
Test more CI combinations
The effort is greatest for organizations with heterogeneous infrastructure.
Migration Checklist
Before moving to platform-specific CodeQL bundles, verify:
[ ] All CodeQL installations identified
[ ] GitHub-managed and direct CLI usage separated
[ ] Self-hosted runners inventoried
[ ] Operating systems documented
[ ] CPU architectures documented
[ ] CodeQL versions recorded
[ ] Download scripts reviewed
[ ] Package URLs reviewed
[ ] Integrity verification preserved
[ ] Installation paths verified
[ ] PATH configuration verified
[ ] Container images reviewed
[ ] Caches considered
[ ] Database creation tested
[ ] Custom queries tested
[ ] Query packs tested
[ ] SARIF generation tested
[ ] SARIF upload tested
[ ] CI permissions reviewed
[ ] Rollback plan preparedSummary of the Article
Moving CodeQL CLI from an all-platform bundle to platform-specific bundles requires a careful review of how CodeQL is installed and executed across your CI infrastructure.
Start by identifying every CodeQL installation, including direct CLI downloads, self-hosted runners, containers, scripts, and custom tooling. Then document the operating system, CPU architecture, CodeQL version, installation path, and package source for each environment.
During the migration, verify more than the CLI version. Test database creation, custom queries, query packs, analysis, SARIF generation, and result upload. Pay particular attention to persistent self-hosted runners and cached container images because they can cause CI to continue using an older installation.
The safest approach is to migrate one environment at a time, keep the change isolated, preserve existing security controls, and maintain a rollback path.
The key lesson is: before changing the CodeQL package, understand exactly how your CI installs, locates, and runs CodeQL today.

Join the conversation! Your thoughts help the community grow.