mongoid 8.1.3 → 8.1.11

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +43 -45
  3. data/lib/mongoid/association/accessors.rb +5 -1
  4. data/lib/mongoid/association/eager_loadable.rb +3 -0
  5. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +18 -3
  6. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  7. data/lib/mongoid/association/referenced/has_many/enumerable.rb +21 -4
  8. data/lib/mongoid/association/referenced/has_many/proxy.rb +11 -2
  9. data/lib/mongoid/atomic.rb +9 -7
  10. data/lib/mongoid/attributes/readonly.rb +8 -3
  11. data/lib/mongoid/contextual/mongo.rb +1 -1
  12. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +15 -1
  13. data/lib/mongoid/criteria/queryable/selectable.rb +1 -1
  14. data/lib/mongoid/document.rb +8 -1
  15. data/lib/mongoid/equality.rb +1 -0
  16. data/lib/mongoid/extensions/hash.rb +4 -4
  17. data/lib/mongoid/fields.rb +13 -8
  18. data/lib/mongoid/interceptable.rb +6 -7
  19. data/lib/mongoid/matcher.rb +15 -1
  20. data/lib/mongoid/serializable.rb +7 -7
  21. data/lib/mongoid/timestamps/created.rb +8 -1
  22. data/lib/mongoid/touchable.rb +1 -1
  23. data/lib/mongoid/traversable.rb +36 -1
  24. data/lib/mongoid/validatable/associated.rb +98 -17
  25. data/lib/mongoid/validatable/macros.rb +15 -0
  26. data/lib/mongoid/validatable/numericality.rb +19 -0
  27. data/lib/mongoid/validatable.rb +9 -0
  28. data/lib/mongoid/version.rb +5 -1
  29. data/spec/integration/app_spec.rb +24 -5
  30. data/spec/integration/associations/embeds_one_spec.rb +25 -5
  31. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +40 -0
  32. data/spec/mongoid/association/eager_spec.rb +24 -2
  33. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +35 -0
  34. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +4 -0
  35. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +4 -0
  36. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +28 -37
  37. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +42 -0
  38. data/spec/mongoid/attributes/readonly_spec.rb +19 -0
  39. data/spec/mongoid/attributes_spec.rb +16 -0
  40. data/spec/mongoid/contextual/mongo_spec.rb +78 -3
  41. data/spec/mongoid/criteria/queryable/selectable_spec.rb +29 -0
  42. data/spec/mongoid/document_spec.rb +27 -0
  43. data/spec/mongoid/equality_spec.rb +6 -0
  44. data/spec/mongoid/fields_spec.rb +3 -3
  45. data/spec/mongoid/interceptable_spec.rb +80 -0
  46. data/spec/mongoid/interceptable_spec_models.rb +47 -111
  47. data/spec/mongoid/serializable_spec.rb +16 -9
  48. data/spec/mongoid/timestamps/created_spec.rb +23 -0
  49. data/spec/mongoid/validatable/associated_spec.rb +27 -34
  50. data/spec/mongoid/validatable/numericality_spec.rb +16 -0
  51. data/spec/shared/lib/mrss/docker_runner.rb +1 -2
  52. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  53. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  54. data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
  55. data/spec/shared/lib/tasks/candidate.rake +64 -0
  56. data/spec/shared/share/Dockerfile.erb +15 -85
  57. data/spec/shared/shlib/distro.sh +10 -0
  58. data/spec/shared/shlib/server.sh +33 -26
  59. data/spec/shared/shlib/set_env.sh +9 -68
  60. data/spec/support/expectations.rb +20 -17
  61. data/spec/support/models/band.rb +1 -0
  62. data/spec/support/models/lat_lng.rb +6 -0
  63. data/spec/support/models/name.rb +10 -0
  64. metadata +15 -38
  65. checksums.yaml.gz.sig +0 -0
  66. data.tar.gz.sig +0 -0
  67. metadata.gz.sig +0 -0
@@ -43,4 +43,27 @@ describe Mongoid::Timestamps::Created do
43
43
  expect(quiz.created_at).to be_within(10).of(Time.now.utc)
44
44
  end
45
45
  end
46
+
47
+ context "when the document is destroyed" do
48
+ let(:book) do
49
+ Book.create!
50
+ end
51
+
52
+ before do
53
+ Cover.before_save do
54
+ destroy if title == "delete me"
55
+ end
56
+ end
57
+
58
+ after do
59
+ Cover.reset_callbacks(:save)
60
+ end
61
+
62
+ it "does not set the created_at timestamp" do
63
+ book.covers << Cover.new(title: "delete me")
64
+ expect {
65
+ book.save
66
+ }.not_to raise_error
67
+ end
68
+ end
46
69
  end
@@ -37,12 +37,18 @@ describe Mongoid::Validatable::AssociatedValidator do
37
37
  User.new(name: "test")
38
38
  end
39
39
 
40
- let(:description) do
40
+ let(:description1) do
41
+ Description.new
42
+ end
43
+
44
+ let(:description2) do
41
45
  Description.new
42
46
  end
43
47
 
44
48
  before do
45
- user.descriptions << description
49
+ user.descriptions << description1
50
+ user.descriptions << description2
51
+ user.valid?
46
52
  end
47
53
 
48
54
  it "only validates the parent once" do
@@ -50,12 +56,16 @@ describe Mongoid::Validatable::AssociatedValidator do
50
56
  end
51
57
 
52
58
  it "adds the errors from the relation" do
53
- user.valid?
54
59
  expect(user.errors[:descriptions]).to_not be_nil
55
60
  end
56
61
 
62
+ it 'reports all failed validations' do
63
+ errors = user.descriptions.flat_map { |d| d.errors[:details] }
64
+ expect(errors.length).to be == 2
65
+ end
66
+
57
67
  it "only validates the child once" do
58
- expect(description).to_not be_valid
68
+ expect(description1).to_not be_valid
59
69
  end
60
70
  end
61
71
 
@@ -75,7 +85,6 @@ describe Mongoid::Validatable::AssociatedValidator do
75
85
  end
76
86
 
77
87
  it "does not run validation on them" do
78
- expect(description).to receive(:valid?).never
79
88
  expect(user).to be_valid
80
89
  end
81
90
 
@@ -84,14 +93,14 @@ describe Mongoid::Validatable::AssociatedValidator do
84
93
  end
85
94
  end
86
95
 
87
- describe "#validate_each" do
96
+ describe "#validate" do
88
97
 
89
98
  let(:person) do
90
99
  Person.new
91
100
  end
92
101
 
93
102
  let(:validator) do
94
- described_class.new(attributes: person.attributes)
103
+ described_class.new(attributes: person.relations.keys)
95
104
  end
96
105
 
97
106
  context "when the association is a one to one" do
@@ -99,7 +108,7 @@ describe Mongoid::Validatable::AssociatedValidator do
99
108
  context "when the association is nil" do
100
109
 
101
110
  before do
102
- validator.validate_each(person, :name, nil)
111
+ validator.validate(person)
103
112
  end
104
113
 
105
114
  it "adds no errors" do
@@ -108,14 +117,9 @@ describe Mongoid::Validatable::AssociatedValidator do
108
117
  end
109
118
 
110
119
  context "when the association is valid" do
111
-
112
- let(:associated) do
113
- double(valid?: true, flagged_for_destroy?: false)
114
- end
115
-
116
120
  before do
117
- expect(associated).to receive(:validated?).and_return(false)
118
- validator.validate_each(person, :name, associated)
121
+ person.name = Name.new(first_name: 'A', last_name: 'B')
122
+ validator.validate(person)
119
123
  end
120
124
 
121
125
  it "adds no errors" do
@@ -125,13 +129,9 @@ describe Mongoid::Validatable::AssociatedValidator do
125
129
 
126
130
  context "when the association is invalid" do
127
131
 
128
- let(:associated) do
129
- double(valid?: false, flagged_for_destroy?: false)
130
- end
131
-
132
132
  before do
133
- expect(associated).to receive(:validated?).and_return(false)
134
- validator.validate_each(person, :name, associated)
133
+ person.name = Name.new(first_name: 'Jamis', last_name: 'Buck')
134
+ validator.validate(person)
135
135
  end
136
136
 
137
137
  it "adds errors to the parent document" do
@@ -149,7 +149,7 @@ describe Mongoid::Validatable::AssociatedValidator do
149
149
  context "when the association is empty" do
150
150
 
151
151
  before do
152
- validator.validate_each(person, :addresses, [])
152
+ validator.validate(person)
153
153
  end
154
154
 
155
155
  it "adds no errors" do
@@ -159,13 +159,9 @@ describe Mongoid::Validatable::AssociatedValidator do
159
159
 
160
160
  context "when the association has invalid documents" do
161
161
 
162
- let(:associated) do
163
- double(valid?: false, flagged_for_destroy?: false)
164
- end
165
-
166
162
  before do
167
- expect(associated).to receive(:validated?).and_return(false)
168
- validator.validate_each(person, :addresses, [ associated ])
163
+ person.addresses << Address.new(street: '123')
164
+ validator.validate(person)
169
165
  end
170
166
 
171
167
  it "adds errors to the parent document" do
@@ -175,13 +171,10 @@ describe Mongoid::Validatable::AssociatedValidator do
175
171
 
176
172
  context "when the association has all valid documents" do
177
173
 
178
- let(:associated) do
179
- double(valid?: true, flagged_for_destroy?: false)
180
- end
181
-
182
174
  before do
183
- expect(associated).to receive(:validated?).and_return(false)
184
- validator.validate_each(person, :addresses, [ associated ])
175
+ person.addresses << Address.new(street: '123 First St')
176
+ person.addresses << Address.new(street: '456 Second St')
177
+ validator.validate(person)
185
178
  end
186
179
 
187
180
  it "adds no errors" do
@@ -28,5 +28,21 @@ describe ActiveModel::Validations::NumericalityValidator do
28
28
  expect(model).to_not be_valid
29
29
  end
30
30
  end
31
+
32
+ context 'when the value is numeric' do
33
+ let(:model) { TestModel.new(amount: '15.0') }
34
+
35
+ it 'returns true' do
36
+ expect(model).to be_valid
37
+ end
38
+ end
39
+
40
+ context 'when the value is a BSON::Decimal128' do
41
+ let(:model) { TestModel.new(amount: BSON::Decimal128.new('15.0')) }
42
+
43
+ it 'returns true' do
44
+ expect(model).to be_valid
45
+ end
46
+ end
31
47
  end
32
48
  end
@@ -203,7 +203,6 @@ module Mrss
203
203
  'ubuntu2204' => 'ubuntu:jammy',
204
204
  'rhel62' => 'centos:6',
205
205
  'rhel70' => 'centos:7',
206
- # 'rhel80' => 'redhat/ubi8',
207
206
  'rhel80' => 'rockylinux:8',
208
207
  }.freeze
209
208
 
@@ -283,7 +282,7 @@ module Mrss
283
282
 
284
283
  def num_exposed_ports
285
284
  case @env['TOPOLOGY'] || 'standalone'
286
- when 'standalone'
285
+ when 'standalone', 'replica-set-single-node'
287
286
  1
288
287
  when 'replica-set'
289
288
  3
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require_relative 'product_data'
6
+
7
+ module Mrss
8
+ module Release
9
+ class Candidate
10
+ # Release note section titles, by pr type
11
+ SECTION_TITLE = {
12
+ bcbreak: "Breaking Changes",
13
+ feature: "New Features",
14
+ bug: "Bug Fixes",
15
+ }.freeze
16
+
17
+ # GitHub labels
18
+ BCBREAK = 'bcbreak'
19
+ FEATURE = 'feature'
20
+ BUG = 'bug'
21
+ PATCH = 'patch'
22
+
23
+ def self.instance
24
+ @instance ||= new
25
+
26
+ yield @instance if block_given?
27
+
28
+ @instance
29
+ end
30
+
31
+ def product
32
+ @product ||= ProductData.new
33
+ end
34
+
35
+ def bump_version
36
+ product.bump_version(release_type)
37
+ end
38
+
39
+ def bump_version!
40
+ product.bump_version!(release_type)
41
+ end
42
+
43
+ def branch_name
44
+ @branch_name ||= "rc-#{product.version}"
45
+ end
46
+
47
+ # return a string of commit names since the last release
48
+ def pending_changes
49
+ @changes ||= begin
50
+ range = product.tag_exists? ? "#{product.tag_name}.." : ""
51
+ `git log --pretty=format:"%s" #{range}`
52
+ end
53
+ end
54
+
55
+ # return a list of PR numbers since the last release
56
+ def pending_pr_numbers
57
+ @pending_pr_numbers ||= pending_changes.
58
+ lines.
59
+ map { |line| line.match(/\(#(\d+)\)$/).then { |m| m && m[1] } }.
60
+ compact.
61
+ sort.reverse
62
+ end
63
+
64
+ # return a JSON string of PR data
65
+ def pending_pr_dump
66
+ @pending_pr_dump ||= `gh pr list --state all --limit 256 --json number,title,labels,url,body --jq 'map(select([.number] | inside([#{pending_pr_numbers.join(',')}]))) | sort_by(.number)'`
67
+ end
68
+
69
+ # return a list of PR data since the last release
70
+ def pending_prs
71
+ @pending_prs ||= JSON.parse(pending_pr_dump)
72
+ end
73
+
74
+ # return a list of pending prs with additional attributes (summary,
75
+ # short title, jira issue number).
76
+ def decorated_prs
77
+ @decorated_prs ||= pending_prs.map do |pr|
78
+ jira_issue, pr_title = split_pr_title(pr)
79
+ summary = extract_summary(pr)
80
+ type = pr_type(pr)
81
+ type_code = pr_type_code(type)
82
+ patch_flag = pr_patch_flag?(pr)
83
+
84
+ pr.merge('jira' => jira_issue,
85
+ 'short-title' => pr_title,
86
+ 'summary' => summary,
87
+ 'type' => type,
88
+ 'type-code' => type_code,
89
+ 'patch' => patch_flag)
90
+ end
91
+ end
92
+
93
+ # return a hash of decorated prs grouped by :bcbreak, :feature, or :bug
94
+ def prs_by_type
95
+ @prs_by_type ||= decorated_prs.group_by { |pr| pr['type'] }
96
+ end
97
+
98
+ # returns 'major', 'minor', or 'patch', depending on the presence of
99
+ # (respectively) :bcbreak, :feature, or :bug labels.
100
+ #
101
+ # If the RELEASE environment variable is set, its value will be used
102
+ # directly, ignoring whatever PR labels might exist.
103
+ def release_type
104
+ @release_type ||= if ENV['RELEASE']
105
+ ENV['RELEASE']
106
+ elsif prs_by_type[:bcbreak]
107
+ 'major'
108
+ elsif prs_by_type[:feature] && prs_by_type[:feature].any? { |pr| !pr['patch'] }
109
+ 'minor'
110
+ else
111
+ 'patch'
112
+ end
113
+ end
114
+
115
+ # returns the generated release notes as a string
116
+ def release_notes
117
+ @release_notes ||= release_notes_intro +
118
+ %i[ bcbreak feature bug ].
119
+ flat_map { |type| release_notes_for_type(type) }.join("\n")
120
+ end
121
+
122
+ private
123
+
124
+ # returns an array of strings, each string representing a single line
125
+ # in the release notes for the PR's of the given type.
126
+ def release_notes_for_type(type)
127
+ return [] unless prs_by_type[type]
128
+
129
+ [].tap do |lines|
130
+ lines << "\# #{SECTION_TITLE[type]}"
131
+ lines << ''
132
+
133
+ prs = prs_by_type[type]
134
+ summarized, unsummarized = prs.partition { |pr| pr['summary'] }
135
+
136
+ summarized.each do |pr|
137
+ header = [ '### ' ]
138
+ header << "[#{pr['jira']}](#{jira_url(pr['jira'])}) " if pr['jira']
139
+ header << "#{pr['short-title']} ([PR](#{pr['url']}))"
140
+ lines << header.join
141
+ lines << ''
142
+ lines << pr['summary']
143
+ lines << ''
144
+ end
145
+
146
+ if summarized.any? && unsummarized.any?
147
+ lines << ''
148
+ lines << [ '### Other ', SECTION_TITLE[type] ].join
149
+ lines << ''
150
+ end
151
+
152
+ unsummarized.each do |pr|
153
+ line = [ '* ' ]
154
+ line << "[#{pr['jira']}](#{jira_url(pr['jira'])}) " if pr['jira']
155
+ line << "#{pr['short-title']} ([PR](#{pr['url']}))"
156
+
157
+ lines << line.join
158
+ end
159
+
160
+ lines << ''
161
+ end
162
+ end
163
+
164
+ # returns the URL of for the given jira issue
165
+ def jira_url(issue)
166
+ "https://jira.mongodb.org/browse/#{issue}"
167
+ end
168
+
169
+ # assumes a pr title in the format of "JIRA-1234 PR Title (#1234)",
170
+ # returns a tuple of [ jira-issue, title ], where jira-issue may be
171
+ # blank (if no jira issue is in the title).
172
+ def split_pr_title(pr)
173
+ title = pr['title'].gsub(/\(#\d+\)/, '').strip
174
+
175
+ if title =~ /^(\w+-\d+) (.*)$/
176
+ [ $1, $2 ]
177
+ else
178
+ [ nil, title ]
179
+ end
180
+ end
181
+
182
+ # extracts the summary section from the pr and returns it (or returns nil
183
+ # if no summary section is detected)
184
+ def extract_summary(pr)
185
+ summary = []
186
+ accumulating = false
187
+ level = nil
188
+
189
+ pr['body'].lines.each do |line|
190
+ # a header of any level titled "summary" will begin the summary
191
+ if !accumulating && line =~ /^(\#+)\s+summary\s+$/i
192
+ accumulating = true
193
+ level = $1.length
194
+
195
+ # a header of any level less than or equal to the summary header's
196
+ # level will end the summary
197
+ elsif accumulating && line =~ /^\#{1,#{level}}\s+/
198
+ break
199
+
200
+ # otherwise, the line is part of the summary
201
+ elsif accumulating
202
+ summary << line
203
+ end
204
+ end
205
+
206
+ summary.any? ? summary.join.strip : nil
207
+ end
208
+
209
+ # Returns a symbol (:bcbreak, :feature, or :bug) that identifies the
210
+ # type of this PR that would most strongly influence what type of release
211
+ # it requires.
212
+ def pr_type(pr)
213
+ if pr['labels'].any? { |l| l['name'] == BCBREAK }
214
+ :bcbreak
215
+ elsif pr['labels'].any? { |l| l['name'] == FEATURE }
216
+ :feature
217
+ elsif pr['labels'].any? { |l| l['name'] == BUG }
218
+ :bug
219
+ else
220
+ nil
221
+ end
222
+ end
223
+
224
+ # `true` if the `patch` label is applied to the PR. This is used to
225
+ # indicate that a "feature" PR should be treated as a patch, for
226
+ # determining the release type only.
227
+ def pr_patch_flag?(pr)
228
+ pr['labels'].any? { |l| l['name'] == PATCH }
229
+ end
230
+
231
+ def pr_type_code(type)
232
+ case type
233
+ when :bcbreak then 'x'
234
+ when :feature then 'f'
235
+ when :bug then 'b'
236
+ else '?'
237
+ end
238
+ end
239
+
240
+ def series
241
+ major, minor, = product.version_parts
242
+
243
+ case release_type
244
+ when 'minor' then
245
+ "#{major}.x"
246
+ when 'patch' then
247
+ "#{major}.#{minor}.x"
248
+ end
249
+ end
250
+
251
+ # Return a string containing the markdown-formatted intro block for
252
+ # the release notes of this candidate.
253
+ def release_notes_intro
254
+ release_description = case release_type
255
+ when 'major' then 'major release'
256
+ when 'minor' then "minor release in the #{series} series"
257
+ when 'patch' then "patch release in the #{series} series"
258
+ end
259
+
260
+ <<~INTRO
261
+ The MongoDB Ruby team is pleased to announce version #{product.version} of the `#{product.package}` gem - #{product.description}. This is a new #{release_description} of #{product.name}.
262
+
263
+ Install this release using [RubyGems](https://rubygems.org/) via the command line as follows:
264
+
265
+ ~~~
266
+ gem install -v #{product.version} #{product.package}
267
+ ~~~
268
+
269
+ Or simply add it to your `Gemfile`:
270
+
271
+ ~~~
272
+ gem '#{product.package}', '#{product.version}'
273
+ ~~~
274
+
275
+ Have any feedback? Click on through to MongoDB's JIRA and [open a new ticket](#{product.jira_project_url}) to let us know what's on your mind 🧠.
276
+
277
+ INTRO
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Mrss
6
+ module Release
7
+ class ProductData
8
+ FILE_PATH = 'product.yml'
9
+
10
+ def self.init!
11
+ if File.exist?(FILE_PATH)
12
+ raise "#{FILE_PATH} already exists; refusing to overwrite it"
13
+ end
14
+
15
+ initial_data = {
16
+ 'name' => 'Product Name',
17
+ 'description' => 'a very short description of the product',
18
+ 'package' => 'product_package',
19
+ 'jira' => 'https://url.to.jira/project',
20
+ 'version' => { 'number' => '1.0.0',
21
+ 'file' => 'path/to/version.rb' }
22
+ }
23
+
24
+ File.write(FILE_PATH, initial_data.to_yaml)
25
+ end
26
+
27
+ def initialize
28
+ @hash = YAML.load_file(FILE_PATH)
29
+ end
30
+
31
+ def save_product_file!
32
+ File.write(FILE_PATH, @hash.to_yaml)
33
+ end
34
+
35
+ def rewrite_version_file!
36
+ version_module = File.read(version_file)
37
+ new_module = version_module.
38
+ sub(/^(\s*)(VERSION\s*=\s*).*$/) { "#{$1}#{$2}#{quoted_version}" }
39
+ File.write(version_file, new_module)
40
+ end
41
+
42
+ def version
43
+ @hash['version']['number']
44
+ end
45
+
46
+ def quoted_version
47
+ if version.include?("'")
48
+ version.inspect
49
+ else
50
+ "'#{version}'"
51
+ end
52
+ end
53
+
54
+ def version=(number)
55
+ @hash['version']['number'] = number
56
+ end
57
+
58
+ # returns an array of [ major, minor, patch, suffix ].
59
+ #
60
+ # each element will be returned as a String.
61
+ def version_parts
62
+ version.split(/\./, 4)
63
+ end
64
+
65
+ # bump the version according to the given release type:
66
+ #
67
+ # 'major' -> increment major component, zero the others
68
+ # 'minor' -> increment minor component, zero the patch
69
+ # 'patch' -> increment the patch component
70
+ def bump_version(release)
71
+ major, minor, patch, suffix = version_parts
72
+
73
+ case release
74
+ when 'major' then
75
+ major = major.to_i + 1
76
+ minor = patch = 0
77
+ when 'minor'
78
+ minor = minor.to_i + 1
79
+ patch = 0
80
+ when 'patch'
81
+ patch = patch.to_i + 1
82
+ else
83
+ raise ArgumentError, "invalid release type: #{release.inspect}"
84
+ end
85
+
86
+ self.version = [ major, minor, patch ].join('.')
87
+ end
88
+
89
+ # Invokes `#bump_version`, and then saves the new version to the
90
+ # product.yml file and to the version.rb file.
91
+ def bump_version!(release)
92
+ bump_version(release)
93
+ save_product_file!
94
+ rewrite_version_file!
95
+ end
96
+
97
+ def version_file
98
+ @hash['version']['file']
99
+ end
100
+
101
+ def name
102
+ @hash['name']
103
+ end
104
+
105
+ # The description is intended to be used in places where it can be
106
+ # appended to the end of a sentence, e.g.
107
+ #
108
+ # "We just released #{product.name} - #{product.description}!"
109
+ #
110
+ # Markdown formatting is allowed (even expected).
111
+ def description
112
+ @hash['description']
113
+ end
114
+
115
+ def package
116
+ @hash['package']
117
+ end
118
+
119
+ def jira_project_url
120
+ @hash['jira']
121
+ end
122
+
123
+ def tag_name
124
+ "v#{version}"
125
+ end
126
+
127
+ def tag_exists?(tag = tag_name)
128
+ `git tag -l #{tag}`.strip == tag
129
+ end
130
+
131
+ def branch_exists?(branch)
132
+ `git branch -l #{branch}`.strip == branch
133
+ end
134
+
135
+ def base_branch
136
+ @base_branch ||= begin
137
+ major, minor, = version_parts
138
+ branch = "#{major}.#{minor}-stable"
139
+ branch_exists?(branch) ? branch : 'master'
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -19,7 +19,7 @@ module Mrss
19
19
  end
20
20
 
21
21
  def initialize(desired_version, arch)
22
- @desired_version, @arch = desired_version, arch
22
+ @desired_version, @arch = desired_version, arch.sub(/-arm$/, '')
23
23
  end
24
24
 
25
25
  attr_reader :desired_version, :arch