Skip to content

Build AppFlowy Application #408

Build AppFlowy Application

Build AppFlowy Application #408

Workflow file for this run

name: Build AppFlowy Application
on:
workflow_dispatch:
inputs:
version:
description: "Release version (e.g., 0.9.4)"
required: true
type: string
branch:
description: "Source branch for build"
required: true
default: "main"
type: string
platforms:
description: "Platforms to build for"
required: true
type: choice
options:
- "all"
- "android"
- "ios"
- "macos"
- "windows"
- "linux"
- "mobile" # android + ios
- "desktop" # macos + windows + linux
android_build_type:
description: "Android build type"
required: false
type: choice
default: "both"
options:
- "both"
- "apk"
- "appbundle"
macos_arch:
description: "macOS architecture"
required: false
type: choice
default: "All"
options:
- "All"
- "x86_64"
- "aarch64"
- "universal"
internal_build:
description: "Internal build (1 for internal, 0 for external)"
required: false
type: choice
default: "1"
options:
- "0"
- "1"
upload_to_store:
description: "Upload to app stores (not implemented yet)"
required: false
type: boolean
default: false
release_notes:
description: "Custom release notes (optional)"
required: false
type: string
push:
tags:
- "*"
env:
FLUTTER_VERSION: "3.27.4"
RUST_TOOLCHAIN: "1.85.0"
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.parse_inputs.outputs.version }}
release_tag: ${{ steps.parse_inputs.outputs.release_tag }}
branch_name: ${{ steps.parse_inputs.outputs.branch_name }}
build_number: ${{ steps.parse_inputs.outputs.build_number }}
platforms: ${{ steps.parse_inputs.outputs.platforms }}
android_build_type: ${{ steps.parse_inputs.outputs.android_build_type }}
macos_arch: ${{ steps.parse_inputs.outputs.macos_arch }}
internal_build: ${{ steps.parse_inputs.outputs.internal_build }}
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Parse Inputs
id: parse_inputs
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
# Manual trigger
VERSION="${{ github.event.inputs.version }}"
BRANCH_NAME="${{ github.event.inputs.branch }}"
PLATFORMS="${{ github.event.inputs.platforms }}"
ANDROID_BUILD_TYPE="${{ github.event.inputs.android_build_type }}"
MACOS_ARCH="${{ github.event.inputs.macos_arch }}"
INTERNAL_BUILD="${{ github.event.inputs.internal_build }}"
else
# Tag trigger
TAG_NAME=${GITHUB_REF#refs/tags/}
VERSION=$(echo "$TAG_NAME" | cut -f1 -d"_")
BRANCH_NAME=$(echo "$TAG_NAME" | cut -f2- -d"_")
PLATFORMS="all"
ANDROID_BUILD_TYPE="both"
MACOS_ARCH="All"
INTERNAL_BUILD="1"
if [ -z "$BRANCH_NAME" ]; then
echo "Error: BRANCH_NAME is empty. The tag name should be in the format of <version>_<branch_name>"
exit 1
fi
fi
# Input validation
if [ -z "$VERSION" ]; then
echo "❌ Error: Version cannot be empty"
exit 1
fi
if [ -z "$BRANCH_NAME" ]; then
echo "❌ Error: Branch name cannot be empty"
exit 1
fi
# Validate version format (semantic versioning)
if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][a-zA-Z0-9]+)*$ ]]; then
echo "❌ Error: Version must follow semantic versioning format (e.g., 1.0.0, 1.0.0-beta.1)"
exit 1
fi
# Validate platforms
VALID_PLATFORMS=("all" "android" "ios" "macos" "windows" "linux" "mobile" "desktop")
if [[ ! " ${VALID_PLATFORMS[@]} " =~ " ${PLATFORMS} " ]]; then
echo "❌ Error: Invalid platform '$PLATFORMS'. Valid options: ${VALID_PLATFORMS[*]}"
exit 1
fi
# Generate build number from timestamp
BUILD_NUMBER=$(date +%s)
# Generate timestamped release tag (no colons allowed in tag names)
TIMESTAMP=$(date +"%y%m%d-%H%M")
RELEASE_TAG="${VERSION}-${TIMESTAMP}"
echo "βœ… Validation passed!"
echo "πŸ“¦ Version: $VERSION"
echo "🏷️ Release Tag: $RELEASE_TAG"
echo "🌿 Branch: $BRANCH_NAME"
echo "πŸš€ Platforms: $PLATFORMS"
echo "πŸ”’ Build Number: $BUILD_NUMBER"
echo "πŸ“± Android Build Type: $ANDROID_BUILD_TYPE"
echo "πŸ’» macOS Architecture: $MACOS_ARCH"
echo "πŸ—οΈ Internal Build: $INTERNAL_BUILD"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
echo "platforms=$PLATFORMS" >> $GITHUB_OUTPUT
echo "android_build_type=$ANDROID_BUILD_TYPE" >> $GITHUB_OUTPUT
echo "macos_arch=$MACOS_ARCH" >> $GITHUB_OUTPUT
echo "internal_build=$INTERNAL_BUILD" >> $GITHUB_OUTPUT
- name: Validate Branch Exists
run: |
echo "πŸ” Checking if branch '${{ steps.parse_inputs.outputs.branch_name }}' exists..."
# Use GitHub API to check if branch exists (requires token)
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${{ secrets.PRIVATE_REPO_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/AppFlowy-IO/AppFlowy-Premium/branches/${{ steps.parse_inputs.outputs.branch_name }}")
if [ "$HTTP_STATUS" = "200" ]; then
echo "βœ… Branch '${{ steps.parse_inputs.outputs.branch_name }}' exists"
elif [ "$HTTP_STATUS" = "404" ]; then
echo "❌ Error: Branch '${{ steps.parse_inputs.outputs.branch_name }}' does not exist in AppFlowy-Premium repository"
exit 1
else
echo "⚠️ Warning: Could not verify branch existence (HTTP $HTTP_STATUS). Proceeding anyway..."
echo "This will be validated during checkout."
fi
- name: Check Required Secrets
run: |
echo "πŸ” Checking required secrets availability..."
MISSING_SECRETS=()
# Check for basic secrets
if [ -z "${{ secrets.PRIVATE_REPO_TOKEN }}" ]; then
MISSING_SECRETS+=("PRIVATE_REPO_TOKEN")
fi
if [ -z "${{ secrets.ADMIN_GITHUB_TOKEN }}" ]; then
MISSING_SECRETS+=("ADMIN_GITHUB_TOKEN")
fi
# Check platform-specific secrets if needed
if [[ "${{ steps.parse_inputs.outputs.platforms }}" == *"android"* ]] || [[ "${{ steps.parse_inputs.outputs.platforms }}" == "all" ]] || [[ "${{ steps.parse_inputs.outputs.platforms }}" == "mobile" ]]; then
if [ -z "${{ secrets.ANDROID_UPLOAD_KEYSTORE_BASE64 }}" ]; then
MISSING_SECRETS+=("ANDROID_UPLOAD_KEYSTORE_BASE64")
fi
fi
if [[ "${{ steps.parse_inputs.outputs.platforms }}" == *"ios"* ]] || [[ "${{ steps.parse_inputs.outputs.platforms }}" == "all" ]] || [[ "${{ steps.parse_inputs.outputs.platforms }}" == "mobile" ]]; then
if [ -z "${{ secrets.IOS_CERTIFICATE_BASE64 }}" ]; then
MISSING_SECRETS+=("IOS_CERTIFICATE_BASE64")
fi
fi
if [[ "${{ steps.parse_inputs.outputs.platforms }}" == *"macos"* ]] || [[ "${{ steps.parse_inputs.outputs.platforms }}" == "all" ]] || [[ "${{ steps.parse_inputs.outputs.platforms }}" == "desktop" ]]; then
if [ -z "${{ secrets.MACOS_CERTIFICATE_BASE64 }}" ]; then
echo "⚠️ Warning: MACOS_CERTIFICATE_BASE64 not set. macOS builds will not be code signed."
fi
fi
if [ ${#MISSING_SECRETS[@]} -ne 0 ]; then
echo "❌ Error: Missing required secrets: ${MISSING_SECRETS[*]}"
echo "Please configure these secrets in the repository settings."
exit 1
fi
echo "βœ… All required secrets are available"
- name: Checkout source code
uses: actions/checkout@v3
with:
repository: AppFlowy-IO/AppFlowy-Premium
ref: ${{ steps.parse_inputs.outputs.branch_name }}
token: ${{ secrets.PRIVATE_REPO_TOKEN }}
fetch-depth: 0
- name: Create release notes
id: create_release_notes
run: |
if [[ "${{ github.event.inputs.release_notes }}" != "" ]]; then
RELEASE_NOTES="${{ github.event.inputs.release_notes }}"
else
RELEASE_NOTES=$(cat CHANGELOG.md | sed -e '/./{H;$!d;}' -e "x;/##\ Version\ ${{ steps.parse_inputs.outputs.version }}/"'!d;' || echo "Release ${{ steps.parse_inputs.outputs.version }}")
fi
echo "release_notes=$RELEASE_NOTES" >> $GITHUB_OUTPUT
shell: bash
- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.parse_inputs.outputs.release_tag }}
release_name: ${{ steps.parse_inputs.outputs.release_tag }}
body: ${{ steps.create_release_notes.outputs.release_notes }}
build-android-apk:
name: Build Android APK
needs: prepare
if: (contains(needs.prepare.outputs.platforms, 'all') || contains(needs.prepare.outputs.platforms, 'android') || contains(needs.prepare.outputs.platforms, 'mobile')) && (needs.prepare.outputs.android_build_type == 'apk' || needs.prepare.outputs.android_build_type == 'both')
uses: ./.github/workflows/android.yaml
with:
repo: AppFlowy-IO/AppFlowy-Premium
branch: ${{ needs.prepare.outputs.branch_name }}
build_name: ${{ needs.prepare.outputs.version }}
build_number: ${{ needs.prepare.outputs.build_number }}
build_type: apk
internal_build: ${{ needs.prepare.outputs.internal_build }}
upload_url: ${{ needs.prepare.outputs.upload_url }}
secrets: inherit
build-android-aab:
name: Build Android AAB
needs: prepare
if: (contains(needs.prepare.outputs.platforms, 'all') || contains(needs.prepare.outputs.platforms, 'android') || contains(needs.prepare.outputs.platforms, 'mobile')) && (needs.prepare.outputs.android_build_type == 'appbundle' || needs.prepare.outputs.android_build_type == 'both')
uses: ./.github/workflows/android.yaml
with:
repo: AppFlowy-IO/AppFlowy-Premium
branch: ${{ needs.prepare.outputs.branch_name }}
build_name: ${{ needs.prepare.outputs.version }}
build_number: ${{ needs.prepare.outputs.build_number }}
build_type: appbundle
internal_build: ${{ needs.prepare.outputs.internal_build }}
upload_url: ${{ needs.prepare.outputs.upload_url }}
secrets: inherit
build-ios:
name: Build iOS
needs: prepare
if: contains(needs.prepare.outputs.platforms, 'all') || contains(needs.prepare.outputs.platforms, 'ios') || contains(needs.prepare.outputs.platforms, 'mobile')
uses: ./.github/workflows/ios.yaml
with:
repo: AppFlowy-IO/AppFlowy-Premium
branch: ${{ needs.prepare.outputs.branch_name }}
build_name: ${{ needs.prepare.outputs.version }}
build_number: ${{ needs.prepare.outputs.build_number }}
internal_build: ${{ needs.prepare.outputs.internal_build }}
upload_url: ${{ needs.prepare.outputs.upload_url }}
secrets: inherit
build-macos:
name: Build macOS
needs: prepare
if: contains(needs.prepare.outputs.platforms, 'all') || contains(needs.prepare.outputs.platforms, 'macos') || contains(needs.prepare.outputs.platforms, 'desktop')
uses: ./.github/workflows/macos.yaml
with:
repo: AppFlowy-IO/AppFlowy-Premium
branch: ${{ needs.prepare.outputs.branch_name }}
build_name: ${{ needs.prepare.outputs.version }}
arch: ${{ needs.prepare.outputs.macos_arch }}
internal_build: ${{ needs.prepare.outputs.internal_build }}
upload_url: ${{ needs.prepare.outputs.upload_url }}
secrets: inherit
build-windows:
name: Build Windows
needs: prepare
if: contains(needs.prepare.outputs.platforms, 'all') || contains(needs.prepare.outputs.platforms, 'windows') || contains(needs.prepare.outputs.platforms, 'desktop')
uses: ./.github/workflows/windows.yaml
with:
repo: AppFlowy-IO/AppFlowy-Premium
branch: ${{ needs.prepare.outputs.branch_name }}
build_name: ${{ needs.prepare.outputs.version }}
internal_build: ${{ needs.prepare.outputs.internal_build }}
upload_url: ${{ needs.prepare.outputs.upload_url }}
secrets: inherit
build-linux:
name: Build Linux
needs: prepare
if: contains(needs.prepare.outputs.platforms, 'all') || contains(needs.prepare.outputs.platforms, 'linux') || contains(needs.prepare.outputs.platforms, 'desktop')
uses: ./.github/workflows/linux.yaml
with:
repo: AppFlowy-IO/AppFlowy-Premium
branch: ${{ needs.prepare.outputs.branch_name }}
build_name: ${{ needs.prepare.outputs.version }}
internal_build: ${{ needs.prepare.outputs.internal_build }}
upload_url: ${{ needs.prepare.outputs.upload_url }}
secrets: inherit
# notify-failure:
# runs-on: ubuntu-latest
# needs: [build-android-apk, build-android-aab, build-ios, build-macos, build-windows, build-linux]
# if: failure()
# steps:
# - uses: 8398a7/action-slack@v3
# with:
# status: ${{ job.status }}
# text: |
# πŸ”΄πŸ”΄πŸ”΄ Workflow ${{ github.workflow }} in repository ${{ github.repository }} failed πŸ”΄πŸ”΄πŸ”΄
# Version: ${{ needs.prepare.outputs.version }}
# Branch: ${{ needs.prepare.outputs.branch_name }}
# Platforms: ${{ needs.prepare.outputs.platforms }}
# fields: repo,message,author,eventName,ref,workflow
# env:
# SLACK_WEBHOOK_URL: ${{ secrets.RELEASE_SLACK_WEBHOOK }}
# if: always()
notify-success:
runs-on: ubuntu-latest
needs: [prepare, build-android-apk, build-android-aab, build-ios, build-macos, build-windows, build-linux]
if: success()
steps:
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
βœ… Release build completed successfully!
**Version:** ${{ needs.prepare.outputs.version }}
**Release Tag:** ${{ needs.prepare.outputs.release_tag }}
**Branch:** ${{ needs.prepare.outputs.branch_name }}
**Platforms:** ${{ needs.prepare.outputs.platforms }}
πŸ“¦ Check the release at: https://github.com/${{ github.repository }}/releases/tag/${{ needs.prepare.outputs.release_tag }}
fields: repo,message,commit,author,eventName,ref,workflow,job,took
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: always()