bson 5.0.2 → 5.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 +4 -4
- data/Rakefile +2 -0
- data/ext/bson/extconf.rb +1 -1
- data/lib/bson/binary.rb +126 -4
- data/lib/bson/document.rb +8 -0
- data/lib/bson/object_id.rb +1 -1
- data/lib/bson/regexp.rb +3 -3
- data/lib/bson/vector.rb +44 -0
- data/lib/bson/version.rb +5 -16
- data/lib/bson.rb +1 -0
- data/spec/bson/document_as_spec.rb +14 -0
- data/spec/bson/vector_spec.rb +33 -0
- data/spec/runners/binary_vector.rb +78 -0
- data/spec/shared/LICENSE +20 -0
- data/spec/shared/bin/get-mongodb-download-url +17 -0
- data/spec/shared/bin/s3-copy +45 -0
- data/spec/shared/bin/s3-upload +69 -0
- data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
- data/spec/shared/lib/mrss/cluster_config.rb +231 -0
- data/spec/shared/lib/mrss/constraints.rb +378 -0
- data/spec/shared/lib/mrss/docker_runner.rb +298 -0
- data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
- data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
- data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
- data/spec/shared/lib/mrss/release/candidate.rb +284 -0
- data/spec/shared/lib/mrss/release/product_data.rb +144 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
- data/spec/shared/lib/mrss/session_registry.rb +69 -0
- data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
- data/spec/shared/lib/mrss/utils.rb +37 -0
- data/spec/shared/lib/tasks/candidate.rake +64 -0
- data/spec/shared/share/Dockerfile.erb +251 -0
- data/spec/shared/share/haproxy-1.conf +16 -0
- data/spec/shared/share/haproxy-2.conf +17 -0
- data/spec/shared/shlib/config.sh +27 -0
- data/spec/shared/shlib/distro.sh +84 -0
- data/spec/shared/shlib/server.sh +423 -0
- data/spec/shared/shlib/set_env.sh +110 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_tests/binary_vector_spec.rb +82 -0
- data/spec/spec_tests/data/binary_vector/README.md +61 -0
- data/spec/spec_tests/data/binary_vector/float32.json +65 -0
- data/spec/spec_tests/data/binary_vector/int8.json +57 -0
- data/spec/spec_tests/data/binary_vector/packed_bit.json +83 -0
- data/spec/spec_tests/data/corpus/binary.json +30 -0
- metadata +70 -6
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
autoload :JSON, 'json'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module Mrss
|
8
|
+
class ServerVersionRegistry
|
9
|
+
class Error < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class UnknownVersion < Error
|
13
|
+
end
|
14
|
+
|
15
|
+
class MissingDownloadUrl < Error
|
16
|
+
end
|
17
|
+
|
18
|
+
class BrokenDownloadUrl < Error
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(desired_version, arch)
|
22
|
+
@desired_version, @arch = desired_version, arch.sub(/-arm$/, '')
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :desired_version, :arch
|
26
|
+
|
27
|
+
def target_arch
|
28
|
+
# can't use RbConfig::CONFIG["arch"] because JRuby doesn't
|
29
|
+
# return anything meaningful there.
|
30
|
+
#
|
31
|
+
# also, need to use `uname -a` instead of (e.g.) `uname -p`
|
32
|
+
# because debian (at least) does not return anything meaningful
|
33
|
+
# for `uname -p`.
|
34
|
+
uname = `uname -a`.strip
|
35
|
+
@target_arch ||= case uname
|
36
|
+
when /aarch/ then "aarch64"
|
37
|
+
when /x86/ then "x86_64"
|
38
|
+
else raise "unsupported architecture #{uname.inspect}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def download_url
|
43
|
+
@download_url ||= begin
|
44
|
+
version, version_ok = detect_version(current_catalog)
|
45
|
+
if version.nil?
|
46
|
+
version, full_version_ok = detect_version(full_catalog)
|
47
|
+
version_ok ||= full_version_ok
|
48
|
+
end
|
49
|
+
if version.nil?
|
50
|
+
if version_ok
|
51
|
+
raise MissingDownloadUrl, "No downloads for version #{desired_version}"
|
52
|
+
else
|
53
|
+
raise UnknownVersion, "No version #{desired_version}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
dl = version['downloads'].detect do |dl|
|
57
|
+
dl['archive']['url'].index("enterprise-#{arch}") &&
|
58
|
+
dl['arch'] == target_arch
|
59
|
+
end
|
60
|
+
unless dl
|
61
|
+
raise MissingDownloadUrl, "No download for #{arch} for #{version['version']}"
|
62
|
+
end
|
63
|
+
url = dl['archive']['url']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def uri_open(*args)
|
70
|
+
if RUBY_VERSION < '2.5'
|
71
|
+
open(*args)
|
72
|
+
else
|
73
|
+
URI.open(*args)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def detect_version(catalog)
|
78
|
+
candidate_versions = catalog['versions'].select do |version|
|
79
|
+
version['version'].start_with?(desired_version) &&
|
80
|
+
!version['version'].include?('-')
|
81
|
+
end
|
82
|
+
version_ok = !candidate_versions.empty?
|
83
|
+
# Sometimes the download situation is borked and there is a release
|
84
|
+
# with no downloads... skip those.
|
85
|
+
version = candidate_versions.detect do |version|
|
86
|
+
!version['downloads'].empty?
|
87
|
+
end
|
88
|
+
# Allow RC releases if there isn't a GA release.
|
89
|
+
if version.nil?
|
90
|
+
candidate_versions = catalog['versions'].select do |version|
|
91
|
+
version['version'].start_with?(desired_version)
|
92
|
+
end
|
93
|
+
version_ok ||= !candidate_versions.empty?
|
94
|
+
version = candidate_versions.detect do |version|
|
95
|
+
!version['downloads'].empty?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
[version, version_ok]
|
99
|
+
end
|
100
|
+
|
101
|
+
def current_catalog
|
102
|
+
@current_catalog ||= begin
|
103
|
+
JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def full_catalog
|
108
|
+
@full_catalog ||= begin
|
109
|
+
JSON.load(uri_open('http://downloads.mongodb.org/full.json').read)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Mrss
|
7
|
+
|
8
|
+
def self.patch_mongo_for_session_registry
|
9
|
+
|
10
|
+
Mongo::Client.class_eval do
|
11
|
+
alias :get_session_without_tracking :get_session
|
12
|
+
|
13
|
+
def get_session(options = {})
|
14
|
+
get_session_without_tracking(options).tap do |session|
|
15
|
+
SessionRegistry.instance.register(session) if session&.materialized?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Mongo::Session.class_eval do
|
21
|
+
alias :end_session_without_tracking :end_session
|
22
|
+
|
23
|
+
def end_session
|
24
|
+
SessionRegistry.instance.unregister(self)
|
25
|
+
end_session_without_tracking
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :materialize_if_needed_without_tracking :materialize_if_needed
|
29
|
+
|
30
|
+
def materialize_if_needed
|
31
|
+
materialize_if_needed_without_tracking.tap do
|
32
|
+
SessionRegistry.instance.register(self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Mrss
|
40
|
+
class SessionRegistry
|
41
|
+
include Singleton
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@registry = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def register(session)
|
48
|
+
@registry[session.session_id] = session if session
|
49
|
+
end
|
50
|
+
|
51
|
+
def unregister(session)
|
52
|
+
return if session.ended? || !session.materialized?
|
53
|
+
@registry.delete(session.session_id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def verify_sessions_ended!
|
57
|
+
@registry.delete_if { |_, session| session.ended? }
|
58
|
+
|
59
|
+
unless @registry.empty?
|
60
|
+
sessions = @registry.map { |_, session| session }
|
61
|
+
raise "Session registry contains live sessions: #{sessions.join(', ')}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def clear_registry
|
66
|
+
@registry = {}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Mrss
|
7
|
+
|
8
|
+
def self.patch_mongo_for_session_registry
|
9
|
+
|
10
|
+
Mongo::Client.class_eval do
|
11
|
+
alias :get_session_without_tracking :get_session
|
12
|
+
|
13
|
+
def get_session(options = {})
|
14
|
+
get_session_without_tracking(options).tap do |session|
|
15
|
+
SessionRegistry.instance.register(session)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Mongo::Session.class_eval do
|
21
|
+
alias :end_session_without_tracking :end_session
|
22
|
+
|
23
|
+
def end_session
|
24
|
+
SessionRegistry.instance.unregister(self)
|
25
|
+
end_session_without_tracking
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Mrss
|
32
|
+
class SessionRegistry
|
33
|
+
include Singleton
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@registry = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def register(session)
|
40
|
+
@registry[session.session_id] = session if session
|
41
|
+
end
|
42
|
+
|
43
|
+
def unregister(session)
|
44
|
+
@registry.delete(session.session_id) unless session.ended?
|
45
|
+
end
|
46
|
+
|
47
|
+
def verify_sessions_ended!
|
48
|
+
@registry.delete_if { |_, session| session.ended? }
|
49
|
+
|
50
|
+
unless @registry.empty?
|
51
|
+
sessions = @registry.map { |_, session| session }
|
52
|
+
raise "Session registry contains live sessions: #{sessions.join(', ')}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear_registry
|
57
|
+
@registry = {}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
autoload :JSON, 'json'
|
5
|
+
autoload :FileUtils, 'fileutils'
|
6
|
+
autoload :Find, 'find'
|
7
|
+
|
8
|
+
module Mrss
|
9
|
+
|
10
|
+
autoload :ChildProcessHelper, 'mrss/child_process_helper'
|
11
|
+
|
12
|
+
# Organizes and runs all of the tests in the test suite in batches.
|
13
|
+
#
|
14
|
+
# Organizing the tests in batches serves two purposes:
|
15
|
+
#
|
16
|
+
# 1. This allows running unit tests before integration tests, therefore
|
17
|
+
# in theory revealing failures quicker on average.
|
18
|
+
# 2. This allows running some tests that have high intermittent failure rate
|
19
|
+
# in their own test process.
|
20
|
+
#
|
21
|
+
# This class aggregates RSpec results after the test runs.
|
22
|
+
class SpecOrganizer
|
23
|
+
|
24
|
+
class BucketsNotPrioritized < StandardError
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(root: nil, classifiers:, priority_order:,
|
28
|
+
spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil,
|
29
|
+
randomize: false
|
30
|
+
)
|
31
|
+
@spec_root = spec_root || File.join(root, 'spec')
|
32
|
+
@classifiers = classifiers
|
33
|
+
@priority_order = priority_order
|
34
|
+
@rspec_json_path = rspec_json_path || File.join(root, 'tmp/rspec.json')
|
35
|
+
@rspec_all_json_path = rspec_all_json_path || File.join(root, 'tmp/rspec-all.json')
|
36
|
+
@randomize = !!randomize
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :spec_root, :classifiers, :priority_order
|
40
|
+
attr_reader :rspec_json_path, :rspec_all_json_path
|
41
|
+
|
42
|
+
def randomize?
|
43
|
+
@randomize
|
44
|
+
end
|
45
|
+
|
46
|
+
def seed
|
47
|
+
@seed ||= (rand * 100_000).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def buckets
|
51
|
+
@buckets ||= {}.tap do |buckets|
|
52
|
+
Find.find(spec_root) do |path|
|
53
|
+
next unless File.file?(path)
|
54
|
+
next unless path =~ /_spec\.rb\z/
|
55
|
+
rel_path = path[(spec_root.length + 1)..path.length]
|
56
|
+
|
57
|
+
found = false
|
58
|
+
classifiers.each do |(regexp, category)|
|
59
|
+
if regexp =~ rel_path
|
60
|
+
buckets[category] ||= []
|
61
|
+
buckets[category] << File.join('spec', rel_path)
|
62
|
+
found = true
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
unless found
|
68
|
+
buckets[nil] ||= []
|
69
|
+
buckets[nil] << File.join('spec', rel_path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end.freeze
|
73
|
+
end
|
74
|
+
|
75
|
+
def ordered_buckets
|
76
|
+
@ordered_buckets ||= {}.tap do |ordered_buckets|
|
77
|
+
buckets = self.buckets.dup
|
78
|
+
priority_order.each do |category|
|
79
|
+
files = buckets.delete(category)
|
80
|
+
ordered_buckets[category] = files
|
81
|
+
end
|
82
|
+
|
83
|
+
if files = buckets.delete(nil)
|
84
|
+
ordered_buckets[nil] = files
|
85
|
+
end
|
86
|
+
|
87
|
+
unless buckets.empty?
|
88
|
+
raise BucketsNotPrioritized, "Some buckets were not prioritized: #{buckets.keys.map(&:to_s).join(', ')}"
|
89
|
+
end
|
90
|
+
end.freeze
|
91
|
+
end
|
92
|
+
|
93
|
+
def run
|
94
|
+
run_buckets(*buckets.keys)
|
95
|
+
end
|
96
|
+
|
97
|
+
def run_buckets(*buckets)
|
98
|
+
FileUtils.rm_f(rspec_all_json_path)
|
99
|
+
|
100
|
+
buckets.each do |bucket|
|
101
|
+
if bucket && !self.buckets[bucket]
|
102
|
+
raise "Unknown bucket #{bucket}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
buckets = Hash[self.buckets.select { |k, v| buckets.include?(k) }]
|
106
|
+
|
107
|
+
failed = []
|
108
|
+
|
109
|
+
priority_order.each do |category|
|
110
|
+
if files = buckets.delete(category)
|
111
|
+
unless run_files(category, files)
|
112
|
+
failed << category
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
if files = buckets.delete(nil)
|
117
|
+
unless run_files('remaining', files)
|
118
|
+
failed << 'remaining'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
unless buckets.empty?
|
123
|
+
raise "Some buckets were not executed: #{buckets.keys.map(&:to_s).join(', ')}"
|
124
|
+
end
|
125
|
+
|
126
|
+
if failed.any?
|
127
|
+
raise "The following buckets failed: #{failed.map(&:to_s).join(', ')}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def run_files(category, paths)
|
132
|
+
puts "Running #{category.to_s.gsub('_', ' ')} tests"
|
133
|
+
FileUtils.rm_f(rspec_json_path)
|
134
|
+
cmd = %w(rspec) + paths
|
135
|
+
if randomize?
|
136
|
+
cmd += %W(--order rand:#{seed})
|
137
|
+
end
|
138
|
+
|
139
|
+
begin
|
140
|
+
puts "Running #{cmd.join(' ')}"
|
141
|
+
ChildProcessHelper.check_call(cmd)
|
142
|
+
ensure
|
143
|
+
if File.exist?(rspec_json_path)
|
144
|
+
if File.exist?(rspec_all_json_path)
|
145
|
+
merge_rspec_results
|
146
|
+
else
|
147
|
+
FileUtils.cp(rspec_json_path, rspec_all_json_path)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
true
|
153
|
+
rescue ChildProcessHelper::SpawnError
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
def merge_rspec_results
|
158
|
+
all = JSON.parse(File.read(rspec_all_json_path))
|
159
|
+
new = JSON.parse(File.read(rspec_json_path))
|
160
|
+
all['examples'] += new.delete('examples')
|
161
|
+
new.delete('summary').each do |k, v|
|
162
|
+
all['summary'][k] += v
|
163
|
+
end
|
164
|
+
new.delete('version')
|
165
|
+
new.delete('summary_line')
|
166
|
+
# The spec organizer runs all buckets with the same seed, hence
|
167
|
+
# we can drop the seed from new results.
|
168
|
+
new.delete('seed')
|
169
|
+
unless new.empty?
|
170
|
+
raise "Unhandled rspec results keys: #{new.keys.join(', ')}"
|
171
|
+
end
|
172
|
+
# We do not merge summary lines, delete them from aggregated results
|
173
|
+
all.delete('summary_line')
|
174
|
+
File.open(rspec_all_json_path, 'w') do |f|
|
175
|
+
f << JSON.dump(all)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
module Mrss
|
5
|
+
module Utils
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def print_backtrace(dest=STDERR)
|
9
|
+
raise
|
10
|
+
rescue => e
|
11
|
+
dest.puts e.backtrace.join("\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Parses the given version string, accounting for suffix information that
|
15
|
+
# Gem::Version cannot successfully parse.
|
16
|
+
#
|
17
|
+
# @param [ String ] version the version to parse
|
18
|
+
#
|
19
|
+
# @return [ Gem::Version ] the parsed version
|
20
|
+
#
|
21
|
+
# @raise [ ArgumentError ] if the string cannot be parsed.
|
22
|
+
def parse_version(version)
|
23
|
+
Gem::Version.new(version)
|
24
|
+
rescue ArgumentError
|
25
|
+
match = version.match(/\A(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?(-[A-Za-z\+\d]+)?\z/)
|
26
|
+
raise ArgumentError.new("Malformed version number string #{version}") if match.nil?
|
27
|
+
|
28
|
+
Gem::Version.new(
|
29
|
+
[
|
30
|
+
match[:major],
|
31
|
+
match[:minor],
|
32
|
+
match[:patch]
|
33
|
+
].join('.')
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../mrss/release/candidate'
|
4
|
+
|
5
|
+
namespace :candidate do
|
6
|
+
desc 'Initialize a new product.yml file'
|
7
|
+
task :init do
|
8
|
+
Mrss::Release::ProductData.init!
|
9
|
+
puts "product.yml file created"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Print the release notes for the next candidate release'
|
13
|
+
task :preview do
|
14
|
+
Mrss::Release::Candidate.instance do |candidate|
|
15
|
+
# load the pending changes before bumping the version, since it
|
16
|
+
# depends on the value of the current version.
|
17
|
+
candidate.pending_changes
|
18
|
+
candidate.bump_version
|
19
|
+
puts candidate.release_notes
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'List the pull requests to be included in the next release'
|
24
|
+
task :prs do
|
25
|
+
Mrss::Release::Candidate.instance.decorated_prs.each do |pr|
|
26
|
+
print "\##{pr['number']}[#{pr['type-code']}] "
|
27
|
+
print "#{pr['jira']} " if pr['jira']
|
28
|
+
puts pr['short-title']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Create a new branch and pull request for the candidate'
|
33
|
+
task create: :check_branch_status do
|
34
|
+
Mrss::Release::Candidate.instance do |candidate|
|
35
|
+
origin = `git config get remote.origin.url`
|
36
|
+
match = origin.match(/:(.*?)\//) or raise "origin url is not in expected format: #{origin.inspect}"
|
37
|
+
user = match[1]
|
38
|
+
|
39
|
+
puts 'gathering candidate info and bumping version...'
|
40
|
+
candidate.bump_version!
|
41
|
+
|
42
|
+
puts 'writing release notes to /tmp/pr-body.md...'
|
43
|
+
File.write('/tmp/pr-body.md', candidate.release_notes)
|
44
|
+
|
45
|
+
sh 'git', 'checkout', '-b', candidate.branch_name
|
46
|
+
sh 'git', 'commit', '-am', "Bump version to #{candidate.product.version}"
|
47
|
+
sh 'git', 'push', 'origin', candidate.branch_name
|
48
|
+
|
49
|
+
sh 'gh', 'pr', 'create',
|
50
|
+
'--head', "#{user}:#{candidate.branch_name}",
|
51
|
+
'--base', candidate.product.base_branch,
|
52
|
+
'--title', "Release candidate for #{candidate.product.version}",
|
53
|
+
'--label', 'release-candidate',
|
54
|
+
'--body-file', '/tmp/pr-body.md'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Ensures the current branch is up-to-date with no uncommitted changes
|
59
|
+
task :check_branch_status do
|
60
|
+
sh 'git pull >/dev/null', verbose: false
|
61
|
+
changes = `git status --short --untracked-files=no`.strip
|
62
|
+
abort "There are uncommitted changes. Commit (or revert) the changes and try again." if changes.length > 0
|
63
|
+
end
|
64
|
+
end
|