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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +16 -0
  4. data/README.md +41 -29
  5. data/Rakefile +15 -0
  6. data/bin/bake +1 -2
  7. data/features/actions/bump.feature +22 -0
  8. data/features/actions/changelog.feature +45 -0
  9. data/features/actions/dev.feature +18 -0
  10. data/features/actions/upload.feature +48 -0
  11. data/features/plugins/git.feature +24 -0
  12. data/features/rake.feature +1 -2
  13. data/features/step_definitions/cli_steps.rb +1 -27
  14. data/features/step_definitions/{community_site_steps.rb → community_steps.rb} +9 -5
  15. data/features/step_definitions/config_steps.rb +24 -0
  16. data/features/step_definitions/cookbook_steps.rb +28 -6
  17. data/features/step_definitions/cucumber_steps.rb +12 -0
  18. data/features/step_definitions/git_steps.rb +10 -7
  19. data/features/support/env.rb +12 -28
  20. data/features/support/stove/git.rb +48 -0
  21. data/lib/stove.rb +102 -19
  22. data/lib/stove/actions/base.rb +21 -0
  23. data/lib/stove/actions/bump.rb +25 -0
  24. data/lib/stove/actions/changelog.rb +71 -0
  25. data/lib/stove/actions/dev.rb +22 -0
  26. data/lib/stove/actions/finish.rb +8 -0
  27. data/lib/stove/actions/start.rb +7 -0
  28. data/lib/stove/actions/upload.rb +27 -0
  29. data/lib/stove/cli.rb +107 -79
  30. data/lib/stove/community.rb +124 -0
  31. data/lib/stove/config.rb +62 -13
  32. data/lib/stove/cookbook.rb +76 -238
  33. data/lib/stove/cookbook/metadata.rb +16 -11
  34. data/lib/stove/error.rb +13 -107
  35. data/lib/stove/filter.rb +59 -0
  36. data/lib/stove/jira.rb +74 -30
  37. data/lib/stove/middlewares/chef_authentication.rb +60 -0
  38. data/lib/stove/middlewares/exceptions.rb +17 -0
  39. data/lib/stove/mixins/filterable.rb +11 -0
  40. data/lib/stove/mixins/insideable.rb +13 -0
  41. data/lib/stove/mixins/instanceable.rb +23 -0
  42. data/lib/stove/mixins/loggable.rb +32 -0
  43. data/lib/stove/mixins/optionable.rb +41 -0
  44. data/lib/stove/mixins/validatable.rb +7 -0
  45. data/lib/stove/packager.rb +23 -22
  46. data/lib/stove/plugins/base.rb +35 -0
  47. data/lib/stove/plugins/git.rb +71 -0
  48. data/lib/stove/plugins/github.rb +108 -0
  49. data/lib/stove/plugins/jira.rb +72 -0
  50. data/lib/stove/rake_task.rb +56 -37
  51. data/lib/stove/runner.rb +84 -0
  52. data/lib/stove/util.rb +56 -0
  53. data/lib/stove/validator.rb +67 -0
  54. data/lib/stove/version.rb +1 -1
  55. data/locales/en.yml +231 -0
  56. data/stove.gemspec +11 -11
  57. metadata +85 -67
  58. data/features/changelog.feature +0 -22
  59. data/features/cli.feature +0 -11
  60. data/features/devodd.feature +0 -19
  61. data/features/git.feature +0 -34
  62. data/features/upload.feature +0 -40
  63. data/lib/stove/community_site.rb +0 -85
  64. data/lib/stove/formatter.rb +0 -7
  65. data/lib/stove/formatter/base.rb +0 -32
  66. data/lib/stove/formatter/human.rb +0 -9
  67. data/lib/stove/formatter/silent.rb +0 -10
  68. data/lib/stove/git.rb +0 -82
  69. data/lib/stove/github.rb +0 -43
  70. data/lib/stove/logger.rb +0 -56
  71. data/lib/stove/uploader.rb +0 -64
  72. data/spec/support/community_site.rb +0 -33
  73. 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
- Dir.chdir(fake_git_remote) do
3
- shellout 'touch myfile.txt'
4
- git 'add myfile.txt'
5
- git 'commit -m "Add a new file"'
6
- end
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 will( not)? have the commit "(.+)"$/ do |negate, message|
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 will( not)? have the tag "(.+)"$/ do |negate, tag|
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
@@ -1,40 +1,24 @@
1
+ require 'bundler/setup'
2
+
1
3
  require 'aruba/api'
2
4
  require 'aruba/cucumber'
3
- require 'aruba/in_process'
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
- require_relative '../../spec/support/community_site'
8
- require_relative '../../spec/support/git'
14
+ require File.expand_path('../stove/git', __FILE__)
9
15
 
10
16
  World(Aruba::Api)
11
- World(Stove::RSpec::Git)
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
- @dirs = [Dir.mktmpdir]
28
- Stove::RSpec::CommunitySite.reset!
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
- require_relative 'stove/config'
3
- require_relative 'stove/git'
4
- require_relative 'stove/logger'
5
-
6
- require_relative 'stove/cli'
7
- require_relative 'stove/community_site'
8
- require_relative 'stove/cookbook'
9
- require_relative 'stove/error'
10
- require_relative 'stove/formatter'
11
- require_relative 'stove/github'
12
- require_relative 'stove/jira'
13
- require_relative 'stove/mash'
14
- require_relative 'stove/packager'
15
- require_relative 'stove/uploader'
16
- require_relative 'stove/version'
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
- def formatter
20
- @formatter ||= Stove::Formatter::Human.new
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
- def set_formatter(name)
24
- @formatter = Stove::Formatter::Base.formatters[name.to_sym].new
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,8 @@
1
+ module Stove
2
+ class Action::Finish < Action::Base
3
+ def run
4
+ log.debug('Running cleanup hooks...')
5
+ log.info('Done!')
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Stove
2
+ class Action::Start < Action::Base
3
+ def run
4
+ log.info("Running Stove #{Stove::VERSION} on `#{cookbook.name}'")
5
+ end
6
+ end
7
+ 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