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/features/upload.feature
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
Feature: Upload
|
2
|
-
Background:
|
3
|
-
* I have a cookbook named "bacon"
|
4
|
-
* the CLI options are all off
|
5
|
-
|
6
|
-
Scenario: --no-upload
|
7
|
-
* I successfully run `bake 1.0.0 --no-upload`
|
8
|
-
* the Community Site will not have the cookbook:
|
9
|
-
| bacon | 1.0.0 |
|
10
|
-
|
11
|
-
Scenario: --upload (no category, no existing)
|
12
|
-
* I run `bake 1.0.0 --upload`
|
13
|
-
* the Community Site will not have the cookbook:
|
14
|
-
| bacon | 1.0.0 |
|
15
|
-
* the exit status will be "CookbookCategoryNotFound"
|
16
|
-
|
17
|
-
Scenario: --upload (no category, existing)
|
18
|
-
* the Community Site has the cookbook:
|
19
|
-
| bacon | 0.0.0 | Application |
|
20
|
-
* I successfully run `bake 1.0.0 --upload`
|
21
|
-
* the Community Site will have the cookbook:
|
22
|
-
| bacon | 1.0.0 | Application |
|
23
|
-
|
24
|
-
Scenario: --upload (category, no existing)
|
25
|
-
* I successfully run `bake 1.0.0 --upload --category Application`
|
26
|
-
* the Community Site will have the cookbook:
|
27
|
-
| bacon | 1.0.0 | Application |
|
28
|
-
|
29
|
-
Scenario: --upload (category, existing)
|
30
|
-
* the Community Site has the cookbook:
|
31
|
-
| bacon | 0.0.0 | Application |
|
32
|
-
* I successfully run `bake 1.0.0 --upload --category Application`
|
33
|
-
* the Community Site will have the cookbook:
|
34
|
-
| bacon | 1.0.0 | Application |
|
35
|
-
|
36
|
-
Scenario: --upload (existing version)
|
37
|
-
* the Community Site has the cookbook:
|
38
|
-
| bacon | 1.0.0 | Application |
|
39
|
-
* I run `bake 1.0.0 --upload`
|
40
|
-
* the exit status will be "UploadError"
|
data/lib/stove/community_site.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
|
3
|
-
module Stove
|
4
|
-
class CommunitySite
|
5
|
-
include HTTParty
|
6
|
-
base_uri 'https://cookbooks.opscode.com/api/v1'
|
7
|
-
headers 'Content-Type' => 'application/json', 'Accept' => 'application/json'
|
8
|
-
|
9
|
-
class << self
|
10
|
-
# The URI for the web-based version of the site. (default:
|
11
|
-
# https://community.opscode.com).
|
12
|
-
#
|
13
|
-
# If a parameter is given, the {http_uri} is set to that value.
|
14
|
-
#
|
15
|
-
# @return [String]
|
16
|
-
def http_uri(arg = nil)
|
17
|
-
if arg.nil?
|
18
|
-
@http_uri ||= 'https://community.opscode.com'
|
19
|
-
else
|
20
|
-
@http_uri = arg
|
21
|
-
@http_uri
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Get and cache a community cookbook's JSON response from the given name
|
26
|
-
# and version.
|
27
|
-
#
|
28
|
-
# @example Find a cookbook by name
|
29
|
-
# CommunitySite.cookbook('apache2') #=> {...}
|
30
|
-
#
|
31
|
-
# @example Find a cookbook by name and version
|
32
|
-
# CommunitySite.cookbook('apache2', '1.0.0') #=> {...}
|
33
|
-
#
|
34
|
-
# @example Find a non-existent cookbook
|
35
|
-
# CommunitySite.cookbook('not-real') #=> CommunitySite::BadResponse
|
36
|
-
#
|
37
|
-
# @raise [CommunitySite::BadResponse]
|
38
|
-
# if the given cookbook (or cookbook version) does not exist on the community site
|
39
|
-
#
|
40
|
-
# @param [String] name
|
41
|
-
# the name of the cookbook on the community site
|
42
|
-
# @param [String] version (optional)
|
43
|
-
# the version of the cookbook to find
|
44
|
-
def cookbook(name, version = nil)
|
45
|
-
if version.nil?
|
46
|
-
get("/cookbooks/#{name}")
|
47
|
-
else
|
48
|
-
get("/cookbooks/#{name}/versions/#{format_version(version)}")
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
# Convert a version string (x.y.z) to a community-site friendly format
|
54
|
-
# (x_y_z).
|
55
|
-
#
|
56
|
-
# @example Convert a version to a version string
|
57
|
-
# format_version('1.2.3') #=> 1_2_3
|
58
|
-
#
|
59
|
-
# @param [#to_s] version
|
60
|
-
# the version string to convert
|
61
|
-
#
|
62
|
-
# @return [String]
|
63
|
-
def format_version(version)
|
64
|
-
version.gsub('.', '_')
|
65
|
-
end
|
66
|
-
|
67
|
-
# @override [HTTParty.get]
|
68
|
-
def get(path, options = {}, &block)
|
69
|
-
cache[path] ||= begin
|
70
|
-
Stove::Logger.debug "Getting #{path}"
|
71
|
-
response = super(path)
|
72
|
-
raise Stove::BadResponse.new(response) unless response.ok?
|
73
|
-
response.parsed_response
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# A small, unpersisted cache for storing responses
|
78
|
-
#
|
79
|
-
# @return [Hash]
|
80
|
-
def cache
|
81
|
-
@cache ||= {}
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
data/lib/stove/formatter.rb
DELETED
data/lib/stove/formatter/base.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Stove
|
2
|
-
module Formatter
|
3
|
-
class Base
|
4
|
-
class << self
|
5
|
-
def inherited(base)
|
6
|
-
key = base.to_s.split('::').last.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
|
7
|
-
formatters[key] = base
|
8
|
-
end
|
9
|
-
|
10
|
-
def formatter_method(*methods)
|
11
|
-
methods.each do |name|
|
12
|
-
formatter_methods << name
|
13
|
-
|
14
|
-
define_method(name) do |*args|
|
15
|
-
raise Stove::AbstractFunction
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def formatters
|
21
|
-
@formatters ||= {}
|
22
|
-
end
|
23
|
-
|
24
|
-
def formatter_methods
|
25
|
-
@formatter_methods ||= []
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
formatter_method :upload
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/lib/stove/git.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
|
3
|
-
module Stove
|
4
|
-
module Git
|
5
|
-
# Run a git command.
|
6
|
-
#
|
7
|
-
# @param [String] command
|
8
|
-
# the command to run
|
9
|
-
#
|
10
|
-
# @return [String]
|
11
|
-
# the stdout from the command
|
12
|
-
def git(command)
|
13
|
-
Stove::Logger.debug "shellout 'git #{command}'"
|
14
|
-
response = shellout("git #{command}")
|
15
|
-
|
16
|
-
Stove::Logger.debug response.stdout
|
17
|
-
|
18
|
-
unless response.success?
|
19
|
-
Stove::Logger.debug response.stderr
|
20
|
-
raise Stove::GitError, response.stderr
|
21
|
-
end
|
22
|
-
|
23
|
-
response.stdout.strip
|
24
|
-
end
|
25
|
-
|
26
|
-
# Return true if the current working directory is a valid
|
27
|
-
# git repot, false otherwise.
|
28
|
-
#
|
29
|
-
# @return [Boolean]
|
30
|
-
def git_repo?
|
31
|
-
git('rev-parse --show-toplevel')
|
32
|
-
true
|
33
|
-
rescue
|
34
|
-
false
|
35
|
-
end
|
36
|
-
|
37
|
-
# Return true if the current working directory is clean,
|
38
|
-
# false otherwise
|
39
|
-
#
|
40
|
-
# @return [Boolean]
|
41
|
-
def git_repo_clean?
|
42
|
-
!!git('status -s').strip.empty?
|
43
|
-
rescue
|
44
|
-
false
|
45
|
-
end
|
46
|
-
|
47
|
-
def git_remote_uptodate?(options = {})
|
48
|
-
git('fetch')
|
49
|
-
local = git("rev-parse #{options[:branch]}").strip
|
50
|
-
remote = git("rev-parse #{options[:remote]}/#{options[:branch]}").strip
|
51
|
-
|
52
|
-
local == remote
|
53
|
-
end
|
54
|
-
|
55
|
-
def shellout(command)
|
56
|
-
out, err = Tempfile.new('shellout.stdout'), Tempfile.new('shellout.stderr')
|
57
|
-
|
58
|
-
begin
|
59
|
-
pid = Process.spawn(command, out: out.to_i, err: err.to_i)
|
60
|
-
pid, status = Process.waitpid2(pid)
|
61
|
-
|
62
|
-
# Check if we're getting back a process status because win32-process 6.x was a fucking MURDERER.
|
63
|
-
# https://github.com/djberg96/win32-process/blob/master/lib/win32/process.rb#L494-L519
|
64
|
-
exitstatus = status.is_a?(Process::Status) ? status.exitstatus : status
|
65
|
-
rescue Errno::ENOENT => e
|
66
|
-
err.write('')
|
67
|
-
err.write('Command not found: ' + command)
|
68
|
-
end
|
69
|
-
|
70
|
-
out.close
|
71
|
-
err.close
|
72
|
-
|
73
|
-
OpenStruct.new({
|
74
|
-
exitstatus: exitstatus,
|
75
|
-
stdout: File.read(out).strip,
|
76
|
-
stderr: File.read(err).strip,
|
77
|
-
success?: exitstatus == 0,
|
78
|
-
error?: exitstatus == 0,
|
79
|
-
})
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
data/lib/stove/github.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'octokit'
|
2
|
-
|
3
|
-
module Stove
|
4
|
-
class GitHub
|
5
|
-
attr_reader :cookbook
|
6
|
-
|
7
|
-
def initialize(cookbook)
|
8
|
-
@cookbook = cookbook
|
9
|
-
|
10
|
-
Octokit.configure do |config|
|
11
|
-
config.access_token = Stove::Config['github_access_token']
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def publish_release!
|
16
|
-
release = Octokit.create_release(repository, cookbook.tag_version,
|
17
|
-
name: cookbook.tag_version,
|
18
|
-
body: changeset,
|
19
|
-
)
|
20
|
-
asset = Octokit.upload_asset("repos/#{repository}/releases/#{release.id}", cookbook.tarball,
|
21
|
-
content_type: 'application/x-gzip',
|
22
|
-
name: filename,
|
23
|
-
)
|
24
|
-
Octokit.update_release_asset("repos/#{repository}/releases/assets/#{asset.id}",
|
25
|
-
name: filename,
|
26
|
-
label: 'Download Cookbook',
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
def repository
|
32
|
-
@repository ||= Octokit::Repository.from_url(cookbook.repository_url)
|
33
|
-
end
|
34
|
-
|
35
|
-
def changeset
|
36
|
-
cookbook.changeset.split("\n")[2..-1].join("\n").strip
|
37
|
-
end
|
38
|
-
|
39
|
-
def filename
|
40
|
-
@filename ||= "#{cookbook.name}-#{cookbook.version}.tar.gz"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/lib/stove/logger.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
|
-
module Stove
|
4
|
-
module Logger
|
5
|
-
class << self
|
6
|
-
def set_level(level)
|
7
|
-
logger.level = level_to_constant(level)
|
8
|
-
logger
|
9
|
-
end
|
10
|
-
|
11
|
-
def set_output(output)
|
12
|
-
old_level = @logger.sev_threshold
|
13
|
-
|
14
|
-
@logger = ::Logger.new(output)
|
15
|
-
@logger.level = old_level
|
16
|
-
@logger
|
17
|
-
end
|
18
|
-
|
19
|
-
[:fatal, :error, :warn, :info, :debug, :sev_threshold].each do |name|
|
20
|
-
define_method(name) do |*args|
|
21
|
-
logger.send(name, *args)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def logger
|
27
|
-
@logger ||= begin
|
28
|
-
logger = ::Logger.new($stdout)
|
29
|
-
logger.level = ::Logger::WARN
|
30
|
-
logger
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Convert a string to it's logger constant.
|
35
|
-
#
|
36
|
-
# @return [Object]
|
37
|
-
def level_to_constant(level)
|
38
|
-
return level if level.kind_of?(Fixnum)
|
39
|
-
case level.to_s.strip.downcase.to_sym
|
40
|
-
when :fatal
|
41
|
-
::Logger::FATAL
|
42
|
-
when :error
|
43
|
-
::Logger::ERROR
|
44
|
-
when :warn
|
45
|
-
::Logger::WARN
|
46
|
-
when :info
|
47
|
-
::Logger::INFO
|
48
|
-
when :debug
|
49
|
-
::Logger::DEBUG
|
50
|
-
else
|
51
|
-
::Logger::INFO
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
data/lib/stove/uploader.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
require 'httmultiparty'
|
3
|
-
require 'mixlib/authentication/signedheaderauth'
|
4
|
-
require 'openssl'
|
5
|
-
|
6
|
-
module Stove
|
7
|
-
class Uploader
|
8
|
-
include HTTMultiParty
|
9
|
-
|
10
|
-
# The cookbook associated with this uploader
|
11
|
-
#
|
12
|
-
# @return [Stove::Cookbook]
|
13
|
-
attr_reader :cookbook
|
14
|
-
|
15
|
-
# Create a new uploader instance for the given cookbook.
|
16
|
-
#
|
17
|
-
# @param [Stove::Cookbook] cookbook
|
18
|
-
# the cookbook for this uploader
|
19
|
-
def initialize(cookbook)
|
20
|
-
@cookbook = cookbook
|
21
|
-
end
|
22
|
-
|
23
|
-
def upload!
|
24
|
-
response = self.class.post(upload_url, {
|
25
|
-
:headers => headers,
|
26
|
-
:query => {
|
27
|
-
:tarball => File.new(cookbook.tarball),
|
28
|
-
:cookbook => { category: cookbook.category }.to_json,
|
29
|
-
},
|
30
|
-
})
|
31
|
-
|
32
|
-
if response.success?
|
33
|
-
Stove.formatter.upload(cookbook)
|
34
|
-
else
|
35
|
-
raise Stove::UploadError.new(response)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
def headers
|
41
|
-
{
|
42
|
-
'Accept' => 'application/json',
|
43
|
-
}.merge(Mixlib::Authentication::SignedHeaderAuth.signing_object({
|
44
|
-
:http_method => 'post',
|
45
|
-
:timestamp => Time.now.utc.iso8601,
|
46
|
-
:user_id => username,
|
47
|
-
:path => URI.parse(upload_url).path,
|
48
|
-
:file => File.new(cookbook.tarball),
|
49
|
-
}).sign(pem_file))
|
50
|
-
end
|
51
|
-
|
52
|
-
def pem_file
|
53
|
-
OpenSSL::PKey::RSA.new(File.read(File.expand_path(Stove::Config['opscode_pem_file'])))
|
54
|
-
end
|
55
|
-
|
56
|
-
def username
|
57
|
-
Stove::Config['opscode_username']
|
58
|
-
end
|
59
|
-
|
60
|
-
def upload_url
|
61
|
-
"#{Stove::CommunitySite.base_uri}/cookbooks"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'community_zero/server'
|
2
|
-
|
3
|
-
module Stove
|
4
|
-
module RSpec
|
5
|
-
module CommunitySite
|
6
|
-
class << self
|
7
|
-
def start(options = {})
|
8
|
-
return @server if @server
|
9
|
-
|
10
|
-
@server = CommunityZero::Server.new(options)
|
11
|
-
@server.start_background
|
12
|
-
@server
|
13
|
-
end
|
14
|
-
|
15
|
-
def stop
|
16
|
-
@server.stop if running?
|
17
|
-
end
|
18
|
-
|
19
|
-
def running?
|
20
|
-
!!(@server && @server.running?)
|
21
|
-
end
|
22
|
-
|
23
|
-
def reset!
|
24
|
-
@server && @server.reset!
|
25
|
-
end
|
26
|
-
|
27
|
-
def server_url
|
28
|
-
@server && @server.url
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|