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 installation

These 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_URL

For 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 configuration

Determine Whether You Use the CLI Directly

A workflow such as:

- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: csharp

is 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
    ;;
esac

The 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 -m

can help identify the machine architecture on Unix-like systems.

A simplified decision process might look like:

Operating System
      +
Architecture
      |
      v
CodeQL Package

Possible combinations can include:

Linux + x64
Linux + ARM64
macOS + x64
macOS + ARM64
Windows + x64

Only select combinations supported by the CodeQL distribution you are using.

Avoid Hard-Coded Platform Assumptions

A fragile script might effectively assume:

Every runner = Linux x64

That 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
Install

This 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 directory

Do 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.gz

The 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.zip

Search for:

Download URLs
File names
Archive names
Extraction paths
PATH settings
Version variables

The 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 version

should resolve to the expected installation.

On Linux or macOS:

which codeql

On Windows PowerShell:

Get-Command codeql

Check 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/codeql

may 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 images

After 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:latest

Even 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:

  1. Build the updated image.

  2. Push it to the appropriate registry.

  3. Confirm the runner pulls the intended version.

  4. Run codeql version.

  5. Execute a complete analysis.

This prevents an old cached installation from hiding migration problems.

Check CodeQL Database Creation

Do not stop after:

codeql version

The next test should be database creation.

A simplified workflow looks like:

Source Code
    |
    v
CodeQL Database Creation
    |
    v
CodeQL Database
    |
    v
Query Analysis

For 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 Format

A 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 Results

After migration, verify:

Test Custom Queries

Some organizations use custom CodeQL queries.

For example:

queries/
|
+-- security/
|   +-- insecure-api.ql
|
+-- performance/
    +-- expensive-call.ql

If 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
SARIF

Testing 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: read

The 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 permissions

If the pipeline fails, identifying the cause becomes difficult.

A better sequence is:

Package Migration
      |
      v
Validation
      |
      v
Version Upgrade
      |
      v
Validation

Small changes are easier to test and roll back.

Common Migration Problems

The Wrong CodeQL Version Is Running

Check:

codeql version

Then verify the executable:

which codeql

or:

Get-Command codeql

The Package Does Not Run

Check:

The CLI Works but Database Creation Fails

Review:

Analysis Works but Upload Fails

Review:

Custom Queries Fail

Check:

CI Still Uses the Old Bundle

Check:

PATH
Runner cache
Container image
Preinstalled tools
Installation order

A 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 version

Step 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:

Trade-Offs

The migration can also require additional work.

Teams may need to:

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 prepared

Summary 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.