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
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ on:
permissions:
contents: read

concurrency:
group: release-${{ github.event.inputs.version || github.ref_name }}
cancel-in-progress: true

jobs:
# ─────────────────────────────────────────────────────────────────────────────
# Step 1: Determine the release version
Expand Down
172 changes: 104 additions & 68 deletions .github/workflows/update-codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ jobs:
#
# Compares the current CodeQL CLI version in qlt.conf.json against the latest
# release from github/codeql-cli-binaries. If a newer version is available,
# downstream jobs orchestrate a full release using the same child workflows
# as release.yml, guarded by environment approval gates.
# downstream jobs orchestrate the update and PR creation.
# ─────────────────────────────────────────────────────────────────────────────
detect-update:
name: Detect CodeQL CLI Update
Expand All @@ -38,8 +37,21 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
echo "Checking latest CodeQL CLI version..."

# Read current version from qlt.conf.json
current_version=$(jq -r .CodeQLCLI qlt.conf.json)

# Get latest release from codeql-cli-binaries
latest_tag=$(gh release list --repo github/codeql-cli-binaries --json 'tagName,isLatest' --jq '.[] | select(.isLatest == true) | .tagName')

# Validate that we found a latest release
if [ -z "${latest_tag}" ]; then
echo "❌ Error: Could not determine latest CodeQL CLI version from github/codeql-cli-binaries" >&2
echo "No release marked as 'latest' was found. This may indicate an API issue or repository change." >&2
echo "update_needed=false" >> $GITHUB_OUTPUT
exit 1
fi

latest_clean="${latest_tag#v}"

echo "Current CodeQL CLI version: ${current_version}"
Expand All @@ -63,91 +75,115 @@ jobs:
if [ "${{ steps.check-version.outputs.update_needed }}" == "true" ]; then
echo "✅ Update available: ${{ steps.check-version.outputs.current_version }} → ${{ steps.check-version.outputs.latest_version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Initiating release pipeline for \`v${{ steps.check-version.outputs.latest_version }}\`..." >> $GITHUB_STEP_SUMMARY
echo "Initiating update pipeline for \`${{ steps.check-version.outputs.version }}\`..." >> $GITHUB_STEP_SUMMARY
else
echo "ℹ️ CodeQL CLI is already up-to-date. No release needed." >> $GITHUB_STEP_SUMMARY
echo "ℹ️ CodeQL CLI is already up-to-date. No changes needed." >> $GITHUB_STEP_SUMMARY
fi

# ─────────────────────────────────────────────────────────────────────────────
# Step 2: Create release tag
# Step 2: Update version, test, and create PR
#
# Calls the same release-tag workflow used by release.yml. This ensures the
# version update, CodeQL installation, pack lock upgrade, unit tests, and tag
# creation all follow the same validated process.
# Updates all version-bearing files (qlt.conf.json, qlpack.yml files),
# installs CodeQL, upgrades pack lock files, compiles CDS files, runs unit
# tests, and creates a pull request with the changes.
#
# The release-tag environment approval gate provides human-in-the-loop review
# before any changes are committed.
# This does NOT trigger the release pipeline. Merging the PR and creating a
# release tag is a separate, human-initiated step via release.yml.
# ─────────────────────────────────────────────────────────────────────────────
ensure-tag:
name: Ensure Release Tag
create-pr:
name: Create Update Pull Request
needs: detect-update
if: needs.detect-update.outputs.update_needed == 'true'
permissions:
contents: write
uses: ./.github/workflows/release-tag.yml
with:
version: ${{ needs.detect-update.outputs.version }}

# ─────────────────────────────────────────────────────────────────────────────
# Step 3: Publish and bundle CodeQL packs
#
# Calls the same release-codeql workflow used by release.yml. Publishes packs
# to GHCR and bundles them as artifacts for the GitHub Release.
# ─────────────────────────────────────────────────────────────────────────────
publish-codeql:
name: Publish CodeQL Packs
needs: [detect-update, ensure-tag]
if: needs.detect-update.outputs.update_needed == 'true'
permissions:
contents: read
packages: write
uses: ./.github/workflows/release-codeql.yml
with:
publish_codeql_packs: true
version: ${{ needs.detect-update.outputs.version }}

# ─────────────────────────────────────────────────────────────────────────────
# Step 4: Create GitHub Release
#
# Downloads the CodeQL pack bundles and creates the GitHub Release with
# auto-generated release notes and attached pack artifacts.
# ─────────────────────────────────────────────────────────────────────────────
create-release:
name: Create GitHub Release
needs: [detect-update, ensure-tag, publish-codeql]
if: >-
always() && !failure() && !cancelled()
&& needs.detect-update.outputs.update_needed == 'true'
runs-on: ubuntu-latest

permissions:
contents: write
pull-requests: write

steps:
- name: Release - Download CodeQL pack artifacts
uses: actions/download-artifact@v7
with:
name: codeql-pack-bundles-${{ needs.detect-update.outputs.version }}
path: dist-packs
- name: Update - Checkout repository
uses: actions/checkout@v6

- name: Update - Update version in all files
run: |
LATEST="${{ needs.detect-update.outputs.latest_version }}"
echo "Updating all version-bearing files to ${LATEST}..."
./scripts/update-release-version.sh "${LATEST}"

- name: Release - Create GitHub Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
- name: Update - Install CodeQL via GitHub CLI
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
CODEQL_VERSION="${{ needs.detect-update.outputs.latest_version }}"
echo "Installing CodeQL CLI ${CODEQL_VERSION} via gh-codeql..."
gh extension install github/gh-codeql
gh codeql set-version "${CODEQL_VERSION}"
STUB_DIR="$HOME/.local/bin"
mkdir -p "${STUB_DIR}"
gh codeql install-stub "${STUB_DIR}/"
echo "${STUB_DIR}" >> "$GITHUB_PATH"
export PATH="${STUB_DIR}:${PATH}"
echo "CodeQL version: $(codeql version --format=terse)"

- name: Update - Upgrade CodeQL pack lock files
run: ./scripts/upgrade-packs.sh

- name: Update - Setup Node.js for CDS compilation
uses: actions/setup-node@v6
with:
files: |
dist-packs/*.tar.gz
generate_release_notes: true
tag_name: ${{ needs.detect-update.outputs.version }}
node-version: '20'
cache: 'npm'
cache-dependency-path: 'extractors/cds/tools/package-lock.json'

- name: Release - Summary
- name: Update - Compile CAP CDS files
run: ./extractors/cds/tools/workflow/cds-compilation-for-actions.sh

- name: Update - Run CodeQL unit tests
env:
LGTM_INDEX_XML_MODE: all
LGTM_INDEX_FILETYPES: ".json:JSON\n.cds:JSON"
shell: bash
run: |
echo "Running CodeQL unit tests to validate update..."
codeql test run \
--threads=0 \
--strict-test-discovery \
--additional-packs="${GITHUB_WORKSPACE}" \
-- javascript/

- name: Update - Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
with:
title: 'Upgrade CodeQL CLI dependency to ${{ needs.detect-update.outputs.version }}'
body: |
This PR upgrades the CodeQL CLI version to ${{ needs.detect-update.outputs.version }}.

**Changes made:**
- Updated `qlt.conf.json` (CodeQLCLI, CodeQLStandardLibrary, CodeQLCLIBundle) to `${{ needs.detect-update.outputs.latest_version }}`
- Updated all version-bearing qlpack.yml files to `${{ needs.detect-update.outputs.latest_version }}`
- Upgraded CodeQL pack lock files
- Compiled CAP CDS files
- CodeQL unit tests passed ✅

**To complete the release**, merge this PR and then trigger the release workflow
via `workflow_dispatch` on `release.yml` with version `${{ needs.detect-update.outputs.version }}`.
commit-message: 'Upgrade CodeQL CLI dependency to ${{ needs.detect-update.outputs.version }}'
delete-branch: true
branch: 'codeql/upgrade-to-${{ needs.detect-update.outputs.version }}'

- name: Update - Summary
run: |
VERSION="${{ needs.detect-update.outputs.version }}"
RELEASE_NAME="${{ needs.detect-update.outputs.latest_version }}"
echo "## Automated Release Summary" >> $GITHUB_STEP_SUMMARY
CURRENT="${{ needs.detect-update.outputs.current_version }}"
LATEST="${{ needs.detect-update.outputs.latest_version }}"
echo "## CodeQL CLI Update Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Triggered by CodeQL CLI update: ${CURRENT} → ${LATEST}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Triggered by CodeQL CLI update: ${{ needs.detect-update.outputs.current_version }} → ${RELEASE_NAME}" >> $GITHUB_STEP_SUMMARY
echo "| Property | Old Value | New Value |" >> $GITHUB_STEP_SUMMARY
echo "| -------- | --------- | --------- |" >> $GITHUB_STEP_SUMMARY
echo "| qlt.conf.json CodeQLCLI | ${CURRENT} | ${LATEST} |" >> $GITHUB_STEP_SUMMARY
echo "| qlpack.yml versions | ${CURRENT} | ${LATEST} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Step | Status |" >> $GITHUB_STEP_SUMMARY
echo "| ---- | ------ |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | ✅ ${VERSION} |" >> $GITHUB_STEP_SUMMARY
echo "| CodeQL pack publish | ✅ Published to GHCR |" >> $GITHUB_STEP_SUMMARY
echo "| GitHub Release | ✅ Created |" >> $GITHUB_STEP_SUMMARY
echo "A pull request has been created with these changes." >> $GITHUB_STEP_SUMMARY
126 changes: 126 additions & 0 deletions scripts/upgrade-packs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -euo pipefail

## upgrade-packs.sh
## Upgrade CodeQL pack dependencies for packs in the codeql-sap-js repository.
##
## This script upgrades lock files for both source and test packs, installing
## the latest compatible version of each dependency (ignoring existing lock files).
##
## Usage:
## ./scripts/upgrade-packs.sh
## ./scripts/upgrade-packs.sh --framework cap
## ./scripts/upgrade-packs.sh --framework ui5

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

FRAMEWORK=""

usage() {
cat <<EOF
Usage: $0 [OPTIONS]

Upgrade CodeQL pack dependencies for all packs in the repository.

OPTIONS:
--framework <name> Upgrade packs only for the specified framework
Valid values: cap, heuristic-models, ui5, ui5-webcomponents, xsjs
-h, --help Show this help message

By default, the script upgrades packs for all frameworks.
EOF
}

while [[ $# -gt 0 ]]; do
case $1 in
--framework)
if [[ $# -lt 2 || "${2-}" == -* ]]; then
echo "Error: --framework requires a value" >&2
usage >&2
exit 1
fi
FRAMEWORK="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Error: Unknown option $1" >&2
usage >&2
exit 1
;;
esac
done

## Validate framework if provided
VALID_FRAMEWORKS=("cap" "heuristic-models" "ui5" "ui5-webcomponents" "xsjs")
if [[ -n "${FRAMEWORK}" ]]; then
FRAMEWORK_VALID=false
for valid_fw in "${VALID_FRAMEWORKS[@]}"; do
if [[ "${FRAMEWORK}" == "${valid_fw}" ]]; then
FRAMEWORK_VALID=true
break
fi
done

if [[ "${FRAMEWORK_VALID}" == false ]]; then
echo "Error: Invalid framework '${FRAMEWORK}'" >&2
echo "Valid frameworks: ${VALID_FRAMEWORKS[*]}" >&2
exit 1
fi
fi

cd "${REPO_ROOT}"

## Upgrade a single pack given its qlpack.yml directory
upgrade_pack() {
local pack_dir="$1"
if [[ -d "${pack_dir}" ]]; then
echo "INFO: Running 'codeql pack upgrade' for '${pack_dir}'..."
codeql pack upgrade -- "${pack_dir}"
else
echo "WARNING: Directory '${pack_dir}' not found, skipping" >&2
fi
}

## Upgrade packs for a framework (all subdirectories that contain qlpack.yml)
upgrade_framework() {
local framework_path="$1"
echo "Upgrading packs for: ${framework_path}"

# Find all qlpack.yml files under this framework and upgrade their packs
find "${REPO_ROOT}/${framework_path}" -name "qlpack.yml" -type f | sort | while read -r qlpack_file; do
local pack_dir
pack_dir=$(dirname "${qlpack_file}")
# Use relative path for cleaner output
local rel_path="${pack_dir#${REPO_ROOT}/}"
upgrade_pack "${rel_path}"
done
}

if [[ -n "${FRAMEWORK}" ]]; then
case "${FRAMEWORK}" in
heuristic-models)
upgrade_framework "javascript/heuristic-models"
;;
ui5-webcomponents)
upgrade_framework "javascript/frameworks/ui5-webcomponents"
;;
*)
upgrade_framework "javascript/frameworks/${FRAMEWORK}"
;;
esac
else
echo "Upgrading packs for all frameworks..."
upgrade_framework "javascript/frameworks/cap"
upgrade_framework "javascript/frameworks/ui5"
upgrade_framework "javascript/frameworks/ui5-webcomponents"
upgrade_framework "javascript/frameworks/xsjs"
upgrade_framework "javascript/heuristic-models"
fi

echo ""
echo "✅ All CodeQL pack lock files upgraded successfully."
Loading