Weekly Auto Build FAP for Release and Dev Channels #7
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: Weekly Auto Build FAP for Release and Dev Channels | |
on: | |
schedule: | |
# Run every Sunday at 12:00 UTC | |
- cron: '0 12 * * 0' | |
workflow_dispatch: | |
inputs: | |
force_build: | |
description: 'Force build for release and dev channels' | |
required: false | |
default: false | |
type: boolean | |
jobs: | |
build: | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false # Prevents one failing job from canceling others | |
matrix: | |
channel: | |
- release | |
- dev | |
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 | |
ufbt --version | |
python --version | |
echo "Working directory: $(pwd)" | |
- name: Setup ufbt | |
run: | | |
echo "Setting up ufbt for ${{ matrix.channel }} channel..." | |
ufbt update --channel ${{ matrix.channel }} || { | |
echo "::error::ufbt update failed for ${{ matrix.channel }} channel" | |
exit 1 | |
} | |
echo "ufbt status:" | |
ufbt status --verbose || ufbt status | |
- name: Pre-build validation | |
run: | | |
echo "Validating build environment for ${{ matrix.channel }} channel..." | |
echo "Checking for application.fam..." | |
if [ -f "application.fam" ]; then | |
echo "application.fam found:" | |
cat application.fam | |
else | |
echo "::warning::No application.fam found in $(pwd)" | |
fi | |
echo "Listing source files (*.c, *.h)..." | |
ls -la *.c *.h 2>/dev/null || echo "No .c or .h files found" | |
echo "Checking dist directory..." | |
mkdir -p dist | |
ls -la dist/ 2>/dev/null || echo "dist directory is empty" | |
echo "SDK configuration:" | |
ufbt sdk || { | |
echo "::error::Failed to display SDK configuration" | |
exit 1 | |
} | |
- name: Get app name from source | |
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 ls *.c 2>/dev/null; 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 | |
echo "App name: $APP_NAME" | |
- name: Build FAP | |
run: | | |
echo "Building FAP for ${{ matrix.channel }} channel..." | |
# Run ufbt build and capture output to a log file | |
mkdir -p build_logs | |
BUILD_LOG="build_logs/ufbt_build_${{ matrix.channel }}.log" | |
ufbt build 2>&1 | tee "$BUILD_LOG" | |
BUILD_EXIT_CODE=${PIPESTATUS[0]} | |
if [ $BUILD_EXIT_CODE -ne 0 ]; then | |
echo "::error::ufbt build failed for ${{ matrix.channel }} channel (exit code $BUILD_EXIT_CODE)" | |
echo "Build log contents:" | |
cat "$BUILD_LOG" | |
echo "ufbt status:" | |
ufbt status --verbose || ufbt status | |
echo "Directory contents:" | |
ls -la | |
echo "dist directory contents:" | |
ls -la dist/ 2>/dev/null || echo "dist directory is empty" | |
echo "application.fam contents:" | |
cat application.fam 2>/dev/null || echo "No application.fam found" | |
exit 1 | |
fi | |
# Check if a .fap file was generated | |
FAP_FILE=$(find . -name "*.fap" -path "*/dist/*" | head -1) | |
if [ -z "$FAP_FILE" ]; then | |
echo "::error::No .fap file generated for ${{ matrix.channel }} channel" | |
echo "dist directory contents:" | |
ls -la dist/ 2>/dev/null || echo "dist directory is empty" | |
exit 1 | |
fi | |
- name: Get current timestamp | |
id: timestamp | |
run: | | |
TIMESTAMP=$(date -u +%Y%m%d_%H%M%S) | |
echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT | |
- name: Find and rename FAP | |
id: fap-info | |
run: | | |
FAP_FILE=$(find . -name "*.fap" -path "*/dist/*" | head -1) | |
if [ -z "$FAP_FILE" ]; then | |
echo "::error::No FAP file found for ${{ matrix.channel }} channel" | |
exit 1 | |
fi | |
NEW_NAME="${{ steps.app-info.outputs.clean_name }}-${{ matrix.channel }}-${{ steps.timestamp.outputs.timestamp }}.fap" | |
cp "$FAP_FILE" "$NEW_NAME" | |
echo "filename=$NEW_NAME" >> $GITHUB_OUTPUT | |
- name: Upload artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ matrix.channel }}-${{ steps.timestamp.outputs.timestamp }} | |
path: ${{ steps.fap-info.outputs.filename }} | |
create-releases: | |
needs: build | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
channel: | |
- release | |
- dev | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Get current timestamp | |
id: timestamp | |
run: | | |
TIMESTAMP=$(date -u +%Y%m%d_%H%M%S) | |
echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT | |
- name: Download artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: ${{ matrix.channel }}-${{ steps.timestamp.outputs.timestamp }} | |
path: ./release-files | |
- name: Get app info | |
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 | |
uses: softprops/action-gh-release@v1 | |
with: | |
tag_name: ${{ matrix.channel }}-${{ steps.timestamp.outputs.timestamp }} | |
name: ${{ steps.app-info.outputs.name }} for ${{ matrix.channel }} (${{ steps.timestamp.outputs.timestamp }}) | |
files: ./release-files/*.fap | |
body: | | |
## ${{ steps.app-info.outputs.name }} | |
**Built for:** ${{ matrix.channel }} channel (compatible with official, momentum, unleashed firmwares) | |
This FAP was automatically built on ${{ steps.timestamp.outputs.timestamp }}. | |
**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:** | |
- ✅ Official, Momentum, Unleashed firmwares (${{ matrix.channel }} channel, latest at build time) | |
- ❌ Other 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: [build, create-releases] | |
runs-on: ubuntu-latest | |
steps: | |
- name: Cleanup releases older than 10 weeks | |
run: | | |
# Calculate the cutoff date (70 days ago) | |
CUTOFF_DATE=$(date -u -d "70 days ago" +%s) | |
echo "Cutoff timestamp: $CUTOFF_DATE" | |
# Fetch all releases | |
RELEASES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
https://api.github.com/repos/${{ github.repository }}/releases) | |
# Filter releases older than 70 days | |
OLD_RELEASES=$(echo "$RELEASES" | jq -r --argjson cutoff "$CUTOFF_DATE" \ | |
'.[] | select((.created_at | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601) < $cutoff) | .id') | |
for release_id in $OLD_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 |