LaunchArgs.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. /// Describes an object that can check if a file eists in the filesystem. Used to allow for better
  18. /// testing with FileManager.
  19. protocol FileChecker {
  20. /// Returns a Boolean value that indicates whether a file or directory exists at a specified path.
  21. /// This matches the `FileManager` API.
  22. func fileExists(atPath: String) -> Bool
  23. }
  24. // Make FileManager a FileChecker. This is empty since FileManager already provides this
  25. // functionality (natively and through our extensions).
  26. extension FileManager: FileChecker {}
  27. // TODO: Evaluate if we should switch to Swift Package Manager's internal `Utility` module that
  28. // contains `ArgumentParser`. No immediate need, but provides some nice benefits.
  29. /// LaunchArgs reads from UserDefaults to assemble all launch arguments coming from the command line
  30. /// or the Xcode scheme. UserDefaults contains all launch arguments that are in the format of:
  31. /// `-myKey myValue`.
  32. struct LaunchArgs {
  33. /// Keys associated with the launch args. See `Usage` for descriptions of each flag.
  34. private enum Key: String, CaseIterable {
  35. case gitRoot
  36. case releasingPods
  37. /// Usage description for the key.
  38. var usage: String {
  39. switch self {
  40. case .gitRoot:
  41. return "The root of the firebase-ios-sdk checked out git repo."
  42. case .releasingPods:
  43. return "The file path to a textproto file containing all the releasing Pods, of type."
  44. }
  45. }
  46. }
  47. /// A file URL to a textproto with the contents of a `FirebasePod_Release` object. Used to verify
  48. /// expected version numbers.
  49. let currentReleasePath: URL
  50. /// A file URL to the checked out gitRepo to update
  51. let gitRootPath: String
  52. /// The shared instance for processing launch args using default arguments.
  53. static let shared: LaunchArgs = LaunchArgs()
  54. /// Initializes with values pulled from the instance of UserDefaults passed in.
  55. ///
  56. /// - Parameters:
  57. /// - defaults: User defaults containing launch arguments. Defaults to `standard`.
  58. /// - fileChecker: An object that can check if a file exists or not. Defaults to
  59. /// `FileManager.default`.
  60. init(userDefaults defaults: UserDefaults = UserDefaults.standard,
  61. fileChecker: FileChecker = FileManager.default) {
  62. // Parse the current releases key.
  63. guard let currentRelease = defaults.string(forKey: Key.releasingPods.rawValue) else {
  64. LaunchArgs.exitWithUsageAndLog("Missing required key: `\(Key.releasingPods)` for the file " +
  65. "containing the list of releasing pods and versions.")
  66. }
  67. let url = URL(fileURLWithPath: currentRelease)
  68. guard fileChecker.fileExists(atPath: url.path) else {
  69. LaunchArgs.exitWithUsageAndLog("Could not parse \(Key.releasingPods) key: value passed " +
  70. "in is not a file URL or the file does not exist. Value: \(currentRelease)." +
  71. " Do you need to run prodaccess?")
  72. }
  73. currentReleasePath = url.standardizedFileURL
  74. // Parse the gitRoot key.
  75. guard let gitRoot = defaults.string(forKey: Key.gitRoot.rawValue) else {
  76. LaunchArgs.exitWithUsageAndLog("Missing required key: `\(Key.gitRoot)` for the path " +
  77. "of the checked out git repo.")
  78. }
  79. let gitUrl = URL(fileURLWithPath: gitRoot)
  80. guard fileChecker.fileExists(atPath: gitUrl.path) else {
  81. LaunchArgs.exitWithUsageAndLog("Could not parse \(Key.gitRoot) key: value passed " +
  82. "in is not a file URL or the file does not exist. Value: \(gitRoot)")
  83. }
  84. gitRootPath = gitRoot
  85. }
  86. /// Prints an error that occurred, the proper usage String, and quits the application.
  87. private static func exitWithUsageAndLog(_ errorText: String) -> Never {
  88. print(errorText)
  89. // Loop over all the possible keys and print their description.
  90. print("Usage: `swift run FirebasePod [ARGS]` where args are:")
  91. for option in Key.allCases {
  92. print("""
  93. -\(option.rawValue) <VALUE>
  94. \(option.usage)
  95. """)
  96. }
  97. fatalError("Invalid arguments. See output above for specific error and usage instructions.")
  98. }
  99. }