Skip to content

Weekly Auto Build FAP for Release and Dev Channels #9

Weekly Auto Build FAP for Release and Dev Channels

Weekly Auto Build FAP for Release and Dev Channels #9

Workflow file for this run

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
echo "ufbt build directory:"
ls -la /home/runner/.ufbt/build/ 2>/dev/null || echo "Build directory not found"
- 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 "::error::No application.fam found in $(pwd)"
exit 1
fi
echo "Listing source files (*.c, *.h)..."
ls -la *.c *.h 2>/dev/null || echo "::warning::No .c or .h files found"
echo "Checking assets directory..."
if [ -d "assets" ]; then
echo "assets directory found:"
ls -la assets/
else
echo "::warning::No assets directory found"
fi
echo "Checking icons directory..."
if [ -d "icons" ]; then
echo "icons directory found:"
ls -la icons/
if [ -f "icons/jammer_icon.png" ]; then
echo "jammer_icon.png found"
else
echo "::warning::jammer_icon.png not found in icons/"
fi
else
echo "::warning::No icons directory found"
fi
echo "Checking dist directory..."
mkdir -p dist
ls -la dist/ 2>/dev/null || echo "dist directory is empty"
- 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
echo "Directory contents:"
ls -la
echo "dist directory contents:"
ls -la dist/ 2>/dev/null || echo "dist directory is empty"
echo "ufbt build directory contents:"
ls -la /home/runner/.ufbt/build/ 2>/dev/null || echo "ufbt build directory is empty"
echo "application.fam contents:"
cat application.fam 2>/dev/null || echo "No application.fam found"
exit 1
fi
# Check ufbt build directory for FAP file
echo "Checking ufbt build directory for FAP file..."
FAP_FILE_UFBT=$(find /home/runner/.ufbt/build/ -name "*.fap" | head -1)
if [ -n "$FAP_FILE_UFBT" ]; then
echo "FAP file found in ufbt build directory: $FAP_FILE_UFBT"
mkdir -p dist
cp "$FAP_FILE_UFBT" dist/jammer_app.fap
echo "Copied FAP file to dist/jammer_app.fap"
fi
# Check dist directory for FAP file
FAP_FILE=$(find . -name "*.fap" -path "*/dist/*" | head -1)
if [ -z "$FAP_FILE" ]; then
echo "::error::No .fap file generated in dist/ for ${{ matrix.channel }} channel"
echo "dist directory contents:"
ls -la dist/ 2>/dev/null || echo "dist directory is empty"
echo "ufbt build directory contents:"
ls -la /home/runner/.ufbt/build/ 2>/dev/null || echo "ufbt build 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 in dist/ 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