Build pipeline for both platforms #42
| @@ -39,11 +39,23 @@ jobs: | |||||||
|           # remove the 'v' prefix from the tag name |           # remove the 'v' prefix from the tag name | ||||||
|           echo "BUILD_NAME=${REF_NAME//v}" >> $GITHUB_ENV |           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: | |         run: | | ||||||
|           echo "${{ secrets.ANDROID_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties |           echo "${{ steps.load-secrets.outputs.ANDROID_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties | ||||||
|           echo "${{ secrets.ANDROID_GOOGLE_PLAY_JSON_BASE64 }}" | base64 -d > google-key.json |           echo "${{ steps.load-secrets.outputs.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_KEYSTORE_BASE64 }}" | base64 -d > release.keystore | ||||||
|         working-directory: android |         working-directory: android | ||||||
|  |  | ||||||
|       - name: Install fastlane |       - name: Install fastlane | ||||||
| @@ -56,4 +68,4 @@ jobs: | |||||||
|         env: |         env: | ||||||
|           BUILD_NUMBER: ${{ github.run_number }} |           BUILD_NUMBER: ${{ github.run_number }} | ||||||
|           # BUILD_NAME is implicitly available |           # 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 |           # remove the 'v' prefix from the tag name | ||||||
|           echo "BUILD_NAME=${REF_NAME//v}" >> $GITHUB_ENV |           echo "BUILD_NAME=${REF_NAME//v}" >> $GITHUB_ENV | ||||||
|  |  | ||||||
|       - name: Load secrets from github |       - name: Load secrets | ||||||
|         run: | |         id: load-secrets | ||||||
|           echo "${{ secrets.IOS_SECRET_PROPERTIES_BASE64 }}" | base64 -d > secrets.properties |         uses: hashicorp/vault-action@v3 | ||||||
|           echo "${{ secrets.IOS_GOOGLE_PLAY_JSON_BASE64 }}" | base64 -d > google-key.json |         with: | ||||||
|           echo "${{ secrets.IOS_KEYSTORE_BASE64 }}" | base64 -d > release.keystore |           url: https://api.hashicorp.com | ||||||
|         working-directory: ios |           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 |       - name: Install fastlane | ||||||
|         run: bundle install |         run: bundle install | ||||||
| @@ -47,7 +52,7 @@ jobs: | |||||||
|         env: |         env: | ||||||
|           BUILD_NUMBER: ${{ github.run_number }} |           BUILD_NUMBER: ${{ github.run_number }} | ||||||
|           # BUILD_NAME is implicitly available |           # 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 }} | ||||||
|           IOS_ASC_KEY_ID: ${{ secrets.IOS_ASC_KEY_ID }} |           IOS_ASC_KEY_ID: ${{ GOOGLE_MAPS_API_KEY.IOS_ASC_KEY_ID }} | ||||||
|           IOS_ASC_ISSUER_ID: ${{ secrets.IOS_ASC_ISSUER_ID }} |           IOS_ASC_ISSUER_ID: ${{ GOOGLE_MAPS_API_KEY.IOS_ASC_ISSUER_ID }} | ||||||
|           IOS_ASC_KEY_P8: ${{ secrets.IOS_ASC_KEY_P8 }} |           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. | 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: | Secrets used by fastlane are stored on hashicorp vault and are fetched by the CI/CD pipeline. See below. | ||||||
| - 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 |  | ||||||
|  |  | ||||||
| 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) | default_platform(:ios) | ||||||
|  |  | ||||||
| platform :ios do | platform :ios do | ||||||
|   before_all do |  | ||||||
|     load_asc_api_token |  | ||||||
|   end |  | ||||||
|    |  | ||||||
|   desc "Load the App Store Connect API token" |   desc "Load the App Store Connect API token" | ||||||
|   lane :load_asc_api_token do |   lane :load_asc_api_token do | ||||||
|     app_store_connect_api_key( |     app_store_connect_api_key( | ||||||
| @@ -16,17 +13,66 @@ platform :ios do | |||||||
|     ) |     ) | ||||||
|   end |   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)" |   desc "Deploy a new version to closed testing (testflight)" | ||||||
|   lane :deploy_testing do |   lane :deploy_testing do | ||||||
|     build_name = ENV["BUILD_NAME"] |     build_name = ENV["BUILD_NAME"] | ||||||
|     build_number = ENV["BUILD_NUMBER"] |     build_number = ENV["BUILD_NUMBER"] | ||||||
|  |  | ||||||
|     api_key = lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] |     app_identifier = CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier) | ||||||
|     sync_code_signing( |      | ||||||
|       api_key: api_key, |     load_asc_api_token | ||||||
|       type: "appstore", |     prepare_signing | ||||||
|       readonly: true, |  | ||||||
|  |     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( |     sh( | ||||||
|       "flutter", |       "flutter", | ||||||
| @@ -40,7 +86,11 @@ platform :ios do | |||||||
|     # sign the app (whithout rebuilding it) |     # sign the app (whithout rebuilding it) | ||||||
|     build_app( |     build_app( | ||||||
|       skip_build_archive: true, |       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 |     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