Weekly Auto Build FAP for Release and Dev Channels #2
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
name: Auto Build FAP for All Firmwares and Channels | |
on: | |
schedule: | |
# Check for new firmware releases daily at 12:00 UTC | |
- cron: '0 12 * * *' | |
workflow_dispatch: | |
inputs: | |
force_build: | |
description: 'Force build even if no new firmware detected' | |
required: false | |
default: false | |
type: boolean | |
jobs: | |
check-firmware-updates: | |
runs-on: ubuntu-latest | |
outputs: | |
official-release-latest: ${{ steps.check-official-release.outputs.latest }} | |
official-dev-latest: ${{ steps.check-official-dev.outputs.latest }} | |
momentum-release-latest: ${{ steps.check-momentum-release.outputs.latest }} | |
momentum-dev-latest: ${{ steps.check-momentum-dev.outputs.latest }} | |
unleashed-release-latest: ${{ steps.check-unleashed-release.outputs.latest }} | |
unleashed-dev-latest: ${{ steps.check-unleashed-dev.outputs.latest }} | |
official-release-changed: ${{ steps.check-official-release.outputs.changed }} | |
official-dev-changed: ${{ steps.check-official-dev.outputs.changed }} | |
momentum-release-changed: ${{ steps.check-momentum-release.outputs.changed }} | |
momentum-dev-changed: ${{ steps.check-momentum-dev.outputs.changed }} | |
unleashed-release-changed: ${{ steps.check-unleashed-release.outputs.changed }} | |
unleashed-dev-changed: ${{ steps.check-unleashed-dev.outputs.changed }} | |
should-build: ${{ steps.determine-build.outputs.should-build }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.11' | |
- name: Install ufbt | |
run: | | |
python -m pip install --upgrade pip | |
pip install ufbt | |
- name: Check Official Release Channel | |
id: check-official-release | |
run: | | |
ufbt update --channel=release | |
LATEST=$(ufbt status | grep -E "Firmware origin:|Target:" | head -1 | sed 's/.*: *//' | cut -d' ' -f1 || echo "unknown") | |
echo "latest=$LATEST" >> $GITHUB_OUTPUT | |
if [[ "$LATEST" == "unknown" ]] || [[ -z "$LATEST" ]]; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
elif curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '.[].tag_name' | grep -q "official-release-$LATEST"; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
else | |
echo "changed=true" >> $GITHUB_OUTPUT | |
fi | |
- name: Check Official Dev Channel | |
id: check-official-dev | |
run: | | |
ufbt update --channel=dev | |
LATEST=$(ufbt status | grep -E "Firmware origin:|Target:" | head -1 | sed 's/.*: *//' | cut -d' ' -f1 || echo "unknown") | |
echo "latest=$LATEST" >> $GITHUB_OUTPUT | |
if [[ "$LATEST" == "unknown" ]] || [[ -z "$LATEST" ]]; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
elif curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '.[].tag_name' | grep -q "official-dev-$LATEST"; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
else | |
echo "changed=true" >> $GITHUB_OUTPUT | |
fi | |
- name: Check Momentum Release Channel | |
id: check-momentum-release | |
run: | | |
LATEST=$(curl -s https://api.github.com/repos/Next-Flip/Momentum-Firmware/releases | jq -r '[.[] | select(.prerelease == false)][0].tag_name // "unknown"') | |
echo "latest=$LATEST" >> $GITHUB_OUTPUT | |
if [[ "$LATEST" == "unknown" ]] || [[ "$LATEST" == "null" ]] || [[ -z "$LATEST" ]]; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
elif curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '.[].tag_name' | grep -q "momentum-release-$LATEST"; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
else | |
echo "changed=true" >> $GITHUB_OUTPUT | |
fi | |
- name: Check Momentum Dev Channel | |
id: check-momentum-dev | |
run: | | |
LATEST=$(curl -s https://api.github.com/repos/Next-Flip/Momentum-Firmware/releases | jq -r '[.[] | select(.prerelease == true)][0].tag_name // "unknown"') | |
echo "latest=$LATEST" >> $GITHUB_OUTPUT | |
if [[ "$LATEST" == "unknown" ]] || [[ "$LATEST" == "null" ]] || [[ -z "$LATEST" ]]; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
elif curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '.[].tag_name' | grep -q "momentum-dev-$LATEST"; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
else | |
echo "changed=true" >> $GITHUB_OUTPUT | |
fi | |
- name: Check Unleashed Release Channel | |
id: check-unleashed-release | |
run: | | |
LATEST=$(curl -s https://api.github.com/repos/DarkFlippers/unleashed-firmware/releases | jq -r '[.[] | select(.prerelease == false)][0].tag_name // "unknown"') | |
echo "latest=$LATEST" >> $GITHUB_OUTPUT | |
if [[ "$LATEST" == "unknown" ]] || [[ "$LATEST" == "null" ]] || [[ -z "$LATEST" ]]; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
elif curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '.[].tag_name' | grep -q "unleashed-release-$LATEST"; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
else | |
echo "changed=true" >> $GITHUB_OUTPUT | |
fi | |
- name: Check Unleashed Dev Channel | |
id: check-unleashed-dev | |
run: | | |
LATEST=$(curl -s https://api.github.com/repos/DarkFlippers/unleashed-firmware/releases | jq -r '[.[] | select(.prerelease == true)][0].tag_name // "unknown"') | |
echo "latest=$LATEST" >> $GITHUB_OUTPUT | |
if [[ "$LATEST" == "unknown" ]] || [[ "$LATEST" == "null" ]] || [[ -z "$LATEST" ]]; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
elif curl -s https://api.github.com/repos/${{ github.repository }}/releases | jq -r '.[].tag_name' | grep -q "unleashed-dev-$LATEST"; then | |
echo "changed=false" >> $GITHUB_OUTPUT | |
else | |
echo "changed=true" >> $GITHUB_OUTPUT | |
fi | |
- name: Determine if build is needed | |
id: determine-build | |
run: | | |
if [[ "${{ github.event.inputs.force_build }}" == "true" ]] || \ | |
[[ "${{ steps.check-official-release.outputs.changed }}" == "true" ]] || \ | |
[[ "${{ steps.check-official-dev.outputs.changed }}" == "true" ]] || \ | |
[[ "${{ steps.check-momentum-release.outputs.changed }}" == "true" ]] || \ | |
[[ "${{ steps.check-momentum-dev.outputs.changed }}" == "true" ]] || \ | |
[[ "${{ steps.check-unleashed-release.outputs.changed }}" == "true" ]] || \ | |
[[ "${{ steps.check-unleashed-dev.outputs.changed }}" == "true" ]]; then | |
echo "should-build=true" >> $GITHUB_OUTPUT | |
else | |
echo "should-build=false" >> $GITHUB_OUTPUT | |
fi | |
build: | |
needs: check-firmware-updates | |
if: needs.check-firmware-updates.outputs.should-build == 'true' | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
include: | |
# Official Firmware | |
- firmware: official | |
channel: release | |
ufbt_channel: release | |
version: ${{ needs.check-firmware-updates.outputs.official-release-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.official-release-changed }} | |
- firmware: official | |
channel: dev | |
ufbt_channel: dev | |
version: ${{ needs.check-firmware-updates.outputs.official-dev-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.official-dev-changed }} | |
# Momentum Firmware | |
- firmware: momentum | |
channel: release | |
ufbt_channel: release | |
version: ${{ needs.check-firmware-updates.outputs.momentum-release-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.momentum-release-changed }} | |
- firmware: momentum | |
channel: dev | |
ufbt_channel: dev | |
version: ${{ needs.check-firmware-updates.outputs.momentum-dev-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.momentum-dev-changed }} | |
# Unleashed Firmware | |
- firmware: unleashed | |
channel: release | |
ufbt_channel: release | |
version: ${{ needs.check-firmware-updates.outputs.unleashed-release-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.unleashed-release-changed }} | |
- firmware: unleashed | |
channel: dev | |
ufbt_channel: dev | |
version: ${{ needs.check-firmware-updates.outputs.unleashed-dev-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.unleashed-dev-changed }} | |
steps: | |
- name: Check if build is needed | |
id: check-build | |
run: | | |
if [[ "${{ matrix.changed }}" == "false" ]] && [[ "${{ github.event.inputs.force_build }}" != "true" ]]; then | |
echo "skip=true" >> $GITHUB_OUTPUT | |
elif [[ "${{ matrix.version }}" == "null" ]] || [[ "${{ matrix.version }}" == "unknown" ]] || [[ -z "${{ matrix.version }}" ]]; then | |
echo "skip=true" >> $GITHUB_OUTPUT | |
else | |
echo "skip=false" >> $GITHUB_OUTPUT | |
fi | |
- name: Skip if no changes or invalid version | |
if: steps.check-build.outputs.skip == 'true' | |
run: | | |
echo "Skipping ${{ matrix.firmware }} ${{ matrix.channel }} - no new version or invalid version" | |
exit 0 | |
- name: Checkout code | |
if: steps.check-build.outputs.skip != 'true' | |
uses: actions/checkout@v4 | |
- name: Set up Python | |
if: steps.check-build.outputs.skip != 'true' | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.11' | |
- name: Install ufbt | |
if: steps.check-build.outputs.skip != 'true' | |
run: | | |
python -m pip install --upgrade pip | |
pip install ufbt | |
- name: Setup ufbt for Official firmware | |
if: matrix.firmware == 'official' && steps.check-build.outputs.skip != 'true' | |
run: | | |
ufbt update --channel=${{ matrix.ufbt_channel }} | |
- name: Setup ufbt for Momentum firmware | |
if: matrix.firmware == 'momentum' && steps.check-build.outputs.skip != 'true' | |
run: | | |
# Use Momentum's custom index | |
ufbt update --channel=${{ matrix.ufbt_channel }} --index-url=https://up.momentum-fw.dev/directory.json | |
- name: Setup ufbt for Unleashed firmware | |
if: matrix.firmware == 'unleashed' && steps.check-build.outputs.skip != 'true' | |
run: | | |
# Use Unleashed's custom index | |
ufbt update --channel=${{ matrix.ufbt_channel }} --index-url=https://up.unleashedflip.com/directory.json | |
- name: Get app name from source | |
if: steps.check-build.outputs.skip != 'true' | |
id: app-info | |
run: | | |
# Try to extract app name from common patterns | |
if [ -f "application.fam" ]; then | |
APP_NAME=$(grep -E "name\s*=" application.fam | sed 's/.*name\s*=\s*"\([^"]*\)".*/\1/' | head -1) | |
elif [ -f "*.c" ]; then | |
# Look for app name in C files | |
APP_NAME=$(grep -r "\.name.*=" *.c | sed 's/.*\.name.*=.*"\([^"]*\)".*/\1/' | head -1) | |
fi | |
# Fallback to directory name | |
if [ -z "$APP_NAME" ]; then | |
APP_NAME=$(basename $(pwd)) | |
fi | |
# Clean up name for filename | |
APP_NAME_CLEAN=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/_/g') | |
echo "name=$APP_NAME" >> $GITHUB_OUTPUT | |
echo "clean_name=$APP_NAME_CLEAN" >> $GITHUB_OUTPUT | |
- name: Build FAP | |
if: steps.check-build.outputs.skip != 'true' | |
run: | | |
ufbt build | |
- name: Find and rename FAP | |
if: steps.check-build.outputs.skip != 'true' | |
id: fap-info | |
run: | | |
FAP_FILE=$(find . -name "*.fap" -path "*/dist/*" | head -1) | |
if [ -z "$FAP_FILE" ]; then | |
echo "No FAP file found!" | |
exit 1 | |
fi | |
NEW_NAME="${{ steps.app-info.outputs.clean_name }}-${{ matrix.firmware }}-${{ matrix.channel }}-${{ matrix.version }}.fap" | |
cp "$FAP_FILE" "$NEW_NAME" | |
echo "filename=$NEW_NAME" >> $GITHUB_OUTPUT | |
- name: Upload artifact | |
if: steps.check-build.outputs.skip != 'true' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ matrix.firmware }}-${{ matrix.channel }}-${{ matrix.version }} | |
path: ${{ steps.fap-info.outputs.filename }} | |
create-releases: | |
needs: [check-firmware-updates, build] | |
if: needs.check-firmware-updates.outputs.should-build == 'true' | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
include: | |
# Official Firmware | |
- firmware: official | |
channel: release | |
version: ${{ needs.check-firmware-updates.outputs.official-release-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.official-release-changed }} | |
- firmware: official | |
channel: dev | |
version: ${{ needs.check-firmware-updates.outputs.official-dev-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.official-dev-changed }} | |
# Momentum Firmware | |
- firmware: momentum | |
channel: release | |
version: ${{ needs.check-firmware-updates.outputs.momentum-release-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.momentum-release-changed }} | |
- firmware: momentum | |
channel: dev | |
version: ${{ needs.check-firmware-updates.outputs.momentum-dev-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.momentum-dev-changed }} | |
# Unleashed Firmware | |
- firmware: unleashed | |
channel: release | |
version: ${{ needs.check-firmware-updates.outputs.unleashed-release-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.unleashed-release-changed }} | |
- firmware: unleashed | |
channel: dev | |
version: ${{ needs.check-firmware-updates.outputs.unleashed-dev-latest }} | |
changed: ${{ needs.check-firmware-updates.outputs.unleashed-dev-changed }} | |
steps: | |
- name: Check if release is needed | |
id: check-release | |
run: | | |
if [[ "${{ matrix.changed }}" == "false" ]] && [[ "${{ github.event.inputs.force_build }}" != "true" ]]; then | |
echo "skip=true" >> $GITHUB_OUTPUT | |
elif [[ "${{ matrix.version }}" == "null" ]] || [[ "${{ matrix.version }}" == "unknown" ]] || [[ -z "${{ matrix.version }}" ]]; then | |
echo "skip=true" >> $GITHUB_OUTPUT | |
else | |
echo "skip=false" >> $GITHUB_OUTPUT | |
fi | |
- name: Skip if no changes or invalid version | |
if: steps.check-release.outputs.skip == 'true' | |
run: | | |
echo "Skipping release for ${{ matrix.firmware }} ${{ matrix.channel }} - no new version or invalid version" | |
exit 0 | |
- name: Checkout code | |
if: steps.check-release.outputs.skip != 'true' | |
uses: actions/checkout@v4 | |
- name: Download artifact | |
if: steps.check-release.outputs.skip != 'true' | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ matrix.firmware }}-${{ matrix.channel }}-${{ matrix.version }} | |
path: ./release-files | |
- name: Get app info | |
if: steps.check-release.outputs.skip != 'true' | |
id: app-info | |
run: | | |
if [ -f "application.fam" ]; then | |
APP_NAME=$(grep -E "name\s*=" application.fam | sed 's/.*name\s*=\s*"\([^"]*\)".*/\1/' | head -1) | |
else | |
APP_NAME=$(basename $(pwd)) | |
fi | |
echo "name=$APP_NAME" >> $GITHUB_OUTPUT | |
- name: Create Release | |
if: steps.check-release.outputs.skip != 'true' | |
uses: softprops/action-gh-release@v1 | |
with: | |
tag_name: ${{ matrix.firmware }}-${{ matrix.channel }}-${{ matrix.version }} | |
name: ${{ steps.app-info.outputs.name }} for ${{ matrix.firmware }} ${{ matrix.channel }} ${{ matrix.version }} | |
files: ./release-files/*.fap | |
body: | | |
## ${{ steps.app-info.outputs.name }} | |
**Built for:** ${{ matrix.firmware }} firmware ${{ matrix.channel }} channel ${{ matrix.version }} | |
This FAP was automatically built when ${{ matrix.firmware }} firmware ${{ matrix.channel }} ${{ matrix.version }} was released. | |
**Installation:** | |
1. Download the .fap file | |
2. Copy to your Flipper Zero's `apps` folder | |
3. The app will appear in your applications menu | |
**Firmware Compatibility:** | |
- ✅ ${{ matrix.firmware }} ${{ matrix.channel }} ${{ matrix.version }} | |
- ❌ Other firmware versions/channels (use the appropriate release) | |
**Channel Info:** | |
- **Release**: Stable, tested firmware | |
- **Dev**: Latest features, may be unstable | |
draft: false | |
prerelease: ${{ matrix.channel == 'dev' }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
cleanup-old-releases: | |
needs: [check-firmware-updates, create-releases] | |
if: needs.check-firmware-updates.outputs.should-build == 'true' | |
runs-on: ubuntu-latest | |
steps: | |
- name: Cleanup old releases | |
run: | | |
# Keep only the latest 2 releases per firmware/channel combo to avoid clutter | |
for firmware in official momentum unleashed; do | |
for channel in release dev; do | |
RELEASES=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases | \ | |
jq -r --arg fw "$firmware" --arg ch "$channel" \ | |
'.[] | select(.tag_name | startswith($fw + "-" + $ch)) | .id' | tail -n +3) | |
for release_id in $RELEASES; do | |
echo "Deleting old release: $release_id" | |
curl -X DELETE -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
"https://api.github.com/repos/${{ github.repository }}/releases/$release_id" | |
done | |
done | |
done |