Build main/0.11.1 - 1 #708
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 | |
| run-name: Build ${{ github.event.inputs.branch || 'main' }}/${{ github.event.inputs.version || '0.11.1' }} - ${{ github.event.inputs.internal_build || '1' }} | |
| on: | |
| schedule: | |
| # Daily builds at 8 AM SGT (00:00 UTC) and 4 PM SGT (08:00 UTC) | |
| - cron: '0 0 * * *' # 8 AM SGT | |
| - cron: '0 8 * * *' # 4 PM SGT | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (e.g., 0.10.7)" | |
| required: true | |
| type: string | |
| default: "0.11.2" | |
| 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: "apk" | |
| options: | |
| - "both" | |
| - "apk" | |
| - "appbundle" | |
| macos_arch: | |
| description: "macOS architecture" | |
| required: false | |
| type: choice | |
| default: "All" | |
| options: | |
| - "All" | |
| 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: | |
| skip_build: ${{ steps.parse_inputs.outputs.skip_build }} | |
| 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 }}" | |
| elif [[ "${{ github.event_name }}" == "schedule" ]]; then | |
| # Scheduled trigger - build all platforms with default settings | |
| VERSION="0.11.2" | |
| BRANCH_NAME="main" | |
| PLATFORMS="all" | |
| ANDROID_BUILD_TYPE="both" | |
| MACOS_ARCH="All" | |
| INTERNAL_BUILD="1" | |
| CURRENT_TIME_SGT=$(TZ='Asia/Singapore' date +"%Y-%m-%d %H:%M:%S %Z") | |
| echo "📅 Scheduled build triggered at $CURRENT_TIME_SGT" | |
| # Check if there are new commits since last scheduled build | |
| # Scheduled builds run at 8 AM and 4 PM SGT (00:00 and 08:00 UTC) | |
| CURRENT_HOUR=$(TZ='Asia/Singapore' date +"%H") | |
| # Get last commit time from source repository | |
| LAST_COMMIT_TIME=$(curl -s \ | |
| -H "Authorization: token ${{ secrets.PRIVATE_REPO_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/AppFlowy-IO/AppFlowy-Premium/commits/$BRANCH_NAME" \ | |
| | jq -r '.commit.committer.date') | |
| if [ -z "$LAST_COMMIT_TIME" ] || [ "$LAST_COMMIT_TIME" == "null" ]; then | |
| echo "⚠️ Warning: Could not fetch last commit time, proceeding with build..." | |
| else | |
| LAST_COMMIT_TIMESTAMP=$(date -u -d "$LAST_COMMIT_TIME" +%s) | |
| CURRENT_TIMESTAMP=$(date +%s) | |
| # Calculate time difference in hours | |
| TIME_DIFF_HOURS=$(( ($CURRENT_TIMESTAMP - $LAST_COMMIT_TIMESTAMP) / 3600 )) | |
| echo "📝 Last commit time: $LAST_COMMIT_TIME" | |
| echo "⏱️ Time since last commit: ${TIME_DIFF_HOURS} hours" | |
| # If last commit is older than 12 hours, skip build | |
| # This ensures we catch commits made after the previous scheduled build | |
| # (builds run every 8 hours, so 12-hour window provides overlap) | |
| if [ $TIME_DIFF_HOURS -ge 12 ]; then | |
| echo "✅ No new commits in the last 12 hours (last commit: ${TIME_DIFF_HOURS} hours ago)" | |
| echo "⏭️ Skipping build to save resources" | |
| echo "skip_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| else | |
| echo "🆕 Found new commits within last 12 hours, proceeding with build" | |
| fi | |
| fi | |
| 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 in SGT (GMT+8) with branch name | |
| TIMESTAMP=$(TZ='Asia/Singapore' date +"%y%m%d-%H%M") | |
| RELEASE_TAG="${VERSION}-${BRANCH_NAME}-${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 | |
| if: steps.parse_inputs.outputs.skip_build != 'true' | |
| 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 | |
| if: steps.parse_inputs.outputs.skip_build != 'true' | |
| 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 | |
| if: steps.parse_inputs.outputs.skip_build != 'true' | |
| 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 | |
| if: steps.parse_inputs.outputs.skip_build != 'true' | |
| id: create_release_notes | |
| run: | | |
| BUILD_TIME_SGT=$(TZ='Asia/Singapore' date +"%Y-%m-%d %H:%M:%S %Z") | |
| COMMITS=$(git log -5 --pretty=format:"- %h - %s (%an)" || echo "No commits available") | |
| ADDITIONAL_NOTES="" | |
| if [[ "${{ github.event.inputs.release_notes }}" != "" ]]; then | |
| ADDITIONAL_NOTES="${{ github.event.inputs.release_notes }}" | |
| fi | |
| RELEASE_NOTES=$(cat <<EOF | |
| # Release ${{ steps.parse_inputs.outputs.version }} | |
| **Branch:** ${{ steps.parse_inputs.outputs.branch_name }} | |
| **Build Time (SGT/GMT+8):** ${BUILD_TIME_SGT} | |
| **Triggered By:** @${{ github.actor }} | |
| **Build Number:** ${{ steps.parse_inputs.outputs.build_number }} | |
| **Platforms:** ${{ steps.parse_inputs.outputs.platforms }} | |
| --- | |
| ## What's Changed | |
| ### Recent Commits | |
| ${COMMITS} | |
| ${ADDITIONAL_NOTES:+### Additional Notes} | |
| ${ADDITIONAL_NOTES} | |
| --- | |
| ### Build Artifacts | |
| - Android APK/AAB: Available for download | |
| - iOS IPA: Available for download | |
| - macOS DMG: Available for download | |
| - Windows EXE: Available for download | |
| - Linux AppImage: Available for download | |
| *Note: Only artifacts for the selected platforms will be available.* | |
| EOF | |
| ) | |
| echo "release_notes<<EOF" >> $GITHUB_OUTPUT | |
| echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| shell: bash | |
| - name: Create release | |
| if: steps.parse_inputs.outputs.skip_build != 'true' | |
| 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: needs.prepare.outputs.skip_build != 'true' && (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: needs.prepare.outputs.skip_build != 'true' && (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: needs.prepare.outputs.skip_build != 'true' && (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: needs.prepare.outputs.skip_build != 'true' && (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: needs.prepare.outputs.skip_build != 'true' && (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: needs.prepare.outputs.skip_build != 'true' && (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() && needs.prepare.outputs.skip_build != 'true' | |
| 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() |