diff --git a/.github/workflows/deploy_app_store.yml b/.github/workflows/deploy_app_store.yml index c065d75b..a054675e 100644 --- a/.github/workflows/deploy_app_store.yml +++ b/.github/workflows/deploy_app_store.yml @@ -97,3 +97,8 @@ jobs: ${{ env.DSYM_OUTPUT_PATH }} env: TAG_TYPE: App_Store + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/workflows/deploy_production_firebase.yml b/.github/workflows/deploy_production_firebase.yml index 2b9209c9..ad71aed9 100644 --- a/.github/workflows/deploy_production_firebase.yml +++ b/.github/workflows/deploy_production_firebase.yml @@ -91,3 +91,8 @@ jobs: ${{ env.DSYM_OUTPUT_PATH }} env: TAG_TYPE: Production_Firebase + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/workflows/deploy_staging_firebase.yml b/.github/workflows/deploy_staging_firebase.yml index d5ac0b71..fff0630a 100644 --- a/.github/workflows/deploy_staging_firebase.yml +++ b/.github/workflows/deploy_staging_firebase.yml @@ -97,3 +97,8 @@ jobs: ${{ env.DSYM_OUTPUT_PATH }} env: TAG_TYPE: Staging_Firebase + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/workflows/test_upload_build_to_firebase.yml b/.github/workflows/test_upload_build_to_firebase.yml index 3ccdfb6c..5f8d2655 100644 --- a/.github/workflows/test_upload_build_to_firebase.yml +++ b/.github/workflows/test_upload_build_to_firebase.yml @@ -78,3 +78,8 @@ jobs: ${{ env.DSYM_OUTPUT_PATH }} env: TAG_TYPE: Staging_Firebase + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/workflows/test_upload_build_to_test_flight.yml b/.github/workflows/test_upload_build_to_test_flight.yml index 1606fdab..28baf490 100644 --- a/.github/workflows/test_upload_build_to_test_flight.yml +++ b/.github/workflows/test_upload_build_to_test_flight.yml @@ -47,7 +47,7 @@ jobs: run: sh make.sh --bundle-id co.nimblehq.ios.templates --bundle-id-staging co.nimblehq.ios.templates.staging --project-name TemplateApp --interface UIKit - name: Start Setup Script for Template App TestFlight Upload - run: sh set_up_test_testflight.sh + run: cat Scripts/Swift/SetUpTestTestFlight.swift Scripts/Swift/Extensions/FileManager+Utils.swift | swift - env: MATCH_REPO: ${{ secrets.MATCH_REPO }} API_KEY_ID: ${{ secrets.API_KEY_ID }} @@ -70,3 +70,8 @@ jobs: ISSUER_ID: ${{ secrets.ISSUER_ID }} SKIP_FIREBASE_DSYM: "true" BUMP_APP_STORE_BUILD_NUMBER: "true" + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/Scripts/Swift/Extensions/FileManager+Utils.swift b/Scripts/Swift/Extensions/FileManager+Utils.swift index c55c4e4a..6fd75e11 100644 --- a/Scripts/Swift/Extensions/FileManager+Utils.swift +++ b/Scripts/Swift/Extensions/FileManager+Utils.swift @@ -34,6 +34,28 @@ extension FileManager { } } + func copy(file: String, to destination: String) { + let currentDirectory = currentDirectoryPath + do { + try copyItem( + atPath: "\(currentDirectory)/\(file)", + toPath:"\(currentDirectory)/\(destination)" + ) + } catch { + print("Error \(error)") + } + } + + func createFile(name: String, at directory: String) { + let currentDirectory = currentDirectoryPath + do { + try createDirectory(atPath: "\(currentDirectory)/\(directory)", withIntermediateDirectories: true, attributes: nil) + } catch { + print("Error \(error)") + } + createFile(atPath: "\(currentDirectory)\(directory)\(name)", contents: nil) + } + func removeItems(in directory: String) { let currentDirectory = currentDirectoryPath do { @@ -63,9 +85,11 @@ extension FileManager { if let enumerator = enumerator( at: url, includingPropertiesForKeys: [.isRegularFileKey], - options: [.skipsHiddenFiles, .skipsPackageDescendants] + options: [.skipsPackageDescendants] ) { + let hiddenFolderRegex = "\(directory.replacingOccurrences(of: "/", with: "\\/"))\\/\\..*\\/" for case let fileURL as URL in enumerator { + guard !(fileURL.relativePath ~= hiddenFolderRegex) else { continue } let fileAttributes = try? fileURL.resourceValues(forKeys:[.isRegularFileKey]) guard fileAttributes?.isRegularFile ?? false else { continue } files.append(fileURL) diff --git a/Scripts/Swift/Extensions/String+Utils.swift b/Scripts/Swift/Extensions/String+Utils.swift new file mode 100644 index 00000000..76ccddaf --- /dev/null +++ b/Scripts/Swift/Extensions/String+Utils.swift @@ -0,0 +1,8 @@ +extension String { + + static func ~= (lhs: String, rhs: String) -> Bool { + guard let regex = try? NSRegularExpression(pattern: rhs) else { return false } + let range = NSRange(location: 0, length: lhs.utf16.count) + return regex.firstMatch(in: lhs, options: [], range: range) != nil + } +} diff --git a/Scripts/Swift/SetUpCICDService.swift b/Scripts/Swift/SetUpCICDService.swift index bc8ab07b..b94d97d4 100644 --- a/Scripts/Swift/SetUpCICDService.swift +++ b/Scripts/Swift/SetUpCICDService.swift @@ -1,50 +1,51 @@ -#!/usr/bin/swift - -import Foundation - -let fileManager = FileManager.default - -enum CICDService { - case github, bitrise, codemagic, later - - init?(_ name: String) { - switch name.lowercased() { - case "g", "github": - self = .github - case "b", "bitrise": - self = .bitrise - case "c", "codemagic": - self = .codemagic - case "l", "later": - self = .later - default: - return nil +struct SetUpCICDService { + + enum CICDService { + + case github, bitrise, codemagic, later + + init?(_ name: String) { + switch name.lowercased() { + case "g", "github": + self = .github + case "b", "bitrise": + self = .bitrise + case "c", "codemagic": + self = .codemagic + case "l", "later": + self = .later + default: + return nil + } } } -} -var service: CICDService? = nil + private let fileManager = FileManager.default -while service == nil { - print("Which CI/CD service do you use (Can be edited later) [(g)ithub/(b)itrise/(c)odemagic/(l)ater]: ") - service = CICDService(readLine() ?? "") -} + func perform() { + var service: CICDService? = nil + while service == nil { + print("Which CI/CD service do you use (Can be edited later) [(g)ithub/(b)itrise/(c)odemagic/(l)ater]: ") + service = CICDService(readLine() ?? "") + } -switch service { -case .github: - print("Setting template for Github Actions") - fileManager.removeItems(in: "bitrise.yml") - fileManager.removeItems(in: "codemagic.yaml") -case .bitrise: - print("Setting template for Bitrise") - fileManager.removeItems(in: "codemagic.yaml") - fileManager.removeItems(in: ".github/workflows") -case .codemagic: - print("Setting template for CodeMagic") - fileManager.removeItems(in: "bitrise.yml") - fileManager.removeItems(in: ".github/workflows") -case .later, .none: - print("You can manually setup the template later.") -} + switch service { + case .github: + print("Setting template for Github Actions") + fileManager.removeItems(in: "bitrise.yml") + fileManager.removeItems(in: "codemagic.yaml") + case .bitrise: + print("Setting template for Bitrise") + fileManager.removeItems(in: "codemagic.yaml") + fileManager.removeItems(in: ".github/workflows") + case .codemagic: + print("Setting template for CodeMagic") + fileManager.removeItems(in: "bitrise.yml") + fileManager.removeItems(in: ".github/workflows") + case .later, .none: + print("You can manually setup the template later.") + } -print("✅ Completed") + print("✅ Completed") + } +} diff --git a/Scripts/Swift/SetUpDeliveryConstants.swift b/Scripts/Swift/SetUpDeliveryConstants.swift index dfaf1f43..e33e8608 100644 --- a/Scripts/Swift/SetUpDeliveryConstants.swift +++ b/Scripts/Swift/SetUpDeliveryConstants.swift @@ -1,18 +1,21 @@ -#!/usr/bin/swift +struct SetUpDeliveryConstants { -import Foundation + func perform() { + print("Do you want to set up Constants values? (Can be edited later) [Y/n]: ") -let fileManager = FileManager.default + let arg = readLine() ?? "y" -print("Do you want to set up Constants values? (Can be edited later) [Y/n]: ") - -var arg = readLine() ?? "y" - -switch arg.lowercased() { - case "y", "yes", "": - let error = try safeShell("open -a Xcode fastlane/Constants/Constant.swift") - guard let error = error, !error.isEmpty else { break } - print("Could not open Xcode. Make sure Xcode is installed and try again.\nRaw error: \(error)") - default: - print("✅ Completed. You can edit this file at 'fastlane/Constants/Constant.swift'.") + switch arg.lowercased() { + case "y", "yes": + do { + let error = try safeShell("open -a Xcode fastlane/Constants/Constant.swift") + guard let error = error, !error.isEmpty else { break } + print("Could not open Xcode. Make sure Xcode is installed and try again.\nRaw error: \(error)") + } catch { + print("Error: \(error)") + } + default: + print("✅ Completed. You can edit this file at 'fastlane/Constants/Constant.swift'.") + } + } } diff --git a/Scripts/Swift/SetUpInterface.swift b/Scripts/Swift/SetUpInterface.swift index 44d82628..f34d42b8 100644 --- a/Scripts/Swift/SetUpInterface.swift +++ b/Scripts/Swift/SetUpInterface.swift @@ -1,25 +1,46 @@ -#!/usr/bin/swift - -import Foundation - -let fileManager = FileManager.default - -var interface = "UIKit" - -switch CommandLine.arguments[1] { -case "SwiftUI", "s", "S": - print("=> 🦅 Setting up SwiftUI") - interface = "SwiftUI" - let swiftUIAppDirectory = "tuist/Interfaces/SwiftUI/Sources/Application" - fileManager.rename( - file: "\(swiftUIAppDirectory)/App.swift", - to: "\(swiftUIAppDirectory)/\(CommandLine.arguments[2])App.swift" - ) -default: - print("=> 🦉 Setting up UIKit") - interface = "UIKit" -} +struct SetUpInterface { + + enum Interface { + + case swiftUI, uiKit + + init?(_ name: String) { + switch name.lowercased() { + case "s", "swiftui": + self = .swiftUI + case "u", "uikit": + self = .uiKit + default: return nil + } + } + + var folderName: String { + switch self { + case .swiftUI: return "SwiftUI" + case .uiKit: return "UIKit" + } + } + } -fileManager.moveFiles(in: "tuist/Interfaces/\(interface)/Project", to: "") -fileManager.moveFiles(in: "tuist/Interfaces/\(interface)/Sources", to: "{PROJECT_NAME}/Sources") -fileManager.removeItems(in: "tuist/Interfaces") + private let fileManager = FileManager.default + + func perform(_ interface: Interface, _ projectName: String) { + switch interface { + case .swiftUI: + print("=> 🦅 Setting up SwiftUI") + let swiftUIAppDirectory = "tuist/Interfaces/SwiftUI/Sources/Application" + fileManager.rename( + file: "\(swiftUIAppDirectory)/App.swift", + to: "\(swiftUIAppDirectory)/\(projectName)App.swift" + ) + case .uiKit: + print("=> 🦉 Setting up UIKit") + } + + let folderName = interface.folderName + + fileManager.moveFiles(in: "tuist/Interfaces/\(folderName)/Project", to: "") + fileManager.moveFiles(in: "tuist/Interfaces/\(folderName)/Sources", to: "TemplateApp/Sources") + fileManager.removeItems(in: "tuist/Interfaces") + } +} diff --git a/Scripts/Swift/SetUpTestTestFlight.swift b/Scripts/Swift/SetUpTestTestFlight.swift new file mode 100644 index 00000000..950c4952 --- /dev/null +++ b/Scripts/Swift/SetUpTestTestFlight.swift @@ -0,0 +1,16 @@ +let teamIdPlaceholder = "<#teamId#>" +let apiKeyIdPlaceholder = "<#API_KEY_ID#>" +let issuerIdPlaceholder = "<#ISSUER_ID#>" +let matchRepoPlaceholder = "git@github.com:{organization}/{repo}.git" + +let envMatchRepo = ProcessInfo.processInfo.environment["MATCH_REPO"] ?? "" +let envApiKey = ProcessInfo.processInfo.environment["API_KEY_ID"] ?? "" +let envIssuerId = ProcessInfo.processInfo.environment["ISSUER_ID"] ?? "" +let envTeamId = ProcessInfo.processInfo.environment["TEAM_ID"] ?? "" + +let fileManager = FileManager.default + +fileManager.replaceAllOccurrences(of: teamIdPlaceholder, to: envTeamId) +fileManager.replaceAllOccurrences(of: apiKeyIdPlaceholder, to: envApiKey) +fileManager.replaceAllOccurrences(of: issuerIdPlaceholder, to: envIssuerId) +fileManager.replaceAllOccurrences(of: matchRepoPlaceholder, to: envMatchRepo) diff --git a/Scripts/Swift/SetUpiOSProject.swift b/Scripts/Swift/SetUpiOSProject.swift new file mode 100644 index 00000000..9ad2ac5f --- /dev/null +++ b/Scripts/Swift/SetUpiOSProject.swift @@ -0,0 +1,195 @@ +#!/usr/bin/swift + +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_MINIMUM_VERSION = "{TARGET_VERSION}" + private let fileManager = FileManager.default + + private var bundleIdProduction = "" + private var bundleIdStaging = "" + private var projectName = "" + private var minimumVersion = "" + private var interface: SetUpInterface.Interface? + private var projectNameNoSpace: String { projectName.trimmingCharacters(in: .whitespacesAndNewlines) } + private var isCI = !(ProcessInfo.processInfo.environment["CI"] ?? "").isEmpty + + func perform() { + readArguments() + print("=> 🐢 Starting init \(projectName) ...") + replaceFileStructure() + createPlaceholderFiles() + SetUpInterface().perform(interface ?? .uiKit, projectName) + try? replaceTextInFiles() + try? runTuist() + try? installDependencies() + try? removeGitkeepFromXcodeProject() + try? removeTemplateFiles() + setUpCICD() + + print("=> 🚀 Done! App is ready to be tested 🙌") + try? openProject() + } + + private func readArguments() { + // TODO: Should be replaced with ArgumentParser instead of command line + for (index, argument) in CommandLine.arguments.enumerated() { + switch index { + case 1: bundleIdProduction = argument + case 2: bundleIdStaging = argument + case 3: projectName = argument + case 4: minimumVersion = argument + case 5: interface = .init(argument) + default: break + } + } + + if isCI { + minimumVersion = "14.0" + } + + while bundleIdProduction.isEmpty || !checkPackageName(bundleIdProduction) { + print("BUNDLE ID PRODUCTION (i.e. com.example.project):") + bundleIdProduction = readLine() ?? "" + } + while bundleIdStaging.isEmpty || !checkPackageName(bundleIdStaging) { + print("BUNDLE ID STAGING (i.e. com.example.project.staging):") + bundleIdStaging = readLine() ?? "" + } + while projectName.isEmpty { + print("PROJECT NAME (i.e. NewProject):") + projectName = readLine() ?? "" + } + while minimumVersion.isEmpty || !checkVersion(minimumVersion) { + print("iOS Minimum Version (i.e. 14.0):") + let version = readLine() ?? "" + minimumVersion = !version.isEmpty ? version : "14.0" + } + while interface == nil { + print("Interface [(S)wiftUI or (U)IKit]:") + interface = SetUpInterface.Interface(readLine() ?? "") + } + } + + private func replaceFileStructure() { + print("=> 🔎 Replacing files structure...") + fileManager.rename(file: "\(CONSTANT_PROJECT_NAME)Tests", to: "\(projectNameNoSpace)Tests") + fileManager.rename(file: "\(CONSTANT_PROJECT_NAME)KIFUITests", to: "\(projectNameNoSpace)KIFUITests") + fileManager.rename(file: "\(CONSTANT_PROJECT_NAME)", to: "\(projectNameNoSpace)") + } + + private func createPlaceholderFiles() { + // Duplicate the env example file to env file + fileManager.copy(file: ".env.example", to: ".env") + + // Add AutoMockable.generated.swift file + fileManager.createFile(name: "AutoMockable.generated.swift", at: "\(projectNameNoSpace)Tests/Sources/Mocks/Sourcery") + + // Add R.generated.swift file. + fileManager.createFile(name: "R.generated.swift", at: "\(projectNameNoSpace)/Sources/Supports/Helpers/Rswift") + } + + private func replaceTextInFiles() throws { + print("=> 🔎 Replacing package and package name within files...") + fileManager.replaceAllOccurrences(of: CONSTANT_BUNDLE_STAGING, to: bundleIdStaging) + fileManager.replaceAllOccurrences(of: CONSTANT_BUNDLE_PRODUCTION, to: bundleIdProduction) + fileManager.replaceAllOccurrences(of: CONSTANT_PROJECT_NAME, to: projectNameNoSpace) + fileManager.replaceAllOccurrences(of: CONSTANT_MINIMUM_VERSION, to: minimumVersion) + print("✅ Completed") + } + + private func installTuistIfNeeded() throws { + let tuistLocation = try safeShell("command -v tuist") + if let tuistLocation, tuistLocation.isEmpty { + print("Tuist could not be found") + print("Installing tuist") + try safeShell( + """ + readonly TUIST_VERSION=`cat .tuist-version` + curl -Ls https://install.tuist.io | bash + tuist install ${TUIST_VERSION} + """ + ) + } + } + + private func runTuist() throws { + try installTuistIfNeeded() + try safeShell("tuist generate --no-open") + print("✅ Completed") + } + + private func installDependencies() throws { + print("Installing gems") + try safeShell("bundle install") + + // Install dependencies + print("Run Arkana") + try safeShell("bundle exec arkana") + + print("Installing pod dependencies") + try safeShell("bundle exec pod install --repo-update") + print("✅ Completed") + } + + private func removeGitkeepFromXcodeProject() throws { + print("Remove gitkeep files from project") + let escapedProjectNameNoSpace = projectNameNoSpace.replacingOccurrences(of: ".", with: "\\.") + try safeShell("sed -i \"\" \"s/.*\\(gitkeep\\).*,//\" \(escapedProjectNameNoSpace).xcodeproj/project.pbxproj") + print("✅ Complete") + } + + private func removeTemplateFiles() throws { + print("Remove tuist files") + fileManager.removeItems(in: ".tuist-version") + fileManager.removeItems(in: "tuist") + fileManager.removeItems(in: "Project.swift") + fileManager.removeItems(in: "Workspace.swift") + + print("Remove script files and git/index") + fileManager.removeItems(in: "make.sh") + fileManager.removeItems(in: ".github/workflows/test_install_script.yml") + fileManager.removeItems(in: ".git/index") + try safeShell("git reset") + } + + private func setUpCICD() { + if !isCI { + SetUpCICDService().perform() + SetUpDeliveryConstants().perform() + fileManager.removeItems(in: "fastlane/Tests") + fileManager.removeItems(in: "set_up_test_testflight.sh") + fileManager.removeItems(in: "Scripts") + } + print("✅ Completed") + } + + private func openProject() throws { + if !isCI { + print("=> 🛠 Opening the project.") + try safeShell("open -a Xcode \(projectNameNoSpace).xcworkspace") + } + } + + private func checkPackageName(_ name: String) -> Bool { + let packageNameRegex="^[a-z][a-z0-9_]*(\\.[a-z0-9_-]+)+[0-9a-z_-]$" + let valid = name ~= packageNameRegex + if !valid { + print("Please pick a valid package name with pattern {com.example.package}") + } + return valid + } + + private func checkVersion(_ version: String) -> Bool { + let versionRegex="^[0-9_]+(\\.[0-9]+)+$" + let valid = version ~= versionRegex + if !valid { + print("Please pick a valid version with pattern {x.y}") + } + return valid + } +} + +SetUpIOSProject().perform() diff --git a/Tuist/Interfaces/UIKit/Sources/Presentation/Modules/HomeViewController.swift b/Tuist/Interfaces/UIKit/Sources/Presentation/Modules/HomeViewController.swift index a9afdb16..6196825e 100644 --- a/Tuist/Interfaces/UIKit/Sources/Presentation/Modules/HomeViewController.swift +++ b/Tuist/Interfaces/UIKit/Sources/Presentation/Modules/HomeViewController.swift @@ -7,7 +7,7 @@ final class HomeViewController: UIViewController { view.backgroundColor = .white let helloLabel = UILabel() - helloLabel.text = "Hello" + helloLabel.text = "Hello, world!" helloLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(helloLabel) diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index ae8ddf83..eb134fb6 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -26,7 +26,7 @@ enum Constant { static let appleStagingTeamId = "<#teamId#>" static let appleProductionUserName = "<#userName#>" static let appleProductionTeamId = "<#teamId#>" - static let keychainName = "github_action_keychain" + static let keychainName = "{PROJECT_NAME}_keychain" static let matchURL = "git@github.com:{organization}/{repo}.git" // MARK: - Path diff --git a/fastlane/Fastfile.swift b/fastlane/Fastfile.swift index ccba6fe1..a5377234 100644 --- a/fastlane/Fastfile.swift +++ b/fastlane/Fastfile.swift @@ -51,6 +51,11 @@ class Fastfile: LaneFile { environment: .production ) } + + func removeKeychainLane() { + desc("Delete keychain") + Keychain.remove() + } // MARK: - Build diff --git a/fastlane/Helpers/Keychain.swift b/fastlane/Helpers/Keychain.swift index 4482c788..522a8704 100644 --- a/fastlane/Helpers/Keychain.swift +++ b/fastlane/Helpers/Keychain.swift @@ -6,6 +6,8 @@ // Copyright © 2022 Nimble. All rights reserved. // +import Foundation + enum Keychain { static func create() { @@ -17,4 +19,10 @@ enum Keychain { timeout: 3600 ) } + + static func remove() { + deleteKeychain( + name: .userDefined(Constant.keychainName) + ) + } } diff --git a/make.sh b/make.sh index e9162149..61a83ee0 100644 --- a/make.sh +++ b/make.sh @@ -17,13 +17,14 @@ usage() { fi cat << EOF -Usage: $PROGNAME --bundle-id [BUNDLE_ID_PRODUCTION] --bundle-id-staging [BUNDLE_ID_STAGING] --project-name [PROJECT_NAME] +Usage: $PROGNAME --bundle-id [BUNDLE_ID_PRODUCTION] --bundle-id-staging [BUNDLE_ID_STAGING] --project-name [PROJECT_NAME] --minimum-version [MINIMUM_VERSION] --interface [INTERFACE] Set up an iOS app from tuist template. Options: -h, --help display this usage message and exit -b, --bundle-id [BUNDLE_ID_PRODUCTION] the production id (i.e. com.example.package) -s, --bundle-id-staging [BUNDLE_ID_STAGING] the staging id (i.e. com.example.package.staging) -n, --project-name [PROJECT_NAME] the project name (i.e. MyApp) +-m, --minimum-version [MINIMUM_VERSION] the minimum version of the project (i.e. 14.0) -i, --interface [INTERFACE] the user interface frameword (SwiftUI or UIKit) EOF exit 1 @@ -35,11 +36,6 @@ project_name="" minimum_version="" interface="" -readonly CONSTANT_PROJECT_NAME="{PROJECT_NAME}" -readonly CONSTANT_BUNDLE_PRODUCTION="{BUNDLE_ID_PRODUCTION}" -readonly CONSTANT_BUNDLE_STAGING="{BUNDLE_ID_STAGING}" -readonly CONSTANT_MINIMUM_VERSION="{TARGET_VERSION}" - while [ $# -gt 0 ] ; do case "$1" in -h|--help) @@ -57,6 +53,10 @@ while [ $# -gt 0 ] ; do project_name="$2" shift ;; + -m|--minimum-version) + minimum_version="$2" + shift + ;; -i|--interface) interface="$2" shift @@ -71,163 +71,4 @@ while [ $# -gt 0 ] ; do shift done -if [ -z "$bundle_id_production" ] ; then - read -p "BUNDLE ID PRODUCTION (i.e. com.example.project):" bundle_id_production -fi - -if [ -z "$bundle_id_staging" ] ; then - read -p "BUNDLE ID STAGING (i.e. com.example.project.staging):" bundle_id_staging -fi - -if [ -z "$project_name" ] ; then - read -p "PROJECT NAME (i.e. NewProject):" project_name -fi - -if [[ -z "${CI}" ]]; then - read -p "iOS Minimum Version (i.e. 14.0):" minimum_version -fi -if [ -z "$minimum_version" ]; then - minimum_version="14.0" -fi - -if [ -z "$interface" ]; then - read -p "Interface [(S)wiftUI or (U)IKit]:" interface - if [ -z "$interface" ]; then - interface="SwiftUI" - fi -fi - -# Enforce minimum version -version_regex='^[0-9_]+(\.[0-9]+)+$' -if ! [[ $minimum_version =~ $version_regex ]]; then - echo "=> Minimum version incorrect. Reverting to default version." - minimum_version="14.0" -fi - -if [ -z "$bundle_id_production" ] || [ -z "$bundle_id_staging" ] || [ -z "$project_name" ] ; then - usage "Input cannot be blank." -fi - -# Enforce package name -regex='^[a-z][a-z0-9_]*(\.[a-z0-9_-]+)+[0-9a-z_-]$' -if ! [[ $bundle_id_production =~ $regex ]]; then - die "Invalid Package Name: $bundle_id_production (needs to follow standard pattern {com.example.package})" -fi - -# Select the Interface -cat Scripts/Swift/SetUpInterface.swift Scripts/Swift/Extensions/FileManager+Utils.swift | swift - $interface $project_name - -echo "=> 🐢 Starting init $project_name ..." - -# Trim spaces in APP_NAME -readonly PROJECT_NAME_NO_SPACES=$(echo "$project_name" | sed "s/ //g") - -# Rename files structure -echo "=> 🔎 Replacing files structure..." - - -## user define function -rename_folder(){ - local DIR=$1 - local NEW_DIR=$2 - if [ -d "$DIR" ] - then - mv ${DIR} ${NEW_DIR} - fi -} - -# Rename test folder structure -rename_folder "${CONSTANT_PROJECT_NAME}Tests" "${PROJECT_NAME_NO_SPACES}Tests" - -# Rename KIF UI Test folder structure -rename_folder "${CONSTANT_PROJECT_NAME}KIFUITests" "${PROJECT_NAME_NO_SPACES}KIFUITests" - -# Rename app folder structure -rename_folder "${CONSTANT_PROJECT_NAME}" "${PROJECT_NAME_NO_SPACES}" - -# Duplicate the env example file and rename it to env file -cp "./.env.example" "./.env" - -# Add AutoMockable.generated.swift file -mkdir -p "${PROJECT_NAME_NO_SPACES}Tests/Sources/Mocks/Sourcery" -touch "${PROJECT_NAME_NO_SPACES}Tests/Sources/Mocks/Sourcery/AutoMockable.generated.swift" - -# Add R.generated.swift file -mkdir -p "${PROJECT_NAME_NO_SPACES}/Sources/Supports/Helpers/Rswift" -touch "${PROJECT_NAME_NO_SPACES}/Sources/Supports/Helpers/Rswift/R.generated.swift" - -echo "✅ Completed" - -# Search and replace in files -echo "=> 🔎 Replacing package and package name within files..." -BUNDLE_ID_PRODUCTION_ESCAPED="${bundle_id_production//./\.}" -BUNDLE_ID_STAGING_ESCAPED="${bundle_id_staging//./\.}" -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_BUNDLE_STAGING/$BUNDLE_ID_STAGING_ESCAPED/g" {} + -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_BUNDLE_PRODUCTION/$BUNDLE_ID_PRODUCTION_ESCAPED/g" {} + -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_PROJECT_NAME/$PROJECT_NAME_NO_SPACES/g" {} + -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_MINIMUM_VERSION/$minimum_version/g" {} + -echo "✅ Completed" - -# check for tuist and install -if ! command -v tuist &> /dev/null -then - echo "Tuist could not be found" - echo "Installing tuist" - readonly TUIST_VERSION=`cat .tuist-version` - curl -Ls https://install.tuist.io | bash - tuist install ${TUIST_VERSION} -fi - -# Generate with tuist -echo "Tuist found" -tuist generate --no-open -echo "✅ Completed" - -# Install dependencies -echo "Installing gems" -bundle install - -echo "Run Arkana" -bundle exec arkana - -echo "Installing pod dependencies" -bundle exec pod install --repo-update -echo "✅ Completed" - -# Remove gitkeep files -echo "Remove gitkeep files from project" -sed -i "" "s/.*\(gitkeep\).*,//" $PROJECT_NAME_NO_SPACES.xcodeproj/project.pbxproj -echo "✅ Completed" - -# Remove Tuist files -echo "Remove tuist files" -rm -rf .tuist-version -rm -rf tuist -rm -rf Project.swift -rm -rf Workspace.swift - -# Remove script files and git/index -echo "Remove script files and git/index" -rm -rf make.sh -rm -rf .github/workflows/test_install_script.yml -rm -f .git/index -git reset - -if [[ -z "${CI}" ]]; then - rm -rf fastlane/Tests - rm -f set_up_test_testflight.sh - cat Scripts/Swift/SetUpCICDService.swift Scripts/Swift/Extensions/FileManager+Utils.swift Scripts/Swift/Helpers/SafeShell.swift > t.swift && swift t.swift && rm -rf 't.swift' - cat Scripts/Swift/SetUpDeliveryConstants.swift Scripts/Swift/Extensions/FileManager+Utils.swift Scripts/Swift/Helpers/SafeShell.swift > t.swift && swift t.swift && rm -rf 't.swift' - rm -rf Scripts -fi - - -echo "✅ Completed" - -# Done! -echo "=> 🚀 Done! App is ready to be tested 🙌" - -if [[ -z "${CI}" ]]; then - echo "=> 🛠 Opening the project." - open -a Xcode $PROJECT_NAME_NO_SPACES.xcworkspace -fi +cat Scripts/Swift/SetUpiOSProject.swift Scripts/Swift/SetUpCICDService.swift Scripts/Swift/SetUpDeliveryConstants.swift Scripts/Swift/SetUpInterface.swift Scripts/Swift/Extensions/FileManager+Utils.swift Scripts/Swift/Extensions/String+Utils.swift Scripts/Swift/Helpers/SafeShell.swift > t.swift && swift t.swift $bundle_id_production $bundle_id_staging $project_name "$minimum_version" $interface && rm -rf 't.swift' diff --git a/set_up_test_testflight.sh b/set_up_test_testflight.sh deleted file mode 100644 index c1e1290e..00000000 --- a/set_up_test_testflight.sh +++ /dev/null @@ -1,12 +0,0 @@ -readonly CONSTANT_TEAM_ID="<#teamId#>" -readonly CONSTANT_API_KEY_ID="<#API_KEY_ID#>" -readonly CONSTANT_ISSUER_ID="<#ISSUER_ID#>" -readonly CONSTANT_MATCH_REPO="git@github.com:{organization}\/{repo}.git" - -readonly WORKING_DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P) -MATCH_REPO_ESCAPED=$(echo "${MATCH_REPO//\//\\\/}") - -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_TEAM_ID/$TEAM_ID/g" {} + -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_API_KEY_ID/$API_KEY_ID/g" {} + -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_ISSUER_ID/$ISSUER_ID/g" {} + -LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_MATCH_REPO/$MATCH_REPO_ESCAPED/g" {} + diff --git a/{PROJECT_NAME}KIFUITests/Sources/Specs/Application/ApplicationSpec.swift b/{PROJECT_NAME}KIFUITests/Sources/Specs/Application/ApplicationSpec.swift index 8dded39b..9380ba9f 100644 --- a/{PROJECT_NAME}KIFUITests/Sources/Specs/Application/ApplicationSpec.swift +++ b/{PROJECT_NAME}KIFUITests/Sources/Specs/Application/ApplicationSpec.swift @@ -23,7 +23,7 @@ final class ApplicationSpec: QuickSpec { context("when opens") { it("shows its UI components") { - self.tester().waitForView(withAccessibilityLabel: "Hello") + self.tester().waitForView(withAccessibilityLabel: "Hello, world!") } } }