capsulecd 1.0.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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.dockerignore +5 -0
  4. data/.gitignore +95 -0
  5. data/.rspec +3 -0
  6. data/.simplecov +9 -0
  7. data/Dockerfile +16 -0
  8. data/Dockerfile.chef +26 -0
  9. data/Dockerfile.javascript +7 -0
  10. data/Dockerfile.node +7 -0
  11. data/Dockerfile.python +7 -0
  12. data/Dockerfile.ruby +4 -0
  13. data/FEATURES.md +12 -0
  14. data/Gemfile +26 -0
  15. data/LICENSE.md +22 -0
  16. data/README.md +227 -0
  17. data/Rakefile +43 -0
  18. data/bin/capsulecd +4 -0
  19. data/capsulecd.gemspec +27 -0
  20. data/circle.yml +24 -0
  21. data/lib/capsulecd/base/common/git_utils.rb +90 -0
  22. data/lib/capsulecd/base/common/validation_utils.rb +22 -0
  23. data/lib/capsulecd/base/configuration.rb +151 -0
  24. data/lib/capsulecd/base/engine.rb +163 -0
  25. data/lib/capsulecd/base/runner/circleci.rb +37 -0
  26. data/lib/capsulecd/base/runner/default.rb +38 -0
  27. data/lib/capsulecd/base/source/github.rb +183 -0
  28. data/lib/capsulecd/base/transform_engine.rb +62 -0
  29. data/lib/capsulecd/chef/chef_engine.rb +172 -0
  30. data/lib/capsulecd/chef/chef_helper.rb +29 -0
  31. data/lib/capsulecd/cli.rb +64 -0
  32. data/lib/capsulecd/error.rb +51 -0
  33. data/lib/capsulecd/javascript/javascript_engine.rb +213 -0
  34. data/lib/capsulecd/node/node_engine.rb +141 -0
  35. data/lib/capsulecd/python/python_engine.rb +157 -0
  36. data/lib/capsulecd/ruby/ruby_engine.rb +191 -0
  37. data/lib/capsulecd/ruby/ruby_helper.rb +60 -0
  38. data/lib/capsulecd/version.rb +3 -0
  39. data/lib/capsulecd.rb +16 -0
  40. data/logo.svg +1 -0
  41. data/spec/fixtures/chef/cookbook_analogj_test/CHANGELOG.md +3 -0
  42. data/spec/fixtures/chef/cookbook_analogj_test/Gemfile +18 -0
  43. data/spec/fixtures/chef/cookbook_analogj_test/LICENSE +21 -0
  44. data/spec/fixtures/chef/cookbook_analogj_test/README.md +13 -0
  45. data/spec/fixtures/chef/cookbook_analogj_test/Rakefile +1 -0
  46. data/spec/fixtures/chef/cookbook_analogj_test/Thorfile +12 -0
  47. data/spec/fixtures/chef/cookbook_analogj_test/chefignore +94 -0
  48. data/spec/fixtures/chef/cookbook_analogj_test/metadata.rb +5 -0
  49. data/spec/fixtures/chef/cookbook_analogj_test/recipes/default.rb +6 -0
  50. data/spec/fixtures/incorrect_configuration.yml +4 -0
  51. data/spec/fixtures/javascript/javascript_analogj_test/LICENSE +21 -0
  52. data/spec/fixtures/javascript/javascript_analogj_test/README.md +2 -0
  53. data/spec/fixtures/javascript/javascript_analogj_test/package.json +19 -0
  54. data/spec/fixtures/node/npm_analogj_test/LICENSE +21 -0
  55. data/spec/fixtures/node/npm_analogj_test/README.md +2 -0
  56. data/spec/fixtures/node/npm_analogj_test/package.json +19 -0
  57. data/spec/fixtures/python/pip_analogj_test/LICENSE +21 -0
  58. data/spec/fixtures/python/pip_analogj_test/MANIFEST.in +1 -0
  59. data/spec/fixtures/python/pip_analogj_test/README.md +1 -0
  60. data/spec/fixtures/python/pip_analogj_test/VERSION +1 -0
  61. data/spec/fixtures/python/pip_analogj_test/setup.cfg +5 -0
  62. data/spec/fixtures/python/pip_analogj_test/setup.py +80 -0
  63. data/spec/fixtures/python/pip_analogj_test/tox.ini +14 -0
  64. data/spec/fixtures/ruby/gem_analogj_test/Gemfile +4 -0
  65. data/spec/fixtures/ruby/gem_analogj_test/LICENSE.txt +21 -0
  66. data/spec/fixtures/ruby/gem_analogj_test/README.md +41 -0
  67. data/spec/fixtures/ruby/gem_analogj_test/Rakefile +6 -0
  68. data/spec/fixtures/ruby/gem_analogj_test/bin/console +14 -0
  69. data/spec/fixtures/ruby/gem_analogj_test/bin/setup +8 -0
  70. data/spec/fixtures/ruby/gem_analogj_test/gem_analogj_test.gemspec +25 -0
  71. data/spec/fixtures/ruby/gem_analogj_test/lib/gem_analogj_test/version.rb +3 -0
  72. data/spec/fixtures/ruby/gem_analogj_test/lib/gem_analogj_test.rb +5 -0
  73. data/spec/fixtures/ruby/gem_analogj_test/spec/gem_analogj_test_spec.rb +7 -0
  74. data/spec/fixtures/ruby/gem_analogj_test/spec/spec_helper.rb +2 -0
  75. data/spec/fixtures/ruby/gem_analogj_test-0.1.4.gem +0 -0
  76. data/spec/fixtures/sample_chef_configuration.yml +8 -0
  77. data/spec/fixtures/sample_configuration.yml +7 -0
  78. data/spec/fixtures/sample_global_configuration.yml +23 -0
  79. data/spec/fixtures/sample_node_configuration.yml +7 -0
  80. data/spec/fixtures/sample_python_configuration.yml +8 -0
  81. data/spec/fixtures/sample_repo_configuration.yml +22 -0
  82. data/spec/fixtures/sample_ruby_configuration.yml +5 -0
  83. data/spec/fixtures/vcr_cassettes/chef_build_step.yml +636 -0
  84. data/spec/fixtures/vcr_cassettes/gem_build_step.yml +653 -0
  85. data/spec/fixtures/vcr_cassettes/gem_build_step_without_version_rb.yml +653 -0
  86. data/spec/fixtures/vcr_cassettes/integration_chef.yml +1399 -0
  87. data/spec/fixtures/vcr_cassettes/integration_node.yml +1388 -0
  88. data/spec/fixtures/vcr_cassettes/integration_python.yml +1388 -0
  89. data/spec/fixtures/vcr_cassettes/integration_ruby.yml +1377 -0
  90. data/spec/fixtures/vcr_cassettes/node_build_step.yml +647 -0
  91. data/spec/fixtures/vcr_cassettes/pip_build_step.yml +653 -0
  92. data/spec/lib/capsulecd/base/configuration_spec.rb +75 -0
  93. data/spec/lib/capsulecd/base/engine_spec.rb +51 -0
  94. data/spec/lib/capsulecd/base/source/github_spec.rb +253 -0
  95. data/spec/lib/capsulecd/base/transform_engine_spec.rb +55 -0
  96. data/spec/lib/capsulecd/chef/chef_engine_spec.rb +114 -0
  97. data/spec/lib/capsulecd/cli_spec.rb +57 -0
  98. data/spec/lib/capsulecd/node/node_engine_spec.rb +113 -0
  99. data/spec/lib/capsulecd/python/python_engine_spec.rb +118 -0
  100. data/spec/lib/capsulecd/ruby/ruby_engine_spec.rb +128 -0
  101. data/spec/spec_helper.rb +105 -0
  102. data/spec/support/file_system.rb +21 -0
  103. data/spec/support/package_types.rb +11 -0
  104. 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