From 9c05449787a20d332a00a53142d2072800064297 Mon Sep 17 00:00:00 2001 From: Alexander Widerberg Date: Wed, 4 Oct 2017 17:02:33 +0200 Subject: [PATCH] Added capablility to supply manual license information in the config yaml file. --- Sources/LicensePlistCore/Entity/Config.swift | 16 ++++- Sources/LicensePlistCore/Entity/Manual.swift | 68 +++++++++++++++++++ .../Entity/ManualLicense.swift | 25 +++++++ .../LicensePlistCore/Entity/PlistInfo.swift | 19 ++++-- Sources/LicensePlistCore/LicensePlist.swift | 1 + .../Entity/CocoaPodsTests.swift | 2 +- .../Entity/ConfigTests.swift | 15 ++-- .../Entity/PlistInfoTests.swift | 12 +++- .../Resources/license_plist.yml | 35 +++++++++- 9 files changed, 175 insertions(+), 18 deletions(-) create mode 100644 Sources/LicensePlistCore/Entity/Manual.swift create mode 100644 Sources/LicensePlistCore/Entity/ManualLicense.swift diff --git a/Sources/LicensePlistCore/Entity/Config.swift b/Sources/LicensePlistCore/Entity/Config.swift index 15b75dbc..40c90606 100644 --- a/Sources/LicensePlistCore/Entity/Config.swift +++ b/Sources/LicensePlistCore/Entity/Config.swift @@ -4,13 +4,14 @@ import Yaml public struct Config { let githubs: [GitHub] + let manuals: [Manual] let excludes: [String] let renames: [String: String] public var force = false public var addVersionNumbers = false public var suppressOpeningDirectory = false - public static let empty = Config(githubs: [], excludes: [], renames: [:]) + public static let empty = Config(githubs: [], manuals: [], excludes: [], renames: [:]) public init(yaml: String) { let value = try! Yaml.load(yaml) @@ -21,6 +22,8 @@ public struct Config { sum[from] = to return sum } ?? [:] + let manuals = value["manual"].array ?? [] + let manualList = Manual.load(manuals, renames: renames) let githubs = value["github"].array?.map { $0.string }.flatMap { $0 } ?? [] let gitHubList = githubs.map { GitHub.load($0, renames: renames, mark: "", quotes: "") }.flatMap { $0 } gitHubList.forEach { @@ -39,11 +42,12 @@ public struct Config { owner: owner, version: dictionary["version"]?.string) }.flatMap { $0 } ?? [] - self = Config(githubs: githubsVersion + gitHubList, excludes: excludes, renames: renames) + self = Config(githubs: githubsVersion + gitHubList, manuals: manualList, excludes: excludes, renames: renames) } - init(githubs: [GitHub], excludes: [String], renames: [String: String]) { + init(githubs: [GitHub], manuals: [Manual], excludes: [String], renames: [String: String]) { self.githubs = githubs + self.manuals = manuals self.excludes = excludes self.renames = renames } @@ -93,15 +97,21 @@ public struct Config { return result } } + func apply(githubs: [GitHub]) -> [GitHub] { self.githubs.forEach { Log.warning("\($0.name) was loaded from config YAML.") } return filterExcluded((self.githubs + githubs)) } + + func applyManual(manuals: [Manual]) -> [Manual] { + return filterExcluded((self.manuals + manuals)) + } } extension Config: Equatable { public static func==(lhs: Config, rhs: Config) -> Bool { return lhs.githubs == rhs.githubs && + lhs.manuals == rhs.manuals && lhs.excludes == rhs.excludes && lhs.renames == rhs.renames } diff --git a/Sources/LicensePlistCore/Entity/Manual.swift b/Sources/LicensePlistCore/Entity/Manual.swift new file mode 100644 index 00000000..be636cad --- /dev/null +++ b/Sources/LicensePlistCore/Entity/Manual.swift @@ -0,0 +1,68 @@ +import Foundation +import APIKit +import LoggerAPI +import Yaml + +public class Manual: Library { + public let name: String + public var body: String? // Used as a container between YAML and ManualLicence + public var source: String? + public var nameSpecified: String? + public var version: String? + + init(name n: String, source: String?, nameSpecified: String?, version: String?) { + self.name = n + self.source = source + self.nameSpecified = nameSpecified + self.version = version + } +} + +extension Manual { + public static func==(lhs: Manual, rhs: Manual) -> Bool { + return lhs.name == rhs.name && + lhs.nameSpecified == rhs.nameSpecified && + lhs.version == rhs.version + } +} + +extension Manual: CustomStringConvertible { + public var description: String { + return "name: \(name), source: \(source ?? ""), nameSpecified: \(nameSpecified ?? ""), version: \(version ?? "")" + } +} + +extension Manual { + public static func load(_ raw: [Yaml], + renames: [String: String]) -> [Manual] { + return raw.map { (manualEntry) -> Manual in + var name = "" + var body = "" + var source = "" + var rename = "" + var version = "" + for valuePair in manualEntry.dictionary ?? [:] { + switch valuePair.key.string ?? "" { + case "source": + source = valuePair.value.string ?? "" + break + case "name": + name = valuePair.value.string ?? "" + break + case "version": + version = valuePair.value.string ?? "" + break + case "body": + body = valuePair.value.string ?? "" + break + default: + Log.warning("Tried to parse an unknown YAML key") + } + } + rename = renames[name] ?? "" + let manual = Manual(name: name, source: source, nameSpecified: rename, version: version) + manual.body = body // This is so that we do not have to store a body at all ( for testing purposes mostly ) + return manual + } + } +} diff --git a/Sources/LicensePlistCore/Entity/ManualLicense.swift b/Sources/LicensePlistCore/Entity/ManualLicense.swift new file mode 100644 index 00000000..99d94893 --- /dev/null +++ b/Sources/LicensePlistCore/Entity/ManualLicense.swift @@ -0,0 +1,25 @@ +import Foundation +import LoggerAPI + +public struct ManualLicense: License, Equatable { + public let library: Manual + public let body: String + + public static func==(lhs: ManualLicense, rhs: ManualLicense) -> Bool { + return lhs.library == rhs.library + } +} + +extension ManualLicense: CustomStringConvertible { + public var description: String { + return "name: \(library.name), nameSpecified: \(library.nameSpecified ?? ""), version: \(library.version ?? "")\nbody: \(String(body.characters.prefix(20)))โ€ฆ" + } +} + +extension ManualLicense { + public static func load(_ manuals: [Manual]) -> [ManualLicense] { + return manuals.map { + return ManualLicense(library: $0, body: $0.body ?? "") + } + } +} diff --git a/Sources/LicensePlistCore/Entity/PlistInfo.swift b/Sources/LicensePlistCore/Entity/PlistInfo.swift index f41191f1..cdb6c61c 100644 --- a/Sources/LicensePlistCore/Entity/PlistInfo.swift +++ b/Sources/LicensePlistCore/Entity/PlistInfo.swift @@ -4,6 +4,7 @@ import LoggerAPI struct PlistInfo { let options: Options var cocoaPodsLicenses: [CocoaPodsLicense]? + var manualLicenses: [ManualLicense]? var githubLibraries: [GitHub]? var githubLicenses: [GitHubLicense]? var summary: String? @@ -32,13 +33,21 @@ struct PlistInfo { githubLibraries = options.config.apply(githubs: GitHub.load(cartfile ?? "", renames: options.config.renames)) } + mutating func loadManualLibraries() { + Log.info("Manual License start") + manualLicenses = ManualLicense.load(options.config.manuals) + } + mutating func compareWithLatestSummary() { - guard let cocoaPodsLicenses = cocoaPodsLicenses, let githubLibraries = githubLibraries else { preconditionFailure() } + guard let cocoaPodsLicenses = cocoaPodsLicenses, + let githubLibraries = githubLibraries, + let manualLicenses = manualLicenses else { preconditionFailure() } let config = options.config let contents = (cocoaPodsLicenses.map { String(describing: $0) } + githubLibraries.map { String(describing: $0) } + + manualLicenses.map { String(describing: $0) } + ["add-version-numbers: \(options.config.addVersionNumbers)", "LicensePlist Version: \(Consts.version)"]) .joined(separator: "\n\n") let savePath = options.outputPath.appendingPathComponent("\(Consts.prefix).latest_result.txt") @@ -60,9 +69,11 @@ struct PlistInfo { } mutating func collectLicenseInfos() { - guard let cocoaPodsLicenses = cocoaPodsLicenses, let githubLicenses = githubLicenses else { preconditionFailure() } + guard let cocoaPodsLicenses = cocoaPodsLicenses, + let githubLicenses = githubLicenses, + let manualLicenses = manualLicenses else { preconditionFailure() } - licenses = ((cocoaPodsLicenses as [LicenseInfo]) + (githubLicenses as [LicenseInfo])) + licenses = ((cocoaPodsLicenses as [LicenseInfo]) + (githubLicenses as [LicenseInfo]) + (manualLicenses as [LicenseInfo])) .reduce([String: LicenseInfo]()) { sum, e in var sum = sum sum[e.name] = e @@ -92,7 +103,7 @@ struct PlistInfo { Log.info("# Missing license:") let missing = Set(githubLibraries.map { $0.name }).subtracting(Set(licenses.map { $0.name })) if missing.isEmpty { - Log.info("None๐ŸŽ‰") + Log.info("None ๐ŸŽ‰") } else { Array(missing).sorted { $0 < $1 }.forEach { Log.warning($0) } } diff --git a/Sources/LicensePlistCore/LicensePlist.swift b/Sources/LicensePlistCore/LicensePlist.swift index 80f4b9fd..4b15620e 100644 --- a/Sources/LicensePlistCore/LicensePlist.swift +++ b/Sources/LicensePlistCore/LicensePlist.swift @@ -11,6 +11,7 @@ public final class LicensePlist { var info = PlistInfo(options: options) info.loadCocoaPodsLicense(acknowledgements: readPodsAcknowledgements(path: options.podsPath)) info.loadGitHubLibraries(cartfile: readCartfile(path: options.cartfilePath)) + info.loadManualLibraries() info.compareWithLatestSummary() info.downloadGitHubLicenses() info.collectLicenseInfos() diff --git a/Tests/LicensePlistTests/Entity/CocoaPodsTests.swift b/Tests/LicensePlistTests/Entity/CocoaPodsTests.swift index 0337f004..d06cc26e 100644 --- a/Tests/LicensePlistTests/Entity/CocoaPodsTests.swift +++ b/Tests/LicensePlistTests/Entity/CocoaPodsTests.swift @@ -14,7 +14,7 @@ class CocoaPodsTests: XCTestCase { let content = try! String(contentsOf: URL(string: path)!) let results = CocoaPodsLicense.load(content, versionInfo: VersionInfo(dictionary: ["Firebase": "1.2.3"]), - config: Config(githubs: [], excludes: [], renames: ["Firebase": "Firebase2"])) + config: Config(githubs: [], manuals: [], excludes: [], renames: ["Firebase": "Firebase2"])) XCTAssertFalse(results.isEmpty) XCTAssertEqual(results.count, 11) let licenseFirst = results.first! diff --git a/Tests/LicensePlistTests/Entity/ConfigTests.swift b/Tests/LicensePlistTests/Entity/ConfigTests.swift index 5f22e518..ab3a0687 100644 --- a/Tests/LicensePlistTests/Entity/ConfigTests.swift +++ b/Tests/LicensePlistTests/Entity/ConfigTests.swift @@ -5,19 +5,20 @@ import XCTest class ConfigTests: XCTestCase { func testInit_empty_yaml() { - XCTAssertEqual(Config(yaml: ""), Config(githubs: [], excludes: [], renames: [:])) + XCTAssertEqual(Config(yaml: ""), Config(githubs: [], manuals: [], excludes: [], renames: [:])) } func testInit_sample() { let path = "https://raw.githubusercontent.com/mono0926/LicensePlist/master/Tests/LicensePlistTests/Resources/license_plist.yml" XCTAssertEqual(Config(yaml: URL(string: path)!.lp.download().resultSync().value!), Config(githubs: [GitHub(name: "LicensePlist", nameSpecified: "License Plist", owner: "mono0926", version: "1.2.0"), GitHub(name: "NativePopup", nameSpecified: nil, owner: "mono0926", version: nil)], + manuals: [Manual(name: "WebRTC", source: "https://webrtc.googlesource.com/src", nameSpecified: "Web RTC", version: "M61")], excludes: ["RxSwift", "ios-license-generator", "/^Core.*$/"], - renames: ["LicensePlist": "License Plist"])) + renames: ["LicensePlist": "License Plist", "WebRTC": "Web RTC"])) } func testExcluded() { - let target = Config(githubs: [], excludes: ["lib1"], renames: [:]) + let target = Config(githubs: [], manuals: [], excludes: ["lib1"], renames: [:]) XCTAssertTrue(target.excluded(name: "lib1")) XCTAssertFalse(target.excluded(name: "lib2")) } @@ -28,22 +29,22 @@ class ConfigTests: XCTestCase { } func testExcluded_regex() { - let target = Config(githubs: [], excludes: ["/^lib.*$/"], renames: [:]) + let target = Config(githubs: [], manuals: [], excludes: ["/^lib.*$/"], renames: [:]) XCTAssertTrue(target.excluded(name: "lib1")) XCTAssertTrue(target.excluded(name: "lib2")) XCTAssertFalse(target.excluded(name: "hello")) } func testApply_filterExcluded() { - let config = Config(githubs: [], excludes: ["lib2"], renames: [:]) + let config = Config(githubs: [], manuals: [], excludes: ["lib2"], renames: [:]) let shouldBeIncluded = GitHub(name: "lib1", nameSpecified: nil, owner: "o1", version: nil) let result = config.filterExcluded([shouldBeIncluded, GitHub(name: "lib2", nameSpecified: nil, owner: "o2", version: nil)]) XCTAssertEqual(result, [shouldBeIncluded]) } func testApply_githubs() { - let github1 = GitHub(name: "github1", nameSpecified: nil, owner: "g1", version: nil) - let config = Config(githubs: [github1], excludes: ["lib2"], renames: [:]) + let github1 = GitHub(name: "github1", nameSpecified: nil, owner: "g1", version: nil) + let config = Config(githubs: [github1], manuals: [], excludes: ["lib2"], renames: [:]) let shouldBeIncluded = GitHub(name: "lib1", nameSpecified: nil, owner: "o1", version: nil) let result = config.apply(githubs: [shouldBeIncluded, GitHub(name: "lib2", nameSpecified: nil, owner: "o2", version: nil)]) XCTAssertEqual(result, [github1, shouldBeIncluded]) diff --git a/Tests/LicensePlistTests/Entity/PlistInfoTests.swift b/Tests/LicensePlistTests/Entity/PlistInfoTests.swift index 7699cd94..fa36ef53 100644 --- a/Tests/LicensePlistTests/Entity/PlistInfoTests.swift +++ b/Tests/LicensePlistTests/Entity/PlistInfoTests.swift @@ -21,6 +21,7 @@ class PlistInfoTests: XCTestCase { nameSpecified: nil, owner: "owner", version: nil)], + manuals: [], excludes: ["exclude"], renames: ["Himotoki": "Himotoki2"])) @@ -69,6 +70,7 @@ class PlistInfoTests: XCTestCase { func testCompareWithLatestSummary() { var target = PlistInfo(options: options) target.cocoaPodsLicenses = [] + target.manualLicenses = [] target.githubLibraries = [] XCTAssertNil(target.summary) @@ -106,13 +108,17 @@ class PlistInfoTests: XCTestCase { kind: LicenseKindResponse(name: "name", spdxId: nil))) target.cocoaPodsLicenses = [] + let manual = Manual(name: "FooBar", source: "https://foo.bar", nameSpecified: nil, version: nil) + let manualLicense = ManualLicense(library: manual, + body: "body") + target.manualLicenses = [manualLicense] target.githubLicenses = [githubLicense] XCTAssertNil(target.licenses) target.collectLicenseInfos() let licenses = target.licenses! - XCTAssertEqual(licenses.count, 1) - let license = licenses.first! + XCTAssertEqual(licenses.count, 2) + let license = licenses.last! XCTAssertEqual(license.name, "LicensePlist") } @@ -160,6 +166,8 @@ class PlistInfoTests: XCTestCase { target.githubLicenses = [githubLicense] let podsLicense = CocoaPodsLicense(library: CocoaPods(name: "", nameSpecified: nil, version: nil), body: "body") target.cocoaPodsLicenses = [podsLicense] + let manualLicense = ManualLicense(library: Manual(name: "", source: nil, nameSpecified: nil, version: nil), body: "body") + target.manualLicenses = [manualLicense] target.licenses = [githubLicense, podsLicense] target.summary = "" target.summaryPath = URL(fileURLWithPath: "") diff --git a/Tests/LicensePlistTests/Resources/license_plist.yml b/Tests/LicensePlistTests/Resources/license_plist.yml index 2342e49f..9d3b0a1e 100644 --- a/Tests/LicensePlistTests/Resources/license_plist.yml +++ b/Tests/LicensePlistTests/Resources/license_plist.yml @@ -8,6 +8,38 @@ github: # Depricated(Will be removed at Version 2. Use above.) - mono0926/NativePopup +# Specify libraries manually +manual: + - source: https://webrtc.googlesource.com/src + name: WebRTC + version: M61 + body: ' + Copyright (c) 2011, The WebRTC project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +' + # Specify libraries to be excluded. exclude: - RxSwift @@ -16,4 +48,5 @@ exclude: # Specify libraries' names to be renamed. rename: - LicensePlist: License Plist # Rename LicensePlist to "License Plist" \ No newline at end of file + LicensePlist: License Plist # Rename LicensePlist to "License Plist" + WebRTC: Web RTC # Rename WebRTC to "Web RTC" (which is faulty, but used for test) \ No newline at end of file