capsulecd 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.dockerignore +5 -0
- data/.gitignore +95 -0
- data/.rspec +3 -0
- data/.simplecov +9 -0
- data/Dockerfile +16 -0
- data/Dockerfile.chef +26 -0
- data/Dockerfile.javascript +7 -0
- data/Dockerfile.node +7 -0
- data/Dockerfile.python +7 -0
- data/Dockerfile.ruby +4 -0
- data/FEATURES.md +12 -0
- data/Gemfile +26 -0
- data/LICENSE.md +22 -0
- data/README.md +227 -0
- data/Rakefile +43 -0
- data/bin/capsulecd +4 -0
- data/capsulecd.gemspec +27 -0
- data/circle.yml +24 -0
- data/lib/capsulecd/base/common/git_utils.rb +90 -0
- data/lib/capsulecd/base/common/validation_utils.rb +22 -0
- data/lib/capsulecd/base/configuration.rb +151 -0
- data/lib/capsulecd/base/engine.rb +163 -0
- data/lib/capsulecd/base/runner/circleci.rb +37 -0
- data/lib/capsulecd/base/runner/default.rb +38 -0
- data/lib/capsulecd/base/source/github.rb +183 -0
- data/lib/capsulecd/base/transform_engine.rb +62 -0
- data/lib/capsulecd/chef/chef_engine.rb +172 -0
- data/lib/capsulecd/chef/chef_helper.rb +29 -0
- data/lib/capsulecd/cli.rb +64 -0
- data/lib/capsulecd/error.rb +51 -0
- data/lib/capsulecd/javascript/javascript_engine.rb +213 -0
- data/lib/capsulecd/node/node_engine.rb +141 -0
- data/lib/capsulecd/python/python_engine.rb +157 -0
- data/lib/capsulecd/ruby/ruby_engine.rb +191 -0
- data/lib/capsulecd/ruby/ruby_helper.rb +60 -0
- data/lib/capsulecd/version.rb +3 -0
- data/lib/capsulecd.rb +16 -0
- data/logo.svg +1 -0
- data/spec/fixtures/chef/cookbook_analogj_test/CHANGELOG.md +3 -0
- data/spec/fixtures/chef/cookbook_analogj_test/Gemfile +18 -0
- data/spec/fixtures/chef/cookbook_analogj_test/LICENSE +21 -0
- data/spec/fixtures/chef/cookbook_analogj_test/README.md +13 -0
- data/spec/fixtures/chef/cookbook_analogj_test/Rakefile +1 -0
- data/spec/fixtures/chef/cookbook_analogj_test/Thorfile +12 -0
- data/spec/fixtures/chef/cookbook_analogj_test/chefignore +94 -0
- data/spec/fixtures/chef/cookbook_analogj_test/metadata.rb +5 -0
- data/spec/fixtures/chef/cookbook_analogj_test/recipes/default.rb +6 -0
- data/spec/fixtures/incorrect_configuration.yml +4 -0
- data/spec/fixtures/javascript/javascript_analogj_test/LICENSE +21 -0
- data/spec/fixtures/javascript/javascript_analogj_test/README.md +2 -0
- data/spec/fixtures/javascript/javascript_analogj_test/package.json +19 -0
- data/spec/fixtures/node/npm_analogj_test/LICENSE +21 -0
- data/spec/fixtures/node/npm_analogj_test/README.md +2 -0
- data/spec/fixtures/node/npm_analogj_test/package.json +19 -0
- data/spec/fixtures/python/pip_analogj_test/LICENSE +21 -0
- data/spec/fixtures/python/pip_analogj_test/MANIFEST.in +1 -0
- data/spec/fixtures/python/pip_analogj_test/README.md +1 -0
- data/spec/fixtures/python/pip_analogj_test/VERSION +1 -0
- data/spec/fixtures/python/pip_analogj_test/setup.cfg +5 -0
- data/spec/fixtures/python/pip_analogj_test/setup.py +80 -0
- data/spec/fixtures/python/pip_analogj_test/tox.ini +14 -0
- data/spec/fixtures/ruby/gem_analogj_test/Gemfile +4 -0
- data/spec/fixtures/ruby/gem_analogj_test/LICENSE.txt +21 -0
- data/spec/fixtures/ruby/gem_analogj_test/README.md +41 -0
- data/spec/fixtures/ruby/gem_analogj_test/Rakefile +6 -0
- data/spec/fixtures/ruby/gem_analogj_test/bin/console +14 -0
- data/spec/fixtures/ruby/gem_analogj_test/bin/setup +8 -0
- data/spec/fixtures/ruby/gem_analogj_test/gem_analogj_test.gemspec +25 -0
- data/spec/fixtures/ruby/gem_analogj_test/lib/gem_analogj_test/version.rb +3 -0
- data/spec/fixtures/ruby/gem_analogj_test/lib/gem_analogj_test.rb +5 -0
- data/spec/fixtures/ruby/gem_analogj_test/spec/gem_analogj_test_spec.rb +7 -0
- data/spec/fixtures/ruby/gem_analogj_test/spec/spec_helper.rb +2 -0
- data/spec/fixtures/ruby/gem_analogj_test-0.1.4.gem +0 -0
- data/spec/fixtures/sample_chef_configuration.yml +8 -0
- data/spec/fixtures/sample_configuration.yml +7 -0
- data/spec/fixtures/sample_global_configuration.yml +23 -0
- data/spec/fixtures/sample_node_configuration.yml +7 -0
- data/spec/fixtures/sample_python_configuration.yml +8 -0
- data/spec/fixtures/sample_repo_configuration.yml +22 -0
- data/spec/fixtures/sample_ruby_configuration.yml +5 -0
- data/spec/fixtures/vcr_cassettes/chef_build_step.yml +636 -0
- data/spec/fixtures/vcr_cassettes/gem_build_step.yml +653 -0
- data/spec/fixtures/vcr_cassettes/gem_build_step_without_version_rb.yml +653 -0
- data/spec/fixtures/vcr_cassettes/integration_chef.yml +1399 -0
- data/spec/fixtures/vcr_cassettes/integration_node.yml +1388 -0
- data/spec/fixtures/vcr_cassettes/integration_python.yml +1388 -0
- data/spec/fixtures/vcr_cassettes/integration_ruby.yml +1377 -0
- data/spec/fixtures/vcr_cassettes/node_build_step.yml +647 -0
- data/spec/fixtures/vcr_cassettes/pip_build_step.yml +653 -0
- data/spec/lib/capsulecd/base/configuration_spec.rb +75 -0
- data/spec/lib/capsulecd/base/engine_spec.rb +51 -0
- data/spec/lib/capsulecd/base/source/github_spec.rb +253 -0
- data/spec/lib/capsulecd/base/transform_engine_spec.rb +55 -0
- data/spec/lib/capsulecd/chef/chef_engine_spec.rb +114 -0
- data/spec/lib/capsulecd/cli_spec.rb +57 -0
- data/spec/lib/capsulecd/node/node_engine_spec.rb +113 -0
- data/spec/lib/capsulecd/python/python_engine_spec.rb +118 -0
- data/spec/lib/capsulecd/ruby/ruby_engine_spec.rb +128 -0
- data/spec/spec_helper.rb +105 -0
- data/spec/support/file_system.rb +21 -0
- data/spec/support/package_types.rb +11 -0
- metadata +281 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'semverly'
|
2
|
+
require 'open3'
|
3
|
+
require 'bundler'
|
4
|
+
require 'json'
|
5
|
+
require_relative '../base/engine'
|
6
|
+
|
7
|
+
module CapsuleCD
|
8
|
+
module Javascript
|
9
|
+
class JavascriptEngine < Engine
|
10
|
+
def build_step
|
11
|
+
super
|
12
|
+
|
13
|
+
@_is_bower = File.exist?(@source_git_local_path + '/bower.json')
|
14
|
+
@_is_npm = File.exist?(@source_git_local_path + '/package.json')
|
15
|
+
|
16
|
+
# validate that the metadata files exist
|
17
|
+
if !@_is_bower && !@_is_npm
|
18
|
+
fail CapsuleCD::Error::BuildPackageInvalid, 'package.json or bower.json file must be present for Javascript packages'
|
19
|
+
end
|
20
|
+
|
21
|
+
# we can't bump the npm version here because the npm version patch command will set it.
|
22
|
+
# howerver we need to make sure the bower.json and package.json versions are insync.
|
23
|
+
# we'll take the latest version of either the package.json or bower.json and set that as the version of both.
|
24
|
+
sync_versions
|
25
|
+
|
26
|
+
# now that the bower and package versions are in sync, lets bump the version of bower.json
|
27
|
+
# (because package.json will be bumped automatically.)
|
28
|
+
if @_is_bower
|
29
|
+
bower_file = File.read(@source_git_local_path + '/bower.json')
|
30
|
+
bower_data = JSON.parse(bower_file)
|
31
|
+
next_version = bump_version(SemVer.parse(bower_data['version']))
|
32
|
+
bower_data['version'] = next_version.to_s
|
33
|
+
File.write(@source_git_local_path + '/bower.json', JSON.pretty_generate(bower_data))
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# TODO: check if this module name and version already exist.
|
38
|
+
|
39
|
+
# check for/create any required missing folders/files
|
40
|
+
unless File.exist?(@source_git_local_path + '/test')
|
41
|
+
FileUtils.mkdir(@source_git_local_path + '/test')
|
42
|
+
end
|
43
|
+
unless File.exist?(@source_git_local_path + '/.gitignore')
|
44
|
+
CapsuleCD::GitUtils.create_gitignore(@source_git_local_path, ['Node','Yeoman'])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_step
|
49
|
+
super
|
50
|
+
|
51
|
+
if @_is_npm
|
52
|
+
# the module has already been downloaded. lets make sure all its dependencies are available.
|
53
|
+
Open3.popen3('npm install', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
54
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
55
|
+
Thread.new do
|
56
|
+
until (line = stream_buffer.gets).nil?
|
57
|
+
puts "#{name} -> #{line}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# wait for process
|
62
|
+
external.join
|
63
|
+
unless external.value.success?
|
64
|
+
fail CapsuleCD::Error::TestDependenciesError, 'npm install failed. Check module dependencies'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# create a shrinkwrap file.
|
68
|
+
Open3.popen3('npm shrinkwrap', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
69
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
70
|
+
Thread.new do
|
71
|
+
until (line = stream_buffer.gets).nil?
|
72
|
+
puts "#{name} -> #{line}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
# wait for process
|
77
|
+
external.join
|
78
|
+
unless external.value.success?
|
79
|
+
fail CapsuleCD::Error::TestDependenciesError, 'npm shrinkwrap failed. Check log for exact error'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# run test command
|
84
|
+
test_cmd = @config.engine_cmd_test || 'npm test'
|
85
|
+
Open3.popen3(ENV, test_cmd, chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
86
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
87
|
+
Thread.new do
|
88
|
+
until (line = stream_buffer.gets).nil?
|
89
|
+
puts "#{name} -> #{line}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# wait for process
|
94
|
+
external.join
|
95
|
+
unless external.value.success?
|
96
|
+
fail CapsuleCD::Error::TestRunnerError, test_cmd + ' failed. Check log for exact error'
|
97
|
+
end
|
98
|
+
end unless @config.engine_disable_test
|
99
|
+
end
|
100
|
+
|
101
|
+
if @_is_bower
|
102
|
+
# lets make sure all the bower dependencies are available.
|
103
|
+
Open3.popen3('bower install --allow-root', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
104
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
105
|
+
Thread.new do
|
106
|
+
until (line = stream_buffer.gets).nil?
|
107
|
+
puts "#{name} -> #{line}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
# wait for process
|
112
|
+
external.join
|
113
|
+
unless external.value.success?
|
114
|
+
fail CapsuleCD::Error::TestDependenciesError, 'npm install failed. Check module dependencies'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# run npm publish
|
121
|
+
def package_step
|
122
|
+
super
|
123
|
+
|
124
|
+
# commit changes to the cookbook. (test run occurs before this, and it should clean up any instrumentation files, created,
|
125
|
+
# as they will be included in the commmit and any release artifacts)
|
126
|
+
CapsuleCD::GitUtils.commit(@source_git_local_path, 'Committing automated changes before packaging.') rescue puts 'No changes to commit..'
|
127
|
+
if @_is_bower && !@_is_npm
|
128
|
+
bower_file = File.read(@source_git_local_path + '/bower.json')
|
129
|
+
bower_data = JSON.parse(bower_file)
|
130
|
+
next_version = SemVer.parse(bower_data['version'])
|
131
|
+
@source_release_commit = CapsuleCD::GitUtils.tag(@source_git_local_path, "v#{next_version}")
|
132
|
+
|
133
|
+
else
|
134
|
+
|
135
|
+
# run npm publish
|
136
|
+
Open3.popen3("npm version #{@config.engine_version_bump_type} -m '(v%s) Automated packaging of release by CapsuleCD'", chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
137
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
138
|
+
Thread.new do
|
139
|
+
until (line = stream_buffer.gets).nil?
|
140
|
+
puts "#{name} -> #{line}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
# wait for process
|
145
|
+
external.join
|
146
|
+
fail 'npm version bump failed' unless external.value.success?
|
147
|
+
end
|
148
|
+
@source_release_commit = CapsuleCD::GitUtils.get_latest_tag_commit(@source_git_local_path)
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
# this step should push the release to the package repository (ie. npm, chef supermarket, rubygems)
|
155
|
+
def release_step
|
156
|
+
super
|
157
|
+
|
158
|
+
if @_is_npm
|
159
|
+
npmrc_path = File.expand_path('~/.npmrc')
|
160
|
+
|
161
|
+
unless @config.npm_auth_token
|
162
|
+
fail CapsuleCD::Error::ReleaseCredentialsMissing, 'cannot deploy page to npm, credentials missing'
|
163
|
+
return
|
164
|
+
end
|
165
|
+
|
166
|
+
# write the knife.rb config file.
|
167
|
+
File.open(npmrc_path, 'w+') do |file|
|
168
|
+
file.write("//registry.npmjs.org/:_authToken=#{@config.npm_auth_token}")
|
169
|
+
end
|
170
|
+
|
171
|
+
# run npm publish
|
172
|
+
Open3.popen3('npm publish .', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
173
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
174
|
+
Thread.new do
|
175
|
+
until (line = stream_buffer.gets).nil?
|
176
|
+
puts "#{name} -> #{line}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
# wait for process
|
181
|
+
external.join
|
182
|
+
unless external.value.success?
|
183
|
+
fail CapsuleCD::Error::ReleasePackageError, 'npm publish failed. Check log for exact error'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
def sync_versions
|
191
|
+
#this method only needs to run if bower and package json files exist.
|
192
|
+
if !@_is_bower || !@_is_npm
|
193
|
+
return
|
194
|
+
end
|
195
|
+
|
196
|
+
bower_file = File.read(@source_git_local_path + '/bower.json')
|
197
|
+
bower_data = JSON.parse(bower_file)
|
198
|
+
bower_version = SemVer.parse(bower_data['version'])
|
199
|
+
package_file = File.read(@source_git_local_path + '/package.json')
|
200
|
+
package_data = JSON.parse(package_file)
|
201
|
+
package_version = SemVer.parse(package_data['version'])
|
202
|
+
|
203
|
+
if(bower_version>package_version)
|
204
|
+
package_data['version'] = bower_version.to_s
|
205
|
+
File.write(@source_git_local_path + '/package.json', JSON.pretty_generate(package_data))
|
206
|
+
else
|
207
|
+
bower_data['version'] = package_version.to_s
|
208
|
+
File.write(@source_git_local_path + '/bower.json', JSON.pretty_generate(bower_data))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'semverly'
|
2
|
+
require 'open3'
|
3
|
+
require 'bundler'
|
4
|
+
require_relative '../base/engine'
|
5
|
+
|
6
|
+
module CapsuleCD
|
7
|
+
module Node
|
8
|
+
class NodeEngine < Engine
|
9
|
+
def build_step
|
10
|
+
super
|
11
|
+
# validate that the chef metadata.rb file exists
|
12
|
+
unless File.exist?(@source_git_local_path + '/package.json')
|
13
|
+
fail CapsuleCD::Error::BuildPackageInvalid, 'package.json file is required to process Npm package'
|
14
|
+
end
|
15
|
+
|
16
|
+
# no need to bump up the version here. It will automatically be bumped up via the npm version patch command.
|
17
|
+
# however we need to read the version from the package.json file and check if a npm module already exists.
|
18
|
+
|
19
|
+
# TODO: check if this module name and version already exist.
|
20
|
+
|
21
|
+
# check for/create any required missing folders/files
|
22
|
+
unless File.exist?(@source_git_local_path + '/test')
|
23
|
+
FileUtils.mkdir(@source_git_local_path + '/test')
|
24
|
+
end
|
25
|
+
unless File.exist?(@source_git_local_path + '/.gitignore')
|
26
|
+
CapsuleCD::GitUtils.create_gitignore(@source_git_local_path, ['Node'])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_step
|
31
|
+
super
|
32
|
+
|
33
|
+
# the module has already been downloaded. lets make sure all its dependencies are available.
|
34
|
+
Open3.popen3('npm install', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
35
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
36
|
+
Thread.new do
|
37
|
+
until (line = stream_buffer.gets).nil?
|
38
|
+
puts "#{name} -> #{line}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
# wait for process
|
43
|
+
external.join
|
44
|
+
unless external.value.success?
|
45
|
+
fail CapsuleCD::Error::TestDependenciesError, 'npm install failed. Check module dependencies'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# create a shrinkwrap file.
|
50
|
+
Open3.popen3('npm shrinkwrap', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
51
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
52
|
+
Thread.new do
|
53
|
+
until (line = stream_buffer.gets).nil?
|
54
|
+
puts "#{name} -> #{line}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
# wait for process
|
59
|
+
external.join
|
60
|
+
unless external.value.success?
|
61
|
+
fail CapsuleCD::Error::TestDependenciesError, 'npm shrinkwrap failed. Check log for exact error'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# run test command
|
66
|
+
test_cmd = @config.engine_cmd_test || 'npm test'
|
67
|
+
Open3.popen3(ENV, test_cmd, chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
68
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
69
|
+
Thread.new do
|
70
|
+
until (line = stream_buffer.gets).nil?
|
71
|
+
puts "#{name} -> #{line}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
# wait for process
|
76
|
+
external.join
|
77
|
+
unless external.value.success?
|
78
|
+
fail CapsuleCD::Error::TestRunnerError, test_cmd + ' failed. Check log for exact error'
|
79
|
+
end
|
80
|
+
end unless @config.engine_disable_test
|
81
|
+
end
|
82
|
+
|
83
|
+
# run npm publish
|
84
|
+
def package_step
|
85
|
+
super
|
86
|
+
|
87
|
+
# commit changes to the cookbook. (test run occurs before this, and it should clean up any instrumentation files, created,
|
88
|
+
# as they will be included in the commmit and any release artifacts)
|
89
|
+
CapsuleCD::GitUtils.commit(@source_git_local_path, 'Committing automated changes before packaging.') rescue puts 'No changes to commit..'
|
90
|
+
|
91
|
+
# run npm publish
|
92
|
+
Open3.popen3("npm version #{@config.engine_version_bump_type} -m '(v%s) Automated packaging of release by CapsuleCD'", chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
93
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
94
|
+
Thread.new do
|
95
|
+
until (line = stream_buffer.gets).nil?
|
96
|
+
puts "#{name} -> #{line}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
# wait for process
|
101
|
+
external.join
|
102
|
+
fail 'npm version bump failed' unless external.value.success?
|
103
|
+
end
|
104
|
+
|
105
|
+
@source_release_commit = CapsuleCD::GitUtils.get_latest_tag_commit(@source_git_local_path)
|
106
|
+
end
|
107
|
+
|
108
|
+
# this step should push the release to the package repository (ie. npm, chef supermarket, rubygems)
|
109
|
+
def release_step
|
110
|
+
super
|
111
|
+
npmrc_path = File.expand_path('~/.npmrc')
|
112
|
+
|
113
|
+
unless @config.npm_auth_token
|
114
|
+
fail CapsuleCD::Error::ReleaseCredentialsMissing, 'cannot deploy page to npm, credentials missing'
|
115
|
+
return
|
116
|
+
end
|
117
|
+
|
118
|
+
# write the knife.rb config file.
|
119
|
+
File.open(npmrc_path, 'w+') do |file|
|
120
|
+
file.write("//registry.npmjs.org/:_authToken=#{@config.npm_auth_token}")
|
121
|
+
end
|
122
|
+
|
123
|
+
# run npm publish
|
124
|
+
Open3.popen3('npm publish .', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
125
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
126
|
+
Thread.new do
|
127
|
+
until (line = stream_buffer.gets).nil?
|
128
|
+
puts "#{name} -> #{line}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
# wait for process
|
133
|
+
external.join
|
134
|
+
unless external.value.success?
|
135
|
+
fail CapsuleCD::Error::ReleasePackageError, 'npm publish failed. Check log for exact error'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'semverly'
|
2
|
+
require 'open3'
|
3
|
+
require 'bundler'
|
4
|
+
require_relative '../base/engine'
|
5
|
+
|
6
|
+
module CapsuleCD
|
7
|
+
module Python
|
8
|
+
class PythonEngine < Engine
|
9
|
+
def build_step
|
10
|
+
super
|
11
|
+
unless File.exist?(@source_git_local_path + '/setup.py')
|
12
|
+
fail CapsuleCD::Error::BuildPackageInvalid, 'setup.py file is required to process Python package'
|
13
|
+
end
|
14
|
+
|
15
|
+
# check for/create required VERSION file
|
16
|
+
unless File.exist?(@source_git_local_path + '/VERSION')
|
17
|
+
File.open(@source_git_local_path + '/VERSION', 'w') { |file| file.write('0.0.0') }
|
18
|
+
end
|
19
|
+
|
20
|
+
# bump up the version here.
|
21
|
+
# since there's no standardized way to bump up the version in the setup.py file, we're going to assume that the version
|
22
|
+
# is specified in a VERSION file in the root of the source repository
|
23
|
+
# this is option #4 in the python packaging guide:
|
24
|
+
# https://packaging.python.org/en/latest/single_source_version/#single-sourcing-the-version
|
25
|
+
#
|
26
|
+
# additional packaging structures, like those listed below, may also be supported in the future.
|
27
|
+
# http://stackoverflow.com/a/7071358/1157633
|
28
|
+
|
29
|
+
version = File.read(@source_git_local_path + '/VERSION').strip
|
30
|
+
next_version = bump_version(SemVer.parse(version))
|
31
|
+
File.open(@source_git_local_path + '/VERSION', 'w') do |file|
|
32
|
+
file.write(next_version)
|
33
|
+
end
|
34
|
+
|
35
|
+
# make sure the package testing manager is available.
|
36
|
+
# there is a standardized way to test packages (python setup.py tests), however for automation tox is preferred
|
37
|
+
# because of virtualenv and its support for multiple interpreters.
|
38
|
+
unless File.exist?(@source_git_local_path + '/tox.ini')
|
39
|
+
# if a tox.ini file is not present, we'll create a default one and specify 'python setup.py test' as the test
|
40
|
+
# runner command, and requirements.txt as the dependencies for this package.
|
41
|
+
File.open(@source_git_local_path + '/tox.ini', 'w') { |file|
|
42
|
+
file.write(<<-TOX.gsub(/^\s+/, '')
|
43
|
+
# Tox (http://tox.testrun.org/) is a tool for running tests
|
44
|
+
# in multiple virtualenvs. This configuration file will run the
|
45
|
+
# test suite on all supported python versions. To use it, "pip install tox"
|
46
|
+
# and then run "tox" from this directory.
|
47
|
+
|
48
|
+
[tox]
|
49
|
+
envlist = py27
|
50
|
+
usedevelop = True
|
51
|
+
|
52
|
+
[testenv]
|
53
|
+
commands = python setup.py test
|
54
|
+
deps =
|
55
|
+
-rrequirements.txt
|
56
|
+
TOX
|
57
|
+
)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
# check for/create any required missing folders/files
|
62
|
+
unless File.exist?(@source_git_local_path + '/requirements.txt')
|
63
|
+
File.open(@source_git_local_path + '/requirements.txt', 'w') { |file| file.write('') }
|
64
|
+
end
|
65
|
+
|
66
|
+
unless File.exist?(@source_git_local_path + '/tests')
|
67
|
+
FileUtils.mkdir(@source_git_local_path + '/tests')
|
68
|
+
end
|
69
|
+
unless File.exist?(@source_git_local_path + '/tests/__init__.py')
|
70
|
+
File.open(@source_git_local_path + '/tests/__init__.py', 'w') { |file| file.write('') }
|
71
|
+
end
|
72
|
+
unless File.exist?(@source_git_local_path + '/.gitignore')
|
73
|
+
CapsuleCD::GitUtils.create_gitignore(@source_git_local_path, ['Python'])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_step
|
78
|
+
super
|
79
|
+
|
80
|
+
|
81
|
+
# download the package dependencies and register it in the virtualenv using tox (which will do pip install -e .)
|
82
|
+
# https://packaging.python.org/en/latest/distributing/
|
83
|
+
# once that's done, tox will run tests
|
84
|
+
# run test command
|
85
|
+
test_cmd = @config.engine_cmd_test || 'tox'
|
86
|
+
Open3.popen3(test_cmd, chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
87
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
88
|
+
Thread.new do
|
89
|
+
until (line = stream_buffer.gets).nil?
|
90
|
+
puts "#{name} -> #{line}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
# wait for process
|
95
|
+
external.join
|
96
|
+
unless external.value.success?
|
97
|
+
fail CapsuleCD::Error::TestDependenciesError, test_cmd + ' failed to test package.'
|
98
|
+
end
|
99
|
+
end unless @config.engine_disable_test
|
100
|
+
end
|
101
|
+
|
102
|
+
# run npm publish
|
103
|
+
def package_step
|
104
|
+
super
|
105
|
+
|
106
|
+
# commit changes to the cookbook. (test run occurs before this, and it should clean up any instrumentation files, created,
|
107
|
+
# as they will be included in the commmit and any release artifacts)
|
108
|
+
version = File.read(@source_git_local_path + '/VERSION').strip
|
109
|
+
next_version = SemVer.parse(version)
|
110
|
+
CapsuleCD::GitUtils.commit(@source_git_local_path, "(v#{next_version}) Automated packaging of release by CapsuleCD")
|
111
|
+
@source_release_commit = CapsuleCD::GitUtils.tag(@source_git_local_path, "v#{next_version}")
|
112
|
+
end
|
113
|
+
|
114
|
+
# this step should push the release to the package repository (ie. npm, chef supermarket, rubygems)
|
115
|
+
def release_step
|
116
|
+
super
|
117
|
+
pypirc_path = File.expand_path('~/.pypirc')
|
118
|
+
|
119
|
+
unless @config.pypi_username || @config.pypi_password
|
120
|
+
fail CapsuleCD::Error::ReleaseCredentialsMissing, 'cannot deploy package to pip, credentials missing'
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
# write the knife.rb config file.
|
125
|
+
File.open(pypirc_path, 'w+') do |file|
|
126
|
+
file.write(<<-EOT.gsub(/^\s+/, '')
|
127
|
+
[distutils]
|
128
|
+
index-servers=pypi
|
129
|
+
|
130
|
+
[pypi]
|
131
|
+
repository = https://pypi.python.org/pypi
|
132
|
+
username = #{@config.pypi_username}
|
133
|
+
password = #{@config.pypi_password}
|
134
|
+
EOT
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
# run python setup.py sdist upload
|
139
|
+
# TODO: use twine instead (it supports HTTPS.)https://python-packaging-user-guide.readthedocs.org/en/latest/distributing/#uploading-your-project-to-pypi
|
140
|
+
Open3.popen3('python setup.py sdist upload', chdir: @source_git_local_path) do |_stdin, stdout, stderr, external|
|
141
|
+
{ stdout: stdout, stderr: stderr }. each do |name, stream_buffer|
|
142
|
+
Thread.new do
|
143
|
+
until (line = stream_buffer.gets).nil?
|
144
|
+
puts "#{name} -> #{line}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
# wait for process
|
149
|
+
external.join
|
150
|
+
unless external.value.success?
|
151
|
+
fail CapsuleCD::Error::ReleasePackageError, 'python setup.py upload failed. Check log for exact error'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|