stove 1.1.2 → 2.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|