Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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: 115 additions & 0 deletions .github/scripts/build-codeql-lib-versions-table.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
# Builds a markdown section documenting the pinned CodeQL CLI version and
# comparing our locked `codeql/<lang>-all` library version (from each
# language's src/codeql-pack.lock.yml - the queries pack CONTRIBUTING.md's
# "Supported CodeQL versions" table treats as the source of truth) against
# the canonical version bundled with that CLI release (from github/codeql's
# own <lang>/ql/lib/qlpack.yml at tag codeql-cli/v<.codeqlversion>).
#
# This exists because CI's "codeql pack install" step is non-resolving: it
# only installs whatever is already pinned in the checked-in lock file, it
# never re-resolves/upgrades versions. If a maintainer bumps .codeqlversion
# but forgets to run `codeql pack upgrade <lang>/src` (and lib/ext) for a
# language, that language's lock file silently stays on a stale library
# version forever, and CI stays green. This table is a machine-checkable
# tripwire for that drift - see CONTRIBUTING.md's "Updating the pinned
# CodeQL CLI/library version" section. It checks one representative
# directory (src) per language as a coarse signal, not every pack
# directory; a mismatch there is reason enough to re-run
# `codeql pack upgrade` across all of that language's directories.
#
# Usage: build-codeql-lib-versions-table.sh
# Requires: gh (authenticated), awk, base64
set -euo pipefail

REPO="${GITHUB_REPOSITORY:-GitHubSecurityLab/CodeQL-Community-Packs}"

CODEQL_VERSION=$(tr -d '[:space:]' < .codeqlversion)
if [ -z "$CODEQL_VERSION" ]; then
echo "::error::Could not read .codeqlversion" >&2
exit 1
fi

LANGUAGES=(cpp csharp go java javascript python ruby)

lang_label() {
case "$1" in
cpp) echo "C++" ;;
csharp) echo "C#" ;;
go) echo "Go" ;;
java) echo "Java" ;;
javascript) echo "JavaScript" ;;
python) echo "Python" ;;
ruby) echo "Ruby" ;;
*) echo "$1" ;;
esac
}

# Reads the version pinned to `codeql/<lang>-all` in a codeql-pack.lock.yml.
locked_version() {
local lockfile="$1" pkg="$2"
awk -v pkg=" ${pkg}:" '
{ sub(/\r$/, "") }
$0 == pkg { found=1; next }
found && /^[[:space:]]+version:/ { gsub(/\r/, ""); print $2; exit }
found && /^[^[:space:]]/ { exit }
' "$lockfile"
}

# Fetches the version github/codeql itself ships for codeql/<lang>-all at
# the tag corresponding to our pinned CLI version.
upstream_version() {
local lang="$1"
gh api "repos/github/codeql/contents/${lang}/ql/lib/qlpack.yml?ref=refs%2Ftags%2Fcodeql-cli%2Fv${CODEQL_VERSION}" \
--jq '.content' 2>/dev/null | base64 -d 2>/dev/null | \
grep -E '^version:' | head -1 | awk '{print $2}' || true
}

MISMATCH=0

echo "## CodeQL standard library versions"
echo
echo "Pinned CodeQL CLI/library version ([\`.codeqlversion\`](https://github.com/${REPO}/blob/main/.codeqlversion)): [\`v${CODEQL_VERSION}\`](https://github.com/github/codeql-cli-binaries/releases/tag/v${CODEQL_VERSION})"
echo
echo "_Comparing our locked \`codeql/<lang>-all\` version (from each language's \`src/codeql-pack.lock.yml\`) against the version [github/codeql](https://github.com/github/codeql) itself bundles with CLI \`v${CODEQL_VERSION}\` (tag [\`codeql-cli/v${CODEQL_VERSION}\`](https://github.com/github/codeql/tree/codeql-cli/v${CODEQL_VERSION})). A mismatch means \`codeql pack upgrade <lang>/src\` (and \`lib\`/\`ext\` as needed) hasn't been run since the last \`.codeqlversion\` bump - see [CONTRIBUTING.md: Updating the pinned CodeQL CLI/library version](https://github.com/${REPO}/blob/main/CONTRIBUTING.md#updating-the-pinned-codeql-clilibrary-version)._"
echo
echo "| Language | Our locked \`codeql/<lang>-all\` | Upstream CLI v${CODEQL_VERSION} bundles | Status |"
echo "| --- | --- | --- | --- |"

for lang in "${LANGUAGES[@]}"; do
lockfile="${lang}/src/codeql-pack.lock.yml"
pkg="codeql/${lang}-all"
label=$(lang_label "$lang")

if [ ! -f "$lockfile" ]; then
echo "| $label | ❓ no lock file | - | ❓ |"
MISMATCH=1
continue
fi

locked=$(locked_version "$lockfile" "$pkg")
upstream=$(upstream_version "$lang")

if [ -z "$locked" ]; then
echo "| $label | ❓ not found in lock file | - | ❓ |"
MISMATCH=1
continue
fi

if [ -z "$upstream" ]; then
echo "| $label | \`$locked\` | ❓ fetch failed | ❓ |"
MISMATCH=1
continue
fi

if [ "$locked" == "$upstream" ]; then
echo "| $label | \`$locked\` | \`$upstream\` | ✅ |"
else
echo "| $label | \`$locked\` | \`$upstream\` | ⚠️ drift |"
MISMATCH=1
fi
done

if [ "$MISMATCH" -eq 1 ]; then
echo "::warning::One or more CodeQL standard library versions are out of sync with CLI v${CODEQL_VERSION}. Run 'codeql pack upgrade <lang>/src' (and lib/ext as needed) for the affected language(s)." >&2
fi
99 changes: 99 additions & 0 deletions .github/scripts/build-publish-summary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
# Builds a markdown table summarizing the package versions produced by a
# publish.yml run, from the per-job JSON result fragments it wrote.
#
# Usage: build-publish-summary.sh <results-dir>
#
# Each fragment in <results-dir> looks like:
# {
# "language": "cpp",
# "type": "src",
# "package": "codeql-cpp-queries",
# "previous_version": "0.2.1",
# "target_version": "0.2.2",
# "status": "published" # published | up-to-date | failed
# }
set -euo pipefail

RESULTS_DIR="${1:?usage: build-publish-summary.sh <results-dir>}"
REPO="${GITHUB_REPOSITORY:-GitHubSecurityLab/CodeQL-Community-Packs}"
RUN_URL="${GITHUB_SERVER_URL:-https://github.com}/${REPO}/actions/runs/${GITHUB_RUN_ID:-}"

shopt -s nullglob
FRAGMENTS=("$RESULTS_DIR"/*.json)
if [ "${#FRAGMENTS[@]}" -eq 0 ]; then
MERGED='[]'
else
MERGED=$(jq -s '.' "${FRAGMENTS[@]}")
fi

LANGUAGES=(cpp csharp go java javascript python ruby)
TYPES=(src lib ext ext-library-sources)
EXT_LANGUAGES=(csharp java)

lang_label() {
case "$1" in
cpp) echo "C++" ;;
csharp) echo "C#" ;;
go) echo "Go" ;;
java) echo "Java" ;;
javascript) echo "JavaScript" ;;
python) echo "Python" ;;
ruby) echo "Ruby" ;;
*) echo "$1" ;;
esac
}

is_ext_language() {
local lang="$1"
for l in "${EXT_LANGUAGES[@]}"; do
[ "$l" == "$lang" ] && return 0
done
return 1
}

cell() {
local lang="$1" type="$2"
local entry
entry=$(echo "$MERGED" | jq -c --arg l "$lang" --arg t "$type" \
'[.[] | select(.language == $l and .type == $t)] | first // empty')

if [ -z "$entry" ] || [ "$entry" == "null" ]; then
# This combo is expected to publish (unlike the "n/a" combos filtered out
# by the caller) but no result fragment was found for it.
echo "❓ no result"
return
fi

local package version status previous
package=$(echo "$entry" | jq -r '.package')
version=$(echo "$entry" | jq -r '.target_version')
status=$(echo "$entry" | jq -r '.status')
previous=$(echo "$entry" | jq -r '.previous_version')
local url="https://github.com/${REPO}/pkgs/container/${package}?tag=${version}"

case "$status" in
published) echo "[${version}](${url}) 🆕" ;;
up-to-date) echo "[${version}](${url})" ;;
failed) echo "⚠️ publish failed (still \`${previous}\`)" ;;
Comment thread
felickz marked this conversation as resolved.
Outdated
*) echo "❓ unknown" ;;
esac
Comment thread
felickz marked this conversation as resolved.
}

echo "## Publish summary"
echo
echo "_Generated by [publish.yml run #${GITHUB_RUN_NUMBER:-}](${RUN_URL}) — 🆕 marks a package republished by this run._"
echo
echo "| Language | Queries (\`src\`) | Library (\`lib\`) | Extensions (\`ext\`) | Library sources (\`ext-library-sources\`) |"
echo "| --- | --- | --- | --- | --- |"
for lang in "${LANGUAGES[@]}"; do
row="| $(lang_label "$lang") |"
for type in "${TYPES[@]}"; do
if { [ "$type" == "ext" ] || [ "$type" == "ext-library-sources" ]; } && ! is_ext_language "$lang"; then
row="$row n/a |"
continue
fi
row="$row $(cell "$lang" "$type") |"
done
echo "$row"
done
74 changes: 74 additions & 0 deletions .github/scripts/upsert-release-table.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Upserts a generated markdown block (e.g. the publish-summary table from
# build-publish-summary.sh, or the CodeQL library-versions table from
# build-codeql-lib-versions-table.sh) into the GitHub Release matching
# .release.yml's current version, creating that release as a pre-release if
# it doesn't exist yet.
#
# Usage: upsert-release-table.sh <table-markdown-file> [block-name]
#
# block-name defaults to "publish-summary" for backwards compatibility. Each
# distinct block-name gets its own marker-comment pair, so multiple blocks
# (e.g. "publish-summary" and "codeql-lib-versions") can be upserted
# independently into the same release body without clobbering each other.
# Re-runs only replace the matching block and leave the rest of the release
# body (e.g. "What's Changed") alone.
set -euo pipefail

TABLE_FILE="${1:?usage: upsert-release-table.sh <table-markdown-file> [block-name]}"
BLOCK_NAME="${2:-publish-summary}"
START_MARKER="<!-- ${BLOCK_NAME}:start -->"
END_MARKER="<!-- ${BLOCK_NAME}:end -->"

VERSION=$(grep -E '^version:' .release.yml | head -1 | sed -E 's/^version:[[:space:]]*"?([^"[:space:]]*)"?/\1/')
if [ -z "$VERSION" ]; then
echo "::error::Could not read version from .release.yml" >&2
exit 1
fi
TAG="v${VERSION}"

BLOCK_FILE=$(mktemp)
{
echo "$START_MARKER"
cat "$TABLE_FILE"
echo "$END_MARKER"
} > "$BLOCK_FILE"

if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG exists; refreshing its $BLOCK_NAME block."
CURRENT_BODY=$(mktemp)
gh release view "$TAG" --json body -q .body > "$CURRENT_BODY"

NEW_BODY=$(mktemp)
if grep -qF "$START_MARKER" "$CURRENT_BODY" && grep -qF "$END_MARKER" "$CURRENT_BODY"; then
awk -v start="$START_MARKER" -v end="$END_MARKER" -v blockfile="$BLOCK_FILE" '
$0 == start {
while ((getline line < blockfile) > 0) print line
skip = 1
next
}
$0 == end {
skip = 0
next
}
skip { next }
{ print }
' "$CURRENT_BODY" > "$NEW_BODY"
else
# Older release predating this automation, or a corrupted/partial marker
# pair: append the block once rather than risk truncating the body.
cat "$CURRENT_BODY" > "$NEW_BODY"
echo "" >> "$NEW_BODY"
cat "$BLOCK_FILE" >> "$NEW_BODY"
fi

gh release edit "$TAG" --notes-file "$NEW_BODY"
else
echo "Release $TAG does not exist yet; creating it as a pre-release."
gh release create "$TAG" \
--title "$TAG" \
--notes-file "$BLOCK_FILE" \
--generate-notes \
--prerelease \
--target "${GITHUB_SHA:-main}"
fi
Comment thread
felickz marked this conversation as resolved.
Loading
Loading