builderator 0.3.15 → 1.0.0.pre.rc.1
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/Gemfile +9 -0
- data/Gemfile.lock +440 -0
- data/README.md +72 -18
- data/Rakefile +1 -2
- data/VERSION +1 -1
- data/bin/build-clean +102 -0
- data/bin/build-data +45 -0
- data/builderator.gemspec +7 -4
- data/docs/configuration.md +154 -0
- data/docs/configuration/cookbook.md +19 -0
- data/docs/configuration/profile.md +71 -0
- data/docs/versioning.md +65 -0
- data/lib/builderator.rb +3 -0
- data/lib/builderator/config.rb +93 -0
- data/lib/builderator/config/attributes.rb +287 -0
- data/lib/builderator/config/defaults.rb +163 -0
- data/lib/builderator/config/file.rb +336 -0
- data/lib/builderator/config/rash.rb +80 -0
- data/lib/builderator/control/cleaner.rb +138 -0
- data/lib/builderator/control/cookbook.rb +16 -0
- data/lib/builderator/control/data.rb +16 -0
- data/lib/builderator/control/data/image.rb +98 -0
- data/lib/builderator/control/version.rb +128 -0
- data/lib/builderator/control/version/auto.rb +48 -0
- data/lib/builderator/control/version/bump.rb +82 -0
- data/lib/builderator/control/version/comparable.rb +77 -0
- data/lib/builderator/control/version/git.rb +45 -0
- data/lib/builderator/control/version/scm.rb +92 -0
- data/lib/builderator/interface.rb +67 -0
- data/lib/builderator/interface/berkshelf.rb +38 -0
- data/lib/builderator/interface/packer.rb +75 -0
- data/lib/builderator/interface/vagrant.rb +31 -0
- data/lib/builderator/metadata.rb +5 -3
- data/lib/builderator/model/cleaner.rb +49 -0
- data/lib/builderator/model/cleaner/images.rb +93 -0
- data/lib/builderator/model/cleaner/instances.rb +58 -0
- data/lib/builderator/model/cleaner/launch_configs.rb +47 -0
- data/lib/builderator/model/cleaner/scaling_groups.rb +45 -0
- data/lib/builderator/model/cleaner/snapshots.rb +50 -0
- data/lib/builderator/model/cleaner/volumes.rb +48 -0
- data/lib/builderator/patch/berkshelf.rb +18 -0
- data/lib/builderator/patch/thor-actions.rb +47 -0
- data/lib/builderator/tasks.rb +127 -17
- data/lib/builderator/tasks/berkshelf.rb +63 -0
- data/lib/builderator/tasks/packer.rb +17 -56
- data/lib/builderator/tasks/vagrant.rb +111 -42
- data/lib/builderator/tasks/vendor.rb +94 -0
- data/lib/builderator/tasks/version.rb +58 -0
- data/lib/builderator/util.rb +37 -11
- data/lib/builderator/util/aws_exception.rb +1 -1
- data/lib/builderator/util/limit_exception.rb +12 -11
- data/lib/builderator/util/task_exception.rb +0 -2
- data/mkmf.log +4 -0
- data/spec/config_spec.rb +30 -0
- data/spec/data/Berksfile +6 -0
- data/spec/data/Buildfile +0 -0
- data/spec/data/Vagrantfile +0 -0
- data/spec/data/history.json +483 -0
- data/spec/data/packer.json +0 -0
- data/spec/interface_spec.rb +36 -0
- data/spec/resource/Buildfile +27 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/version_spec.rb +282 -0
- data/template/Berksfile.erb +10 -0
- data/template/Buildfile.erb +28 -0
- data/template/Gemfile.erb +16 -0
- data/template/README.md.erb +61 -0
- data/template/Vagrantfile.erb +75 -0
- data/template/gitignore.erb +104 -0
- data/{.rubocop.yml → template/rubocop.erb} +0 -0
- metadata +203 -56
- data/.gitignore +0 -14
- data/lib/builderator/control/ami.rb +0 -65
- data/lib/builderator/control/clean.rb +0 -130
- data/lib/builderator/model.rb +0 -46
- data/lib/builderator/model/images.rb +0 -89
- data/lib/builderator/model/instances.rb +0 -55
- data/lib/builderator/model/launch_configs.rb +0 -46
- data/lib/builderator/model/scaling_groups.rb +0 -43
- data/lib/builderator/model/snapshots.rb +0 -49
- data/lib/builderator/model/volumes.rb +0 -48
- data/lib/builderator/tasks/ami.rb +0 -47
- data/lib/builderator/tasks/berks.rb +0 -68
- data/lib/builderator/tasks/clean.rb +0 -97
- data/lib/builderator/util/berkshim.rb +0 -34
- data/lib/builderator/util/cookbook.rb +0 -87
- data/lib/builderator/util/packer.rb +0 -39
- data/lib/builderator/util/shell.rb +0 -44
@@ -0,0 +1,80 @@
|
|
1
|
+
module Builderator
|
2
|
+
module Config
|
3
|
+
##
|
4
|
+
# A self-populating sparse Hash by Rapid7 ([R]apid7 h[ASH]). Definetly
|
5
|
+
# not a Mash or Smash...
|
6
|
+
##
|
7
|
+
class Rash < Hash
|
8
|
+
class << self
|
9
|
+
def coerce(somehting)
|
10
|
+
return somehting if somehting.is_a?(self)
|
11
|
+
return new(somehting) if somehting.is_a?(Hash)
|
12
|
+
|
13
|
+
## `somehting` is not a valid input. Just give back an instance.
|
14
|
+
new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :sealed
|
19
|
+
|
20
|
+
def initialize(from = {}, seal = false)
|
21
|
+
@sealed = seal
|
22
|
+
super() do |_, k|
|
23
|
+
self[k] = self.class.new unless sealed
|
24
|
+
end
|
25
|
+
|
26
|
+
merge!(from) ## Clone a Rash or coerce a Hash to a new Rash
|
27
|
+
end
|
28
|
+
|
29
|
+
def seal(action = true)
|
30
|
+
@sealed = action
|
31
|
+
each_value { |v| v.seal(action) if v.is_a?(self.class) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def unseal
|
35
|
+
seal(false)
|
36
|
+
end
|
37
|
+
|
38
|
+
alias_method :has?, :include?
|
39
|
+
|
40
|
+
## Symbolize keys
|
41
|
+
[:include?, :[], :fetch, :[]=, :store].each do |m|
|
42
|
+
define_method(m) do |key, *args|
|
43
|
+
super(key.to_sym, *args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def merge!(other)
|
48
|
+
fail TypeError, 'Argument other of `Rash#merge!(other)` must be a Hash.'\
|
49
|
+
" Recieved #{other.class}" unless other.is_a?(Hash)
|
50
|
+
|
51
|
+
other.each do |k, v|
|
52
|
+
## Replace `-`s with `_`s in in String keys
|
53
|
+
k = k.gsub(/\-/, '_') if k.is_a?(String)
|
54
|
+
|
55
|
+
## Merge Arrays
|
56
|
+
next self[k] |= v if fetch(k, nil).is_a?(Array) && v.is_a?(Array)
|
57
|
+
|
58
|
+
## Overwrite non-Hash values
|
59
|
+
next self[k] = v unless v.is_a?(Hash)
|
60
|
+
|
61
|
+
## Replace `self[k]` with a new Rash unless it already is one
|
62
|
+
self[k] = self.class.new unless fetch(k, nil).is_a?(self.class)
|
63
|
+
|
64
|
+
## Merge recursivly coerces `v` to a Rash
|
65
|
+
self[k].merge!(v)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_hash
|
70
|
+
each_with_object({}) do |(k, v), hash|
|
71
|
+
## Not a hash-value
|
72
|
+
next hash[k] = v unless v.is_a?(self.class)
|
73
|
+
|
74
|
+
## Recursivly coerces `v` to a Hash
|
75
|
+
hash[k] = v.to_hash
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require_relative '../model/cleaner'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Control
|
6
|
+
##
|
7
|
+
# Control logic for cleanup tasks
|
8
|
+
##
|
9
|
+
module Cleaner
|
10
|
+
class << self
|
11
|
+
def configs!
|
12
|
+
resources = Model::Cleaner.launch_configs.unused
|
13
|
+
|
14
|
+
yield :launch_configs, "Found #{resources.length} Launch Configurations to remove"
|
15
|
+
verify!(:launch_configs, 'Cleanup Launch Configurations', resources, &Proc.new)
|
16
|
+
aborted!(&Proc.new)
|
17
|
+
|
18
|
+
resources.keys.sort.each do |id|
|
19
|
+
yield :remove, "Launch Configuration #{id}", :red
|
20
|
+
Model::Cleaner.launch_configs.resources.delete(id)
|
21
|
+
|
22
|
+
next unless commit?
|
23
|
+
Util.asg.delete_launch_configuration(:launch_configuration_name => id)
|
24
|
+
end
|
25
|
+
rescue Aws::AutoScaling::Errors::ServiceError => e
|
26
|
+
exceptions << Util::AwsException.new('Cleanerup Launch Configurations', e)
|
27
|
+
yield(*exceptions.last.status)
|
28
|
+
end
|
29
|
+
|
30
|
+
def images!
|
31
|
+
resources = Model::Cleaner.images.unused
|
32
|
+
|
33
|
+
yield :images, "Found #{resources.length} Images to remove"
|
34
|
+
yield :grouping, "Groupd images by #{Config.cleaner.group_by}" if Config.cleaner.group_by
|
35
|
+
yield :keep, "Keeping #{Config.cleaner.keep} images in each group"
|
36
|
+
verify!(:images, 'Cleanup Images', resources, &Proc.new)
|
37
|
+
aborted!(&Proc.new)
|
38
|
+
|
39
|
+
resources.values
|
40
|
+
.sort { |a, b| a[:properties]['name'] <=> b[:properties]['name'] }
|
41
|
+
.each do |image|
|
42
|
+
yield :remove, "Image #{image[:id]} (#{image[:properties]['name']})", :red
|
43
|
+
Model::Cleaner.images.resources.delete(image[:id])
|
44
|
+
|
45
|
+
next unless commit?
|
46
|
+
Util.ec2.deregister_image(:image_id => image[:id])
|
47
|
+
end
|
48
|
+
|
49
|
+
rescue Aws::EC2::Errors::ServiceError => e
|
50
|
+
exceptions << Util::AwsException.new('Cleanerup Images', e)
|
51
|
+
yield(*exceptions.last.status)
|
52
|
+
end
|
53
|
+
|
54
|
+
def snapshots!
|
55
|
+
resources = Model::Cleaner.snapshots.unused
|
56
|
+
|
57
|
+
yield :snapshots, "Found #{resources.length} Snapshots to remove"
|
58
|
+
verify!(:snapshots, 'Cleanup Snapshots', resources, &Proc.new)
|
59
|
+
aborted!(&Proc.new)
|
60
|
+
|
61
|
+
resources.keys.sort.each do |id|
|
62
|
+
yield :remove, "Snapshot #{id}", :red
|
63
|
+
Model::Cleaner.snapshots.resources.delete(id)
|
64
|
+
|
65
|
+
next unless commit?
|
66
|
+
Util.ec2.delete_snapshot(:snapshot_id => id)
|
67
|
+
end
|
68
|
+
rescue Aws::EC2::Errors::ServiceError => e
|
69
|
+
exceptions << Util::AwsException.new('Cleanerup Snapshots', e)
|
70
|
+
yield(*exceptions.last.status)
|
71
|
+
end
|
72
|
+
|
73
|
+
def volumes!
|
74
|
+
resources = Model::Cleaner.volumes.unused
|
75
|
+
|
76
|
+
yield :volumes, "Found #{resources.length} Volumes to remove"
|
77
|
+
verify!(:volumes, 'Cleanup Volumes', resources, &Proc.new)
|
78
|
+
aborted!(&Proc.new)
|
79
|
+
|
80
|
+
resources.keys.sort.each do |id|
|
81
|
+
yield :remove, "Volume #{id}", :red
|
82
|
+
Model::Cleaner.volumes.resources.delete(id)
|
83
|
+
|
84
|
+
next unless commit?
|
85
|
+
Util.ec2.delete_volume(:volume_id => id)
|
86
|
+
end
|
87
|
+
rescue Aws::EC2::Errors::ServiceError => e
|
88
|
+
exceptions << Util::AwsException.new('Cleanerup Volumes', e)
|
89
|
+
yield(*exceptions.last.status)
|
90
|
+
end
|
91
|
+
|
92
|
+
def commit?
|
93
|
+
@commit && !@abort
|
94
|
+
end
|
95
|
+
|
96
|
+
def aborted?
|
97
|
+
@commit && @abort
|
98
|
+
end
|
99
|
+
|
100
|
+
def exceptions
|
101
|
+
@exceptions ||= []
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def aborted!
|
107
|
+
yield :aborted, 'The following resources will NOT be removed because'\
|
108
|
+
' safty constraints have not been met!', :yellow if aborted?
|
109
|
+
end
|
110
|
+
|
111
|
+
def verify!(resource_name, task, resources)
|
112
|
+
if Config.cleaner.commit
|
113
|
+
yield :commit, 'This is not a dry-run. Press CTL-C to stop! '\
|
114
|
+
'(continuing in 5 seconds)', :red
|
115
|
+
|
116
|
+
sleep(5) ## Give $USER a few seconds to stop
|
117
|
+
end
|
118
|
+
|
119
|
+
return unless resources.size >= Config.cleaner.limits[resource_name]
|
120
|
+
|
121
|
+
ex = Util::LimitException.new(resource_name, task, resources)
|
122
|
+
yield(*ex.status)
|
123
|
+
|
124
|
+
if Config.cleaner.force
|
125
|
+
yield :force, 'Limits will be ignored. Press CTL-C to stop! '\
|
126
|
+
'(continuing in 5 seconds)', :red
|
127
|
+
sleep(5) ## Give $USER a few seconds to stop
|
128
|
+
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
exceptions << ex
|
133
|
+
@abort = true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Builderator
|
2
|
+
module Control
|
3
|
+
##
|
4
|
+
# Wrapper module for lookup controllers
|
5
|
+
##
|
6
|
+
module Data
|
7
|
+
def self.lookup(source, query)
|
8
|
+
fail "#{ source } is not a valid data type!" unless respond_to?(source)
|
9
|
+
|
10
|
+
send(source, query)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require_relative './data/image'
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
require_relative '../../util'
|
5
|
+
|
6
|
+
module Builderator
|
7
|
+
module Control
|
8
|
+
# :nodoc:
|
9
|
+
module Data
|
10
|
+
def self.image(query = {})
|
11
|
+
Image.search(query)
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Find AMI IDs to use for sources
|
16
|
+
##
|
17
|
+
module Image
|
18
|
+
## Account IDs of public image owners
|
19
|
+
OWNERS = {
|
20
|
+
:self => 'self'.freeze,
|
21
|
+
:ubuntu => '099720109477'.freeze,
|
22
|
+
:amazon => 'amazon'.freeze,
|
23
|
+
:marketplace => 'aws-marketplace'.freeze
|
24
|
+
}
|
25
|
+
|
26
|
+
## Pre-defined filters
|
27
|
+
FILTERS = {
|
28
|
+
'ubuntu-14.04-daily' => {
|
29
|
+
'owner' => OWNERS[:ubuntu],
|
30
|
+
'architecture' => 'x86_64',
|
31
|
+
'root-device-type' => 'ebs',
|
32
|
+
'virtualization-type' => 'hvm',
|
33
|
+
'block-device-mapping.volume-type' => 'gp2',
|
34
|
+
'name' => '*ubuntu-trusty-daily-amd64-server-201*'
|
35
|
+
},
|
36
|
+
'windows-server2012-r2' => {
|
37
|
+
'owner' => OWNERS[:amazon],
|
38
|
+
'architecture' => 'x86_64',
|
39
|
+
'root-device-type' => 'ebs',
|
40
|
+
'virtualization-type' => 'hvm',
|
41
|
+
'block-device-mapping.volume-type' => 'gp2',
|
42
|
+
'name' => 'Windows_Server-2012-R2_RTM-English-64Bit-Base*'
|
43
|
+
},
|
44
|
+
'windows-server2012-r2-core' => {
|
45
|
+
'owner' => OWNERS[:amazon],
|
46
|
+
'architecture' => 'x86_64',
|
47
|
+
'root-device-type' => 'ebs',
|
48
|
+
'virtualization-type' => 'hvm',
|
49
|
+
'block-device-mapping.volume-type' => 'gp2',
|
50
|
+
'name' => 'Windows_Server-2012-R2_RTM-English-64Bit-Core*'
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
## Filter fields defined in http://docs.aws.amazon.com/sdkforruby/api/Aws/EC2/Builderator::Util.ec2.html#describe_images-instance_method
|
55
|
+
PROPERTIES = %w(architecture block-device-mapping.delete-on-termination
|
56
|
+
block-device-mapping.device-name block-device-mapping.snapshot-id
|
57
|
+
block-device-mapping.volume-size block-device-mapping.volume-type
|
58
|
+
description hypervisor image-id image-type is-public kernel-id
|
59
|
+
manifest-location name owner-alias owner-id platform product-code
|
60
|
+
product-code.type ramdisk-id root-device-name root-device-type
|
61
|
+
state state-reason-code state-reason-message virtualization-type).freeze
|
62
|
+
|
63
|
+
class << self
|
64
|
+
def search(query = {})
|
65
|
+
options = {}
|
66
|
+
|
67
|
+
## Reverse-merge a pre-defined filter into the query
|
68
|
+
if query.include?('filter')
|
69
|
+
query = FILTERS[query['filter']].merge(query) if FILTERS.include?(query['filter'])
|
70
|
+
|
71
|
+
query.delete('filter')
|
72
|
+
end
|
73
|
+
|
74
|
+
options['image_ids'] = Util.to_array(query.delete('image_id')) if query.include?('image_id')
|
75
|
+
options['owners'] = Util.to_array(query.delete('owner') { 'self' })
|
76
|
+
|
77
|
+
options['filters'] = query.each_with_object([]) do |(k, v), memo|
|
78
|
+
next if v.nil?
|
79
|
+
|
80
|
+
## Construct filter objects. Assume that non-enumerated keys are tags
|
81
|
+
memo << {
|
82
|
+
:name => PROPERTIES.include?(k.to_s) ? k.to_s : "tag:#{ k }",
|
83
|
+
:values => Util.to_array(v)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
## Don't send an empty filters array
|
88
|
+
options.delete('filters') if options['filters'].empty?
|
89
|
+
|
90
|
+
Util.ec2.describe_images(options)
|
91
|
+
.each_with_object([]) { |page, images| images.push(*page.images) }
|
92
|
+
.sort { |a, b| DateTime.iso8601(b.creation_date) <=> DateTime.iso8601(a.creation_date) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require_relative './version/auto'
|
2
|
+
require_relative './version/bump'
|
3
|
+
require_relative './version/comparable'
|
4
|
+
require_relative './version/scm'
|
5
|
+
require_relative './version/git'
|
6
|
+
|
7
|
+
module Builderator
|
8
|
+
module Control
|
9
|
+
##
|
10
|
+
# Version management tools
|
11
|
+
#
|
12
|
+
# Initial version boosted shamelessly from
|
13
|
+
# https://github.com/RiotGamesMinions/thor-scmversion
|
14
|
+
##
|
15
|
+
class Version
|
16
|
+
FORMAT = /(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?<prerelease>-(?<prerelease_name>[A-Za-z0-9]+)\.(?<prerelease_iteration>\d+))?(?:\+build\.(?<build>\d+))?$/
|
17
|
+
DEFAULT_PRERELEASE_NAME = 'alpha'.freeze
|
18
|
+
|
19
|
+
## Order of precedence for release types
|
20
|
+
RELEASE_TYPES = {
|
21
|
+
'major' => 0,
|
22
|
+
'major-prerelease' => 5,
|
23
|
+
'minor' => 10,
|
24
|
+
'minor-prerelease' => 15,
|
25
|
+
'patch' => 20,
|
26
|
+
'patch-prerelease' => 25,
|
27
|
+
'release' => 30,
|
28
|
+
'prerelease' => 35,
|
29
|
+
'build' => 40
|
30
|
+
}
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def current
|
34
|
+
@current ||= SCM.tags.last
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_config_version
|
38
|
+
Config.defaults.version = current.to_s
|
39
|
+
Config.recompile
|
40
|
+
end
|
41
|
+
|
42
|
+
def write
|
43
|
+
current.write
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Alias `bump` to the current version
|
48
|
+
##
|
49
|
+
def bump(type = nil, prerelease_name = nil)
|
50
|
+
@current = current.clone
|
51
|
+
|
52
|
+
current.bump(type, prerelease_name)
|
53
|
+
SCM.tags << current
|
54
|
+
|
55
|
+
current
|
56
|
+
end
|
57
|
+
|
58
|
+
## Parse a SemVer string into a Version
|
59
|
+
def from_string(arg, options = {})
|
60
|
+
matchdata = arg.match(FORMAT)
|
61
|
+
fail "Builderator::Control::Version.from_string: #{arg} is not a supported semver string" if matchdata.nil?
|
62
|
+
|
63
|
+
new(matchdata[:major], matchdata[:minor], matchdata[:patch], matchdata[:build], options).tap do |version|
|
64
|
+
version.is_prerelease = !matchdata[:prerelease].nil?
|
65
|
+
if version.is_prerelease
|
66
|
+
version.prerelease_name = matchdata[:prerelease_name]
|
67
|
+
version.prerelease_iteration = matchdata[:prerelease_iteration].to_i
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialize(major, minor, patch, build = nil, **options)
|
74
|
+
@major = major.to_i
|
75
|
+
@minor = minor.to_i
|
76
|
+
@patch = patch.to_i
|
77
|
+
@build = build.to_i unless build.nil?
|
78
|
+
|
79
|
+
@ref = options[:ref]
|
80
|
+
end
|
81
|
+
|
82
|
+
include Auto
|
83
|
+
include Bump
|
84
|
+
include Comparable
|
85
|
+
|
86
|
+
attr_accessor :ref
|
87
|
+
|
88
|
+
attr_accessor :major
|
89
|
+
attr_accessor :minor
|
90
|
+
attr_accessor :patch
|
91
|
+
|
92
|
+
attr_accessor :is_prerelease
|
93
|
+
attr_accessor :prerelease_name
|
94
|
+
attr_accessor :prerelease_iteration
|
95
|
+
|
96
|
+
attr_accessor :build
|
97
|
+
|
98
|
+
## Create or bump a new prerelease train
|
99
|
+
def prerelease(name = nil)
|
100
|
+
self.build = nil ## Reset the build counter
|
101
|
+
|
102
|
+
## Increment current prerelease train
|
103
|
+
if is_prerelease && (name.nil? || name == prerelease_name)
|
104
|
+
self.prerelease_iteration += 1
|
105
|
+
return self
|
106
|
+
end
|
107
|
+
|
108
|
+
## New prerelease train
|
109
|
+
self.is_prerelease = true
|
110
|
+
self.prerelease_name = name.nil? ? DEFAULT_PRERELEASE_NAME : name
|
111
|
+
self.prerelease_iteration = 0
|
112
|
+
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
def write
|
117
|
+
Util.relative_path('VERSION').write(to_s)
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
string = [major, minor, patch].join('.')
|
122
|
+
string << "-#{prerelease_name}.#{prerelease_iteration}" if is_prerelease
|
123
|
+
string << "+build.#{build}" unless build.nil?
|
124
|
+
string
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|