From 1af604df20234458951ba5b4ad8c292e2543f0eb Mon Sep 17 00:00:00 2001 From: Shady Mohamed Date: Fri, 10 Jul 2026 15:11:13 +0200 Subject: [PATCH 1/2] Update release workflow to create .md format --- .github/RELEASE_TEMPLATE.md | 40 ++++++++++ .github/workflows/build.yml | 1 + .github/workflows/code_coverage.yml | 1 + .github/workflows/release.yml | 108 ++++++++++++++++++++------ scripts/prepare_release_notes.sh | 114 ++++++++++++++++++++++++++++ 5 files changed, 240 insertions(+), 24 deletions(-) create mode 100644 scripts/prepare_release_notes.sh diff --git a/.github/RELEASE_TEMPLATE.md b/.github/RELEASE_TEMPLATE.md index 0ef19cba4..37f7b6811 100644 --- a/.github/RELEASE_TEMPLATE.md +++ b/.github/RELEASE_TEMPLATE.md @@ -16,3 +16,43 @@ The work products compiled in the safety package are created with care according For details on the features, see https://eclipse-score.github.io/score/main/features/lifecycle/index.html +--- + +## Changes to the Module + +### New Features + +$NEW_FEATURES + +### Improvements + +$IMPROVEMENTS + +### Bug Fixes + +$BUG_FIXES + +### Other Changes by Label + +$OTHER_CHANGES + +### Compatibility + +$COMPATIBILITY + +### Performed Verification + +$PERFORMED_VERIFICATION + +### Known Issues + +$KNOWN_ISSUES + +### Known Vulnerabilities + +$KNOWN_VULNERABILITIES + +### Upgrade Instructions + +$UPGRADE_INSTRUCTIONS + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b292b0c31..eb4d70c03 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,6 +13,7 @@ name: Bazel Build on: + workflow_call: pull_request: types: [opened, reopened, synchronize] merge_group: diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index 8d75225e5..bb71a7fd3 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -12,6 +12,7 @@ # ******************************************************************************* name: Code Coverage on: + workflow_call: pull_request: types: [opened, reopened, synchronize] push: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 36afe9bf0..c32c80d33 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,13 +11,42 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* # -# This workflow automates the release process for the lifecycle repository. -# It is triggered manually to build, test, and validate release artifacts. -# The workflow creates a draft release with a filled-out description for -# maintainers to approve and publish. +# GitHub Actions Workflow: Release +# +# Purpose: +# Automates the complete release process for the lifecycle module. +# Orchestrates builds on all supported platforms, collects coverage reports, +# generates release notes from RST templates, and creates a draft release. +# +# Trigger: +# Manual workflow dispatch with required release tag input (e.g., v1.0.0) +# +# Process: +# 1. Run parallel build jobs: +# - build-linux: Build and test on Linux (x86_64, arm64) +# - build-qnx: Build and test on QNX (x86_64, arm64) +# - coverage-report: Generate C++ code coverage report +# 2. After all builds pass, create-release job: +# - Generates release body from template (Markdown) +# - Extracts latest release notes from RST file +# - Converts RST markup to Markdown (links only) +# - Downloads and archives coverage report +# - Creates draft GitHub release for maintainer review +# +# Release Notes Format: +# - Documentation: .rst format (Sphinx compatible, stored in docs/release/) +# - GitHub Release: .md format (generated from .rst template) +# - Conversion: Only RST links converted; structure preserved +# +# Notes: +# - Concurrent runs prevented with concurrency.group: release +# - Draft releases allow maintainers to review before publishing +# - All build artifacts available for debugging name: Release run-name: Release ${{ inputs.tag }} + +# Trigger: Manual workflow dispatch with a required tag input parameter permissions: contents: read on: @@ -27,13 +56,20 @@ on: description: 'Release tag (e.g., v1.0.0)' required: true type: string + +# Prevent concurrent release runs to avoid conflicts and race conditions concurrency: group: release + jobs: + # Parallel build jobs (all must succeed before creating release) + build-linux: + # Build and test on Linux platforms: x86_64, aarch64 uses: ./.github/workflows/build.yml build-qnx: + # Build and test on QNX platforms: x86_64, aarch64 uses: ./.github/workflows/qnx8.yml permissions: contents: read @@ -41,62 +77,82 @@ jobs: secrets: inherit coverage-report: + # Generate C++ code coverage report; artifact downloaded by create-release job uses: ./.github/workflows/code_coverage.yml - # lint: - # uses: ./.github/workflows/lint.yml - # sanitizers: - # uses: ./.github/workflows/sanitizers_linux.yml - + # Release orchestration: runs after all builds pass create-release: needs: [build-linux, build-qnx, coverage-report] runs-on: ubuntu-latest permissions: - contents: write + contents: write # Needed to create releases env: COVERAGE_ARCHIVE: ${{ github.event.repository.name }}-${{ inputs.tag }}-cpp-coverage steps: - name: Checkout Repository + # Access source files, templates, and release notes uses: actions/checkout@v6 - - name: Get Current Date and Previous Release Tag + + - name: Get Previous Release Tag and Current Date + # Query: Previous release tag (for version comparison in release notes) + # Compute: Current date (added to release metadata) env: GH_TOKEN: ${{ github.token }} run: | PREVIOUS_TAG=$(gh release view --json tagName --jq '.tagName' 2>/dev/null || echo "N/A") echo "RELEASE_DATE=$(date --rfc-3339=date)" >> $GITHUB_ENV echo "PREVIOUS_RELEASE_TAG=$PREVIOUS_TAG" >> $GITHUB_ENV - - name: Generate Release Body + + - name: Generate Release Body from Template + # Substitute variables into .md template + # Output: release_body.md with placeholders replaced ($RELEASE_TAG, $COMMIT_SHA, etc.) env: RELEASE_TAG: ${{ inputs.tag }} COMMIT_SHA: ${{ github.sha }} ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | envsubst '$RELEASE_TAG $PREVIOUS_RELEASE_TAG $COMMIT_SHA $RELEASE_DATE $ACTION_RUN_URL' < .github/RELEASE_TEMPLATE.md > release_body.md + - name: Find Latest Release Note + # Searches docs/release/release_note_v_X_Y_Z.rst files + # Sorts versions numerically, returns the highest version found run: | - LATEST_RELEASE_NOTE=$( + LATEST=$( for f in docs/release/release_note_v_*.rst; do [ -e "$f" ] || continue ver=$(basename "$f" .rst | sed 's/release_note_v_//' | tr '_' '.') echo "$ver $f" done | sort -t '.' -k1,1n -k2,2n -k3,3n | tail -1 | awk '{print $2}' ) - echo "LATEST_RELEASE_NOTE=$LATEST_RELEASE_NOTE" >> "$GITHUB_ENV" - - name: Append Release Note to Release Body - if: env.LATEST_RELEASE_NOTE != '' - run: | - printf '\n---\n\n## Changes to the Module\n\n' >> release_body.md - sed -n '/^Changes to the Module$/,$p' "$LATEST_RELEASE_NOTE" | sed '1,2d' >> release_body.md + echo "LATEST_RELEASE_NOTE=${LATEST:--}" >> "$GITHUB_ENV" + + - name: Populate Release Note Sections + # Extracts release note sections from .rst file and converts to .md format + # Helper script: scripts/prepare_release_notes.sh + # Conversion: RST links (`text `_) → Markdown links ([text](url)) + if: env.LATEST_RELEASE_NOTE != '-' + run: ./scripts/prepare_release_notes.sh release_body.md "${{ env.LATEST_RELEASE_NOTE }}" + - name: Download Coverage Report Artifact + # Retrieves C++ coverage report generated by coverage-report job + # Artifact name format: {repo-name}_cpp_coverage_cpp uses: actions/download-artifact@v7 with: name: ${{ format('{0}_cpp_coverage{1}', github.event.repository.name, '_cpp') }} path: ${{ env.COVERAGE_ARCHIVE }} + - name: Create Coverage Report Archive + # Compresses coverage report into .tar.xz for attachment + # Reproducible flags: sorted names, fixed ownership (0:0), fixed timestamps run: | tar --sort=name --owner=0 --group=0 --numeric-owner \ -cJf "$COVERAGE_ARCHIVE.tar.xz" "$COVERAGE_ARCHIVE" + - name: Create Draft Release + # Creates GitHub release with: + # - Generated release body (template + extracted sections from .rst) + # - Coverage report archive as downloadable attachment + # - Draft status: maintainers review and publish when ready uses: softprops/action-gh-release@v2 id: draft_release with: @@ -107,10 +163,14 @@ jobs: target_commitish: ${{ github.sha }} files: | ${{ env.COVERAGE_ARCHIVE }}.tar.xz + - name: Summary + # Output release details to GitHub Actions UI for maintainer review + # Includes: tag, branch, commit hash, link to draft release run: | - echo "### Draft Release Created :rocket:" >> $GITHUB_STEP_SUMMARY - echo "Tag: **${{ inputs.tag }}**" >> $GITHUB_STEP_SUMMARY - echo "Branch: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY - echo "Commit: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY - echo "Maintainers can review at: ${{ steps.draft_release.outputs.url }}" >> $GITHUB_STEP_SUMMARY + echo "### Draft Release Created 🚀" >> $GITHUB_STEP_SUMMARY + echo "**Tag:** \`${{ inputs.tag }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "👉 [Review draft release](${{ steps.draft_release.outputs.url }})" >> $GITHUB_STEP_SUMMARY diff --git a/scripts/prepare_release_notes.sh b/scripts/prepare_release_notes.sh new file mode 100644 index 000000000..12873fdaf --- /dev/null +++ b/scripts/prepare_release_notes.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set -euo pipefail + +# Script: prepare_release_notes.sh +# Purpose: Extract release note sections from RST file and convert to Markdown format +# Usage: ./scripts/prepare_release_notes.sh +# Environment: Expects release_body.md to contain $SECTION_NAME placeholders + +if [[ $# -ne 2 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +RELEASE_BODY="$1" +RELEASE_NOTE="$2" + +if [[ ! -f "$RELEASE_BODY" ]]; then + echo "Error: Release body file not found: $RELEASE_BODY" >&2 + exit 1 +fi + +if [[ ! -f "$RELEASE_NOTE" ]]; then + echo "Warning: Release note file not found: $RELEASE_NOTE" >&2 + echo "Skipping release note extraction (sections will remain as placeholders)" + exit 0 +fi + +# --------------------- +# Helper Functions +# --------------------- + +# Extract a section between two RST headings (underline style) +# Uses sed to find the section and remove heading/underline lines +extract_section() { + local heading="$1" + local next_heading="${2:-}" + local file="$3" + + if [[ -z "$next_heading" ]]; then + # Extract from heading to next heading at same level + sed -n "/^${heading}$/,/^[=-]\{1,\}$/p" "$file" | sed '1,2d;$d' + else + # Extract from heading to specific next heading + sed -n "/^${heading}$/,/^${next_heading}$/p" "$file" | sed '1,2d;$d' + fi +} + +# Convert RST inline links to Markdown format +# RST: `link text `_ +# Markdown: [link text](https://example.com) +convert_rst_links() { + sed "s/\`\([^<]*\) <\([^>]*\)>\`_/[\1](\2)/g" +} + +# --------------------- +# Extract Sections +# --------------------- + +declare -A SECTIONS + +# Define all release note sections in order +# Key: section name (used in template) +# Value: "heading_in_rst" "next_heading_in_rst" (or "" for last section) +declare -A SECTION_MAP=( + [NEW_FEATURES]="New Features|Improvements" + [IMPROVEMENTS]="Improvements|Bug Fixes" + [BUG_FIXES]="Bug Fixes|Other Changes by Label" + [OTHER_CHANGES]="Other Changes by Label|Compatibility" + [COMPATIBILITY]="Compatibility|Performed Verification" + [PERFORMED_VERIFICATION]="Performed Verification|Known Issues" + [KNOWN_ISSUES]="Known Issues|Known Vulnerabilities" + [KNOWN_VULNERABILITIES]="Known Vulnerabilities|Upgrade Instructions" + [UPGRADE_INSTRUCTIONS]="Upgrade Instructions|" +) + +# Extract each section and convert links +for section_var in "${!SECTION_MAP[@]}"; do + IFS='|' read -r heading next_heading <<< "${SECTION_MAP[$section_var]}" + + if [[ -z "$next_heading" ]]; then + SECTIONS[$section_var]=$(extract_section "$heading" "" "$RELEASE_NOTE" | convert_rst_links) + else + SECTIONS[$section_var]=$(extract_section "$heading" "$next_heading" "$RELEASE_NOTE" | convert_rst_links) + fi +done + +# --------------------- +# Update Release Body +# --------------------- + +# Replace all section placeholders using envsubst +# Must export all variables for envsubst to find them +for section_var in "${!SECTIONS[@]}"; do + export "$section_var"="${SECTIONS[$section_var]}" +done + +# Create temporary file with all sections substituted +envsubst "$(printf '$%s ' "${!SECTIONS[@]}")" < "$RELEASE_BODY" > "${RELEASE_BODY}.tmp" +mv "${RELEASE_BODY}.tmp" "$RELEASE_BODY" + +echo "✅ Release notes prepared successfully" From 43e33acd3d4e5bb966a4993ca77ce148a7708c23 Mon Sep 17 00:00:00 2001 From: Shady Mohamed Date: Fri, 10 Jul 2026 15:23:31 +0200 Subject: [PATCH 2/2] Update description --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c32c80d33..6d93e24f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,8 @@ # # Trigger: # Manual workflow dispatch with required release tag input (e.g., v1.0.0) +# Input tag shall create the tag for the repo even if release is not published yet. +# In case Input tag already exist only the relevant github release page will be updated. # # Process: # 1. Run parallel build jobs: