MajdataPlay build #2939
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 Project | |
| run-name: MajdataPlay build | |
| on: | |
| push: | |
| branches: [ "dev", "main", "fastlane-ios", "test_workflow", "github-ci" ] | |
| pull_request: | |
| branches: [ "main" ] | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: nightly-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} | |
| UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} | |
| UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} | |
| UNITY_VERSION: 6000.3.17f1 | |
| IOS_BUILD_NUMBER_OFFSET: 3162 | |
| jobs: | |
| build: | |
| name: Build (Windows/Linux) | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'push' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| configuration: [Release] | |
| platform: [StandaloneWindows64, StandaloneLinux64] | |
| env: | |
| CONFIGURATION: ${{ matrix.configuration }} | |
| TARGET_PLATFORM: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout the repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Extract version from unity project | |
| run: | | |
| VERSION=$(grep -m1 'bundleVersion:' ProjectSettings/ProjectSettings.asset | awk '{print $2}') | |
| VERSION_WITH_HASH="${VERSION}-$(date +%Y%m%d)-${GITHUB_SHA::7}" | |
| echo "BUNDLE_VERSION=$VERSION" >> $GITHUB_ENV | |
| echo "BUNDLE_VERSION_WITH_HASH=$VERSION_WITH_HASH" >> $GITHUB_ENV | |
| - name: Cache Unity Library | |
| uses: actions/cache@v4 | |
| with: | |
| path: Library | |
| key: Library-${{ runner.os }}-${{ env.TARGET_PLATFORM }}-${{ hashFiles('ProjectSettings/ProjectVersion.txt', 'ProjectSettings/**', 'Packages/manifest.json', 'Packages/packages-lock.json') }} | |
| restore-keys: | | |
| Library-${{ runner.os }}-${{ env.TARGET_PLATFORM }}- | |
| - name: Build Release | |
| uses: game-ci/unity-builder@v4 | |
| with: | |
| unityVersion: ${{ env.UNITY_VERSION }} | |
| targetPlatform: ${{ env.TARGET_PLATFORM }} | |
| versioning: Custom | |
| version: ${{ env.BUNDLE_VERSION_WITH_HASH }} | |
| buildName: MajdataPlay | |
| buildsPath: ${{ env.CONFIGURATION }} | |
| - name: Package output as zip | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| ZIP_NAME="MajdataPlay-${{ env.TARGET_PLATFORM }}-nightly.zip" | |
| cd "${{ env.CONFIGURATION }}" | |
| zip -qry "../out/${ZIP_NAME}" . | |
| cd - | |
| ls -la out | |
| - name: Upload build outputs as workflow artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: out-${{ env.TARGET_PLATFORM }} | |
| path: out/* | |
| if-no-files-found: error | |
| retention-days: 7 | |
| build-android: | |
| name: Build (Android) | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'push' }} | |
| env: | |
| CONFIGURATION: Release | |
| TARGET_PLATFORM: Android | |
| PROJECT_ROOT: ${{ github.workspace }} | |
| UNITY_PROJECT_EXPORT_PATH: Release/Android/MajdataPlay | |
| steps: | |
| - name: Checkout the repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| fetch-depth: 0 | |
| - name: Extract version from unity project | |
| run: | | |
| VERSION=$(grep -m1 'bundleVersion:' ProjectSettings/ProjectSettings.asset | awk '{print $2}') | |
| VERSION_WITH_HASH="${VERSION}-$(date +%Y%m%d)-${GITHUB_SHA::7}" | |
| echo "BUNDLE_VERSION=$VERSION" >> $GITHUB_ENV | |
| echo "BUNDLE_VERSION_WITH_HASH=$VERSION_WITH_HASH" >> $GITHUB_ENV | |
| - name: Calculate Android version code | |
| run: | | |
| COMMIT_COUNT=$(git rev-list HEAD --count) | |
| COUNT=$((COMMIT_COUNT + 100000)) | |
| echo "BUNDLE_VERSION_CODE=$COUNT" >> $GITHUB_ENV | |
| echo "BUNDLE_VERSION_CODE_FOR_APK=$COMMIT_COUNT" >> $GITHUB_ENV | |
| echo "Android AAB version code: $COUNT" | |
| echo "Android APK version code: $COMMIT_COUNT" | |
| - name: Cache Unity Library | |
| uses: actions/cache@v4 | |
| with: | |
| path: Library | |
| key: Library-${{ runner.os }}-${{ env.TARGET_PLATFORM }}-${{ hashFiles('ProjectSettings/ProjectVersion.txt', 'ProjectSettings/**', 'Packages/manifest.json', 'Packages/packages-lock.json') }} | |
| restore-keys: | | |
| Library-${{ runner.os }}-${{ env.TARGET_PLATFORM }}- | |
| - name: Setup Ruby and Fastlane | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: '3.2' | |
| - name: Install Fastlane | |
| run: gem install fastlane | |
| - name: Export Android Project | |
| uses: game-ci/unity-builder@v4 | |
| with: | |
| unityVersion: ${{ env.UNITY_VERSION }} | |
| targetPlatform: ${{ env.TARGET_PLATFORM }} | |
| versioning: Custom | |
| version: ${{ env.BUNDLE_VERSION_WITH_HASH }} | |
| androidVersionCode: ${{ env.BUNDLE_VERSION_CODE_FOR_APK }} | |
| buildName: MajdataPlay | |
| buildsPath: ${{ env.CONFIGURATION }} | |
| androidExportType: androidStudioProject | |
| - name: Fix Directory Permissions | |
| run: sudo chown -R $USER:$USER ${{ env.CONFIGURATION }}/${{ env.TARGET_PLATFORM }} | |
| - name: Setup Java & Gradle Cache | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| cache: 'gradle' | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| with: | |
| gradle-version: '9.1.0' | |
| - name: Install Android NDK via SDK Manager | |
| run: | | |
| echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "ndk;23.1.7779620" | |
| - name: Fix Android SDK and NDK paths | |
| run: | | |
| cd ${{ env.UNITY_PROJECT_EXPORT_PATH }} | |
| BAD_NDK="/opt/unity/Editor/Data/PlaybackEngines/AndroidPlayer/NDK" | |
| BAD_SDK="/opt/unity/Editor/Data/PlaybackEngines/AndroidPlayer/SDK" | |
| GOOD_NDK="$ANDROID_HOME/ndk/23.1.7779620" | |
| GOOD_SDK="$ANDROID_HOME" | |
| find . -type f \( -name "*.gradle" -o -name "*.properties" \) -exec sed -i "s|$BAD_NDK|$GOOD_NDK|g" {} + | |
| find . -type f \( -name "*.gradle" -o -name "*.properties" \) -exec sed -i "s|$BAD_SDK|$GOOD_SDK|g" {} + | |
| echo "sdk.dir=$GOOD_SDK" > local.properties | |
| echo "ndk.dir=$GOOD_NDK" >> local.properties | |
| - name: Generate Gradle Wrapper | |
| run: | | |
| cd ${{ env.UNITY_PROJECT_EXPORT_PATH }} | |
| gradle wrapper | |
| - name: Prepare Keystore | |
| run: | | |
| echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android-publish.keystore | |
| echo "KEYSTORE_PATH=$(pwd)/android-publish.keystore" >> $GITHUB_ENV | |
| - name: Create Fastfile | |
| run: | | |
| mkdir -p fastlane | |
| cat << 'EOF' > fastlane/Fastfile | |
| default_platform(:android) | |
| platform :android do | |
| before_all do | |
| gradle_path = File.join( | |
| ENV['PROJECT_ROOT'], | |
| ENV['UNITY_PROJECT_EXPORT_PATH'], | |
| 'launcher', | |
| 'build.gradle' | |
| ) | |
| content = File.read(gradle_path) | |
| content = content.gsub(/versionCode\s+\d+/, "versionCode #{ENV['BUNDLE_VERSION_CODE']}") | |
| content = content.gsub(/versionName\s+['"].*?['"]/, "versionName '#{ENV['BUNDLE_VERSION_WITH_HASH']}'") | |
| signing_block = <<-GRADLE | |
| // --- Injected by Fastlane --- | |
| android { | |
| signingConfigs { | |
| release { | |
| storeFile file("#{ENV['KEYSTORE_PATH']}") | |
| storePassword "#{ENV['ANDROID_KEYSTORE_PASS']}" | |
| keyAlias "#{ENV['ANDROID_KEYALIAS_NAME']}" | |
| keyPassword "#{ENV['ANDROID_KEYALIAS_PASS']}" | |
| } | |
| } | |
| buildTypes { | |
| release { | |
| signingConfig signingConfigs.release | |
| } | |
| } | |
| } | |
| GRADLE | |
| File.open(gradle_path, 'w') { |file| file.puts(content + signing_block) } | |
| end | |
| desc "Build foss version (APK)" | |
| lane :build_foss do | |
| gradle( | |
| project_dir: ENV['UNITY_PROJECT_EXPORT_PATH'], | |
| task: "assemble", | |
| build_type: "Release", | |
| flags: "--build-cache" | |
| ) | |
| end | |
| desc "Build Play version (AAB)" | |
| lane :build_play do | |
| gradle( | |
| project_dir: ENV['UNITY_PROJECT_EXPORT_PATH'], | |
| task: "bundle", | |
| build_type: "Release", | |
| flags: "--build-cache" | |
| ) | |
| end | |
| end | |
| EOF | |
| - name: Build foss (APK) | |
| env: | |
| ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }} | |
| ANDROID_KEYALIAS_NAME: ${{ secrets.ANDROID_KEYALIAS_NAME }} | |
| ANDROID_KEYALIAS_PASS: ${{ secrets.ANDROID_KEYALIAS_PASS }} | |
| run: | | |
| ROOT="${{ env.UNITY_PROJECT_EXPORT_PATH }}" | |
| # Unity property | |
| find "$ROOT" -type f -name "gradle.properties" \ | |
| -exec sed -i 's/^unity\.targetSdkVersion=.*/unity.targetSdkVersion=29/' {} + | |
| # AGP 9 syntax | |
| find "$ROOT" -type f -name "*.gradle" \ | |
| -exec sed -i -E 's/(targetSdk[[:space:]]+)[0-9]+/\129/g' {} + | |
| # Old AGP syntax, if any | |
| find "$ROOT" -type f -name "*.gradle" \ | |
| -exec sed -i -E 's/(targetSdkVersion[[:space:]]+)[0-9]+/\129/g' {} + | |
| echo "=== Final SDK configuration ===" | |
| grep -RIn \ | |
| --include='*.gradle' \ | |
| --include='*.properties' \ | |
| -E 'targetSdk|targetSdkVersion|unity.targetSdkVersion' \ | |
| "$ROOT" | |
| fastlane build_foss | |
| - name: Collect Android APKs | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| OUTPUT_DIR="${{ env.UNITY_PROJECT_EXPORT_PATH }}/launcher/build/outputs/apk/release" | |
| for file in "$OUTPUT_DIR"/*.apk; do | |
| if [[ "$file" == *"v7a"* ]]; then | |
| cp "$file" "out/MajdataPlay-Android-${{ env.BUNDLE_VERSION_WITH_HASH }}-nightly.armeabi-v7a.apk" | |
| elif [[ "$file" == *"v8a"* ]]; then | |
| cp "$file" "out/MajdataPlay-Android-${{ env.BUNDLE_VERSION_WITH_HASH }}-nightly.arm64-v8a.apk" | |
| else | |
| cp "$file" "out/MajdataPlay-Android-${{ env.BUNDLE_VERSION_WITH_HASH }}-nightly.apk" | |
| fi | |
| done | |
| ls -la out | |
| - name: Build Play (AAB) | |
| env: | |
| ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }} | |
| ANDROID_KEYALIAS_NAME: ${{ secrets.ANDROID_KEYALIAS_NAME }} | |
| ANDROID_KEYALIAS_PASS: ${{ secrets.ANDROID_KEYALIAS_PASS }} | |
| run: | | |
| ROOT="${{ env.UNITY_PROJECT_EXPORT_PATH }}" | |
| find "$ROOT" -type f -name "gradle.properties" \ | |
| -exec sed -i 's/^unity\.targetSdkVersion=.*/unity.targetSdkVersion=36/' {} + | |
| find "$ROOT" -type f -name "*.gradle" \ | |
| -exec sed -i -E 's/(targetSdk[[:space:]]+)[0-9]+/\136/g' {} + | |
| find "$ROOT" -type f -name "*.gradle" \ | |
| -exec sed -i -E 's/(targetSdkVersion[[:space:]]+)[0-9]+/\136/g' {} + | |
| echo "=== Final SDK configuration ===" | |
| grep -RIn \ | |
| --include='*.gradle' \ | |
| --include='*.properties' \ | |
| -E 'targetSdk|targetSdkVersion|unity.targetSdkVersion' \ | |
| "$ROOT" | |
| fastlane build_play | |
| - name: Collect Android AAB | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| cp "${{ env.UNITY_PROJECT_EXPORT_PATH }}/launcher/build/outputs/bundle/release/launcher-release.aab" "out/MajdataPlay-Android-${{ env.BUNDLE_VERSION_WITH_HASH }}-nightly.aab" | |
| ls -la out | |
| - name: Upload foss APK outputs as workflow artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: MajdataPlay-Android-foss-APK | |
| path: out/*.apk | |
| if-no-files-found: error | |
| retention-days: 7 | |
| - name: Upload Play AAB outputs as workflow artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: MajdataPlay-Android-Play-AAB | |
| path: out/*.aab | |
| if-no-files-found: error | |
| retention-days: 7 | |
| - name: Upload to Google Play | |
| uses: r0adkll/upload-google-play@v1 | |
| continue-on-error: true | |
| with: | |
| serviceAccountJsonPlainText: ${{ secrets.PLAY_DEPLOY_ACCOUNT_JSON }} | |
| packageName: net.majdata.majdataplay | |
| releaseFiles: out/MajdataPlay-Android-${{ env.BUNDLE_VERSION_WITH_HASH }}-nightly.aab | |
| track: internal | |
| status: completed | |
| build-apple: | |
| name: Build (macOS/iOS) | |
| runs-on: macos-26 | |
| if: ${{ github.event_name == 'push' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| configuration: [Release] | |
| platform: [StandaloneOSX, iOS] | |
| env: | |
| CONFIGURATION: ${{ matrix.configuration }} | |
| TARGET_PLATFORM: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout the repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| fetch-depth: 0 | |
| - name: Extract version from unity project | |
| run: | | |
| VERSION=$(grep -m1 'bundleVersion:' ProjectSettings/ProjectSettings.asset | awk '{print $2}') | |
| VERSION_WITH_HASH="${VERSION}-${GITHUB_SHA::7}" | |
| echo "BUNDLE_VERSION=$VERSION" >> $GITHUB_ENV | |
| echo "BUNDLE_VERSION_WITH_HASH=$VERSION_WITH_HASH" >> $GITHUB_ENV | |
| - name: Calculate Apple build number | |
| run: | | |
| BUILD_NUMBER=$((GITHUB_RUN_NUMBER + IOS_BUILD_NUMBER_OFFSET)) | |
| echo "BUNDLE_VERSION_CODE=$BUILD_NUMBER" >> $GITHUB_ENV | |
| echo "Apple build number: $BUILD_NUMBER" | |
| - name: Cache Unity Library | |
| uses: actions/cache@v4 | |
| with: | |
| path: Library | |
| key: Library-${{ runner.os }}-${{ env.TARGET_PLATFORM }}-${{ hashFiles('ProjectSettings/ProjectVersion.txt', 'ProjectSettings/**', 'Packages/manifest.json', 'Packages/packages-lock.json') }} | |
| restore-keys: | | |
| Library-${{ runner.os }}-${{ env.TARGET_PLATFORM }}- | |
| - name: Build Release | |
| uses: game-ci/unity-builder@v4 | |
| with: | |
| unityVersion: ${{ env.UNITY_VERSION }} | |
| targetPlatform: ${{ env.TARGET_PLATFORM }} | |
| versioning: Custom | |
| version: ${{ env.BUNDLE_VERSION }} | |
| buildName: MajdataPlay | |
| buildsPath: ${{ env.CONFIGURATION }} | |
| - name: Import Apple certificate | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| uses: apple-actions/import-codesign-certs@v3 | |
| with: | |
| p12-file-base64: ${{ secrets.APPSTORE_CERTIFICATES_FILE_BASE64 }} | |
| p12-password: ${{ secrets.APPSTORE_CERTIFICATES_PASSWORD }} | |
| - name: Install provisioning profile | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles | |
| echo "${{ secrets.APPSTORE_PROFILE_BASE64 }}" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/appstore.mobileprovision | |
| ls -la ~/Library/MobileDevice/Provisioning\ Profiles | |
| - name: Setup Ruby | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: '3.3' | |
| - name: Install fastlane | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| run: | | |
| gem install bundler | |
| cat > Gemfile <<'EOF' | |
| source "https://rubygems.org" | |
| gem "fastlane", "2.236.1" | |
| gem "xcodeproj" | |
| gem "multi_json" | |
| EOF | |
| bundle install | |
| - name: Build IPA Release | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| env: | |
| APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} | |
| APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} | |
| APPSTORE_PRIVATE_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPSTORE_PROFILE_NAME: ${{ secrets.APPSTORE_PROFILE_NAME }} | |
| IOS_PROJECT_PATH: ${{ env.CONFIGURATION }}/iOS/MajdataPlay/Unity-iPhone.xcodeproj | |
| IOS_EXPORT_DIR: ${{ env.CONFIGURATION }}/iOS/export | |
| run: | | |
| set -euo pipefail | |
| mkdir -p fastlane | |
| cat > fastlane/Fastfile <<'EOF' | |
| default_platform(:ios) | |
| platform :ios do | |
| lane :ci do | |
| update_code_signing_settings( | |
| use_automatic_signing: false, | |
| path: ENV["IOS_PROJECT_PATH"], | |
| team_id: ENV["APPLE_TEAM_ID"], | |
| targets: ["Unity-iPhone"], | |
| profile_name: ENV["APPSTORE_PROFILE_NAME"], | |
| code_sign_identity: "Apple Distribution" | |
| ) | |
| update_code_signing_settings( | |
| use_automatic_signing: false, | |
| path: ENV["IOS_PROJECT_PATH"], | |
| team_id: ENV["APPLE_TEAM_ID"], | |
| targets: ["UnityFramework"], | |
| code_sign_identity: "Apple Distribution" | |
| ) | |
| increment_version_number( | |
| xcodeproj: ENV["IOS_PROJECT_PATH"], | |
| version_number: ENV["BUNDLE_VERSION"] | |
| ) | |
| increment_build_number( | |
| xcodeproj: ENV["IOS_PROJECT_PATH"], | |
| build_number: ENV["BUNDLE_VERSION_CODE"] | |
| ) | |
| UI.message("Version: #{ENV["BUNDLE_VERSION"]}") | |
| UI.message("Build number: #{ENV["BUNDLE_VERSION_CODE"]}") | |
| gym( | |
| project: ENV["IOS_PROJECT_PATH"], | |
| scheme: "Unity-iPhone", | |
| configuration: ENV["CONFIGURATION"], | |
| export_method: "app-store", | |
| output_directory: ENV["IOS_EXPORT_DIR"], | |
| output_name: "MajdataPlay.ipa", | |
| clean: true, | |
| export_options: { | |
| method: "app-store", | |
| signingStyle: "manual", | |
| provisioningProfiles: { | |
| "net.majdata.majdataplay" => ENV["APPSTORE_PROFILE_NAME"] | |
| } | |
| } | |
| ) | |
| end | |
| end | |
| EOF | |
| bundle exec fastlane ios ci | |
| - name: Verify IPA build number | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| run: | | |
| set -euo pipefail | |
| IPA_PATH="${{ env.CONFIGURATION }}/iOS/export/MajdataPlay.ipa" | |
| TMP_DIR="$(mktemp -d)" | |
| unzip -q "$IPA_PATH" -d "$TMP_DIR" | |
| APP_DIR="$(find "$TMP_DIR/Payload" -maxdepth 1 -name "*.app" -type d | head -n 1)" | |
| INFO_PLIST="$APP_DIR/Info.plist" | |
| echo "App bundle: $APP_DIR" | |
| echo "Info.plist: $INFO_PLIST" | |
| echo "CFBundleIdentifier:" | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST" | |
| echo "CFBundleShortVersionString:" | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$INFO_PLIST" || true | |
| echo "CFBundleVersion:" | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$INFO_PLIST" | |
| - name: Upload to TestFlight | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| continue-on-error: true | |
| env: | |
| APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} | |
| APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} | |
| APPSTORE_PRIVATE_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }} | |
| IOS_EXPORT_DIR: ${{ env.CONFIGURATION }}/iOS/export | |
| GITHUB_SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| BUILD_AT="$(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M:%S')" | |
| export BUILD_AT | |
| mkdir -p fastlane | |
| cat > fastlane/Fastfile <<'EOF' | |
| default_platform(:ios) | |
| platform :ios do | |
| lane :ci do | |
| api_key = app_store_connect_api_key( | |
| key_id: ENV["APPSTORE_KEY_ID"], | |
| issuer_id: ENV["APPSTORE_ISSUER_ID"], | |
| key_content: ENV["APPSTORE_PRIVATE_KEY"] | |
| ) | |
| upload_to_testflight( | |
| api_key: api_key, | |
| ipa: File.join(ENV["IOS_EXPORT_DIR"], "MajdataPlay.ipa"), | |
| distribute_external: true, | |
| groups: ["beta"], | |
| changelog: "Automated CI build\nCommit: #{ENV["GITHUB_SHA"]}\nBuild: #{ENV["BUNDLE_VERSION_CODE"]}\nBuild at: #{ENV["BUILD_AT"]} (UTC+8)", | |
| submit_beta_review: true, | |
| skip_waiting_for_build_processing: false | |
| ) | |
| end | |
| end | |
| EOF | |
| bundle exec fastlane ios ci | |
| - name: Package macOS output as zip | |
| if: ${{ env.TARGET_PLATFORM == 'StandaloneOSX' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| ZIP_NAME="MajdataPlay-StandaloneOSX-nightly.zip" | |
| cd "${{ env.CONFIGURATION }}" | |
| zip -qry "../out/${ZIP_NAME}" . | |
| cd - | |
| ls -la out | |
| - name: Collect iOS IPA to out | |
| if: ${{ env.TARGET_PLATFORM == 'iOS' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| cp "${{ env.CONFIGURATION }}/iOS/export/MajdataPlay.ipa" out/ | |
| ls -la out | |
| - name: Upload build outputs as workflow artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: out-${{ env.TARGET_PLATFORM }} | |
| path: out/* | |
| if-no-files-found: error | |
| retention-days: 7 | |
| publish-nightly: | |
| name: Publish nightly release (replace only after all builds succeed) | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event_name == 'push' }} | |
| needs: [build, build-android, build-apple] | |
| steps: | |
| - name: Checkout for tagging | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: collected | |
| - name: Show collected files | |
| run: | | |
| set -euo pipefail | |
| ls -R collected || true | |
| - name: Move nightly tag to this commit | |
| run: | | |
| set -e | |
| git tag -f nightly "${GITHUB_SHA}" | |
| git push origin -f nightly | |
| - name: Delete old nightly release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| if gh release view nightly >/dev/null 2>&1; then | |
| gh release delete nightly -y | |
| else | |
| echo "No existing nightly release." | |
| fi | |
| - name: Create new nightly release with notes | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| BUILD_AT="$(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M:%S')" | |
| NOTES_FILE="$(mktemp)" | |
| printf "Automated nightly build.\nCommit: %s\nBuild at: %s (UTC+8)\n" \ | |
| "${GITHUB_SHA}" \ | |
| "${BUILD_AT}" \ | |
| > "${NOTES_FILE}" | |
| gh release create nightly \ | |
| --title "Nightly" \ | |
| --notes-file "${NOTES_FILE}" \ | |
| --prerelease | |
| - name: Upload all artifacts to the new nightly release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| gh release upload nightly collected/**/* --clobber |