stove 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +6 -1
- data/CHANGELOG.md +20 -0
- data/README.md +34 -80
- data/Rakefile +9 -1
- data/bin/bake +2 -0
- data/bin/stove +4 -0
- data/features/plugins/community.feature +11 -26
- data/features/plugins/git.feature +17 -6
- data/features/step_definitions/community_steps.rb +3 -1
- data/features/step_definitions/config_steps.rb +4 -21
- data/features/step_definitions/git_steps.rb +38 -1
- data/features/support/env.rb +17 -11
- data/features/support/stove/git.rb +28 -8
- data/lib/stove/cli.rb +72 -53
- data/lib/stove/community.rb +16 -67
- data/lib/stove/config.rb +55 -46
- data/lib/stove/cookbook/metadata.rb +3 -5
- data/lib/stove/cookbook.rb +2 -41
- data/lib/stove/error.rb +37 -8
- data/lib/stove/filter.rb +2 -1
- data/lib/stove/mixins/instanceable.rb +3 -2
- data/lib/stove/mixins/validatable.rb +5 -1
- data/lib/stove/packager.rb +11 -3
- data/lib/stove/plugins/base.rb +26 -13
- data/lib/stove/plugins/community.rb +3 -7
- data/lib/stove/plugins/git.rb +27 -30
- data/lib/stove/rake_task.rb +3 -63
- data/lib/stove/runner.rb +16 -65
- data/lib/stove/validator.rb +7 -6
- data/lib/stove/version.rb +1 -1
- data/lib/stove.rb +3 -21
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/error_spec.rb +148 -0
- data/stove.gemspec +10 -14
- data/templates/errors/abstract_method.erb +5 -0
- data/templates/errors/community_category_validation_failed.erb +5 -0
- data/templates/errors/community_key_validation_failed.erb +3 -0
- data/templates/errors/community_username_validation_failed.erb +3 -0
- data/templates/errors/git_clean_validation_failed.erb +1 -0
- data/templates/errors/git_failed.erb +5 -0
- data/templates/errors/git_repository_validation_failed.erb +3 -0
- data/templates/errors/git_up_to_date_validation_failed.erb +7 -0
- data/templates/errors/metadata_not_found.erb +1 -0
- data/templates/errors/server_unavailable.erb +1 -0
- data/templates/errors/stove_error.erb +1 -0
- metadata +32 -114
- data/features/actions/bump.feature +0 -22
- data/features/actions/changelog.feature +0 -52
- data/features/actions/dev.feature +0 -18
- data/features/actions/upload.feature +0 -26
- data/features/rake.feature +0 -15
- data/features/step_definitions/cli_steps.rb +0 -3
- data/lib/stove/actions/base.rb +0 -21
- data/lib/stove/actions/bump.rb +0 -25
- data/lib/stove/actions/changelog.rb +0 -71
- data/lib/stove/actions/dev.rb +0 -22
- data/lib/stove/actions/finish.rb +0 -8
- data/lib/stove/actions/start.rb +0 -7
- data/lib/stove/actions/upload.rb +0 -11
- data/lib/stove/jira.rb +0 -88
- data/lib/stove/middlewares/chef_authentication.rb +0 -60
- data/lib/stove/middlewares/exceptions.rb +0 -17
- data/lib/stove/mixins/filterable.rb +0 -11
- data/lib/stove/plugins/github.rb +0 -107
- data/lib/stove/plugins/jira.rb +0 -72
- data/locales/en.yml +0 -230
data/lib/stove/cli.rb
CHANGED
@@ -15,6 +15,29 @@ module Stove
|
|
15
15
|
# Parse the options hash
|
16
16
|
option_parser.parse!(@argv)
|
17
17
|
|
18
|
+
# Stupid special use cases
|
19
|
+
if @argv.first == 'login'
|
20
|
+
if options[:username].nil? || options[:username].to_s.strip.empty?
|
21
|
+
raise "Missing argument `--username'!"
|
22
|
+
end
|
23
|
+
|
24
|
+
if options[:key].nil? || options[:key].to_s.strip.empty?
|
25
|
+
raise "Missing argument `--key'!"
|
26
|
+
end
|
27
|
+
|
28
|
+
Config.username = options[:username]
|
29
|
+
Config.key = options[:key]
|
30
|
+
Config.save
|
31
|
+
|
32
|
+
@stdout.puts "Successfully saved config to `#{Config.__path__}'!"
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
# Override configs
|
37
|
+
Config.endpoint = options[:endpoint] if options[:endpoint]
|
38
|
+
Config.username = options[:username] if options[:username]
|
39
|
+
Config.key = options[:key] if options[:key]
|
40
|
+
|
18
41
|
# Set the log level
|
19
42
|
Stove.log_level = options[:log_level]
|
20
43
|
|
@@ -26,12 +49,6 @@ module Stove
|
|
26
49
|
log.info("Options: #{options.inspect}")
|
27
50
|
log.info("ARGV: #{@argv.inspect}")
|
28
51
|
|
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]
|
33
|
-
end
|
34
|
-
|
35
52
|
# Make a new cookbook object - this will raise an exception if there is
|
36
53
|
# no cookbook at the given path
|
37
54
|
cookbook = Cookbook.new(options[:path])
|
@@ -42,7 +59,8 @@ module Stove
|
|
42
59
|
end
|
43
60
|
|
44
61
|
# Now execute the actual runners (validations and errors might occur)
|
45
|
-
Runner.
|
62
|
+
runner = Runner.new(cookbook, options)
|
63
|
+
runner.run
|
46
64
|
|
47
65
|
# If we got this far, everything was successful :)
|
48
66
|
@kernel.exit(0)
|
@@ -66,55 +84,60 @@ module Stove
|
|
66
84
|
#
|
67
85
|
def option_parser
|
68
86
|
@option_parser ||= OptionParser.new do |opts|
|
69
|
-
opts.banner = 'Usage:
|
87
|
+
opts.banner = 'Usage: stove [OPTIONS]'
|
70
88
|
|
71
89
|
opts.separator ''
|
72
|
-
opts.separator '
|
90
|
+
opts.separator 'Plugins:'
|
73
91
|
|
74
|
-
|
75
|
-
|
76
|
-
opts.on("--[no-]#{action.id}", action.description) do |v|
|
77
|
-
options[action.id.to_sym] = v
|
78
|
-
end
|
92
|
+
opts.on('--no-git', 'Do not use the git plugin') do
|
93
|
+
options[:no_git] = true
|
79
94
|
end
|
80
95
|
|
81
96
|
opts.separator ''
|
82
|
-
opts.separator '
|
97
|
+
opts.separator 'Upload Options:'
|
83
98
|
|
84
|
-
|
85
|
-
|
86
|
-
opts.on("--[no-]#{plugin.id}", plugin.description) do |v|
|
87
|
-
options[plugin.id.to_sym] = v
|
88
|
-
end
|
99
|
+
opts.on('--endpoint [URL]', 'Upload URL endpoint') do |v|
|
100
|
+
options[:endpoint] = v
|
89
101
|
end
|
90
102
|
|
91
|
-
opts.
|
92
|
-
|
93
|
-
|
94
|
-
opts.on('--locale [LANGUAGE]', 'Change the language to output messages') do |locale|
|
95
|
-
I18n.locale = locale
|
103
|
+
opts.on('--username [USERNAME]', 'Username to authenticate with') do |v|
|
104
|
+
options[:username] = v
|
96
105
|
end
|
97
106
|
|
98
|
-
opts.on('--
|
99
|
-
options[:
|
107
|
+
opts.on('--key [PATH]', 'Path to the private key on disk') do |v|
|
108
|
+
options[:key] = v
|
100
109
|
end
|
101
110
|
|
102
|
-
opts.on('--category [CATEGORY]', '
|
111
|
+
opts.on('--category [CATEGORY]', 'Category for the cookbook') do |v|
|
103
112
|
options[:category] = v
|
104
113
|
end
|
105
114
|
|
106
|
-
opts.
|
107
|
-
|
108
|
-
end
|
115
|
+
opts.separator ''
|
116
|
+
opts.separator 'Git Options:'
|
109
117
|
|
110
|
-
opts.on('--remote [REMOTE]', '
|
118
|
+
opts.on('--remote [REMOTE]', 'Name of the git remote') do |v|
|
111
119
|
options[:remote] = v
|
112
120
|
end
|
113
121
|
|
114
|
-
opts.on('--branch [BRANCH]', '
|
122
|
+
opts.on('--branch [BRANCH]', 'Name of the git branch') do |v|
|
115
123
|
options[:branch] = v
|
116
124
|
end
|
117
125
|
|
126
|
+
opts.on('--sign', 'Sign git tags') do
|
127
|
+
options[:sign] = true
|
128
|
+
end
|
129
|
+
|
130
|
+
opts.separator ''
|
131
|
+
opts.separator 'Global Options:'
|
132
|
+
|
133
|
+
opts.on('--log-level [LEVEL]', 'Set the log verbosity') do |v|
|
134
|
+
options[:log_level] = v
|
135
|
+
end
|
136
|
+
|
137
|
+
opts.on('--path [PATH]', 'Change the path to a cookbook') do |v|
|
138
|
+
options[:path] = v
|
139
|
+
end
|
140
|
+
|
118
141
|
opts.on_tail('-h', '--help', 'Show this message') do
|
119
142
|
puts opts
|
120
143
|
exit
|
@@ -132,26 +155,22 @@ module Stove
|
|
132
155
|
#
|
133
156
|
# @return [Hash]
|
134
157
|
def options
|
135
|
-
@options ||=
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
!!(ENV['CLI_DEFAULT'] =~ /^(true|t|yes|y|1)$/i)
|
152
|
-
else
|
153
|
-
true
|
154
|
-
end
|
158
|
+
@options ||= {
|
159
|
+
# Upload options
|
160
|
+
:endpoint => nil,
|
161
|
+
:username => Config.username,
|
162
|
+
:key => Config.key,
|
163
|
+
:category => nil,
|
164
|
+
|
165
|
+
# Git options
|
166
|
+
:remote => 'origin',
|
167
|
+
:branch => 'master',
|
168
|
+
:sign => false,
|
169
|
+
|
170
|
+
# Global options
|
171
|
+
:log_level => :warn,
|
172
|
+
:path => Dir.pwd,
|
173
|
+
}
|
155
174
|
end
|
156
175
|
end
|
157
176
|
end
|
data/lib/stove/community.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
require '
|
2
|
-
require 'faraday_middleware'
|
1
|
+
require 'chef-api'
|
3
2
|
|
4
3
|
module Stove
|
5
4
|
class Community
|
6
5
|
include Mixin::Instanceable
|
7
6
|
include Mixin::Optionable
|
8
|
-
include Logify
|
9
7
|
|
10
|
-
|
11
|
-
|
8
|
+
#
|
9
|
+
# The default endpoint where the community site lives.
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
#
|
13
|
+
DEFAULT_ENDPOINT = 'https://supermarket.getchef.com/api/v1'
|
12
14
|
|
13
15
|
#
|
14
16
|
# Get and cache a community cookbook's JSON response from the given name
|
@@ -36,9 +38,9 @@ module Stove
|
|
36
38
|
#
|
37
39
|
def cookbook(name, version = nil)
|
38
40
|
if version.nil?
|
39
|
-
connection.get("cookbooks/#{name}")
|
41
|
+
connection.get("cookbooks/#{name}")
|
40
42
|
else
|
41
|
-
connection.get("cookbooks/#{name}/versions/#{Util.version_for_url(version)}")
|
43
|
+
connection.get("cookbooks/#{name}/versions/#{Util.version_for_url(version)}")
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
@@ -50,75 +52,22 @@ module Stove
|
|
50
52
|
#
|
51
53
|
def upload(cookbook)
|
52
54
|
connection.post('cookbooks', {
|
53
|
-
tarball
|
54
|
-
cookbook
|
55
|
+
'tarball' => File.open(cookbook.tarball, 'rb'),
|
56
|
+
'cookbook' => { 'category' => cookbook.category }.to_json,
|
55
57
|
})
|
56
58
|
end
|
57
59
|
|
58
60
|
private
|
59
61
|
|
60
62
|
#
|
61
|
-
# The
|
63
|
+
# The ChefAPI connection object with lots of pretty middleware.
|
62
64
|
#
|
63
65
|
def connection
|
64
|
-
@connection ||=
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
66
|
+
@connection ||= ChefAPI::Connection.new do |conn|
|
67
|
+
conn.endpoint = ENV['STOVE_ENDPOINT'] || Config.endpoint || DEFAULT_ENDPOINT
|
68
|
+
conn.client = ENV['STOVE_USERNAME'] || Config.username
|
69
|
+
conn.key = ENV['STOVE_KEY'] || Config.key
|
100
70
|
end
|
101
71
|
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
72
|
end
|
124
73
|
end
|
data/lib/stove/config.rb
CHANGED
@@ -1,67 +1,76 @@
|
|
1
|
+
require 'fileutils'
|
1
2
|
require 'json'
|
2
3
|
|
3
4
|
module Stove
|
4
5
|
class Config
|
5
|
-
include Mixin::Instanceable
|
6
6
|
include Logify
|
7
|
+
include Mixin::Instanceable
|
8
|
+
|
9
|
+
def method_missing(m, *args, &block)
|
10
|
+
if m.to_s.end_with?('=')
|
11
|
+
__set__(m.to_s.chomp('='), args.first)
|
12
|
+
else
|
13
|
+
__get__(m)
|
14
|
+
end
|
15
|
+
end
|
7
16
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# hash as the data structure.
|
12
|
-
#
|
13
|
-
def initialize
|
14
|
-
log.debug("Reading from config at `#{__path__}'")
|
17
|
+
def respond_to_missing?(m, include_private = false)
|
18
|
+
__has__?(m) || super
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
def save
|
22
|
+
FileUtils.mkdir_p(File.dirname(__path__))
|
23
|
+
File.open(__path__, 'w') do |f|
|
24
|
+
f.write(JSON.fast_generate(__raw__))
|
25
|
+
end
|
26
|
+
end
|
18
27
|
|
19
|
-
|
28
|
+
def to_s
|
29
|
+
"#<#{self.class.name} #{__raw__.to_s}>"
|
30
|
+
end
|
20
31
|
|
21
|
-
|
22
|
-
|
23
|
-
|
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:
|
32
|
+
def inspect
|
33
|
+
"#<#{self.class.name} #{__raw__.inspect}>"
|
34
|
+
end
|
27
35
|
|
28
|
-
|
29
|
-
|
36
|
+
def __get__(key)
|
37
|
+
__raw__[key.to_sym]
|
38
|
+
end
|
30
39
|
|
31
|
-
|
40
|
+
def __has__?(key)
|
41
|
+
__raw__.key?(key.to_sym)
|
42
|
+
end
|
43
|
+
|
44
|
+
def __set__(key, value)
|
45
|
+
__raw__[key.to_sym] = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def __unset__(key)
|
49
|
+
__raw__.delete(key.to_sym)
|
32
50
|
end
|
33
51
|
|
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
52
|
def __path__
|
41
53
|
@path ||= File.expand_path(ENV['STOVE_CONFIG'] || '~/.stove')
|
42
54
|
end
|
43
55
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
56
|
+
def __raw__
|
57
|
+
return @__raw__ if @__raw__
|
58
|
+
|
59
|
+
@__raw__ = JSON.parse(File.read(__path__), symbolize_names: true)
|
60
|
+
|
61
|
+
if @__raw__.key?(:community)
|
62
|
+
$stderr.puts "Detected old Stove configuration file, converting..."
|
63
|
+
|
64
|
+
@__raw__ = {
|
65
|
+
:username => @__raw__[:community][:username],
|
66
|
+
:key => @__raw__[:community][:key],
|
67
|
+
}
|
68
|
+
end
|
50
69
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
if value.is_a?(Hash)
|
56
|
-
[key, sanitize(value)]
|
57
|
-
else
|
58
|
-
if key =~ /access|token|password/
|
59
|
-
[key, '[FILTERED]']
|
60
|
-
else
|
61
|
-
[key, value]
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end.flatten(1)]
|
70
|
+
@__raw__
|
71
|
+
rescue Errno::ENOENT => e
|
72
|
+
log.warn { "No config file found at `#{__path__}'!" }
|
73
|
+
@__raw__ = {}
|
65
74
|
end
|
66
75
|
end
|
67
76
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'json'
|
2
|
-
require 'solve'
|
3
2
|
|
4
3
|
module Stove
|
5
4
|
class Cookbook
|
@@ -41,7 +40,7 @@ module Stove
|
|
41
40
|
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
42
41
|
def #{field}(thing, *args)
|
43
42
|
version = args.first
|
44
|
-
@#{instance_variable}[thing] =
|
43
|
+
@#{instance_variable}[thing] = version.to_s
|
45
44
|
@#{instance_variable}[thing]
|
46
45
|
end
|
47
46
|
EOM
|
@@ -146,10 +145,9 @@ module Stove
|
|
146
145
|
|
147
146
|
def version(arg = UNSET_VALUE)
|
148
147
|
if arg == UNSET_VALUE
|
149
|
-
@version
|
148
|
+
@version
|
150
149
|
else
|
151
|
-
@version =
|
152
|
-
@version.to_s
|
150
|
+
@version = arg.to_s
|
153
151
|
end
|
154
152
|
end
|
155
153
|
|
data/lib/stove/cookbook.rb
CHANGED
@@ -72,20 +72,11 @@ module Stove
|
|
72
72
|
#
|
73
73
|
def category
|
74
74
|
@category ||= Community.cookbook(name)['category']
|
75
|
-
rescue
|
75
|
+
rescue ChefAPI::Error::HTTPError
|
76
76
|
log.warn("Cookbook `#{name}' not found on the Chef community site")
|
77
77
|
nil
|
78
78
|
end
|
79
79
|
|
80
|
-
#
|
81
|
-
# The URL for the cookbook on the Community Site.
|
82
|
-
#
|
83
|
-
# @return [String]
|
84
|
-
#
|
85
|
-
def url
|
86
|
-
URI.join(Community.base_url, 'cookbooks', name)
|
87
|
-
end
|
88
|
-
|
89
80
|
#
|
90
81
|
# The tag version. This is just the current version prefixed with the
|
91
82
|
# letter "v".
|
@@ -113,18 +104,10 @@ module Stove
|
|
113
104
|
def released?
|
114
105
|
Community.cookbook(name, version)
|
115
106
|
true
|
116
|
-
rescue
|
107
|
+
rescue ChefAPI::Error::HTTPNotFound
|
117
108
|
false
|
118
109
|
end
|
119
110
|
|
120
|
-
#
|
121
|
-
def release!
|
122
|
-
if options[:changelog]
|
123
|
-
log.info('Updating changelog')
|
124
|
-
update_changelog
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
111
|
#
|
129
112
|
# So there's this really really crazy bug that the tmp directory could
|
130
113
|
# be deleted mid-request...
|
@@ -140,28 +123,6 @@ module Stove
|
|
140
123
|
@tarball
|
141
124
|
end
|
142
125
|
|
143
|
-
#
|
144
|
-
# Bump the version in the metdata.rb to the specified
|
145
|
-
# parameter.
|
146
|
-
#
|
147
|
-
# @param [String] new_version
|
148
|
-
# the version to bump to
|
149
|
-
#
|
150
|
-
# @return [String]
|
151
|
-
# the new version string
|
152
|
-
#
|
153
|
-
def bump(new_version)
|
154
|
-
return true if new_version.to_s == version.to_s
|
155
|
-
|
156
|
-
metadata_path = path.join('metadata.rb')
|
157
|
-
contents = File.read(metadata_path)
|
158
|
-
|
159
|
-
contents.sub!(/^version(\s+)('|")#{version}('|")/, "version\\1\\2#{new_version}\\3")
|
160
|
-
|
161
|
-
File.open(metadata_path, 'w') { |f| f.write(contents) }
|
162
|
-
reload_metadata!
|
163
|
-
end
|
164
|
-
|
165
126
|
private
|
166
127
|
# Load the metadata and set the @metadata instance variable.
|
167
128
|
#
|
data/lib/stove/error.rb
CHANGED
@@ -1,24 +1,53 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
1
3
|
module Stove
|
2
4
|
module Error
|
5
|
+
class ErrorBinding
|
6
|
+
def initialize(options = {})
|
7
|
+
options.each do |key, value|
|
8
|
+
instance_variable_set(:"@#{key}", value)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_binding
|
13
|
+
binding
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
3
17
|
class StoveError < StandardError
|
4
18
|
def initialize(options = {})
|
5
|
-
|
19
|
+
@options = options
|
20
|
+
@filename = options.delete(:_template)
|
6
21
|
|
7
|
-
|
8
|
-
|
22
|
+
super()
|
23
|
+
end
|
9
24
|
|
10
|
-
|
25
|
+
def message
|
26
|
+
erb = ERB.new(File.read(template))
|
27
|
+
erb.result(ErrorBinding.new(@options).get_binding)
|
11
28
|
end
|
12
|
-
|
29
|
+
alias_method :to_s, :message
|
13
30
|
|
14
|
-
|
15
|
-
|
16
|
-
|
31
|
+
private
|
32
|
+
|
33
|
+
def template
|
34
|
+
class_name = self.class.to_s.split('::').last
|
35
|
+
filename = @filename || Util.underscore(class_name)
|
36
|
+
Stove.root.join('templates', 'errors', "#{filename}.erb")
|
17
37
|
end
|
18
38
|
end
|
19
39
|
|
20
40
|
class GitFailed < StoveError; end
|
21
41
|
class MetadataNotFound < StoveError; end
|
22
42
|
class ServerUnavailable < StoveError; end
|
43
|
+
|
44
|
+
# Validations
|
45
|
+
class ValidationFailed < StoveError; end
|
46
|
+
class CommunityCategoryValidationFailed < ValidationFailed; end
|
47
|
+
class CommunityKeyValidationFailed < ValidationFailed; end
|
48
|
+
class CommunityUsernameValidationFailed < ValidationFailed; end
|
49
|
+
class GitCleanValidationFailed < ValidationFailed; end
|
50
|
+
class GitRepositoryValidationFailed < ValidationFailed; end
|
51
|
+
class GitUpToDateValidationFailed < ValidationFailed; end
|
23
52
|
end
|
24
53
|
end
|
data/lib/stove/filter.rb
CHANGED
@@ -4,17 +4,18 @@ module Stove
|
|
4
4
|
module Mixin::Instanceable
|
5
5
|
def self.included(base)
|
6
6
|
base.send(:include, Singleton)
|
7
|
-
base.send(:undef_method, :inspect, :to_s)
|
8
7
|
base.send(:extend, ClassMethods)
|
9
8
|
end
|
10
9
|
|
11
10
|
def self.extended(base)
|
12
11
|
base.send(:include, Singleton)
|
13
|
-
base.send(:undef_method, :inspect, :to_s)
|
14
12
|
base.send(:extend, ClassMethods)
|
15
13
|
end
|
16
14
|
|
17
15
|
module ClassMethods
|
16
|
+
def to_s; instance.to_s; end
|
17
|
+
def inspect; instance.inspect; end
|
18
|
+
|
18
19
|
def method_missing(m, *args, &block)
|
19
20
|
instance.send(m, *args, &block)
|
20
21
|
end
|
data/lib/stove/packager.rb
CHANGED
@@ -18,7 +18,14 @@ module Stove
|
|
18
18
|
'recipes',
|
19
19
|
'resources',
|
20
20
|
'templates',
|
21
|
-
]
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
ACCEPTABLE_FILES_LIST = ACCEPTABLE_FILES.join(',').freeze
|
24
|
+
|
25
|
+
TMP_FILES = [
|
26
|
+
/^(?:.*[\\\/])?\.[^\\\/]+\.sw[p-z]$/,
|
27
|
+
/~$/,
|
28
|
+
].freeze
|
22
29
|
|
23
30
|
# The cookbook to package.
|
24
31
|
#
|
@@ -38,7 +45,8 @@ module Stove
|
|
38
45
|
# @return [Array]
|
39
46
|
# the array of file paths
|
40
47
|
def cookbook_files
|
41
|
-
|
48
|
+
path = File.expand_path("#{cookbook.path}/{#{ACCEPTABLE_FILES_LIST}}")
|
49
|
+
Dir[path].reject { |f| TMP_FILES.any? { |regex| f.match(regex) } }
|
42
50
|
end
|
43
51
|
|
44
52
|
# The path to the tar.gz package in the temporary directory.
|
@@ -51,7 +59,7 @@ module Stove
|
|
51
59
|
private
|
52
60
|
|
53
61
|
def pack!
|
54
|
-
destination = Tempfile.new(cookbook.name).path
|
62
|
+
destination = Tempfile.new([cookbook.name, '.tar.gz']).path
|
55
63
|
|
56
64
|
# Sandbox
|
57
65
|
sandbox = Dir.mktmpdir
|