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
@@ -0,0 +1,12 @@
|
|
1
|
+
# These are steps that should really exist in cucumber, but they don't...
|
2
|
+
When /^the environment variable "(.+)" is "(.+)"/ do |variable, value|
|
3
|
+
set_env(variable, value)
|
4
|
+
end
|
5
|
+
|
6
|
+
When /^the environment variable "(.+)" is unset$/ do |variable|
|
7
|
+
set_env(variable, nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^it should (pass|fail) with "(.+)"$/ do |pass_fail, partial|
|
11
|
+
self.__send__("assert_#{pass_fail}ing_with", partial)
|
12
|
+
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
Given /^the remote repository has additional commits/ do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
git
|
6
|
-
|
2
|
+
cmd = [
|
3
|
+
'cd "' + fake_git_remote + '"',
|
4
|
+
'touch myfile.txt',
|
5
|
+
'git add --force myfile.txt',
|
6
|
+
'git commit --message "Add new file"',
|
7
|
+
].join(' && ')
|
8
|
+
|
9
|
+
%x|#{cmd}|
|
7
10
|
end
|
8
11
|
|
9
|
-
Then /^the git remote
|
12
|
+
Then /^the git remote should( not)? have the commit "(.+)"$/ do |negate, message|
|
10
13
|
commits = git_commits(fake_git_remote)
|
11
14
|
|
12
15
|
if negate
|
@@ -16,7 +19,7 @@ Then /^the git remote will( not)? have the commit "(.+)"$/ do |negate, message|
|
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
|
-
Then /^the git remote
|
22
|
+
Then /^the git remote should( not)? have the tag "(.+)"$/ do |negate, tag|
|
20
23
|
tags = git_tags(fake_git_remote)
|
21
24
|
|
22
25
|
if negate
|
data/features/support/env.rb
CHANGED
@@ -1,40 +1,24 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
1
3
|
require 'aruba/api'
|
2
4
|
require 'aruba/cucumber'
|
3
|
-
require '
|
5
|
+
require 'cucumber/rspec/doubles'
|
4
6
|
require 'rspec/expectations'
|
7
|
+
|
8
|
+
require 'community_zero/rspec'
|
9
|
+
CommunityZero::RSpec.start
|
10
|
+
Before { CommunityZero::RSpec.reset! }
|
11
|
+
|
5
12
|
require 'stove'
|
6
13
|
|
7
|
-
|
8
|
-
require_relative '../../spec/support/git'
|
14
|
+
require File.expand_path('../stove/git', __FILE__)
|
9
15
|
|
10
16
|
World(Aruba::Api)
|
11
|
-
World(Stove::
|
12
|
-
|
13
|
-
Stove.set_formatter(:silent)
|
14
|
-
Stove::Config.instance_variable_set(:@instance, {
|
15
|
-
'jira_username' => 'default',
|
16
|
-
'jira_password' => 'default',
|
17
|
-
'github_usernmae' => 'default',
|
18
|
-
'github_password' => 'default',
|
19
|
-
'opscode_username' => 'stove',
|
20
|
-
'opscode_pem_file' => File.expand_path(File.join(__FILE__, '..', 'stove.pem')),
|
21
|
-
})
|
22
|
-
Stove::RSpec::CommunitySite.start(port: 3390)
|
23
|
-
Stove::CommunitySite.base_uri(Stove::RSpec::CommunitySite.server_url)
|
24
|
-
Stove::CommunitySite.http_uri(Stove::RSpec::CommunitySite.server_url)
|
17
|
+
World(Stove::Git)
|
25
18
|
|
26
19
|
Before do
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
Before('~@spawn') do
|
32
|
-
Aruba::InProcess.main_class = Stove::Cli
|
33
|
-
Aruba.process = Aruba::InProcess
|
34
|
-
end
|
35
|
-
|
36
|
-
Before('@spawn') do
|
37
|
-
Aruba.process = Aruba::SpawnProcess
|
20
|
+
FileUtils.rm_rf(tmp_path)
|
21
|
+
@aruba_timeout_seconds = 15
|
38
22
|
end
|
39
23
|
|
40
24
|
# The path to Aruba's "stuff"
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Stove
|
4
|
+
module Git
|
5
|
+
def git_init(path = Dir.pwd)
|
6
|
+
cmd = [
|
7
|
+
'cd "' + path + '"',
|
8
|
+
'git init .',
|
9
|
+
'git add --all',
|
10
|
+
'git commit --message "Initial commit"',
|
11
|
+
'git remote add origin file://' + fake_git_remote,
|
12
|
+
'git push --quiet --force origin master',
|
13
|
+
].join(' && ')
|
14
|
+
|
15
|
+
%x|#{cmd}|
|
16
|
+
end
|
17
|
+
|
18
|
+
def fake_git_remote
|
19
|
+
path = File.expand_path(File.join(tmp_path, 'remote.git'))
|
20
|
+
return path if File.exists?(path)
|
21
|
+
|
22
|
+
FileUtils.mkdir_p(path)
|
23
|
+
cmd = [
|
24
|
+
'cd "' + path + '"',
|
25
|
+
'git init .',
|
26
|
+
'git config receive.denyCurrentBranch ignore',
|
27
|
+
'git config receive.denyNonFastforwards true',
|
28
|
+
'git config core.sharedrepository 1',
|
29
|
+
].join(' && ')
|
30
|
+
|
31
|
+
%x|#{cmd}|
|
32
|
+
|
33
|
+
path
|
34
|
+
end
|
35
|
+
|
36
|
+
def git_shas(path)
|
37
|
+
%x|cd "#{path}" && git log --oneline|.split("\n").map { |line| line.split(/\s+/, 2).first.strip } rescue []
|
38
|
+
end
|
39
|
+
|
40
|
+
def git_commits(path)
|
41
|
+
%x|cd "#{path}" && git log --oneline|.split("\n").map { |line| line.split(/\s+/, 2).last.strip } rescue []
|
42
|
+
end
|
43
|
+
|
44
|
+
def git_tags(path)
|
45
|
+
%x|cd "#{path}" && git tag --list|.split("\n").map(&:strip) rescue []
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/stove.rb
CHANGED
@@ -1,27 +1,110 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
require 'log4r'
|
4
|
+
Log4r.define_levels(*Log4r::Log4rConfig::LogLevels)
|
5
|
+
|
1
6
|
module Stove
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
autoload :Config, 'stove/config'
|
8
|
+
autoload :Community, 'stove/community'
|
9
|
+
autoload :Cookbook, 'stove/cookbook'
|
10
|
+
autoload :Cli, 'stove/cli'
|
11
|
+
autoload :Error, 'stove/error'
|
12
|
+
autoload :Filter, 'stove/filter'
|
13
|
+
autoload :JIRA, 'stove/jira'
|
14
|
+
autoload :Mash, 'stove/mash'
|
15
|
+
autoload :Packager, 'stove/packager'
|
16
|
+
autoload :Runner, 'stove/runner'
|
17
|
+
autoload :Util, 'stove/util'
|
18
|
+
autoload :Validator, 'stove/validator'
|
19
|
+
autoload :VERSION, 'stove/version'
|
20
|
+
|
21
|
+
module Action
|
22
|
+
autoload :Base, 'stove/actions/base'
|
23
|
+
autoload :Bump, 'stove/actions/bump'
|
24
|
+
autoload :Changelog, 'stove/actions/changelog'
|
25
|
+
autoload :Dev, 'stove/actions/dev'
|
26
|
+
autoload :Finish, 'stove/actions/finish'
|
27
|
+
autoload :Start, 'stove/actions/start'
|
28
|
+
autoload :Upload, 'stove/actions/upload'
|
29
|
+
end
|
30
|
+
|
31
|
+
module Middleware
|
32
|
+
autoload :ChefAuthentication, 'stove/middlewares/chef_authentication'
|
33
|
+
autoload :Exceptions, 'stove/middlewares/exceptions'
|
34
|
+
end
|
35
|
+
|
36
|
+
module Mixin
|
37
|
+
autoload :Filterable, 'stove/mixins/filterable'
|
38
|
+
autoload :Insideable, 'stove/mixins/insideable'
|
39
|
+
autoload :Instanceable, 'stove/mixins/instanceable'
|
40
|
+
autoload :Loggable, 'stove/mixins/loggable'
|
41
|
+
autoload :Optionable, 'stove/mixins/optionable'
|
42
|
+
autoload :Validatable, 'stove/mixins/validatable'
|
43
|
+
end
|
44
|
+
|
45
|
+
module Plugin
|
46
|
+
autoload :Base, 'stove/plugins/base'
|
47
|
+
autoload :Git, 'stove/plugins/git'
|
48
|
+
autoload :GitHub, 'stove/plugins/github'
|
49
|
+
autoload :JIRA, 'stove/plugins/jira'
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# A constant to represent an unset value. +nil+ is too generic and doesn't
|
54
|
+
# allow users to specify a value as +nil+. Using this constant, we can
|
55
|
+
# safely create +set_or_return+-style methods.
|
56
|
+
#
|
57
|
+
# @return [Object]
|
58
|
+
#
|
59
|
+
UNSET_VALUE = Object.new
|
60
|
+
|
61
|
+
#
|
62
|
+
# The User-Agent to use for HTTP requests
|
63
|
+
#
|
64
|
+
# @return [String]
|
65
|
+
#
|
66
|
+
USER_AGENT = "Stove #{VERSION}"
|
17
67
|
|
18
68
|
class << self
|
19
|
-
|
20
|
-
|
69
|
+
#
|
70
|
+
# The source root of the ChefAPI gem. This is useful when requiring files
|
71
|
+
# that are relative to the root of the project.
|
72
|
+
#
|
73
|
+
# @return [Pathname]
|
74
|
+
#
|
75
|
+
def root
|
76
|
+
@root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
21
77
|
end
|
22
78
|
|
23
|
-
|
24
|
-
|
79
|
+
#
|
80
|
+
# The current log level for the entire application.
|
81
|
+
#
|
82
|
+
# @return [Integer]
|
83
|
+
#
|
84
|
+
def log_level
|
85
|
+
Log4r::Logger.global.level
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Set the global log level.
|
90
|
+
#
|
91
|
+
# @example Set the log level to warn
|
92
|
+
# Stove.log_level = :warn
|
93
|
+
#
|
94
|
+
# @param [String, Symbol] id
|
95
|
+
# the log level to set
|
96
|
+
#
|
97
|
+
def log_level=(id)
|
98
|
+
level = Log4r.const_get(id.to_s.upcase)
|
99
|
+
raise NameError unless level.is_a?(Integer)
|
100
|
+
|
101
|
+
Log4r::Logger.global.level = level
|
102
|
+
rescue NameError
|
103
|
+
$stderr.puts "ERROR `#{id}' is not a valid Log Level!"
|
25
104
|
end
|
26
105
|
end
|
27
106
|
end
|
107
|
+
|
108
|
+
require 'i18n'
|
109
|
+
I18n.enforce_available_locales = true
|
110
|
+
I18n.load_path << Dir[Stove.root.join('locales', '*.yml').to_s]
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Stove
|
2
|
+
class Action::Base
|
3
|
+
extend Mixin::Loggable
|
4
|
+
extend Mixin::Optionable
|
5
|
+
extend Mixin::Validatable
|
6
|
+
|
7
|
+
option :id
|
8
|
+
option :description
|
9
|
+
|
10
|
+
attr_reader :cookbook
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def initialize(cookbook, options = {})
|
14
|
+
@cookbook, @options = cookbook, options
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
raise Error::AbstractMethod.new(method: 'Action::Base#run')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Stove
|
2
|
+
class Action::Bump < Action::Base
|
3
|
+
id 'bump'
|
4
|
+
description 'Perform a version bump the local version automatically'
|
5
|
+
|
6
|
+
validate(:changed) do
|
7
|
+
cookbook.version != options[:version]
|
8
|
+
end
|
9
|
+
|
10
|
+
validate(:incremented) do
|
11
|
+
version = Gem::Version.new(options[:version])
|
12
|
+
Gem::Requirement.new("> #{cookbook.version}").satisfied_by?(version)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
log.info('Performing version bump')
|
17
|
+
log.debug("Version is currently #{cookbook.version}")
|
18
|
+
log.debug("Bumped version is #{options[:version]}")
|
19
|
+
|
20
|
+
cookbook.bump(options[:version])
|
21
|
+
|
22
|
+
log.debug("Version is now #{cookbook.version}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Stove
|
2
|
+
class Action::Changelog < Action::Base
|
3
|
+
id 'changelog'
|
4
|
+
description 'Generate and prompt for a CHANGELOG'
|
5
|
+
|
6
|
+
validate(:exists) do
|
7
|
+
File.exists?('CHANGELOG.md')
|
8
|
+
end
|
9
|
+
|
10
|
+
validate(:format) do
|
11
|
+
lines = File.read('CHANGELOG.md')
|
12
|
+
lines.match(/^[\w\s]+\n=+(.*\n)+v[0-9\.]+(\ \(.+\))?\n\-+/)
|
13
|
+
end
|
14
|
+
|
15
|
+
validate(:editor) do
|
16
|
+
!ENV['EDITOR'].nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
log.info('Generating new Changelog')
|
21
|
+
log.debug("Generated changeset:\n#{default_changeset}")
|
22
|
+
|
23
|
+
# Open a file prompt for changes
|
24
|
+
prompt_for_changeset
|
25
|
+
|
26
|
+
log.debug("New changeset:\n#{cookbook.changeset}")
|
27
|
+
|
28
|
+
# Write the new changelog to disk
|
29
|
+
path = File.join(cookbook.path, 'CHANGELOG.md')
|
30
|
+
contents = File.readlines(path)
|
31
|
+
index = contents.find_index { |line| line =~ /^(--)+/ }
|
32
|
+
|
33
|
+
log.debug("Writing changelog at `#{path}', index #{index}")
|
34
|
+
|
35
|
+
contents.insert(index - 2, "\n" + cookbook.changeset + "\n\n")
|
36
|
+
|
37
|
+
File.open(path, 'w') { |file| file.write(contents.join('')) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def prompt_for_changeset
|
41
|
+
tempfile = Tempfile.new(["#{cookbook.name}-changeset-#{Time.now}", '.md'])
|
42
|
+
tempfile.write(default_changeset)
|
43
|
+
tempfile.rewind
|
44
|
+
|
45
|
+
# Shell out to the default editor
|
46
|
+
system %Q|$EDITOR "#{tempfile.path}"|
|
47
|
+
|
48
|
+
# Save the resulting changes back to the cookbook object
|
49
|
+
cookbook.changeset = File.read(tempfile.path).strip
|
50
|
+
|
51
|
+
# Cleanup
|
52
|
+
tempfile.close
|
53
|
+
tempfile.unlink
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_changeset
|
57
|
+
return @default_changeset if @default_changeset
|
58
|
+
|
59
|
+
header = "v#{cookbook.version} (#{Time.now.to_date})"
|
60
|
+
|
61
|
+
contents = []
|
62
|
+
contents << header
|
63
|
+
contents << '-'*header.length
|
64
|
+
contents << cookbook.changeset || 'Enter CHANGELOG entries here'
|
65
|
+
contents << ''
|
66
|
+
|
67
|
+
@default_changeset = contents.join("\n")
|
68
|
+
@default_changeset
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Stove
|
2
|
+
class Action::Dev < Action::Base
|
3
|
+
id 'dev'
|
4
|
+
description 'Bump a minor version release for development purposes'
|
5
|
+
|
6
|
+
def run
|
7
|
+
log.info('Bumping for development release')
|
8
|
+
log.debug("Version is currently #{cookbook.version}")
|
9
|
+
log.debug("Bumped version is #{dev_version}")
|
10
|
+
|
11
|
+
cookbook.bump(dev_version)
|
12
|
+
|
13
|
+
log.debug("Version is now #{cookbook.version}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def dev_version
|
17
|
+
split = cookbook.version.split('.').map(&:to_i)
|
18
|
+
split[2] += 1
|
19
|
+
split.join('.')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Stove
|
2
|
+
class Action::Upload < Action::Base
|
3
|
+
id 'upload'
|
4
|
+
description 'Upload the cookbook to the community site'
|
5
|
+
|
6
|
+
validate(:configuration) do
|
7
|
+
Config.has_key?(:community)
|
8
|
+
end
|
9
|
+
|
10
|
+
validate(:username) do
|
11
|
+
Config[:community].has_key?(:username)
|
12
|
+
end
|
13
|
+
|
14
|
+
validate(:key) do
|
15
|
+
Config[:community].has_key?(:key)
|
16
|
+
end
|
17
|
+
|
18
|
+
validate(:category) do
|
19
|
+
!cookbook.category.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
log.info('Uploading to the Chef community site')
|
24
|
+
Community.upload(cookbook)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|