Skip to content

Commit

Permalink
Merge pull request #211 from vladvlasov256/feature/checkout_path
Browse files Browse the repository at this point in the history
Add '--swift-package-sources-path' option support
  • Loading branch information
mono0926 authored Mar 9, 2023
2 parents 2d6d402 + 69f884c commit 1137b62
Show file tree
Hide file tree
Showing 19 changed files with 323 additions and 27 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ 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-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-sources-path ./SourcePackages`

#### `--xcodeproj-path`

- Default: `"*.xcodeproj"`
Expand Down Expand Up @@ -157,6 +163,14 @@ 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.*`.
- 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`

- Default: false
Expand Down Expand Up @@ -248,12 +262,16 @@ options:
podsPath: Pods
packagePaths:
- Package.swift
packageSourcesPath: "./SourcePackages"
xcodeprojPath: "*.xcodeproj"
xcworkspacePath: "*.xcworkspace"
prefix: com.mono0926.LicensePlist
gitHubToken: YOUR_GITHUB_TOKEN
htmlPath: acknowledgements.html
markdownPath: acknowledgements.md
licenseFileNames:
- LICENSE
- LICENSE.*
force: false
addVersionNumbers: false
suppressOpeningDirectory: false
Expand Down
11 changes: 11 additions & 0 deletions Sources/LicensePlist/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,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-sources-path")], completion: .directory)
var packageSourcesPath: String?

@Option(name: .long, completion: .file())
var xcworkspacePath: String?

Expand All @@ -60,6 +63,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?

Expand Down Expand Up @@ -103,24 +109,29 @@ 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 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
let githubToken = githubToken ?? config.options.gitHubToken ?? Environment.shared[.githubToken]
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) },
packageSourcesPath: packageSourcesPath.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)
Expand Down
1 change: 1 addition & 0 deletions Sources/LicensePlistCore/Consts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public struct Consts {
public static let configPath = "license_plist.yml"
public static let version = "3.24.2"
public static let encoding = String.Encoding.utf8
public static let licenseFileNames = ["LICENSE", "LICENSE.*"]
}
12 changes: 12 additions & 0 deletions Sources/LicensePlistCore/Entity/GeneralOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ public struct GeneralOptions {
public let mintfilePath: String?
public let podsPath: String?
public let packagePaths: [String]?
public let packageSourcesPath: 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?
Expand All @@ -26,12 +28,14 @@ public struct GeneralOptions {
mintfilePath: nil,
podsPath: nil,
packagePaths: nil,
packageSourcesPath: nil,
xcworkspacePath: nil,
xcodeprojPath: nil,
prefix: nil,
gitHubToken: nil,
htmlPath: nil,
markdownPath: nil,
licenseFileNames: nil,
force: nil,
addVersionNumbers: nil,
suppressOpeningDirectory: nil,
Expand All @@ -44,12 +48,14 @@ public struct GeneralOptions {
mintfilePath: String?,
podsPath: String?,
packagePaths: [String]?,
packageSourcesPath: String?,
xcworkspacePath: String?,
xcodeprojPath: String?,
prefix: String?,
gitHubToken: String?,
htmlPath: String?,
markdownPath: String?,
licenseFileNames: [String]?,
force: Bool?,
addVersionNumbers: Bool?,
suppressOpeningDirectory: Bool?,
Expand All @@ -61,12 +67,14 @@ public struct GeneralOptions {
self.mintfilePath = mintfilePath
self.podsPath = podsPath
self.packagePaths = packagePaths
self.packageSourcesPath = packageSourcesPath
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
Expand All @@ -83,12 +91,14 @@ extension GeneralOptions {
lhs.mintfilePath == rhs.mintfilePath &&
lhs.podsPath == rhs.podsPath &&
lhs.packagePaths == rhs.packagePaths &&
lhs.packageSourcesPath == rhs.packageSourcesPath &&
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 &&
Expand All @@ -105,12 +115,14 @@ extension GeneralOptions {
mintfilePath: raw["mintfilePath"]?.string,
podsPath: raw["podsPath"]?.string,
packagePaths: raw["packagePaths"]?.array?.compactMap(\.string),
packageSourcesPath: raw["packageSourcesPath"]?.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,
Expand Down
55 changes: 54 additions & 1 deletion Sources/LicensePlistCore/Entity/GitHubLicense.swift
Original file line number Diff line number Diff line change
@@ -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 &&
Expand Down Expand Up @@ -66,6 +67,58 @@ 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 }

// Check several variants of license file name
for fileName in licenseFileNames {
guard let url = findFile(with: fileName, in: libraryFileUrls) else {
continue
}
do {
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 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 {
return nil
Expand Down
8 changes: 8 additions & 0 deletions Sources/LicensePlistCore/Entity/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,58 @@ public struct Options {
public let mintfilePath: URL
public let podsPath: URL
public let packagePaths: [URL]
public let packageSourcesPath: 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: ""),
cartfilePath: URL(fileURLWithPath: ""),
mintfilePath: URL(fileURLWithPath: ""),
podsPath: URL(fileURLWithPath: ""),
packagePaths: [],
packageSourcesPath: nil,
xcworkspacePath: URL(fileURLWithPath: ""),
xcodeprojPath: URL(fileURLWithPath: ""),
prefix: Consts.prefix,
gitHubToken: nil,
htmlPath: nil,
markdownPath: nil,
licenseFileNames: [],
config: Config.empty)

public init(outputPath: URL,
cartfilePath: URL,
mintfilePath: URL,
podsPath: URL,
packagePaths: [URL],
packageSourcesPath: 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.packageSourcesPath = packageSourcesPath
self.xcworkspacePath = xcworkspacePath
self.xcodeprojPath = xcodeprojPath
self.prefix = prefix
self.gitHubToken = gitHubToken
self.htmlPath = htmlPath
self.markdownPath = markdownPath
self.licenseFileNames = licenseFileNames
self.config = config
}
}
56 changes: 40 additions & 16 deletions Sources/LicensePlistCore/Entity/PlistInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ 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 checkoutPath = options.packageSourcesPath?.appendingPathComponent("checkouts")
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)
}
Expand Down Expand Up @@ -77,21 +84,12 @@ struct PlistInfo {
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 packageSourcesPath = options.packageSourcesPath {
readCheckedOutLicenses(from: packageSourcesPath)
} else {
downloadGitHubLicenses()
}
}

mutating func collectLicenseInfos() {
Expand Down Expand Up @@ -162,4 +160,30 @@ 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 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)
}
}
Loading

0 comments on commit 1137b62

Please sign in to comment.