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
@@ -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"
@@ -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
@@ -1,7 +0,0 @@
1
- module Stove
2
- module Formatter
3
- require_relative 'formatter/base'
4
- require_relative 'formatter/human'
5
- require_relative 'formatter/silent'
6
- end
7
- end
@@ -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
@@ -1,9 +0,0 @@
1
- module Stove
2
- module Formatter
3
- class Human < Base
4
- def upload(cookbook)
5
- puts "Uploaded #{cookbook.name} (#{cookbook.version}) to '#{cookbook.url}'"
6
- end
7
- end
8
- end
9
- end
@@ -1,10 +0,0 @@
1
- module Stove
2
- module Formatter
3
- # Silence all output
4
- class Silent < Base
5
- Stove::Formatter::Base.formatter_methods.each do |name|
6
- define_method(name) do |*args|; end
7
- end
8
- end
9
- end
10
- 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
@@ -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