diff --git a/.github/workflows/android-deploy.yml b/.github/workflows/android-deploy.yml index dd06843d..e631c4d7 100644 --- a/.github/workflows/android-deploy.yml +++ b/.github/workflows/android-deploy.yml @@ -21,9 +21,9 @@ jobs: java-version: '17' cache: 'gradle' - - name: Decode and create .env file + - name: Decode and create .env file (prod) run: | - echo "${{ secrets.ENV_B64 }}" | base64 --decode > .env + echo "${{ secrets.ENV_PROD_B64 }}" | base64 --decode > .env # Flutter SDK (subosito) 이전에 pub 캐시 복원 - name: Cache Flutter pub packages @@ -84,7 +84,7 @@ jobs: - name: Auto-increment build number run: | - BUILD_NO=$(date +%s) + BUILD_NO="9${{ github.run_number }}" echo "BUILD_NO=$BUILD_NO" >> $GITHUB_ENV - name: Build Flutter Android App Bundle diff --git a/.github/workflows/android-test.yml b/.github/workflows/android-test.yml new file mode 100644 index 00000000..ca950321 --- /dev/null +++ b/.github/workflows/android-test.yml @@ -0,0 +1,120 @@ +name: Google Play Console Deploy - Android Test + +on: + push: + branches: [ develop ] + +jobs: + build-android-test: + timeout-minutes: 30 + name: Google Play Console Deploy - Android Test + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Java 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + cache: 'gradle' + + - name: Decode and create .env file (test) + run: | + echo "${{ secrets.ENV_TEST_B64 }}" | base64 --decode > .env + + - name: Cache Flutter pub packages + uses: actions/cache@v3 + with: + path: ~/.pub-cache + key: pubcache-${{ runner.os }}-stable-3.27.1-${{ hashFiles('pubspec.lock') }} + restore-keys: pubcache-${{ runner.os }}-stable-3.27.1- + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.35.4' + channel: 'stable' + architecture: x64 + cache: true + + - name: Decode google-services.json + run: | + mkdir -p android/app + echo "${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}" | base64 --decode > ./android/app/google-services.json + + - name: Decode firebase_options.dart + run: | + mkdir -p lib + echo "${{ secrets.FIREBASE_OPTIONS_B64 }}" | base64 --decode > lib/firebase_options.dart + + - name: Create android/local.properties (CI) + run: | + mkdir -p android + ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}" + echo "sdk.dir=$ANDROID_SDK_ROOT" > android/local.properties + echo "KAKAO_NATIVE_APP_KEY=${{ secrets.KAKAO_NATIVE_APP_KEY }}" >> android/local.properties + cat android/local.properties | sed 's/KAKAO_NATIVE_APP_KEY=.*/KAKAO_NATIVE_APP_KEY=***REDACTED***/' + + - name: Verify decoded files + run: | + test -f lib/firebase_options.dart || (echo "missing lib/firebase_options.dart" && exit 1) + test -f .env || (echo "missing .env" && exit 1) + + - name: Decode and install signing key + run: | + mkdir -p android/app/keystore + echo "${{ secrets.ANDROID_KEYSTORE_B64 }}" | base64 --decode > android/app/keystore/my-release-key.jks + echo "storeFile=keystore/my-release-key.jks" > android/app/keystore/key.properties + echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/app/keystore/key.properties + echo "storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> android/app/keystore/key.properties + echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/app/keystore/key.properties + + - name: Install dependencies + run: | + flutter clean + flutter pub get + + - name: Analyze code + run: flutter analyze --no-fatal-warnings || echo "Analysis completed with warnings" + continue-on-error: false + + - name: Auto-increment build number + run: | + BUILD_NO="1${{ github.run_number }}" + echo "BUILD_NO=$BUILD_NO" >> $GITHUB_ENV + + - name: Build Flutter Android App Bundle + run: flutter build appbundle --release --build-number=$BUILD_NO + + - name: Upload app to Google Play (Internal) + uses: r0adkll/upload-google-play@v1 + with: + serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }} + packageName: com.dongsoop.site.dongsoop + releaseFiles: build/app/outputs/bundle/release/app-release.aab + track: internal + status: completed + + - name: Extract build date (KST) + run: | + TZ=Asia/Seoul date +"%Y-%m-%d %H:%M" > .build_date + echo "BUILD_DATE=$(cat .build_date)" >> $GITHUB_ENV + + - name: Build Success Summary + run: | + echo "🎉 Flutter Android test build completed successfully!" + echo "📦 App Bundle uploaded to Google Play Internal track" + echo "🔗 Build #${{ github.run_number }}" + echo "🗓️ Date (KST): $BUILD_DATE" + + - name: Send Discord Notification + env: + DISCORD_WEBHOOK: ${{ secrets.APPLE_BUILD_DISCORD_WEBHOOK }} + run: | + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\": \"🧪 [Internal] Android 테스트 빌드가 업로드되었습니다! (테스트 광고 적용) 🗓️ 날짜(KST): $BUILD_DATE\"}" \ + $DISCORD_WEBHOOK diff --git a/.github/workflows/flutter-ios.yml b/.github/workflows/flutter-ios.yml index 62d3af93..a74e2fcd 100644 --- a/.github/workflows/flutter-ios.yml +++ b/.github/workflows/flutter-ios.yml @@ -8,15 +8,14 @@ on: jobs: build-ios: - timeout-minutes: 15 + timeout-minutes: 30 name: Build Flutter iOS App - runs-on: macos-latest - + runs-on: macos-15 + steps: - name: Checkout repository uses: actions/checkout@v4 - # Flutter SDK (subosito) 이전에 pub 캐시 복원 - name: Cache Flutter pub packages uses: actions/cache@v3 with: @@ -32,9 +31,14 @@ jobs: architecture: x64 cache: true - - name: Decode and create .env file + - name: Select Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '26.2' + + - name: Decode and create .env file (test) run: | - echo "${{ secrets.ENV_B64 }}" | base64 --decode > .env + echo "${{ secrets.ENV_TEST_B64 }}" | base64 --decode > .env - name: Decode Secrets.xcconfig run: | @@ -46,17 +50,17 @@ jobs: mkdir -p ios/Runner echo "${{ secrets.GOOGLE_SERVICE_INFO_PLIST_BASE64 }}" | base64 --decode > ./ios/Runner/GoogleService-Info.plist - # firebase_options.dart 복원 - name: Decode firebase_options.dart run: | mkdir -p lib echo "${{ secrets.FIREBASE_OPTIONS_B64 }}" | base64 --decode > lib/firebase_options.dart - - name: Verify firebase_options.dart exists + - name: Verify decoded files run: | - test -f lib/firebase_options.dart && head -n 5 lib/firebase_options.dart || (echo "missing lib/firebase_options.dart" && exit 1) + test -f lib/firebase_options.dart || (echo "missing lib/firebase_options.dart" && exit 1) + test -s .env || (echo ".env is empty — ENV_TEST_B64 secret may not be set" && exit 1) + grep -q 'BASE_URL' .env || (echo "BASE_URL missing from .env" && exit 1) - # iOS Pods 캐시 복원 - name: Cache iOS Pods uses: actions/cache@v3 with: @@ -66,42 +70,77 @@ jobs: - name: Install dependencies run: | + flutter clean flutter pub get cd ios && pod install --repo-update - # 코드 분석 수행 (경고는 허용, 에러만 실패 처리) - name: Analyze code run: flutter analyze --no-fatal-warnings || echo "Analysis completed with warnings" continue-on-error: false - # 무료 버전 - Apple Developer 계정 없이 빌드만 - - name: Build iOS (no codesign) + # PR: 코드사인 없이 빌드만 (빠른 CI 검증) + - name: Build iOS (no codesign) - PR only + if: github.event_name == 'pull_request' run: | flutter build ios --release --no-codesign echo "iOS build completed successfully!" - # 빌드 결과 확인 - - name: List build artifacts - run: | - echo "Build artifacts:" - ls -la build/ios/iphoneos/ - du -sh build/ios/iphoneos/Runner.app - - # iOS 빌드 결과물을 GitHub Artifacts에 업로드 - - name: Upload iOS App as Artifact + - name: Upload iOS App as Artifact - PR only + if: github.event_name == 'pull_request' uses: actions/upload-artifact@v4 with: name: dongsoop-ios-app-${{ github.run_number }} path: build/ios/iphoneos/Runner.app retention-days: 7 - # 빌드 성공 알림 - - name: Build Success Summary + # Push to develop: 코드사인 + TestFlight 업로드 (테스트 광고) + - name: Decode certificates/profiles - push only + if: github.event_name == 'push' + run: | + echo "${{ secrets.IOS_DISTRIBUTION_P12 }}" | base64 --decode > ios_distribution.p12 + echo "${{ secrets.IOS_PROVISIONING_PROFILE }}" | base64 --decode > profile.mobileprovision + + - name: Import certificate to keychain - push only + if: github.event_name == 'push' + run: | + security create-keychain -p "${{ secrets.IOS_DISTRIBUTION_P12_PASSWORD }}" build.keychain || true + security import ios_distribution.p12 -k build.keychain -P "${{ secrets.IOS_DISTRIBUTION_P12_PASSWORD }}" -T /usr/bin/codesign + security list-keychains -s build.keychain + security unlock-keychain -p "${{ secrets.IOS_DISTRIBUTION_P12_PASSWORD }}" build.keychain + security set-keychain-settings -t 3600 -l ~/Library/Keychains/build.keychain + security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k "${{ secrets.IOS_DISTRIBUTION_P12_PASSWORD }}" build.keychain + + - name: Install provisioning profile - push only + if: github.event_name == 'push' + run: | + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/ + + - name: Auto-increment build number - push only + if: github.event_name == 'push' + run: | + BUILD_NO="1${{ github.run_number }}" + echo "BUILD_NO=$BUILD_NO" >> $GITHUB_ENV + + - name: Build Flutter iOS IPA - push only + if: github.event_name == 'push' + run: flutter build ipa --release --build-number=$BUILD_NO --export-options-plist=ios/ExportOptions_AppStore.plist + + - name: Upload app to TestFlight - push only + if: github.event_name == 'push' + uses: apple-actions/upload-testflight-build@v3 + with: + app-path: ${{ github.workspace }}/build/ios/ipa/dongsoop.ipa + issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} + api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} + api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} + + - name: Send Discord Notification - push only + if: github.event_name == 'push' + env: + DISCORD_WEBHOOK: ${{ secrets.APPLE_BUILD_DISCORD_WEBHOOK }} run: | - echo "🎉 Flutter iOS build completed successfully!" - echo "📱 iOS app built without code signing" - echo "📦 Artifacts uploaded to GitHub Actions" - echo "⬇️ Download from: Actions tab > Artifacts" - echo "🔗 Build #${{ github.run_number }}" - echo "📁 Artifact name: dongsoop-ios-app-${{ github.run_number }}" - echo "🍎 App name: Dongsoop (dongyang mirae university service)" + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\": \"🧪 [TestFlight] iOS 테스트 빌드가 업로드되었습니다! (테스트 광고 적용)\"}" \ + $DISCORD_WEBHOOK diff --git a/.github/workflows/ios-deploy.yml b/.github/workflows/ios-deploy.yml index f6556d5e..0b0f0623 100644 --- a/.github/workflows/ios-deploy.yml +++ b/.github/workflows/ios-deploy.yml @@ -35,9 +35,9 @@ jobs: with: xcode-version: '26.2' - - name: Decode and create .env file + - name: Decode and create .env file (prod) run: | - echo "${{ secrets.ENV_B64 }}" | base64 --decode > .env + echo "${{ secrets.ENV_PROD_B64 }}" | base64 --decode > .env - name: Decode Secrets.xcconfig run: | @@ -100,7 +100,7 @@ jobs: - name: Auto-increment build number run: | - BUILD_NO=$(date +%s) # 타임스탬프를 빌드 번호로 사용 + BUILD_NO="9${{ github.run_number }}" echo "BUILD_NO=$BUILD_NO" >> $GITHUB_ENV - name: Build Flutter iOS