forked from apple/swift-argument-parser
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.swift
145 lines (118 loc) · 3.73 KB
/
main.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//
import ArgumentParser
import Foundation
// MARK: GitHub API response modeling
struct Comparison: Codable {
var commits: [Commit]
}
struct Commit: Codable {
var sha: String
var author: Author
}
struct Author: Codable {
var login: String
var htmlURL: String
enum CodingKeys: String, CodingKey {
case login
case htmlURL = "html_url"
}
var inlineLink: String {
"[\(login)]"
}
}
// MARK: Helpers
extension Sequence {
func uniqued<T: Hashable>(by transform: (Element) throws -> T) rethrows -> [Element] {
var seen: Set<T> = []
var result: [Element] = []
for element in self {
if try seen.insert(transform(element)).inserted {
result.append(element)
}
}
return result
}
}
// MARK: Command
struct ChangelogAuthors: ParsableCommand {
static var configuration: CommandConfiguration {
CommandConfiguration(
abstract: "A helper tool for generating author info for the changelog.",
discussion: """
Call this tool with a starting and ending tag to list authors of
commits between those two releases. Provide only a single tag to
list authors from that release up to the current top-of-tree.
""")
}
@Argument(help: "The starting point for the comparison.")
var startingTag: String
@Argument(help: "The ending point for the comparison.")
var endingTag: String?
@Option(name: [.short, .customLong("repo")], help: "The GitHub repository to search for changes.")
var repository: String = "apple/swift-argument-parser"
func validate() throws {
func checkTag(_ tag: String) -> Bool {
tag.allSatisfy {
$0.isLetter || $0.isNumber || $0 == "."
}
}
guard checkTag(startingTag) else {
throw ValidationError("Invalid starting tag: \(startingTag)")
}
if let endingTag = endingTag {
guard checkTag(endingTag) else {
throw ValidationError("Invalid ending tag: \(endingTag)")
}
}
}
func links(for authors: [Author]) -> String {
if authors.count <= 2 {
return authors.map({ $0.inlineLink }).joined(separator: " and ")
} else {
let result = authors.dropLast()
.map({ $0.inlineLink })
.joined(separator: ", ")
return "\(result), and \(authors.last!.inlineLink)"
}
}
func linkReference(for author: Author) -> String {
"""
[\(author.login)]: \
https://github.com/\(repository)/commits?author=\(author.login)
"""
}
func references(for authors: [Author]) -> String {
authors
.map({ linkReference(for: $0) })
.joined(separator: "\n")
}
func comparisonURL() throws -> URL {
guard let url = URL(
string: "https://api.github.com/repos/\(repository)/compare/\(startingTag)...\(endingTag ?? "HEAD")")
else {
print("Couldn't create url string")
throw ExitCode.failure
}
return url
}
mutating func run() throws {
let data = try Data(contentsOf: try comparisonURL())
let comparison = try JSONDecoder().decode(Comparison.self, from: data)
let authors = comparison.commits.map({ $0.author })
.uniqued(by: { $0.login })
.sorted(by: { $0.login.lowercased() < $1.login.lowercased() })
print(links(for: authors))
print("---")
print(references(for: authors))
}
}
ChangelogAuthors.main()