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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +36 -21
  5. data/create_github_release.gemspec +1 -0
  6. data/exe/create-github-release +12 -8
  7. data/lib/create_github_release/assertion_base.rb +25 -11
  8. data/lib/create_github_release/assertions/bundle_is_up_to_date.rb +4 -3
  9. data/lib/create_github_release/assertions/{docker_is_running.rb → gh_authenticated.rb} +9 -8
  10. data/lib/create_github_release/assertions/gh_command_exists.rb +3 -2
  11. data/lib/create_github_release/assertions/git_command_exists.rb +3 -2
  12. data/lib/create_github_release/assertions/in_git_repo.rb +3 -2
  13. data/lib/create_github_release/assertions/in_repo_root_directory.rb +3 -2
  14. data/lib/create_github_release/assertions/last_release_tag_exists.rb +45 -0
  15. data/lib/create_github_release/assertions/local_and_remote_on_same_commit.rb +4 -3
  16. data/lib/create_github_release/assertions/local_release_branch_does_not_exist.rb +6 -5
  17. data/lib/create_github_release/assertions/local_release_tag_does_not_exist.rb +3 -3
  18. data/lib/create_github_release/assertions/no_staged_changes.rb +3 -2
  19. data/lib/create_github_release/assertions/no_uncommitted_changes.rb +3 -2
  20. data/lib/create_github_release/assertions/on_default_branch.rb +5 -4
  21. data/lib/create_github_release/assertions/remote_release_branch_does_not_exist.rb +6 -5
  22. data/lib/create_github_release/assertions/remote_release_tag_does_not_exist.rb +6 -5
  23. data/lib/create_github_release/assertions.rb +2 -2
  24. data/lib/create_github_release/backtick_debug.rb +69 -0
  25. data/lib/create_github_release/change.rb +73 -0
  26. data/lib/create_github_release/changelog.rb +40 -68
  27. data/lib/create_github_release/command_line_options.rb +367 -0
  28. data/lib/create_github_release/command_line_parser.rb +113 -25
  29. data/lib/create_github_release/project.rb +787 -0
  30. data/lib/create_github_release/release_assertions.rb +3 -3
  31. data/lib/create_github_release/release_tasks.rb +1 -1
  32. data/lib/create_github_release/task_base.rb +25 -11
  33. data/lib/create_github_release/tasks/commit_release.rb +4 -3
  34. data/lib/create_github_release/tasks/create_github_release.rb +16 -35
  35. data/lib/create_github_release/tasks/create_release_branch.rb +6 -5
  36. data/lib/create_github_release/tasks/create_release_pull_request.rb +10 -42
  37. data/lib/create_github_release/tasks/create_release_tag.rb +6 -5
  38. data/lib/create_github_release/tasks/push_release.rb +5 -4
  39. data/lib/create_github_release/tasks/update_changelog.rb +16 -67
  40. data/lib/create_github_release/tasks/update_version.rb +4 -3
  41. data/lib/create_github_release/version.rb +1 -1
  42. data/lib/create_github_release.rb +4 -2
  43. metadata +23 -8
  44. data/.vscode/settings.json +0 -7
  45. data/lib/create_github_release/assertions/changelog_docker_container_exists.rb +0 -73
  46. data/lib/create_github_release/options.rb +0 -397
  47. 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