From 8ad6ccad9c377b8fd7a5a7ec9e89916021905951 Mon Sep 17 00:00:00 2001 From: Vladimir Vlasov Date: Sat, 25 Feb 2023 14:31:00 +0100 Subject: [PATCH 1/5] Add '--package-checkout-path' and '--license-file-names' parameters support --- README.md | 15 ++++++ Sources/LicensePlist/main.swift | 11 ++++ Sources/LicensePlistCore/Consts.swift | 1 + .../Entity/GeneralOptions.swift | 12 +++++ .../Entity/GitHubLicense.swift | 26 +++++++++- Sources/LicensePlistCore/Entity/Options.swift | 8 +++ .../LicensePlistCore/Entity/PlistInfo.swift | 52 +++++++++++++------ .../Entity/SwiftPackage.swift | 30 ++++++++--- Sources/LicensePlistCore/LicensePlist.swift | 2 +- .../Entity/ConfigTests.swift | 2 + .../Entity/GitHubLicense.collectorTests.swift | 4 +- .../Entity/PlistInfoTests.swift | 4 +- .../Resources/license_plist.yml | 5 ++ 13 files changed, 144 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 6ab8b902..454d336c 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,11 @@ You can see options by `license-plist --help`. - Support for multiple `Package.swift` - Example: `license-plist --package-paths /path/to/package1/Package.swift /path/to/package2/Package.swift` +#### `--package-checkout-path` + +- The directory with cloned Swift package sources. If specified `LicensePlist` uses cloned files instead of GitHub API. For more information, see parameter `-clonedSourcePackagesDirPath` of [xcodebuild](https://developer.apple.com/library/archive/technotes/tn2339/_index.html). +- Example: `license-plist --package-checkout-path ./checkouts` + #### `--xcodeproj-path` - Default: `"*.xcodeproj"` @@ -157,6 +162,11 @@ You can see options by `license-plist --help`. - If this path is specified, a markdown acknowledgements file will be generated. - [Example is here](https://github.com/mono0926/LicensePlist/blob/master/Assets/acknowledgements.md) +#### `--license-file-names` + +- Default: `LICENSE, LICENSE.txt, LICENSE.md`. +- License file name variants. Might be used only in combination with `--package-checkout-path`. + #### `--force` - Default: false @@ -248,12 +258,17 @@ options: podsPath: Pods packagePaths: - Package.swift + packageCheckoutPath: ""./checkouts" xcodeprojPath: "*.xcodeproj" xcworkspacePath: "*.xcworkspace" prefix: com.mono0926.LicensePlist gitHubToken: YOUR_GITHUB_TOKEN htmlPath: acknowledgements.html markdownPath: acknowledgements.md + licenseFileNames: + - LICENSE + - LICENSE.txt + - LICENSE.md force: false addVersionNumbers: false suppressOpeningDirectory: false diff --git a/Sources/LicensePlist/main.swift b/Sources/LicensePlist/main.swift index 814872c3..df6b269a 100644 --- a/Sources/LicensePlist/main.swift +++ b/Sources/LicensePlist/main.swift @@ -35,6 +35,9 @@ struct LicensePlist: ParsableCommand { @Option(name: [.customLong("package-path"), .customLong("swift-package-path"), .long, .customLong("swift-package-paths")], parsing: .upToNextOption, completion: .file()) var packagePaths = [String]() + + @Option(name: [.long, .customLong("swift-package-checkout-path")], completion: .directory) + var packageCheckoutPath: String? @Option(name: .long, completion: .file()) var xcworkspacePath: String? @@ -59,6 +62,9 @@ struct LicensePlist: ParsableCommand { @Option(name: .long, completion: .file()) var markdownPath: String? + + @Option(name: .long, parsing: .upToNextOption, completion: .empty) + var licenseFileNames = [String]() @Flag(name: .long, inversion: .prefixedNo) var force: Bool? @@ -103,6 +109,7 @@ struct LicensePlist: ParsableCommand { let podsPath = podsPath ?? config.options.podsPath ?? Consts.podsDirectoryName let configPackagePaths = config.options.packagePaths ?? [Consts.packageName] let packagePaths = packagePaths.isEmpty ? configPackagePaths : packagePaths + let packageCheckoutPath = packageCheckoutPath ?? config.options.packageCheckoutPath let xcworkspacePath = xcworkspacePath ?? config.options.xcworkspacePath ?? Consts.xcworkspacePath let xcodeprojPath = xcodeprojPath ?? config.options.xcodeprojPath ?? Consts.xcodeprojPath let outputPath = outputPath ?? config.options.outputPath ?? Consts.outputPath @@ -110,17 +117,21 @@ struct LicensePlist: ParsableCommand { let prefix = prefix ?? config.options.prefix ?? Consts.prefix let htmlPath = htmlPath ?? config.options.htmlPath let markdownPath = markdownPath ?? config.options.markdownPath + let configLicenseFileNames = config.options.licenseFileNames ?? Consts.licenseFileNames + let licenseFileNames = licenseFileNames.isEmpty ? configLicenseFileNames : licenseFileNames let options = Options(outputPath: URL(fileURLWithPath: outputPath), cartfilePath: URL(fileURLWithPath: cartfilePath), mintfilePath: URL(fileURLWithPath: mintfilePath), podsPath: URL(fileURLWithPath: podsPath), packagePaths: packagePaths.map { URL(fileURLWithPath: $0) }, + packageCheckoutPath: packageCheckoutPath.map { URL(fileURLWithPath: $0, isDirectory: true) }, xcworkspacePath: URL(fileURLWithPath: xcworkspacePath), xcodeprojPath: URL(fileURLWithPath: xcodeprojPath), prefix: prefix, gitHubToken: githubToken, htmlPath: htmlPath.map { return URL(fileURLWithPath: $0) }, markdownPath: markdownPath.map { return URL(fileURLWithPath: $0) }, + licenseFileNames: licenseFileNames, config: config) let tool = LicensePlistCore.LicensePlist() tool.process(options: options) diff --git a/Sources/LicensePlistCore/Consts.swift b/Sources/LicensePlistCore/Consts.swift index f9942c0b..9465bdb6 100644 --- a/Sources/LicensePlistCore/Consts.swift +++ b/Sources/LicensePlistCore/Consts.swift @@ -14,4 +14,5 @@ public struct Consts { public static let configPath = "license_plist.yml" public static let version = "3.24.1" public static let encoding = String.Encoding.utf8 + public static let licenseFileNames = ["LICENSE", "LICENSE.txt", "LICENSE.md"] } diff --git a/Sources/LicensePlistCore/Entity/GeneralOptions.swift b/Sources/LicensePlistCore/Entity/GeneralOptions.swift index 884b6d58..1b07c141 100644 --- a/Sources/LicensePlistCore/Entity/GeneralOptions.swift +++ b/Sources/LicensePlistCore/Entity/GeneralOptions.swift @@ -8,12 +8,14 @@ public struct GeneralOptions { public let mintfilePath: String? public let podsPath: String? public let packagePaths: [String]? + public let packageCheckoutPath: String? public let xcworkspacePath: String? public let xcodeprojPath: String? public let prefix: String? public let gitHubToken: String? public let htmlPath: String? public let markdownPath: String? + public let licenseFileNames: [String]? public let force: Bool? public let addVersionNumbers: Bool? public let suppressOpeningDirectory: Bool? @@ -26,12 +28,14 @@ public struct GeneralOptions { mintfilePath: nil, podsPath: nil, packagePaths: nil, + packageCheckoutPath: nil, xcworkspacePath: nil, xcodeprojPath: nil, prefix: nil, gitHubToken: nil, htmlPath: nil, markdownPath: nil, + licenseFileNames: nil, force: nil, addVersionNumbers: nil, suppressOpeningDirectory: nil, @@ -44,12 +48,14 @@ public struct GeneralOptions { mintfilePath: String?, podsPath: String?, packagePaths: [String]?, + packageCheckoutPath: String?, xcworkspacePath: String?, xcodeprojPath: String?, prefix: String?, gitHubToken: String?, htmlPath: String?, markdownPath: String?, + licenseFileNames: [String]?, force: Bool?, addVersionNumbers: Bool?, suppressOpeningDirectory: Bool?, @@ -61,12 +67,14 @@ public struct GeneralOptions { self.mintfilePath = mintfilePath self.podsPath = podsPath self.packagePaths = packagePaths + self.packageCheckoutPath = packageCheckoutPath self.xcworkspacePath = xcworkspacePath self.xcodeprojPath = xcodeprojPath self.prefix = prefix self.gitHubToken = gitHubToken self.htmlPath = htmlPath self.markdownPath = markdownPath + self.licenseFileNames = licenseFileNames self.force = force self.addVersionNumbers = addVersionNumbers self.suppressOpeningDirectory = suppressOpeningDirectory @@ -83,12 +91,14 @@ extension GeneralOptions { lhs.mintfilePath == rhs.mintfilePath && lhs.podsPath == rhs.podsPath && lhs.packagePaths == rhs.packagePaths && + lhs.packageCheckoutPath == rhs.packageCheckoutPath && lhs.xcworkspacePath == rhs.xcworkspacePath && lhs.xcodeprojPath == rhs.xcodeprojPath && lhs.prefix == rhs.prefix && lhs.gitHubToken == rhs.gitHubToken && lhs.htmlPath == rhs.htmlPath && lhs.markdownPath == rhs.markdownPath && + lhs.licenseFileNames == rhs.licenseFileNames && lhs.force == rhs.force && lhs.addVersionNumbers == rhs.addVersionNumbers && lhs.suppressOpeningDirectory == rhs.suppressOpeningDirectory && @@ -105,12 +115,14 @@ extension GeneralOptions { mintfilePath: raw["mintfilePath"]?.string, podsPath: raw["podsPath"]?.string, packagePaths: raw["packagePaths"]?.array?.compactMap(\.string), + packageCheckoutPath: raw["packageCheckoutPath"]?.string, xcworkspacePath: raw["xcworkspacePath"]?.string, xcodeprojPath: raw["xcodeprojPath"]?.string, prefix: raw["prefix"]?.string, gitHubToken: raw["gitHubToken"]?.string, htmlPath: raw["htmlPath"]?.string, markdownPath: raw["markdownPath"]?.string, + licenseFileNames: raw["licenseFileNames"]?.array?.compactMap(\.string), force: raw["force"]?.bool, addVersionNumbers: raw["addVersionNumbers"]?.bool, suppressOpeningDirectory: raw["suppressOpeningDirectory"]?.bool, diff --git a/Sources/LicensePlistCore/Entity/GitHubLicense.swift b/Sources/LicensePlistCore/Entity/GitHubLicense.swift index a2cbd002..7b372bed 100644 --- a/Sources/LicensePlistCore/Entity/GitHubLicense.swift +++ b/Sources/LicensePlistCore/Entity/GitHubLicense.swift @@ -1,10 +1,11 @@ +import Foundation import LoggerAPI import APIKit public struct GitHubLicense: License, Equatable { public let library: GitHub public let body: String - let githubResponse: LicenseResponse + let githubResponse: LicenseResponse? public static func==(lhs: GitHubLicense, rhs: GitHubLicense) -> Bool { return lhs.library == rhs.library && @@ -65,6 +66,29 @@ extension GitHubLicense { } } } + + public static func readFromDisk(_ libraries: [GitHub], checkoutPath: URL, licenseFileNames: [String]) -> [GitHubLicense] { + return libraries.compactMap { library in + let owner = library.owner + let name = library.name + Log.info("license reading from disk start(owner: \(owner), name: \(name))") + + // Check several variants of license file name + for fileName in licenseFileNames { + do { + let url = checkoutPath.appendingPathComponent(name).appendingPathComponent(fileName) + let content = try String(contentsOf: url) + // Return the content of the first matched file + return GitHubLicense(library: library, body: content, githubResponse: nil) + } catch { + continue + } + } + + Log.warning("Failed to read from disk \(name)") + return nil + } + } private static func statusCode(from error: Error) -> Int? { guard let taskError = error as? SessionTaskError else { diff --git a/Sources/LicensePlistCore/Entity/Options.swift b/Sources/LicensePlistCore/Entity/Options.swift index aabce74e..6a0446b6 100644 --- a/Sources/LicensePlistCore/Entity/Options.swift +++ b/Sources/LicensePlistCore/Entity/Options.swift @@ -6,12 +6,14 @@ public struct Options { public let mintfilePath: URL public let podsPath: URL public let packagePaths: [URL] + public let packageCheckoutPath: URL? public let xcworkspacePath: URL public let xcodeprojPath: URL public let prefix: String public let gitHubToken: String? public let htmlPath: URL? public let markdownPath: URL? + public let licenseFileNames: [String] public let config: Config public static let empty = Options(outputPath: URL(fileURLWithPath: ""), @@ -19,12 +21,14 @@ public struct Options { mintfilePath: URL(fileURLWithPath: ""), podsPath: URL(fileURLWithPath: ""), packagePaths: [], + packageCheckoutPath: nil, xcworkspacePath: URL(fileURLWithPath: ""), xcodeprojPath: URL(fileURLWithPath: ""), prefix: Consts.prefix, gitHubToken: nil, htmlPath: nil, markdownPath: nil, + licenseFileNames: [], config: Config.empty) public init(outputPath: URL, @@ -32,24 +36,28 @@ public struct Options { mintfilePath: URL, podsPath: URL, packagePaths: [URL], + packageCheckoutPath: URL?, xcworkspacePath: URL, xcodeprojPath: URL, prefix: String, gitHubToken: String?, htmlPath: URL?, markdownPath: URL?, + licenseFileNames: [String], config: Config) { self.outputPath = outputPath self.cartfilePath = cartfilePath self.mintfilePath = mintfilePath self.podsPath = podsPath self.packagePaths = packagePaths + self.packageCheckoutPath = packageCheckoutPath self.xcworkspacePath = xcworkspacePath self.xcodeprojPath = xcodeprojPath self.prefix = prefix self.gitHubToken = gitHubToken self.htmlPath = htmlPath self.markdownPath = markdownPath + self.licenseFileNames = licenseFileNames self.config = config } } diff --git a/Sources/LicensePlistCore/Entity/PlistInfo.swift b/Sources/LicensePlistCore/Entity/PlistInfo.swift index dd7be5ef..3299beab 100644 --- a/Sources/LicensePlistCore/Entity/PlistInfo.swift +++ b/Sources/LicensePlistCore/Entity/PlistInfo.swift @@ -46,7 +46,9 @@ struct PlistInfo { Log.info("Swift Package Manager License collect start") let packages = packageFiles.flatMap { SwiftPackage.loadPackages($0) } - let packagesAsGithubLibraries = packages.compactMap { $0.toGitHub(renames: options.config.renames) }.sorted() + let packagesAsGithubLibraries = packages.compactMap { + $0.toGitHub(renames: options.config.renames, checkoutPath: options.packageCheckoutPath) + }.sorted() githubLibraries = (githubLibraries ?? []) + options.config.apply(githubs: packagesAsGithubLibraries) } @@ -76,22 +78,13 @@ struct PlistInfo { summary = contents summaryPath = savePath } - - mutating func downloadGitHubLicenses() { - guard let githubLibraries = githubLibraries else { preconditionFailure() } - - let queue = OperationQueue() - queue.maxConcurrentOperationCount = 10 - let carthageOperations = githubLibraries.map { GitHubLicense.download($0) } - queue.addOperations(carthageOperations, waitUntilFinished: true) - githubLicenses = carthageOperations.map { operation in - switch operation.result { - case let .success(value): - return value - default: - return nil - } - }.compactMap { $0 } + + mutating func loadGitHubLicenses() { + if let checkoutPath = options.packageCheckoutPath { + readCheckedOutLicenses(from: checkoutPath) + } else { + downloadGitHubLicenses() + } } mutating func collectLicenseInfos() { @@ -162,4 +155,29 @@ struct PlistInfo { Log.error("Failed to save summary. Error: \(String(describing: e))") } } + + private mutating func downloadGitHubLicenses() { + guard let githubLibraries = githubLibraries else { preconditionFailure() } + + let queue = OperationQueue() + queue.maxConcurrentOperationCount = 10 + let carthageOperations = githubLibraries.map { GitHubLicense.download($0) } + queue.addOperations(carthageOperations, waitUntilFinished: true) + githubLicenses = carthageOperations.map { operation in + switch operation.result { + case let .success(value): + return value + default: + return nil + } + }.compactMap { $0 } + } + + private mutating func readCheckedOutLicenses(from checkoutPath: URL) { + guard let githubLibraries = githubLibraries else { preconditionFailure() } + guard !options.licenseFileNames.isEmpty else { preconditionFailure() } + githubLicenses = GitHubLicense.readFromDisk(githubLibraries, + checkoutPath: checkoutPath, + licenseFileNames: options.licenseFileNames) + } } diff --git a/Sources/LicensePlistCore/Entity/SwiftPackage.swift b/Sources/LicensePlistCore/Entity/SwiftPackage.swift index 0fb7cb5f..7c272144 100644 --- a/Sources/LicensePlistCore/Entity/SwiftPackage.swift +++ b/Sources/LicensePlistCore/Entity/SwiftPackage.swift @@ -69,7 +69,7 @@ extension SwiftPackage { } } - func toGitHub(renames: [String: String]) -> GitHub? { + func toGitHub(renames: [String: String], checkoutPath: URL? = nil) -> GitHub? { guard repositoryURL.contains("github.com") else { return nil } let urlParts = repositoryURL @@ -87,20 +87,38 @@ extension SwiftPackage { // ssh owner = urlParts.first?.components(separatedBy: ":").last ?? "" } + + let nameSpecified = renames[name] ?? getDefaultName(for: owner, and: name, checkoutPath: checkoutPath) return GitHub(name: name, - nameSpecified: renames[name] ?? getDefaultName(for: owner, and: name), + nameSpecified: nameSpecified, owner: owner, version: version) } - private func getDefaultName(for owner: String, and name: String) -> String { + private func getDefaultName(for owner: String, and name: String, checkoutPath: URL?) -> String { guard packageDefinitionVersion != 1 else { return package } // In SPM v1 the Package.resolved JSON always contains the correct name, no need for anything else. - guard let version = version else { return fallbackName(using: name) } - guard let packageDefinitionURL = URL(string: "https://raw.githubusercontent.com/\(owner)/\(name)/\(version)/Package.swift") else { return fallbackName(using: name) } - guard let packageDefinition = try? String(contentsOf: packageDefinitionURL) else { return fallbackName(using: name) } + guard let packageDefinition = packageDefinition(for: owner, name: name, checkoutPath: checkoutPath) else { return fallbackName(using: name) } return parseName(from: packageDefinition) ?? fallbackName(using: name) } + + private func packageDefinition(for owner: String, name: String, checkoutPath: URL?) -> String? { + // Try to read from a checkout directory in checkout folder + let packageDefinitionFromDisk = checkoutPath.map { readPackageDefinition(name: name, checkoutPath: $0) } + // Then download the definition from Github as a fallback + return packageDefinitionFromDisk ?? loadPackageDefinitionFromGithub(for: owner, name: name) + } + + private func readPackageDefinition(name: String, checkoutPath: URL) -> String? { + let url = checkoutPath.appendingPathComponent(name).appendingPathComponent("Package.swift") + return try? String(contentsOf: url) + } + + private func loadPackageDefinitionFromGithub(for owner: String, name: String) -> String? { + guard let version = version else { return nil } + guard let packageDefinitionURL = URL(string: "https://raw.githubusercontent.com/\(owner)/\(name)/\(version)/Package.swift") else { return nil } + return try? String(contentsOf: packageDefinitionURL) + } private func fallbackName(using githubName: String) -> String { packageDefinitionVersion == 1 ? package : githubName diff --git a/Sources/LicensePlistCore/LicensePlist.swift b/Sources/LicensePlistCore/LicensePlist.swift index 63e1bad1..6096c86a 100644 --- a/Sources/LicensePlistCore/LicensePlist.swift +++ b/Sources/LicensePlistCore/LicensePlist.swift @@ -36,7 +36,7 @@ public final class LicensePlist { info.loadManualLibraries() info.compareWithLatestSummary() - info.downloadGitHubLicenses() + info.loadGitHubLicenses() info.collectLicenseInfos() info.outputPlist() Log.info("End") diff --git a/Tests/LicensePlistTests/Entity/ConfigTests.swift b/Tests/LicensePlistTests/Entity/ConfigTests.swift index 99b0b7f2..6be048d8 100644 --- a/Tests/LicensePlistTests/Entity/ConfigTests.swift +++ b/Tests/LicensePlistTests/Entity/ConfigTests.swift @@ -24,12 +24,14 @@ class ConfigTests: XCTestCase { mintfilePath: "Mintfile", podsPath: "Pods", packagePaths: ["Package.swift"], + packageCheckoutPath: "./checkouts", xcworkspacePath: "*.xcworkspace", xcodeprojPath: "*.xcodeproj", prefix: "com.mono0926.LicensePlist", gitHubToken: "YOUR_GITHUB_TOKEN", htmlPath: "acknowledgements.html", markdownPath: "acknowledgements.md", + licenseFileNames: ["LICENSE", "LICENSE.txt", "LICENSE.md"], force: false, addVersionNumbers: false, suppressOpeningDirectory: false, diff --git a/Tests/LicensePlistTests/Entity/GitHubLicense.collectorTests.swift b/Tests/LicensePlistTests/Entity/GitHubLicense.collectorTests.swift index eea2373d..3950f714 100644 --- a/Tests/LicensePlistTests/Entity/GitHubLicense.collectorTests.swift +++ b/Tests/LicensePlistTests/Entity/GitHubLicense.collectorTests.swift @@ -15,7 +15,7 @@ class GitHubLicenseTests: XCTestCase { let license = try GitHubLicense.download(carthage).resultSync().get() XCTAssertEqual(license.library, carthage) XCTAssertTrue(license.body.hasPrefix("MIT License")) - XCTAssertEqual(license.githubResponse.kind.spdxId, "MIT") + XCTAssertEqual(license.githubResponse?.kind.spdxId, "MIT") } func testCollect_forked() throws { @@ -23,7 +23,7 @@ class GitHubLicenseTests: XCTestCase { let license = try GitHubLicense.download(carthage).resultSync().get() XCTAssertEqual(license.library, carthage) XCTAssertTrue(license.body.hasPrefix("The MIT License (MIT)")) - XCTAssertEqual(license.githubResponse.kind.spdxId, "MIT") + XCTAssertEqual(license.githubResponse?.kind.spdxId, "MIT") } func testCollect_invalid() { let carthage = GitHub(name: "abcde", nameSpecified: nil, owner: "invalid", version: nil) diff --git a/Tests/LicensePlistTests/Entity/PlistInfoTests.swift b/Tests/LicensePlistTests/Entity/PlistInfoTests.swift index 6ea12ab4..9504c218 100644 --- a/Tests/LicensePlistTests/Entity/PlistInfoTests.swift +++ b/Tests/LicensePlistTests/Entity/PlistInfoTests.swift @@ -14,12 +14,14 @@ class PlistInfoTests: XCTestCase { mintfilePath: URL(fileURLWithPath: "test_result_dir"), podsPath: URL(fileURLWithPath: "test_result_dir"), packagePaths: [URL(fileURLWithPath: "test_result_dir")], + packageCheckoutPath: nil, xcworkspacePath: URL(fileURLWithPath: "test_result_dir"), xcodeprojPath: URL(fileURLWithPath: "test_result_dir"), prefix: Consts.prefix, gitHubToken: nil, htmlPath: nil, markdownPath: nil, + licenseFileNames: [], config: Config(githubs: [GitHub(name: "facebook-ios-sdk", nameSpecified: nil, owner: "facebook", @@ -96,7 +98,7 @@ class PlistInfoTests: XCTestCase { target.githubLibraries = [github] XCTAssertNil(target.githubLicenses) - target.downloadGitHubLicenses() + target.loadGitHubLicenses() let licenses = try XCTUnwrap(target.githubLicenses) XCTAssertEqual(licenses.count, 1) let license = licenses.first diff --git a/Tests/LicensePlistTests/Resources/license_plist.yml b/Tests/LicensePlistTests/Resources/license_plist.yml index 055e1be5..75d2f350 100644 --- a/Tests/LicensePlistTests/Resources/license_plist.yml +++ b/Tests/LicensePlistTests/Resources/license_plist.yml @@ -8,12 +8,17 @@ options: podsPath: Pods packagePaths: - Package.swift + packageCheckoutPath: ""./checkouts" xcodeprojPath: "*.xcodeproj" xcworkspacePath: "*.xcworkspace" prefix: com.mono0926.LicensePlist gitHubToken: YOUR_GITHUB_TOKEN htmlPath: acknowledgements.html markdownPath: acknowledgements.md + licenseFileNames: + - LICENSE + - LICENSE.txt + - LICENSE.md force: false addVersionNumbers: false suppressOpeningDirectory: false From 1b4c706a66ff76508d32eff92cdbb5990c962f43 Mon Sep 17 00:00:00 2001 From: Vladimir Vlasov Date: Sun, 26 Feb 2023 11:22:28 +0100 Subject: [PATCH 2/5] Rename '--package-checkout-path' to '--package-sources-path', add any non-empty extension support for license file names --- README.md | 17 +++++----- Sources/LicensePlist/main.swift | 8 ++--- Sources/LicensePlistCore/Consts.swift | 2 +- .../Entity/GeneralOptions.swift | 12 +++---- .../Entity/GitHubLicense.swift | 31 ++++++++++++++++++- Sources/LicensePlistCore/Entity/Options.swift | 8 ++--- .../LicensePlistCore/Entity/PlistInfo.swift | 10 +++--- .../Entity/ConfigTests.swift | 4 +-- .../Entity/PlistInfoTests.swift | 2 +- .../Resources/license_plist.yml | 5 ++- 10 files changed, 66 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 454d336c..512d60d7 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,11 @@ You can see options by `license-plist --help`. - Support for multiple `Package.swift` - Example: `license-plist --package-paths /path/to/package1/Package.swift /path/to/package2/Package.swift` -#### `--package-checkout-path` +#### `--package-sources-path` +- Default: None. - The directory with cloned Swift package sources. If specified `LicensePlist` uses cloned files instead of GitHub API. For more information, see parameter `-clonedSourcePackagesDirPath` of [xcodebuild](https://developer.apple.com/library/archive/technotes/tn2339/_index.html). -- Example: `license-plist --package-checkout-path ./checkouts` +- Example: `license-plist --package-sources-path ./SourcePackages` #### `--xcodeproj-path` @@ -164,8 +165,11 @@ You can see options by `license-plist --help`. #### `--license-file-names` -- Default: `LICENSE, LICENSE.txt, LICENSE.md`. -- License file name variants. Might be used only in combination with `--package-checkout-path`. +- Default: `LICENSE, LICENSE.*`. +- License file name variants for cloned Swift packages. + - Case insensitive. + - Supports any non-empty path extensions if `FILENAME.*` is specified. + - Used only in combination with `--package-sources-path`. #### `--force` @@ -258,7 +262,7 @@ options: podsPath: Pods packagePaths: - Package.swift - packageCheckoutPath: ""./checkouts" + packageSourcesPath: ""./SourcePackages" xcodeprojPath: "*.xcodeproj" xcworkspacePath: "*.xcworkspace" prefix: com.mono0926.LicensePlist @@ -267,8 +271,7 @@ options: markdownPath: acknowledgements.md licenseFileNames: - LICENSE - - LICENSE.txt - - LICENSE.md + - LICENSE.* force: false addVersionNumbers: false suppressOpeningDirectory: false diff --git a/Sources/LicensePlist/main.swift b/Sources/LicensePlist/main.swift index df6b269a..cd46a718 100644 --- a/Sources/LicensePlist/main.swift +++ b/Sources/LicensePlist/main.swift @@ -36,8 +36,8 @@ struct LicensePlist: ParsableCommand { @Option(name: [.customLong("package-path"), .customLong("swift-package-path"), .long, .customLong("swift-package-paths")], parsing: .upToNextOption, completion: .file()) var packagePaths = [String]() - @Option(name: [.long, .customLong("swift-package-checkout-path")], completion: .directory) - var packageCheckoutPath: String? + @Option(name: [.long, .customLong("swift-package-sources-path")], completion: .directory) + var packageSourcesPath: String? @Option(name: .long, completion: .file()) var xcworkspacePath: String? @@ -109,7 +109,7 @@ struct LicensePlist: ParsableCommand { let podsPath = podsPath ?? config.options.podsPath ?? Consts.podsDirectoryName let configPackagePaths = config.options.packagePaths ?? [Consts.packageName] let packagePaths = packagePaths.isEmpty ? configPackagePaths : packagePaths - let packageCheckoutPath = packageCheckoutPath ?? config.options.packageCheckoutPath + let packageSourcesPath = packageSourcesPath ?? config.options.packageSourcesPath let xcworkspacePath = xcworkspacePath ?? config.options.xcworkspacePath ?? Consts.xcworkspacePath let xcodeprojPath = xcodeprojPath ?? config.options.xcodeprojPath ?? Consts.xcodeprojPath let outputPath = outputPath ?? config.options.outputPath ?? Consts.outputPath @@ -124,7 +124,7 @@ struct LicensePlist: ParsableCommand { mintfilePath: URL(fileURLWithPath: mintfilePath), podsPath: URL(fileURLWithPath: podsPath), packagePaths: packagePaths.map { URL(fileURLWithPath: $0) }, - packageCheckoutPath: packageCheckoutPath.map { URL(fileURLWithPath: $0, isDirectory: true) }, + packageSourcesPath: packageSourcesPath.map { URL(fileURLWithPath: $0, isDirectory: true) }, xcworkspacePath: URL(fileURLWithPath: xcworkspacePath), xcodeprojPath: URL(fileURLWithPath: xcodeprojPath), prefix: prefix, diff --git a/Sources/LicensePlistCore/Consts.swift b/Sources/LicensePlistCore/Consts.swift index 9465bdb6..2327db09 100644 --- a/Sources/LicensePlistCore/Consts.swift +++ b/Sources/LicensePlistCore/Consts.swift @@ -14,5 +14,5 @@ public struct Consts { public static let configPath = "license_plist.yml" public static let version = "3.24.1" public static let encoding = String.Encoding.utf8 - public static let licenseFileNames = ["LICENSE", "LICENSE.txt", "LICENSE.md"] + public static let licenseFileNames = ["LICENSE", "LICENSE.*"] } diff --git a/Sources/LicensePlistCore/Entity/GeneralOptions.swift b/Sources/LicensePlistCore/Entity/GeneralOptions.swift index 1b07c141..dd150c75 100644 --- a/Sources/LicensePlistCore/Entity/GeneralOptions.swift +++ b/Sources/LicensePlistCore/Entity/GeneralOptions.swift @@ -8,7 +8,7 @@ public struct GeneralOptions { public let mintfilePath: String? public let podsPath: String? public let packagePaths: [String]? - public let packageCheckoutPath: String? + public let packageSourcesPath: String? public let xcworkspacePath: String? public let xcodeprojPath: String? public let prefix: String? @@ -28,7 +28,7 @@ public struct GeneralOptions { mintfilePath: nil, podsPath: nil, packagePaths: nil, - packageCheckoutPath: nil, + packageSourcesPath: nil, xcworkspacePath: nil, xcodeprojPath: nil, prefix: nil, @@ -48,7 +48,7 @@ public struct GeneralOptions { mintfilePath: String?, podsPath: String?, packagePaths: [String]?, - packageCheckoutPath: String?, + packageSourcesPath: String?, xcworkspacePath: String?, xcodeprojPath: String?, prefix: String?, @@ -67,7 +67,7 @@ public struct GeneralOptions { self.mintfilePath = mintfilePath self.podsPath = podsPath self.packagePaths = packagePaths - self.packageCheckoutPath = packageCheckoutPath + self.packageSourcesPath = packageSourcesPath self.xcworkspacePath = xcworkspacePath self.xcodeprojPath = xcodeprojPath self.prefix = prefix @@ -91,7 +91,7 @@ extension GeneralOptions { lhs.mintfilePath == rhs.mintfilePath && lhs.podsPath == rhs.podsPath && lhs.packagePaths == rhs.packagePaths && - lhs.packageCheckoutPath == rhs.packageCheckoutPath && + lhs.packageSourcesPath == rhs.packageSourcesPath && lhs.xcworkspacePath == rhs.xcworkspacePath && lhs.xcodeprojPath == rhs.xcodeprojPath && lhs.prefix == rhs.prefix && @@ -115,7 +115,7 @@ extension GeneralOptions { mintfilePath: raw["mintfilePath"]?.string, podsPath: raw["podsPath"]?.string, packagePaths: raw["packagePaths"]?.array?.compactMap(\.string), - packageCheckoutPath: raw["packageCheckoutPath"]?.string, + packageSourcesPath: raw["packageSourcesPath"]?.string, xcworkspacePath: raw["xcworkspacePath"]?.string, xcodeprojPath: raw["xcodeprojPath"]?.string, prefix: raw["prefix"]?.string, diff --git a/Sources/LicensePlistCore/Entity/GitHubLicense.swift b/Sources/LicensePlistCore/Entity/GitHubLicense.swift index 7b372bed..a8e322cc 100644 --- a/Sources/LicensePlistCore/Entity/GitHubLicense.swift +++ b/Sources/LicensePlistCore/Entity/GitHubLicense.swift @@ -72,11 +72,16 @@ extension GitHubLicense { let owner = library.owner let name = library.name Log.info("license reading from disk start(owner: \(owner), name: \(name))") + + let libraryUrl = checkoutPath.appendingPathComponent(name) + let libraryFileUrls = libraryUrl.lp.listDir().filter { !$0.lp.isDirectory } // Check several variants of license file name for fileName in licenseFileNames { + guard let url = findFile(with: fileName, in: libraryFileUrls) else { + continue + } do { - let url = checkoutPath.appendingPathComponent(name).appendingPathComponent(fileName) let content = try String(contentsOf: url) // Return the content of the first matched file return GitHubLicense(library: library, body: content, githubResponse: nil) @@ -89,6 +94,30 @@ extension GitHubLicense { return nil } } + + private static func findFile(with fileName: String, in fileUrls: [URL]) -> URL? { + let anyExtensionSuffix = ".*" + if fileName.hasSuffix(anyExtensionSuffix) { + // Check file names without extensions + let fileNameWithoutExtension = String(fileName.prefix(fileName.count - anyExtensionSuffix.count)) + let lowercasedFileName = fileNameWithoutExtension.lowercased() + for fileUrl in fileUrls { + let candidateFileName = fileUrl.deletingPathExtension().lastPathComponent + if candidateFileName.lowercased() == lowercasedFileName { + return fileUrl + } + } + } else { + // Check lowercased file names + let lowercasedFileName = fileName.lowercased() + for fileUrl in fileUrls { + if fileUrl.lastPathComponent.lowercased() == lowercasedFileName { + return fileUrl + } + } + } + return nil + } private static func statusCode(from error: Error) -> Int? { guard let taskError = error as? SessionTaskError else { diff --git a/Sources/LicensePlistCore/Entity/Options.swift b/Sources/LicensePlistCore/Entity/Options.swift index 6a0446b6..649c19d6 100644 --- a/Sources/LicensePlistCore/Entity/Options.swift +++ b/Sources/LicensePlistCore/Entity/Options.swift @@ -6,7 +6,7 @@ public struct Options { public let mintfilePath: URL public let podsPath: URL public let packagePaths: [URL] - public let packageCheckoutPath: URL? + public let packageSourcesPath: URL? public let xcworkspacePath: URL public let xcodeprojPath: URL public let prefix: String @@ -21,7 +21,7 @@ public struct Options { mintfilePath: URL(fileURLWithPath: ""), podsPath: URL(fileURLWithPath: ""), packagePaths: [], - packageCheckoutPath: nil, + packageSourcesPath: nil, xcworkspacePath: URL(fileURLWithPath: ""), xcodeprojPath: URL(fileURLWithPath: ""), prefix: Consts.prefix, @@ -36,7 +36,7 @@ public struct Options { mintfilePath: URL, podsPath: URL, packagePaths: [URL], - packageCheckoutPath: URL?, + packageSourcesPath: URL?, xcworkspacePath: URL, xcodeprojPath: URL, prefix: String, @@ -50,7 +50,7 @@ public struct Options { self.mintfilePath = mintfilePath self.podsPath = podsPath self.packagePaths = packagePaths - self.packageCheckoutPath = packageCheckoutPath + self.packageSourcesPath = packageSourcesPath self.xcworkspacePath = xcworkspacePath self.xcodeprojPath = xcodeprojPath self.prefix = prefix diff --git a/Sources/LicensePlistCore/Entity/PlistInfo.swift b/Sources/LicensePlistCore/Entity/PlistInfo.swift index 3299beab..843f421d 100644 --- a/Sources/LicensePlistCore/Entity/PlistInfo.swift +++ b/Sources/LicensePlistCore/Entity/PlistInfo.swift @@ -46,8 +46,9 @@ struct PlistInfo { Log.info("Swift Package Manager License collect start") let packages = packageFiles.flatMap { SwiftPackage.loadPackages($0) } + let checkoutPath = options.packageSourcesPath?.appendingPathComponent("checkouts") let packagesAsGithubLibraries = packages.compactMap { - $0.toGitHub(renames: options.config.renames, checkoutPath: options.packageCheckoutPath) + $0.toGitHub(renames: options.config.renames, checkoutPath: checkoutPath) }.sorted() githubLibraries = (githubLibraries ?? []) + options.config.apply(githubs: packagesAsGithubLibraries) @@ -80,8 +81,8 @@ struct PlistInfo { } mutating func loadGitHubLicenses() { - if let checkoutPath = options.packageCheckoutPath { - readCheckedOutLicenses(from: checkoutPath) + if let packageSourcesPath = options.packageSourcesPath { + readCheckedOutLicenses(from: packageSourcesPath) } else { downloadGitHubLicenses() } @@ -173,9 +174,10 @@ struct PlistInfo { }.compactMap { $0 } } - private mutating func readCheckedOutLicenses(from checkoutPath: URL) { + private mutating func readCheckedOutLicenses(from packageSourcesPath: URL) { guard let githubLibraries = githubLibraries else { preconditionFailure() } guard !options.licenseFileNames.isEmpty else { preconditionFailure() } + let checkoutPath = packageSourcesPath.appendingPathComponent("checkouts") githubLicenses = GitHubLicense.readFromDisk(githubLibraries, checkoutPath: checkoutPath, licenseFileNames: options.licenseFileNames) diff --git a/Tests/LicensePlistTests/Entity/ConfigTests.swift b/Tests/LicensePlistTests/Entity/ConfigTests.swift index 6be048d8..7eaaeb28 100644 --- a/Tests/LicensePlistTests/Entity/ConfigTests.swift +++ b/Tests/LicensePlistTests/Entity/ConfigTests.swift @@ -24,14 +24,14 @@ class ConfigTests: XCTestCase { mintfilePath: "Mintfile", podsPath: "Pods", packagePaths: ["Package.swift"], - packageCheckoutPath: "./checkouts", + packageSourcesPath: "./SourcePackages", xcworkspacePath: "*.xcworkspace", xcodeprojPath: "*.xcodeproj", prefix: "com.mono0926.LicensePlist", gitHubToken: "YOUR_GITHUB_TOKEN", htmlPath: "acknowledgements.html", markdownPath: "acknowledgements.md", - licenseFileNames: ["LICENSE", "LICENSE.txt", "LICENSE.md"], + licenseFileNames: ["LICENSE", "LICENSE.*"], force: false, addVersionNumbers: false, suppressOpeningDirectory: false, diff --git a/Tests/LicensePlistTests/Entity/PlistInfoTests.swift b/Tests/LicensePlistTests/Entity/PlistInfoTests.swift index 9504c218..f0cfe04d 100644 --- a/Tests/LicensePlistTests/Entity/PlistInfoTests.swift +++ b/Tests/LicensePlistTests/Entity/PlistInfoTests.swift @@ -14,7 +14,7 @@ class PlistInfoTests: XCTestCase { mintfilePath: URL(fileURLWithPath: "test_result_dir"), podsPath: URL(fileURLWithPath: "test_result_dir"), packagePaths: [URL(fileURLWithPath: "test_result_dir")], - packageCheckoutPath: nil, + packageSourcesPath: nil, xcworkspacePath: URL(fileURLWithPath: "test_result_dir"), xcodeprojPath: URL(fileURLWithPath: "test_result_dir"), prefix: Consts.prefix, diff --git a/Tests/LicensePlistTests/Resources/license_plist.yml b/Tests/LicensePlistTests/Resources/license_plist.yml index 75d2f350..ecd5ef0f 100644 --- a/Tests/LicensePlistTests/Resources/license_plist.yml +++ b/Tests/LicensePlistTests/Resources/license_plist.yml @@ -8,7 +8,7 @@ options: podsPath: Pods packagePaths: - Package.swift - packageCheckoutPath: ""./checkouts" + packageSourcesPath: "./SourcePackages" xcodeprojPath: "*.xcodeproj" xcworkspacePath: "*.xcworkspace" prefix: com.mono0926.LicensePlist @@ -17,8 +17,7 @@ options: markdownPath: acknowledgements.md licenseFileNames: - LICENSE - - LICENSE.txt - - LICENSE.md + - LICENSE.* force: false addVersionNumbers: false suppressOpeningDirectory: false From dbf0f3f1bb14f8960c00981c9f59ab70ed0c7b32 Mon Sep 17 00:00:00 2001 From: Vladimir Vlasov Date: Sun, 26 Feb 2023 11:38:21 +0100 Subject: [PATCH 3/5] Add warning about not supported filtering in combination with specified package sources path --- README.md | 2 +- Sources/LicensePlistCore/Entity/PlistInfo.swift | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 512d60d7..a2a61e29 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ options: podsPath: Pods packagePaths: - Package.swift - packageSourcesPath: ""./SourcePackages" + packageSourcesPath: "./SourcePackages" xcodeprojPath: "*.xcodeproj" xcworkspacePath: "*.xcworkspace" prefix: com.mono0926.LicensePlist diff --git a/Sources/LicensePlistCore/Entity/PlistInfo.swift b/Sources/LicensePlistCore/Entity/PlistInfo.swift index 843f421d..5ed7d4dd 100644 --- a/Sources/LicensePlistCore/Entity/PlistInfo.swift +++ b/Sources/LicensePlistCore/Entity/PlistInfo.swift @@ -50,6 +50,10 @@ struct PlistInfo { let packagesAsGithubLibraries = packages.compactMap { $0.toGitHub(renames: options.config.renames, checkoutPath: checkoutPath) }.sorted() + + if checkoutPath != nil && options.config.excludes.contains(where: { $0.licenseType != nil }) { + Log.warning("Filtering by license type is not supported in combination with specified package sources path") + } githubLibraries = (githubLibraries ?? []) + options.config.apply(githubs: packagesAsGithubLibraries) } From 33555d43cbf4d1d10b956e698733b513d5358f87 Mon Sep 17 00:00:00 2001 From: Vladimir Vlasov Date: Sun, 26 Feb 2023 13:39:15 +0100 Subject: [PATCH 4/5] Add tests for package sources --- .../PlistInfoWithSourcePackagesTests.swift | 74 +++++++++++++++++++ .../Entity/SwiftPackageManagerTests.swift | 14 +++- .../SourcePackages/checkouts/R.swift/License | 1 + .../checkouts/R.swift/License.md | 1 + .../checkouts/R.swift/Package.resolved | 34 +++++++++ .../checkouts/R.swift/Package.swift | 21 ++++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift create mode 100644 Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License create mode 100644 Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License.md create mode 100644 Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.resolved create mode 100644 Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift diff --git a/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift b/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift new file mode 100644 index 00000000..4e4c6a1d --- /dev/null +++ b/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift @@ -0,0 +1,74 @@ +import XCTest +@testable import LicensePlistCore + +final class PlistInfoWithSourcePackagesTests: XCTestCase { + private let github = GitHub(name: "R.swift", nameSpecified: "rswit", owner: "mac-cain13", version: "0.5.4") + private let sourcePackagesPath = TestUtil.testResourceDir.appendingPathComponent("SourcePackages").lp.fileURL + + func testReadLicenseFromDisk() throws { + var target = plistInfo() + + XCTAssertNil(target.githubLicenses) + target.loadGitHubLicenses() + let licenses = try XCTUnwrap(target.githubLicenses) + XCTAssertEqual(licenses.count, 1) + let license = licenses.first + + XCTAssertEqual(license?.library, github) + XCTAssertEqual(license?.body, "license text") + XCTAssertNil(license?.githubResponse) + } + + func testReadLicenseMDFromDisk() throws { + var target = plistInfo(licenseFileNames: ["LICENSE.md"]) + + target.loadGitHubLicenses() + let license = target.githubLicenses?.first + + XCTAssertEqual(license?.body, "license.md text") + } + + func testReadLicenseWithAsteriskFromDisk() throws { + var target = plistInfo(licenseFileNames: ["LICENSE.*"]) + + target.loadGitHubLicenses() + let license = target.githubLicenses?.first + + XCTAssertEqual(license?.body, "license.md text") + } + + func testReadMissedLicenseFromDisk() throws { + var target = PlistInfo(options: options(licenseFileNames: ["Not-a-license"])) + target.githubLibraries = [github] + + target.loadGitHubLicenses() + let licenses = try XCTUnwrap(target.githubLicenses) + + XCTAssertTrue(licenses.isEmpty) + } + + // MARK: Helpers + + private func plistInfo(licenseFileNames: [String] = ["LICENSE"]) -> PlistInfo { + var target = PlistInfo(options: options(licenseFileNames: licenseFileNames)) + target.githubLibraries = [github] + return target + } + + private func options(licenseFileNames: [String]) -> Options { + return Options(outputPath: URL(fileURLWithPath: "test_result_dir"), + cartfilePath: URL(fileURLWithPath: "test_result_dir"), + mintfilePath: URL(fileURLWithPath: "test_result_dir"), + podsPath: URL(fileURLWithPath: "test_result_dir"), + packagePaths: [URL(fileURLWithPath: "test_result_dir")], + packageSourcesPath: sourcePackagesPath, + xcworkspacePath: URL(fileURLWithPath: "test_result_dir"), + xcodeprojPath: URL(fileURLWithPath: "test_result_dir"), + prefix: Consts.prefix, + gitHubToken: nil, + htmlPath: nil, + markdownPath: nil, + licenseFileNames: licenseFileNames, + config: .empty) + } +} diff --git a/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift b/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift index a323ee37..fffc0a02 100644 --- a/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift +++ b/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift @@ -443,5 +443,17 @@ class SwiftPackageManagerTests: XCTestCase { let result = package.toGitHub(renames: [:]) XCTAssertEqual(result?.nameSpecified, "better-name-parsed-from-repo", "For SPM v2 we try to parse the Package.swift from the repository to get the name. But when that fails, we fall back to the name in the Repository URL which is still an improvement to the name we get as `identity` from the generated JSON.") } - + + // MARK: Source Packages + + func testResolvingNameFromCheckoutSources() { + let package = SwiftPackage(package: "R.swift", + repositoryURL: "https://github.com/mac-cain13/R.swift", + revision: "18ad905c6f8f0865042e1d1ee4effc7291aa899d", + version: "0.5.4", + packageDefinitionVersion: 2) + let checkoutPath = TestUtil.testResourceDir.appendingPathComponent("SourcePackages/checkouts").lp.fileURL + let result = package.toGitHub(renames: [:], checkoutPath: checkoutPath) + XCTAssertEqual(result, GitHub(name: "R.swift", nameSpecified: "rswift", owner: "mac-cain13", version: "0.5.4")) + } } diff --git a/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License new file mode 100644 index 00000000..d57a8c7b --- /dev/null +++ b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License @@ -0,0 +1 @@ +license text \ No newline at end of file diff --git a/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License.md b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License.md new file mode 100644 index 00000000..ae3fc045 --- /dev/null +++ b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/License.md @@ -0,0 +1 @@ +license.md text \ No newline at end of file diff --git a/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.resolved b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.resolved new file mode 100644 index 00000000..3c71e9da --- /dev/null +++ b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.resolved @@ -0,0 +1,34 @@ +{ + "object": { + "pins": [ + { + "package": "Commander", + "repositoryURL": "https://github.com/kylef/Commander.git", + "state": { + "branch": null, + "revision": "4b6133c3071d521489a80c38fb92d7983f19d438", + "version": "0.9.1" + } + }, + { + "package": "Spectre", + "repositoryURL": "https://github.com/kylef/Spectre.git", + "state": { + "branch": null, + "revision": "f14ff47f45642aa5703900980b014c2e9394b6e5", + "version": "0.9.0" + } + }, + { + "package": "XcodeEdit", + "repositoryURL": "https://github.com/tomlokhorst/XcodeEdit", + "state": { + "branch": null, + "revision": "dab519997ca05833470c88f0926b27498911ecbf", + "version": "2.7.7" + } + } + ] + }, + "version": 1 +} diff --git a/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift new file mode 100644 index 00000000..e3a694cf --- /dev/null +++ b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift @@ -0,0 +1,21 @@ +// swift-tools-version:5.0 +import PackageDescription + +let package = Package( + name: "rswift", + platforms: [ + .macOS(.v10_11) + ], + products: [ + .executable(name: "rswift", targets: ["rswift"]) + ], + dependencies: [ + .package(url: "https://github.com/kylef/Commander.git", from: "0.8.0"), + .package(url: "https://github.com/tomlokhorst/XcodeEdit", from: "2.7.0") + ], + targets: [ + .target(name: "rswift", dependencies: ["RswiftCore"]), + .target(name: "RswiftCore", dependencies: ["Commander", "XcodeEdit"]), + .testTarget(name: "RswiftCoreTests", dependencies: ["RswiftCore"]), + ] +) From c1f34012931b6b2fd3443898622de42dba905ff9 Mon Sep 17 00:00:00 2001 From: Vladimir Vlasov Date: Sun, 26 Feb 2023 14:26:04 +0100 Subject: [PATCH 5/5] Fix linter issues --- Sources/LicensePlist/main.swift | 4 ++-- .../LicensePlistCore/Entity/GitHubLicense.swift | 6 +++--- Sources/LicensePlistCore/Entity/PlistInfo.swift | 8 ++++---- Sources/LicensePlistCore/Entity/SwiftPackage.swift | 8 ++++---- .../Entity/PlistInfoWithSourcePackagesTests.swift | 14 +++++++------- .../Entity/SwiftPackageManagerTests.swift | 2 +- .../SourcePackages/checkouts/R.swift/Package.swift | 2 +- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Sources/LicensePlist/main.swift b/Sources/LicensePlist/main.swift index cd46a718..c55757ea 100644 --- a/Sources/LicensePlist/main.swift +++ b/Sources/LicensePlist/main.swift @@ -35,7 +35,7 @@ struct LicensePlist: ParsableCommand { @Option(name: [.customLong("package-path"), .customLong("swift-package-path"), .long, .customLong("swift-package-paths")], parsing: .upToNextOption, completion: .file()) var packagePaths = [String]() - + @Option(name: [.long, .customLong("swift-package-sources-path")], completion: .directory) var packageSourcesPath: String? @@ -62,7 +62,7 @@ struct LicensePlist: ParsableCommand { @Option(name: .long, completion: .file()) var markdownPath: String? - + @Option(name: .long, parsing: .upToNextOption, completion: .empty) var licenseFileNames = [String]() diff --git a/Sources/LicensePlistCore/Entity/GitHubLicense.swift b/Sources/LicensePlistCore/Entity/GitHubLicense.swift index a8e322cc..3144f330 100644 --- a/Sources/LicensePlistCore/Entity/GitHubLicense.swift +++ b/Sources/LicensePlistCore/Entity/GitHubLicense.swift @@ -66,13 +66,13 @@ extension GitHubLicense { } } } - + public static func readFromDisk(_ libraries: [GitHub], checkoutPath: URL, licenseFileNames: [String]) -> [GitHubLicense] { return libraries.compactMap { library in let owner = library.owner let name = library.name Log.info("license reading from disk start(owner: \(owner), name: \(name))") - + let libraryUrl = checkoutPath.appendingPathComponent(name) let libraryFileUrls = libraryUrl.lp.listDir().filter { !$0.lp.isDirectory } @@ -94,7 +94,7 @@ extension GitHubLicense { return nil } } - + private static func findFile(with fileName: String, in fileUrls: [URL]) -> URL? { let anyExtensionSuffix = ".*" if fileName.hasSuffix(anyExtensionSuffix) { diff --git a/Sources/LicensePlistCore/Entity/PlistInfo.swift b/Sources/LicensePlistCore/Entity/PlistInfo.swift index 5ed7d4dd..727b37a6 100644 --- a/Sources/LicensePlistCore/Entity/PlistInfo.swift +++ b/Sources/LicensePlistCore/Entity/PlistInfo.swift @@ -50,7 +50,7 @@ struct PlistInfo { let packagesAsGithubLibraries = packages.compactMap { $0.toGitHub(renames: options.config.renames, checkoutPath: checkoutPath) }.sorted() - + if checkoutPath != nil && options.config.excludes.contains(where: { $0.licenseType != nil }) { Log.warning("Filtering by license type is not supported in combination with specified package sources path") } @@ -83,7 +83,7 @@ struct PlistInfo { summary = contents summaryPath = savePath } - + mutating func loadGitHubLicenses() { if let packageSourcesPath = options.packageSourcesPath { readCheckedOutLicenses(from: packageSourcesPath) @@ -160,7 +160,7 @@ struct PlistInfo { Log.error("Failed to save summary. Error: \(String(describing: e))") } } - + private mutating func downloadGitHubLicenses() { guard let githubLibraries = githubLibraries else { preconditionFailure() } @@ -177,7 +177,7 @@ struct PlistInfo { } }.compactMap { $0 } } - + private mutating func readCheckedOutLicenses(from packageSourcesPath: URL) { guard let githubLibraries = githubLibraries else { preconditionFailure() } guard !options.licenseFileNames.isEmpty else { preconditionFailure() } diff --git a/Sources/LicensePlistCore/Entity/SwiftPackage.swift b/Sources/LicensePlistCore/Entity/SwiftPackage.swift index 7c272144..4d06bab2 100644 --- a/Sources/LicensePlistCore/Entity/SwiftPackage.swift +++ b/Sources/LicensePlistCore/Entity/SwiftPackage.swift @@ -87,7 +87,7 @@ extension SwiftPackage { // ssh owner = urlParts.first?.components(separatedBy: ":").last ?? "" } - + let nameSpecified = renames[name] ?? getDefaultName(for: owner, and: name, checkoutPath: checkoutPath) return GitHub(name: name, @@ -101,19 +101,19 @@ extension SwiftPackage { guard let packageDefinition = packageDefinition(for: owner, name: name, checkoutPath: checkoutPath) else { return fallbackName(using: name) } return parseName(from: packageDefinition) ?? fallbackName(using: name) } - + private func packageDefinition(for owner: String, name: String, checkoutPath: URL?) -> String? { // Try to read from a checkout directory in checkout folder let packageDefinitionFromDisk = checkoutPath.map { readPackageDefinition(name: name, checkoutPath: $0) } // Then download the definition from Github as a fallback return packageDefinitionFromDisk ?? loadPackageDefinitionFromGithub(for: owner, name: name) } - + private func readPackageDefinition(name: String, checkoutPath: URL) -> String? { let url = checkoutPath.appendingPathComponent(name).appendingPathComponent("Package.swift") return try? String(contentsOf: url) } - + private func loadPackageDefinitionFromGithub(for owner: String, name: String) -> String? { guard let version = version else { return nil } guard let packageDefinitionURL = URL(string: "https://raw.githubusercontent.com/\(owner)/\(name)/\(version)/Package.swift") else { return nil } diff --git a/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift b/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift index 4e4c6a1d..5482a33a 100644 --- a/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift +++ b/Tests/LicensePlistTests/Entity/PlistInfoWithSourcePackagesTests.swift @@ -4,7 +4,7 @@ import XCTest final class PlistInfoWithSourcePackagesTests: XCTestCase { private let github = GitHub(name: "R.swift", nameSpecified: "rswit", owner: "mac-cain13", version: "0.5.4") private let sourcePackagesPath = TestUtil.testResourceDir.appendingPathComponent("SourcePackages").lp.fileURL - + func testReadLicenseFromDisk() throws { var target = plistInfo() @@ -18,7 +18,7 @@ final class PlistInfoWithSourcePackagesTests: XCTestCase { XCTAssertEqual(license?.body, "license text") XCTAssertNil(license?.githubResponse) } - + func testReadLicenseMDFromDisk() throws { var target = plistInfo(licenseFileNames: ["LICENSE.md"]) @@ -27,7 +27,7 @@ final class PlistInfoWithSourcePackagesTests: XCTestCase { XCTAssertEqual(license?.body, "license.md text") } - + func testReadLicenseWithAsteriskFromDisk() throws { var target = plistInfo(licenseFileNames: ["LICENSE.*"]) @@ -36,7 +36,7 @@ final class PlistInfoWithSourcePackagesTests: XCTestCase { XCTAssertEqual(license?.body, "license.md text") } - + func testReadMissedLicenseFromDisk() throws { var target = PlistInfo(options: options(licenseFileNames: ["Not-a-license"])) target.githubLibraries = [github] @@ -46,15 +46,15 @@ final class PlistInfoWithSourcePackagesTests: XCTestCase { XCTAssertTrue(licenses.isEmpty) } - + // MARK: Helpers - + private func plistInfo(licenseFileNames: [String] = ["LICENSE"]) -> PlistInfo { var target = PlistInfo(options: options(licenseFileNames: licenseFileNames)) target.githubLibraries = [github] return target } - + private func options(licenseFileNames: [String]) -> Options { return Options(outputPath: URL(fileURLWithPath: "test_result_dir"), cartfilePath: URL(fileURLWithPath: "test_result_dir"), diff --git a/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift b/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift index fffc0a02..081d6427 100644 --- a/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift +++ b/Tests/LicensePlistTests/Entity/SwiftPackageManagerTests.swift @@ -443,7 +443,7 @@ class SwiftPackageManagerTests: XCTestCase { let result = package.toGitHub(renames: [:]) XCTAssertEqual(result?.nameSpecified, "better-name-parsed-from-repo", "For SPM v2 we try to parse the Package.swift from the repository to get the name. But when that fails, we fall back to the name in the Repository URL which is still an improvement to the name we get as `identity` from the generated JSON.") } - + // MARK: Source Packages func testResolvingNameFromCheckoutSources() { diff --git a/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift index e3a694cf..fafd6821 100644 --- a/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift +++ b/Tests/LicensePlistTests/Resources/SourcePackages/checkouts/R.swift/Package.swift @@ -16,6 +16,6 @@ let package = Package( targets: [ .target(name: "rswift", dependencies: ["RswiftCore"]), .target(name: "RswiftCore", dependencies: ["Commander", "XcodeEdit"]), - .testTarget(name: "RswiftCoreTests", dependencies: ["RswiftCore"]), + .testTarget(name: "RswiftCoreTests", dependencies: ["RswiftCore"]) ] )