Skip to content

Commit

Permalink
feat: allow selecting signing for release and debug independently
Browse files Browse the repository at this point in the history
  • Loading branch information
GMinucci committed Jan 27, 2025
1 parent 559a21f commit 5ac0892
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 156 deletions.
6 changes: 3 additions & 3 deletions Sources/VariantsCore/Factory/FastlaneParametersFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class FastlaneParametersFactory: ParametersFactory {
else { return }

// Populate 'fastlane/parameters/match_params.rb' from template
let parameters: [CustomProperty] = variant.signing?.customProperties() ?? []
let parameters: [CustomProperty] = variant.releaseSigning?.customProperties() ?? []
try? createParametersFile(in: StaticPath.Fastlane.matchParametersFile,
renderTemplate: StaticPath.Template.matchParametersFileName,
with: parameters)
Expand All @@ -49,11 +49,11 @@ class FastlaneParametersFactory: ParametersFactory {
.reduce(into: [], { $0.append($1) })
let appBundleID = [variant.makeBundleID(for: configuration.target)]
var context: [String: Any] = [
"export_method": (variant.signing?.exportMethod ?? .appstore).rawValue,
"export_method": (variant.releaseSigning?.exportMethod ?? .appstore).rawValue,
"app_identifiers": appBundleID + extensionBundleIDs
]

if let matchURL = variant.signing?.matchURL {
if let matchURL = variant.releaseSigning?.matchURL {
context["git_url"] = matchURL
} else {
Logger.shared.logWarning(item:
Expand Down
98 changes: 65 additions & 33 deletions Sources/VariantsCore/Factory/iOS/XCConfigFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ class XCConfigFactory: XCFactory {
/*
* Adjust signing configuration in project.pbxproj
*/
updateSigningConfig(for: variant, configuration: configuration, projectPath: xcodeProjPath)
updateSigningConfigForExtensions(for: variant, configuration: configuration, projectPath: xcodeProjPath)
updateSigning(using: variant.releaseSigning, targetName: configuration.target.source.info, isRelease: true, projectPath: xcodeProjPath)
updateSigning(using: variant.debugSigning, targetName: configuration.target.source.info, isRelease: false, projectPath: xcodeProjPath)

updateSigningConfigForExtensions(signing: variant.releaseSigning, variant: variant, configuration: configuration, isRelease: true, projectPath: xcodeProjPath)
updateSigningConfigForExtensions(signing: variant.debugSigning, variant: variant, configuration: configuration, isRelease: false, projectPath: xcodeProjPath)

/*
* INFO.plist
Expand Down Expand Up @@ -167,58 +170,87 @@ class XCConfigFactory: XCFactory {
}
}

private func updateSigningConfig(
for variant: iOSVariant,
configuration: iOSConfiguration,
private func updateSigning(
using signing: iOSSigning?,
targetName: String,
isRelease: Bool,
projectPath: Path
) {
guard
let exportMethod = variant.signing?.exportMethod,
let teamName = variant.signing?.teamName,
let teamID = variant.signing?.teamID,
!teamID.isEmpty,
!teamName.isEmpty
let signing,
let teamID = signing.teamID, !teamID.isEmpty
else { return }

let isDistribution = exportMethod == .appstore || exportMethod == .enterprise
let certType = isDistribution ? "Distribution" : "Development"
let signingSettings = [
"PROVISIONING_PROFILE_SPECIFIER": "$(V_MATCH_PROFILE)",
"CODE_SIGN_STYLE": "Manual",
"CODE_SIGN_IDENTITY": "Apple \(certType): \(teamName) (\(teamID))"
var signingSettings = [
"PROVISIONING_PROFILE_SPECIFIER": "",
"CODE_SIGN_STYLE": "\(signing.style.rawValue.capitalized)",
"DEVELOPMENT_TEAM": "\(teamID)"
]

if signing.style == .manual {
guard
let exportMethod = signing.exportMethod,
let teamName = signing.teamName, !teamName.isEmpty
else { return }

let isDistribution = exportMethod == .appstore || exportMethod == .enterprise
let certType = isDistribution ? "Distribution" : "Development"
signingSettings["PROVISIONING_PROFILE_SPECIFIER"] = "$(V_MATCH_PROFILE)"
signingSettings["CODE_SIGN_IDENTITY"] = "Apple \(certType): \(teamName) (\(teamID))"
}

let xcodeFactory = XcodeProjFactory()
xcodeFactory.modify(signingSettings, in: projectPath, targetName: configuration.target.source.info)
xcodeFactory.modify(
signingSettings,
in: projectPath,
targetName: targetName,
configurationTypes: [isRelease ? .release : .debug])
}

private func updateSigningConfigForExtensions(
for variant: iOSVariant,
signing: iOSSigning?,
variant: iOSVariant,
configuration: iOSConfiguration,
isRelease: Bool,
projectPath: Path
) {
let targetExtensions = configuration.extensions.filter({ $0.signed })
guard

guard
!targetExtensions.isEmpty,
let exportMethod = variant.signing?.exportMethod,
let teamName = variant.signing?.teamName,
let teamID = variant.signing?.teamID,
!teamID.isEmpty,
!teamName.isEmpty
let signing,
let teamID = signing.teamID, !teamID.isEmpty
else { return }

let isDistribution = exportMethod == .appstore || exportMethod == .enterprise
let certType = isDistribution ? "Distribution" : "Development"
var signingSettings = [
"PROVISIONING_PROFILE_SPECIFIER": "",
"CODE_SIGN_STYLE": "\(signing.style.rawValue.capitalized)",
"DEVELOPMENT_TEAM": "\(teamID)"
]

if signing.style == .manual {
guard
let exportMethod = signing.exportMethod,
let teamName = signing.teamName, !teamName.isEmpty
else { return }

let isDistribution = exportMethod == .appstore || exportMethod == .enterprise
let certType = isDistribution ? "Distribution" : "Development"
signingSettings["CODE_SIGN_IDENTITY"] = "Apple \(certType): \(teamName) (\(teamID))"
}

let xcodeFactory = XcodeProjFactory()
for targetExtension in targetExtensions {
let bundleID = targetExtension.makeBundleID(variant: variant, target: configuration.target)
let signingSettings = [
"PROVISIONING_PROFILE_SPECIFIER": "\(exportMethod.prefix) \(bundleID)",
"CODE_SIGN_STYLE": "Manual",
"CODE_SIGN_IDENTITY": "Apple \(certType): \(teamName) (\(teamID))"
]
xcodeFactory.modify(signingSettings, in: projectPath, targetName: targetExtension.name)
if signing.style == .manual, let exportMethod = signing.exportMethod {
let bundleID = targetExtension.makeBundleID(variant: variant, target: configuration.target)
signingSettings["PROVISIONING_PROFILE_SPECIFIER"] = "\(exportMethod.prefix) \(bundleID)"
}

xcodeFactory.modify(
signingSettings,
in: projectPath,
targetName: targetExtension.name,
configurationTypes: [isRelease ? .release : .debug])
}
}

Expand Down
17 changes: 12 additions & 5 deletions Sources/VariantsCore/Factory/iOS/XcodeProjFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import XcodeProj
import PathKit

struct XcodeProjFactory {
enum BuildConfigType: String, CaseIterable {
case debug, release
}

private let logger: Logger

init(enableVerboseLog: Bool = false) {
init(enableVerboseLog: Bool = true) {
logger = Logger(verbose: enableVerboseLog)
}

Expand Down Expand Up @@ -152,23 +156,26 @@ struct XcodeProjFactory {
/// - keyValue: Key/value pair to be modified
/// - projectPath: Path to Xcode project
/// - targetName: Name of the target on which the `buildSettings` should be changed.
/// - asTestSettings: If true, add configuraiton to test/non-host targets.
/// - silent: Flag to determine if final logs are necessary
func modify(_ keyValue: [String: String],
in projectPath: Path,
targetName: String,
asTestSettings: Bool = false,
configurationTypes: [BuildConfigType] = BuildConfigType.allCases,
silent: Bool = false) {
do {
let project = try XcodeProj(path: projectPath)
let configTypeNames = configurationTypes.map { $0.rawValue.lowercased() }
logger.logInfo("Updating: ", item: projectPath)

project.pbxproj.buildConfigurations
.filter({ ($0.buildSettings["INFOPLIST_FILE"] as? String)?.contains(targetName) ?? false })
.filter({ configTypeNames.contains($0.name.lowercased()) })
.forEach { conf in
logger.logDebug(
"Build configuration type: ", item: conf.name, indentationLevel: 1, color: .blue)
keyValue.forEach { (key, value) in
Logger.shared.logDebug("Item: ", item: "\(key) = \(value)",
indentationLevel: 1, color: .purple)
logger.logDebug(
"Item: ", item: "\(key) = \(value)", indentationLevel: 2, color: .purple)
conf.buildSettings[key] = value
}
}
Expand Down
54 changes: 39 additions & 15 deletions Sources/VariantsCore/Schemas/iOS/iOSSigning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,38 @@ import Foundation
struct iOSSigning: Codable {
let teamName: String?
let teamID: String?
let exportMethod: Type?
let exportMethod: ExportMethod?
let matchURL: String?

let style: SigningStyle

enum CodingKeys: String, CodingKey {
case teamName = "team_name"
case teamID = "team_id"
case exportMethod = "export_method"
case matchURL = "match_url"
case style
}

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.teamName = try container.decodeIfPresent(String.self, forKey: .teamName)
self.teamID = try container.decodeIfPresent(String.self, forKey: .teamID)
self.exportMethod = try container.decodeIfPresent(ExportMethod.self, forKey: .exportMethod)
self.matchURL = try container.decodeIfPresent(String.self, forKey: .matchURL)
self.style = try container.decodeIfPresent(iOSSigning.SigningStyle.self, forKey: .style) ?? .manual
}

init(teamName: String?, teamID: String?, exportMethod: ExportMethod?, matchURL: String?, style: SigningStyle) {
self.teamName = teamName
self.teamID = teamID
self.exportMethod = exportMethod
self.matchURL = matchURL
self.style = style
}
}

extension iOSSigning {
enum `Type`: String, Codable {
enum ExportMethod: String, Codable {
case appstore
case development
case adhoc
Expand All @@ -42,6 +61,11 @@ extension iOSSigning {
}
}
}

enum SigningStyle: String, Codable {
case automatic
case manual
}
}

extension iOSSigning {
Expand All @@ -62,7 +86,7 @@ extension iOSSigning {
for property in mirroredObject.children {
if let label = property.label {
let stringValue = property.value as? String
let typeValue = (property.value as? Type)?.rawValue
let typeValue = (property.value as? ExportMethod)?.rawValue
if let value = stringValue ?? typeValue {
customProperties.append(CustomProperty(name: label.uppercased(),
value: value,
Expand All @@ -77,17 +101,17 @@ extension iOSSigning {
infix operator ~: AdditionPrecedence
extension iOSSigning {
static func ~ (lhs: iOSSigning, rhs: iOSSigning?) throws -> iOSSigning {
let signing = iOSSigning(teamName: lhs.teamName ?? rhs?.teamName,
teamID: lhs.teamID ?? rhs?.teamID,
exportMethod: lhs.exportMethod ?? rhs?.exportMethod,
matchURL: lhs.matchURL ?? rhs?.matchURL)
if signing.teamName == nil {
throw iOSSigning.missingParameterError(CodingKeys.teamName)
} else if signing.teamID == nil {
throw iOSSigning.missingParameterError(CodingKeys.teamID)
} else if signing.exportMethod == nil {
throw iOSSigning.missingParameterError(CodingKeys.exportMethod)
}
let signing = iOSSigning(
teamName: lhs.teamName ?? rhs?.teamName,
teamID: lhs.teamID ?? rhs?.teamID,
exportMethod: lhs.exportMethod ?? rhs?.exportMethod,
matchURL: lhs.matchURL ?? rhs?.matchURL,
style: lhs.style)

guard signing.teamName != nil else { throw iOSSigning.missingParameterError(CodingKeys.teamName) }
guard signing.teamID != nil else { throw iOSSigning.missingParameterError(CodingKeys.teamID) }
guard signing.exportMethod != nil else { throw iOSSigning.missingParameterError(CodingKeys.exportMethod) }

return signing
}
}
34 changes: 22 additions & 12 deletions Sources/VariantsCore/Schemas/iOS/iOSVariant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public struct iOSVariant: Variant {
let appIcon: String?
let appName: String?
let storeDestination: Destination
let signing: iOSSigning?
let debugSigning: iOSSigning?
let releaseSigning: iOSSigning?
let custom: [CustomProperty]?
let postSwitchScript: String?

Expand All @@ -40,7 +41,7 @@ public struct iOSVariant: Variant {
name: String, versionName: String, versionNumber: Int, appIcon: String?, appName: String?,
storeDestination: String?, idSuffix: String?, bundleID: String?,
globalCustomProperties: [CustomProperty]?, variantCustomProperties: [CustomProperty]?,
globalSigning: iOSSigning?, variantSigning: iOSSigning?,
globalSigning: iOSSigning?, debugSigning: iOSSigning?, releaseSigning: iOSSigning?,
globalPostSwitchScript: String?, variantPostSwitchScript: String?)
throws {
self.name = name
Expand All @@ -49,7 +50,8 @@ public struct iOSVariant: Variant {
self.appIcon = appIcon
self.appName = appName
self.storeDestination = try Self.parseDestination(name: name, destination: storeDestination) ?? .appStore
self.signing = try Self.parseSigning(name: name, variantSigning: variantSigning, globalSigning: globalSigning)
self.debugSigning = try Self.parseSigning(name: name, override: debugSigning, base: globalSigning)
self.releaseSigning = try Self.parseSigning(name: name, override: releaseSigning, base: globalSigning)
self.custom = Self.parseCustomProperties(variantCustom: variantCustomProperties, globalCustom: globalCustomProperties)
self.bundleNamingOption = try Self.parseBundleConfiguration(name: name, idSuffix: idSuffix, bundleID: bundleID)
self.postSwitchScript = Self.parsePostSwitchScript(globalScript: globalPostSwitchScript,
Expand All @@ -67,6 +69,7 @@ public struct iOSVariant: Variant {
}
}

// TODO: is debug?
func getDefaultValues(for target: iOSTarget) -> [(key: String, value: String)] {
var customDictionary: [String: String] = [
"V_APP_NAME": appName ?? target.name + configName,
Expand All @@ -76,7 +79,7 @@ public struct iOSVariant: Variant {
"V_APP_ICON": appIcon ?? target.app_icon
]

if signing?.matchURL != nil, let exportMethod = signing?.exportMethod {
if releaseSigning?.matchURL != nil, let exportMethod = releaseSigning?.exportMethod {
customDictionary["V_MATCH_PROFILE"] = "\(exportMethod.prefix) \(makeBundleID(for: target))"
}
(custom?.projectConfigurationValues ?? []).forEach { customDictionary[$0.name] = $0.value }
Expand All @@ -98,13 +101,13 @@ public struct iOSVariant: Variant {
return destination
}

private static func parseSigning(name: String, variantSigning: iOSSigning?, globalSigning: iOSSigning?) throws -> iOSSigning? {
if let variantSigning = variantSigning, let globalSigning = globalSigning {
return try variantSigning ~ globalSigning
} else if let variantSigning = variantSigning {
return try variantSigning ~ nil
} else if let globalSigning = globalSigning {
return try globalSigning ~ nil
private static func parseSigning(name: String, override: iOSSigning?, base: iOSSigning?) throws -> iOSSigning? {
if let override, let base {
return try override ~ base
} else if let override {
return try override ~ nil
} else if let base {
return try base ~ nil
} else {
throw RuntimeError(
"""
Expand Down Expand Up @@ -175,6 +178,8 @@ struct UnnamediOSVariant: Codable {
let idSuffix: String?
let bundleID: String?
let signing: iOSSigning?
let debugSigning: iOSSigning?
let releaseSigning: iOSSigning?
let custom: [CustomProperty]?
let storeDestination: String?
let postSwitchScript: String?
Expand All @@ -187,6 +192,8 @@ struct UnnamediOSVariant: Codable {
case idSuffix = "id_suffix"
case bundleID = "bundle_id"
case signing
case releaseSigning = "release_signing"
case debugSigning = "debug_signing"
case custom
case storeDestination = "store_destination"
case postSwitchScript
Expand All @@ -203,6 +210,8 @@ extension UnnamediOSVariant {
idSuffix = try values.decodeIfPresentOrReadFromEnv(String.self, forKey: .idSuffix)
bundleID = try values.decodeIfPresentOrReadFromEnv(String.self, forKey: .bundleID)
signing = try values.decodeIfPresent(iOSSigning.self, forKey: .signing)
debugSigning = try values.decodeIfPresent(iOSSigning.self, forKey: .debugSigning)
releaseSigning = try values.decodeIfPresent(iOSSigning.self, forKey: .releaseSigning)
custom = try values.decodeIfPresent([CustomProperty].self, forKey: .custom)
storeDestination = try values.decodeIfPresentOrReadFromEnv(String.self, forKey: .storeDestination)
postSwitchScript = try values.decodeIfPresent(String.self, forKey: .postSwitchScript)
Expand All @@ -225,7 +234,8 @@ extension iOSVariant {
globalCustomProperties: globalCustomProperties,
variantCustomProperties: unnamediOSVariant.custom,
globalSigning: globalSigning,
variantSigning: unnamediOSVariant.signing,
debugSigning: unnamediOSVariant.debugSigning ?? unnamediOSVariant.signing,
releaseSigning: unnamediOSVariant.releaseSigning ?? unnamediOSVariant.signing,
globalPostSwitchScript: globalPostSwitchScript,
variantPostSwitchScript: unnamediOSVariant.postSwitchScript)
}
Expand Down
Loading

0 comments on commit 5ac0892

Please sign in to comment.