Skip to content

Update CodeQL CLI Version #30

Update CodeQL CLI Version

Update CodeQL CLI Version #30

name: Update CodeQL CLI Version
# Bumps the pinned CodeQL CLI/library version (.codeqlversion) and refreshes every
# pack's codeql-pack.lock.yml against it, then opens a PR with the result.
#
# By default this does NOT publish anything and does NOT bump any pack's `version:`
# field - it only prepares the dependency-refresh half of the process documented in
# CONTRIBUTING.md's "Updating the pinned CodeQL CLI/library version" section. A human
# (or a delegated Copilot coding agent) still needs to fix any compilation/test
# breakage the new CLI/library versions introduce before merging, and the existing
# "CodeQL Update Release" workflow (update-release.yml) is still what bumps every
# pack's version and triggers the real batch publish once this PR is merged.
#
# Optionally, set `release_bump` (patch/minor/major) to fold that release bump into
# THIS SAME PR - it runs the same `patch-release-me` step update-release.yml uses,
# which bumps `.release.yml`'s repo-wide version and propagates it to every pack's
# own `version:` field, `configs/*.yml` references, and cross-pack `-libs` pins in
# one pass. Since publish.yml's auto-trigger fires on any push to `main` that changes
# `.release.yml`, merging this one combined PR is then enough to kick off the real
# batch publish immediately - no separate "CodeQL Update Release" run needed
# afterward. Leave `release_bump` empty (the default) to only refresh dependencies,
# e.g. when you expect CI breakage that needs fixing before it's safe to publish.
on:
workflow_dispatch:
inputs:
codeql_version:
description: "New CodeQL CLI version to pin, e.g. 2.22.0 (a leading 'v' is fine too)"
required: true
type: string
release_bump:
description: "Optional: patch/minor/major - also bump the repo release version and every pack's version in this same PR to trigger a real publish on merge. Leave empty to only refresh dependencies (default)."
required: false
type: string
default: ""
release_prerelease:
description: "Only used if release_bump is set: mark the resulting GitHub Release as a pre-release (default: off, a full release)"
required: true
type: boolean
default: false
jobs:
update-codeql-version:
runs-on: ubuntu-latest
permissions:
contents: read # PR creation uses a scoped GitHub App token (SECLABS_APP_ID/SECLABS_APP_KEY), not GITHUB_TOKEN
packages: read # `codeql pack upgrade` resolves codeql/* library deps from GHCR (see ci.yml)
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Validate and normalize version input
id: version
run: |
set -euo pipefail
VERSION="${{ inputs.codeql_version }}"
VERSION="${VERSION#"${VERSION%%[![:space:]]*}"}" # trim leading whitespace
VERSION="${VERSION%"${VERSION##*[![:space:]]}"}" # trim trailing whitespace
VERSION="${VERSION#v}" # tolerate an accidental leading "v"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::codeql_version must look like X.Y.Z, got: $VERSION"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Validate release_bump input
id: release_bump
run: |
set -euo pipefail
BUMP="${{ inputs.release_bump }}"
if [[ -n "$BUMP" && ! "$BUMP" =~ ^(patch|minor|major)$ ]]; then
echo "::error::release_bump must be 'patch', 'minor', 'major', or empty - got: ${{ inputs.release_bump }}" >&2
exit 1
fi
echo "bump=$BUMP" >> "$GITHUB_OUTPUT"
- name: Update .codeqlversion
run: |
echo "${{ steps.version.outputs.version }}" > .codeqlversion
echo "Pinned CodeQL CLI version bumped to ${{ steps.version.outputs.version }}"
- name: Setup CodeQL
uses: ./.github/actions/install-codeql
- name: Pin codeql/* library dependencies to the CodeQL Bundle version
env:
GH_TOKEN: ${{ github.token }}
run: |
# NOTE: this only re-pins `dependencies:` in query/library packs
# (<language>/src, <language>/lib, etc.) - it deliberately skips
# <language>/ext and <language>/ext-library-sources, whose
# `extensionTargets:` is intentionally left as an unconstrained
# `'*'` and must never be auto-rewritten (see
# pin-codeql-library-versions.sh's header comment and
# CONTRIBUTING.md's "Supported CodeQL versions" section).
.github/scripts/pin-codeql-library-versions.sh "${{ steps.version.outputs.version }}"
- name: Upgrade every pack's dependencies
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Exclusions (all four are also excluded in
# .github/scripts/pin-codeql-library-versions.sh, though ext/
# ext-library-sources are excluded there for a different reason -
# see that script's header comment):
# - ./ql/hotspots is a standalone local dev tool (see
# ql/hotspots/README.md and .github/workflows/hotspots.yml), not
# one of the per-language src/lib/ext/ext-library-sources packs
# that ci.yml and publish.yml operate on. Its `codeql/ql: '*'`
# dependency isn't a real resolvable GHCR package, so it always
# fails `codeql pack upgrade`.
# - ./codeql_home is where .github/actions/install-codeql
# downloads/extracts the CodeQL CLI; it ships its own small
# vendored qlpack.yml packs (e.g. codeql/<lang>/downgrades) that
# have nothing to do with this repo and shouldn't be touched.
# - ./codeql and */.codeql are a locally-cloned github/codeql
# checkout (used by ql/hotspots) and CodeQL's own per-pack build
# caches, respectively - neither is a repo pack either.
# - <lang>/ext and <lang>/ext-library-sources are CodeQL model/extension packs
# (`extensionTargets`, no `dependencies`) - excluded here for TWO independent
# reasons, either of which alone would be enough: (1) `codeql pack upgrade`
# (re)writes their codeql-pack.lock.yml with an empty `dependencies: {}` map,
# and a checked-in lock file in that state makes a later `codeql pack
# create`/`publish` emit a bogus `addsTo.pack '...' is not an extension target
# of '...'` warning for every data extension in the pack (a known CodeQL CLI
# bug, see https://github.com/github/codeql/issues/20211); (2) `extensionTargets`
# is intentionally left as an unconstrained `'*'` (see qlpack.yml's own comment
# and CONTRIBUTING.md) and must never be auto-rewritten by tooling - see
# GitHubSecurityLab/CodeQL-Community-Packs#206.
for dir in $(find . -name qlpack.yml -not -path "./ql/hotspots/*" -not -path "./codeql_home/*" -not -path "./codeql/*" -not -path "*/.codeql/*" -not -path "*/ext/*" -not -path "*/ext-library-sources/*" -exec dirname {} \;); do
echo "::group::codeql pack upgrade $dir"
codeql pack upgrade "$dir"
echo "::endgroup::"
done
- name: Bump release version (optional)
if: steps.release_bump.outputs.bump != ''
# TODO: Return to the upstream action after its runtime image is digest-pinned:
# https://github.com/42ByteLabs/patch-release-me/issues/169
uses: docker://ghcr.io/42bytelabs/patch-release-me:0.6.7@sha256:b9624359ce08707dfb4d22bcbf9d1a75f7f4718ccec610ddaa62280ffea98958
with:
args: --disable-banner bump -m ${{ steps.release_bump.outputs.bump }}
- name: Set release prerelease flag (optional)
if: steps.release_bump.outputs.bump != ''
run: |
set -euo pipefail
# See the matching step/comment in update-release.yml: patch-release-me's
# Config struct doesn't know about a `prerelease` key, so it would get
# silently dropped on the NEXT bump - harmless here since we always
# re-set it fresh right after this bump, before the PR is opened.
if grep -qE '^prerelease:' .release.yml; then
sed -i -E "s/^prerelease:.*/prerelease: ${{ inputs.release_prerelease }}/" .release.yml
else
sed -i -E "/^version:/a prerelease: ${{ inputs.release_prerelease }}" .release.yml
fi
- name: Determine new release version (if bumped)
id: release_version
if: steps.release_bump.outputs.bump != ''
run: |
set -euo pipefail
NEW_VERSION="$(grep -E '^version:' .release.yml | head -1 | sed -E 's/^version:[[:space:]]*"?([^"[:space:]]+)"?/\1/')"
echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
echo "Release version bumped to ${NEW_VERSION}"
- name: Summarize changed files
run: |
{
echo "## Files changed by this CodeQL CLI bump"
echo
git status --porcelain | sed 's/^/- /'
} >> "$GITHUB_STEP_SUMMARY"
- name: Get Token
id: get_workflow_token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.SECLABS_APP_ID }}
private-key: ${{ secrets.SECLABS_APP_KEY }}
- name: Compute PR labels
id: pr_labels
run: |
set -euo pipefail
{
echo "list<<EOF"
echo "version"
echo "codeql-cli-v${{ steps.version.outputs.version }}"
if [[ -n "${{ steps.release_bump.outputs.bump }}" ]]; then
echo "release-v${{ steps.release_version.outputs.version }}"
fi
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Ensure version labels exist
env:
GH_TOKEN: ${{ steps.get_workflow_token.outputs.token }}
run: |
set -euo pipefail
# `version` already exists in the repo; only the version-specific
# labels below need to be created on demand. `gh label create --force`
# is idempotent, so re-running this workflow for the same version is
# safe.
while IFS= read -r LABEL; do
if [[ -z "$LABEL" || "$LABEL" == "version" ]]; then
continue
fi
gh label create "$LABEL" --color ededed --force
done <<< "${{ steps.pr_labels.outputs.list }}"
- name: Build PR title and body
id: pr_meta
run: |
set -euo pipefail
if [[ -n "${{ steps.release_bump.outputs.bump }}" ]]; then
TITLE="chore: bump pinned CodeQL CLI to v${{ steps.version.outputs.version }} and release v${{ steps.release_version.outputs.version }}"
else
TITLE="chore: bump pinned CodeQL CLI to v${{ steps.version.outputs.version }}"
fi
echo "title=$TITLE" >> "$GITHUB_OUTPUT"
{
echo 'Automated CLI version bump, requested via the "Update CodeQL CLI Version"'
echo "workflow (\`workflow_dispatch\`, \`codeql_version: ${{ steps.version.outputs.version }}\`, \`release_bump: ${{ steps.release_bump.outputs.bump || '(none)' }}\`)."
echo
echo "This PR:"
echo '- Updates `.codeqlversion` to `'"${{ steps.version.outputs.version }}"'`.'
echo '- Pins every `codeql/<lang>-all` / `codeql/<lang>-queries` `dependencies:`'
echo ' entry across query/library `qlpack.yml` files (not `*/ext` or'
echo ' `*/ext-library-sources`, whose `extensionTargets` is intentionally left'
echo ' unconstrained and must never be auto-rewritten) to the exact version'
echo ' shipped in the official CodeQL Bundle for this CLI release (see'
echo ' `.github/scripts/pin-codeql-library-versions.sh`) - this keeps'
echo ' `codeql pack upgrade` from jumping those libraries to registry-latest instead'
echo ' of the version this CLI actually ships/tests against.'
echo '- Runs `codeql pack upgrade <dir>` for every query/library pack directory (again'
echo ' excluding `*/ext` and `*/ext-library-sources`) to refresh its'
echo ' `codeql-pack.lock.yml` against the new CLI and pinned library versions.'
echo
if [[ -n "${{ steps.release_bump.outputs.bump }}" ]]; then
if [[ "${{ inputs.release_prerelease }}" == "true" ]]; then
RELEASE_KIND="pre-release"
else
RELEASE_KIND="full release"
fi
echo "- Also bumps the repo release version (\`${{ steps.release_bump.outputs.bump }}\`, via the same"
echo ' `patch-release-me` step `update-release.yml` uses) to `'"${{ steps.release_version.outputs.version }}"'`,'
echo " propagating it to every pack's own \`version:\` field, \`configs/*.yml\`"
echo ' references, and cross-pack `-libs` pins.'
echo
echo '**Merging this PR triggers the real batch publish** - `publish.yml`'"'"'s'
echo 'auto-trigger fires on any push to `main` that changes `.release.yml`, which this'
echo "PR does. No separate \"CodeQL Update Release\" run is needed. That run's \`summary\`"
echo "job will create the matching GitHub Release as a **${RELEASE_KIND}**"
echo "(\`release_prerelease: ${{ inputs.release_prerelease }}\`)."
echo
echo 'Remaining steps (see CONTRIBUTING.md'"'"'s "Updating the pinned CodeQL CLI/library'
echo 'version" section):'
echo
echo '- [ ] Check CI on this PR - fix any compilation/test errors caused by upstream'
echo ' API changes. This is usually the hardest part; consider delegating it to a'
echo ' Copilot coding agent session pointed at this PR/branch.'
echo '- [ ] Review and merge - this alone will trigger the real batch publish.'
else
echo '**This PR does not publish anything by itself** - no pack `version:` field is'
echo "bumped here, so \`publish.yml\`'s version-diff trigger won't fire for it yet."
echo 'Remaining steps (see CONTRIBUTING.md'"'"'s "Updating the pinned CodeQL CLI/library'
echo 'version" section):'
echo
echo '- [ ] Check CI on this PR - fix any compilation/test errors caused by upstream'
echo ' API changes. This is usually the hardest part; consider delegating it to a'
echo ' Copilot coding agent session pointed at this PR/branch.'
echo '- [ ] Review and merge.'
echo '- [ ] Once merged, run the "CodeQL Update Release" workflow'
echo ' (`update-release.yml`) to bump every pack'"'"'s `version:` in lockstep via'
echo ' `.release.yml` and trigger the real batch publish.'
fi
} > "${{ runner.temp }}/pr-body.md"
- name: Create Pull Request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ steps.get_workflow_token.outputs.token }}
title: ${{ steps.pr_meta.outputs.title }}
commit-message: ${{ steps.pr_meta.outputs.title }}
body-path: "${{ runner.temp }}/pr-body.md"
branch: "chore/update-codeql-cli-${{ steps.version.outputs.version }}"
labels: ${{ steps.pr_labels.outputs.list }}
delete-branch: true