-
Notifications
You must be signed in to change notification settings - Fork 24
feat(publish): gate auto-publish on releases + publish summary table + docs #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 9 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
3d932eb
feat(publish): add publish-summary table + GitHub Release upsert
felickz e5b4080
fix(publish): avoid tee+pipe fragility in Build summary table step
felickz cb01617
Merge remote-tracking branch 'origin/main' into publish-summary-table
felickz 75e4b88
feat(publish): gate auto-publish on .release.yml changes; document re…
felickz cd50d6a
fix: address Copilot review feedback on PR #160
felickz 113ed4a
Merge main (PR #159 - bump remaining packs to 0.2.3) into publish-sum…
felickz ec6dd17
feat(publish): add CodeQL standard library versions cross-check table
felickz 7b75ce1
fix: mark build-codeql-lib-versions-table.sh executable
felickz 0659595
fix(build-codeql-lib-versions-table): check src lock file, not lib
felickz 16c7cb0
feat(publish): commit/tag-anchored links + queries pack in lib-versio…
felickz 1341fae
feat: automate CodeQL CLI version bump detection and dependency refresh
felickz d518638
fix(publish): gate release-table upsert to push trigger, clarify fail…
felickz 0843ff8
fix(publish): check both src and lib lock files for CodeQL version drift
felickz faa5fe6
docs: fix publish.yml job count in CONTRIBUTING.md (five jobs, not four)
felickz bda4965
fix(publish): don't fail check_version when a pack's GHCR container d…
felickz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}\`)" ;; | ||
| *) echo "❓ unknown" ;; | ||
| esac | ||
|
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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
|
felickz marked this conversation as resolved.
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.