stove 5.1.0 → 5.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86092b84ffa5f03569b57944b56f09c82c0b24a9
4
- data.tar.gz: b42a43be5248d0ab029a93abf3a54e906521a88b
3
+ metadata.gz: 8a7b6e003eaa819a032278c65bdb28317ba36543
4
+ data.tar.gz: 71ab7d2ff0f46152784c3e4d0d60dec1cf157f01
5
5
  SHA512:
6
- metadata.gz: 7f694c9b7f8921db5683cfdc2fd326e2458545a54ba5ac9eb9ff17c5ed6e463343404a7f57ba45a55d29b8da43b429d35ca1f3037b79ff7a44ad0436d019807a
7
- data.tar.gz: a79d23a0c76f6917ef065a3a8b09afc5e78bcc2218326efb8eef091ee35bc69e300579038818d5e9a8a1e74a0d6d2d0ab7dc46580ad3b8d33145e5795846f0fd
6
+ metadata.gz: cf2361dfafbaaca8e631d0b807f6b08c9fb0c1322c0865a7ac1188fc07c1e3ada760b4bd2af1712f2e295b5595d8bd3cc0869e82a534d0f0fd71f97ea953c5c7
7
+ data.tar.gz: a8a0a61d146ac8c7df835a5ff6613528d69c664e293edbffeca7fd7b8a9c8cf34f642f7a2c2492360615ac0c75b8f42116d96d299ea485ce637156692ed83ec1
@@ -2,6 +2,10 @@
2
2
 
3
3
  This is the Changelog for the Stove gem.
4
4
 
5
+ ## v5.2.0 (2017-05-12)
6
+
7
+ - Support for uploading to Artifactory
8
+
5
9
  ## v5.1.0 (2017-03-31)
6
10
 
7
11
  - Match Chef's metadata behavior with chef_version and ohai_version to allow version constraints like '>= 12.5', '< 14'
data/README.md CHANGED
@@ -76,6 +76,21 @@ And then use rake to publish the cookbook:
76
76
  $ bin/rake publish
77
77
  ```
78
78
 
79
+ ## Artifactory Support
80
+
81
+ In addition to uploading to a standard [Supermarket](https://github.com/chef/supermarket) server, Stove can upload to an Artifactory repository [configured to be in Chef mode](https://www.jfrog.com/confluence/display/RTF/Chef+Cookbook+Repositories).
82
+
83
+ You specify the URL to upload to via `--artifactory`, which you can find the URL Artifactory repository configuration. Your API key can be specified either in the `$ARTIFACTORY_API_KEY` environment variable or via `--artifactory-key`:
84
+
85
+ ```bash
86
+ # API key in $ARTIFACTORY_API_KEY
87
+ $ stove --artifactory https://artifactory.example.com/api/chef/nameofrepo
88
+ # API key in a file
89
+ $ stove --artifactory https://artifactory.example.com/api/chef/nameofrepo --artifactory-key=@path/to/key/file
90
+ # API key on the command line
91
+ $ stove --artifactory https://artifactory.example.com/api/chef/nameofrepo --artifactory-key=XZg6tefUZYfiglmSbQ1s1e6habk
92
+ ```
93
+
79
94
  ## Contributing
80
95
 
81
96
  1. Fork it
@@ -2,18 +2,19 @@ require 'logify'
2
2
  require 'pathname'
3
3
 
4
4
  module Stove
5
- autoload :Community, 'stove/community'
6
- autoload :Config, 'stove/config'
7
- autoload :Cookbook, 'stove/cookbook'
8
- autoload :Cli, 'stove/cli'
9
- autoload :Error, 'stove/error'
10
- autoload :Filter, 'stove/filter'
11
- autoload :Mash, 'stove/mash'
12
- autoload :Packager, 'stove/packager'
13
- autoload :Runner, 'stove/runner'
14
- autoload :Util, 'stove/util'
15
- autoload :Validator, 'stove/validator'
16
- autoload :VERSION, 'stove/version'
5
+ autoload :Artifactory, 'stove/artifactory'
6
+ autoload :Community, 'stove/community'
7
+ autoload :Config, 'stove/config'
8
+ autoload :Cookbook, 'stove/cookbook'
9
+ autoload :Cli, 'stove/cli'
10
+ autoload :Error, 'stove/error'
11
+ autoload :Filter, 'stove/filter'
12
+ autoload :Mash, 'stove/mash'
13
+ autoload :Packager, 'stove/packager'
14
+ autoload :Runner, 'stove/runner'
15
+ autoload :Util, 'stove/util'
16
+ autoload :Validator, 'stove/validator'
17
+ autoload :VERSION, 'stove/version'
17
18
 
18
19
  module Middleware
19
20
  autoload :ChefAuthentication, 'stove/middlewares/chef_authentication'
@@ -28,9 +29,10 @@ module Stove
28
29
  end
29
30
 
30
31
  module Plugin
31
- autoload :Base, 'stove/plugins/base'
32
- autoload :Community, 'stove/plugins/community'
33
- autoload :Git, 'stove/plugins/git'
32
+ autoload :Artifactory, 'stove/plugins/artifactory'
33
+ autoload :Base, 'stove/plugins/base'
34
+ autoload :Community, 'stove/plugins/community'
35
+ autoload :Git, 'stove/plugins/git'
34
36
  end
35
37
 
36
38
  #
@@ -0,0 +1,83 @@
1
+ require 'net/http'
2
+
3
+ module Stove
4
+ class Artifactory
5
+ include Mixin::Instanceable
6
+
7
+ #
8
+ # Upload a cookbook to an Artifactory server.
9
+ #
10
+ # @param [Cookbook] cookbook
11
+ # the cookbook to upload
12
+ #
13
+ def upload(cookbook, extended_metadata = false)
14
+ # Artifactory doesn't prevent uploading over an existing release in
15
+ # some cases so let's check for that. Seriously never do this, go delete
16
+ # and then re-upload if you have to.
17
+ response = request(:get, "api/v1/cookbooks/#{cookbook.name}/versions/#{cookbook.version}")
18
+ # Artifactory's version of the cookbook_version endpoint returns an
19
+ # empty 200 on an unknown version.
20
+ unless response.code == '404' || (response.code == '200' && response.body.to_s == '')
21
+ raise Error::CookbookAlreadyExists.new(cookbook: cookbook)
22
+ end
23
+
24
+ # Run the upload.
25
+ response = request(:post, "api/v1/cookbooks/#{cookbook.name}.tgz") do |req|
26
+ req.body_stream = cookbook.tarball(extended_metadata)
27
+ req.content_length = req.body_stream.size
28
+ req['Content-Type'] = 'application/x-binary'
29
+ end
30
+ response.error! unless response.code == '201'
31
+ end
32
+
33
+ private
34
+
35
+ #
36
+ # Create an HTTP connect to the Artifactory server.
37
+ #
38
+ # @return [Net::HTTP]
39
+ #
40
+ def connection
41
+ @connection ||= begin
42
+ uri = URI(Config.artifactory.strip)
43
+ # Open the HTTP connection to artifactory.
44
+ http = Net::HTTP.new(uri.host, uri.port)
45
+ if uri.scheme == 'https'
46
+ http.use_ssl = true
47
+ # Mimic the behavior of the Cookbook uploader for SSL verification.
48
+ if ENV['STOVE_NO_SSL_VERIFY'] || !Config.ssl_verify
49
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
50
+ end
51
+ end
52
+ http.start
53
+ end
54
+ end
55
+
56
+ #
57
+ # Send an HTTP request to the Artifactory server.
58
+ #
59
+ # @param [Symbol] method
60
+ # HTTP method to use
61
+ #
62
+ # @param [String] path
63
+ # URI path to request
64
+ #
65
+ # @param [Proc] block
66
+ # Optional block to set request values
67
+ #
68
+ # @return [Net::HTTPResponse]
69
+ #
70
+ def request(method, path, &block)
71
+ uri_string = Config.artifactory.strip
72
+ # Make sure we end up with the right number of separators.
73
+ uri_string << '/' unless uri_string.end_with?('/')
74
+ uri_string << path
75
+ uri = URI(uri_string)
76
+ request = Net::HTTP.const_get(method.to_s.capitalize).new(uri)
77
+ request['X-Jfrog-Art-Api'] = Config.artifactory_key.strip
78
+ block.call(request) if block
79
+ connection.request(request)
80
+ end
81
+
82
+ end
83
+ end
@@ -36,10 +36,12 @@ module Stove
36
36
  end
37
37
 
38
38
  # Override configs
39
- Config.endpoint = options[:endpoint] if options[:endpoint]
40
- Config.username = options[:username] if options[:username]
41
- Config.key = options[:key] if options[:key]
42
- Config.ssl_verify = options[:ssl_verify]
39
+ Config.endpoint = options[:endpoint] if options[:endpoint]
40
+ Config.username = options[:username] if options[:username]
41
+ Config.key = options[:key] if options[:key]
42
+ Config.artifactory = options[:artifactory] if options[:artifactory]
43
+ Config.artifactory_key = options[:artifactory_key] if options[:artifactory_key]
44
+ Config.ssl_verify = options[:ssl_verify]
43
45
 
44
46
  # Set the log level
45
47
  Stove.log_level = options[:log_level]
@@ -141,6 +143,23 @@ module Stove
141
143
  options[:sign] = true
142
144
  end
143
145
 
146
+ opts.separator ''
147
+ opts.separator 'Artifactory Options:'
148
+
149
+ opts.on('--artifactory [URL]', 'URL for the Artifactory repository') do |v|
150
+ options[:artifactory] = v
151
+ end
152
+
153
+ opts.on('--artifactory-key [KEY]', 'Artifactory API key to use') do |v|
154
+ options[:artifactory_key] = if v[0] == '@'
155
+ # If passed a key looking like @foo, read it as a file. This allows
156
+ # passing in the key securely.
157
+ IO.read(File.expand_path(v[1..-1]))
158
+ else
159
+ v
160
+ end
161
+ end
162
+
144
163
  opts.separator ''
145
164
  opts.separator 'Global Options:'
146
165
 
@@ -182,6 +201,10 @@ module Stove
182
201
  :branch => 'master',
183
202
  :sign => false,
184
203
 
204
+ # Artifactory options
205
+ :artifactory => false,
206
+ :artifactory_key => ENV['ARTIFACTORY_API_KEY'],
207
+
185
208
  # Global options
186
209
  :log_level => :warn,
187
210
  :path => Dir.pwd,
@@ -63,7 +63,7 @@ module Stove
63
63
  end
64
64
 
65
65
  #
66
- # Delete the given cookbook from the communit site.
66
+ # Delete the given cookbook from the community site.
67
67
  #
68
68
  # @param [String] name
69
69
  # the name of the cookbook to delete
@@ -40,6 +40,7 @@ module Stove
40
40
  class GitFailed < StoveError; end
41
41
  class MetadataNotFound < StoveError; end
42
42
  class ServerUnavailable < StoveError; end
43
+ class CookbookAlreadyExists < StoveError; end
43
44
 
44
45
  # Validations
45
46
  class ValidationFailed < StoveError; end
@@ -49,5 +50,6 @@ module Stove
49
50
  class GitCleanValidationFailed < ValidationFailed; end
50
51
  class GitRepositoryValidationFailed < ValidationFailed; end
51
52
  class GitUpToDateValidationFailed < ValidationFailed; end
53
+ class ArtifactoryKeyValidationFailed < ValidationFailed; end
52
54
  end
53
55
  end
@@ -0,0 +1,14 @@
1
+ module Stove
2
+ class Plugin::Artifactory < Plugin::Base
3
+ id 'artifactory'
4
+ description 'Publish the release to an Artifactory server'
5
+
6
+ validate(:key) do
7
+ Config.artifactory_key && !Config.artifactory_key.strip.empty?
8
+ end
9
+
10
+ run('Publishing the release to the Artifactory server') do
11
+ Artifactory.upload(cookbook, options[:extended_metadata])
12
+ end
13
+ end
14
+ end
@@ -12,7 +12,11 @@ module Stove
12
12
 
13
13
  def run
14
14
  run_plugin :git
15
- run_plugin :community
15
+ if Config.artifactory
16
+ run_plugin :artifactory
17
+ else
18
+ run_plugin :community
19
+ end
16
20
  end
17
21
 
18
22
  private
@@ -1,3 +1,3 @@
1
1
  module Stove
2
- VERSION = '5.1.0'
2
+ VERSION = '5.2.0'
3
3
  end
@@ -0,0 +1 @@
1
+ default['key'] = 'value'
@@ -0,0 +1,2 @@
1
+ name 'stove_integration_test'
2
+ version '1.0.0'
@@ -0,0 +1 @@
1
+ log 'this is a test recipe'
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ # This integration test uses some environment variables to configure which
4
+ # Artifactory server to talk to, as there is no ArtifactoryZero to test against.
5
+ # If those aren't present, we skip the tests.
6
+ #
7
+ # $TEST_STOVE_ARTIFACTORY - URL to the Chef virtual repository.
8
+ # $TEST_STOVE_ARTIFACTORY_REAL - URL to the non-virtual repository.
9
+ # $TEST_STOVE_ARTIFACTORY_API_KEY - API key to use.
10
+
11
+ describe 'artifactory integration test', artifactory_integration: true do
12
+ include RSpecCommand
13
+ let(:upload_url) { ENV['TEST_STOVE_ARTIFACTORY'] }
14
+ let(:delete_url) { ENV['TEST_STOVE_ARTIFACTORY_REAL'] }
15
+ let(:api_key) { ENV['TEST_STOVE_ARTIFACTORY_API_KEY'] }
16
+ around do |ex|
17
+ WebMock.disable!
18
+ request(:delete, "#{delete_url}/stove_integration_test")
19
+ begin
20
+ ex.run
21
+ ensure
22
+ request(:delete, "#{delete_url}/stove_integration_test")
23
+ WebMock.enable!
24
+ end
25
+ end
26
+
27
+ def request(method, url)
28
+ uri = URI(url)
29
+ req = Net::HTTP.const_get(method.to_s.capitalize).new(uri)
30
+ req['X-Jfrog-Art-Api'] = api_key
31
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
32
+ http.request(req)
33
+ end
34
+ end
35
+
36
+ let(:upload) { JSON.parse(request(:get, "#{upload_url}/api/v1/cookbooks/stove_integration_test/versions/1.0.0").body) }
37
+
38
+ describe 'help output' do
39
+ command 'stove --help'
40
+ its(:stdout) { is_expected.to include('--artifactory ').and(include('--artifactory-key ')) }
41
+ end
42
+
43
+ describe 'uploading a cookbook' do
44
+ context 'with no key' do
45
+ fixture_file 'integration_cookbook'
46
+ command(nil, allow_error: true) { "stove --no-git --artifactory #{upload_url}" }
47
+
48
+ it 'fails to upload' do
49
+ expect(subject.exitstatus).to_not eq 0
50
+ expect(subject.stdout).to match /You did not specify and Artifactory API key/
51
+ end
52
+ end
53
+
54
+ context 'with $ARTIFACTORY_API_KEY' do
55
+ before { _environment['ARTIFACTORY_API_KEY'] = api_key }
56
+ fixture_file 'integration_cookbook'
57
+ command { "stove --no-git --artifactory #{upload_url}" }
58
+
59
+ it 'uploads the cookbook' do
60
+ expect(subject.stdout).to eq ''
61
+ expect(upload['version']).to eq '1.0.0'
62
+ end
63
+ end
64
+
65
+ context 'with --artifactory-key=@key' do
66
+ fixture_file 'integration_cookbook'
67
+ file('key') { api_key }
68
+ command { "stove --no-git --artifactory #{upload_url} --artifactory-key=@key" }
69
+
70
+ it 'uploads the cookbook' do
71
+ expect(subject.stdout).to eq ''
72
+ expect(upload['version']).to eq '1.0.0'
73
+ end
74
+ end
75
+
76
+ context 'with --artifactory-key=key' do
77
+ fixture_file 'integration_cookbook'
78
+ file('key') { api_key }
79
+ # Using allow_error here so the command isn't shown if things fail.
80
+ command(nil, allow_error: true) { "stove --no-git --artifactory #{upload_url} --artifactory-key=#{api_key}" }
81
+
82
+ it 'uploads the cookbook' do
83
+ expect(subject.stdout).to eq ''
84
+ expect(subject.exitstatus).to eq 0
85
+ expect(upload['version']).to eq '1.0.0'
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -1,4 +1,6 @@
1
1
  require 'rspec'
2
+ require 'webmock/rspec'
3
+ require 'rspec_command'
2
4
  require 'stove'
3
5
 
4
6
  RSpec.configure do |config|
@@ -8,13 +10,17 @@ RSpec.configure do |config|
8
10
 
9
11
  # Basic configuraiton
10
12
  config.run_all_when_everything_filtered = true
11
- config.filter_run(:focus)
13
+ config.filter_run(:focus) unless ENV['CI']
12
14
 
13
15
  # Run specs in random order to surface order dependencies. If you find an
14
16
  # order dependency and want to debug it, you can fix the order by providing
15
17
  # the seed, which is printed after each run.
16
18
  # --seed 1234
17
19
  config.order = 'random'
20
+
21
+ # Don't try running the Artifactory integration tests without the config for it.
22
+ # See integration/artifactory_spec for more info.
23
+ config.filter_run_excluding(:artifactory_integration) unless ENV['TEST_STOVE_ARTIFACTORY']
18
24
  end
19
25
 
20
26
  def tmp_path
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ # Can't use a double() for this because of the late-binding way StoveError
4
+ # renders the Erb content, which is after the example ends.
5
+ FakeCookbook = Struct.new(:name, :version)
6
+
7
+ describe Stove::Artifactory do
8
+ before { Stove::Config.ssl_verify = true }
9
+
10
+ describe '.upload' do
11
+ let(:cookbook) { FakeCookbook.new('testcook', '1.2.3') }
12
+ subject { described_class.upload(cookbook) }
13
+ before do
14
+ # Default configuration for the test.
15
+ Stove::Config.artifactory = 'https://artifactory.example/api/chef/chef'
16
+ Stove::Config.artifactory_key = 'secret'
17
+ # cookbook.tarball() stub.
18
+ allow(cookbook).to receive(:tarball) {|ext| StringIO.new(ext ? 'extended' : 'simple') }
19
+ end
20
+
21
+ context 'with defaults' do
22
+ it 'uploads the file' do
23
+ stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
24
+ stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 201)
25
+ expect { subject }.to_not raise_error
26
+ end
27
+ end
28
+
29
+ context 'with a 404 for an unknown cookbook' do
30
+ # This is how real Supermarket returns a non-existent cookbook so make sure it works with that too.
31
+ it 'uploads the file' do
32
+ stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(status: 404, body: '{some json error}')
33
+ stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 201)
34
+ expect { subject }.to_not raise_error
35
+ end
36
+ end
37
+
38
+ context 'with extended_metadata = true' do
39
+ subject { described_class.upload(cookbook, true) }
40
+ it 'uploads with extended metadata' do
41
+ stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
42
+ stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'extended').to_return(status: 201)
43
+ expect { subject }.to_not raise_error
44
+ end
45
+ end
46
+
47
+ context 'with a colliding upload' do
48
+ it 'should raise an exception' do
49
+ stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '{some json}')
50
+ expect { subject }.to raise_error Stove::Error::CookbookAlreadyExists
51
+ end
52
+ end
53
+
54
+ context 'with a failed upload' do
55
+ it 'uploads the file' do
56
+ stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
57
+ stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 500)
58
+ expect { subject }.to raise_error Net::HTTPFatalError
59
+ end
60
+ end
61
+
62
+ context 'with a newline in the API key' do
63
+ before { Stove::Config.artifactory_key = "secret\n" }
64
+ it 'uploads the file' do
65
+ stub_request(:get, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook/versions/1.2.3').with(headers: {'X-Jfrog-Art-Api' => 'secret'}).to_return(body: '')
66
+ stub_request(:post, 'https://artifactory.example/api/chef/chef/api/v1/cookbooks/testcook.tgz').with(headers: {'X-Jfrog-Art-Api' => 'secret'}, body: 'simple').to_return(status: 201)
67
+ expect { subject }.to_not raise_error
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ # Break encapsulation a bit to test the ssl_verify configuration.
74
+ describe '#connection' do
75
+ let(:url) { 'https://artifactory.example/api/chef/chef' }
76
+ let(:http) { double('Net::HTTP') }
77
+ # Make sure we don't use the singleton instance so this stub HTTP object
78
+ # doesn't get cached on it.
79
+ subject { described_class.send(:new).send(:connection) }
80
+ before do
81
+ allow(http).to receive(:start).and_return(http)
82
+ Stove::Config.artifactory = url
83
+ end
84
+
85
+ context 'with an HTTPS URI' do
86
+ it 'enables TLS' do
87
+ expect(Net::HTTP).to receive(:new).with('artifactory.example', 443).and_return(http)
88
+ expect(http).to receive(:use_ssl=).with(true)
89
+ expect(subject).to eq http
90
+ end
91
+ end
92
+
93
+ context 'with an HTTP URI' do
94
+ let(:url) { 'http://artifactory.example/api/chef/chef' }
95
+ it 'does not enable TLS' do
96
+ expect(Net::HTTP).to receive(:new).with('artifactory.example', 80).and_return(http)
97
+ expect(http).to_not receive(:use_ssl=)
98
+ expect(subject).to eq http
99
+ end
100
+ end
101
+
102
+ context 'with Config.ssl_verify = false' do
103
+ before { Stove::Config.ssl_verify = false }
104
+ it 'sets verify mode VERIFY_NONE' do
105
+ expect(Net::HTTP).to receive(:new).with('artifactory.example', 443).and_return(http)
106
+ expect(http).to receive(:use_ssl=).with(true)
107
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
108
+ expect(subject).to eq http
109
+ end
110
+ end
111
+
112
+ context 'with $STOVE_NO_SSL_VERIFY' do
113
+ around do |ex|
114
+ old_value = ENV['STOVE_NO_SSL_VERIFY']
115
+ ENV['STOVE_NO_SSL_VERIFY'] = 'true'
116
+ begin
117
+ ex.run
118
+ ensure
119
+ ENV['STOVE_NO_SSL_VERIFY'] = old_value
120
+ end
121
+ end
122
+ it 'sets verify mode VERIFY_NONE' do
123
+ expect(Net::HTTP).to receive(:new).with('artifactory.example', 443).and_return(http)
124
+ expect(http).to receive(:use_ssl=).with(true)
125
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
126
+ expect(subject).to eq http
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -29,4 +29,6 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'community-zero', '~> 2.0'
30
30
  spec.add_development_dependency 'rake'
31
31
  spec.add_development_dependency 'rspec', '~> 3.0'
32
+ spec.add_development_dependency 'rspec-command', '~> 1.0'
33
+ spec.add_development_dependency 'webmock', '~> 3.0'
32
34
  end
@@ -0,0 +1,11 @@
1
+ You did not specify an Artifactory API key! You can pass the key either via a command line argument:
2
+
3
+ stove --artifactory-key sUeEpLXJvfhw3UZHbVPrGCgdou8VI6fXpD5sUHd0pKAxCmuGWNHLgerpFPkCJ2EjBBPYcM4My
4
+
5
+ Or via a file path:
6
+
7
+ stove --artifactory-key @~/.artifactory/api.key
8
+
9
+ Or via an environment variable:
10
+
11
+ export ARTIFACTORY_API_KEY=sUeEpLXJvfhw3UZHbVPrGCgdou8VI6fXpD5sUHd0pKAxCmuGWNHLgerpFPkCJ2EjBBPYcM4My
@@ -0,0 +1,5 @@
1
+ Cookbook <%= @cookbook.name %> already has a version <%= @cookbook.version %> uploaded.
2
+
3
+ Please change the version number before uploading again.
4
+
5
+ If you need to change an existing version, please remove it first.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stove
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Vargo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-31 00:00:00.000000000 Z
11
+ date: 2017-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-api
@@ -108,6 +108,34 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-command
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
111
139
  description: A utility for releasing Chef community cookbooks
112
140
  email:
113
141
  - sethvargo@gmail.com
@@ -137,6 +165,7 @@ files:
137
165
  - features/support/stove.pem
138
166
  - features/support/stove/git.rb
139
167
  - lib/stove.rb
168
+ - lib/stove/artifactory.rb
140
169
  - lib/stove/cli.rb
141
170
  - lib/stove/community.rb
142
171
  - lib/stove/config.rb
@@ -150,6 +179,7 @@ files:
150
179
  - lib/stove/mixins/optionable.rb
151
180
  - lib/stove/mixins/validatable.rb
152
181
  - lib/stove/packager.rb
182
+ - lib/stove/plugins/artifactory.rb
153
183
  - lib/stove/plugins/base.rb
154
184
  - lib/stove/plugins/community.rb
155
185
  - lib/stove/plugins/git.rb
@@ -158,15 +188,22 @@ files:
158
188
  - lib/stove/util.rb
159
189
  - lib/stove/validator.rb
160
190
  - lib/stove/version.rb
191
+ - spec/fixtures/integration_cookbook/attributes/default.rb
192
+ - spec/fixtures/integration_cookbook/metadata.rb
193
+ - spec/fixtures/integration_cookbook/recipes/default.rb
194
+ - spec/integration/artifactory_spec.rb
161
195
  - spec/integration/cookbook_spec.rb
162
196
  - spec/spec_helper.rb
163
197
  - spec/support/generators.rb
198
+ - spec/unit/artifactory_spec.rb
164
199
  - spec/unit/cookbook/metadata_spec.rb
165
200
  - spec/unit/error_spec.rb
166
201
  - stove.gemspec
167
202
  - templates/errors/abstract_method.erb
203
+ - templates/errors/artifactory_key_validation_failed.erb
168
204
  - templates/errors/community_key_validation_failed.erb
169
205
  - templates/errors/community_username_validation_failed.erb
206
+ - templates/errors/cookbook_already_exists.erb
170
207
  - templates/errors/git_clean_validation_failed.erb
171
208
  - templates/errors/git_failed.erb
172
209
  - templates/errors/git_repository_validation_failed.erb
@@ -209,8 +246,13 @@ test_files:
209
246
  - features/support/env.rb
210
247
  - features/support/stove.pem
211
248
  - features/support/stove/git.rb
249
+ - spec/fixtures/integration_cookbook/attributes/default.rb
250
+ - spec/fixtures/integration_cookbook/metadata.rb
251
+ - spec/fixtures/integration_cookbook/recipes/default.rb
252
+ - spec/integration/artifactory_spec.rb
212
253
  - spec/integration/cookbook_spec.rb
213
254
  - spec/spec_helper.rb
214
255
  - spec/support/generators.rb
256
+ - spec/unit/artifactory_spec.rb
215
257
  - spec/unit/cookbook/metadata_spec.rb
216
258
  - spec/unit/error_spec.rb