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.
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