-
-
Notifications
You must be signed in to change notification settings - Fork 395
/
changelog_generator
executable file
·196 lines (152 loc) · 4.37 KB
/
changelog_generator
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/env ruby
# frozen_string_literal: true
require "json"
require "ruby-progressbar"
require "./lib/decidim/github_manager/querier"
class EmptyTokenError < RuntimeError
def full_message
<<~HELP
Decidim's changelog generator.
## Example
bin/changelog_generator <GITHUB PERSONAL ACCESS TOKEN> <SHA>
## Params
The <GITHUB PERSONAL ACCESS TOKEN> can be obtained from https://github.com/settings/tokens/new
You'll need to create one with `public_repo` access.
The <SHA> is the git commit SHA from which to consider the changes. It's
usually the last commit that modified the `.decidim_version` file.
## Explanation
This script will generate the sections needed for the changelog, using the
labels of the merged PRs as a source of truth. It will add a section at the
end, "Unsorted", with the list of PRs that couldn't be classified.
It ignores any Crowdin PR.
HELP
end
end
class ChangeLogGenerator
TYPES = {
"Added" => {
label: "type: feature",
skip_modules: false
},
"Changed" => {
label: "type: change",
skip_modules: false
},
"Fixed" => {
label: "type: fix",
skip_modules: false
},
"Removed" => {
label: "type: removal",
skip_modules: false
},
"Developer improvements" => {
label: "target: developer-experience",
skip_modules: true
},
"Internal" => {
label: "type: internal",
skip_modules: false
}
}.freeze
def initialize(github_token, since_sha)
@github_token = github_token
@since_sha = since_sha
@output_file = []
@handled_ids = []
@progress_bar = ProgressBar.create(title: "PRs", total: list_of_commits.count)
end
def run
raise EmptyTokenError if github_token.nil? || github_token == "--help"
TYPES.each do |type_title, type_data|
type_prs = prs.select do |_commit_title, data|
next unless data
data[:type].include?(type_data[:label])
end
output "### #{type_title}"
output ""
if type_prs.any?
type_prs.each do |_pr_title, data|
process_single_pr(data, type_data)
end
else
output "Nothing."
end
output ""
end
process_unsorted_prs
write_data_file!
end
private
def write_data_file!
File.write("temporary_changelog.md", @output_file.join("\n"))
puts "Written file: temporary_changelog.md"
end
def process_single_pr(data, type_data)
modules_list = data[:modules].map { |l| "**decidim-#{l.delete_prefix("module: ")}**" }
id = data[:id]
title = data[:title]
@handled_ids << id
if type_data[:skip_modules] || modules_list.empty?
output "- #{title} #{pr_link(id)}"
else
output "- #{modules_list.join(", ")}: #{title} #{pr_link(id)}"
end
end
def process_unsorted_prs
return unless unsorted_prs.any?
output "### Unsorted"
output ""
unsorted_prs.map do |title, data|
pr_data = data || {}
output "- #{title} #{pr_link(pr_data[:id])} || #{data}"
end
end
def unsorted_prs
@unsorted_prs ||= prs.reject do |_commit_title, data|
pr_data = data || {}
@handled_ids.include?(pr_data[:id])
end
end
attr_reader :github_token, :since_sha
def prs
@prs ||= list_of_commits.inject({}) do |acc, commit|
next acc if crowdin?(commit)
acc.update(commit => get_pr_data(commit))
end
end
def list_of_commits
@list_of_commits ||= `git log #{since_sha}..HEAD --oneline`.split("\n").reverse
end
def crowdin?(commit)
!commit.match(/New Crowdin updates/).nil?
end
def get_pr_id(commit)
id = commit.scan(/#\d+/).last
return unless id
id.delete_prefix("#")
end
def get_pr_data(commit)
@progress_bar.increment
id = get_pr_id(commit)
return nil unless id
Decidim::GithubManager::Querier.new(
token: github_token,
issue_id: id
).call
end
def pr_link(id)
# We need to do this so that it generates the expected Markdown format.
# String interpolation messes with the format.
"[#{"\\#" + id.to_s}](https://github.com/decidim/decidim/pull/#{id})" # rubocop:disable Style/StringConcatenation
end
def output(str)
@output_file << str
end
end
begin
ChangeLogGenerator.new(ARGV[0], ARGV[1] || "HEAD~5").run
rescue EmptyTokenError => e
puts e.full_message
exit 1
end