diff --git a/.github/workflows/ipad-build.yaml b/.github/workflows/ipad-build.yaml new file mode 100644 index 000000000..30b8b2916 --- /dev/null +++ b/.github/workflows/ipad-build.yaml @@ -0,0 +1,99 @@ +name: Tonkeeper iPad Build +on: + workflow_call: + secrets: + APP_STORE_CONNECT_TEAM_ID: + required: true + APPLE_API_ISSUER: + required: true + APPLE_API_KEY: + required: true + APPLE_API_KEY_ID: + required: true + IDENTITY_P12_B64: + required: true + IDENTITY_PASSPHRASE: + required: true + REACT_APP_MEASUREMENT_ID: + required: true + VITE_APP_APTABASE: + required: true + REACT_APP_TG_BOT_ID: + required: true + REACT_APP_STONFI_REFERRAL_ADDRESS: + required: true +env: + node-version: 20.11.1 + ruby-version: 3.3.0 + +jobs: + ipad-build: + name: ipad-build + runs-on: macos-13 + timeout-minutes: 20 + + steps: + - name: Checkout to git repository + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.node-version }} + + - name: Pods cache + uses: actions/cache@v3 + with: + path: ./apps/tablet/ios/App/Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Set up Ruby and Gemfile dependencies + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ env.ruby-version }} + bundler-cache: true + working-directory: './apps/tablet' + + - name: Decode service account into a file + env: + CREDENTIALS: ${{ secrets.APPLE_API_KEY }} + run: | + echo $CREDENTIALS | base64 -d > ${{ github.workspace }}/AuthKey.p8 + + - name: Enable Corepack + run: | + corepack enable + + - name: Run install + uses: borales/actions-yarn@v5 + with: + cmd: install + + - name: Run build + uses: borales/actions-yarn@v5 + env: + VITE_APP_AMPLITUDE: "" + VITE_APP_MEASUREMENT_ID: ${{ secrets.REACT_APP_MEASUREMENT_ID }} + VITE_APP_APTABASE: ${{ secrets.VITE_APP_APTABASE }} + VITE_APP_APTABASE_HOST: https://anonymous-analytics.tonkeeper.com + VITE_APP_LOCALES: en,zh_TW,zh_CN,id,ru,it,es,uk,tr,bg,uz,bn + VITE_APP_TONCONSOLE_HOST: https://pro.tonconsole.com + VITE_APP_TG_BOT_ID: ${{ secrets.REACT_APP_TG_BOT_ID }} + VITE_APP_STONFI_REFERRAL_ADDRESS: ${{ secrets.REACT_APP_STONFI_REFERRAL_ADDRESS }} + with: + cmd: build:ipad + + - name: Run build ios and upload test flight + uses: maierj/fastlane-action@v3.1.0 + env: + APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }} + BUNDLE_IDENTIFIER: com.tonapps.tonkeeperpro + BUILD_CERTIFICATE_BASE64: ${{ secrets.IDENTITY_P12_B64 }} + P12_PASSWORD: ${{ secrets.IDENTITY_PASSPHRASE }} + APPLE_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER }} + APPLE_KEY_CONTENT: ${{ github.workspace }}/AuthKey.p8 + with: + lane: ios beta \ No newline at end of file diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 9bfff0516..02fda82db 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -235,6 +235,10 @@ jobs: path: | ${{ github.workspace }}/apps/extension/dist/firefox + ipad-build: + uses: ./.github/workflows/ipad-build.yaml + secrets: inherit + web-tests: needs: web-build uses: ./.github/workflows/web-tests.yaml diff --git a/apps/tablet/Gemfile b/apps/tablet/Gemfile new file mode 100644 index 000000000..8bdd597b6 --- /dev/null +++ b/apps/tablet/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' +# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version +ruby ">= 3.3.0" +gem 'cocoapods', '~> 1.13' +gem "fastlane" \ No newline at end of file diff --git a/apps/tablet/fastlane/Fastfile b/apps/tablet/fastlane/Fastfile new file mode 100644 index 000000000..a79136cb9 --- /dev/null +++ b/apps/tablet/fastlane/Fastfile @@ -0,0 +1,140 @@ +platform :ios do + desc 'Export ipa and submit to TestFlight' + lane :beta do + keychain_info = { keychain_name: "ios-build-#{Time.now.to_i}.keychain", keychain_password: SecureRandom.uuid } + + begin + setup_signing(keychain_info) + bump_build_number + build_app_with_signing(keychain_info) + submit_to_testflight + ensure + cleanup_keychain(keychain_info) + end + end + + private_lane :setup_signing do |options| + create_keychain( + name: options[:keychain_name], + password: options[:keychain_password], + unlock: true, + timeout: 0, + lock_when_sleeps: false, + add_to_search_list: true + ) + import_cert(options) + install_profile + update_project_settings + end + + lane :bump_build_number do + file = File.read('../package.json') + data_hash = JSON.parse(file) + api_key = app_store_connect_api_key( + key_id: ENV['APPLE_KEY_ID'], + issuer_id: ENV['APPLE_ISSUER_ID'], + key_content: Base64.decode64(ENV['APPLE_KEY_CONTENT']), + duration: 1200, + in_house: false + ) + build_num = app_store_build_number( + api_key: api_key, + app_identifier: ENV['BUNDLE_IDENTIFIER'], + live: false + ) + build_num = build_num + 1 + UI.message("Bumped build number to #{build_num}") + increment_build_number( + build_number: build_num, + xcodeproj: "./ios/App/App.xcodeproj", + skip_info_plist: true + ) + end + + private_lane :import_cert do |options| + cert_path = "#{Dir.tmpdir}/build_certificate.p12" + File.write(cert_path, Base64.decode64(ENV['BUILD_CERTIFICATE_BASE64'])) + import_certificate( + certificate_path: cert_path, + certificate_password: ENV['P12_PASSWORD'] || "", + keychain_name: options[:keychain_name], + keychain_password: options[:keychain_password], + log_output: true + ) + File.delete(cert_path) + end + + private_lane :cleanup_keychain do |options| + delete_keychain( + name: options[:keychain_name] + ) + end + + private_lane :install_profile do + profile_path = "#{Dir.tmpdir}/build_pp.mobileprovision" + File.write(profile_path, Base64.decode64(ENV['BUILD_PROVISION_PROFILE_BASE64'])) + UI.user_error!("Failed to create provisioning profile at #{profile_path}") unless File.exist?(profile_path) + ENV['PROVISIONING_PROFILE_PATH'] = profile_path + install_provisioning_profile(path: profile_path) + File.delete(profile_path) + end + + private_lane :update_project_settings do + update_code_signing_settings( + path: "./ios/App/App.xcodeproj", + use_automatic_signing: false, + code_sign_identity: "iPhone Distribution", + team_id: ENV['APP_STORE_CONNECT_TEAM_ID'], + app_identifier: ENV['BUNDLE_IDENTIFIER'] + ) + + ## profile_name: ENV['APPLE_PROFILE_NAME'], + ## bundle_identifier: ENV['BUNDLE_IDENTIFIER'], + + update_project_team( + path: "./ios/App/App.xcodeproj", + teamid: ENV['APP_STORE_CONNECT_TEAM_ID'] + ) + end + + private_lane :build_app_with_signing do |options| + unlock_keychain( + path: options[:keychain_name], + password: options[:keychain_password], + set_default: false + ) + build_app( + workspace: "./ios/App/App.xcworkspace", + scheme: "App", + configuration: "Release", + export_method: "app-store", + output_name: "App.ipa", + export_options: { + provisioningProfiles: { + ENV['BUNDLE_IDENTIFIER'] => ENV['APPLE_PROFILE_NAME'] + } + }, + xcargs: "-verbose", + buildlog_path: "./build_logs", + export_xcargs: "-allowProvisioningUpdates", + ) + end + + private_lane :submit_to_testflight do + api_key = app_store_connect_api_key( + key_id: ENV['APPLE_KEY_ID'], + issuer_id: ENV['APPLE_ISSUER_ID'], + key_content: Base64.decode64(ENV['APPLE_KEY_CONTENT']), + duration: 1200, + in_house: false + ) + pilot( + api_key: api_key, + skip_waiting_for_build_processing: true, + skip_submission: true, + distribute_external: false, + notify_external_testers: false, + ipa: "./App.ipa" + ) + end + end \ No newline at end of file diff --git a/apps/tablet/ios/App/App.xcodeproj/project.pbxproj b/apps/tablet/ios/App/App.xcodeproj/project.pbxproj index fca8a7dc5..1595e0911 100644 --- a/apps/tablet/ios/App/App.xcodeproj/project.pbxproj +++ b/apps/tablet/ios/App/App.xcodeproj/project.pbxproj @@ -370,7 +370,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; - PRODUCT_BUNDLE_IDENTIFIER = com.tonkeeper.pro.app; + PRODUCT_BUNDLE_IDENTIFIER = com.tonapps.tonkeeperpro; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; @@ -391,7 +391,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.tonkeeper.pro.app; + PRODUCT_BUNDLE_IDENTIFIER = com.tonapps.tonkeeperpro; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_VERSION = 5.0;