toys-release 0.1.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 +7 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.md +21 -0
- data/README.md +87 -0
- data/docs/guide.md +7 -0
- data/lib/toys/release/version.rb +11 -0
- data/lib/toys-release.rb +23 -0
- data/toys/.data/templates/gh-pages-404.html.erb +25 -0
- data/toys/.data/templates/gh-pages-empty.html.erb +11 -0
- data/toys/.data/templates/gh-pages-gitignore.erb +1 -0
- data/toys/.data/templates/gh-pages-index.html.erb +15 -0
- data/toys/.data/templates/release-hook-on-closed.yml.erb +34 -0
- data/toys/.data/templates/release-hook-on-open.yml.erb +30 -0
- data/toys/.data/templates/release-hook-on-push.yml.erb +32 -0
- data/toys/.data/templates/release-perform.yml.erb +46 -0
- data/toys/.data/templates/release-request.yml.erb +37 -0
- data/toys/.data/templates/release-retry.yml.erb +42 -0
- data/toys/.lib/toys/release/artifact_dir.rb +70 -0
- data/toys/.lib/toys/release/change_set.rb +259 -0
- data/toys/.lib/toys/release/changelog_file.rb +136 -0
- data/toys/.lib/toys/release/component.rb +388 -0
- data/toys/.lib/toys/release/environment_utils.rb +246 -0
- data/toys/.lib/toys/release/performer.rb +346 -0
- data/toys/.lib/toys/release/pull_request.rb +154 -0
- data/toys/.lib/toys/release/repo_settings.rb +855 -0
- data/toys/.lib/toys/release/repository.rb +661 -0
- data/toys/.lib/toys/release/request_logic.rb +217 -0
- data/toys/.lib/toys/release/request_spec.rb +188 -0
- data/toys/.lib/toys/release/semver.rb +112 -0
- data/toys/.lib/toys/release/steps.rb +580 -0
- data/toys/.lib/toys/release/version_rb_file.rb +91 -0
- data/toys/.toys.rb +5 -0
- data/toys/_onclosed.rb +113 -0
- data/toys/_onopen.rb +158 -0
- data/toys/_onpush.rb +57 -0
- data/toys/create-labels.rb +115 -0
- data/toys/gen-gh-pages.rb +146 -0
- data/toys/gen-settings.rb +46 -0
- data/toys/gen-workflows.rb +70 -0
- data/toys/perform.rb +152 -0
- data/toys/request.rb +162 -0
- data/toys/retry.rb +133 -0
- metadata +106 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Toys
|
|
4
|
+
module Release
|
|
5
|
+
##
|
|
6
|
+
# Miscellaneous logic related to release requests.
|
|
7
|
+
#
|
|
8
|
+
class RequestLogic
|
|
9
|
+
##
|
|
10
|
+
# Construct a RequestLogic
|
|
11
|
+
#
|
|
12
|
+
# @param repository [Toys::Release::Repository]
|
|
13
|
+
# @param request_spec [Toys::Release::RequestSpec]
|
|
14
|
+
# @param target_branch [String] Optional target branch. Defaults to the
|
|
15
|
+
# current branch.
|
|
16
|
+
#
|
|
17
|
+
def initialize(repository, request_spec, target_branch: nil)
|
|
18
|
+
@repository = repository
|
|
19
|
+
@settings = repository.settings
|
|
20
|
+
@utils = repository.utils
|
|
21
|
+
@request_spec = request_spec
|
|
22
|
+
@target_branch = target_branch || @repository.current_branch
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Perform component verification, including:
|
|
27
|
+
#
|
|
28
|
+
# * That there is at least one component to release in the request spec
|
|
29
|
+
# * That each component has not already released the specified version
|
|
30
|
+
#
|
|
31
|
+
def verify_component_status
|
|
32
|
+
if @request_spec.empty?
|
|
33
|
+
@utils.error("No components to release.")
|
|
34
|
+
end
|
|
35
|
+
@utils.accumulate_errors("One or more components was in an inconsistent state") do
|
|
36
|
+
@request_spec.resolved_components.each do |component_spec|
|
|
37
|
+
component = @repository.component_named(component_spec.component_name)
|
|
38
|
+
changelog_version = component.changelog_file.current_version
|
|
39
|
+
if changelog_version && changelog_version >= component_spec.version
|
|
40
|
+
@utils.error("Cannot add version #{component_spec.version} to #{component.name} changelog because the" \
|
|
41
|
+
" existing changelog already contains version #{changelog_version}.")
|
|
42
|
+
end
|
|
43
|
+
constant_version = component.version_rb_file.current_version
|
|
44
|
+
if constant_version >= component_spec.version
|
|
45
|
+
@utils.error("Cannot change #{component.name} version constant to #{component_spec.version} because the" \
|
|
46
|
+
" existing version constant is already at #{constant_version}.")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Attempt to verify that no other release pull request is already open
|
|
55
|
+
# for this release.
|
|
56
|
+
# This is currently somewhat conservative. If any multi-release pull
|
|
57
|
+
# request is already open for the target branch, it will be noted as
|
|
58
|
+
# conflicting, even if it doesn't actually overlap. We don't currently
|
|
59
|
+
# have logic to dig into the existing pull requests and determine which
|
|
60
|
+
# components they actually want to release.
|
|
61
|
+
#
|
|
62
|
+
def verify_pull_request_status
|
|
63
|
+
@utils.accumulate_errors("One or more existing release pull requests conflicts with this release") do
|
|
64
|
+
existing_prs = @repository.find_release_prs(branch: @target_branch)
|
|
65
|
+
if @request_spec.single_component?
|
|
66
|
+
component_name = @request_spec.resolved_components.first.component_name
|
|
67
|
+
release_branch_name = @repository.release_branch_name(@target_branch, component_name)
|
|
68
|
+
existing_prs.each do |pr|
|
|
69
|
+
if pr.head_ref == release_branch_name
|
|
70
|
+
@utils.error("A release pull request (##{pr.number}) is already open for #{component_name}")
|
|
71
|
+
elsif pr.head_ref =~ %r{release/multi/\d{14}-\d{6}/#{@target_branch}}
|
|
72
|
+
@utils.error("A release pull request (##{pr.number}) is already open for multiple components")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
existing_prs.each do |pr|
|
|
77
|
+
if pr.head_ref.end_with?("/#{@target_branch}")
|
|
78
|
+
@utils.error("A release pull request (##{pr.number}) is already open")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# @return [String] A release branch name for this release
|
|
88
|
+
#
|
|
89
|
+
def determine_release_branch
|
|
90
|
+
if @request_spec.single_component?
|
|
91
|
+
@repository.release_branch_name(@target_branch, @request_spec.resolved_components[0].component_name)
|
|
92
|
+
else
|
|
93
|
+
@repository.multi_release_branch_name(@target_branch)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
##
|
|
98
|
+
# @return [String] A commit title for this release
|
|
99
|
+
#
|
|
100
|
+
def build_commit_title
|
|
101
|
+
if @request_spec.single_component?
|
|
102
|
+
"release: Release #{format_component_info(@request_spec.resolved_components[0])}"
|
|
103
|
+
else
|
|
104
|
+
"release: Release #{@request_spec.resolved_components.size} items"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# @return [String] Commit details for this release
|
|
110
|
+
#
|
|
111
|
+
def build_commit_details
|
|
112
|
+
if @request_spec.single_component?
|
|
113
|
+
""
|
|
114
|
+
else
|
|
115
|
+
lines = @request_spec.resolved_components.map do |resolved_component|
|
|
116
|
+
"* #{format_component_info(resolved_component)}"
|
|
117
|
+
end
|
|
118
|
+
lines.join("\n")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
##
|
|
123
|
+
# @return [String] Pull reqeust body for this release
|
|
124
|
+
#
|
|
125
|
+
def build_pr_body
|
|
126
|
+
if @settings.enable_release_automation?
|
|
127
|
+
build_automation_pr_body
|
|
128
|
+
else
|
|
129
|
+
build_standalone_pr_body
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
##
|
|
134
|
+
# @return [Array<String>] The set of labels to apply to a pull request
|
|
135
|
+
#
|
|
136
|
+
def determine_pr_labels
|
|
137
|
+
return unless @settings.enable_release_automation?
|
|
138
|
+
[@settings.release_pending_label]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
##
|
|
142
|
+
# Go through and update changelog and version files for each component.
|
|
143
|
+
#
|
|
144
|
+
def change_files
|
|
145
|
+
@request_spec.resolved_components.each do |resolved_component|
|
|
146
|
+
component = @repository.component_named(resolved_component.component_name)
|
|
147
|
+
component.changelog_file.append(resolved_component.change_set, resolved_component.version)
|
|
148
|
+
component.version_rb_file.update_version(resolved_component.version)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def format_component_info(resolved_component, bold: false)
|
|
155
|
+
last_release = resolved_component.last_version ? "was #{resolved_component.last_version}" : "initial release"
|
|
156
|
+
decor = bold ? "**" : ""
|
|
157
|
+
"#{decor}#{resolved_component.component_name} #{resolved_component.version}#{decor} (#{last_release})"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def build_automation_pr_body
|
|
161
|
+
<<~STR
|
|
162
|
+
#{build_pr_body_header}
|
|
163
|
+
|
|
164
|
+
* To confirm this release, merge this pull request, ensuring the \
|
|
165
|
+
#{@settings.release_pending_label.inspect} label is set. The release \
|
|
166
|
+
script will trigger automatically on merge.
|
|
167
|
+
* To abort this release, close this pull request without merging.
|
|
168
|
+
|
|
169
|
+
#{build_pr_body_footer}
|
|
170
|
+
STR
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def build_standalone_pr_body
|
|
174
|
+
<<~STR
|
|
175
|
+
#{build_pr_body_header}
|
|
176
|
+
|
|
177
|
+
You can run the `release perform` script once these changes are merged.
|
|
178
|
+
|
|
179
|
+
#{build_pr_body_footer}
|
|
180
|
+
STR
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def build_pr_body_header
|
|
184
|
+
lines = [
|
|
185
|
+
"This pull request prepares new releases for the following components:",
|
|
186
|
+
"",
|
|
187
|
+
]
|
|
188
|
+
@request_spec.resolved_components.each do |resolved_component|
|
|
189
|
+
lines << " * #{format_component_info(resolved_component, bold: true)}"
|
|
190
|
+
end
|
|
191
|
+
lines << ""
|
|
192
|
+
lines <<
|
|
193
|
+
"For each releasable component, this pull request modifies the" \
|
|
194
|
+
" version and provides an initial changelog entry based on" \
|
|
195
|
+
" [conventional commit](https://conventionalcommits.org) messages." \
|
|
196
|
+
" You can edit these changes before merging, to release a different" \
|
|
197
|
+
" version or to alter the changelog text."
|
|
198
|
+
lines.join("\n")
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def build_pr_body_footer
|
|
202
|
+
lines = ["The generated changelog entries have been copied below:"]
|
|
203
|
+
@request_spec.resolved_components.each do |resolved_component|
|
|
204
|
+
lines << ""
|
|
205
|
+
lines << "----"
|
|
206
|
+
lines << ""
|
|
207
|
+
lines << "## #{resolved_component.component_name}"
|
|
208
|
+
lines << ""
|
|
209
|
+
resolved_component.change_set.change_groups.each do |group|
|
|
210
|
+
lines.concat(group.prefixed_changes.map { |line| " * #{line}" })
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
lines.join("\n")
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "semver"
|
|
4
|
+
|
|
5
|
+
module Toys
|
|
6
|
+
module Release
|
|
7
|
+
##
|
|
8
|
+
# Represents a release request specification.
|
|
9
|
+
#
|
|
10
|
+
class RequestSpec
|
|
11
|
+
##
|
|
12
|
+
# Details about the resolved request for a particular component
|
|
13
|
+
#
|
|
14
|
+
# @!attribute [r] component_name
|
|
15
|
+
# @return [String]
|
|
16
|
+
#
|
|
17
|
+
# @!attribute [r] change_set
|
|
18
|
+
# @return [Toys::Release::ChangeSet]
|
|
19
|
+
#
|
|
20
|
+
# @!attribute [r] last_version
|
|
21
|
+
# @return [::Gem::Version, nil]
|
|
22
|
+
#
|
|
23
|
+
# @!attribute [r] version
|
|
24
|
+
# @return [::Gem::Version]
|
|
25
|
+
#
|
|
26
|
+
ResolvedComponent = ::Struct.new :component_name, :change_set, :last_version, :version
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Create an empty request.
|
|
30
|
+
#
|
|
31
|
+
# @param environment_utils [Toys::Release::EnvironmentUtils]
|
|
32
|
+
#
|
|
33
|
+
def initialize(environment_utils)
|
|
34
|
+
@utils = environment_utils
|
|
35
|
+
@resolved_components = nil
|
|
36
|
+
@requested_components = {}
|
|
37
|
+
@release_sha = nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# @return [boolean] Whether the request has been resolved.
|
|
42
|
+
#
|
|
43
|
+
def resolved?
|
|
44
|
+
!@resolved_components.nil?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# @return [boolean] Whether the request is empty, i.e. has no changed
|
|
49
|
+
# components
|
|
50
|
+
#
|
|
51
|
+
def empty?
|
|
52
|
+
resolved_components.empty?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# @return [boolean] Whether the request is for a single component
|
|
57
|
+
#
|
|
58
|
+
def single_component?
|
|
59
|
+
resolved_components.size == 1
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# @return [Array<ResolvedComponent>] Info about the components to release.
|
|
64
|
+
# Valid only after resolution.
|
|
65
|
+
#
|
|
66
|
+
attr_reader :resolved_components
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# @return [String] The git SHA at which the release will be cut.
|
|
70
|
+
# Valid only after resolution.
|
|
71
|
+
#
|
|
72
|
+
attr_reader :release_sha
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Add a component and version constraint.
|
|
76
|
+
#
|
|
77
|
+
# @param component_name [String,:all] The name of the component to release
|
|
78
|
+
# @param version [::Gem::Version,Toys::Release::Semver,String,Symbol,nil]
|
|
79
|
+
# The version to release, or the kind of version bump to use. If `nil`
|
|
80
|
+
# (the default), infers a version bump from the changeset, and omits
|
|
81
|
+
# the component if no release is needed.
|
|
82
|
+
#
|
|
83
|
+
def add(component_name, version: nil)
|
|
84
|
+
raise "Release request already resolved" if resolved?
|
|
85
|
+
if !version.nil? && !version.is_a?(::Gem::Version) && !version.is_a?(Semver)
|
|
86
|
+
name = version.to_s
|
|
87
|
+
version = if name =~ /^\d/
|
|
88
|
+
::Gem::Version.new(name)
|
|
89
|
+
else
|
|
90
|
+
Semver.for_name(name)
|
|
91
|
+
end
|
|
92
|
+
@utils.error("Malformed version or semver name: #{name}") unless version
|
|
93
|
+
end
|
|
94
|
+
@utils.error("Cannot release with no version change") if version == Semver::NONE
|
|
95
|
+
if @requested_components[component_name] && @requested_components[component_name] != version
|
|
96
|
+
@utils.error("Requested release of #{component_name.inspect} twice with different versions")
|
|
97
|
+
else
|
|
98
|
+
@requested_components[component_name] = version
|
|
99
|
+
end
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
##
|
|
104
|
+
# Resolve which components and versions to release.
|
|
105
|
+
#
|
|
106
|
+
# @param repository [Toys::Release::Repository]
|
|
107
|
+
# @param release_ref [String,nil] Git ref at which the release should be
|
|
108
|
+
# cut. If nil, uses the current HEAD.
|
|
109
|
+
#
|
|
110
|
+
def resolve_versions(repository, release_ref: nil)
|
|
111
|
+
raise "Release request already resolved" if resolved?
|
|
112
|
+
@utils.accumulate_errors("Conflicts detected in the components and versions requested.") do
|
|
113
|
+
@release_sha = repository.current_sha(release_ref)
|
|
114
|
+
candidate_groups = determine_candidate_groups(repository)
|
|
115
|
+
@resolved_components = []
|
|
116
|
+
candidate_groups.each do |group, version|
|
|
117
|
+
resolved_group, version = resolve_one_group(group, version)
|
|
118
|
+
if version
|
|
119
|
+
resolved_group.each do |resolved_component|
|
|
120
|
+
resolved_component.version = version
|
|
121
|
+
resolved_component.change_set.force_release!
|
|
122
|
+
end
|
|
123
|
+
@resolved_components.concat(resolved_group)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
self
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# Determines candidate groups, groups that could be released based on the
|
|
134
|
+
# release request alone (but we haven't yet checked commits.) Thus, the
|
|
135
|
+
# actual released groups will be a subset of this.
|
|
136
|
+
#
|
|
137
|
+
def determine_candidate_groups(repository)
|
|
138
|
+
candidate_groups = {}
|
|
139
|
+
if @requested_components.empty?
|
|
140
|
+
repository.coordination_groups.each { |group| candidate_groups[group] = nil }
|
|
141
|
+
else
|
|
142
|
+
@requested_components.each do |component_name, version|
|
|
143
|
+
component = repository.component_named(component_name)
|
|
144
|
+
unless component
|
|
145
|
+
@utils.error("Unknown component name #{component_name.inspect}")
|
|
146
|
+
next
|
|
147
|
+
end
|
|
148
|
+
group = component.coordination_group
|
|
149
|
+
group.each do |elem|
|
|
150
|
+
elem_name = elem.name
|
|
151
|
+
elem_version = @requested_components[elem_name]
|
|
152
|
+
if elem != component && version && elem_version && elem_version != version
|
|
153
|
+
@utils.error("#{component_name} #{version} implies #{elem_name} #{version} but " \
|
|
154
|
+
"#{elem_name} #{elem_version} was already requested.")
|
|
155
|
+
end
|
|
156
|
+
candidate_groups[group] ||= version
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
candidate_groups
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
##
|
|
164
|
+
# Resolves one candidate group. Returns an array of resolved components
|
|
165
|
+
# along with the version to release for the group.
|
|
166
|
+
#
|
|
167
|
+
def resolve_one_group(group, version)
|
|
168
|
+
suggested_next_version = nil
|
|
169
|
+
resolved_group = group.map do |component|
|
|
170
|
+
last_version = component.latest_tag_version(ref: @release_sha)
|
|
171
|
+
if last_version && version.is_a?(::Gem::Version) && last_version >= version
|
|
172
|
+
@utils.error("Requested #{component.name} #{version} but #{last_version} is the latest.")
|
|
173
|
+
end
|
|
174
|
+
latest_tag = component.version_tag(last_version)
|
|
175
|
+
changeset = component.make_change_set(from: latest_tag, to: @release_sha)
|
|
176
|
+
unless version.is_a?(::Gem::Version)
|
|
177
|
+
cur_suggested = version ? version.bump(last_version) : changeset.suggested_version(last_version)
|
|
178
|
+
if suggested_next_version.nil? || (cur_suggested && cur_suggested > suggested_next_version)
|
|
179
|
+
suggested_next_version = cur_suggested
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
ResolvedComponent.new(component.name, changeset, last_version, nil)
|
|
183
|
+
end
|
|
184
|
+
[resolved_group, suggested_next_version || version]
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Toys
|
|
4
|
+
module Release
|
|
5
|
+
##
|
|
6
|
+
# A semver level
|
|
7
|
+
#
|
|
8
|
+
class Semver
|
|
9
|
+
class << self
|
|
10
|
+
##
|
|
11
|
+
# Return the semver level for the given name.
|
|
12
|
+
#
|
|
13
|
+
# @param name [String,Symbol] The name
|
|
14
|
+
# @return [Semver] The semver level
|
|
15
|
+
# @return [nil] If the name is not recognized
|
|
16
|
+
#
|
|
17
|
+
def for_name(name)
|
|
18
|
+
@name_mapping[name.to_s.downcase]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
include ::Comparable
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# @return [Symbol] The name of this semver level
|
|
26
|
+
#
|
|
27
|
+
attr_reader :name
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# @return [Integer,nil] Which version segment to bump (0 is major), or
|
|
31
|
+
# nil for the "none" level.
|
|
32
|
+
#
|
|
33
|
+
attr_reader :segment
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# @return [boolean] Whether this semver implies any change
|
|
37
|
+
#
|
|
38
|
+
def significant?
|
|
39
|
+
!segment.nil?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# @return [String] The name of this semver level as a string
|
|
44
|
+
#
|
|
45
|
+
def to_s
|
|
46
|
+
name.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Bump the given version.
|
|
51
|
+
#
|
|
52
|
+
# @param version [::Gem::Version] The original version
|
|
53
|
+
# @return [::Gem::Version] The new version
|
|
54
|
+
#
|
|
55
|
+
def bump(version)
|
|
56
|
+
return version if segment.nil?
|
|
57
|
+
bump_seg = segment
|
|
58
|
+
version_segs = version&.segments || [0, 0, 0]
|
|
59
|
+
max_seg = bump_seg
|
|
60
|
+
max_seg = 2 if max_seg < 2
|
|
61
|
+
version_segs = version_segs[0..max_seg]
|
|
62
|
+
bump_seg = 1 if bump_seg.zero? && version_segs[0].zero?
|
|
63
|
+
version_segs[bump_seg] += 1
|
|
64
|
+
::Gem::Version.new(version_segs.fill(0, bump_seg + 1).join("."))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @private
|
|
68
|
+
def initialize(name, segment)
|
|
69
|
+
@name = name
|
|
70
|
+
@segment = segment
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @private
|
|
74
|
+
def <=>(other)
|
|
75
|
+
(other.segment || 99) <=> (segment || 99)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# Major version bump
|
|
80
|
+
#
|
|
81
|
+
MAJOR = new(:major, 0)
|
|
82
|
+
|
|
83
|
+
##
|
|
84
|
+
# Minor version bump
|
|
85
|
+
#
|
|
86
|
+
MINOR = new(:minor, 1)
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Patch version bump
|
|
90
|
+
#
|
|
91
|
+
PATCH = new(:patch, 2)
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
# Patch2 version bump
|
|
95
|
+
#
|
|
96
|
+
PATCH2 = new(:patch2, 3)
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# No version bump
|
|
100
|
+
#
|
|
101
|
+
NONE = new(:none, nil)
|
|
102
|
+
|
|
103
|
+
@name_mapping = {
|
|
104
|
+
"major" => MAJOR,
|
|
105
|
+
"minor" => MINOR,
|
|
106
|
+
"patch" => PATCH,
|
|
107
|
+
"patch2" => PATCH2,
|
|
108
|
+
"none" => NONE,
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|