| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- #!/usr/bin/env xcrun --sdk macosx swift
- import Foundation
- // MARK: Types
- struct InfoPlist {
- private typealias Plist = (dict: [String: Any], format: PropertyListSerialization.PropertyListFormat)
- private var fileURL: URL
- private var plist: Plist
- enum Const: String {
- case version = "CFBundleShortVersionString"
- case build = "CFBundleVersion"
- case settings = "PreferenceSpecifiers"
- case settingsValue = "DefaultValue"
- }
- var version: String? {
- get {
- plist.dict[InfoPlist.Const.version.rawValue] as? String
- }
- set {
- plist.dict[InfoPlist.Const.version.rawValue] = newValue
- }
- }
- var build: String? {
- get {
- plist.dict[InfoPlist.Const.build.rawValue] as? String
- }
- set {
- plist.dict[InfoPlist.Const.build.rawValue] = newValue
- }
- }
- var settingsVersion: String? {
- get {
- (
- plist
- .dict[InfoPlist.Const.settings.rawValue] as! [[String: Any]]
- )[1][
- InfoPlist.Const.settingsValue
- .rawValue
- ] as? String
- }
- set {
- var dictCopy = plist.dict
- var specs = dictCopy[InfoPlist.Const.settings.rawValue] as! [[String: Any]]
- specs[1][InfoPlist.Const.settingsValue.rawValue] = newValue ?? ""
- dictCopy[InfoPlist.Const.settings.rawValue] = specs
- plist.dict = dictCopy
- }
- }
- private init(fileURL: URL, plist: Plist) {
- self.fileURL = fileURL
- self.plist = plist
- }
- init?(fromFileAtURL fileURL: URL) {
- guard let plist = InfoPlist.readPlist(fromFileAtURL: fileURL) else { return nil }
- self.init(fileURL: fileURL, plist: plist)
- }
- func save() {
- InfoPlist.writePlist(plist, toFileAtURL: fileURL)
- }
- // MARK: Plist file read/write
- private static func readPlist(fromFileAtURL fileURL: URL) -> Plist? {
- var data: Data
- do {
- data = try Data(contentsOf: fileURL)
- } catch {
- return nil
- }
- var format: PropertyListSerialization.PropertyListFormat = .xml
- let dict: [String: Any]
- do {
- dict = try PropertyListSerialization.propertyList(from: data, format: &format) as! [String: Any]
- } catch {
- print("error: Failed to deserialize plist read from file: \(fileURL.absoluteString)")
- print("Error details: \(error)")
- return nil
- }
- return (dict: dict, format: format)
- }
- private static func writePlist(_ plist: Plist, toFileAtURL fileURL: URL) {
- let data: Data
- do {
- data = try PropertyListSerialization.data(fromPropertyList: plist.dict, format: plist.format, options: 0)
- } catch {
- print("error: Failed to serialize plist!")
- return
- }
- do {
- try data.write(to: fileURL)
- } catch {
- print("error: Failed to write file: \(fileURL.absoluteString)")
- print("Error details: \(error)")
- return
- }
- }
- }
- enum Branch: CustomStringConvertible {
- private static var releasePrefix = "release"
- case Release(version: String)
- case Other(name: String)
- init(_ string: String) {
- let parts = string.components(separatedBy: "/")
- if parts.count >= 2, parts[0] == Branch.releasePrefix {
- self = .Release(version: parts[1])
- } else {
- self = .Other(name: string)
- }
- }
- var description: String {
- switch self {
- case let .Release(version):
- return "\(Branch.releasePrefix)/\(version)"
- case let .Other(name):
- return name
- }
- }
- }
- // MARK: Helpers
- func execute(command: String, args: [String]) -> String? {
- let process = Process()
- process.launchPath = command
- process.arguments = args
- let pipe = Pipe()
- process.standardOutput = pipe
- process.launch()
- let data = pipe.fileHandleForReading.readDataToEndOfFile()
- return String(data: data, encoding: .utf8)?
- .trimmingCharacters(in: .newlines)
- }
- func checkdSYM(buildNumber: String?) {
- // Не правильная версия в dSYM может привести к проблемам в работе с сервисами, которые эти dSYM используют (например: Crashlytics)
- // http://tgoode.com/2014/06/05/sensible-way-increment-bundle-version-cfbundleversion-xcode/#comment-600
- // http://tgoode.com/2014/06/05/sensible-way-increment-bundle-version-cfbundleversion-xcode/#comment-2704
- // http://stackoverflow.com/q/13323728
- // http://stackoverflow.com/a/22460268
- if let dsymFolderPath = ProcessInfo.processInfo.environment["DWARF_DSYM_FOLDER_PATH"], !dsymFolderPath.isEmpty,
- let productName = ProcessInfo.processInfo.environment["PRODUCT_NAME"], !productName.isEmpty
- {
- let dsymPlistURL = URL(fileURLWithPath: "\(dsymFolderPath)/\(productName).app.dSYM/Contents/Info.plist")
- guard var dsymInfoPlist = InfoPlist(fromFileAtURL: dsymPlistURL) else {
- print("Failed to read dsym at url \(dsymPlistURL). This is ok for debug builds.")
- return
- }
- dsymInfoPlist.build = buildNumber
- dsymInfoPlist.save()
- }
- }
- // MARK: Implementation
- guard let currentBranch = execute(command: "/usr/bin/git", args: ["rev-parse", "--abbrev-ref", "HEAD"]).map(Branch.init) else {
- print("Can't determine current branch! Skiping...")
- exit(0)
- }
- guard let commitsCount = execute(command: "/usr/bin/git", args: ["rev-list", "--count", "HEAD"]), !commitsCount.isEmpty else {
- print("Can't determine commits count! Skiping..")
- exit(0)
- }
- guard let targetBuildDir = ProcessInfo.processInfo.environment["TARGET_BUILD_DIR"], !targetBuildDir.isEmpty else {
- print("error: TARGET_BUILD_DIR environment variable is empty!")
- exit(1)
- }
- guard let infoPlistPath = ProcessInfo.processInfo.environment["INFOPLIST_PATH"], !infoPlistPath.isEmpty else {
- print("error: INFOPLIST_PATH environment variable is empty!")
- exit(1)
- }
- guard let infoPlistFile = ProcessInfo.processInfo.environment["INFOPLIST_FILE"], !infoPlistFile.isEmpty else {
- print("error: INFOPLIST_FILE environment variable is empty!")
- exit(1)
- }
- guard let currentVersion = ProcessInfo.processInfo.environment["CURRENT_PROJECT_VERSION"] else {
- print("error: Current version can't be determined!")
- exit(1)
- }
- guard var buildInfoPlist = InfoPlist(fromFileAtURL: URL(fileURLWithPath: "\(targetBuildDir)/\(infoPlistPath)")) else {
- print("error: Build Info Plist cannot be load!")
- exit(1)
- }
- guard let scrRoot = ProcessInfo.processInfo.environment["SRCROOT"], !targetBuildDir.isEmpty else {
- print("error: SRCROOT environment variable is empty!")
- exit(1)
- }
- guard var sourceInfoPlist = InfoPlist(fromFileAtURL: URL(fileURLWithPath: "\(scrRoot)/FreeAPS/Resources/Info.plist")) else {
- print("error: Source Info Plist cannot be load!")
- exit(1)
- }
- switch currentBranch {
- case let .Release(version):
- buildInfoPlist.version = version
- buildInfoPlist.build = commitsCount
- sourceInfoPlist.build = commitsCount
- default:
- buildInfoPlist.build = "\(commitsCount)"
- }
- buildInfoPlist.save()
- sourceInfoPlist.save()
- checkdSYM(buildNumber: buildInfoPlist.build)
|