builderator 0.3.15 → 1.0.0.pre.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|