From 2d414b8885b1592b67beb95e0360012d84b08d9c Mon Sep 17 00:00:00 2001 From: gh-action-runner Date: Wed, 27 Nov 2024 22:55:09 +0000 Subject: [PATCH] Squashed 'apollo-ios-codegen/' changes from c497c31b..cfe27487 cfe27487 feature: Adding codegen config support for spm module type versions (apollographql/apollo-ios-dev#539) git-subtree-dir: apollo-ios-codegen git-subtree-split: cfe274876fa5e45b4bebef97beae278331681fd1 --- .../ApolloCodegenConfiguration.swift | 210 +++++++++++++++++- .../ConfigurationValidation.swift | 4 +- Sources/ApolloCodegenLib/Constants.swift | 5 + .../FileGenerators/FileGenerator.swift | 6 +- .../SchemaModuleFileGenerator.swift | 3 +- .../SwiftPackageManagerModuleTemplate.swift | 50 ++++- .../Templates/TemplateRenderer.swift | 3 +- scripts/version-constants.sh | 1 + 8 files changed, 271 insertions(+), 11 deletions(-) rename Sources/ApolloCodegenLib/{ => CodegenConfiguration}/ApolloCodegenConfiguration.swift (89%) create mode 100644 Sources/ApolloCodegenLib/Constants.swift diff --git a/Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift b/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift similarity index 89% rename from Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift rename to Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift index df4de29c4..cb5d9f2d6 100644 --- a/Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift +++ b/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift @@ -268,7 +268,7 @@ public struct ApolloCodegenConfiguration: Codable, Equatable { moduleType: ModuleType ) { self.path = path - self.moduleType = moduleType + self.moduleType = moduleType == .swiftPackageManager ? .swiftPackage(apolloSDKDependency: .default) : moduleType } /// Compatible dependency manager automation. @@ -283,7 +283,12 @@ public struct ApolloCodegenConfiguration: Codable, Equatable { case embeddedInTarget(name: String, accessModifier: AccessModifier = .internal) /// Generates a `Package.swift` file that is suitable for linking the generated schema types /// files to your project using Swift Package Manager. + /// Attention: This case has been deprecated, use .swiftPackage(apolloSDKVersion:) case instead. case swiftPackageManager + /// Generates a `Package.swift` file that is suitable for linking then generated schema types + /// files to your project using Swift Package Manager. Uses the `apolloSDKDependency` + /// to determine how to setup the dependency on `apollo-ios`. + case swiftPackage(apolloSDKDependency: ApolloSDKDependency = .default) /// No module will be created for the generated types and you are required to create the /// module to support your preferred dependency manager. You must specify the name of the /// module you will create in the `schemaNamespace` property as this will be used in `import` @@ -321,12 +326,193 @@ public struct ApolloCodegenConfiguration: Codable, Equatable { self = .embeddedInTarget(name: name, accessModifier: accessModifier) case .swiftPackageManager: - self = .swiftPackageManager + self = .swiftPackage(apolloSDKDependency: .default) + + case .swiftPackage: + let nestedContainer = try container.nestedContainer( + keyedBy: SwiftPackageCodingKeys.self, + forKey: .swiftPackage + ) + + let apolloSDKDependency = try nestedContainer.decodeIfPresent(ApolloSDKDependency.self, forKey: .apolloSDKDependency) ?? ApolloSDKDependency() + self = .swiftPackage(apolloSDKDependency: apolloSDKDependency) case .other: self = .other } } + + /// Configuation for apollo-ios dependency in SPM modules + public struct ApolloSDKDependency: Codable, Equatable { + /// URL for the SPM package dependency, not used for local dependencies. + /// Defaults to 'https://github.com/apollographql/apollo-ios'. + let url: String + /// Type of SPM dependency to use. + let sdkVersion: SDKVersion + + public static let `default` = ApolloSDKDependency() + + public init( + url: String = "https://github.com/apollographql/apollo-ios", + sdkVersion: SDKVersion = .default + ) { + self.url = url + self.sdkVersion = sdkVersion + } + + enum CodingKeys: CodingKey, CaseIterable { + case url + case sdkVersion + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.url, forKey: .url) + + switch self.sdkVersion { + case .default: + try container.encode(self.sdkVersion.stringValue, forKey: .sdkVersion) + default: + try container.encode(self.sdkVersion, forKey: .sdkVersion) + } + } + + public init(from decoder: any Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + try throwIfContainsUnexpectedKey( + container: values, + type: Self.self, + decoder: decoder + ) + + url = try values.decode(String.self, forKey: .url) + + if let version = try? values.decodeIfPresent(SDKVersion.self, forKey: .sdkVersion) { + sdkVersion = version + } else if let versionString = try? values.decodeIfPresent(String.self, forKey: .sdkVersion) { + let version = try SDKVersion(fromString: versionString) + sdkVersion = version + } else { + throw DecodingError.typeMismatch(Self.self, DecodingError.Context.init( + codingPath: values.codingPath, + debugDescription: "No valid 'sdkVersion' provided.", + underlyingError: nil + )) + } + } + + /// Type of SPM dependency + public enum SDKVersion: Codable, Equatable { + /// Configures SPM dependency to use the exact version of apollo-ios + /// that matches the code generation library version currently in use. + /// Results in a dependency that looks like: + /// '.package(url: "https://github.com/apollographql/apollo-ios.git", exact: "{version}")' + case `default` + /// Configures SPM dependency to use the given branch name + /// for the apollo-ios dependency. + /// Results in a dependency that looks like: + /// '.package(url: "...", branch: "{name}")' + case branch(name: String) + /// Configures SPM dependency to use the given commit hash + /// for the apollo-ios dependency. + /// Results in a dependency that looks like: + /// '.package(url: "...", revision: "{hash}")' + case commit(hash: String) + /// Configures SPM dependency to use the given exact version + /// for the apollo-ios dependency. + /// Results in a dependency that looks like: + /// '.package(url: "...", exact: "{version}")' + case exact(version: String) + /// Configures SPM dependency to use a version + /// starting at the given version for the apollo-ios dependency. + /// Results in a dependency that looks like: + /// '.package(url: "...", from: "{version}")' + case from(version: String) + /// Configures SPM dependency to use a local + /// path for the apollo-ios dependency. + /// Results in a dependency that looks like: + /// '.package(path: "{path}")' + case local(path: String) + + public var stringValue: String { + switch self { + case .default: return "default" + case .branch(_): return "branch" + case .commit(_): return "commit" + case .exact(_): return "exact" + case .from(_): return "from" + case .local(_): return "local" + } + } + + public init(fromString str: String) throws { + switch str { + case Self.default.stringValue: + self = .default + default: + throw ApolloConfigurationError.invalidValueForKey(key: "sdkVersion", value: str) + } + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + guard let key = container.allKeys.first else { + throw DecodingError.typeMismatch(Self.self, DecodingError.Context.init( + codingPath: container.codingPath, + debugDescription: "Invalid number of keys found, expected one.", + underlyingError: nil + )) + } + + switch key { + case .default: + self = .default + case .branch: + let nestedContainer = try container.nestedContainer( + keyedBy: BranchCodingKeys.self, + forKey: .branch + ) + + let name = try nestedContainer.decode(String.self, forKey: .name) + self = .branch(name: name) + case .commit: + let nestedContainer = try container.nestedContainer( + keyedBy: CommitCodingKeys.self, + forKey: .commit + ) + + let hash = try nestedContainer.decode(String.self, forKey: .hash) + self = .commit(hash: hash) + case .exact: + let nestedContainer = try container.nestedContainer( + keyedBy: ExactCodingKeys.self, + forKey: .exact + ) + + let version = try nestedContainer.decode(String.self, forKey: .version) + self = .exact(version: version) + case .from: + let nestedContainer = try container.nestedContainer( + keyedBy: FromCodingKeys.self, + forKey: .from + ) + + let version = try nestedContainer.decode(String.self, forKey: .version) + self = .from(version: version) + case .local: + let nestedContainer = try container.nestedContainer( + keyedBy: LocalCodingKeys.self, + forKey: .local + ) + + let path = try nestedContainer.decode(String.self, forKey: .path) + self = .local(path: path) + } + } + } + } } } @@ -1176,6 +1362,24 @@ public struct ApolloCodegenConfiguration: Codable, Equatable { operationManifest: operationManifest ?? Default.operationManifest ) } + +} + +// MARK: Errors + +extension ApolloCodegenConfiguration { + public enum ApolloConfigurationError: Error, LocalizedError { + case invalidValueForKey(key: String, value: String) + + public var errorDescription: String? { + switch self { + case .invalidValueForKey(let key, let value): + return """ + Invalid value '\(value)' provided for key '\(key)'. + """ + } + } + } } // MARK: - Helpers @@ -1185,7 +1389,7 @@ extension ApolloCodegenConfiguration.SchemaTypesFileOutput { var isInModule: Bool { switch moduleType { case .embeddedInTarget: return false - case .swiftPackageManager, .other: return true + case .swiftPackageManager, .swiftPackage, .other: return true } } } diff --git a/Sources/ApolloCodegenLib/ConfigurationValidation.swift b/Sources/ApolloCodegenLib/ConfigurationValidation.swift index f644df9c4..552c74afd 100644 --- a/Sources/ApolloCodegenLib/ConfigurationValidation.swift +++ b/Sources/ApolloCodegenLib/ConfigurationValidation.swift @@ -34,11 +34,11 @@ extension ApolloCodegen.ConfigurationContext { } if case .swiftPackage = self.output.testMocks, - self.output.schemaTypes.moduleType != .swiftPackageManager { + self.output.schemaTypes.moduleType != .swiftPackage() { throw ApolloCodegen.Error.testMocksInvalidSwiftPackageConfiguration } - if case .swiftPackageManager = self.output.schemaTypes.moduleType, + if case .swiftPackage = self.output.schemaTypes.moduleType, self.options.cocoapodsCompatibleImportStatements == true { throw ApolloCodegen.Error.invalidConfiguration(message: """ cocoapodsCompatibleImportStatements cannot be set to 'true' when the output schema types \ diff --git a/Sources/ApolloCodegenLib/Constants.swift b/Sources/ApolloCodegenLib/Constants.swift new file mode 100644 index 000000000..94ad05648 --- /dev/null +++ b/Sources/ApolloCodegenLib/Constants.swift @@ -0,0 +1,5 @@ +import Foundation + +public enum Constants { + public static let CodegenVersion: String = "1.15.3" +} diff --git a/Sources/ApolloCodegenLib/FileGenerators/FileGenerator.swift b/Sources/ApolloCodegenLib/FileGenerators/FileGenerator.swift index beb58147f..0e8f33499 100644 --- a/Sources/ApolloCodegenLib/FileGenerators/FileGenerator.swift +++ b/Sources/ApolloCodegenLib/FileGenerators/FileGenerator.swift @@ -112,7 +112,7 @@ enum FileTarget: Equatable { forConfig config: ApolloCodegen.ConfigurationContext ) -> String { var moduleSubpath: String = "/" - if config.output.schemaTypes.moduleType == .swiftPackageManager { + if config.output.schemaTypes.moduleType == .swiftPackage() { moduleSubpath += "Sources/" } if config.output.operations.isInModule { @@ -132,7 +132,7 @@ enum FileTarget: Equatable { switch config.output.operations { case .inSchemaModule: var url = URL(fileURLWithPath: config.output.schemaTypes.path, relativeTo: config.rootURL) - if config.output.schemaTypes.moduleType == .swiftPackageManager { + if config.output.schemaTypes.moduleType == .swiftPackage() { url = url.appendingPathComponent("Sources") } @@ -167,7 +167,7 @@ enum FileTarget: Equatable { switch config.output.operations { case .inSchemaModule: var url = URL(fileURLWithPath: config.output.schemaTypes.path, relativeTo: config.rootURL) - if config.output.schemaTypes.moduleType == .swiftPackageManager { + if config.output.schemaTypes.moduleType == .swiftPackage() { url = url.appendingPathComponent("Sources") } if !operation.isLocalCacheMutation { diff --git a/Sources/ApolloCodegenLib/FileGenerators/SchemaModuleFileGenerator.swift b/Sources/ApolloCodegenLib/FileGenerators/SchemaModuleFileGenerator.swift index f47dec6d6..ca524e5b6 100644 --- a/Sources/ApolloCodegenLib/FileGenerators/SchemaModuleFileGenerator.swift +++ b/Sources/ApolloCodegenLib/FileGenerators/SchemaModuleFileGenerator.swift @@ -20,7 +20,8 @@ struct SchemaModuleFileGenerator { let errors: [ApolloCodegen.NonFatalError] switch config.output.schemaTypes.moduleType { - case .swiftPackageManager: + case .swiftPackageManager, + .swiftPackage(_): filePath = pathURL.appendingPathComponent("Package.swift").path (rendered, errors) = SwiftPackageManagerModuleTemplate( testMockConfig: config.output.testMocks, diff --git a/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift b/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift index 7c3fa07dc..c544e1dfd 100644 --- a/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift +++ b/Sources/ApolloCodegenLib/Templates/SwiftPackageManagerModuleTemplate.swift @@ -10,6 +10,23 @@ struct SwiftPackageManagerModuleTemplate: TemplateRenderer { let target: TemplateTarget = .moduleFile let config: ApolloCodegen.ConfigurationContext + + let apolloSDKDependency: ApolloCodegenConfiguration.SchemaTypesFileOutput.ModuleType.ApolloSDKDependency + + init( + testMockConfig: ApolloCodegenConfiguration.TestMockFileOutput, + config: ApolloCodegen.ConfigurationContext + ) { + self.testMockConfig = testMockConfig + self.config = config + + switch config.config.output.schemaTypes.moduleType { + case .swiftPackage(let apolloSDKDependency): + self.apolloSDKDependency = apolloSDKDependency + default: + self.apolloSDKDependency = .default + } + } func renderHeaderTemplate( nonFatalErrorRecorder: ApolloCodegen.NonFatalError.Recorder @@ -42,7 +59,7 @@ struct SwiftPackageManagerModuleTemplate: TemplateRenderer { """}) ], dependencies: [ - .package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.0.0"), + \(apolloSDKDependency.dependencyString), ], targets: [ .target( @@ -81,3 +98,34 @@ struct SwiftPackageManagerModuleTemplate: TemplateRenderer { } } } + +fileprivate extension ApolloCodegenConfiguration.SchemaTypesFileOutput.ModuleType.ApolloSDKDependency { + var dependencyString: TemplateString { + switch self.sdkVersion { + case .default: + return """ + .package(url: "\(self.url)", exact: "\(Constants.CodegenVersion)") + """ + case .branch(let name): + return """ + .package(url: "\(self.url)", branch: "\(name)") + """ + case .commit(let hash): + return """ + .package(url: "\(self.url)", revision: "\(hash)") + """ + case .exact(let version): + return """ + .package(url: "\(self.url)", exact: "\(version)") + """ + case .from(let version): + return """ + .package(url: "\(self.url)", from: "\(version)") + """ + case .local(let path): + return """ + .package(path: "\(path)") + """ + } + } +} diff --git a/Sources/ApolloCodegenLib/Templates/TemplateRenderer.swift b/Sources/ApolloCodegenLib/Templates/TemplateRenderer.swift index fd0118c72..c25f9d42b 100644 --- a/Sources/ApolloCodegenLib/Templates/TemplateRenderer.swift +++ b/Sources/ApolloCodegenLib/Templates/TemplateRenderer.swift @@ -250,6 +250,7 @@ extension TemplateRenderer { return ApolloCodegenConfiguration.AccessModifier.internal.swiftString case (.swiftPackageManager, _), + (.swiftPackage, _), (.other, _): return ApolloCodegenConfiguration.AccessModifier.public.swiftString } @@ -378,7 +379,7 @@ fileprivate extension ApolloCodegenConfiguration { var schemaModuleName: String { switch output.schemaTypes.moduleType { case let .embeddedInTarget(targetName, _): return targetName - case .swiftPackageManager, .other: return schemaNamespace.firstUppercased + case .swiftPackageManager, .swiftPackage, .other: return schemaNamespace.firstUppercased } } } diff --git a/scripts/version-constants.sh b/scripts/version-constants.sh index 1b9dafd1d..211cd7cdd 100644 --- a/scripts/version-constants.sh +++ b/scripts/version-constants.sh @@ -1 +1,2 @@ CLI_CONSTANTS_FILE="Sources/CodegenCLI/Constants.swift" +CODEGEN_CONSTANTS_FILE="Sources/ApolloCodegenLib/Constants.swift"