create_github_release 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'English'
4
- require 'tmpdir'
5
- require 'create_github_release/assertion_base'
6
-
7
- module CreateGithubRelease
8
- module Assertions
9
- # Assert that the changelog docker container exists
10
- #
11
- # @api public
12
- #
13
- class ChangelogDockerContainerExists < AssertionBase
14
- # Make sure the changelog docker container exists
15
- #
16
- # @example
17
- # require 'create_github_release'
18
- #
19
- # options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
20
- # assertion = CreateGithubRelease::Assertions::ChangelogDockerContainerExists.new(options)
21
- # begin
22
- # assertion.assert
23
- # puts 'Assertion passed'
24
- # rescue SystemExit
25
- # puts 'Assertion failed'
26
- # end
27
- #
28
- # @return [void]
29
- #
30
- # @raise [SystemExit] if the assertion fails
31
- #
32
- def assert
33
- Dir.mktmpdir do |dir|
34
- @dir = dir
35
- @docker_file = "#{dir}/Dockerfile"
36
- File.write(@docker_file, DOCKERFILE)
37
- assert_changelog_docker_container_exists
38
- end
39
- end
40
-
41
- private
42
-
43
- # Create the changelog docker container
44
- # @return [void]
45
- # @raise [SystemExit] if docker build fails
46
- # @api private
47
- def assert_changelog_docker_container_exists
48
- print 'Checking that the changelog docker container exists (might take time to build)...'
49
- `docker build --file "#{@docker_file}" --tag changelog-rs . 1>/dev/null 2>#{@dir}/stderr`
50
- if $CHILD_STATUS.success?
51
- puts 'OK'
52
- else
53
- error 'Failed to build the changelog-rs docker container'
54
- end
55
- end
56
-
57
- DOCKERFILE = <<~CONTENTS
58
- FROM rust
59
-
60
- # Build the docker image (from this project's root directory):
61
- # docker build --file Dockerfile.changelog-rs --tag changelog-rs .
62
- #
63
- # Use this image to output a changelog (from this project's root directory):
64
- # docker run --rm --volume "$PWD:/worktree" changelog-rs v1.9.1 v1.10.0
65
-
66
- RUN cargo install changelog-rs
67
- WORKDIR /worktree
68
-
69
- ENTRYPOINT ["/usr/local/cargo/bin/changelog-rs", "/worktree"]
70
- CONTENTS
71
- end
72
- end
73
- end
@@ -1,397 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bump'
4
- require 'uri'
5
-
6
- module CreateGithubRelease
7
- # Captures the options for this script
8
- #
9
- # @api public
10
- #
11
- class Options
12
- # Initialize a new instance of Options
13
- #
14
- # @example Without a block
15
- # options = Options.new
16
- # options.release_type = 'minor'
17
- #
18
- # @example With a block
19
- # options = Options.new do |o|
20
- # o.release_type = 'minor'
21
- # end
22
- #
23
- # @yield [self] an initialization block
24
- # @yieldparam self [Options] the instance being initialized
25
- # @yieldreturn [void] the return value is ignored
26
- #
27
- def initialize
28
- yield self if block_given?
29
- end
30
-
31
- # @!attribute [rw] branch
32
- #
33
- # The release branch based on the `next_tag`
34
- #
35
- # @example By default, `branch` is based on the tag
36
- # options = Options.new
37
- # options.tag = 'v1.2.3'
38
- # options.branch # => 'release-v1.2.3'
39
- #
40
- # @example `branch` can be set explicitly
41
- # options = Options.new
42
- # options.branch = 'prod-v9.9.9'
43
- # options.branch # => 'prod-v9.9.9'
44
- #
45
- # @return [String] the release branch
46
- #
47
- def branch
48
- @branch ||= "release-#{tag}"
49
- end
50
-
51
- # @!attribute [rw] current_tag
52
- #
53
- # The git tag of the previous release
54
- #
55
- # @example By default, `current_tag` is based on `current_version`
56
- # options = Options.new
57
- # options.current_version = '0.1.0'
58
- # options.current_tag # => 'v0.1.0'
59
- #
60
- # @example `current_tag` can be set explicitly
61
- # options = Options.new
62
- # options.current_tag = 'v9.9.9'
63
- # options.current_tag # => 'v9.9.9'
64
- #
65
- # @return [String] the current tag
66
- #
67
- def current_tag
68
- @current_tag ||= "v#{current_version}"
69
- end
70
-
71
- # @!attribute [rw] current_version
72
- #
73
- # The current version of the project's gem as determined by `Bump::Bump.current`
74
- #
75
- # @example By default, `current_version` is based on `Bump::Bump.current`
76
- # options = Options.new
77
- # options.current_version # => 'v0.1.0'
78
- #
79
- # @example `current_version` can be set explicitly
80
- # options = Options.new
81
- # options.current_version = '9.9.9'
82
- # options.current_version # => '9.9.9'
83
- #
84
- # @return [String] the current version
85
- #
86
- def current_version
87
- @current_version ||= Bump::Bump.current
88
- end
89
-
90
- def current_version=(current_version)
91
- self.current_tag = self.next_version = nil
92
- @current_version = current_version
93
- end
94
-
95
- # @!attribute [rw] default_branch
96
- #
97
- # The default branch of the remote repository
98
- #
99
- # This is the default branch as reported by the `HEAD branch` returned by
100
- # `git remote show #{remote}`.
101
- #
102
- # Uses the value of `remote` to determine the remote repository to query.
103
- #
104
- # @example By default, `default_branch` is based on `git remote show`
105
- # options = Options.new
106
- # options.default_branch # => 'main'
107
- #
108
- # @example `default_branch` can be set explicitly
109
- # options = Options.new
110
- # options.default_branch = 'master'
111
- # options.current_version # => 'master'
112
- #
113
- # @return [String] the default branch of the remote repository
114
- #
115
- def default_branch
116
- @default_branch ||= `git remote show '#{remote}'`.match(/HEAD branch: (.*?)$/)[1]
117
- end
118
-
119
- # @!attribute [rw] next_tag
120
- #
121
- # The tag to use for the next release based on `next_version`
122
- #
123
- # @example By default, `next_tag` is based on `next_version`
124
- # options = Options.new
125
- # options.next_version = '0.2.0'
126
- # options.next_tag # => 'v0.2.0 '
127
- #
128
- # @example `next_tag` can be set explicitly
129
- # options = Options.new
130
- # options.next_tag = 'v9.9.9'
131
- # options.next_tag # => 'v9.9.9'
132
- #
133
- # @return [String] the tag to use for the next release
134
- #
135
- def next_tag
136
- @next_tag ||= "v#{next_version}"
137
- end
138
-
139
- def next_tag=(next_tag)
140
- self.branch = self.release_url = nil
141
- @next_tag = next_tag
142
- end
143
-
144
- alias tag next_tag
145
- alias tag= next_tag=
146
-
147
- # @!attribute [rw] next_version
148
- #
149
- # The version of the next release
150
- #
151
- # Both `release_type` and `current_version` are used to determine the next
152
- # version using `Bump::Bump.next_version(release_type, current_version)`.
153
- #
154
- # @example `next_version` is determined by `release_type` and `current_version`
155
- # options = Options.new
156
- # options.release_type = 'major'
157
- # options.current_version = '0.1.0'
158
- # options.next_version # => '2.0.0'
159
- #
160
- # @example `next_version` can be set explicitly
161
- # options = Options.new
162
- # options.next_version = '9.9.9'
163
- # options.next_version # => '9.9.9'
164
- #
165
- # @return [String] the version of the next release
166
- #
167
- def next_version
168
- @next_version ||= Bump::Bump.next_version(release_type, current_version)
169
- end
170
-
171
- def next_version=(next_version)
172
- self.tag = nil
173
- @next_version = next_version
174
- end
175
-
176
- # Valid values for the `release_type` attribute
177
- #
178
- VALID_RELEASE_TYPES = %w[major minor patch].freeze
179
-
180
- # @!attribute [rw] release_type
181
- #
182
- # The type of release to make: major, minor, or patch
183
- #
184
- # This is passed to `Bump` when determining the next version. `release_type`
185
- # must be set before it is read.
186
- #
187
- # Reading this attribute when it has not been explicitly set will raise a RuntimeError.
188
- # Writing anything by major, minor, or path will raise an ArgumentError.
189
- #
190
- # @example `release_type` can be set explicitly
191
- # options = Options.new
192
- # options.release_type = 'major'
193
- # options.release_type # => 'major'
194
- #
195
- # @return [String] the release type
196
- #
197
- def release_type
198
- raise 'release_type not set. Must be major, minor, or patch' if @release_type.nil?
199
-
200
- @release_type
201
- end
202
-
203
- def release_type=(release_type)
204
- unless VALID_RELEASE_TYPES.include?(release_type)
205
- raise(ArgumentError, "Invalid release_type #{release_type}: must be major, minor, or patch")
206
- end
207
-
208
- self.next_version = nil
209
- @release_type = release_type
210
- end
211
-
212
- # @!attribute [rw] quiet
213
- #
214
- # Run the script without no or minimal output
215
- #
216
- # @example By default, `quiet` is false
217
- # options = Options.new
218
- # options.quiet # => false
219
- #
220
- # @example `quiet` can be set explicitly
221
- # options = Options.new
222
- # options.quiet = true
223
- # options.quiet # => true
224
- #
225
- # @return [Boolean] the quiet flag
226
- #
227
- def quiet
228
- @quiet = false unless instance_variable_defined?(:@quiet)
229
- @quiet
230
- end
231
-
232
- # @!attribute [rw] release_url
233
- #
234
- # The address of the GitHub release notes for the next release
235
- #
236
- # `remote_url` and `next_tag` are used to determine the release URL.
237
- #
238
- # @example Given the remote_url is https://github.com/user/repo.git and `next_tag` is v0.2.0
239
- # options = Options.new
240
- # options.release_url # => https://github.com/user/repo/releases/tag/v1.0.0
241
- #
242
- # @example `release_url` can be set explicitly
243
- # options = Options.new
244
- # options.release_url = "https://github.com/user/repo/releases/tag/v9.9.9"
245
- # options.release_url # => "https://github.com/user/repo/releases/tag/v9.9.9"
246
- #
247
- # @return [String] the address of the GitHub release notes for the next release
248
- #
249
- def release_url
250
- @release_url ||= begin
251
- url = remote_url.to_s.sub(/\.git$/, '')
252
- "#{url}/releases/tag/#{next_tag}"
253
- end
254
- end
255
-
256
- # @!attribute [rw] remote
257
- #
258
- # The Git remote to use to get the remote repository URL
259
- #
260
- # The default is 'origin'.
261
- #
262
- # @example The default `remote`` is origin
263
- # options = Options.new
264
- # options.remote # => "origin"
265
- #
266
- # @example `remote` can be set explicitly
267
- # options = Options.new
268
- # options.remote = 'upstream'
269
- # options.remote # => "upstream"
270
- #
271
- # @return [String] the address of the GitHub release notes for the next release
272
- #
273
- def remote
274
- @remote ||= 'origin'
275
- end
276
-
277
- def remote=(remote)
278
- self.default_branch = self.remote_url = nil
279
- @remote = remote
280
- end
281
-
282
- # @!attribute [rw] remote_url
283
- #
284
- # The URL of the remote repository
285
- #
286
- # Uses the value of `remote` to determine the remote repository URL. This is
287
- # the URL reported by `git remote get-url #{remote}`.
288
- #
289
- # @example
290
- # options = Options.new
291
- # options.remote_url # => "https://github.com/user/repo.git"
292
- #
293
- # @example Using a different `remote`
294
- # options = Options.new
295
- # options.remote = 'upstream'
296
- # options.remote_url # => "https://github.com/another_user/upstream_repo.git"
297
- #
298
- # @return [URI] the URL of the remote repository
299
- #
300
- def remote_url
301
- @remote_url ||= URI.parse(`git remote get-url '#{remote}'`.chomp)
302
- end
303
-
304
- def remote_url=(remote_url)
305
- self.remote_base_url = self.remote_repository = self.release_url = nil
306
-
307
- @remote_url = remote_url.nil? || remote_url.is_a?(URI) ? remote_url : URI.parse(remote_url)
308
- end
309
-
310
- # @!attribute [rw] remote_base_url
311
- #
312
- # The base URL of the remote repository
313
- #
314
- # This is the part of the `remote_url` excluding the path.
315
- #
316
- # @example Given the `remote_url` is https://github.com/user/repo.git
317
- # options = Options.new
318
- # options.remote_base_url # => "https://github.com/"
319
- #
320
- # @example `remote_base_url` can be set explicitly
321
- # options = Options.new
322
- # options.remote_base_url = 'https://gitlab.com/'
323
- # options.remote_base_url # => "https://gitlab.com/"
324
- #
325
- # @return [String] the bsae URL of the remote repository
326
- #
327
- def remote_base_url
328
- @remote_base_url ||= remote_url.to_s[0..-remote_url.path.length]
329
- end
330
-
331
- # @!attribute [rw] remote_repository
332
- #
333
- # The user and repository name of the remote repository
334
- #
335
- # This is the extracted from the `remote_url`.
336
- #
337
- # @example Given the `remote_url` is https://github.com/user/repo.git
338
- # options = Options.new
339
- # options.remote_repository # => "user/repo"
340
- #
341
- # @example `remote_repository` can be set explicitly
342
- # options = Options.new
343
- # options.remote_repository = 'foo/bar'
344
- # options.remote_repository # => "foo/bar"
345
- #
346
- # @return [String] the bsae URL of the remote repository
347
- #
348
- def remote_repository
349
- @remote_repository ||= remote_url.path.sub(%r{^/}, '').sub(/\.git$/, '')
350
- end
351
-
352
- attr_writer :branch, :current_tag, :default_branch, :quiet, :remote_base_url, :remote_repository, :release_url
353
-
354
- # Returns a string representation of the options
355
- #
356
- # @example
357
- # # Given that:
358
- # # * the remote is 'origin'
359
- # # * the url of origin is 'http://githib.com/main-branch/create_github_release.git'
360
- # # * the default branch is 'main'
361
- # # * the current version is '0.1.0'
362
- # options = Options.new { |o| o.release_type = 'major' }
363
- # puts options.to_s
364
- # branch='release-v1.0.0'
365
- # current_tag='v0.1.0'
366
- # current_version='0.1.0'
367
- # default_branch='main'
368
- # next_tag='v1.0.0'
369
- # next_version='1.0.0'
370
- # quiet=false
371
- # release_type='major'
372
- # remote='origin'
373
- # remote_url='https://github.com/main-branch/create_github_release.git'
374
- # remote_base_url='https://github.com/'
375
- # remote_repository='main-branch/create_github_release'
376
- #
377
- # @return [String] a string representation of the options
378
- #
379
- def to_s
380
- <<~OUTPUT
381
- branch='#{branch}'
382
- current_tag='#{current_tag}'
383
- current_version='#{current_version}'
384
- default_branch='#{default_branch}'
385
- next_tag='#{next_tag}'
386
- next_version='#{next_version}'
387
- quiet=#{quiet}
388
- release_type='#{release_type}'
389
- remote='#{remote}'
390
- remote_url='#{remote_url}'
391
- remote_base_url='#{remote_base_url}'
392
- remote_repository='#{remote_repository}'
393
- tag='#{tag}'
394
- OUTPUT
395
- end
396
- end
397
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CreateGithubRelease
4
- # The release information needed to generate a changelog
5
- #
6
- # @api public
7
- #
8
- class Release
9
- # Create a new release object
10
- #
11
- # @example
12
- # tag = 'v0.1.1'
13
- # date = Date.new(2022, 11, 7)
14
- # description = <<~DESCRIPTION
15
- # * e718690 Release v0.1.1 (#3)
16
- # * a92453c Bug fix (#2)
17
- # DESCRIPTION
18
- #
19
- # release = CreateGithubRelease::Release.new(tag, date, description)
20
- # release.tag # => 'v0.1.1'
21
- # release.date # => #<Date: 2022-11-07 ((2459773j,0s,0n),+0s,2299161j)>
22
- # release.description # => "* e718690 Release v0.1.1 (#3)\n* a92453c Bug fix (#2)\n"
23
- #
24
- # @param tag [String] The tag of the release
25
- # @param date [Date] The date of the release
26
- # @param description [String] The description of the release (usually a bullet list of changes)
27
- #
28
- def initialize(tag, date, description)
29
- @tag = tag
30
- @date = date
31
- @description = description
32
- end
33
-
34
- # The Git release tag
35
- #
36
- # @example
37
- # tag = 'v0.1.1'
38
- # date = Date.new(2022, 11, 7)
39
- # description = <<~DESCRIPTION
40
- # * e718690 Release v0.1.1 (#3)
41
- # * a92453c Bug fix (#2)
42
- # DESCRIPTION
43
- #
44
- # release = CreateGithubRelease::Release.new(tag, date, description)
45
- # release.tag # => 'v0.1.1'
46
- #
47
- # @return [String] The Git release tag
48
- attr_reader :tag
49
-
50
- # The date the release tag was created
51
- #
52
- # @example
53
- # tag = 'v0.1.1'
54
- # date = Date.new(2022, 11, 7)
55
- # description = <<~DESCRIPTION
56
- # * e718690 Release v0.1.1 (#3)
57
- # * a92453c Bug fix (#2)
58
- # DESCRIPTION
59
- #
60
- # release = CreateGithubRelease::Release.new(tag, date, description)
61
- # release.date # => #<Date: 2022-11-07 ((2459773j,0s,0n),+0s,2299161j)>
62
- #
63
- # @return [Date] The date the release tag was created
64
- attr_reader :date
65
-
66
- # The description of the release
67
- #
68
- # @example
69
- # tag = 'v0.1.1'
70
- # date = Date.new(2022, 11, 7)
71
- # description = <<~DESCRIPTION
72
- # * e718690 Release v0.1.1 (#3)
73
- # * a92453c Bug fix (#2)
74
- # DESCRIPTION
75
- #
76
- # release = CreateGithubRelease::Release.new(tag, date, description)
77
- # release.description # => "* e718690 Release v0.1.1 (#3)\n* a92453c Bug fix (#2)\n"
78
- #
79
- # @return [String] The description of the release
80
- attr_reader :description
81
- end
82
- end