ci: updates to release-drafter and nightly actions, use git-cliff for changelog (#7091)
This commit is contained in:
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user