Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 93 additions & 22 deletions .github/actions/setup-codeql-environment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,23 @@ inputs:
description: 'Whether to install language-specific runtimes and build tools'
required: false
default: 'true'
enable-cache:
description: "Whether to restore/save caches (gh-codeql, language runtimes, package managers). MUST be set to 'false' for release-generating workflows to avoid cache-poisoning attacks where a feature-branch contributor can populate the cache that a release job later restores."
required: false
default: 'true'

# Language selection (only used if install-language-runtimes is true)
languages:
description: 'Comma-separated list of target programming languages for which dependencies should be installed'
description: |
Comma-separated list of target programming languages for which
dependencies should be installed. The default list intentionally does
NOT include 'rust' because the Rust toolchain (rustc + cargo + rust-src)
is a several-hundred-megabyte download and is only needed for
Rust-specific CodeQL extraction (e.g. macro expansion in PrintAST /
PrintCFG tests). Callers that need Rust support MUST pass an explicit
`languages:` override that includes 'rust' (for example
`languages: 'rust'` from a per-language matrix entry, or
`languages: 'java,rust'` for a multi-language job).
required: false
default: 'csharp,go,java,javascript,python,ruby'

Expand All @@ -38,6 +51,15 @@ inputs:
description: 'Ruby version to install'
required: false
default: '3.2'
rust-version:
description: |
Rust toolchain version to install. Only takes effect when the
`languages` input explicitly includes 'rust' (Rust is NOT installed by
default — see the `languages` input documentation). A specific version
(e.g. 1.80.0) is recommended so that the Rust extractor produces
deterministic macro expansions across local and CI runs.
required: false
default: '1.80.0'

outputs:
codeql-home:
Expand Down Expand Up @@ -81,7 +103,7 @@ runs:

- name: Cache `gh-codeql` extension and CodeQL packages (Unix)
id: cache-codeql-unix
if: runner.os != 'Windows'
if: runner.os != 'Windows' && inputs.enable-cache == 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
Expand All @@ -93,7 +115,7 @@ runs:

- name: Cache `gh-codeql` extension and CodeQL packages (Windows)
id: cache-codeql-windows
if: runner.os == 'Windows'
if: runner.os == 'Windows' && inputs.enable-cache == 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
Expand Down Expand Up @@ -318,18 +340,24 @@ runs:
echo "No .NET dependency files found"
fi

- name: Setup Node.js
if: inputs.install-language-runtimes == 'true'
- name: Setup Node.js (with cache)
if: inputs.install-language-runtimes == 'true' && inputs.enable-cache == 'true'
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
cache: 'npm'
cache-dependency-path: 'package-lock.json'
node-version-file: '.node-version'

- name: Setup Node.js (without cache)
if: inputs.install-language-runtimes == 'true' && inputs.enable-cache != 'true'
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version-file: '.node-version'

# Cache language runtimes to avoid repeated downloads (excluding .NET which is cached separately)
- name: Cache language runtimes
id: cache-runtimes
if: inputs.install-language-runtimes == 'true'
if: inputs.install-language-runtimes == 'true' && inputs.enable-cache == 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
Expand All @@ -343,50 +371,50 @@ runs:
language-runtimes-${{ runner.os }}-

- name: Setup Python (with cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'python') && steps.check-deps.outputs.python-deps == 'true'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',python,') && steps.check-deps.outputs.python-deps == 'true' && inputs.enable-cache == 'true'
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ inputs.python-version }}
cache: 'pip'

- name: Setup Python (without cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'python') && steps.check-deps.outputs.python-deps == 'false'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',python,') && (steps.check-deps.outputs.python-deps == 'false' || inputs.enable-cache != 'true')
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ inputs.python-version }}

- name: Setup Java (with cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'java') && steps.check-deps.outputs.java-deps == 'true'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',java,') && steps.check-deps.outputs.java-deps == 'true' && inputs.enable-cache == 'true'
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
distribution: 'temurin'
java-version: ${{ inputs.java-version }}
cache: 'maven'

- name: Setup Java (without cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'java') && steps.check-deps.outputs.java-deps == 'false'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',java,') && (steps.check-deps.outputs.java-deps == 'false' || inputs.enable-cache != 'true')
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
distribution: 'temurin'
java-version: ${{ inputs.java-version }}

- name: Setup Go (with cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'go') && steps.check-deps.outputs.go-deps == 'true'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',go,') && steps.check-deps.outputs.go-deps == 'true' && inputs.enable-cache == 'true'
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: ${{ inputs.go-version }}
cache: true

- name: Setup Go (without cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'go') && steps.check-deps.outputs.go-deps == 'false'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',go,') && (steps.check-deps.outputs.go-deps == 'false' || inputs.enable-cache != 'true')
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version: ${{ inputs.go-version }}
cache: false

# Cache .NET packages and tools
- name: Cache .NET packages
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'csharp')
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',csharp,') && inputs.enable-cache == 'true'
id: cache-dotnet-packages
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
Expand All @@ -399,55 +427,98 @@ runs:
dotnet-packages-${{ runner.os }}-

- name: Setup .NET (for C#)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'csharp')
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',csharp,')
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5
with:
dotnet-version: ${{ inputs.dotnet-version }}

- name: Setup Ruby (with cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'ruby') && steps.check-deps.outputs.ruby-deps == 'true'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',ruby,') && steps.check-deps.outputs.ruby-deps == 'true' && inputs.enable-cache == 'true'
uses: ruby/setup-ruby@4dc28cf14d77b0afa6832d9765ac422dbf0dfedd # v1
with:
ruby-version: ${{ inputs.ruby-version }}
bundler-cache: true

- name: Setup Ruby (without cache)
if: inputs.install-language-runtimes == 'true' && contains(inputs.languages, 'ruby') && steps.check-deps.outputs.ruby-deps == 'false'
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',ruby,') && (steps.check-deps.outputs.ruby-deps == 'false' || inputs.enable-cache != 'true')
uses: ruby/setup-ruby@4dc28cf14d77b0afa6832d9765ac422dbf0dfedd # v1
with:
ruby-version: ${{ inputs.ruby-version }}
bundler-cache: false

# Cache Rust toolchain components and cargo registry. The CodeQL rust
# extractor invokes cargo/rustc to resolve std-library macro expansions
# (format!, println!, vec!, ...). Without a working toolchain, the
# extractor produces partial AST nodes with no getMacroCallExpansion()
# subtrees, which breaks PrintAST/PrintCFG tests.
#
# NOTE: 'rust' is NOT part of the default `languages` value. The Rust
# toolchain is only installed when a caller passes an explicit
# `languages:` override that includes 'rust' (e.g. from a per-language
# matrix entry). See the `languages` input documentation above.
- name: Cache Rust toolchain and cargo registry
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',rust,') && inputs.enable-cache == 'true'
id: cache-rust
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
~/.rustup
key: rust-${{ runner.os }}-${{ inputs.rust-version }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-${{ inputs.rust-version }}-

- name: Setup Rust toolchain
if: inputs.install-language-runtimes == 'true' && contains(format(',{0},', inputs.languages), ',rust,')
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8
with:
toolchain: ${{ inputs.rust-version }}
components: rust-src

- name: Verify language-specific tools
if: inputs.install-language-runtimes == 'true' && inputs.languages != ''
shell: bash
env:
LANGUAGES: ${{ inputs.languages }}
run: |
echo "=== Language-specific tool verification ==="

if [[ "${{ inputs.languages }}" == *"javascript"* ]] || [[ "${{ inputs.languages }}" == *"typescript"* ]]; then
# Wrap the comma-separated list in commas so glob patterns can match
# whole tokens (',java,' vs ',javascript,') instead of substrings.
_LANGS=",${LANGUAGES},"

if [[ "${_LANGS}" == *",javascript,"* ]] || [[ "${_LANGS}" == *",typescript,"* ]]; then
echo "Node.js version: $(node --version)"
echo "npm version: $(npm --version)"
fi

if [[ "${{ inputs.languages }}" == *"python"* ]]; then
if [[ "${_LANGS}" == *",python,"* ]]; then
echo "Python version: $(python --version)"
echo "pip version: $(pip --version)"
fi

if [[ "${{ inputs.languages }}" == *"java"* ]]; then
if [[ "${_LANGS}" == *",java,"* ]]; then
echo "Java version: $(java -version 2>&1 | head -1)"
fi

if [[ "${{ inputs.languages }}" == *"go"* ]]; then
if [[ "${_LANGS}" == *",go,"* ]]; then
echo "Go version: $(go version)"
fi

if [[ "${{ inputs.languages }}" == *"csharp"* ]]; then
if [[ "${_LANGS}" == *",csharp,"* ]]; then
echo "dotnet version: $(dotnet --version)"
fi

if [[ "${{ inputs.languages }}" == *"ruby"* ]]; then
if [[ "${_LANGS}" == *",ruby,"* ]]; then
echo "Ruby version: $(ruby --version)"
fi

if [[ "${_LANGS}" == *",rust,"* ]]; then
echo "Rust version: $(rustc --version 2>/dev/null || echo 'rustc not found')"
echo "Cargo version: $(cargo --version 2>/dev/null || echo 'cargo not found')"
fi

echo "================================="
3 changes: 2 additions & 1 deletion .github/workflows/query-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ permissions:
jobs:
query-unit-tests:
name: Query Unit Tests - ${{ matrix.language }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

strategy:
fail-fast: false
Expand All @@ -63,6 +63,7 @@ jobs:
uses: ./.github/actions/setup-codeql-environment
with:
install-language-runtimes: true
languages: ${{ matrix.language }}

## Install packs used in the unit tests that we're about to run.
- name: Query Unit Tests - ${{ matrix.language }} - Install CodeQL packs used in unit tests
Expand Down
32 changes: 20 additions & 12 deletions .github/workflows/release-codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
required: false
type: boolean
version:
description: 'Release version tag (e.g., vX.Y.Z). Must start with "v".'
description: 'Release version tag (e.g., vX.Y.Z or vX.Y.Z-PRERELEASE). Must match ^vMAJOR.MINOR.PATCH(-PRERELEASE)?$ where PRERELEASE may contain alphanumerics, dots, and hyphens.'
required: true
type: string
outputs:
Expand All @@ -32,7 +32,7 @@ permissions:
jobs:
publish-codeql-packs:
name: Publish and Bundle CodeQL Packs
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

environment: release-codeql

Expand All @@ -47,25 +47,32 @@ jobs:
steps:
- name: CodeQL - Validate and parse version
id: version
env:
RAW_VERSION: ${{ inputs.version }}
run: |
VERSION="${{ inputs.version }}"
if [[ ! "${VERSION}" =~ ^v ]]; then
echo "::error::Version '${VERSION}' must start with 'v'"
if [[ ! "${RAW_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$ ]]; then
echo "::error::Version '${RAW_VERSION}' does not match ^vMAJOR.MINOR.PATCH(-PRERELEASE)?$"
exit 1
Comment thread
data-douser marked this conversation as resolved.
fi
if ! git check-ref-format "refs/tags/${RAW_VERSION}" >/dev/null 2>&1; then
echo "::error::Version '${RAW_VERSION}' is not a valid git tag ref name"
exit 1
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "release_name=${VERSION#v}" >> $GITHUB_OUTPUT
echo "version=${RAW_VERSION}" >> "$GITHUB_OUTPUT"
echo "release_name=${RAW_VERSION#v}" >> "$GITHUB_OUTPUT"

- name: CodeQL - Checkout tag
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: refs/tags/${{ steps.version.outputs.version }}
persist-credentials: false

- name: CodeQL - Setup CodeQL environment
uses: ./.github/actions/setup-codeql-environment
with:
add-to-path: true
install-language-runtimes: false
enable-cache: false

- name: CodeQL - Install CodeQL pack dependencies
run: server/scripts/install-packs.sh
Expand All @@ -84,19 +91,20 @@ jobs:
RELEASE_NAME="${{ steps.version.outputs.release_name }}"
LANGUAGES="actions cpp csharp go java javascript python ruby rust swift"

# Prerelease versions (containing a hyphen) require --allow-prerelease
PRERELEASE_FLAG=""
# Prerelease versions (containing a hyphen) require --allow-prerelease.
# Use an array to avoid word-splitting / quoting hazards.
PUBLISH_ARGS=(--threads=-1)
if [[ "${RELEASE_NAME}" == *-* ]]; then
PRERELEASE_FLAG="--allow-prerelease"
echo "Detected prerelease version — using ${PRERELEASE_FLAG}"
PUBLISH_ARGS+=(--allow-prerelease)
echo "Detected prerelease version — using --allow-prerelease"
fi

echo "Publishing CodeQL tool query packs..."
for lang in ${LANGUAGES}; do
PACK_DIR="server/ql/${lang}/tools/src"
if [ -d "${PACK_DIR}" ]; then
echo "📦 Publishing ${PACK_DIR}..."
codeql pack publish --threads=-1 ${PRERELEASE_FLAG} -- "${PACK_DIR}"
codeql pack publish "${PUBLISH_ARGS[@]}" -- "${PACK_DIR}"
echo "✅ Published ${lang} tool query pack"
else
echo "⚠️ Skipping ${lang}: ${PACK_DIR} not found"
Expand Down
Loading
Loading