ci: updates to release-drafter and nightly actions, use git-cliff for changelog (#7091)

This commit is contained in:
Peter Feerick
2026-02-10 11:26:45 +10:00
committed by GitHub
parent 26617473d7
commit 0a999261dd
3 changed files with 152 additions and 330 deletions
+11 -81
View File
@@ -91,94 +91,21 @@ jobs:
run: |
echo "release_filename=edgetx-firmware-nightly-${GITHUB_SHA::8}.zip" >> $GITHUB_ENV
echo "build_date=$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_ENV
echo "old_nightly_sha=$(git rev-parse nightly)" >> $GITHUB_ENV
- name: Zip release file
uses: montudor/action-zip@v1
with:
args: zip -qq -j -r ${{ env.release_filename }} ./edgetx-firmware-nightly
- name: Build Changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v6
- name: Generate changelog
id: changelog
uses: orhun/git-cliff-action@v4
with:
mode: "HYBRID"
configurationJson: |
{
"categories": [
{
"title": "## 🖥️ Black & White Radio Changes",
"labels": ["scope:bw"]
},
{
"title": "## 🎨 Color Radio Changes",
"labels": ["scope:color"]
},
{
"title": "## 🎮 Firmware (All Radios Generally)",
"labels": ["scope:firmware", "scope:fw"]
},
{
"title": "## 💻 Companion Software Changes",
"labels": ["scope:cpn", "companion"]
},
{
"title": "## 🚀 Features",
"labels": ["feat", "enhancement ✨"]
},
{
"title": "## 🐛 Fixes",
"labels": ["fix", "bugfix", "bug 🪲", "bug/regression ↩️"]
},
{
"title": "## 🧹 Chores",
"labels": ["chore", "house keeping 🧹"]
},
{
"title": "## 🔧 CI/CD",
"labels": ["ci", "ci/cd 🔧"]
},
{
"title": "## 🧪 Tests",
"labels": ["scope:test", "test", "unit tests 🧪"]
},
{
"title": "## 📝 Documentation",
"labels": ["scope:docs", "docs", "documentation 📝"]
},
{
"title": "## 🔨 Refactoring",
"labels": ["refactor"]
},
{
"title": "## 🎨 Style",
"labels": ["style"]
},
{
"title": "## 📦 Other",
"labels": []
}
],
"template": "#{{CHANGELOG}}",
"pr_template": "- #{{TITLE}} (##{{NUMBER}})",
"commit_template": "- #{{TITLE}} (#{{MERGE_SHA}})",
"empty_template": "- No changes",
"label_extractor": [
{
"pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([^)]+\\))?(!)?:",
"on_property": "title",
"target": "$1"
},
{
"pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)\\(([^)]+)\\)",
"on_property": "title",
"target": "scope:$2"
}
]
}
toTag: HEAD
fromTag: nightly
config: cliff.toml
args: --verbose --strip all nightly..HEAD
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OUTPUT: CHANGELOG.md
- name: Update nightly tag
run: |
@@ -213,6 +140,9 @@ jobs:
## Changes since last nightly build
${{ steps.build_changelog.outputs.changelog }}
${{ steps.changelog.outputs.content }}
---
**Full Diff:** https://github.com/${{ github.repository }}/compare/${{ env.old_nightly_sha }}...${{ github.sha }}
files: |
${{ env.release_filename }}
+84 -249
View File
@@ -1,20 +1,6 @@
name: Create Release Draft
on:
workflow_run:
workflows:
- "Run tests and package firmware"
- "MacOSX Companion"
- "Windows Companion 64-bit"
- "Linux Companion"
types:
- completed
branches:
- 'main'
- '[0-9]+.[0-9]+'
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
@@ -26,145 +12,34 @@ permissions:
contents: write
jobs:
check-tag:
name: Check if triggered by tag
runs-on: ubuntu-latest
outputs:
is_tag: ${{ steps.check.outputs.is_tag }}
tag_name: ${{ steps.check.outputs.tag_name }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if this is a tag push
id: check
run: |
echo "=== Release Draft Trigger Check ==="
echo "Event: ${{ github.event_name }}"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "✓ Manually triggered with tag: ${{ inputs.tag }}"
echo "is_tag=true" >> $GITHUB_OUTPUT
echo "tag_name=${{ inputs.tag }}" >> $GITHUB_OUTPUT
else
# Get the ref that triggered the workflow_run
REF="${{ github.event.workflow_run.head_branch }}"
echo "Triggered by workflow_run for ref: $REF"
if git show-ref --verify --quiet "refs/tags/$REF"; then
echo "✓ This is a tag push: $REF"
echo "is_tag=true" >> $GITHUB_OUTPUT
echo "tag_name=$REF" >> $GITHUB_OUTPUT
else
echo "ℹ This is NOT a tag push (branch: $REF)"
echo "is_tag=false" >> $GITHUB_OUTPUT
fi
fi
- name: Summary
run: |
if [ "${{ steps.check.outputs.is_tag }}" = "true" ]; then
echo "### ✅ Release Draft Will Be Created" >> $GITHUB_STEP_SUMMARY
echo "Tag: \`${{ steps.check.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY
else
echo "### ℹ️ Release Draft Skipped" >> $GITHUB_STEP_SUMMARY
echo "This workflow run was triggered by a branch push, not a version tag." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Release drafts are only created when:**" >> $GITHUB_STEP_SUMMARY
echo "- A version tag is pushed (e.g., \`v2.10.0\`, \`v3.0.0-rc1\`)" >> $GITHUB_STEP_SUMMARY
echo "- The workflow is manually triggered with a tag" >> $GITHUB_STEP_SUMMARY
fi
verify-builds:
name: Verify all builds succeeded
needs: check-tag
runs-on: ubuntu-latest
if: needs.check-tag.outputs.is_tag == 'true'
# Set job timeout to 6 hours (GitHub max) to be safe
timeout-minutes: 360
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Wait for companion and firmware builds
run: |
SHA=${{ github.event.workflow_run.head_sha || github.sha }}
mapfile -t WORKFLOWS < <(echo '${{ toJson(github.event.workflow_run.workflow_name || '["Run tests and package firmware", "MacOSX Companion", "Windows Companion 64-bit", "Linux Companion"]') }}' | jq -r '.[]')
echo "Waiting for all workflows to complete for SHA: $SHA"
# 300 retries * 60 seconds = 5 hours
MAX_RETRIES=300
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
ALL_SUCCESS=true
for wf in "${WORKFLOWS[@]}"; do
if [ "$wf" == "${{ github.workflow }}" ]; then continue; fi
RESULT=$(gh run list --commit $SHA --workflow "$wf" --json conclusion,status --jq '.[0] | "\(.status):\(.conclusion)"' || echo "unknown:pending")
STATUS=$(echo $RESULT | cut -d: -f1)
CONCLUSION=$(echo $RESULT | cut -d: -f2)
# Exit immediately if any sibling failed
if [[ "$CONCLUSION" == "failure" || "$CONCLUSION" == "cancelled" ]]; then
echo "::error::Workflow $wf resulted in $CONCLUSION. Aborting."
exit 1
fi
if [ "$CONCLUSION" != "success" ]; then
# Only print status every 5 minutes to keep logs clean
if (( RETRY_COUNT % 5 == 0 )); then
echo "Still waiting for $wf (Current status: $STATUS)..."
fi
ALL_SUCCESS=false
break
fi
done
if [ "$ALL_SUCCESS" = true ]; then
echo "All required workflows finished successfully!"
exit 0
fi
RETRY_COUNT=$((RETRY_COUNT+1))
sleep 60
done
echo "::error::Timed out after 5 hours waiting for builds."
exit 1
release:
name: Create draft release
needs: [check-tag, verify-builds]
runs-on: ubuntu-latest
if: needs.check-tag.outputs.is_tag == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
ref: ${{ needs.check-tag.outputs.tag_name }}
ref: ${{ inputs.tag }}
fetch-depth: 0
- name: Clean up any previous drafts for this version
run: |
# Find and delete any existing draft releases for this tag in case of re-runs
DRAFTS=$(gh release list --exclude-pre-releases=false | grep "Draft" | grep "${{ needs.check-tag.outputs.tag_name }}" | cut -f1 || true)
for draft in $DRAFTS; do
echo "Deleting old draft: $draft"
gh release delete "$draft" --yes
done
TAG="${{ inputs.tag }}"
if gh release view "$TAG" --json isDraft --jq '.isDraft' 2>/dev/null | grep -q true; then
echo "Deleting existing draft release for tag: $TAG"
gh release delete "$TAG" --yes
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version info
id: version
continue-on-error: false
run: |
VERSION="${{ needs.check-tag.outputs.tag_name }}"
VERSION="${{ inputs.tag }}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "release_name=EdgeTX $VERSION" >> $GITHUB_OUTPUT
# Extract codename from CMakeLists.txt
if [ -f "CMakeLists.txt" ]; then
@@ -186,129 +61,90 @@ jobs:
echo "CMakeLists.txt not found"
fi
- name: Build Changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v6
with:
mode: "HYBRID"
configurationJson: |
{
"categories": [
{
"title": "## 🖥️ Black & White Radio Changes",
"labels": ["scope:bw"]
},
{
"title": "## 🎨 Color Radio Changes",
"labels": ["scope:color"]
},
{
"title": "## 🎮 Firmware (All Radios Generally)",
"labels": ["scope:firmware", "scope:fw"]
},
{
"title": "## 💻 Companion Software Changes",
"labels": ["scope:cpn", "companion"]
},
{
"title": "## 🚀 Features",
"labels": ["feat", "enhancement ✨"]
},
{
"title": "## 🐛 Fixes",
"labels": ["fix", "bugfix", "bug 🪲", "bug/regression ↩️"]
},
{
"title": "## 🧹 Chores",
"labels": ["chore", "house keeping 🧹"]
},
{
"title": "## 🔧 CI/CD",
"labels": ["ci", "ci/cd 🔧"]
},
{
"title": "## 🧪 Tests",
"labels": ["scope:test", "test", "unit tests 🧪"]
},
{
"title": "## 📝 Documentation",
"labels": ["scope:docs", "docs", "documentation 📝"]
},
{
"title": "## 🔨 Refactoring",
"labels": ["refactor"]
},
{
"title": "## 🎨 Style",
"labels": ["style"]
},
{
"title": "## 📦 Other",
"labels": []
}
],
"template": "#{{CHANGELOG}}\n\n## Thank you to the following contributors who made this release possible!\n#{{CONTRIBUTORS}}",
"pr_template": "- #{{TITLE}} (##{{NUMBER}})",
"commit_template": "- #{{TITLE}} (#{{MERGE_SHA}})",
"empty_template": "- No changes",
"label_extractor": [
{
"pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([^)]+\\))?(!)?:",
"on_property": "title",
"target": "$1"
},
{
"pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)\\(([^)]+)\\)",
"on_property": "title",
"target": "scope:$2"
}
],
"max_pull_requests": 1000,
"max_back_track_time_days": 365
}
includeOpen: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate Changelog and Contributors List
id: changelog
run: |
# Find the previous tag relative to the input tag
PREV_TAG=$(git describe --tags --abbrev=0 "${{ inputs.tag }}^" 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
echo "No previous tag found. Generating full history for ${{ inputs.tag }}"
RANGE="${{ inputs.tag }}"
CONTRIB_RANGE="${{ inputs.tag }}"
else
echo "Found previous tag: $PREV_TAG"
RANGE="$PREV_TAG..${{ inputs.tag }}"
CONTRIB_RANGE="$RANGE"
fi
# Generate the changelog using the explicit range
git-cliff $RANGE --tag "${{ inputs.tag }}" --strip all --output CHANGELOG.md
# Get unique contributors from the same commit range
CONTRIBUTORS=$(git log --format='%aN' $CONTRIB_RANGE 2>/dev/null | sort -u | sed 's/^/- /' || echo "")
# Combine
CHANGELOG_CONTENT=$(cat CHANGELOG.md)
{
echo "changelog<<EOF"
echo "$CHANGELOG_CONTENT"
if [ -n "$CONTRIBUTORS" ]; then
echo ""
echo "## Thank you to the following contributors who made this release possible!"
echo "$CONTRIBUTORS"
fi
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Download all release artifacts
run: |
TAG="${{ needs.check-tag.outputs.tag_name }}"
TAG="${{ inputs.tag }}"
COMMIT_SHA=$(git rev-parse "${TAG}^{commit}")
echo "Tag $TAG points to commit: $COMMIT_SHA"
COMMIT_SHA=$(git rev-parse $TAG)
echo "Looking for artifacts from commit: $COMMIT_SHA"
# Get the latest successful run ID for each unique workflow
WORKFLOW_RUNS=$(gh run list --commit "$COMMIT_SHA" --status success --json workflowName,databaseId,createdAt --jq 'group_by(.workflowName) | map(sort_by(.createdAt) | last) | .[].databaseId')
# Find all successful workflow runs for this commit
WORKFLOW_RUNS=$(gh run list --commit $COMMIT_SHA --json databaseId,status,conclusion --jq '.[] | select(.conclusion=="success") | .databaseId')
if [ -z "$WORKFLOW_RUNS" ]; then
echo "No successful workflow runs found for commit $COMMIT_SHA"
exit 1
fi
# Download artifacts from each successful run
mkdir -p release-assets
for run_id in $WORKFLOW_RUNS; do
echo "Downloading artifacts from run $run_id..."
gh run download $run_id --dir release-assets --pattern "edgetx-*-${TAG}" || echo "No matching artifacts in run $run_id"
echo "Processing Run ID: $run_id"
gh run download "$run_id" --dir release-assets 2>/dev/null || true
done
cd release-assets
# Clean up: Delete any folders ending in "-logs" BEFORE we start zipping
echo "Cleaning up log artifacts..."
rm -rf *-logs/
# Package remaining folders into zips
for dir in */; do
# Ensure we are actually looking at a directory
[ -d "$dir" ] || continue
dir_name="${dir%/}"
echo "Processing folder: $dir_name"
# Check for existing zip inside (e.g. if firmware was already zipped)
INNER_ZIP=$(find "$dir" -maxdepth 1 -name "*.zip" | head -n 1)
if [ -n "$INNER_ZIP" ]; then
echo " Found existing zip, pulling it out..."
mv "$INNER_ZIP" "${dir_name}.zip"
else
echo " No zip found, creating ${dir_name}.zip..."
(cd "$dir" && zip -r "../${dir_name}.zip" .)
fi
# Remove the folder after zipping/moving
rm -rf "$dir"
done
# Check if we got any artifacts
if [ ! "$(ls -A release-assets 2>/dev/null)" ]; then
echo "::error::No artifacts found matching pattern edgetx-*-${TAG}"
exit 1
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Package release assets
run: |
cd release-assets
for dir in */; do
dir_name="${dir%/}"
echo "Packaging $dir_name..."
(cd "$dir_name" && zip -r "../${dir_name}.zip" .)
done
- name: List release assets
run: |
echo "Release assets to upload:"
@@ -317,9 +153,9 @@ jobs:
- name: Create draft release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.check-tag.outputs.tag_name }}
tag_name: ${{ inputs.tag }}
draft: true
prerelease: ${{ contains(needs.check-tag.outputs.tag_name, '-rc') || contains(needs.check-tag.outputs.tag_name, '-beta') || contains(needs.check-tag.outputs.tag_name, '-alpha') }}
prerelease: ${{ contains(inputs.tag, '-rc') || contains(inputs.tag, '-beta') || contains(inputs.tag, '-alpha') }}
name: EdgeTX "${{ steps.version.outputs.codename }}" ${{ steps.version.outputs.version }}
body: |
We are pleased to offer EdgeTX "${{ steps.version.outputs.codename }}" ${{ steps.version.outputs.version }}.
@@ -364,6 +200,5 @@ jobs:
## Changes
${{ steps.build_changelog.outputs.changelog }}
files: |
release-assets/*.zip
files: release-assets/*.zip
fail_on_unmatched_files: true