From 832aac5ad34ff77f45cb043540e22c1de18aca20 Mon Sep 17 00:00:00 2001 From: Thomas Labrecque Date: Tue, 4 Jun 2024 13:20:15 -0400 Subject: [PATCH 1/2] fix: CI Artifact Names and Android --- build/azure-pipelines.yml | 14 ++++++++------ build/stage-build.yml | 8 +++++++- build/steps-build-android.yml | 4 ++++ build/steps-build-ios.yml | 4 ++-- build/variables.yml | 5 +++-- doc/AzurePipelines.md | 10 +++++----- src/cli/CHANGELOG.md | 3 +++ 7 files changed, 32 insertions(+), 16 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index bd94ab8..dc022db 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -48,13 +48,15 @@ stages: androidArtifactName: 'GeneratedAndroidArtifact_Staging' androidKeyPropertiesFile: $(InternalKeyProperties) androidVariableGroup: 'FlutterApplicationTemplate.Distribution.Internal.Android' + iosArtifactName: 'GeneratediOSArtifact_Staging' iosProvisioningProfileFile: $(InternalProvisioningProfile) iosExportOptionsFile: $(InternalExportOptions) iosCertificateFile: $(InternalCertificate) iosVariableGroup: 'FlutterApplicationTemplate.Distribution.Internal.iOS' + releaseNotesArtifactName: 'GeneratedReleaseNotes_Staging' - stage: Publish_Template_Package - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false'), eq(variables['IsReleaseBranch'], 'true')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false'), eq(variables['IsReleaseBranch'], 'true')) dependsOn: - CopyTool_GeneratedApp - Build_Staging_GeneratedApp @@ -80,7 +82,7 @@ stages: BannerVersionNameText: 'STAGING' - stage: AppCenter_TestFlight_Staging - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) dependsOn: Build_Staging jobs: - template: stage-release-appcenter.yml @@ -100,7 +102,7 @@ stages: - stage: Build_Production dependsOn: Build_Staging - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) jobs: - template: stage-build.yml parameters: @@ -114,7 +116,7 @@ stages: iosVariableGroup: 'FlutterApplicationTemplate.Distribution.AppStore' - stage: AppCenter_Production - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) dependsOn: Build_Production jobs: - template: stage-release-appcenter.yml @@ -128,7 +130,7 @@ stages: appCenterDistributionGroup: $(AppCenterDistributionGroup) - stage: AppStore - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) dependsOn: Build_Production jobs: - template: stage-release-appstore.yml @@ -137,7 +139,7 @@ stages: deploymentEnvironment: AppStore - stage: GooglePlay - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) dependsOn: Build_Production jobs: - template: stage-release-googleplay.yml diff --git a/build/stage-build.yml b/build/stage-build.yml index 3eef75b..96c2bf1 100644 --- a/build/stage-build.yml +++ b/build/stage-build.yml @@ -8,6 +8,9 @@ - name: androidArtifactName type: string default: $(AndroidArtifactName)_$(applicationEnvironment) +- name: iosArtifactName + type: string + default: $(iOSArtifactName)_$(applicationEnvironment) - name: androidKeyStoreFile type: string default: '' @@ -35,6 +38,9 @@ - name: removeHyperlinksFromReleaseNotes type: boolean default: false +- name: releaseNotesArtifactName + type: string + default: $(ReleaseNotesArtifactName) - name: pathToSrc type: string default: '$(Build.SourcesDirectory)/src' @@ -50,7 +56,7 @@ jobs: pool: vmImage : $(windowsHostedAgentImage) variables: - ArtifactName: ReleaseNotes_${{ parameters.artifactName }} + ArtifactName: ${{ parameters.releaseNotesArtifactName }} ApplicationEnvironment: ${{ parameters.applicationEnvironment }} steps: - template: steps-build-release-notes.yml diff --git a/build/steps-build-android.yml b/build/steps-build-android.yml index 0eef577..98351ea 100644 --- a/build/steps-build-android.yml +++ b/build/steps-build-android.yml @@ -44,6 +44,8 @@ steps: inputs: secureFile: ${{ parameters.androidKeyStorePropertiesFile }} +# TODO: If Production -> Download the google-services.json for production. + # The file copied inside the source directory must be aligned with the name chosen in '/src/app/lib/android/app/build.gradle' where 'key.properties' is loaded. - task: PowerShell@2 displayName: Copy Signing Configuration Files @@ -56,6 +58,8 @@ steps: Copy-Item -Path '$(keyStore.secureFilePath)' -Destination '${{ parameters.pathToSrc }}\app\android\app\${{ parameters.androidKeystoreFile }}' Write-Host 'Key store copied to ${{ parameters.pathToSrc }}/app/android/app/${{ parameters.androidKeystoreFile }}.' +# TODO: If Production -> Copy downloaded the google-services.json for production in the Android application folder. + - template: templates/flutter-prepare.yml parameters: projectDirectory: '${{ parameters.pathToSrc }}/app' diff --git a/build/steps-build-ios.yml b/build/steps-build-ios.yml index 89a2a66..7b007e7 100644 --- a/build/steps-build-ios.yml +++ b/build/steps-build-ios.yml @@ -96,7 +96,7 @@ steps: condition: failed() - task: Xcode@5 - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) displayName: 'Xcode Archive iOS' inputs: actions: 'archive' @@ -115,7 +115,7 @@ steps: provisioningProfileName: '$(provisioningProfile.provisioningProfileName)' - task: CopyFiles@2 - condition: and(succeeded(), eq(variables['IsLightBuild'], 'false')) + condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) displayName: 'Copy Binary Files' inputs: sourceFolder: '${{ parameters.pathToSrc }}/app' diff --git a/build/variables.yml b/build/variables.yml index 1956e6e..14a0a44 100644 --- a/build/variables.yml +++ b/build/variables.yml @@ -71,9 +71,10 @@ iOSArtifactName: iOS WindowsArtifactName: Windows TestsArtifactName: Tests + ReleaseNotesArtifactName: ReleaseNotes - # For Application.Building.Light optimizations. - IsLightBuild: $[eq(variables['Build.Reason'], 'PullRequest')] + # To know if it's a Pull Request build. + IsPullRequestBuild: $[eq(variables['Build.Reason'], 'PullRequest')] # Pipeline configuration (Disable shallow fetch). # See https://dev.to/kkazala/azure-devops-pipelines-shallow-fetch-1-is-now-default-4656 for more details. diff --git a/doc/AzurePipelines.md b/doc/AzurePipelines.md index 119a975..a58a6f3 100644 --- a/doc/AzurePipelines.md +++ b/doc/AzurePipelines.md @@ -24,14 +24,14 @@ It also runs automated tests during the build steps. ### Pull request runs Due to the length of mobile builds, pipelines are configured to behave a little differently when building in a context of **pull request (PR) build validation**. -To reduce the build time, some stages and steps are disabled for PR builds. -This requires a specific variable called `IsLightBuild` to be set, hence why it is appearing in the pipeline. +To reduce the CI/CD utilization time, some stages and steps are disabled for PR builds. +This requires a specific variable called `IsPullRequestBuild` to be set, hence why it is appearing in the pipeline. -Also, all release stages are disabled in the context of PR build validation because, with the optimizations differences, the resulting application would not represent the real thing. +Also, all release stages are disabled in the context of PR build validation. ### Release runs -Pipeline runs **triggered on the main branch** don't qualify for `IsLightBuild` and build the application with the goal of releasing it. +Pipeline runs **triggered on the main branch** don't qualify for `IsPullRequestBuild` and build the application with the goal of releasing it. All stages are therefore enabled. ## Stages and Steps @@ -67,7 +67,7 @@ This is where the exact build steps are defined. These vary depending on the pla 1. Install and run [GitVersion](https://gitversion.net/) to calculate the semantic version based on the Git history. 1. Install the proper signing certificates (depending on the platform). 1. Build the application. -1. Archive the application (if on iOS and depending on `IsLightBuild`). +1. Archive the application (if on iOS and depending on `IsPullRequestBuild`). 1. Run the tests and publish both the test results and the code coverage results. 1. Push the built artifacts (.ipa, .aab, release notes, etc.). 1. Cleanup. diff --git a/src/cli/CHANGELOG.md b/src/cli/CHANGELOG.md index 892c341..f6cbb4a 100644 --- a/src/cli/CHANGELOG.md +++ b/src/cli/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) Prefix your items with `(Template)` if the change is about the template and not the resulting application. +## 0.19.4 +- Fix CI/CD artifact name for iOS. + ## 0.19.2 - Cleanup documentation. From c2fcd13018eb4ab634627078c01c8c466fe6267a Mon Sep 17 00:00:00 2001 From: Marc-Antoine Soucy Date: Wed, 5 Jun 2024 11:43:41 -0400 Subject: [PATCH 2/2] ci: inject Firebase configuration --- build/azure-pipelines.yml | 9 +++++ build/stage-build.yml | 14 ++++++- build/steps-build-android.yml | 20 ++++++---- build/steps-build-ios.yml | 19 +++++++--- build/templates/replace-firebase-config.yml | 42 +++++++++++++++++++++ build/variables.yml | 8 ++++ src/app/pubspec.lock | 28 +++++++------- src/cli/CHANGELOG.md | 3 +- 8 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 build/templates/replace-firebase-config.yml diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index dc022db..f576a1b 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -54,6 +54,9 @@ stages: iosCertificateFile: $(InternalCertificate) iosVariableGroup: 'FlutterApplicationTemplate.Distribution.Internal.iOS' releaseNotesArtifactName: 'GeneratedReleaseNotes_Staging' + firebaseJsonFile: $(InternalFirebaseJson) + firebaseOptionsDartFile: $(InternalFirebaseOptionsDart) + googleServicesJsonFile: $(InternalGoogleServicesJson) - stage: Publish_Template_Package condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false'), eq(variables['IsReleaseBranch'], 'true')) @@ -80,6 +83,9 @@ stages: iosCertificateFile: $(InternalCertificate) iosVariableGroup: 'FlutterApplicationTemplate.Distribution.Internal.iOS' BannerVersionNameText: 'STAGING' + firebaseJsonFile: $(InternalFirebaseJson) + firebaseOptionsDartFile: $(InternalFirebaseOptionsDart) + googleServicesJsonFile: $(InternalGoogleServicesJson) - stage: AppCenter_TestFlight_Staging condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) @@ -114,6 +120,9 @@ stages: iosExportOptionsFile: $(AppStoreExportOptions) iosCertificateFile: $(AppStoreCertificate) iosVariableGroup: 'FlutterApplicationTemplate.Distribution.AppStore' + firebaseJsonFile: $(FirebaseJson) + firebaseOptionsDartFile: $(FirebaseOptionsDart) + googleServicesJsonFile: $(GoogleServicesJson) - stage: AppCenter_Production condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false')) diff --git a/build/stage-build.yml b/build/stage-build.yml index 96c2bf1..f3e2002 100644 --- a/build/stage-build.yml +++ b/build/stage-build.yml @@ -50,6 +50,12 @@ - name: BannerVersionNameText type: string default: '' +- name: firebaseJsonFile + type: string +- name: firebaseOptionsDartFile + type: string +- name: googleServicesJsonFile + type: string jobs: - job: OnWindows_ReleaseNotes @@ -111,6 +117,9 @@ jobs: projectName: ${{ parameters.projectName }} androidKeystoreFile: ${{ parameters.androidKeyStoreFile }} androidKeyStorePropertiesFile: ${{ parameters.androidKeyPropertiesFile }} + firebaseJsonFile: '${{ parameters.firebaseJsonFile }}' + firebaseOptionsDartFile: '${{ parameters.firebaseOptionsDartFile }}' + googleServicesJsonFile: '${{ parameters.googleServicesJsonFile }}' - job: OnMac_iOS pool: @@ -132,4 +141,7 @@ jobs: iosCertificatePassword: ${{ parameters.iosCertificatePassword }} iosProvisioningProfileFile: ${{ parameters.iosProvisioningProfileFile }} iosExportOptionsFile: ${{ parameters.iosExportOptionsFile }} - BannerVersionNameText: ${{ parameters.BannerVersionNameText }} \ No newline at end of file + BannerVersionNameText: ${{ parameters.BannerVersionNameText }} + firebaseJsonFile: '${{ parameters.firebaseJsonFile }}' + firebaseOptionsDartFile: '${{ parameters.firebaseOptionsDartFile }}' + googleServicesJsonFile: '${{ parameters.googleServicesJsonFile }}' \ No newline at end of file diff --git a/build/steps-build-android.yml b/build/steps-build-android.yml index 98351ea..30600cc 100644 --- a/build/steps-build-android.yml +++ b/build/steps-build-android.yml @@ -1,16 +1,19 @@ parameters: - name: androidKeystoreFile type: string - default: '' - name: androidKeyStorePropertiesFile type: string - default: '' - name: pathToSrc type: string - default: '' - name: projectName type: string default: '$(ProjectName)' +- name: firebaseJsonFile + type: string +- name: firebaseOptionsDartFile + type: string +- name: googleServicesJsonFile + type: string steps: #-if false @@ -31,6 +34,13 @@ steps: - template: templates/flutter-install.yml +- template : templates/replace-firebase-config.yml + parameters: + pathToSrc: '${{ parameters.pathToSrc }}' + firebaseJsonFile: '${{ parameters.firebaseJsonFile }}' + firebaseOptionsDartFile: '${{ parameters.firebaseOptionsDartFile }}' + googleServicesJsonFile: '${{ parameters.googleServicesJsonFile }}' + - task: DownloadSecureFile@1 name: keyStore displayName: "Download Keystore from Secure Files" @@ -44,8 +54,6 @@ steps: inputs: secureFile: ${{ parameters.androidKeyStorePropertiesFile }} -# TODO: If Production -> Download the google-services.json for production. - # The file copied inside the source directory must be aligned with the name chosen in '/src/app/lib/android/app/build.gradle' where 'key.properties' is loaded. - task: PowerShell@2 displayName: Copy Signing Configuration Files @@ -58,8 +66,6 @@ steps: Copy-Item -Path '$(keyStore.secureFilePath)' -Destination '${{ parameters.pathToSrc }}\app\android\app\${{ parameters.androidKeystoreFile }}' Write-Host 'Key store copied to ${{ parameters.pathToSrc }}/app/android/app/${{ parameters.androidKeystoreFile }}.' -# TODO: If Production -> Copy downloaded the google-services.json for production in the Android application folder. - - template: templates/flutter-prepare.yml parameters: projectDirectory: '${{ parameters.pathToSrc }}/app' diff --git a/build/steps-build-ios.yml b/build/steps-build-ios.yml index 7b007e7..d342b19 100644 --- a/build/steps-build-ios.yml +++ b/build/steps-build-ios.yml @@ -1,25 +1,25 @@ parameters: - name: iosCertificateFile type: string - default: '' - name: iosCertificatePassword type: string - default: '' - name: iosProvisioningProfileFile type: string - default: '' - name: iosExportOptionsFile type: string - default: '' - name: pathToSrc type: string - default: '' - name: projectName type: string default: '$(ProjectName)' - name: BannerVersionNameText type: string - default: '' +- name: firebaseJsonFile + type: string +- name: firebaseOptionsDartFile + type: string +- name: googleServicesJsonFile + type: string steps: #-if false @@ -47,6 +47,13 @@ steps: provisioningProfileLocation: 'secureFiles' provProfileSecureFile: ${{ parameters.iosProvisioningProfileFile }} +- template : templates/replace-firebase-config.yml + parameters: + pathToSrc: '${{ parameters.pathToSrc }}' + firebaseJsonFile: '${{ parameters.firebaseJsonFile }}' + firebaseOptionsDartFile: '${{ parameters.firebaseOptionsDartFile }}' + googleServicesJsonFile: '${{ parameters.googleServicesJsonFile }}' + # Flutter project signing process goes through exportOptions.plist file which holds secure information about signing. - task: DownloadSecureFile@1 name: exportOptions diff --git a/build/templates/replace-firebase-config.yml b/build/templates/replace-firebase-config.yml new file mode 100644 index 0000000..67edea4 --- /dev/null +++ b/build/templates/replace-firebase-config.yml @@ -0,0 +1,42 @@ +parameters: +- name: pathToSrc + type: string +- name: firebaseJsonFile + type: string +- name: firebaseOptionsDartFile + type: string +- name: googleServicesJsonFile + type: string + +steps: + - task: DownloadSecureFile@1 + name: firebaseJson + displayName: "Download Keystore from Secure Files" + inputs: + secureFile: ${{ parameters.firebaseJsonFile }} + + - task: DownloadSecureFile@1 + name: firebaseOptionsDart + displayName: "Download Keystore from Secure Files" + inputs: + secureFile: ${{ parameters.firebaseOptionsDartFile }} + + - task: DownloadSecureFile@1 + name: googleServicesJson + displayName: "Download Keystore from Secure Files" + inputs: + secureFile: ${{ parameters.googleServicesJsonFile }} + + - task: PowerShell@2 + displayName: Copy Firebase Configuration Files + inputs: + targetType: 'inline' + script: | + Copy-Item -Path '$(firebaseJson.secureFilePath)' -Destination '${{ parameters.pathToSrc }}\app\firebase.json' + Write-Host 'Firebase.json copied to ${{ parameters.pathToSrc }}/app/firebase.json' + + Copy-Item -Path '$(firebaseOptionsDart.secureFilePath)' -Destination '${{ parameters.pathToSrc }}\app\lib\firebase_options.dart' + Write-Host 'FirebaseOptions.Dart copied to ${{ parameters.pathToSrc }}/app/lib/firebase_options.dart' + + Copy-Item -Path '$(googleServicesJson.secureFilePath)' -Destination '${{ parameters.pathToSrc }}\app\android\app\google-services.json' + Write-Host 'GoogleServices.json copied to ${{ parameters.pathToSrc }}\app\android\app\google-services.json' diff --git a/build/variables.yml b/build/variables.yml index 14a0a44..32800f4 100644 --- a/build/variables.yml +++ b/build/variables.yml @@ -40,6 +40,14 @@ AppStoreExportOptions: com.nventive.flutterapptemplate.exportOptions.plist # This is the export options file for the AppStore (Production distribution) builds. AppStoreCertificate: nventive.p12 # This is the client production certificate used to sign AppStore builds. + # Firebase + InternalFirebaseJson: firebase-flutter-internal.json + InternalFirebaseOptionsDart: firebase_options-flutter-internal.dart + InternalGoogleServicesJson: google-services-flutter-internal.json + FirebaseJson: firebase-flutter.json + FirebaseOptionsDart: firebase_options-flutter.dart + GoogleServicesJson: google-services-flutter.json + # Prerequisites - Service connections. # Make sure you have the following service connections in your Azure pipeline library. GooglePlayServiceConnection: GooglePlay-nventive-ApplicationTemplate diff --git a/src/app/pubspec.lock b/src/app/pubspec.lock index 7b804cc..74e2607 100644 --- a/src/app/pubspec.lock +++ b/src/app/pubspec.lock @@ -496,10 +496,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -536,26 +536,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -600,10 +600,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -1077,10 +1077,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" timezone: dependency: transitive description: @@ -1197,10 +1197,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" watcher: dependency: transitive description: diff --git a/src/cli/CHANGELOG.md b/src/cli/CHANGELOG.md index f6cbb4a..e62ab3a 100644 --- a/src/cli/CHANGELOG.md +++ b/src/cli/CHANGELOG.md @@ -5,8 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) Prefix your items with `(Template)` if the change is about the template and not the resulting application. -## 0.19.4 +## 0.19.3 - Fix CI/CD artifact name for iOS. +- Firebase configuration is now injected by CI/CD. ## 0.19.2 - Cleanup documentation.