chef-stove 7.1.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 +7 -0
- data/LICENSE +202 -0
- data/bin/stove +5 -0
- data/lib/stove.rb +87 -0
- data/lib/stove/artifactory.rb +83 -0
- data/lib/stove/cli.rb +199 -0
- data/lib/stove/config.rb +76 -0
- data/lib/stove/cookbook.rb +120 -0
- data/lib/stove/cookbook/metadata.rb +245 -0
- data/lib/stove/error.rb +56 -0
- data/lib/stove/filter.rb +60 -0
- data/lib/stove/mash.rb +25 -0
- data/lib/stove/mixins/insideable.rb +13 -0
- data/lib/stove/mixins/instanceable.rb +24 -0
- data/lib/stove/mixins/optionable.rb +41 -0
- data/lib/stove/mixins/validatable.rb +11 -0
- data/lib/stove/packager.rb +156 -0
- data/lib/stove/plugins/artifactory.rb +14 -0
- data/lib/stove/plugins/base.rb +48 -0
- data/lib/stove/plugins/git.rb +70 -0
- data/lib/stove/plugins/supermarket.rb +18 -0
- data/lib/stove/rake_task.rb +22 -0
- data/lib/stove/runner.rb +39 -0
- data/lib/stove/supermarket.rb +79 -0
- data/lib/stove/util.rb +56 -0
- data/lib/stove/validator.rb +68 -0
- data/lib/stove/version.rb +3 -0
- data/templates/errors/abstract_method.erb +5 -0
- data/templates/errors/artifactory_key_validation_failed.erb +11 -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_tagging_failed.erb +5 -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
- data/templates/errors/supermarket_already_exists.erb +5 -0
- data/templates/errors/supermarket_key_validation_failed.erb +3 -0
- data/templates/errors/supermarket_username_validation_failed.erb +3 -0
- metadata +212 -0
data/lib/stove/config.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Stove
|
5
|
+
class Config
|
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
|
16
|
+
|
17
|
+
def respond_to_missing?(m, include_private = false)
|
18
|
+
__has__?(m) || super
|
19
|
+
end
|
20
|
+
|
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
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#<#{self.class.name} #{__raw__.to_s}>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"#<#{self.class.name} #{__raw__.inspect}>"
|
34
|
+
end
|
35
|
+
|
36
|
+
def __get__(key)
|
37
|
+
__raw__[key.to_sym]
|
38
|
+
end
|
39
|
+
|
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)
|
50
|
+
end
|
51
|
+
|
52
|
+
def __path__
|
53
|
+
@path ||= File.expand_path(ENV['STOVE_CONFIG'] || '~/.stove')
|
54
|
+
end
|
55
|
+
|
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
|
69
|
+
|
70
|
+
@__raw__
|
71
|
+
rescue Errno::ENOENT => e
|
72
|
+
log.warn { "No config file found at `#{__path__}'!" }
|
73
|
+
@__raw__ = {}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Stove
|
6
|
+
class Cookbook
|
7
|
+
include Logify
|
8
|
+
|
9
|
+
require_relative 'cookbook/metadata'
|
10
|
+
|
11
|
+
#
|
12
|
+
# The path to this cookbook on disk.
|
13
|
+
#
|
14
|
+
# @return [Pathname]
|
15
|
+
#
|
16
|
+
attr_reader :path
|
17
|
+
|
18
|
+
#
|
19
|
+
# The name of the cookbook (must correspond to the name of the
|
20
|
+
# cookbook on the Supermarket).
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
#
|
24
|
+
attr_reader :name
|
25
|
+
|
26
|
+
#
|
27
|
+
# The version of this cookbook (originally).
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
#
|
31
|
+
attr_reader :version
|
32
|
+
|
33
|
+
#
|
34
|
+
# The metadata for this cookbook.
|
35
|
+
#
|
36
|
+
# @return [Stove::Cookbook::Metadata]
|
37
|
+
#
|
38
|
+
attr_reader :metadata
|
39
|
+
|
40
|
+
#
|
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
|
48
|
+
|
49
|
+
#
|
50
|
+
# Create a new wrapper around the cookbook object.
|
51
|
+
#
|
52
|
+
# @param [String] path
|
53
|
+
# the relative or absolute path to the cookbook on disk
|
54
|
+
#
|
55
|
+
def initialize(path)
|
56
|
+
@path = File.expand_path(path)
|
57
|
+
load_metadata!
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# The tag version. This is just the current version prefixed with the
|
62
|
+
# letter "v".
|
63
|
+
#
|
64
|
+
# @example Tag version for 1.0.0
|
65
|
+
# cookbook.tag_version #=> "v1.0.0"
|
66
|
+
#
|
67
|
+
# @return [String]
|
68
|
+
#
|
69
|
+
def tag_version
|
70
|
+
"v#{version}"
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Deterine if this cookbook version is released on the Supermarket
|
75
|
+
#
|
76
|
+
# @warn
|
77
|
+
# This is a fairly expensive operation and the result cannot be
|
78
|
+
# reliably cached!
|
79
|
+
#
|
80
|
+
# @return [Boolean]
|
81
|
+
# true if this cookbook at the current version exists on the community
|
82
|
+
# site, false otherwise
|
83
|
+
#
|
84
|
+
def released?
|
85
|
+
Supermarket.cookbook(name, version)
|
86
|
+
true
|
87
|
+
rescue ChefAPI::Error::HTTPNotFound
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# So there's this really really crazy bug that the tmp directory could
|
93
|
+
# be deleted mid-request...
|
94
|
+
#
|
95
|
+
# @return [File]
|
96
|
+
#
|
97
|
+
def tarball(extended_metadata = false)
|
98
|
+
@tarball ||= Packager.new(self, extended_metadata).tarball
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
# Load the metadata and set the @metadata instance variable.
|
103
|
+
#
|
104
|
+
# @raise [ArgumentError]
|
105
|
+
# if there is no metadata.rb
|
106
|
+
#
|
107
|
+
# @return [String]
|
108
|
+
# the path to the metadata file
|
109
|
+
def load_metadata!
|
110
|
+
metadata_path = File.join(path, 'metadata.rb')
|
111
|
+
|
112
|
+
@metadata = Stove::Cookbook::Metadata.from_file(metadata_path)
|
113
|
+
@name = @metadata.name
|
114
|
+
@version = @metadata.version
|
115
|
+
|
116
|
+
metadata_path
|
117
|
+
end
|
118
|
+
alias_method :reload_metadata!, :load_metadata!
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Stove
|
4
|
+
class Cookbook
|
5
|
+
# Borrowed and modified from:
|
6
|
+
# {https://raw.github.com/opscode/chef/11.4.0/lib/chef/cookbook/metadata.rb}
|
7
|
+
#
|
8
|
+
# Copyright:: Copyright 2008-2017 Chef Software, Inc.
|
9
|
+
#
|
10
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
11
|
+
# you may not use this file except in compliance with the License.
|
12
|
+
# You may obtain a copy of the License at
|
13
|
+
#
|
14
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
15
|
+
#
|
16
|
+
# Unless required by applicable law or agreed to in writing, software
|
17
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
18
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
19
|
+
# See the License for the specific language governing permissions and
|
20
|
+
# limitations under the License.
|
21
|
+
#
|
22
|
+
# == Chef::Cookbook::Metadata
|
23
|
+
# Chef::Cookbook::Metadata provides a convenient DSL for declaring metadata
|
24
|
+
# about Chef Cookbooks.
|
25
|
+
class Metadata
|
26
|
+
class << self
|
27
|
+
def from_file(path)
|
28
|
+
new.from_file(path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def def_attribute(field)
|
32
|
+
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
33
|
+
def #{field}(arg = nil)
|
34
|
+
set_or_return(:#{field}, arg)
|
35
|
+
end
|
36
|
+
EOM
|
37
|
+
end
|
38
|
+
|
39
|
+
def def_meta_cookbook(field, instance_variable)
|
40
|
+
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
41
|
+
def #{field}(thing, *args)
|
42
|
+
version = args.first
|
43
|
+
@#{instance_variable}[thing] = version || DEFAULT_VERSION
|
44
|
+
@#{instance_variable}[thing]
|
45
|
+
end
|
46
|
+
EOM
|
47
|
+
end
|
48
|
+
|
49
|
+
def def_meta_setter(field, instance_variable)
|
50
|
+
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
51
|
+
def #{field}(name, description)
|
52
|
+
@#{instance_variable}[name] = description
|
53
|
+
@#{instance_variable}
|
54
|
+
end
|
55
|
+
EOM
|
56
|
+
end
|
57
|
+
|
58
|
+
def def_meta_gems(field, instance_variable)
|
59
|
+
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
60
|
+
def #{field}(*args)
|
61
|
+
@#{instance_variable} << args unless args.empty?
|
62
|
+
@#{instance_variable}
|
63
|
+
end
|
64
|
+
EOM
|
65
|
+
end
|
66
|
+
|
67
|
+
def def_meta_version(field)
|
68
|
+
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
69
|
+
def #{field}(*args)
|
70
|
+
@#{field} << args unless args.empty?
|
71
|
+
@#{field}
|
72
|
+
end
|
73
|
+
EOM
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
DEFAULT_VERSION = '>= 0.0.0'.freeze
|
78
|
+
|
79
|
+
COMPARISON_FIELDS = [
|
80
|
+
:name, :description, :long_description, :maintainer,
|
81
|
+
:maintainer_email, :license, :platforms, :dependencies,
|
82
|
+
:recommendations, :suggestions, :conflicting, :providing,
|
83
|
+
:replacing, :attributes, :groupings, :recipes, :version
|
84
|
+
]
|
85
|
+
|
86
|
+
def_attribute :name
|
87
|
+
def_attribute :maintainer
|
88
|
+
def_attribute :maintainer_email
|
89
|
+
def_attribute :license
|
90
|
+
def_attribute :description
|
91
|
+
def_attribute :long_description
|
92
|
+
|
93
|
+
# These attributes are available for reading, but are not written by
|
94
|
+
# default. In order to maintain backwards and forwards compatability,
|
95
|
+
# these attributes are here.
|
96
|
+
def_attribute :source_url
|
97
|
+
def_attribute :issues_url
|
98
|
+
def_meta_version :chef_version
|
99
|
+
def_meta_version :ohai_version
|
100
|
+
|
101
|
+
def_meta_cookbook :supports, :platforms
|
102
|
+
def_meta_cookbook :depends, :dependencies
|
103
|
+
def_meta_cookbook :recommends, :recommendations
|
104
|
+
def_meta_cookbook :suggests, :suggestions
|
105
|
+
def_meta_cookbook :conflicts, :conflicting
|
106
|
+
def_meta_cookbook :provides, :providing
|
107
|
+
def_meta_cookbook :replaces, :replacing
|
108
|
+
|
109
|
+
def_meta_setter :recipe, :recipes
|
110
|
+
def_meta_setter :grouping, :groupings
|
111
|
+
def_meta_setter :attribute, :attributes
|
112
|
+
def_meta_gems :gem, :gems
|
113
|
+
|
114
|
+
attr_reader :cookbook
|
115
|
+
attr_reader :platforms
|
116
|
+
attr_reader :dependencies
|
117
|
+
attr_reader :recommendations
|
118
|
+
attr_reader :gems
|
119
|
+
attr_reader :suggestions
|
120
|
+
attr_reader :conflicting
|
121
|
+
attr_reader :providing
|
122
|
+
attr_reader :replacing
|
123
|
+
attr_reader :attributes
|
124
|
+
attr_reader :groupings
|
125
|
+
attr_reader :recipes
|
126
|
+
attr_reader :version
|
127
|
+
|
128
|
+
def initialize(cookbook = nil, maintainer = 'YOUR_COMPANY_NAME', maintainer_email = 'YOUR_EMAIL', license = 'none')
|
129
|
+
@cookbook = cookbook
|
130
|
+
@name = cookbook ? cookbook.name : ''
|
131
|
+
@long_description = ''
|
132
|
+
@source_url = Stove::Mash.new
|
133
|
+
@issues_url = Stove::Mash.new
|
134
|
+
@gems = []
|
135
|
+
@chef_version = []
|
136
|
+
@ohai_version = []
|
137
|
+
@platforms = Stove::Mash.new
|
138
|
+
@dependencies = Stove::Mash.new
|
139
|
+
@recommendations = Stove::Mash.new
|
140
|
+
@suggestions = Stove::Mash.new
|
141
|
+
@conflicting = Stove::Mash.new
|
142
|
+
@providing = Stove::Mash.new
|
143
|
+
@replacing = Stove::Mash.new
|
144
|
+
@attributes = Stove::Mash.new
|
145
|
+
@groupings = Stove::Mash.new
|
146
|
+
@recipes = Stove::Mash.new
|
147
|
+
|
148
|
+
self.maintainer(maintainer)
|
149
|
+
self.maintainer_email(maintainer_email)
|
150
|
+
self.license(license)
|
151
|
+
self.description('A fabulous new cookbook')
|
152
|
+
self.version('0.0.0')
|
153
|
+
|
154
|
+
if cookbook
|
155
|
+
@recipes = cookbook.fully_qualified_recipe_names.inject({}) do |r, e|
|
156
|
+
e = self.name if e =~ /::default$/
|
157
|
+
r[e] = ""
|
158
|
+
self.provides e
|
159
|
+
r
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def from_file(path)
|
165
|
+
path = path.to_s
|
166
|
+
path_json = File.join(File.dirname(path), 'metadata.json')
|
167
|
+
|
168
|
+
if File.exist?(path) && File.readable?(path)
|
169
|
+
self.instance_eval(IO.read(path), path, 1)
|
170
|
+
self
|
171
|
+
elsif File.exist?(path_json) && File.readable?(path_json)
|
172
|
+
metadata_from_json(path_json)
|
173
|
+
else
|
174
|
+
raise Error::MetadataNotFound.new(path: path)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def ==(other)
|
179
|
+
COMPARISON_FIELDS.inject(true) do |equal_so_far, field|
|
180
|
+
equal_so_far && other.respond_to?(field) && (other.send(field) == send(field))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def version(arg = UNSET_VALUE)
|
185
|
+
if arg == UNSET_VALUE
|
186
|
+
@version
|
187
|
+
else
|
188
|
+
@version = arg.to_s
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def to_hash(extended_metadata = false)
|
193
|
+
hash = {
|
194
|
+
'name' => self.name,
|
195
|
+
'version' => self.version,
|
196
|
+
'description' => self.description,
|
197
|
+
'long_description' => self.long_description,
|
198
|
+
'maintainer' => self.maintainer,
|
199
|
+
'maintainer_email' => self.maintainer_email,
|
200
|
+
'license' => self.license,
|
201
|
+
'platforms' => self.platforms,
|
202
|
+
'dependencies' => self.dependencies,
|
203
|
+
'recommendations' => self.recommendations,
|
204
|
+
'suggestions' => self.suggestions,
|
205
|
+
'conflicting' => self.conflicting,
|
206
|
+
'providing' => self.providing,
|
207
|
+
'replacing' => self.replacing,
|
208
|
+
'attributes' => self.attributes,
|
209
|
+
'groupings' => self.groupings,
|
210
|
+
'recipes' => self.recipes,
|
211
|
+
}
|
212
|
+
|
213
|
+
if extended_metadata
|
214
|
+
hash['source_url'] = self.source_url unless self.source_url.empty?
|
215
|
+
hash['issues_url'] = self.issues_url unless self.issues_url.empty?
|
216
|
+
hash['gems'] = self.gems unless self.gems.empty?
|
217
|
+
hash['chef_version'] = self.chef_version.map(&:sort)
|
218
|
+
hash['ohai_version'] = self.ohai_version.map(&:sort)
|
219
|
+
end
|
220
|
+
|
221
|
+
return hash
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
def metadata_from_json(path)
|
227
|
+
json = JSON.parse(IO.read(path))
|
228
|
+
json.keys.each do |key|
|
229
|
+
set_or_return(key.to_sym, json[key])
|
230
|
+
end
|
231
|
+
self
|
232
|
+
end
|
233
|
+
|
234
|
+
def set_or_return(symbol, arg)
|
235
|
+
iv_symbol = "@#{symbol.to_s}".to_sym
|
236
|
+
|
237
|
+
if arg.nil? && self.instance_variable_defined?(iv_symbol)
|
238
|
+
self.instance_variable_get(iv_symbol)
|
239
|
+
else
|
240
|
+
self.instance_variable_set(iv_symbol, arg)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
data/lib/stove/error.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Stove
|
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
|
+
|
17
|
+
class StoveError < StandardError
|
18
|
+
def initialize(options = {})
|
19
|
+
@options = options
|
20
|
+
@filename = options.delete(:_template)
|
21
|
+
|
22
|
+
super()
|
23
|
+
end
|
24
|
+
|
25
|
+
def message
|
26
|
+
erb = ERB.new(File.read(template))
|
27
|
+
erb.result(ErrorBinding.new(@options).get_binding)
|
28
|
+
end
|
29
|
+
alias_method :to_s, :message
|
30
|
+
|
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")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class GitFailed < StoveError; end
|
41
|
+
class GitTaggingFailed < StoveError; end
|
42
|
+
class MetadataNotFound < StoveError; end
|
43
|
+
class ServerUnavailable < StoveError; end
|
44
|
+
class CookbookAlreadyExists < StoveError; end
|
45
|
+
|
46
|
+
# Validations
|
47
|
+
class ValidationFailed < StoveError; end
|
48
|
+
class SupermarketCategoryValidationFailed < ValidationFailed; end
|
49
|
+
class SupermarketKeyValidationFailed < ValidationFailed; end
|
50
|
+
class SupermarketUsernameValidationFailed < ValidationFailed; end
|
51
|
+
class GitCleanValidationFailed < ValidationFailed; end
|
52
|
+
class GitRepositoryValidationFailed < ValidationFailed; end
|
53
|
+
class GitUpToDateValidationFailed < ValidationFailed; end
|
54
|
+
class ArtifactoryKeyValidationFailed < ValidationFailed; end
|
55
|
+
end
|
56
|
+
end
|