diff --git a/.github/project_workflows/deploy_dev_firebase.yml b/.github/project_workflows/deploy_dev_firebase.yml new file mode 100644 index 00000000..467797bb --- /dev/null +++ b/.github/project_workflows/deploy_dev_firebase.yml @@ -0,0 +1,111 @@ +name: Deploy Dev Build To Firebase + +# SECRETS needed: +### SSH_PRIVATE_KEY for Match Repo +### MATCH_PASS +### FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 + +on: + push: + branches: [ develop ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Run SwiftLint + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 + with: + args: swiftlint --strict + + build: + name: Build + runs-on: macOS-latest + steps: + - uses: actions/checkout@v3 + # Set fetch-depth (default: 1) to get whole tree + with: + fetch-depth: 0 + + - name: Install SSH key + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Install Firebase-Tools + run: | + yarn global add firebase-tools + echo "$(yarn global bin)" >> $GITHUB_PATH + + - name: Setup ENV file + env: + ENV: ${{ secrets.ENV }} + run: | + touch .env + echo $ENV | base64 --decode > .env + + - name: Read Google Service Account + id: firebase_service_account + uses: timheuer/base64-to-file@v1.2 + with: + fileName: 'firebase_service_account.json' + encodedString: ${{ secrets.FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} + + - name: Bundle install + # if: steps.bundleCache.outputs.cache-hit != 'true' + run: bundle install + + - name: Run Arkana + run: bundle exec arkana + + - name: Cache Pods + uses: actions/cache@v2 + id: cocoapodCache + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Install Pods Dependencies + run: bundle exec pod install + shell: bash + + - name: Build and Test + run: bundle exec fastlane buildAndTestDev + + - name: Match Ad-hoc + run: bundle exec fastlane syncAdHocDevCodeSigning + env: + MATCH_PASSWORD: ${{ secrets.MATCH_PASS }} + + - name: Build App and Distribute to Firebase + run: bundle exec fastlane buildDevAndUploadToFirebase + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.firebase_service_account.outputs.filePath }} + + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ format('v{0}({1})-{2}', env.VERSION_NUMBER, env.BUILD_NUMBER, env.TAG_TYPE) }} + path: | + ${{ env.IPA_OUTPUT_PATH }} + ${{ env.DSYM_OUTPUT_PATH }} + env: + TAG_TYPE: Dev_Firebase + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/Models/EnvironmentKey.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/Models/EnvironmentKey.swift index 8fc4fa20..18b51f23 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/Models/EnvironmentKey.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/Models/EnvironmentKey.swift @@ -8,6 +8,7 @@ enum EnvironmentKey: String { case matchRepo = "MATCH_REPO" + case devFirebaseAppId = "DEV_FIREBASE_APP_ID" case stagingFirebaseAppId = "STAGING_FIREBASE_APP_ID" case teamId = "TEAM_ID" case apiKey = "API_KEY_ID" diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpTestFirebase.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpTestFirebase.swift index 5f23b513..63e93535 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpTestFirebase.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpTestFirebase.swift @@ -3,6 +3,7 @@ import Foundation struct SetUpTestFirebase { private let teamIdPlaceholder = "<#teamId#>" + private let devFirebaseAppIdPlaceholder = "<#devFirebaseAppId#>" private let stagingFirebaseAppIdPlaceholder = "<#stagingFirebaseAppId#>" private let firebaseTesterGroupsPlaceholder = "<#group1#>, <#group2#>" private let matchRepoPlaceholder = "git@github.com:{organization}/{repo}.git" @@ -11,11 +12,13 @@ struct SetUpTestFirebase { private let fileManager = FileManager.default let matchRepo: String + let devFirebaseAppId: String let stagingFirebaseAppId: String let teamId: String func perform() { fileManager.replaceAllOccurrences(of: teamIdPlaceholder, to: teamId) + fileManager.replaceAllOccurrences(of: devFirebaseAppIdPlaceholder, to: devFirebaseAppId) fileManager.replaceAllOccurrences(of: stagingFirebaseAppIdPlaceholder, to: stagingFirebaseAppId) fileManager.replaceAllOccurrences(of: firebaseTesterGroupsPlaceholder, to: firebaseTesterGroup) fileManager.replaceAllOccurrences(of: matchRepoPlaceholder, to: matchRepo) diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpiOSProject.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpiOSProject.swift index 2504a54b..98099216 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpiOSProject.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpiOSProject.swift @@ -6,11 +6,13 @@ class SetUpIOSProject { private let CONSTANT_PROJECT_NAME = "{PROJECT_NAME}" private let CONSTANT_BUNDLE_PRODUCTION = "{BUNDLE_ID_PRODUCTION}" private let CONSTANT_BUNDLE_STAGING = "{BUNDLE_ID_STAGING}" + private let CONSTANT_BUNDLE_DEV = "{BUNDLE_ID_DEV}" private let CONSTANT_MINIMUM_VERSION = "{TARGET_VERSION}" private let fileManager = FileManager.default private var bundleIdProduction = "" private var bundleIdStaging = "" + private var bundleIdDev = "" private var projectName = "" private var minimumVersion = "" private var interface: SetUpInterface.Interface? @@ -20,12 +22,14 @@ class SetUpIOSProject { init( bundleIdProduction: String = "", bundleIdStaging: String = "", + bundleIdDev: String = "", projectName: String = "", minimumVersion: String = "", interface: String = "" ) { self.bundleIdProduction = bundleIdProduction self.bundleIdStaging = bundleIdStaging + self.bundleIdDev = bundleIdDev self.projectName = projectName self.minimumVersion = minimumVersion self.interface = .init(interface) @@ -110,6 +114,15 @@ class SetUpIOSProject { onValidate: validatePackageName ) } + + if bundleIdDev.isEmpty { + tryMoveDown() + bundleIdDev = ask( + "Which is the bundle ID for the dev environment?", + note: "Ex: com.example.project.dev", + onValidate: validatePackageName + ) + } if projectName.isEmpty { tryMoveDown() @@ -159,6 +172,7 @@ class SetUpIOSProject { } private func replaceTextInFiles() throws { + fileManager.replaceAllOccurrences(of: CONSTANT_BUNDLE_DEV, to: bundleIdDev) fileManager.replaceAllOccurrences(of: CONSTANT_BUNDLE_STAGING, to: bundleIdStaging) fileManager.replaceAllOccurrences(of: CONSTANT_BUNDLE_PRODUCTION, to: bundleIdProduction) fileManager.replaceAllOccurrences(of: CONSTANT_PROJECT_NAME, to: projectNameNoSpace) diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/iOSTemplateMaker.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/iOSTemplateMaker.swift index 7050547e..a38cc048 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/iOSTemplateMaker.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/iOSTemplateMaker.swift @@ -48,11 +48,13 @@ extension iOSTemplateMaker { mutating func run() { let envMatchRepo = EnvironmentValue.value(for: .matchRepo).string + let envDevFirebaseAppId = EnvironmentValue.value(for: .devFirebaseAppId).string let envStagingFirebaseAppId = EnvironmentValue.value(for: .stagingFirebaseAppId).string let envTeamId = EnvironmentValue.value(for: .teamId).string SetUpTestFirebase( matchRepo: envMatchRepo, + devFirebaseAppId: envDevFirebaseAppId, stagingFirebaseAppId: envStagingFirebaseAppId, teamId: envTeamId ).perform() diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index 155852e1..5c430a7f 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -16,12 +16,15 @@ enum Constant { // MARK: - Firebase + static let devFirebaseAppId = "<#devFirebaseAppId#>" static let stagingFirebaseAppId = "<#stagingFirebaseAppId#>" static let productionFirebaseAppId = "<#productionFirebaseAppId#>" static let firebaseTesterGroups = "<#group1#>, <#group2#>" // MARK: - Match + static let appleDevUserName = "<#userName#>" + static let appleDevTeamId = "<#teamId#>" static let appleStagingUserName = "<#userName#>" static let appleStagingTeamId = "<#teamId#>" static let appleProductionUserName = "<#userName#>" @@ -67,6 +70,7 @@ enum Constant { // MARK: - Project + static let devBundleId = "{BUNDLE_ID_DEV}" static let stagingBundleId = "{BUNDLE_ID_STAGING}" static let productionBundleId = "{BUNDLE_ID_PRODUCTION}" static let projectName = "{PROJECT_NAME}" @@ -89,6 +93,7 @@ extension Constant { enum Environment: String { + case dev = "Dev" case staging = "Staging" case production = "Production" @@ -96,13 +101,14 @@ extension Constant { var scheme: String { switch self { - case .staging: return "\(Constant.projectName) \(rawValue)".trimmed + case .dev, .staging: return "\(Constant.projectName) \(rawValue)".trimmed case .production: return Constant.projectName.trimmed } } var bundleId: String { switch self { + case .dev: return Constant.devBundleId case .staging: return Constant.stagingBundleId case .production: return Constant.productionBundleId } @@ -110,6 +116,7 @@ extension Constant { var firebaseAppId: String { switch self { + case .dev: return Constant.devFirebaseAppId case .staging: return Constant.stagingFirebaseAppId case .production: return Constant.productionFirebaseAppId } @@ -119,6 +126,7 @@ extension Constant { let infoName = "GoogleService-Info.plist" let googleServiceFolder = "./\(Constant.projectName)/Configurations/Plists/GoogleService" switch self { + case .dev: return "\(googleServiceFolder)/Dev/\(infoName)" case .staging: return "\(googleServiceFolder)/Staging/\(infoName)" case .production: return "\(googleServiceFolder)/Production/\(infoName)" } @@ -131,6 +139,7 @@ extension Constant { var appleUsername: String { switch self { + case .dev: return Constant.appleDevUserName case .staging: return Constant.appleStagingUserName case .production: return Constant.appleProductionUserName } @@ -138,6 +147,7 @@ extension Constant { var appleTeamId: String { switch self { + case .dev: return appleDevTeamId case .staging: return Constant.appleStagingTeamId case .production: return Constant.appleProductionTeamId } diff --git a/fastlane/Fastfile.swift b/fastlane/Fastfile.swift index d8e1f09d..8635a5a8 100644 --- a/fastlane/Fastfile.swift +++ b/fastlane/Fastfile.swift @@ -27,6 +27,14 @@ class Fastfile: LaneFile { environment: .production ) } + + func syncAdHocDevCodeSigningLane() { + desc("Sync the Ad Hoc match signing for the Dev build") + Match.syncCodeSigning( + type: .adHoc, + environment: .dev + ) + } func syncAdHocStagingCodeSigningLane() { desc("Sync the Ad Hoc match signing for the Staging build") @@ -58,6 +66,12 @@ class Fastfile: LaneFile { } // MARK: - Build + + func buildAdHocDevLane() { + desc("Build ad-hoc dev") + Build.adHoc(environment: .dev) + } + func buildAdHocStagingLane() { desc("Build ad-hoc staging") @@ -75,6 +89,22 @@ class Fastfile: LaneFile { } // MARK: - Upload builds to Firebase and AppStore + + func buildDevAndUploadToFirebaseLane() { + desc("Build Dev app and upload to Firebase") + + setAppVersion() + bumpBuild() + + buildAdHocDevLane() + + // TODO: - Make release notes + Distribution.uploadToFirebase(environment: .staging, releaseNotes: "") + + Symbol.uploadToCrashlytics(environment: .dev) + + Build.saveBuildContextToCI() + } func buildStagingAndUploadToFirebaseLane() { desc("Build Staging app and upload to Firebase") @@ -154,6 +184,14 @@ class Fastfile: LaneFile { ) } + func buildAndTestDevLane() { + desc("Build and Test dev project") + Test.buildAndTest( + environment: .dev, + devices: Constant.devices + ) + } + func setUpTestProjectLane() { desc("Disable Exempt Encryption") Test.disableExemptEncryption()