stove 1.1.2 → 2.0.0.beta.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/.travis.yml +1 -2
- data/CHANGELOG.md +16 -0
- data/README.md +41 -29
- data/Rakefile +15 -0
- data/bin/bake +1 -2
- data/features/actions/bump.feature +22 -0
- data/features/actions/changelog.feature +45 -0
- data/features/actions/dev.feature +18 -0
- data/features/actions/upload.feature +48 -0
- data/features/plugins/git.feature +24 -0
- data/features/rake.feature +1 -2
- data/features/step_definitions/cli_steps.rb +1 -27
- data/features/step_definitions/{community_site_steps.rb → community_steps.rb} +9 -5
- data/features/step_definitions/config_steps.rb +24 -0
- data/features/step_definitions/cookbook_steps.rb +28 -6
- data/features/step_definitions/cucumber_steps.rb +12 -0
- data/features/step_definitions/git_steps.rb +10 -7
- data/features/support/env.rb +12 -28
- data/features/support/stove/git.rb +48 -0
- data/lib/stove.rb +102 -19
- data/lib/stove/actions/base.rb +21 -0
- data/lib/stove/actions/bump.rb +25 -0
- data/lib/stove/actions/changelog.rb +71 -0
- data/lib/stove/actions/dev.rb +22 -0
- data/lib/stove/actions/finish.rb +8 -0
- data/lib/stove/actions/start.rb +7 -0
- data/lib/stove/actions/upload.rb +27 -0
- data/lib/stove/cli.rb +107 -79
- data/lib/stove/community.rb +124 -0
- data/lib/stove/config.rb +62 -13
- data/lib/stove/cookbook.rb +76 -238
- data/lib/stove/cookbook/metadata.rb +16 -11
- data/lib/stove/error.rb +13 -107
- data/lib/stove/filter.rb +59 -0
- data/lib/stove/jira.rb +74 -30
- data/lib/stove/middlewares/chef_authentication.rb +60 -0
- data/lib/stove/middlewares/exceptions.rb +17 -0
- data/lib/stove/mixins/filterable.rb +11 -0
- data/lib/stove/mixins/insideable.rb +13 -0
- data/lib/stove/mixins/instanceable.rb +23 -0
- data/lib/stove/mixins/loggable.rb +32 -0
- data/lib/stove/mixins/optionable.rb +41 -0
- data/lib/stove/mixins/validatable.rb +7 -0
- data/lib/stove/packager.rb +23 -22
- data/lib/stove/plugins/base.rb +35 -0
- data/lib/stove/plugins/git.rb +71 -0
- data/lib/stove/plugins/github.rb +108 -0
- data/lib/stove/plugins/jira.rb +72 -0
- data/lib/stove/rake_task.rb +56 -37
- data/lib/stove/runner.rb +84 -0
- data/lib/stove/util.rb +56 -0
- data/lib/stove/validator.rb +67 -0
- data/lib/stove/version.rb +1 -1
- data/locales/en.yml +231 -0
- data/stove.gemspec +11 -11
- metadata +85 -67
- data/features/changelog.feature +0 -22
- data/features/cli.feature +0 -11
- data/features/devodd.feature +0 -19
- data/features/git.feature +0 -34
- data/features/upload.feature +0 -40
- data/lib/stove/community_site.rb +0 -85
- data/lib/stove/formatter.rb +0 -7
- data/lib/stove/formatter/base.rb +0 -32
- data/lib/stove/formatter/human.rb +0 -9
- data/lib/stove/formatter/silent.rb +0 -10
- data/lib/stove/git.rb +0 -82
- data/lib/stove/github.rb +0 -43
- data/lib/stove/logger.rb +0 -56
- data/lib/stove/uploader.rb +0 -64
- data/spec/support/community_site.rb +0 -33
- data/spec/support/git.rb +0 -52
data/lib/stove/cli.rb
CHANGED
@@ -3,122 +3,150 @@ require 'stove'
|
|
3
3
|
|
4
4
|
module Stove
|
5
5
|
class Cli
|
6
|
+
include Mixin::Loggable
|
7
|
+
|
6
8
|
def initialize(argv, stdin=STDIN, stdout=STDOUT, stderr=STDERR, kernel=Kernel)
|
7
9
|
@argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
|
8
|
-
$stdout, @stderr = @stdout, @stderr
|
9
10
|
end
|
10
11
|
|
11
12
|
def execute!
|
13
|
+
$stdout, $stderr = @stdout, @stderr
|
14
|
+
|
15
|
+
# Parse the options hash
|
12
16
|
option_parser.parse!(@argv)
|
13
|
-
options[:new_version] = @argv.first
|
14
17
|
|
15
|
-
|
18
|
+
# Set the log level
|
19
|
+
Stove.log_level = options[:log_level]
|
16
20
|
|
17
|
-
|
21
|
+
# Parse out the version from ARGV
|
22
|
+
options[:version] = @argv.shift
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
# Useful debugging output for when people type the wrong fucking command
|
25
|
+
# and then open an issue like it's somehow my fault
|
26
|
+
log.info("Options: #{options.inspect}")
|
27
|
+
log.info("ARGV: #{@argv.inspect}")
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
# Unless the user specified --no-bump, version is a required argument, so
|
30
|
+
# blow up if we don't get it or if it's not a nice version string
|
31
|
+
if options[:bump]
|
32
|
+
raise OptionParser::MissingArgument.new(:version) unless options[:version]
|
26
33
|
end
|
27
34
|
|
35
|
+
# Make a new cookbook object - this will raise an exception if there is
|
36
|
+
# no cookbook at the given path
|
37
|
+
cookbook = Cookbook.new(options[:path])
|
38
|
+
|
39
|
+
# Now execute the actual runners (validations and errors might occur)
|
40
|
+
Runner.run(cookbook, options)
|
41
|
+
|
42
|
+
# If we got this far, everything was successful :)
|
43
|
+
@kernel.exit(0)
|
44
|
+
rescue => e
|
45
|
+
log.error('Stove experienced an error!')
|
46
|
+
log.error(e.class.name)
|
47
|
+
log.error(e.message)
|
48
|
+
log.error(e.backtrace.join("\n"))
|
49
|
+
|
28
50
|
@kernel.exit(e.respond_to?(:exit_code) ? e.exit_code : 500)
|
29
51
|
ensure
|
30
52
|
$stdout, $stderr = STDOUT, STDERR
|
31
53
|
end
|
32
54
|
|
33
55
|
private
|
34
|
-
# The option parser for handling command line flags.
|
35
|
-
#
|
36
|
-
# @return [OptionParser]
|
37
|
-
def option_parser
|
38
|
-
@option_parser ||= OptionParser.new do |opts|
|
39
|
-
opts.banner = "Usage: bake x.y.z"
|
40
|
-
|
41
|
-
opts.on('-l', '--log-level [LEVEL]', [:fatal, :error, :warn, :info, :debug], 'Ruby log level') do |v|
|
42
|
-
options[:log_level] = v
|
43
|
-
end
|
44
56
|
|
45
|
-
|
46
|
-
|
57
|
+
#
|
58
|
+
# The option parser for handling command line flags.
|
59
|
+
#
|
60
|
+
# @return [OptionParser]
|
61
|
+
#
|
62
|
+
def option_parser
|
63
|
+
@option_parser ||= OptionParser.new do |opts|
|
64
|
+
opts.banner = 'Usage: bake x.y.z'
|
65
|
+
|
66
|
+
opts.separator ''
|
67
|
+
opts.separator 'Actions:'
|
68
|
+
|
69
|
+
actions = Action.constants.map(&Action.method(:const_get))
|
70
|
+
actions.select(&:id).each do |action|
|
71
|
+
opts.on("--[no-]#{action.id}", action.description) do |v|
|
72
|
+
options[action.id.to_sym] = v
|
47
73
|
end
|
74
|
+
end
|
48
75
|
|
49
|
-
|
50
|
-
|
51
|
-
end
|
76
|
+
opts.separator ''
|
77
|
+
opts.separator 'Plugins:'
|
52
78
|
|
53
|
-
|
54
|
-
|
79
|
+
plugins = Plugin.constants.map(&Plugin.method(:const_get))
|
80
|
+
plugins.select(&:id).each do |plugin|
|
81
|
+
opts.on("--[no-]#{plugin.id}", plugin.description) do |v|
|
82
|
+
options[plugin.id.to_sym] = v
|
55
83
|
end
|
84
|
+
end
|
56
85
|
|
57
|
-
|
58
|
-
|
59
|
-
options[:github] = v
|
60
|
-
end
|
86
|
+
opts.separator ''
|
87
|
+
opts.separator 'Global Options:'
|
61
88
|
|
62
|
-
|
63
|
-
|
64
|
-
|
89
|
+
opts.on('--locale [LANGUAGE]', 'Change the language to output messages') do |locale|
|
90
|
+
I18n.locale = locale
|
91
|
+
end
|
65
92
|
|
66
|
-
|
67
|
-
|
68
|
-
|
93
|
+
opts.on('--log-level [LEVEL]', 'Set the log verbosity') do |v|
|
94
|
+
options[:log_level] = v
|
95
|
+
end
|
69
96
|
|
70
|
-
|
71
|
-
|
72
|
-
|
97
|
+
opts.on('--category [CATEGORY]', 'Set category for the cookbook') do |v|
|
98
|
+
options[:category] = v
|
99
|
+
end
|
73
100
|
|
74
|
-
|
75
|
-
|
76
|
-
|
101
|
+
opts.on('--path [PATH]', 'Change the path to a cookbook') do |v|
|
102
|
+
options[:path] = v
|
103
|
+
end
|
77
104
|
|
78
|
-
|
79
|
-
|
80
|
-
|
105
|
+
opts.on('--remote [REMOTE]', 'The name of the git remote to push to') do |v|
|
106
|
+
options[:remote] = v
|
107
|
+
end
|
81
108
|
|
82
|
-
|
83
|
-
|
84
|
-
|
109
|
+
opts.on('--branch [BRANCH]', 'The name of the git branch to push to') do |v|
|
110
|
+
options[:branch] = v
|
111
|
+
end
|
85
112
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
113
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
114
|
+
puts opts
|
115
|
+
exit
|
116
|
+
end
|
90
117
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
118
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
119
|
+
puts Stove::VERSION
|
120
|
+
exit(0)
|
95
121
|
end
|
96
122
|
end
|
123
|
+
end
|
97
124
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
}
|
125
|
+
# The options to pass to the cookbook. Includes default values
|
126
|
+
# that are manipulated by the option parser.
|
127
|
+
#
|
128
|
+
# @return [Hash]
|
129
|
+
def options
|
130
|
+
@options ||= Hash.new(default_value).tap do |h|
|
131
|
+
h[:path] = Dir.pwd
|
132
|
+
h[:log_level] = :warn
|
133
|
+
|
134
|
+
# Default actions/plugins
|
135
|
+
h[:jira] = false
|
136
|
+
h[:start] = true
|
137
|
+
h[:finish] = true
|
138
|
+
|
139
|
+
h[:remote] = 'origin'
|
140
|
+
h[:branch] = 'master'
|
115
141
|
end
|
142
|
+
end
|
116
143
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
144
|
+
def default_value
|
145
|
+
@default_value ||= if ENV['CLI_DEFAULT']
|
146
|
+
!!(ENV['CLI_DEFAULT'] =~ /^(true|t|yes|y|1)$/i)
|
147
|
+
else
|
148
|
+
true
|
122
149
|
end
|
150
|
+
end
|
123
151
|
end
|
124
152
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
module Stove
|
5
|
+
class Community
|
6
|
+
include Mixin::Instanceable
|
7
|
+
include Mixin::Loggable
|
8
|
+
include Mixin::Optionable
|
9
|
+
|
10
|
+
option :base_url,
|
11
|
+
ENV['COMMUNITY_URL'] || 'https://cookbooks.opscode.com/api/v1'
|
12
|
+
|
13
|
+
#
|
14
|
+
# Get and cache a community cookbook's JSON response from the given name
|
15
|
+
# and version.
|
16
|
+
#
|
17
|
+
# @example Find a cookbook by name
|
18
|
+
# Community.cookbook('apache2') #=> {...}
|
19
|
+
#
|
20
|
+
# @example Find a cookbook by name and version
|
21
|
+
# Community.cookbook('apache2', '1.0.0') #=> {...}
|
22
|
+
#
|
23
|
+
# @example Find a non-existent cookbook
|
24
|
+
# Community.cookbook('not-real') #=> Community::BadResponse
|
25
|
+
#
|
26
|
+
# @raise [Community::BadResponse]
|
27
|
+
# if the given cookbook (or cookbook version) does not exist on the community site
|
28
|
+
#
|
29
|
+
# @param [String] name
|
30
|
+
# the name of the cookbook on the community site
|
31
|
+
# @param [String] version (optional)
|
32
|
+
# the version of the cookbook to find
|
33
|
+
#
|
34
|
+
# @return [Hash]
|
35
|
+
# the hash of the cookbook
|
36
|
+
#
|
37
|
+
def cookbook(name, version = nil)
|
38
|
+
if version.nil?
|
39
|
+
connection.get("cookbooks/#{name}").body
|
40
|
+
else
|
41
|
+
connection.get("cookbooks/#{name}/versions/#{Util.version_for_url(version)}").body
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Upload a cookbook to the community site.
|
47
|
+
#
|
48
|
+
# @param [Cookbook] cookbook
|
49
|
+
# the cookbook to upload
|
50
|
+
#
|
51
|
+
def upload(cookbook)
|
52
|
+
connection.post('cookbooks', {
|
53
|
+
tarball: Faraday::UploadIO.new(cookbook.tarball, 'application/x-tar'),
|
54
|
+
cookbook: { category: cookbook.category }.to_json,
|
55
|
+
})
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
#
|
61
|
+
# The Faraday connection object with lots of pretty middleware.
|
62
|
+
#
|
63
|
+
def connection
|
64
|
+
@connection ||= Faraday.new(base_url) do |builder|
|
65
|
+
# Enable multi-part requests (for uploading)
|
66
|
+
builder.request :multipart
|
67
|
+
builder.request :url_encoded
|
68
|
+
|
69
|
+
# Encode request bodies as JSON
|
70
|
+
builder.request :json
|
71
|
+
|
72
|
+
# Add Mixlib authentication headers
|
73
|
+
builder.use Stove::Middleware::ChefAuthentication, client, key
|
74
|
+
|
75
|
+
# Handle any common errors
|
76
|
+
builder.use Stove::Middleware::Exceptions
|
77
|
+
|
78
|
+
# Decode responses as JSON if the Content-Type is json
|
79
|
+
builder.response :json
|
80
|
+
builder.response :json_fix
|
81
|
+
|
82
|
+
# Allow up to 3 redirects
|
83
|
+
builder.response :follow_redirects, limit: 3
|
84
|
+
|
85
|
+
# Log all requests and responses (useful for development)
|
86
|
+
builder.response :logger, log
|
87
|
+
|
88
|
+
# Raise errors on 40x and 50x responses
|
89
|
+
builder.response :raise_error
|
90
|
+
|
91
|
+
# Use the default adapter (Net::HTTP)
|
92
|
+
builder.adapter :net_http
|
93
|
+
|
94
|
+
# Set the User-Agent header for logging purposes
|
95
|
+
builder.headers[:user_agent] = Stove::USER_AGENT
|
96
|
+
|
97
|
+
# Set some options, such as timeouts
|
98
|
+
builder.options[:timeout] = 30
|
99
|
+
builder.options[:open_timeout] = 30
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# The name of the client to use (by default, this is the username).
|
105
|
+
#
|
106
|
+
# @return [String]
|
107
|
+
#
|
108
|
+
def client
|
109
|
+
Config[:community][:username]
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# The path to the key on disk for authentication with the community site.
|
114
|
+
# If a relative path is given, it is expanded relative to the configuration
|
115
|
+
# file on disk.
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
# the path to the key on disk
|
119
|
+
#
|
120
|
+
def key
|
121
|
+
File.expand_path(Config[:community][:key], Config.__path__)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/stove/config.rb
CHANGED
@@ -1,18 +1,67 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module Stove
|
2
|
-
class Config
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
class Config
|
5
|
+
include Mixin::Instanceable
|
6
|
+
include Mixin::Loggable
|
7
|
+
|
8
|
+
#
|
9
|
+
# Create a new configuration object. If a configuration file does not
|
10
|
+
# exist, this method will output a warning to the UI and use an empty
|
11
|
+
# hash as the data structure.
|
12
|
+
#
|
13
|
+
def initialize
|
14
|
+
log.debug("Reading from config at `#{__path__}'")
|
15
|
+
|
16
|
+
contents = File.read(__path__)
|
17
|
+
data = JSON.parse(contents, symbolize_names: true)
|
18
|
+
|
19
|
+
log.debug("Config:\n#{JSON.pretty_generate(sanitize(data))}")
|
20
|
+
|
21
|
+
@data = data
|
22
|
+
rescue Errno::ENOENT
|
23
|
+
log.warn(<<-EOH.gsub(/^ {8}/, ''))
|
24
|
+
No Stove configuration file found at `#{__path__}'. Stove will assume an
|
25
|
+
empty configuration, which may cause problems with some plugins. It is
|
26
|
+
recommended that you create a Stove configuration file as documented:
|
27
|
+
|
28
|
+
https://github.com/sethvargo/stove#installation
|
29
|
+
EOH
|
30
|
+
|
31
|
+
@data = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# This is a special key that tells me where stove lives. If you actually
|
36
|
+
# have a key in your config called +__path__+, then it sucks to be you.
|
37
|
+
#
|
38
|
+
# @return [String]
|
39
|
+
#
|
40
|
+
def __path__
|
41
|
+
@path ||= File.expand_path(ENV['STOVE_CONFIG'] || '~/.stove')
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Deletegate all method calls to the underlyng hash.
|
46
|
+
#
|
47
|
+
def method_missing(m, *args, &block)
|
48
|
+
@data.send(m, *args, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def sanitize(data)
|
54
|
+
Hash[*data.map do |key, value|
|
55
|
+
if value.is_a?(Hash)
|
56
|
+
[key, sanitize(value)]
|
57
|
+
else
|
58
|
+
if key =~ /acecss|token|password/
|
59
|
+
[key, '[FILTERED]']
|
60
|
+
else
|
61
|
+
[key, value]
|
62
|
+
end
|
15
63
|
end
|
64
|
+
end.flatten(1)]
|
16
65
|
end
|
17
66
|
end
|
18
67
|
end
|
data/lib/stove/cookbook.rb
CHANGED
@@ -1,165 +1,128 @@
|
|
1
1
|
require 'fileutils'
|
2
|
-
require 'retryable'
|
3
2
|
require 'tempfile'
|
4
3
|
require 'time'
|
5
4
|
|
6
5
|
module Stove
|
7
6
|
class Cookbook
|
8
|
-
|
7
|
+
include Mixin::Loggable
|
9
8
|
|
10
|
-
|
9
|
+
require_relative 'cookbook/metadata'
|
11
10
|
|
12
|
-
# The path to this cookbook.
|
13
11
|
#
|
14
|
-
#
|
12
|
+
# The path to this cookbook on disk.
|
13
|
+
#
|
14
|
+
# @return [Pathname]
|
15
|
+
#
|
15
16
|
attr_reader :path
|
16
17
|
|
18
|
+
#
|
17
19
|
# The name of the cookbook (must correspond to the name of the
|
18
20
|
# cookbook on the community site).
|
19
21
|
#
|
20
22
|
# @return [String]
|
23
|
+
#
|
21
24
|
attr_reader :name
|
22
25
|
|
26
|
+
#
|
23
27
|
# The version of this cookbook (originally).
|
24
28
|
#
|
25
29
|
# @return [String]
|
30
|
+
#
|
26
31
|
attr_reader :version
|
27
32
|
|
28
|
-
# The new version of the cookbook.
|
29
33
|
#
|
30
|
-
# @return [String]
|
31
|
-
attr_reader :new_version
|
32
|
-
|
33
34
|
# The metadata for this cookbook.
|
34
35
|
#
|
35
36
|
# @return [Stove::Cookbook::Metadata]
|
37
|
+
#
|
36
38
|
attr_reader :metadata
|
37
39
|
|
38
|
-
# The list of options passed to the cookbook.
|
39
40
|
#
|
40
|
-
#
|
41
|
-
|
41
|
+
# The changeset for this cookbook. This is written by the changelog
|
42
|
+
# generator and read by various plugins.
|
43
|
+
#
|
44
|
+
# @return [String, nil]
|
45
|
+
# the changeset for this cookbook
|
46
|
+
#
|
47
|
+
attr_accessor :changeset
|
42
48
|
|
49
|
+
#
|
43
50
|
# Create a new wrapper around the cookbook object.
|
44
51
|
#
|
45
|
-
# @param [
|
46
|
-
# the
|
47
|
-
|
48
|
-
|
49
|
-
@
|
50
|
-
@options = options
|
51
|
-
|
52
|
+
# @param [String] path
|
53
|
+
# the relative or absolute path to the cookbook on disk
|
54
|
+
#
|
55
|
+
def initialize(path)
|
56
|
+
@path = Pathname.new(path).expand_path
|
52
57
|
load_metadata!
|
53
58
|
end
|
54
59
|
|
60
|
+
#
|
55
61
|
# The category for this cookbook on the community site.
|
56
62
|
#
|
57
63
|
# @return [String]
|
64
|
+
#
|
58
65
|
def category
|
59
|
-
@category ||=
|
60
|
-
rescue
|
61
|
-
|
66
|
+
@category ||= Community.cookbook(name)['category']
|
67
|
+
rescue Faraday::Error::ResourceNotFound
|
68
|
+
log.warn("Cookbook `#{name}' not found on the Chef community site")
|
69
|
+
nil
|
62
70
|
end
|
63
71
|
|
72
|
+
#
|
64
73
|
# The URL for the cookbook on the Community Site.
|
65
74
|
#
|
66
75
|
# @return [String]
|
76
|
+
#
|
67
77
|
def url
|
68
|
-
|
78
|
+
URI.join(Community.base_url, 'cookbooks', name)
|
69
79
|
end
|
70
80
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
81
|
+
#
|
82
|
+
# The tag version. This is just the current version prefixed with the
|
83
|
+
# letter "v".
|
84
|
+
#
|
85
|
+
# @example Tag version for 1.0.0
|
86
|
+
# cookbook.tag_version #=> "v1.0.0"
|
87
|
+
#
|
88
|
+
# @return [String]
|
89
|
+
#
|
90
|
+
def tag_version
|
91
|
+
"v#{version}"
|
79
92
|
end
|
80
93
|
|
81
|
-
# The unreleased JIRA tickets for this cookbook.
|
82
94
|
#
|
83
|
-
#
|
84
|
-
|
85
|
-
|
95
|
+
# Deterine if this cookbook version is released on the community site
|
96
|
+
#
|
97
|
+
# @warn
|
98
|
+
# This is a fairly expensive operation and the result cannot be
|
99
|
+
# reliably cached!
|
100
|
+
#
|
101
|
+
# @return [Boolean]
|
102
|
+
# true if this cookbook at the current version exists on the community
|
103
|
+
# site, false otherwise
|
104
|
+
#
|
105
|
+
def released?
|
106
|
+
Community.cookbook(name, version)
|
107
|
+
true
|
108
|
+
rescue Faraday::Error::ResourceNotFound
|
109
|
+
false
|
86
110
|
end
|
87
111
|
|
88
112
|
#
|
89
113
|
def release!
|
90
|
-
if options[:git]
|
91
|
-
Stove::Logger.info "Running validations"
|
92
|
-
validate_git_repo!
|
93
|
-
validate_git_clean!
|
94
|
-
validate_remote_updated!
|
95
|
-
end
|
96
|
-
|
97
|
-
Stove::Logger.info "Bumping version"
|
98
|
-
version_bump
|
99
|
-
|
100
114
|
if options[:changelog]
|
101
|
-
|
115
|
+
log.info('Updating changelog')
|
102
116
|
update_changelog
|
103
117
|
end
|
104
|
-
|
105
|
-
if options[:git]
|
106
|
-
Dir.chdir(path) do
|
107
|
-
Stove::Logger.info "Committing git changes in '#{path}'"
|
108
|
-
|
109
|
-
git "add metadata.rb"
|
110
|
-
git "add CHANGELOG.md"
|
111
|
-
git "commit -m 'Version bump to #{tag_version}'"
|
112
|
-
git "push #{options[:remote]} #{options[:branch]}"
|
113
|
-
|
114
|
-
if options[:github]
|
115
|
-
Stove::Logger.info "Pushing release to GitHub"
|
116
|
-
Stove::GitHub.new(self).publish_release!
|
117
|
-
else
|
118
|
-
Stove::Logger.info "Tagging a release"
|
119
|
-
git "tag #{tag_version}"
|
120
|
-
git "push #{options[:remote]} #{tag_version}"
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
if options[:upload]
|
126
|
-
Stove::Logger.info "Uploading cookbook"
|
127
|
-
retryable(tries: 3) do
|
128
|
-
upload
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
if options[:jira]
|
133
|
-
Stove::Logger.info "Resolving JIRA issues"
|
134
|
-
resolve_jira_issues
|
135
|
-
end
|
136
|
-
|
137
|
-
if options[:devodd]
|
138
|
-
Stove::Logger.info "Bumping devodd release"
|
139
|
-
split = version.split('.').map(&:to_i)
|
140
|
-
split[2] += 1
|
141
|
-
devodd = split.join('.')
|
142
|
-
|
143
|
-
version_bump(devodd)
|
144
|
-
|
145
|
-
if options[:git]
|
146
|
-
Dir.chdir(path) do
|
147
|
-
git "add metadata.rb"
|
148
|
-
git "commit -m 'Version bump to #{tag_version}'"
|
149
|
-
git "push #{options[:remote]} #{options[:branch]}"
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def tag_version
|
156
|
-
"v#{version}"
|
157
118
|
end
|
158
119
|
|
120
|
+
#
|
159
121
|
# So there's this really really crazy bug that the tmp directory could
|
160
122
|
# be deleted mid-request...
|
161
123
|
#
|
162
124
|
# @return [File]
|
125
|
+
#
|
163
126
|
def tarball
|
164
127
|
return @tarball if @tarball && File.exists?(@tarball)
|
165
128
|
|
@@ -170,59 +133,25 @@ module Stove
|
|
170
133
|
end
|
171
134
|
|
172
135
|
#
|
173
|
-
|
174
|
-
|
175
|
-
end
|
176
|
-
|
177
|
-
# The URL for this repository on GitHub. This method automatically
|
178
|
-
# translates SSH and git:// URLs to https:// URLs.
|
136
|
+
# Bump the version in the metdata.rb to the specified
|
137
|
+
# parameter.
|
179
138
|
#
|
180
|
-
# @
|
181
|
-
|
182
|
-
@repository_url ||= git("config --get remote.#{options[:remote]}.url")
|
183
|
-
.strip
|
184
|
-
.gsub(/\.git$/, '')
|
185
|
-
.gsub(':', '/')
|
186
|
-
.gsub('@', '://')
|
187
|
-
.gsub('git://', 'https://')
|
188
|
-
end
|
189
|
-
|
190
|
-
# The set of changes for this diff/patch in markdown format.
|
139
|
+
# @param [String] new_version
|
140
|
+
# the version to bump to
|
191
141
|
#
|
192
142
|
# @return [String]
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
contents << "v#{version}"
|
198
|
-
contents << '-'*(version.length+1)
|
199
|
-
|
200
|
-
if options[:jira]
|
201
|
-
by_type = unreleased_tickets.inject({}) do |hash, ticket|
|
202
|
-
issue_type = ticket.fields.current['issuetype']['name']
|
203
|
-
hash[issue_type] ||= []
|
204
|
-
hash[issue_type] << {
|
205
|
-
number: ticket.jira_key,
|
206
|
-
details: ticket.fields.current['summary'],
|
207
|
-
}
|
143
|
+
# the new version string
|
144
|
+
#
|
145
|
+
def bump(new_version)
|
146
|
+
return true if new_version.to_s == version.to_s
|
208
147
|
|
209
|
-
|
210
|
-
|
148
|
+
metadata_path = path.join('metadata.rb')
|
149
|
+
contents = File.read(metadata_path)
|
211
150
|
|
212
|
-
|
213
|
-
contents << "### #{issue_type}"
|
214
|
-
tickets.sort { |a,b| b[:number].to_i <=> a[:number].to_i }.each do |ticket|
|
215
|
-
contents << "- **[#{ticket[:number]}](#{Stove::JIRA::JIRA_URL}/browse/#{ticket[:number]})** - #{ticket[:details]}"
|
216
|
-
end
|
217
|
-
contents << ""
|
218
|
-
end
|
219
|
-
else
|
220
|
-
contents << "_Enter CHANGELOG for #{name} (#{version}) here_"
|
221
|
-
contents << ""
|
222
|
-
end
|
151
|
+
contents.sub!(/^version(\s+)('|")#{version}('|")/, "version\\1\\2#{new_version}\\3")
|
223
152
|
|
224
|
-
|
225
|
-
|
153
|
+
File.open(metadata_path, 'w') { |f| f.write(contents) }
|
154
|
+
reload_metadata!
|
226
155
|
end
|
227
156
|
|
228
157
|
private
|
@@ -234,7 +163,7 @@ module Stove
|
|
234
163
|
# @return [String]
|
235
164
|
# the path to the metadata file
|
236
165
|
def load_metadata!
|
237
|
-
metadata_path =
|
166
|
+
metadata_path = path.join('metadata.rb')
|
238
167
|
|
239
168
|
@metadata = Stove::Cookbook::Metadata.from_file(metadata_path)
|
240
169
|
@name = @metadata.name
|
@@ -243,96 +172,5 @@ module Stove
|
|
243
172
|
metadata_path
|
244
173
|
end
|
245
174
|
alias_method :reload_metadata!, :load_metadata!
|
246
|
-
|
247
|
-
# Update the CHANGELOG with the new contents, but inserting
|
248
|
-
# the newest version's CHANGELOG at the top of the file (after
|
249
|
-
# the header)
|
250
|
-
def update_changelog
|
251
|
-
changelog = File.join(path, 'CHANGELOG.md')
|
252
|
-
contents = File.readlines(changelog)
|
253
|
-
index = contents.find_index { |line| line =~ /(--)+/ }
|
254
|
-
|
255
|
-
if index.nil?
|
256
|
-
raise Stove::InvalidChangelogError, "Your CHANGELOG does not exist" \
|
257
|
-
" or is not in a valid format!"
|
258
|
-
end
|
259
|
-
|
260
|
-
tmpfile = Tempfile.new(['changes', '.md'])
|
261
|
-
tmpfile.write(changeset)
|
262
|
-
tmpfile.rewind
|
263
|
-
response = shellout("$EDITOR #{tmpfile.path}")
|
264
|
-
|
265
|
-
unless response.success?
|
266
|
-
Stove::Logger.debug response.stdout
|
267
|
-
Stove::Logger.debug response.stderr
|
268
|
-
raise Stove::Error, response.stderr
|
269
|
-
end
|
270
|
-
|
271
|
-
@changeset = File.read(tmpfile.path).strip
|
272
|
-
|
273
|
-
contents.insert(index - 2, "\n" + @changeset + "\n\n")
|
274
|
-
File.open(changelog, 'w') { |f| f.write(contents.join('')) }
|
275
|
-
rescue SystemExit, Interrupt
|
276
|
-
raise Stove::UserCanceledError
|
277
|
-
ensure
|
278
|
-
if defined?(tmpfile)
|
279
|
-
tmpfile.close
|
280
|
-
tmpfile.unlink
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
# Bump the version in the metdata.rb to the specified
|
285
|
-
# parameter.
|
286
|
-
#
|
287
|
-
# @return [String]
|
288
|
-
# the new version string
|
289
|
-
def version_bump(bump_version = new_version)
|
290
|
-
return true if bump_version.to_s == version.to_s
|
291
|
-
|
292
|
-
metadata_path = File.join(path, 'metadata.rb')
|
293
|
-
contents = File.read(metadata_path)
|
294
|
-
|
295
|
-
contents.sub!(/^version(\s+)('|")#{version.to_s}('|")/, "version\\1\\2#{bump_version.to_s}\\3")
|
296
|
-
|
297
|
-
File.open(metadata_path, 'w') { |f| f.write(contents) }
|
298
|
-
reload_metadata!
|
299
|
-
end
|
300
|
-
|
301
|
-
# Resolve all the JIRA issues that have been merged.
|
302
|
-
def resolve_jira_issues
|
303
|
-
unreleased_tickets.collect do |ticket|
|
304
|
-
Thread.new { Stove::JIRA.comment_and_close(ticket, self) }
|
305
|
-
end.map(&:join)
|
306
|
-
end
|
307
|
-
|
308
|
-
# Validate that the current working directory is git repo.
|
309
|
-
#
|
310
|
-
# @raise [Stove::GitError::NotARepo]
|
311
|
-
# if this is not currently a git repo
|
312
|
-
def validate_git_repo!
|
313
|
-
Dir.chdir(path) do
|
314
|
-
raise Stove::GitError::NotARepo unless git_repo?
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
# Validate that the current.
|
319
|
-
#
|
320
|
-
# @raise [Stove::GitError::DirtyRepo]
|
321
|
-
# if the current working directory is not clean
|
322
|
-
def validate_git_clean!
|
323
|
-
Dir.chdir(path) do
|
324
|
-
raise Stove::GitError::DirtyRepo unless git_repo_clean?
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
# Validate that the remote git repository is up to date.
|
329
|
-
#
|
330
|
-
# @raise [Stove::GitError::OutOfSync]
|
331
|
-
# if the current git repo is not up to date with the remote
|
332
|
-
def validate_remote_updated!
|
333
|
-
Dir.chdir(path) do
|
334
|
-
raise Stove::GitError::OutOfSync unless git_remote_uptodate?(options)
|
335
|
-
end
|
336
|
-
end
|
337
175
|
end
|
338
176
|
end
|