create_github_release 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.markdownlint.yml +25 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +64 -0
- data/Rakefile +85 -0
- data/create_github_release.gemspec +49 -0
- data/exe/create-github-release +22 -0
- data/lib/create_github_release/assertion_base.rb +62 -0
- data/lib/create_github_release/assertions/bundle_is_up_to_date.rb +73 -0
- data/lib/create_github_release/assertions/changelog_docker_container_exists.rb +73 -0
- data/lib/create_github_release/assertions/docker_is_running.rb +42 -0
- data/lib/create_github_release/assertions/gh_command_exists.rb +42 -0
- data/lib/create_github_release/assertions/git_command_exists.rb +42 -0
- data/lib/create_github_release/assertions/in_git_repo.rb +44 -0
- data/lib/create_github_release/assertions/in_repo_root_directory.rb +47 -0
- data/lib/create_github_release/assertions/local_and_remote_on_same_commit.rb +45 -0
- data/lib/create_github_release/assertions/local_release_branch_does_not_exist.rb +43 -0
- data/lib/create_github_release/assertions/local_release_tag_does_not_exist.rb +45 -0
- data/lib/create_github_release/assertions/no_staged_changes.rb +46 -0
- data/lib/create_github_release/assertions/no_uncommitted_changes.rb +46 -0
- data/lib/create_github_release/assertions/on_default_branch.rb +44 -0
- data/lib/create_github_release/assertions/remote_release_branch_does_not_exist.rb +43 -0
- data/lib/create_github_release/assertions/remote_release_tag_does_not_exist.rb +43 -0
- data/lib/create_github_release/assertions.rb +25 -0
- data/lib/create_github_release/changelog.rb +372 -0
- data/lib/create_github_release/command_line_parser.rb +137 -0
- data/lib/create_github_release/options.rb +397 -0
- data/lib/create_github_release/release.rb +82 -0
- data/lib/create_github_release/release_assertions.rb +86 -0
- data/lib/create_github_release/release_tasks.rb +78 -0
- data/lib/create_github_release/task_base.rb +62 -0
- data/lib/create_github_release/tasks/commit_release.rb +42 -0
- data/lib/create_github_release/tasks/create_github_release.rb +106 -0
- data/lib/create_github_release/tasks/create_release_branch.rb +42 -0
- data/lib/create_github_release/tasks/create_release_pull_request.rb +107 -0
- data/lib/create_github_release/tasks/create_release_tag.rb +42 -0
- data/lib/create_github_release/tasks/push_release.rb +42 -0
- data/lib/create_github_release/tasks/update_changelog.rb +126 -0
- data/lib/create_github_release/tasks/update_version.rb +46 -0
- data/lib/create_github_release/tasks.rb +18 -0
- data/lib/create_github_release/version.rb +6 -0
- data/lib/create_github_release.rb +21 -0
- metadata +235 -0
@@ -0,0 +1,397 @@
|
|
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
|
@@ -0,0 +1,82 @@
|
|
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
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'create_github_release/assertions'
|
4
|
+
|
5
|
+
module CreateGithubRelease
|
6
|
+
# Assertions that must be true for a new Github release to be created
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# require 'create_github_release'
|
10
|
+
#
|
11
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
12
|
+
# assertions = CreateGithubRelease::ReleaseAssertions.new(options)
|
13
|
+
# assertions.options # => #<CreateGithubRelease::Options:0x00007f9b0a0b0a00>
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
#
|
17
|
+
class ReleaseAssertions
|
18
|
+
# The options used in the assertions
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# require 'create_github_release'
|
22
|
+
#
|
23
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
24
|
+
# assertions = CreateGithubRelease::ReleaseAssertions.new(options)
|
25
|
+
# assertions.options # => #<CreateGithubRelease::Options:0x00007f9b0a0b0a00>
|
26
|
+
#
|
27
|
+
# @return [CreateGithubRelease::Options]
|
28
|
+
attr_reader :options
|
29
|
+
|
30
|
+
# Create a new instance of ReleaseAssertions
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# require 'create_github_release'
|
34
|
+
#
|
35
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
36
|
+
# assertions = CreateGithubRelease::ReleaseAssertions.new(options)
|
37
|
+
# assertions.make_assertions
|
38
|
+
#
|
39
|
+
def initialize(options)
|
40
|
+
@options = options
|
41
|
+
end
|
42
|
+
|
43
|
+
# The assertions that must be true for a new Github release to be created
|
44
|
+
#
|
45
|
+
# The assertions are run in the order they are defined in this array.
|
46
|
+
#
|
47
|
+
# @return [Array<Class>] The assertions that must be true for a new Github release to be created
|
48
|
+
#
|
49
|
+
ASSERTIONS = [
|
50
|
+
CreateGithubRelease::Assertions::GitCommandExists,
|
51
|
+
CreateGithubRelease::Assertions::BundleIsUpToDate,
|
52
|
+
CreateGithubRelease::Assertions::InGitRepo,
|
53
|
+
CreateGithubRelease::Assertions::InRepoRootDirectory,
|
54
|
+
CreateGithubRelease::Assertions::OnDefaultBranch,
|
55
|
+
CreateGithubRelease::Assertions::NoUncommittedChanges,
|
56
|
+
CreateGithubRelease::Assertions::NoStagedChanges,
|
57
|
+
CreateGithubRelease::Assertions::LocalAndRemoteOnSameCommit,
|
58
|
+
CreateGithubRelease::Assertions::LocalReleaseTagDoesNotExist,
|
59
|
+
CreateGithubRelease::Assertions::RemoteReleaseTagDoesNotExist,
|
60
|
+
CreateGithubRelease::Assertions::LocalReleaseBranchDoesNotExist,
|
61
|
+
CreateGithubRelease::Assertions::RemoteReleaseBranchDoesNotExist,
|
62
|
+
CreateGithubRelease::Assertions::DockerIsRunning,
|
63
|
+
CreateGithubRelease::Assertions::ChangelogDockerContainerExists,
|
64
|
+
CreateGithubRelease::Assertions::GhCommandExists
|
65
|
+
].freeze
|
66
|
+
|
67
|
+
# Run all assertions
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# require 'create_github_release'
|
71
|
+
#
|
72
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
73
|
+
# assertions = CreateGithubRelease::ReleaseAssertions.new(options)
|
74
|
+
# assertions.make_assertions
|
75
|
+
#
|
76
|
+
# @return [void]
|
77
|
+
#
|
78
|
+
# @raise [SystemExit] if any assertion fails
|
79
|
+
#
|
80
|
+
def make_assertions
|
81
|
+
ASSERTIONS.each do |assertion_class|
|
82
|
+
assertion_class.new(options).assert
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'create_github_release/tasks'
|
4
|
+
|
5
|
+
module CreateGithubRelease
|
6
|
+
# Tasks that must be run to create a new Github release.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# require 'create_github_release'
|
10
|
+
#
|
11
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
12
|
+
# tasks = CreateGithubRelease::ReleaseTasks.new(options)
|
13
|
+
# tasks.run
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
#
|
17
|
+
class ReleaseTasks
|
18
|
+
# The options used to create the Github release
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# require 'create_github_release'
|
22
|
+
#
|
23
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
24
|
+
# tasks = CreateGithubRelease::ReleaseTasks.new(options)
|
25
|
+
# tasks.options # => #<CreateGithubRelease::Options:0x00007f9b0a0b0a00>
|
26
|
+
#
|
27
|
+
# @return [CreateGithubRelease::Options]
|
28
|
+
attr_reader :options
|
29
|
+
|
30
|
+
# Create a new instance of ReleaseTasks
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# require 'create_github_release'
|
34
|
+
#
|
35
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
36
|
+
# tasks = CreateGithubRelease::ReleaseTasks.new(options)
|
37
|
+
# tasks.run
|
38
|
+
#
|
39
|
+
def initialize(options)
|
40
|
+
@options = options
|
41
|
+
end
|
42
|
+
# The tasks that will be run to create a new Github release
|
43
|
+
#
|
44
|
+
# The tasks are run in the order they are defined in this array.
|
45
|
+
#
|
46
|
+
# @return [Array<Class>] The tasks that will be run to create a new Github release
|
47
|
+
#
|
48
|
+
TASKS = [
|
49
|
+
CreateGithubRelease::Tasks::CreateReleaseTag,
|
50
|
+
CreateGithubRelease::Tasks::CreateReleaseBranch,
|
51
|
+
CreateGithubRelease::Tasks::UpdateVersion,
|
52
|
+
CreateGithubRelease::Tasks::UpdateChangelog,
|
53
|
+
CreateGithubRelease::Tasks::CommitRelease,
|
54
|
+
CreateGithubRelease::Tasks::PushRelease,
|
55
|
+
CreateGithubRelease::Tasks::CreateGithubRelease,
|
56
|
+
CreateGithubRelease::Tasks::CreateReleasePullRequest
|
57
|
+
].freeze
|
58
|
+
|
59
|
+
# Run all tasks to create a new Github release
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# require 'create_github_release'
|
63
|
+
#
|
64
|
+
# options = CreateGithubRelease::Options.new { |o| o.release_type = 'major' }
|
65
|
+
# tasks = CreateGithubRelease::ReleaseTasks.new(options)
|
66
|
+
# tasks.run
|
67
|
+
#
|
68
|
+
# @return [void]
|
69
|
+
#
|
70
|
+
# @raise [SystemExit] if any task fails
|
71
|
+
#
|
72
|
+
def run
|
73
|
+
TASKS.each do |task_class|
|
74
|
+
task_class.new(options).run
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CreateGithubRelease
|
4
|
+
# Base class for tasks
|
5
|
+
#
|
6
|
+
# All tasks must inherit from this class.
|
7
|
+
# It holds the options and knows how to print, puts and error while respecting the `quiet` flag.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
#
|
11
|
+
class TaskBase
|
12
|
+
# Create a new tasks object and save the given `options`
|
13
|
+
# @param options [CreateGithubRelease::Options] the options
|
14
|
+
# @api private
|
15
|
+
def initialize(options)
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
# This method must be overriden by a subclass
|
20
|
+
#
|
21
|
+
# The subclass is expected to call `error` if the task fails.
|
22
|
+
#
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def run
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!attribute [r] options
|
31
|
+
#
|
32
|
+
# The options passed to the task object
|
33
|
+
# @return [CreateGithubRelease::Options] the options
|
34
|
+
# @api private
|
35
|
+
attr_reader :options
|
36
|
+
|
37
|
+
# Calls `Kernel.print` if the `quiet` flag is not set in the `options`
|
38
|
+
# @param args [Array] the arguments to pass to `Kernel.print`
|
39
|
+
# @return [void]
|
40
|
+
# @api private
|
41
|
+
def print(*args)
|
42
|
+
super unless options.quiet
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calls `Kernel.puts` if the `quiet` flag is not set in the `options`
|
46
|
+
# @param args [Array] the arguments to pass to `Kernel.puts`
|
47
|
+
# @return [void]
|
48
|
+
# @api private
|
49
|
+
def puts(*args)
|
50
|
+
super unless options.quiet
|
51
|
+
end
|
52
|
+
|
53
|
+
# Writes a message to stderr and exits with exitcode 1
|
54
|
+
# @param message [String] the message to write to stderr
|
55
|
+
# @return [void]
|
56
|
+
# @api private
|
57
|
+
def error(message)
|
58
|
+
warn "ERROR: #{message}"
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|