Build pipeline for both platforms #42
| @@ -39,11 +39,23 @@ jobs: | ||||
|           # remove the 'v' prefix from the tag name | ||||
|           echo "BUILD_NAME=${REF_NAME//v}" >> $GITHUB_ENV | ||||
|  | ||||
|       - name: Load secrets from github | ||||
|       - name: Load secrets | ||||
|         id: load-secrets | ||||
|         uses: hashicorp/vault-action@v3 | ||||
|         with: | ||||
|           url: https://api.hashicorp.com | ||||
|           token: ${{ secrets.VAULT_TOKEN }} | ||||
|           secrets: | | ||||
|             secret/release GOOGLE_MAPS_API_KEY | GOOGLE_MAPS_API_KEY ; | ||||
|             secret/release ANDROID_SECRET_PROPERTIES_BASE64 | ANDROID_SECRET_PROPERTIES_BASE64 ; | ||||
|             secret/release ANDROID_GOOGLE_PLAY_JSON_BASE64 | ANDROID_GOOGLE_PLAY_JSON_BASE64 ; | ||||
|             secret/release ANDROID_KEYSTORE_BASE64 | ANDROID_KEYSTORE_BASE64 ; | ||||
|  | ||||
|       - name: Put selected secrets into files | ||||
|         run: | | ||||
|           echo "${{ secrets.ANDROID_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties | ||||
|           echo "${{ secrets.ANDROID_GOOGLE_PLAY_JSON_BASE64 }}" | base64 -d > google-key.json | ||||
|           echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > release.keystore | ||||
|           echo "${{ steps.load-secrets.outputs.ANDROID_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties | ||||
|           echo "${{ steps.load-secrets.outputs.ANDROID_GOOGLE_PLAY_JSON_BASE64 }}" | base64 -d > google-key.json | ||||
|           echo "${{ steps.load-secrets.outputs.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > release.keystore | ||||
|         working-directory: android | ||||
|  | ||||
|       - name: Install fastlane | ||||
| @@ -56,4 +68,4 @@ jobs: | ||||
|         env: | ||||
|           BUILD_NUMBER: ${{ github.run_number }} | ||||
|           # BUILD_NAME is implicitly available | ||||
|           GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} | ||||
|           GOOGLE_MAPS_API_KEY: ${{ steps.load-secrets.outputs.GOOGLE_MAPS_API_KEY }} | ||||
|   | ||||
							
								
								
									
										25
									
								
								frontend/.github/workflows/build_app_ios.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								frontend/.github/workflows/build_app_ios.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -30,12 +30,17 @@ jobs: | ||||
|           # remove the 'v' prefix from the tag name | ||||
|           echo "BUILD_NAME=${REF_NAME//v}" >> $GITHUB_ENV | ||||
|  | ||||
|       - name: Load secrets from github | ||||
|         run: | | ||||
|           echo "${{ secrets.IOS_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties | ||||
|           echo "${{ secrets.IOS_GOOGLE_PLAY_JSON_BASE64 }}" | base64 -d > google-key.json | ||||
|           echo "${{ secrets.IOS_KEYSTORE_BASE64 }}" | base64 -d > release.keystore | ||||
|         working-directory: ios | ||||
|       - name: Load secrets | ||||
|         id: load-secrets | ||||
|         uses: hashicorp/vault-action@v3 | ||||
|         with: | ||||
|           url: https://api.hashicorp.com | ||||
|           token: ${{ secrets.VAULT_TOKEN }} | ||||
|           secrets: | | ||||
|             secret/release GOOGLE_MAPS_API_KEY | GOOGLE_MAPS_API_KEY ; | ||||
|             secret/release IOS_ASC_KEY_ID | IOS_ASC_KEY_ID ; | ||||
|             secret/release IOS_ASC_ISSUER_ID | IOS_ASC_ISSUER_ID ; | ||||
|             secret/release IOS_ASC_KEY_P8 | IOS_ASC_KEY_P8 ; | ||||
|  | ||||
|       - name: Install fastlane | ||||
|         run: bundle install | ||||
| @@ -47,7 +52,7 @@ jobs: | ||||
|         env: | ||||
|           BUILD_NUMBER: ${{ github.run_number }} | ||||
|           # BUILD_NAME is implicitly available | ||||
|           GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} | ||||
|           IOS_ASC_KEY_ID: ${{ secrets.IOS_ASC_KEY_ID }} | ||||
|           IOS_ASC_ISSUER_ID: ${{ secrets.IOS_ASC_ISSUER_ID }} | ||||
|           IOS_ASC_KEY_P8: ${{ secrets.IOS_ASC_KEY_P8 }} | ||||
|           GOOGLE_MAPS_API_KEY: ${{ steps.load-secrets.outputs.GOOGLE_MAPS_API_KEY }} | ||||
|           IOS_ASC_KEY_ID: ${{ GOOGLE_MAPS_API_KEY.IOS_ASC_KEY_ID }} | ||||
|           IOS_ASC_ISSUER_ID: ${{ GOOGLE_MAPS_API_KEY.IOS_ASC_ISSUER_ID }} | ||||
|           IOS_ASC_KEY_P8: ${{ GOOGLE_MAPS_API_KEY.IOS_ASC_KEY_P8 }} | ||||
|   | ||||
| @@ -46,12 +46,17 @@ bundle exec fastlane <lane> | ||||
| ``` | ||||
| This is reused in the CI/CD pipeline to automate the deployment process. | ||||
|  | ||||
| Fastlane assumes mutliple secrets to be present as files in the platform directories. These are: | ||||
| - for android: | ||||
|     - `secrets.properties` used by gradle to load secrets needed at execution time | ||||
|     - `release.keystore` used by gradle to sign the apk | ||||
|     - `google-key.json` used by fastlane to authenticate with the Google Play Store | ||||
| - for ios: | ||||
|     - TODO | ||||
| Secrets used by fastlane are stored on hashicorp vault and are fetched by the CI/CD pipeline. See below. | ||||
|  | ||||
| These files are stored as secrets in the GitHub repository so that the CI pipeline can access them. | ||||
| ## Secrets | ||||
| These are mostly used by the CI/CD pipeline to deploy the application. The main usage for github actions is documented under [https://github.com/hashicorp/vault-action](https://github.com/hashicorp/vault-action). | ||||
| **Global secrets** are used for both versions of the app (android and ios).  | ||||
| - `GOOGLE_MAPS_API_KEY` is used to authenticate with the Google Maps API | ||||
|  | ||||
| **Platform-specific secrets** are used by the CI/CD pipeline to deploy to the respective app stores. | ||||
| - `ANDROID_KEYSTORE` is used to sign the android apk | ||||
| - `ANDROID_GOOGLE_KEY` is used to authenticate with the Google Play Store api | ||||
| - `IOS_GOOGLE_...` | ||||
| - `IOS_GOOGLE_...` | ||||
| - `IOS_GOOGLE_...` | ||||
| - `IOS_GOOGLE_...` | ||||
| @@ -63,11 +63,3 @@ Compared to the flutter template application, a few changes have to be made: | ||||
|         } | ||||
|     ``` | ||||
|  | ||||
|  | ||||
| ### Using the credentials in CI | ||||
| - Add the secret files to the repository secrets (e.g. `ANDROID_SECRETS_PROPERTIES`). | ||||
|  | ||||
| - temporarily write them back to files during the CI execution: | ||||
|     ```bash | ||||
|     echo {{ secrets.ANDROID_SECRETS }} >> android/secrets.properties | ||||
|     ``` | ||||
|   | ||||
| @@ -1,10 +1,7 @@ | ||||
| default_platform(:ios) | ||||
|  | ||||
| platform :ios do | ||||
|   before_all do | ||||
|     load_asc_api_token | ||||
|   end | ||||
|    | ||||
|  | ||||
|   desc "Load the App Store Connect API token" | ||||
|   lane :load_asc_api_token do | ||||
|     app_store_connect_api_key( | ||||
| @@ -16,17 +13,66 @@ platform :ios do | ||||
|     ) | ||||
|   end | ||||
|  | ||||
|   desc "Installs signing certificate in the keychain and downloads provisioning profiles from App Store Connect" | ||||
|   lane :prepare_signing do |options| | ||||
|     team_id = CredentialsManager::AppfileConfig.try_fetch_value(:team_id) | ||||
|     api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] | ||||
|  | ||||
|     keychain_name = "signing" | ||||
|     keychain_password = "temp" | ||||
|  | ||||
|     delete_keychain( | ||||
|       name: keychain_name | ||||
|     ) if File.exist? File.expand_path("~/Library/Keychains/#{keychain_name}-db") | ||||
|  | ||||
|     create_keychain( | ||||
|       name: keychain_name, | ||||
|       password: keychain_password, | ||||
|       default_keychain: true, | ||||
|       unlock: true, | ||||
|       timeout: 3600 | ||||
|     ) | ||||
|  | ||||
|     import_certificate( | ||||
|       certificate_path: ENV["SIGNING_KEY_FILE_PATH"], | ||||
|       certificate_password: ENV["SIGNING_KEY_PASSWORD"], | ||||
|       keychain_name: keychain_name, | ||||
|       keychain_password: keychain_password | ||||
|     ) | ||||
|  | ||||
|     # fetches and installs provisioning profiles from ASC | ||||
|     sigh( | ||||
|       adhoc: options[:adhoc], | ||||
|       api_key: api_key, | ||||
|       readonly: true | ||||
|     ) | ||||
|   end | ||||
|  | ||||
|  | ||||
|   desc "Deploy a new version to closed testing (testflight)" | ||||
|   lane :deploy_testing do | ||||
|     build_name = ENV["BUILD_NAME"] | ||||
|     build_number = ENV["BUILD_NUMBER"] | ||||
|  | ||||
|     api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] | ||||
|     sync_code_signing( | ||||
|       api_key: api_key, | ||||
|       type: "appstore", | ||||
|       readonly: true, | ||||
|     app_identifier = CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier) | ||||
|      | ||||
|     load_asc_api_token | ||||
|     prepare_signing | ||||
|  | ||||
|     profile_name = "App Provisioning Profile" # replace with the name of the profile to use for the build | ||||
|     output_name = "example-iOS" # specify the name of the .ipa file to generate | ||||
|     export_method = "app-store" # specify the export method | ||||
|      | ||||
|     # turn off automatic signing during build so correct code signing identity is guaranteed to be used | ||||
|     update_code_signing_settings( | ||||
|       use_automatic_signing: false, | ||||
|       targets: ["main-target"], # specify which targets to update code signing settings for | ||||
|       code_sign_identity: "Apple Distribution", # replace with name of code signing identity if different | ||||
|       bundle_identifier: app_identifier, | ||||
|       profile_name: profile_name, | ||||
|       build_configurations: ["Release"] # only toggle code signing settings for Release configurations | ||||
|     ) | ||||
|      | ||||
|  | ||||
|     sh( | ||||
|       "flutter", | ||||
| @@ -40,7 +86,11 @@ platform :ios do | ||||
|     # sign the app (whithout rebuilding it) | ||||
|     build_app( | ||||
|       skip_build_archive: true, | ||||
|       archive_path: "../build/ios/archive/Runner.xarchive" | ||||
|       archive_path: "../build/ios/archive/Runner.xcarchive" | ||||
|       provisioningProfiles: { | ||||
|         app_identifier => profile_name | ||||
|       } | ||||
|  | ||||
|     ) | ||||
|  | ||||
|     upload_to_testflight | ||||
|   | ||||
							
								
								
									
										10
									
								
								frontend/ios/local.env.sample
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/ios/local.env.sample
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| # SAMPLE env file that replicates the env in the CI/CD pipeline | ||||
| # DO NOT EDIT THIS FILE | ||||
| # Copy this file to local.env and edit the values to match your local environment | ||||
| IOS_ASC_KEY_ID="sample" | ||||
| IOS_ASC_ISSUER_ID="sample" | ||||
| IOS_ASC_KEY_P8="sample" | ||||
| SIGNING_KEY_FILE_PATH="sample" | ||||
| SIGNING_KEY_PASSWORD="sample" | ||||
| BUILD_NAME="sample" | ||||
| BUILD_NUMBER="sample" | ||||
		Reference in New Issue
	
	Block a user