create_github_release 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +1 -1
- data/README.md +36 -21
- data/create_github_release.gemspec +1 -0
- data/exe/create-github-release +12 -8
- data/lib/create_github_release/assertion_base.rb +25 -11
- data/lib/create_github_release/assertions/bundle_is_up_to_date.rb +4 -3
- data/lib/create_github_release/assertions/{docker_is_running.rb → gh_authenticated.rb} +9 -8
- data/lib/create_github_release/assertions/gh_command_exists.rb +3 -2
- data/lib/create_github_release/assertions/git_command_exists.rb +3 -2
- data/lib/create_github_release/assertions/in_git_repo.rb +3 -2
- data/lib/create_github_release/assertions/in_repo_root_directory.rb +3 -2
- data/lib/create_github_release/assertions/last_release_tag_exists.rb +45 -0
- data/lib/create_github_release/assertions/local_and_remote_on_same_commit.rb +4 -3
- data/lib/create_github_release/assertions/local_release_branch_does_not_exist.rb +6 -5
- data/lib/create_github_release/assertions/local_release_tag_does_not_exist.rb +3 -3
- data/lib/create_github_release/assertions/no_staged_changes.rb +3 -2
- data/lib/create_github_release/assertions/no_uncommitted_changes.rb +3 -2
- data/lib/create_github_release/assertions/on_default_branch.rb +5 -4
- data/lib/create_github_release/assertions/remote_release_branch_does_not_exist.rb +6 -5
- data/lib/create_github_release/assertions/remote_release_tag_does_not_exist.rb +6 -5
- data/lib/create_github_release/assertions.rb +2 -2
- data/lib/create_github_release/backtick_debug.rb +69 -0
- data/lib/create_github_release/change.rb +73 -0
- data/lib/create_github_release/changelog.rb +40 -68
- data/lib/create_github_release/command_line_options.rb +367 -0
- data/lib/create_github_release/command_line_parser.rb +113 -25
- data/lib/create_github_release/project.rb +787 -0
- data/lib/create_github_release/release_assertions.rb +3 -3
- data/lib/create_github_release/release_tasks.rb +1 -1
- data/lib/create_github_release/task_base.rb +25 -11
- data/lib/create_github_release/tasks/commit_release.rb +4 -3
- data/lib/create_github_release/tasks/create_github_release.rb +16 -35
- data/lib/create_github_release/tasks/create_release_branch.rb +6 -5
- data/lib/create_github_release/tasks/create_release_pull_request.rb +10 -42
- data/lib/create_github_release/tasks/create_release_tag.rb +6 -5
- data/lib/create_github_release/tasks/push_release.rb +5 -4
- data/lib/create_github_release/tasks/update_changelog.rb +16 -67
- data/lib/create_github_release/tasks/update_version.rb +4 -3
- data/lib/create_github_release/version.rb +1 -1
- data/lib/create_github_release.rb +4 -2
- metadata +23 -8
- data/.vscode/settings.json +0 -7
- data/lib/create_github_release/assertions/changelog_docker_container_exists.rb +0 -73
- data/lib/create_github_release/options.rb +0 -397
- data/lib/create_github_release/release.rb +0 -82
@@ -0,0 +1,787 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bump'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module CreateGithubRelease
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
8
|
+
|
9
|
+
# Captures the options for this script
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
#
|
13
|
+
class Project
|
14
|
+
# Initialize a new Project
|
15
|
+
#
|
16
|
+
# Project attributes are set using one of these methods:
|
17
|
+
# 1. The attribute is explicitly set using an attribute writer
|
18
|
+
# 2. The attribute is set using the options object
|
19
|
+
# 3. The attribute is set using the default value or running some command
|
20
|
+
#
|
21
|
+
# Method 1 takes precedence, then method 2, then method 3.
|
22
|
+
#
|
23
|
+
# The recommended way to set the attributes is to use method 2 to override values
|
24
|
+
# and otherwise method 3 to use the default value. Method 1 is only recommended for testing
|
25
|
+
# or if there is no other way to accomplish what you need.
|
26
|
+
#
|
27
|
+
# @example calling `.new` without a block
|
28
|
+
# options = CreateGithubRelease::CommandLineOptions.new { |o| o.release_type = 'minor' }
|
29
|
+
# project = CreateGithubRelease::Project.new(options)
|
30
|
+
# options.release_type = 'minor'
|
31
|
+
#
|
32
|
+
# @example calling `.new` with a block
|
33
|
+
# options = CreateGithubRelease::CommandLineOptions.new { |o| o.release_type = 'minor' }
|
34
|
+
# project = CreateGithubRelease::Project.new(options) do |p|
|
35
|
+
# p.release_type = 'major'
|
36
|
+
# end
|
37
|
+
# options.release_type = 'major'
|
38
|
+
#
|
39
|
+
# @param options [CreateGithubRelease::CommandLineOptions] the options to initialize the instance with
|
40
|
+
#
|
41
|
+
# @yield [self] an initialization block
|
42
|
+
# @yieldparam self [CreateGithubRelease::Project] the instance being initialized aka `self`
|
43
|
+
# @yieldreturn [void] the return value is ignored
|
44
|
+
#
|
45
|
+
def initialize(options)
|
46
|
+
@options = options
|
47
|
+
yield self if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
# @!attribute options [r]
|
51
|
+
#
|
52
|
+
# The command line options used to initialize this project
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
56
|
+
# project = CreateGithubRelease::Project.new(options)
|
57
|
+
# project.options == options #=> true
|
58
|
+
#
|
59
|
+
# @return [CreateGithubRelease::CommandLineOptions]
|
60
|
+
#
|
61
|
+
attr_reader :options
|
62
|
+
|
63
|
+
attr_writer \
|
64
|
+
:default_branch, :next_release_tag, :next_release_date, :next_release_version,
|
65
|
+
:last_release_tag, :last_release_version, :release_branch, :release_log_url,
|
66
|
+
:release_type, :release_url, :remote, :remote_base_url, :remote_repository, :remote_url,
|
67
|
+
:changelog_path, :changes, :next_release_description, :last_release_changelog,
|
68
|
+
:next_release_changelog, :verbose, :quiet
|
69
|
+
|
70
|
+
# @!attribute [rw] default_branch
|
71
|
+
#
|
72
|
+
# The default branch of the remote repository
|
73
|
+
#
|
74
|
+
# This is the default branch as reported by the `HEAD branch` returned by
|
75
|
+
# `git remote show #{remote}`.
|
76
|
+
#
|
77
|
+
# Uses the value of `remote` to determine the remote repository to query.
|
78
|
+
#
|
79
|
+
# @example By default, `default_branch` is based on `git remote show`
|
80
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
81
|
+
# project = CreateGithubRelease::Project.new(options)
|
82
|
+
# options.default_branch # => 'main'
|
83
|
+
#
|
84
|
+
# @example `default_branch` can be set explicitly
|
85
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
86
|
+
# project = CreateGithubRelease::Project.new(options)
|
87
|
+
# project.default_branch = 'master'
|
88
|
+
# project.default_branch #=> 'master'
|
89
|
+
#
|
90
|
+
# @return [String]
|
91
|
+
#
|
92
|
+
# @raise [RuntimeError] if the git command fails
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
#
|
96
|
+
def default_branch
|
97
|
+
@default_branch ||= options.default_branch || begin
|
98
|
+
output = `git remote show '#{remote}'`
|
99
|
+
raise "Could not determine default branch for remote '#{remote}'" unless $CHILD_STATUS.success?
|
100
|
+
|
101
|
+
output.match(/HEAD branch: (.*?)$/)[1]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @!attribute [rw] next_release_tag
|
106
|
+
#
|
107
|
+
# The tag to use for the next release
|
108
|
+
#
|
109
|
+
# Uses the value of `next_release_version` to determine the tag name.
|
110
|
+
#
|
111
|
+
# @example By default, `next_release_tag` is based on `next_release_version`
|
112
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
113
|
+
# project = CreateGithubRelease::Project.new(options)
|
114
|
+
# project.next_release_version = '1.0.0'
|
115
|
+
# project.next_relase_tag #=> 'v1.0.0'
|
116
|
+
#
|
117
|
+
# @example `next_tag` can be set explicitly
|
118
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
119
|
+
# project = CreateGithubRelease::Project.new(options)
|
120
|
+
# project.next_release_tag = 'v1.0.0'
|
121
|
+
# project.next_relase_tag #=> 'v1.0.0'
|
122
|
+
#
|
123
|
+
# @return [String]
|
124
|
+
#
|
125
|
+
# @api public
|
126
|
+
#
|
127
|
+
def next_release_tag
|
128
|
+
@next_release_tag ||= "v#{next_release_version}"
|
129
|
+
end
|
130
|
+
|
131
|
+
# @!attribute [rw] next_release_date
|
132
|
+
#
|
133
|
+
# The date next_release_tag was created
|
134
|
+
#
|
135
|
+
# If the next_release_tag does not exist, Date.today is returned.
|
136
|
+
#
|
137
|
+
# @example By default, `next_release_date` is based on `next_release_tag`
|
138
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
139
|
+
# project = CreateGithubRelease::Project.new(options)
|
140
|
+
# project.next_release_tag = 'v1.0.0'
|
141
|
+
# project.next_release_date #=> #<Date: 2023-02-01 ((2459189j,0s,0n),+0s,2299161j)>
|
142
|
+
#
|
143
|
+
# @example It can also be set explicitly
|
144
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
145
|
+
# project = CreateGithubRelease::Project.new(options)
|
146
|
+
# project.next_release_date = Date.new(2023, 2, 1)
|
147
|
+
# project.next_release_date #=> #<Date: 2023-02-01 ((2459189j,0s,0n),+0s,2299161j)>
|
148
|
+
#
|
149
|
+
# @return [Date]
|
150
|
+
#
|
151
|
+
# @raise [RuntimeError] if the git command fails
|
152
|
+
#
|
153
|
+
# @api public
|
154
|
+
#
|
155
|
+
def next_release_date
|
156
|
+
@next_release_date ||=
|
157
|
+
if tag_exist?(next_release_tag)
|
158
|
+
date = `git show --format=format:%aI --quiet "#{next_release_tag}"`
|
159
|
+
raise "Could not determine date for tag '#{next_release_tag}'" unless $CHILD_STATUS.success?
|
160
|
+
|
161
|
+
Date.parse(date.chomp)
|
162
|
+
else
|
163
|
+
Date.today
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# `true` if the given tag exists in the local repository
|
168
|
+
#
|
169
|
+
# @example
|
170
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
171
|
+
# project = CreateGithubRelease::Project.new(options)
|
172
|
+
# project.tag_exist?('v1.0.0') #=> false
|
173
|
+
#
|
174
|
+
# @param tag [String] the tag to check
|
175
|
+
#
|
176
|
+
# @return [Boolean]
|
177
|
+
#
|
178
|
+
# @api public
|
179
|
+
#
|
180
|
+
def tag_exist?(tag)
|
181
|
+
tags = `git tag --list "#{tag}"`.chomp
|
182
|
+
raise 'Could not list tags' unless $CHILD_STATUS.success?
|
183
|
+
|
184
|
+
!tags.empty?
|
185
|
+
end
|
186
|
+
|
187
|
+
# @!attribute [rw] next_release_version
|
188
|
+
#
|
189
|
+
# The version of the next release
|
190
|
+
#
|
191
|
+
# @example By default, `next_release_version` is based on the value returned by `bump show-next <release_type>`
|
192
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
193
|
+
# project = CreateGithubRelease::Project.new(options)
|
194
|
+
# project.next_release_version #=> '1.0.0'
|
195
|
+
#
|
196
|
+
# @example It can also be set explicitly
|
197
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
198
|
+
# project = CreateGithubRelease::Project.new(options)
|
199
|
+
# project.next_release_version = '1.0.0
|
200
|
+
# project.next_release_version #=> '1.0.0'
|
201
|
+
#
|
202
|
+
# @return [String]
|
203
|
+
#
|
204
|
+
# @raise [RuntimeError] if the bump command fails
|
205
|
+
#
|
206
|
+
# @api public
|
207
|
+
#
|
208
|
+
def next_release_version
|
209
|
+
@next_release_version ||= options.next_release_version || begin
|
210
|
+
output = `bump show-next #{release_type}`
|
211
|
+
raise 'Could not determine next version using bump' unless $CHILD_STATUS.success?
|
212
|
+
|
213
|
+
output.lines.last.chomp
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# @!attribute [rw] last_release_tag
|
218
|
+
#
|
219
|
+
# The tag to used for the last release
|
220
|
+
#
|
221
|
+
# Uses the value of `last_release_version` to determine the tag name.
|
222
|
+
#
|
223
|
+
# @example By default, `last_release_tag` is based on `last_release_version`
|
224
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
225
|
+
# project = CreateGithubRelease::Project.new(options)
|
226
|
+
# project.last_release_version = '0.0.1'
|
227
|
+
# project.last_relase_tag #=> 'v0.0.1'
|
228
|
+
#
|
229
|
+
# @example `last_release_tag` can be set explicitly
|
230
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
231
|
+
# project = CreateGithubRelease::Project.new(options)
|
232
|
+
# project.last_release_tag = 'v0.0.1'
|
233
|
+
# project.last_relase_tag #=> 'v0.0.1'
|
234
|
+
#
|
235
|
+
# @return [String]
|
236
|
+
#
|
237
|
+
# @api public
|
238
|
+
#
|
239
|
+
def last_release_tag
|
240
|
+
@last_release_tag ||= "v#{last_release_version}"
|
241
|
+
end
|
242
|
+
|
243
|
+
# @!attribute [rw] last_release_version
|
244
|
+
#
|
245
|
+
# The version of the last release
|
246
|
+
#
|
247
|
+
# @example By default, `last_release_version` is based on the value returned by `bump current`
|
248
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
249
|
+
# project = CreateGithubRelease::Project.new(options)
|
250
|
+
# project.last_release_version #=> '0.0.1'
|
251
|
+
#
|
252
|
+
# @example It can also be set explicitly
|
253
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
254
|
+
# project = CreateGithubRelease::Project.new(options)
|
255
|
+
# project.last_release_version = '0.0.1
|
256
|
+
# project.last_release_version #=> '0.0.1'
|
257
|
+
#
|
258
|
+
# @return [String]
|
259
|
+
#
|
260
|
+
# @raise [RuntimeError] if the bump command fails
|
261
|
+
#
|
262
|
+
# @api public
|
263
|
+
#
|
264
|
+
def last_release_version
|
265
|
+
@last_release_version ||= options.last_release_version || begin
|
266
|
+
output = `bump current`
|
267
|
+
raise 'Could not determine current version using bump' unless $CHILD_STATUS.success?
|
268
|
+
|
269
|
+
output.lines.last.chomp
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# @!attribute [rw] release_branch
|
274
|
+
#
|
275
|
+
# The name of the release branch being created
|
276
|
+
#
|
277
|
+
# @example By default, `release_branch` is based on the value returned by `next_release_tag`
|
278
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
279
|
+
# project = CreateGithubRelease::Project.new(options)
|
280
|
+
# project.next_release_tag = 'v1.0.0'
|
281
|
+
# project.release_branch #=> 'release-v1.0.0'
|
282
|
+
#
|
283
|
+
# @example It can also be set explicitly
|
284
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
285
|
+
# project = CreateGithubRelease::Project.new(options)
|
286
|
+
# project.next_release_branch = 'release-v1.0.0'
|
287
|
+
# project.next_release_branch #=> 'release-v1.0.0'
|
288
|
+
#
|
289
|
+
# @return [String]
|
290
|
+
#
|
291
|
+
# @raise [RuntimeError] if the bump command fails
|
292
|
+
#
|
293
|
+
# @api public
|
294
|
+
#
|
295
|
+
def release_branch
|
296
|
+
@release_branch ||= options.release_branch || "release-#{next_release_tag}"
|
297
|
+
end
|
298
|
+
|
299
|
+
# @!attribute [rw] release_log_url
|
300
|
+
#
|
301
|
+
# The URL of the page containing a list of the changes in the release
|
302
|
+
#
|
303
|
+
# @example By default, `release_log_url` is based on `remote_url`, `last_release_tag`, and `next_release_tag`
|
304
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
305
|
+
# project = CreateGithubRelease::Project.new(options)
|
306
|
+
# project.remote_url = URI.parse('https://github.com/org/repo')
|
307
|
+
# project.last_release_tag = 'v0.0.1'
|
308
|
+
# project.next_release_tag = 'v1.0.0'
|
309
|
+
# project.release_log_url #=> #<URI::HTTPS https://github.com/org/repo/compare/v0.0.1..v1.0.0>
|
310
|
+
#
|
311
|
+
# @example It can also be set explicitly
|
312
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
313
|
+
# project = CreateGithubRelease::Project.new(options)
|
314
|
+
# project.release_log_url = URI.parse('https://github.com/org/repo/compare/v0.0.1..v1.0.0')
|
315
|
+
# project.release_log_url #=> #<URI::HTTPS https://github.com/org/repo/compare/v0.0.1..v1.0.0>
|
316
|
+
#
|
317
|
+
# @return [URI]
|
318
|
+
#
|
319
|
+
# @raise [RuntimeError] if the bump command fails
|
320
|
+
#
|
321
|
+
# @api public
|
322
|
+
#
|
323
|
+
def release_log_url
|
324
|
+
@release_log_url ||= URI.parse("#{remote_url}/compare/#{last_release_tag}..#{next_release_tag}")
|
325
|
+
end
|
326
|
+
|
327
|
+
# @!attribute [rw] release_type
|
328
|
+
#
|
329
|
+
# The type of the release being created (e.g. 'major', 'minor', 'patch')
|
330
|
+
#
|
331
|
+
# @note this must be one of the values accepted by the `bump` command
|
332
|
+
#
|
333
|
+
# @example By default, this value comes from the options object
|
334
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
335
|
+
# project = CreateGithubRelease::Project.new(options)
|
336
|
+
# project.release_type #=> 'major'
|
337
|
+
#
|
338
|
+
# @example It can also be set explicitly
|
339
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
340
|
+
# project = CreateGithubRelease::Project.new(options)
|
341
|
+
# project.release_type = 'patch'
|
342
|
+
# project.release_type #=> 'patch'
|
343
|
+
#
|
344
|
+
# @return [String]
|
345
|
+
#
|
346
|
+
# @raise [ArgumentError] if a release type was not provided
|
347
|
+
#
|
348
|
+
# @api public
|
349
|
+
#
|
350
|
+
def release_type
|
351
|
+
@release_type ||= options.release_type || raise(ArgumentError, 'release_type is required')
|
352
|
+
end
|
353
|
+
|
354
|
+
# @!attribute [rw] release_url
|
355
|
+
#
|
356
|
+
# The URL of the page containing a list of the changes in the release
|
357
|
+
#
|
358
|
+
# @example By default, `release_url` is based on `remote_url` and `next_release_tag`
|
359
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
360
|
+
# project = CreateGithubRelease::Project.new(options)
|
361
|
+
# project.remote_url = URI.parse('https://github.com/org/repo')
|
362
|
+
# project.next_release_tag = 'v1.0.0'
|
363
|
+
# project.release_url #=> #<URI::HTTPS https://github.com/org/repo/releases/tag/v1.0.0>
|
364
|
+
#
|
365
|
+
# @example It can also be set explicitly
|
366
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
367
|
+
# project = CreateGithubRelease::Project.new(options)
|
368
|
+
# project.release_url = URI.parse('https://github.com/org/repo/releases/tag/v1.0.0')
|
369
|
+
# project.release_url #=> #<URI::HTTPS https://github.com/org/repo/releases/tag/v1.0.0>
|
370
|
+
#
|
371
|
+
# @return [URI]
|
372
|
+
#
|
373
|
+
# @api public
|
374
|
+
#
|
375
|
+
def release_url
|
376
|
+
@release_url ||= URI.parse("#{remote_url}/releases/tag/#{next_release_tag}")
|
377
|
+
end
|
378
|
+
|
379
|
+
# @!attribute [rw] remote
|
380
|
+
#
|
381
|
+
# The git remote used to determine the repository url
|
382
|
+
#
|
383
|
+
# @example By default, 'origin' is used
|
384
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
385
|
+
# project = CreateGithubRelease::Project.new(options)
|
386
|
+
# project.remote #=> 'origin'
|
387
|
+
#
|
388
|
+
# @example It can also be set in the options
|
389
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major', remote: 'upstream')
|
390
|
+
# project = CreateGithubRelease::Project.new(options)
|
391
|
+
# project.remote #=> 'upstream'
|
392
|
+
#
|
393
|
+
# @example It can also be set explicitly
|
394
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
395
|
+
# project = CreateGithubRelease::Project.new(options)
|
396
|
+
# project.remote = 'upstream'
|
397
|
+
# project.remote #=> 'upstream'
|
398
|
+
#
|
399
|
+
# @return [String]
|
400
|
+
#
|
401
|
+
# @api public
|
402
|
+
#
|
403
|
+
def remote
|
404
|
+
@remote ||= options.remote || 'origin'
|
405
|
+
end
|
406
|
+
|
407
|
+
# @!attribute [rw] remote_base_url
|
408
|
+
#
|
409
|
+
# The base part of the remote url (e.g. 'https://github.com/')
|
410
|
+
#
|
411
|
+
# @example By default, this value is based on `remote_url`
|
412
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
413
|
+
# project = CreateGithubRelease::Project.new(options)
|
414
|
+
# project.remote_url = URI.parse('https://github.com/org/repo')
|
415
|
+
# project.remote #=> #<URI::HTTPS https://github.com/>
|
416
|
+
#
|
417
|
+
# @example It can also be set explicitly
|
418
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
419
|
+
# project = CreateGithubRelease::Project.new(options)
|
420
|
+
# project.remote_base_url = URI.parse('https://github.com/')
|
421
|
+
# project.remote_base_url #=> #<URI::HTTPS https://github.com/>
|
422
|
+
#
|
423
|
+
# @return [URI]
|
424
|
+
#
|
425
|
+
# @api public
|
426
|
+
#
|
427
|
+
def remote_base_url
|
428
|
+
@remote_base_url ||= URI.parse(remote_url.to_s[0..-remote_url.path.length])
|
429
|
+
end
|
430
|
+
|
431
|
+
# @!attribute [rw] remote_repository
|
432
|
+
#
|
433
|
+
# The git remote owner and repository name (e.g. 'org/repo')
|
434
|
+
#
|
435
|
+
# @example By default, this value is based on `remote_url`
|
436
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
437
|
+
# project = CreateGithubRelease::Project.new(options)
|
438
|
+
# project.remote_url = URI.parse('htps://github.com/org/repo')
|
439
|
+
# project.remote_repository #=> 'org/repo'
|
440
|
+
#
|
441
|
+
# @example It can also be set explicitly
|
442
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
443
|
+
# project = CreateGithubRelease::Project.new(options)
|
444
|
+
# project.remote_repository = 'org/repo'
|
445
|
+
#
|
446
|
+
# @return [String]
|
447
|
+
#
|
448
|
+
# @api public
|
449
|
+
#
|
450
|
+
def remote_repository
|
451
|
+
@remote_repository ||= remote_url.path.sub(%r{^/}, '').sub(/\.git$/, '')
|
452
|
+
end
|
453
|
+
|
454
|
+
# @!attribute [rw] remote_url
|
455
|
+
#
|
456
|
+
# The URL of the git remote repository (e.g. 'https://github.com/org/repo')
|
457
|
+
#
|
458
|
+
# @example By default, this value is based on `remote` and the `git remote get-url` command
|
459
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
460
|
+
# project = CreateGithubRelease::Project.new(options)
|
461
|
+
# project.remote #=> #<URI::HTTPS https://github.com/org/repo>
|
462
|
+
#
|
463
|
+
# @example It can also be set explicitly
|
464
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
465
|
+
# project = CreateGithubRelease::Project.new(options)
|
466
|
+
# project.remote_url = URI.parse('https://github.com/org/repo')
|
467
|
+
# project.remote_url #=> #<URI::HTTPS https://github.com/org/repo>
|
468
|
+
#
|
469
|
+
# @return [URI]
|
470
|
+
#
|
471
|
+
# @api public
|
472
|
+
#
|
473
|
+
def remote_url
|
474
|
+
@remote_url ||= begin
|
475
|
+
remote_url_string = `git remote get-url '#{remote}'`
|
476
|
+
raise "Could not determine remote url for remote '#{remote}'" unless $CHILD_STATUS.success?
|
477
|
+
|
478
|
+
remote_url_string = remote_url_string.chomp
|
479
|
+
remote_url_string = remote_url_string[0..-5] if remote_url_string.end_with?('.git')
|
480
|
+
URI.parse(remote_url_string)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
# @!attribute [rw] changelog_path
|
485
|
+
#
|
486
|
+
# The path relative to the project root where the changelog is located
|
487
|
+
#
|
488
|
+
# @example By default, this value is 'CHANGELOG.md'
|
489
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
490
|
+
# project = CreateGithubRelease::Project.new(options)
|
491
|
+
# project.changelog_path #=> 'CHANGELOG.md'
|
492
|
+
#
|
493
|
+
# @example It can also be set in the options
|
494
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major', changelog_path: 'docs/CHANGES.txt')
|
495
|
+
# project = CreateGithubRelease::Project.new(options)
|
496
|
+
# project.remote_repository = 'docs/CHANGES.txt'
|
497
|
+
#
|
498
|
+
# @example It can also be set explicitly
|
499
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
500
|
+
# project = CreateGithubRelease::Project.new(options)
|
501
|
+
# project.changelog_path = 'docs/CHANGES.txt'
|
502
|
+
# project.remote_repository = 'docs/CHANGES.txt'
|
503
|
+
#
|
504
|
+
# @return [String]
|
505
|
+
#
|
506
|
+
# @api public
|
507
|
+
#
|
508
|
+
def changelog_path
|
509
|
+
@changelog_path ||= options.changelog_path || 'CHANGELOG.md'
|
510
|
+
end
|
511
|
+
|
512
|
+
# @!attribute [rw] changes
|
513
|
+
#
|
514
|
+
# An array containing the changes since the last_release_tag
|
515
|
+
#
|
516
|
+
# Calls `git log HEAD <next_release_tag>` to list the changes.
|
517
|
+
#
|
518
|
+
# @example By default, uses `git log`
|
519
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
520
|
+
# project = CreateGithubRelease::Project.new(options)
|
521
|
+
# pp project.changes
|
522
|
+
# [
|
523
|
+
# #<CreateGithubRelease::Change:0x00000001084b92f0 @sha="24bdd02", @subject="Foo feature">,
|
524
|
+
# #<CreateGithubRelease::Change:0x00000001084b93e0 @sha="d75e1e9", @subject="Bar feature">
|
525
|
+
# ]
|
526
|
+
#
|
527
|
+
# @example It can also be set explicitly
|
528
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
529
|
+
# project = CreateGithubRelease::Project.new(options)
|
530
|
+
# project.changes = 'All the changes'
|
531
|
+
# project.changes #=> 'All the changes'
|
532
|
+
#
|
533
|
+
# @return [Array<CreateGithubRelease>]
|
534
|
+
#
|
535
|
+
# @api public
|
536
|
+
#
|
537
|
+
def changes
|
538
|
+
@changes ||= begin
|
539
|
+
command = "git log 'HEAD' '^#{last_release_tag}' --oneline --format='format:%h\t%s'"
|
540
|
+
git_log = `#{command}`
|
541
|
+
raise "Could not determine changes since #{last_release_tag}" unless $CHILD_STATUS.success?
|
542
|
+
|
543
|
+
git_log.split("\n").map { |l| ::CreateGithubRelease::Change.new(*l.split("\t")) }
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
# @!attribute [rw] next_release_description
|
548
|
+
#
|
549
|
+
# The formatted release description
|
550
|
+
#
|
551
|
+
# @example
|
552
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
553
|
+
# project = CreateGithubRelease::Project.new(options) do |p|
|
554
|
+
# p.remote_url = URI.parse('https://github.com/username/repo')
|
555
|
+
# p.last_release_tag = 'v0.1.0'
|
556
|
+
# p.next_release_tag = 'v1.0.0'
|
557
|
+
# p.next_release_date = Date.new(2022, 11, 7)
|
558
|
+
# p.changes = [
|
559
|
+
# CreateGithubRelease::Change.new('e718690', 'Release v1.0.0 (#3)'),
|
560
|
+
# CreateGithubRelease::Change.new('ab598f3', 'Fix Rubocop offenses (#2)')
|
561
|
+
# ]
|
562
|
+
# end
|
563
|
+
# puts project.next_release_description
|
564
|
+
# ## v1.0.0 (2022-11-07)
|
565
|
+
#
|
566
|
+
# [Full Changelog](https://github.com/username/repo/compare/v0.1.0...v1.0.0
|
567
|
+
#
|
568
|
+
# * e718690 Release v1.0.0 (#3)
|
569
|
+
# * ab598f3 Fix Rubocop offenses (#2)
|
570
|
+
#
|
571
|
+
# @return [String]
|
572
|
+
#
|
573
|
+
# @api public
|
574
|
+
#
|
575
|
+
def next_release_description
|
576
|
+
@next_release_description ||= <<~DESCRIPTION
|
577
|
+
## #{next_release_tag} (#{next_release_date.strftime('%Y-%m-%d')})
|
578
|
+
|
579
|
+
[Full Changelog](#{release_log_url})
|
580
|
+
|
581
|
+
Changes since #{last_release_tag}:
|
582
|
+
|
583
|
+
#{list_of_changes}
|
584
|
+
DESCRIPTION
|
585
|
+
end
|
586
|
+
|
587
|
+
# @!attribute [rw] last_release_changelog
|
588
|
+
#
|
589
|
+
# The existing changelog (of the last release) as a string
|
590
|
+
#
|
591
|
+
# @example
|
592
|
+
# changelog_path = 'TEST_CHANGELOG.md'
|
593
|
+
# File.write(changelog_path, <<~CHANGELOG)
|
594
|
+
# # Project Changelog
|
595
|
+
#
|
596
|
+
# ## v0.1.0 (2021-11-07)
|
597
|
+
#
|
598
|
+
# * e718690 Release v0.1.0 (#3)
|
599
|
+
# CHANGELOG
|
600
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
601
|
+
# project = CreateGithubRelease::Project.new(options) do |p|
|
602
|
+
# p.changelog_path = changelog_path
|
603
|
+
# end
|
604
|
+
# puts project.last_release_changelog
|
605
|
+
# # Project Changelog
|
606
|
+
#
|
607
|
+
# ## v0.1.0 (2021-11-07)
|
608
|
+
#
|
609
|
+
# * e718690 Release v0.1.0 (#3)
|
610
|
+
#
|
611
|
+
# @return [String]
|
612
|
+
#
|
613
|
+
# @api public
|
614
|
+
#
|
615
|
+
def last_release_changelog
|
616
|
+
@last_release_changelog ||= begin
|
617
|
+
File.read(changelog_path)
|
618
|
+
rescue Errno::ENOENT
|
619
|
+
''
|
620
|
+
rescue StandardError
|
621
|
+
raise 'Could not read the changelog file'
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
# @!attribute [rw] next_release_changelog
|
626
|
+
#
|
627
|
+
# The changelog of the next release as a string
|
628
|
+
#
|
629
|
+
# This is the result of inserting next_release_description into
|
630
|
+
# last_release_changelog.
|
631
|
+
#
|
632
|
+
# @example
|
633
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
634
|
+
# project = CreateGithubRelease::Project.new(options) do |p|
|
635
|
+
# p.last_release_changelog = <<~CHANGELOG
|
636
|
+
# # Project Changelog
|
637
|
+
#
|
638
|
+
# ## v0.1.0 (2021-11-07)
|
639
|
+
#
|
640
|
+
# * e718690 Release v0.1.0 (#3)
|
641
|
+
# CHANGELOG
|
642
|
+
# p.next_release_description = <<~next_release_description
|
643
|
+
# ## v1.0.0 (2022-11-07)
|
644
|
+
#
|
645
|
+
# [Full Changelog](http://github.com/org/repo/compare/v0.1.0...v1.0.0)
|
646
|
+
#
|
647
|
+
# * e718690 Release v1.0.0 (#3)
|
648
|
+
# * ab598f3 Add the FizzBuzz Feature (#2)
|
649
|
+
# next_release_description
|
650
|
+
# end
|
651
|
+
# puts project.next_release_changelog
|
652
|
+
#
|
653
|
+
# @return [String]
|
654
|
+
#
|
655
|
+
# @api public
|
656
|
+
#
|
657
|
+
def next_release_changelog
|
658
|
+
@next_release_changelog ||=
|
659
|
+
CreateGithubRelease::Changelog.new(last_release_changelog, next_release_description).to_s
|
660
|
+
end
|
661
|
+
|
662
|
+
# Show the project details as a string
|
663
|
+
#
|
664
|
+
# @example
|
665
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
666
|
+
# project = CreateGithubRelease::Project.new(options)
|
667
|
+
# puts projects.to_s
|
668
|
+
# default_branch: main
|
669
|
+
# next_release_tag: v1.0.0
|
670
|
+
# next_release_date: 2023-02-01
|
671
|
+
# next_release_version: 1.0.0
|
672
|
+
# last_release_tag: v0.1.0
|
673
|
+
# last_release_version: 0.1.0
|
674
|
+
# release_branch: release-v1.0.0
|
675
|
+
# release_log_url: https://github.com/org/repo/compare/v0.1.0..v1.0.0
|
676
|
+
# release_type: major
|
677
|
+
# release_url: https://github.com/org/repo/releases/tag/v1.0.0
|
678
|
+
# remote: origin
|
679
|
+
# remote_base_url: https://github.com/
|
680
|
+
# remote_repository: org/repo
|
681
|
+
# remote_url: https://github.com/org/repo
|
682
|
+
# changelog_path: CHANGELOG.md
|
683
|
+
# verbose?: false
|
684
|
+
# quiet?: false
|
685
|
+
#
|
686
|
+
# @return [String]
|
687
|
+
#
|
688
|
+
# @api public
|
689
|
+
#
|
690
|
+
def to_s
|
691
|
+
<<~OUTPUT
|
692
|
+
default_branch: #{default_branch}
|
693
|
+
next_release_tag: #{next_release_tag}
|
694
|
+
next_release_date: #{next_release_date}
|
695
|
+
next_release_version: #{next_release_version}
|
696
|
+
last_release_tag: #{last_release_tag}
|
697
|
+
last_release_version: #{last_release_version}
|
698
|
+
release_branch: #{release_branch}
|
699
|
+
release_log_url: #{release_log_url}
|
700
|
+
release_type: #{release_type}
|
701
|
+
release_url: #{release_url}
|
702
|
+
remote: #{remote}
|
703
|
+
remote_base_url: #{remote_base_url}
|
704
|
+
remote_repository: #{remote_repository}
|
705
|
+
remote_url: #{remote_url}
|
706
|
+
verbose?: #{verbose?}
|
707
|
+
quiet?: #{quiet?}
|
708
|
+
OUTPUT
|
709
|
+
end
|
710
|
+
|
711
|
+
# @!attribute [rw] verbose
|
712
|
+
#
|
713
|
+
# If `true` enables verbose output
|
714
|
+
#
|
715
|
+
# @example By default, this value is based on the `verbose` option
|
716
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major', verbose: true)
|
717
|
+
# project = CreateGithubRelease::Project.new(options)
|
718
|
+
# project.verbose? #=> true
|
719
|
+
#
|
720
|
+
# @example It can also be set explicitly
|
721
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
722
|
+
# project = CreateGithubRelease::Project.new(options)
|
723
|
+
# project.verbose = true
|
724
|
+
# project.verbose? #=> true
|
725
|
+
#
|
726
|
+
# @return [Boolean]
|
727
|
+
#
|
728
|
+
# @api public
|
729
|
+
#
|
730
|
+
def verbose
|
731
|
+
@verbose ||= options.verbose || false
|
732
|
+
end
|
733
|
+
|
734
|
+
alias verbose? verbose
|
735
|
+
|
736
|
+
# @!attribute [rw] quiet
|
737
|
+
#
|
738
|
+
# If `true` supresses all output
|
739
|
+
#
|
740
|
+
# @example By default, this value is based on the `quiet` option
|
741
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major', quiet: true)
|
742
|
+
# project = CreateGithubRelease::Project.new(options)
|
743
|
+
# project.quiet? #=> true
|
744
|
+
#
|
745
|
+
# @example It can also be set explicitly
|
746
|
+
# options = CreateGithubRelease::CommandLineOptions.new(release_type: 'major')
|
747
|
+
# project = CreateGithubRelease::Project.new(options)
|
748
|
+
# project.quiet = true
|
749
|
+
# project.quiet? #=> true
|
750
|
+
#
|
751
|
+
# @return [Boolean]
|
752
|
+
#
|
753
|
+
# @api public
|
754
|
+
#
|
755
|
+
def quiet
|
756
|
+
@quiet ||= options.quiet || false
|
757
|
+
end
|
758
|
+
|
759
|
+
alias quiet? quiet
|
760
|
+
|
761
|
+
private
|
762
|
+
|
763
|
+
# The list of changes in the release as a string
|
764
|
+
# @return [String] The list of changes in the release as a string
|
765
|
+
# @api private
|
766
|
+
def list_of_changes
|
767
|
+
return '* No changes' if changes.empty?
|
768
|
+
|
769
|
+
changes.map do |change|
|
770
|
+
"* #{change.sha} #{change.subject}"
|
771
|
+
end.join("\n")
|
772
|
+
end
|
773
|
+
|
774
|
+
# `true` if the `#verbose?` flag is `true`
|
775
|
+
# @return [Boolean]
|
776
|
+
# @api private
|
777
|
+
def backtick_debug?
|
778
|
+
verbose?
|
779
|
+
end
|
780
|
+
|
781
|
+
# Override the backtick operator for this class to call super and output
|
782
|
+
# debug information if `verbose?` is true
|
783
|
+
include CreateGithubRelease::BacktickDebug
|
784
|
+
end
|
785
|
+
|
786
|
+
# rubocop:enable Metrics/ClassLength
|
787
|
+
end
|