From 46a17d9319091fef97c25ba4196ddb90cea8c6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D1=82=D0=B0=D0=BB=D0=B8=D0=B9=20=D0=91=D0=B0?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Tue, 14 Jan 2025 19:02:54 +0300 Subject: [PATCH] Replaced default remote repo provider (#67) * Replaced default remote repo provider - GitHub API can be choosed using --provider github variable - Default API - Forgejo - GenerateCommand can be modified through custom EventProviderGenerator * remote host URI parser added * fix --------- Co-authored-by: v.barabanov --- .../Commands/GenerateCommand.swift | 56 ++++-- .../DefaultDependeciesGenerator.swift | 59 +++++++ .../DependeciesGeneratorError.swift | 15 ++ .../DependenciesGenerator.swift | 11 ++ Sources/AnalyticsGen/Dependencies.swift | 36 ---- .../Event/DefaultEventGenerator.swift | 48 +++--- .../Generators/Event/EventGenerator.swift | 2 +- .../Finders/RemoteRepoReferenceFinder.swift | 22 +-- .../Models/Configuration/Configuration.swift | 1 + .../Finder/FinderConditionConfiguration.swift | 0 .../Finder/FinderSourceConfiguration.swift | 0 ...oteRepoReferenceFinderConfiguration.swift} | 6 +- ...epoReferenceFinderTypeConfiguration.swift} | 2 +- .../RemoteRepoReferenceConfiguration.swift} | 10 +- .../RemoteRepoSourceConfiguration.swift} | 4 +- .../Configuration/SourceConfiguration.swift | 12 +- .../Models/Configuration/Target.swift | 2 +- .../Models/Git/GitReference.swift | 2 +- .../Forgejo/DTOs/ForgejoCommit.swift | 5 + .../Providers/Forgejo/DTOs/ForgejoRef.swift | 5 + .../Providers/Forgejo/DTOs/QueryCount.swift | 3 + .../Forgejo/ForgejoRemoteRepoProvider.swift | 161 ++++++++++++++++++ .../GitHub/GitHubRemoteRepoProvider.swift | 13 +- .../{GitHub => }/RemoteRepoProvider.swift | 0 .../Providers/RemoteRepoProviderType.swift | 15 ++ Sources/AnalyticsGen/main.swift | 2 +- 26 files changed, 378 insertions(+), 114 deletions(-) create mode 100644 Sources/AnalyticsGen/DependeciesGenerator/DefaultDependeciesGenerator.swift create mode 100644 Sources/AnalyticsGen/DependeciesGenerator/DependeciesGeneratorError.swift create mode 100644 Sources/AnalyticsGen/DependeciesGenerator/DependenciesGenerator.swift delete mode 100644 Sources/AnalyticsGen/Dependencies.swift rename Sources/AnalyticsGen/Models/Configuration/{GitHub => RemoteRepo}/Finder/FinderConditionConfiguration.swift (100%) rename Sources/AnalyticsGen/Models/Configuration/{GitHub => RemoteRepo}/Finder/FinderSourceConfiguration.swift (100%) rename Sources/AnalyticsGen/Models/Configuration/{GitHub/Finder/GitHubReferenceFinderConfiguration.swift => RemoteRepo/Finder/RemoteRepoReferenceFinderConfiguration.swift} (90%) rename Sources/AnalyticsGen/Models/Configuration/{GitHub/Finder/GitHubReferenceFinderTypeConfiguration.swift => RemoteRepo/Finder/RemoteRepoReferenceFinderTypeConfiguration.swift} (79%) rename Sources/AnalyticsGen/Models/Configuration/{GitHub/GitHubReferenceConfiguration.swift => RemoteRepo/RemoteRepoReferenceConfiguration.swift} (79%) rename Sources/AnalyticsGen/Models/Configuration/{GitHub/GitHubSourceConfiguration.swift => RemoteRepo/RemoteRepoSourceConfiguration.swift} (61%) create mode 100644 Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoCommit.swift create mode 100644 Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoRef.swift create mode 100644 Sources/AnalyticsGen/Providers/Forgejo/DTOs/QueryCount.swift create mode 100644 Sources/AnalyticsGen/Providers/Forgejo/ForgejoRemoteRepoProvider.swift rename Sources/AnalyticsGen/Providers/{GitHub => }/RemoteRepoProvider.swift (100%) create mode 100644 Sources/AnalyticsGen/Providers/RemoteRepoProviderType.swift diff --git a/Sources/AnalyticsGen/Commands/GenerateCommand.swift b/Sources/AnalyticsGen/Commands/GenerateCommand.swift index 85a98c2..03b2bbc 100644 --- a/Sources/AnalyticsGen/Commands/GenerateCommand.swift +++ b/Sources/AnalyticsGen/Commands/GenerateCommand.swift @@ -1,15 +1,16 @@ +import DictionaryCoder import Foundation import SwiftCLI import PromiseKit import AnalyticsGenTools final class GenerateCommand: AsyncExecutableCommand { - + // MARK: - Instance Properties - + let name = "generate" let shortDescription = "Generate analytics events from schemas" - + let configurationPath = Key( "--config", "-c", @@ -18,7 +19,7 @@ final class GenerateCommand: AsyncExecutableCommand { Defaults to '\(String.defaultConfigurationPath)'. """ ) - + let force = Flag( "--force", description: """ @@ -26,34 +27,51 @@ final class GenerateCommand: AsyncExecutableCommand { By default, generation will perform only if has new commits. """ ) - + let debug = Flag( "--debug", description: "Enable debug logging." ) - - let generator: EventGenerator - - // MARK: - Initializers - - init(generator: EventGenerator) { - self.generator = generator + + let provider = Key( + "--provider", + "-p", + description: "Enter custom remote repository provider. Default - Forgejo" + ) + + let dependeciesGenerator: DependenciesGenerator + private(set) var generator: EventGenerator? + private let fileProvider: FileProvider = YAMLFileProvider() + + init( + dependeciesGenerator: DependenciesGenerator = DefaultDependeciesGenerator() + ) { + self.dependeciesGenerator = dependeciesGenerator } - + // MARK: - AsyncExecutableCommand - + func executeAsyncAndExit() throws { let configurationPath = self.configurationPath.value ?? .defaultConfigurationPath - + let selectedRemoteRepoProvider = self.provider.value ?? .defaultRemoteRepoProvider + let configuration = try fileProvider.readFile(at: configurationPath, type: Configuration.self) + let remoteHost = configuration.remoteHost ?? .defaultRemoteRepoURI + + generator = try dependeciesGenerator.createGenerator(for: selectedRemoteRepoProvider, remoteHost: remoteHost) + Log.isDebugLoggingEnabled = debug.value - + + guard let generator = generator else { + self.fail(message: "No generator setted up") + } + firstly { - generator.generate(configurationPath: configurationPath, force: force.value) + generator.generate(configuration: configuration, force: force.value) }.done { result in switch result { case .success: self.succeed(message: "Generation completed successfully!") - + case .upToDate: self.succeed(message: "Analytic events is up to date!") } @@ -68,4 +86,6 @@ private extension String { // MARK: - Type Properties static let defaultConfigurationPath = ".analyticsGen.yml" + static let defaultRemoteRepoProvider = "forgejo" + static let defaultRemoteRepoURI = "https://forgejo.pyn.ru/api/v1" } diff --git a/Sources/AnalyticsGen/DependeciesGenerator/DefaultDependeciesGenerator.swift b/Sources/AnalyticsGen/DependeciesGenerator/DefaultDependeciesGenerator.swift new file mode 100644 index 0000000..b821480 --- /dev/null +++ b/Sources/AnalyticsGen/DependeciesGenerator/DefaultDependeciesGenerator.swift @@ -0,0 +1,59 @@ +import Foundation +import AnalyticsGenTools +import DictionaryCoder + +final class DefaultDependeciesGenerator: DependenciesGenerator { + let httpService: HTTPService + let yamlFileProvider: FileProvider + let templateContextCoder: TemplateContextCoder + let stencilExtensions: [StencilExtension] + let templateRenderer: TemplateRenderer + + init() { + self.httpService = HTTPService() + self.yamlFileProvider = YAMLFileProvider() + self.templateContextCoder = DefaultTemplateContextCoder() + self.stencilExtensions = [ + StencilStringUppercasePrefixFilter(), + StencilStringUppercaseSuffixFilter(), + StencilStringMultilineFilter(), + StencilStringMultilineAlignmentFilter() + ] + self.templateRenderer = DefaultTemplateRenderer( + contextCoder: templateContextCoder, + stencilExtensions: stencilExtensions + ) + } + + func createGenerator(for provider: String, remoteHost: String) throws -> EventGenerator { + + guard let baseURL = URL(string: remoteHost) else { + throw DependeciesGeneratorError.invalidRemoteHostURI + } + + let repoProviderType = try RemoteRepoProviderType.from(string: provider) + + var remoteRepoProvider: RemoteRepoProvider? + + switch repoProviderType { + case .forgejo: + remoteRepoProvider = ForgejoRemoteRepoProvider(baseURL: baseURL, httpService: httpService) + case .github: + remoteRepoProvider = GitHubRemoteRepoProvider(baseURL: baseURL, httpService: httpService) + } + + guard let remoteRepoProvider = remoteRepoProvider else { + throw DependeciesGeneratorError.unknownProvider + } + + let remoteRepoReferenceFinder = RemoteRepoReferenceFinder(remoteRepoProvider: remoteRepoProvider) + + return DefaultEventGenerator( + fileProvider: yamlFileProvider, + remoteRepoProvider: remoteRepoProvider, + templateRenderer: templateRenderer, + dictionaryDecoder: DictionaryDecoder(), + remoteRepoReferenceFinder: remoteRepoReferenceFinder + ) + } +} diff --git a/Sources/AnalyticsGen/DependeciesGenerator/DependeciesGeneratorError.swift b/Sources/AnalyticsGen/DependeciesGenerator/DependeciesGeneratorError.swift new file mode 100644 index 0000000..44ae221 --- /dev/null +++ b/Sources/AnalyticsGen/DependeciesGenerator/DependeciesGeneratorError.swift @@ -0,0 +1,15 @@ +import Foundation + +enum DependeciesGeneratorError: Error { + case unknownProvider + case invalidRemoteHostURI + + var errorDescription: String { + switch self { + case .unknownProvider: + "The specified remote repository provider is unknown" + case .invalidRemoteHostURI: + "The remote repository URI is not correct" + } + } +} diff --git a/Sources/AnalyticsGen/DependeciesGenerator/DependenciesGenerator.swift b/Sources/AnalyticsGen/DependeciesGenerator/DependenciesGenerator.swift new file mode 100644 index 0000000..bcb3e3e --- /dev/null +++ b/Sources/AnalyticsGen/DependeciesGenerator/DependenciesGenerator.swift @@ -0,0 +1,11 @@ +import AnalyticsGenTools + +protocol DependenciesGenerator { + var httpService: HTTPService { get } + var yamlFileProvider: FileProvider { get } + var templateContextCoder: TemplateContextCoder { get } + var stencilExtensions: [StencilExtension] { get } + var templateRenderer: TemplateRenderer { get } + + func createGenerator(for provider: String, remoteHost: String) throws -> EventGenerator +} diff --git a/Sources/AnalyticsGen/Dependencies.swift b/Sources/AnalyticsGen/Dependencies.swift deleted file mode 100644 index dfb380f..0000000 --- a/Sources/AnalyticsGen/Dependencies.swift +++ /dev/null @@ -1,36 +0,0 @@ -import Foundation -import AnalyticsGenTools -import DictionaryCoder - -enum Dependencies { - - // MARK: - Type Properties - - static let httpService = HTTPService() - - static let gitHubRemoteRepoProvider: RemoteRepoProvider = GitHubRemoteRepoProvider(httpService: httpService) - static let yamlFileProvider: FileProvider = YAMLFileProvider() - static let remoteRepoReferenceFinder = RemoteRepoReferenceFinder(remoteRepoProvider: gitHubRemoteRepoProvider) - - static let templateContextCoder: TemplateContextCoder = DefaultTemplateContextCoder() - - static let stencilExtensions: [StencilExtension] = [ - StencilStringUppercasePrefixFilter(), - StencilStringUppercaseSuffixFilter(), - StencilStringMultilineFilter(), - StencilStringMultilineAlignmentFilter() - ] - - static let templateRenderer: TemplateRenderer = DefaultTemplateRenderer( - contextCoder: templateContextCoder, - stencilExtensions: stencilExtensions - ) - - static let eventGenerator: EventGenerator = DefaultEventGenerator( - fileProvider: yamlFileProvider, - remoteRepoProvider: gitHubRemoteRepoProvider, - templateRenderer: templateRenderer, - dictionaryDecoder: DictionaryDecoder(), - remoteRepoReferenceFinder: remoteRepoReferenceFinder - ) -} diff --git a/Sources/AnalyticsGen/Generators/Event/DefaultEventGenerator.swift b/Sources/AnalyticsGen/Generators/Event/DefaultEventGenerator.swift index c0884e8..dcc02fd 100644 --- a/Sources/AnalyticsGen/Generators/Event/DefaultEventGenerator.swift +++ b/Sources/AnalyticsGen/Generators/Event/DefaultEventGenerator.swift @@ -267,24 +267,24 @@ final class DefaultEventGenerator: EventGenerator { } private func generateFromRemoteRepo( - gitHubConfiguration: GitHubSourceConfiguration, + remoteRepoConfiguration: RemoteRepoSourceConfiguration, ref: String, configuration: GeneratedConfiguration, remoteReferenceSHA: String ) -> Promise { firstly { remoteRepoProvider.fetchRepo( - owner: gitHubConfiguration.owner, - repo: gitHubConfiguration.repo, + owner: remoteRepoConfiguration.owner, + repo: remoteRepoConfiguration.repo, ref: ref, - token: try gitHubConfiguration.accessToken.resolveToken(), + token: try remoteRepoConfiguration.accessToken.resolveToken(), key: configuration.name ) }.map { repoPathURL in try self.generate( configuration: configuration, - targetPath: gitHubConfiguration.path, - schemasPath: gitHubConfiguration.path.map { targetPath in + targetPath: remoteRepoConfiguration.path, + schemasPath: remoteRepoConfiguration.path.map { targetPath in repoPathURL.appendingPathComponent(targetPath) } ?? repoPathURL ) @@ -296,17 +296,17 @@ final class DefaultEventGenerator: EventGenerator { } private func fetchRemoteReferenceSHA( - gitHubConfiguration: GitHubSourceConfiguration, + remoteRepoConfiguration: RemoteRepoSourceConfiguration, gitReferenceType: GitReferenceType ) -> Promise { switch gitReferenceType { case .tag, .branch: return firstly { remoteRepoProvider.fetchReference( - owner: gitHubConfiguration.owner, - repo: gitHubConfiguration.repo, + owner: remoteRepoConfiguration.owner, + repo: remoteRepoConfiguration.repo, ref: gitReferenceType.rawValue, - token: try gitHubConfiguration.accessToken.resolveToken() + token: try remoteRepoConfiguration.accessToken.resolveToken() ) }.map { reference in reference.object.sha @@ -318,13 +318,13 @@ final class DefaultEventGenerator: EventGenerator { } private func generateFromRemoteRepo( - gitHubConfiguration: GitHubSourceConfiguration, + remoteRepoConfiguration: RemoteRepoSourceConfiguration, gitReferenceType: GitReferenceType, configuration: GeneratedConfiguration, force: Bool ) -> Promise { firstly { - fetchRemoteReferenceSHA(gitHubConfiguration: gitHubConfiguration, gitReferenceType: gitReferenceType) + fetchRemoteReferenceSHA(remoteRepoConfiguration: remoteRepoConfiguration, gitReferenceType: gitReferenceType) }.then { remoteReferenceSHA in let shouldPerformGeneration = try self.shouldGenerate( configuration: configuration, @@ -333,7 +333,7 @@ final class DefaultEventGenerator: EventGenerator { if shouldPerformGeneration || force { return self.generateFromRemoteRepo( - gitHubConfiguration: gitHubConfiguration, + remoteRepoConfiguration: remoteRepoConfiguration, ref: gitReferenceType.rawValue, configuration: configuration, remoteReferenceSHA: remoteReferenceSHA @@ -345,8 +345,8 @@ final class DefaultEventGenerator: EventGenerator { } private func performFinders( - finderConfigurations: [GitHubReferenceFinderConfiguration], - gitHubConfiguration: GitHubSourceConfiguration, + finderConfigurations: [RemoteRepoReferenceFinderConfiguration], + remoteRepoConfiguration: RemoteRepoSourceConfiguration, generatedConfiguration: GeneratedConfiguration, force: Bool ) throws -> Promise { @@ -361,7 +361,7 @@ final class DefaultEventGenerator: EventGenerator { return try remoteRepoReferenceFinder .findReference( configurations: finderConfigurations, - gitHubConfiguration: gitHubConfiguration + remoteRepoConfiguration: remoteRepoConfiguration ) .map { gitReferenceType in if let gitReferenceType { @@ -381,7 +381,7 @@ final class DefaultEventGenerator: EventGenerator { } .then { gitReferenceType in self.generateFromRemoteRepo( - gitHubConfiguration: gitHubConfiguration, + remoteRepoConfiguration: remoteRepoConfiguration, gitReferenceType: gitReferenceType, configuration: generatedConfiguration, force: force @@ -402,11 +402,11 @@ final class DefaultEventGenerator: EventGenerator { return .value(.success) - case .gitHub(let gitHubConfiguration): - switch gitHubConfiguration.ref { + case .remoteRepo(let remoteRepoConfiguration): + switch remoteRepoConfiguration.ref { case .tag(let name): return generateFromRemoteRepo( - gitHubConfiguration: gitHubConfiguration, + remoteRepoConfiguration: remoteRepoConfiguration, gitReferenceType: .tag(name: name), configuration: configuration, force: force @@ -414,7 +414,7 @@ final class DefaultEventGenerator: EventGenerator { case .branch(let name): return generateFromRemoteRepo( - gitHubConfiguration: gitHubConfiguration, + remoteRepoConfiguration: remoteRepoConfiguration, gitReferenceType: .branch(name: name), configuration: configuration, force: force @@ -423,7 +423,7 @@ final class DefaultEventGenerator: EventGenerator { case .finders(let finders): return try performFinders( finderConfigurations: finders, - gitHubConfiguration: gitHubConfiguration, + remoteRepoConfiguration: remoteRepoConfiguration, generatedConfiguration: configuration, force: force ) @@ -433,9 +433,9 @@ final class DefaultEventGenerator: EventGenerator { // MARK: - EventGenerator - func generate(configurationPath: String, force: Bool) -> Promise { + func generate(configuration: Configuration, force: Bool) -> Promise { firstly { - Promise.value(try fileProvider.readFile(at: configurationPath, type: Configuration.self)) + Promise.value(configuration) }.map { configuration in configuration.configurations.reversed() }.get { configurations in diff --git a/Sources/AnalyticsGen/Generators/Event/EventGenerator.swift b/Sources/AnalyticsGen/Generators/Event/EventGenerator.swift index 7be1aca..c415dc2 100644 --- a/Sources/AnalyticsGen/Generators/Event/EventGenerator.swift +++ b/Sources/AnalyticsGen/Generators/Event/EventGenerator.swift @@ -5,5 +5,5 @@ protocol EventGenerator { // MARK: - Instance Methods - func generate(configurationPath: String, force: Bool) -> Promise + func generate(configuration: Configuration, force: Bool) -> Promise } diff --git a/Sources/AnalyticsGen/Generators/Event/Finders/RemoteRepoReferenceFinder.swift b/Sources/AnalyticsGen/Generators/Event/Finders/RemoteRepoReferenceFinder.swift index 0e54253..267d49d 100644 --- a/Sources/AnalyticsGen/Generators/Event/Finders/RemoteRepoReferenceFinder.swift +++ b/Sources/AnalyticsGen/Generators/Event/Finders/RemoteRepoReferenceFinder.swift @@ -30,8 +30,8 @@ final class RemoteRepoReferenceFinder { } private func findReference( - configuration: GitHubReferenceFinderConfiguration, - gitHubConfiguration: GitHubSourceConfiguration, + configuration: RemoteRepoReferenceFinderConfiguration, + remoteRepoConfiguration: RemoteRepoSourceConfiguration, tags: [String] ) throws -> GitReferenceType? { guard try configuration.runCondition?.isSatisfyCondition() ?? true else { @@ -84,10 +84,10 @@ final class RemoteRepoReferenceFinder { case .lastCommit(let branch): return try remoteRepoProvider .fetchLastCommitSHA( - owner: gitHubConfiguration.owner, - repo: gitHubConfiguration.repo, + owner: remoteRepoConfiguration.owner, + repo: remoteRepoConfiguration.repo, branch: branch, - token: try gitHubConfiguration.accessToken.resolveToken() + token: try remoteRepoConfiguration.accessToken.resolveToken() ) .map { .commit(sha: $0) } .wait() @@ -95,15 +95,15 @@ final class RemoteRepoReferenceFinder { } func findReference( - configurations: [GitHubReferenceFinderConfiguration], - gitHubConfiguration: GitHubSourceConfiguration + configurations: [RemoteRepoReferenceFinderConfiguration], + remoteRepoConfiguration: RemoteRepoSourceConfiguration ) throws -> Promise { remoteRepoProvider .fetchTagList( - owner: gitHubConfiguration.owner, - repo: gitHubConfiguration.repo, + owner: remoteRepoConfiguration.owner, + repo: remoteRepoConfiguration.repo, count: 100, - token: try gitHubConfiguration.accessToken.resolveToken() + token: try remoteRepoConfiguration.accessToken.resolveToken() ) .map(on: .global()) { tags in try configurations @@ -111,7 +111,7 @@ final class RemoteRepoReferenceFinder { .mapFirst { index, configuration in let gitRefenceType = try self.findReference( configuration: configuration, - gitHubConfiguration: gitHubConfiguration, + remoteRepoConfiguration: remoteRepoConfiguration, tags: tags ) diff --git a/Sources/AnalyticsGen/Models/Configuration/Configuration.swift b/Sources/AnalyticsGen/Models/Configuration/Configuration.swift index 6d2ef57..e92bc9e 100644 --- a/Sources/AnalyticsGen/Models/Configuration/Configuration.swift +++ b/Sources/AnalyticsGen/Models/Configuration/Configuration.swift @@ -3,6 +3,7 @@ import AnalyticsGenTools struct Configuration: Decodable, Equatable { let source: SourceConfiguration + let remoteHost: String? let destination: String? let platform: EventPlatform? let template: TemplateConfiguration? diff --git a/Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/FinderConditionConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/FinderConditionConfiguration.swift similarity index 100% rename from Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/FinderConditionConfiguration.swift rename to Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/FinderConditionConfiguration.swift diff --git a/Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/FinderSourceConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/FinderSourceConfiguration.swift similarity index 100% rename from Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/FinderSourceConfiguration.swift rename to Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/FinderSourceConfiguration.swift diff --git a/Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/GitHubReferenceFinderConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/RemoteRepoReferenceFinderConfiguration.swift similarity index 90% rename from Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/GitHubReferenceFinderConfiguration.swift rename to Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/RemoteRepoReferenceFinderConfiguration.swift index 404fd4e..9d2030c 100644 --- a/Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/GitHubReferenceFinderConfiguration.swift +++ b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/RemoteRepoReferenceFinderConfiguration.swift @@ -1,8 +1,8 @@ import Foundation -struct GitHubReferenceFinderConfiguration: Decodable, Equatable { +struct RemoteRepoReferenceFinderConfiguration: Decodable, Equatable { - let type: GitHubReferenceFinderTypeConfiguration + let type: RemoteRepoReferenceFinderTypeConfiguration let runCondition: FinderSourceConfiguration? init(from decoder: Decoder) throws { @@ -38,7 +38,7 @@ struct GitHubReferenceFinderConfiguration: Decodable, Equatable { } } -extension GitHubReferenceFinderConfiguration { +extension RemoteRepoReferenceFinderConfiguration { private enum CodingKeys: String, CodingKey { case type diff --git a/Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/GitHubReferenceFinderTypeConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/RemoteRepoReferenceFinderTypeConfiguration.swift similarity index 79% rename from Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/GitHubReferenceFinderTypeConfiguration.swift rename to Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/RemoteRepoReferenceFinderTypeConfiguration.swift index 5528006..442ec90 100644 --- a/Sources/AnalyticsGen/Models/Configuration/GitHub/Finder/GitHubReferenceFinderTypeConfiguration.swift +++ b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/Finder/RemoteRepoReferenceFinderTypeConfiguration.swift @@ -1,6 +1,6 @@ import Foundation -enum GitHubReferenceFinderTypeConfiguration: Equatable { +enum RemoteRepoReferenceFinderTypeConfiguration: Equatable { case matchedTag(source: FinderSourceConfiguration, branchRegex: String) case lastMerged(branch: String?, mergeCommitCount: Int, branchRegex: String) case lastTag diff --git a/Sources/AnalyticsGen/Models/Configuration/GitHub/GitHubReferenceConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/RemoteRepoReferenceConfiguration.swift similarity index 79% rename from Sources/AnalyticsGen/Models/Configuration/GitHub/GitHubReferenceConfiguration.swift rename to Sources/AnalyticsGen/Models/Configuration/RemoteRepo/RemoteRepoReferenceConfiguration.swift index e441ebf..9776b69 100644 --- a/Sources/AnalyticsGen/Models/Configuration/GitHub/GitHubReferenceConfiguration.swift +++ b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/RemoteRepoReferenceConfiguration.swift @@ -1,12 +1,12 @@ import Foundation -enum GitHubReferenceConfiguration: Decodable, Equatable { +enum RemoteRepoReferenceConfiguration: Decodable, Equatable { // MARK: - Enumeration Cases case branch(String) case tag(String) - case finders([GitHubReferenceFinderConfiguration]) + case finders([RemoteRepoReferenceFinderConfiguration]) // MARK: - Instance Properties @@ -29,11 +29,11 @@ enum GitHubReferenceConfiguration: Decodable, Equatable { self = .branch(branchName) } else if let tagName = try container.decodeIfPresent(String.self, forKey: .tag) { self = .tag(tagName) - } else if let finders = try container.decodeIfPresent([GitHubReferenceFinderConfiguration].self, forKey: .finders) { + } else if let finders = try container.decodeIfPresent([RemoteRepoReferenceFinderConfiguration].self, forKey: .finders) { self = .finders(finders) } else { throw DecodingError.typeMismatch( - GitHubReferenceConfiguration.self, + RemoteRepoReferenceConfiguration.self, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "Invalid reference, expected 'branch', 'tag' or 'finders'", @@ -44,7 +44,7 @@ enum GitHubReferenceConfiguration: Decodable, Equatable { } } -extension GitHubReferenceConfiguration { +extension RemoteRepoReferenceConfiguration { private enum CodingKeys: String, CodingKey { diff --git a/Sources/AnalyticsGen/Models/Configuration/GitHub/GitHubSourceConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/RemoteRepoSourceConfiguration.swift similarity index 61% rename from Sources/AnalyticsGen/Models/Configuration/GitHub/GitHubSourceConfiguration.swift rename to Sources/AnalyticsGen/Models/Configuration/RemoteRepo/RemoteRepoSourceConfiguration.swift index 004331b..c2bc3dc 100644 --- a/Sources/AnalyticsGen/Models/Configuration/GitHub/GitHubSourceConfiguration.swift +++ b/Sources/AnalyticsGen/Models/Configuration/RemoteRepo/RemoteRepoSourceConfiguration.swift @@ -1,12 +1,12 @@ import Foundation -struct GitHubSourceConfiguration: Decodable, Equatable { +struct RemoteRepoSourceConfiguration: Decodable, Equatable { // MARK: - Instance Properties let owner: String let repo: String let path: String? - let ref: GitHubReferenceConfiguration + let ref: RemoteRepoReferenceConfiguration let accessToken: AccessTokenConfiguration } diff --git a/Sources/AnalyticsGen/Models/Configuration/SourceConfiguration.swift b/Sources/AnalyticsGen/Models/Configuration/SourceConfiguration.swift index 18db2a3..39797a4 100644 --- a/Sources/AnalyticsGen/Models/Configuration/SourceConfiguration.swift +++ b/Sources/AnalyticsGen/Models/Configuration/SourceConfiguration.swift @@ -8,19 +8,19 @@ enum SourceConfiguration: Decodable, Equatable { // MARK: - Enumeration Cases - case gitHub + case remoteRepo } // MARK: - Instance Properties case local(path: String) - case gitHub(configuration: GitHubSourceConfiguration) + case remoteRepo(configuration: RemoteRepoSourceConfiguration) // MARK: - Initializers init(from decoder: Decoder) throws { if let container = try? decoder.container(keyedBy: CodingKeys.self) { - self = .gitHub(configuration: try container.decode(forKey: .gitHub)) + self = .remoteRepo(configuration: try container.decode(forKey: .remoteRepo)) } else { self = .local(path: try String(from: decoder)) } @@ -32,9 +32,9 @@ extension SourceConfiguration { switch source { case .local: self = source - case let .gitHub(configuration: configuration): - self = .gitHub( - configuration: GitHubSourceConfiguration( + case let .remoteRepo(configuration: configuration): + self = .remoteRepo( + configuration: RemoteRepoSourceConfiguration( owner: configuration.owner, repo: configuration.repo, path: target.path ?? configuration.path, diff --git a/Sources/AnalyticsGen/Models/Configuration/Target.swift b/Sources/AnalyticsGen/Models/Configuration/Target.swift index d35a12d..dab5dd2 100644 --- a/Sources/AnalyticsGen/Models/Configuration/Target.swift +++ b/Sources/AnalyticsGen/Models/Configuration/Target.swift @@ -7,5 +7,5 @@ struct Target: Decodable, Equatable { let platform: EventPlatform? let path: String? let destination: String? - let ref: GitHubReferenceConfiguration? + let ref: RemoteRepoReferenceConfiguration? } diff --git a/Sources/AnalyticsGen/Models/Git/GitReference.swift b/Sources/AnalyticsGen/Models/Git/GitReference.swift index c7911e6..efdd5b4 100644 --- a/Sources/AnalyticsGen/Models/Git/GitReference.swift +++ b/Sources/AnalyticsGen/Models/Git/GitReference.swift @@ -28,7 +28,7 @@ struct GitReference: Codable, Equatable { // MARK: - Instance Properties let ref: String - let nodeID: String + let nodeID: String? let url: URL let object: Object } diff --git a/Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoCommit.swift b/Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoCommit.swift new file mode 100644 index 0000000..2ad02b1 --- /dev/null +++ b/Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoCommit.swift @@ -0,0 +1,5 @@ +import Foundation + +struct ForgejoCommit: Decodable, Equatable { + let sha: String +} diff --git a/Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoRef.swift b/Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoRef.swift new file mode 100644 index 0000000..63aceca --- /dev/null +++ b/Sources/AnalyticsGen/Providers/Forgejo/DTOs/ForgejoRef.swift @@ -0,0 +1,5 @@ +import Foundation + +struct ForgejoRef: Decodable, Equatable { + let name: String +} diff --git a/Sources/AnalyticsGen/Providers/Forgejo/DTOs/QueryCount.swift b/Sources/AnalyticsGen/Providers/Forgejo/DTOs/QueryCount.swift new file mode 100644 index 0000000..bf94c44 --- /dev/null +++ b/Sources/AnalyticsGen/Providers/Forgejo/DTOs/QueryCount.swift @@ -0,0 +1,3 @@ +struct ForgejoQueryCount: Encodable { + let limit: Int +} diff --git a/Sources/AnalyticsGen/Providers/Forgejo/ForgejoRemoteRepoProvider.swift b/Sources/AnalyticsGen/Providers/Forgejo/ForgejoRemoteRepoProvider.swift new file mode 100644 index 0000000..839bd84 --- /dev/null +++ b/Sources/AnalyticsGen/Providers/Forgejo/ForgejoRemoteRepoProvider.swift @@ -0,0 +1,161 @@ +import Foundation +import PromiseKit +import AnalyticsGenTools +import ZIPFoundation + +struct ForgejoRemoteRepoProvider: RemoteRepoProvider { + + // MARK: - Instance Properties + let baseURL: URL + let httpService: HTTPService + + init( + baseURL: URL, + httpService: HTTPService + ) { + self.baseURL = baseURL + self.httpService = httpService + } + + // MARK: - RemoteRepoProvider + + func fetchRepo(owner: String, repo: String, ref: String, token: String, key: String) -> Promise { + let downloadURL = baseURL + .appendingPathComponent("repos") + .appendingPathComponent(owner) + .appendingPathComponent(repo) + .appendingPathComponent("archive") + .appendingPathComponent("\(ref).zip") + + return Promise { seal in + httpService + .downloadRequest( + route: HTTPRoute( + method: .get, + url: downloadURL, + headers: [.authorization(bearerToken: token)] + ) + ) + .responseData { httpResponse in + switch httpResponse.result { + case .success(let data): + let fileManager = FileManager.default + + let archiveFileURL = fileManager + .temporaryDirectory + .appendingPathComponent("remote-schemas-\(key).zip") + + let destinationPathURL = fileManager + .temporaryDirectory + .appendingPathComponent("remote-schema-\(key)") + + do { + try data.write(to: archiveFileURL) + try? fileManager.removeItem(at: destinationPathURL) + try fileManager.createDirectory(at: destinationPathURL, withIntermediateDirectories: true) + try fileManager.unzipItem(at: archiveFileURL, to: destinationPathURL) + + let repoPathURL = try fileManager.contentsOfDirectory( + at: destinationPathURL, + includingPropertiesForKeys: nil + ) + + seal.fulfill(repoPathURL[0]) + } catch { + seal.reject(error) + } + + case .failure(let error): + seal.reject(error) + } + } + } + } + + func fetchReference(owner: String, repo: String, ref: String, token: String) -> Promise { + let refURL = baseURL + .appendingPathComponent("repos") + .appendingPathComponent(owner) + .appendingPathComponent(repo) + .appendingPathComponent("git/refs") + .appendingPathComponent(ref) + + return Promise { seal in + httpService + .request( + route: HTTPRoute( + method: .get, + url: refURL, + headers: [.authorization(bearerToken: token)] + ) + ) + .responseDecodable(type: [GitReference].self) { httpResponse in + switch httpResponse.result { + case .success(let result): + seal.fulfill(result[0]) + + case .failure(let error): + seal.reject(error) + } + } + } + } + + func fetchTagList(owner: String, repo: String, count: Int, token: String) -> Promise<[String]> { + let requestURL = baseURL + .appendingPathComponent("repos") + .appendingPathComponent(owner) + .appendingPathComponent(repo) + .appendingPathComponent("tags") + + return Promise { seal in + httpService + .request( + route: HTTPRoute( + method: .get, + url: requestURL, + headers: [.authorization(bearerToken: token)], + queryParameters: ForgejoQueryCount(limit: count) + ) + ) + .responseDecodable(type: [ForgejoRef].self) { httpResponse in + switch httpResponse.result { + case .success(let response): + seal.fulfill(response.map { $0.name }) + + case .failure(let error): + seal.reject(error) + } + } + } + } + + func fetchLastCommitSHA(owner: String, repo: String, branch: String, token: String) -> Promise { + let lastCommitURL = baseURL + .appendingPathComponent("repos") + .appendingPathComponent(owner) + .appendingPathComponent(repo) + .appendingPathComponent("git/commits") + .appendingPathComponent(branch) + + return Promise { seal in + httpService + .request( + route: HTTPRoute( + method: .get, + url: lastCommitURL, + headers: [.authorization(bearerToken: token)] + ) + ) + .responseDecodable(type: ForgejoCommit.self) { httpResponse in + switch httpResponse.result { + case .success(let commit): + seal.fulfill(commit.sha) + + case .failure(let error): + seal.reject(error) + } + } + } + } +} diff --git a/Sources/AnalyticsGen/Providers/GitHub/GitHubRemoteRepoProvider.swift b/Sources/AnalyticsGen/Providers/GitHub/GitHubRemoteRepoProvider.swift index 64e0223..f35c341 100644 --- a/Sources/AnalyticsGen/Providers/GitHub/GitHubRemoteRepoProvider.swift +++ b/Sources/AnalyticsGen/Providers/GitHub/GitHubRemoteRepoProvider.swift @@ -7,12 +7,17 @@ struct GitHubRemoteRepoProvider: RemoteRepoProvider { // MARK: - Instance Properties - private let baseURL = URL(string: "https://api.github.com")! - - // MARK: - - + let baseURL: URL let httpService: HTTPService + init( + baseURL: URL, + httpService: HTTPService + ) { + self.baseURL = baseURL + self.httpService = httpService + } + // MARK: - RemoteRepoProvider func fetchRepo(owner: String, repo: String, ref: String, token: String, key: String) -> Promise { diff --git a/Sources/AnalyticsGen/Providers/GitHub/RemoteRepoProvider.swift b/Sources/AnalyticsGen/Providers/RemoteRepoProvider.swift similarity index 100% rename from Sources/AnalyticsGen/Providers/GitHub/RemoteRepoProvider.swift rename to Sources/AnalyticsGen/Providers/RemoteRepoProvider.swift diff --git a/Sources/AnalyticsGen/Providers/RemoteRepoProviderType.swift b/Sources/AnalyticsGen/Providers/RemoteRepoProviderType.swift new file mode 100644 index 0000000..1023ff4 --- /dev/null +++ b/Sources/AnalyticsGen/Providers/RemoteRepoProviderType.swift @@ -0,0 +1,15 @@ +enum RemoteRepoProviderType: String { + case forgejo + case github + + static func from(string: String) throws -> Self { + switch string.lowercased() { + case "forgejo": + return .forgejo + case "github": + return .github + default: + throw DependeciesGeneratorError.unknownProvider + } + } +} diff --git a/Sources/AnalyticsGen/main.swift b/Sources/AnalyticsGen/main.swift index af819d0..de9895e 100644 --- a/Sources/AnalyticsGen/main.swift +++ b/Sources/AnalyticsGen/main.swift @@ -9,7 +9,7 @@ Path.current = Path(#file).appending("../../../Example") let analyticsGen = CLI(name: "AnalyticsGen", version: .version, description: .description) analyticsGen.commands = [ - GenerateCommand(generator: Dependencies.eventGenerator) + GenerateCommand() ] analyticsGen.goAndExitOnError()