#!/usr/bin/env swift import Foundation protocol TemplateResolverType { static var defaultConfig: String { get } static var developmentTeam: String { get } static var codeSignIdentity: String { get } static var swiftActiveCompilationConditions: String { get } static var applicationIdentifier: String { get } static var targetPrivadoName: String { get } static var cfBundleDisplayName: String { get } static var provisionProfileSpecifier: String { get } static var provisionProfileSpecifierOVPN: String { get } static var provisionProfileSpecifierWireGuard: String { get } } final class YamlGenerator { enum SchemeType: RawRepresentable { case develop case production case beta init?(rawValue: String) { switch rawValue.uppercased() { case "DEV": self = .develop case "PROD": self = .production case "BETA": self = .beta default: return nil } } var rawValue: String { switch self { case .develop: return "DEV" case .production: return "PROD" case .beta: return "BETA" } } } private enum Constants { enum Templates { static let defaultConfig = "{{defaultConfig}}" static let developmentTeam = "{{DEVELOPMENT_TEAM}}" static let codeSignIdentity = "{{CODE_SIGN_IDENTITY}}" static let swiftActiveCompilationConditions = "{{SWIFT_ACTIVE_COMPILATION_CONDITIONS}}" static let applicationIdentifier = "{{APPLICATION_IDENTIFIER}}" static let targetPrivadoName = "{{TARGET_PRIVADO_NAME}}" static let cfBundleDisplayName = "{{CFBundleDisplayName}}" static let provisionProfileSpecifier = "{{PROVISIONING_PROFILE_SPECIFIER}}" static let provisionProfileSpecifierOVPN = "{{PROVISIONING_PROFILE_SPECIFIER_OVPN}}" static let provisionProfileSpecifierWireGuard = "{{PROVISIONING_PROFILE_SPECIFIER_WIREGUARD}}" // static let developmentTeam = "" } enum Develop: TemplateResolverType { static let defaultConfig = "Debug" static let developmentTeam = "4D7YV49724" static let codeSignIdentity = "Apple Development: Yuriy Shikin (S9D2XZUZH2)" static let swiftActiveCompilationConditions = "BETA" static let applicationIdentifier = "io.privado.ios" static let targetPrivadoName = "PrivadoVPN" static let cfBundleDisplayName = "PrivadoVPN" static let provisionProfileSpecifier = "Privado iOS" static let provisionProfileSpecifierOVPN = "Privado iOS OpenVPN Extension" static let provisionProfileSpecifierWireGuard = "Privado iOS Wireguard Extension" // static let developmentTeam = "" } enum Production: TemplateResolverType { static let defaultConfig = "Release" static let developmentTeam = "4D7YV49724" static let codeSignIdentity = "Apple Distribution: PRIVADO NETWORKS LLC (4D7YV49724)" static let swiftActiveCompilationConditions = "PRODUCTION" static let applicationIdentifier = "io.privado.ios" static let targetPrivadoName = "PrivadoVPN" static let cfBundleDisplayName = "PrivadoVPN" static let provisionProfileSpecifier = "Privado iOS AppStore" static let provisionProfileSpecifierOVPN = "Privado iOS OpenVPN Extension AppStore" static let provisionProfileSpecifierWireGuard = "Privado iOS Wireguard Extension AppStore" // static let developmentTeam = "" } enum Beta: TemplateResolverType { static let defaultConfig = "Release" static let developmentTeam = "4D7YV49724" static let codeSignIdentity = "Apple Distribution: PRIVADO NETWORKS LLC (4D7YV49724)" static let swiftActiveCompilationConditions = "BETA" static let applicationIdentifier = "io.privado.beta.ios" static let targetPrivadoName = "PrivadoVPN" static let cfBundleDisplayName = "PrivadoVPN Beta" static let provisionProfileSpecifier = "Privado Beta iOS AppStore" static let provisionProfileSpecifierOVPN = "Privado Beta iOS OpenVPN Extension AppStore" static let provisionProfileSpecifierWireGuard = "Privado Beta iOS Wireguard Extension AppStore" // static let developmentTeam = "" } } @discardableResult static func backup(yaml path: String, data: Data? = nil) -> Data? { if let data = data { try! data.write(to: URL(string: "file://\(path)")!) return data } else { return try! Data(contentsOf: URL(string: "file://\(path)")!) } } static func prepare(yaml path: String, type: SchemeType) { let data = try! Data(contentsOf: URL(string: "file://\(path)")!) let content = String(decoding: data, as: UTF8.self) let resolver: TemplateResolverType.Type switch type { case .develop: resolver = Constants.Develop.self case .production: resolver = Constants.Production.self case .beta: resolver = Constants.Beta.self } let resultContent = content .replacingOccurrences(of: Constants.Templates.applicationIdentifier, with: resolver.applicationIdentifier) .replacingOccurrences(of: Constants.Templates.cfBundleDisplayName, with: resolver.cfBundleDisplayName) .replacingOccurrences(of: Constants.Templates.codeSignIdentity, with: resolver.codeSignIdentity) .replacingOccurrences(of: Constants.Templates.defaultConfig, with: resolver.defaultConfig) .replacingOccurrences(of: Constants.Templates.developmentTeam, with: resolver.developmentTeam) .replacingOccurrences(of: Constants.Templates.provisionProfileSpecifier, with: resolver.provisionProfileSpecifier) .replacingOccurrences(of: Constants.Templates.provisionProfileSpecifierOVPN, with: resolver.provisionProfileSpecifierOVPN) .replacingOccurrences(of: Constants.Templates.provisionProfileSpecifierWireGuard, with: resolver.provisionProfileSpecifierWireGuard) .replacingOccurrences(of: Constants.Templates.swiftActiveCompilationConditions, with: resolver.swiftActiveCompilationConditions) .replacingOccurrences(of: Constants.Templates.targetPrivadoName, with: resolver.targetPrivadoName) let resultData = resultContent.data(using: .utf8)! try! resultData.write(to: URL(string: "file://\(path)")!) } // MARK: - Private } func showHelp() { print("How to use:") print("--path=[PATH TO YAML]") print("--type=[DEV|BETA|PROD] default value is DEV") } guard let pathArguments = CommandLine.arguments.first(where: { $0.contains("path") }) , let path = pathArguments.split(separator: "=").last else { showHelp() exit(1) } guard let xcodegenPath = ["/usr/local/bin/xcodegen", "/opt/homebrew/bin/xcodegen"].first(where: { FileManager.default.fileExists(atPath: $0) }) else { print("Error 🍄 Can't find xcodegen") exit(1) } let type: YamlGenerator.SchemeType if let typeArguments = CommandLine.arguments.first(where: { $0.contains("type") }) , let arg = typeArguments.split(separator: "=").last , let scheme = YamlGenerator.SchemeType(rawValue: String(arg)) { type = scheme } else { type = .develop } // Replace templates let yamlPath = "\(FileManager.default.currentDirectoryPath)/\(String(path))" let data = YamlGenerator.backup(yaml: yamlPath) YamlGenerator.prepare(yaml: yamlPath, type: type) print("Generate xcodeproj 🥃") let projectDirectory = yamlPath .split(separator: "/") .dropLast(1) .map(String.init) .joined(separator: "/") print("PATH: \(yamlPath)") let xcodegenProcess = Process() xcodegenProcess.executableURL = URL(fileURLWithPath: xcodegenPath) xcodegenProcess.arguments = ["generate", "--spec", "\(yamlPath)"] do { try xcodegenProcess.run() xcodegenProcess.waitUntilExit() } catch { print(error) } YamlGenerator.backup(yaml: yamlPath, data: data) exit(0)