Build AppFlowy Application #408
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: 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() |