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
@@ -134,7 +134,7 @@ module Stove
|
|
134
134
|
self.instance_eval(IO.read(path), path, 1)
|
135
135
|
self
|
136
136
|
else
|
137
|
-
raise
|
137
|
+
raise Error::MetadataNotFound.new(path: path)
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
@@ -144,9 +144,13 @@ module Stove
|
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
def version(arg =
|
148
|
-
|
149
|
-
|
147
|
+
def version(arg = UNSET_VALUE)
|
148
|
+
if arg == UNSET_VALUE
|
149
|
+
@version.to_s
|
150
|
+
else
|
151
|
+
@version = Solve::Version.new(arg)
|
152
|
+
@version.to_s
|
153
|
+
end
|
150
154
|
end
|
151
155
|
|
152
156
|
def to_hash
|
@@ -176,15 +180,16 @@ module Stove
|
|
176
180
|
end
|
177
181
|
|
178
182
|
private
|
179
|
-
def set_or_return(symbol, arg)
|
180
|
-
iv_symbol = "@#{symbol.to_s}".to_sym
|
181
183
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
184
|
+
def set_or_return(symbol, arg)
|
185
|
+
iv_symbol = "@#{symbol.to_s}".to_sym
|
186
|
+
|
187
|
+
if arg.nil? && self.instance_variable_defined?(iv_symbol)
|
188
|
+
self.instance_variable_get(iv_symbol)
|
189
|
+
else
|
190
|
+
self.instance_variable_set(iv_symbol, arg)
|
187
191
|
end
|
192
|
+
end
|
188
193
|
end
|
189
194
|
end
|
190
195
|
end
|
data/lib/stove/error.rb
CHANGED
@@ -1,118 +1,24 @@
|
|
1
1
|
module Stove
|
2
|
-
|
3
|
-
class
|
4
|
-
def
|
5
|
-
|
6
|
-
define_singleton_method(:exit_code) { code }
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
set_exit_code 100
|
11
|
-
end
|
12
|
-
|
13
|
-
class InvalidVersionError < Error
|
14
|
-
set_exit_code 101
|
15
|
-
|
16
|
-
def message
|
17
|
-
'You must specify a valid version!'
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class MetadataNotFound < Error
|
22
|
-
set_exit_code 102
|
2
|
+
module Error
|
3
|
+
class StoveError < StandardError
|
4
|
+
def initialize(options = {})
|
5
|
+
return super(options[:_message]) if options[:_message]
|
23
6
|
|
24
|
-
|
25
|
-
|
26
|
-
end
|
7
|
+
class_name = self.class.to_s.split('::').last
|
8
|
+
error_key = Util.underscore(class_name)
|
27
9
|
|
28
|
-
|
29
|
-
"No metadata.rb found at: '#{@filepath}'"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class CookbookCategoryNotFound < Error
|
34
|
-
set_exit_code 110
|
35
|
-
|
36
|
-
def message
|
37
|
-
'The cookbook\'s category could not be inferred from the community site. ' <<
|
38
|
-
'If this is a new cookbook, you must specify the category with the ' <<
|
39
|
-
'--category flag.'
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class UserCanceledError < Error
|
44
|
-
set_exit_code 120
|
45
|
-
|
46
|
-
def message
|
47
|
-
'Action canceled by user!'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
class GitError < Error
|
52
|
-
set_exit_code 130
|
53
|
-
|
54
|
-
def message
|
55
|
-
'Git Error: ' + super
|
56
|
-
end
|
57
|
-
|
58
|
-
class NotARepo < GitError
|
59
|
-
set_exit_code 131
|
60
|
-
|
61
|
-
def message
|
62
|
-
'Not a git repo!'
|
10
|
+
super I18n.t("stove.errors.#{error_key}", options)
|
63
11
|
end
|
64
12
|
end
|
65
13
|
|
66
|
-
class
|
67
|
-
|
68
|
-
|
69
|
-
def message
|
70
|
-
'You have untracked files!'
|
14
|
+
class ValidationFailed < StoveError
|
15
|
+
def initialize(klass, id, options = {})
|
16
|
+
super _message: I18n.t("stove.validations.#{klass}.#{id}", options)
|
71
17
|
end
|
72
18
|
end
|
73
19
|
|
74
|
-
class
|
75
|
-
|
76
|
-
|
77
|
-
def message
|
78
|
-
'Your remote repository is out of sync!'
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class UploadError < Error
|
84
|
-
set_exit_code 140
|
85
|
-
|
86
|
-
def initialize(response)
|
87
|
-
@response = response
|
88
|
-
end
|
89
|
-
|
90
|
-
def message
|
91
|
-
"The following errors occured when uploading:\n" <<
|
92
|
-
(@response.parsed_response['error_messages'] || []).map do |error|
|
93
|
-
" - #{error}"
|
94
|
-
end.join("\n")
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
class BadResponse < Error
|
99
|
-
set_exit_code 150
|
100
|
-
|
101
|
-
def initialize(response)
|
102
|
-
@response = response
|
103
|
-
end
|
104
|
-
|
105
|
-
def message
|
106
|
-
"The following errors occured when making the request:\n" <<
|
107
|
-
@response.parsed_response
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
class AbstractFunction < Error
|
112
|
-
set_exit_code 160
|
113
|
-
end
|
114
|
-
|
115
|
-
class InvalidChangelogFormat < Error
|
116
|
-
set_exit_code 170
|
20
|
+
class GitFailed < StoveError; end
|
21
|
+
class MetadataNotFound < StoveError; end
|
22
|
+
class ServerUnavailable < StoveError; end
|
117
23
|
end
|
118
24
|
end
|
data/lib/stove/filter.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Stove
|
2
|
+
class Filter
|
3
|
+
include Mixin::Insideable
|
4
|
+
include Mixin::Loggable
|
5
|
+
|
6
|
+
#
|
7
|
+
# The class that created this filter.
|
8
|
+
#
|
9
|
+
# @return [~Plugin::Base]
|
10
|
+
#
|
11
|
+
attr_reader :klass
|
12
|
+
|
13
|
+
#
|
14
|
+
# The message given by the filter.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
#
|
18
|
+
attr_reader :message
|
19
|
+
|
20
|
+
#
|
21
|
+
# The block captured by the filter.
|
22
|
+
#
|
23
|
+
# @return [Proc]
|
24
|
+
#
|
25
|
+
attr_reader :block
|
26
|
+
|
27
|
+
#
|
28
|
+
# Create a new filter object.
|
29
|
+
#
|
30
|
+
# @param [~Plugin::Base] klass
|
31
|
+
# the class that created this filter
|
32
|
+
# @param [String] message
|
33
|
+
# the message given by the filter
|
34
|
+
# @param [Proc] block
|
35
|
+
# the block captured by this filter
|
36
|
+
#
|
37
|
+
def initialize(klass, message, &block)
|
38
|
+
@klass = klass
|
39
|
+
@message = message
|
40
|
+
@block = block
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Execute this filter in the context of the creating class, inside the
|
45
|
+
# given cookbook's path.
|
46
|
+
#
|
47
|
+
# @param [Cookbook]
|
48
|
+
# the cookbook to run this filter against
|
49
|
+
#
|
50
|
+
def run(cookbook, options = {})
|
51
|
+
log.info(message)
|
52
|
+
instance = klass.new(cookbook, options)
|
53
|
+
|
54
|
+
inside(cookbook) do
|
55
|
+
instance.instance_eval(&block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/stove/jira.rb
CHANGED
@@ -1,43 +1,87 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
3
|
|
4
4
|
module Stove
|
5
5
|
class JIRA
|
6
|
-
|
6
|
+
include Mixin::Instanceable
|
7
|
+
include Mixin::Loggable
|
8
|
+
include Mixin::Optionable
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
option :base_url,
|
11
|
+
ENV['JIRA_URL'] || 'https://tickets.opscode.com/rest/api/2'
|
12
|
+
|
13
|
+
def issue(key, options = {})
|
14
|
+
connection.get("issue/#{key}", options).body
|
15
|
+
end
|
16
|
+
|
17
|
+
def search(query = {})
|
18
|
+
jql = query.map { |k,v| %Q|#{k} = "#{v}"| }.join(' AND ')
|
19
|
+
connection.get('search', jql: jql).body
|
12
20
|
end
|
13
21
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
].join(' AND ')
|
22
|
-
Stove::Logger.debug "JQL: #{jql.inspect}"
|
23
|
-
|
24
|
-
Jiralicious.search(jql).issues
|
22
|
+
def close_and_comment(key, comment)
|
23
|
+
transitions = issue(key, expand: 'transitions')['transitions']
|
24
|
+
close = transitions.first { |transition| transition['name'] == 'Close' }
|
25
|
+
|
26
|
+
if close.nil?
|
27
|
+
log.warn("Issue #{key} does not have a `Close' transition")
|
28
|
+
return
|
25
29
|
end
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
connection.post("issue/#{key}/transitions", {
|
32
|
+
transition: { id: close['id'] },
|
33
|
+
update: {
|
34
|
+
comment: [
|
35
|
+
{ add: { body: comment.to_s } }
|
36
|
+
]
|
37
|
+
},
|
38
|
+
fields: {
|
39
|
+
resolution: {
|
40
|
+
name: 'Fixed'
|
41
|
+
},
|
42
|
+
assignee: {
|
43
|
+
name: nil
|
44
|
+
}
|
45
|
+
}
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def connection
|
52
|
+
@connection ||= Faraday.new(base_url) do |builder|
|
53
|
+
# Encode request bodies as JSON
|
54
|
+
builder.request :json
|
55
|
+
|
56
|
+
# Add basic authentication information
|
57
|
+
builder.request :basic_auth, Stove::Config[:jira][:username],
|
58
|
+
Stove::Config[:jira][:password]
|
59
|
+
|
60
|
+
# Handle any common errors
|
61
|
+
builder.use Stove::Middleware::Exceptions
|
62
|
+
|
63
|
+
# Decode responses as JSON if the Content-Type is json
|
64
|
+
builder.response :json
|
65
|
+
builder.response :json_fix
|
66
|
+
|
67
|
+
# Allow up to 3 redirects
|
68
|
+
builder.response :follow_redirects, limit: 3
|
69
|
+
|
70
|
+
# Log all requests and responses (useful for development)
|
71
|
+
builder.response :logger, log
|
72
|
+
|
73
|
+
# Raise errors on 40x and 50x responses
|
74
|
+
builder.response :raise_error
|
75
|
+
|
76
|
+
# Use the default adapter (Net::HTTP)
|
77
|
+
builder.adapter :net_http
|
35
78
|
|
36
|
-
|
37
|
-
|
38
|
-
end.last
|
79
|
+
# Set the User-Agent header for logging purposes
|
80
|
+
builder.headers[:user_agent] = Stove::USER_AGENT
|
39
81
|
|
40
|
-
|
82
|
+
# Set some options, such as timeouts
|
83
|
+
builder.options[:timeout] = 30
|
84
|
+
builder.options[:open_timeout] = 30
|
41
85
|
end
|
42
86
|
end
|
43
87
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module Stove
|
4
|
+
class Middleware::ChefAuthentication < Faraday::Middleware
|
5
|
+
dependency do
|
6
|
+
require 'mixlib/authentication/signedheaderauth'
|
7
|
+
require 'openssl'
|
8
|
+
require 'uri'
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# @param [Faraday::Application] app
|
13
|
+
# @param [String] client
|
14
|
+
# the name of the client to use for Chef
|
15
|
+
# @param [OpenSSL::PKey::RSA] key
|
16
|
+
# the RSA private key to sign with
|
17
|
+
#
|
18
|
+
def initialize(app, client, key)
|
19
|
+
super(app)
|
20
|
+
|
21
|
+
@client = client
|
22
|
+
@key = OpenSSL::PKey::RSA.new(File.read(key))
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
env[:request_headers].merge!(signing_object(env))
|
27
|
+
@app.call(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def signing_object(env)
|
33
|
+
params = {
|
34
|
+
:http_method => env[:method],
|
35
|
+
:timestamp => Time.now.utc.iso8601,
|
36
|
+
:user_id => @client,
|
37
|
+
:path => env[:url].path,
|
38
|
+
:body => env[:body] || '',
|
39
|
+
}
|
40
|
+
|
41
|
+
# Royal fucking hack
|
42
|
+
# 1. (n.) This code sample
|
43
|
+
# 2. (v.) Having to decompose a Faraday response because Mixlib
|
44
|
+
# Authentication couldn't get a date to the prom
|
45
|
+
if env[:body] && env[:body].is_a?(Faraday::CompositeReadIO)
|
46
|
+
file = env[:body]
|
47
|
+
.instance_variable_get(:@parts)
|
48
|
+
.first { |part| part.is_a?(Faraday::Parts::FilePart) }
|
49
|
+
.instance_variable_get(:@io)
|
50
|
+
.instance_variable_get(:@ios)[1]
|
51
|
+
.instance_variable_get(:@local_path)
|
52
|
+
|
53
|
+
params[:file] = File.new(file)
|
54
|
+
end
|
55
|
+
|
56
|
+
object = Mixlib::Authentication::SignedHeaderAuth.signing_object(params)
|
57
|
+
object.sign(@key)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Stove
|
2
|
+
class Middleware::Exceptions < Faraday::Middleware
|
3
|
+
include Mixin::Loggable
|
4
|
+
|
5
|
+
def call(env)
|
6
|
+
begin
|
7
|
+
@app.call(env)
|
8
|
+
rescue Faraday::Error::ConnectionFailed
|
9
|
+
url = env[:url].to_s.gsub(env[:url].path, '')
|
10
|
+
raise Error::ServerUnavailable.new(url: url)
|
11
|
+
rescue Faraday::Error::ClientError => e
|
12
|
+
log.debug(env.inspect)
|
13
|
+
raise
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Stove
|
2
|
+
module Mixin::Filterable
|
3
|
+
def before(action, message, &block)
|
4
|
+
Runner.filters[action][:before] << Filter.new(self, message, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def after(action, message, &block)
|
8
|
+
Runner.filters[action][:after] << Filter.new(self, message, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|