satorix 0.0.1 → 1.6.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 (40) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -1
  3. data/.gitlab-ci.yml +45 -0
  4. data/.rspec +2 -1
  5. data/.rubocop.yml +11 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +25 -0
  9. data/Procfile +1 -0
  10. data/README.md +93 -1
  11. data/Rakefile +8 -4
  12. data/bin/console +3 -3
  13. data/bin/satorix +8 -0
  14. data/lib/satorix.rb +338 -2
  15. data/lib/satorix/CI/deploy/flynn.rb +129 -0
  16. data/lib/satorix/CI/deploy/flynn/environment_variables.rb +121 -0
  17. data/lib/satorix/CI/deploy/flynn/resources.rb +77 -0
  18. data/lib/satorix/CI/deploy/flynn/routes.rb +266 -0
  19. data/lib/satorix/CI/deploy/flynn/scale.rb +52 -0
  20. data/lib/satorix/CI/shared/buildpack_manager.rb +213 -0
  21. data/lib/satorix/CI/shared/buildpack_manager/buildpack.rb +153 -0
  22. data/lib/satorix/CI/shared/ruby/gem_manager.rb +79 -0
  23. data/lib/satorix/CI/shared/yarn_manager.rb +22 -0
  24. data/lib/satorix/CI/test/python/django_test.rb +35 -0
  25. data/lib/satorix/CI/test/python/safety.rb +27 -0
  26. data/lib/satorix/CI/test/ruby/brakeman.rb +32 -0
  27. data/lib/satorix/CI/test/ruby/bundler_audit.rb +32 -0
  28. data/lib/satorix/CI/test/ruby/cucumber.rb +26 -0
  29. data/lib/satorix/CI/test/ruby/rails_test.rb +26 -0
  30. data/lib/satorix/CI/test/ruby/rspec.rb +26 -0
  31. data/lib/satorix/CI/test/ruby/rubocop.rb +98 -0
  32. data/lib/satorix/CI/test/shared/database.rb +74 -0
  33. data/lib/satorix/shared/console.rb +180 -0
  34. data/lib/satorix/version.rb +1 -1
  35. data/satorix.gemspec +13 -11
  36. data/satorix/CI/deploy/ie_gem_server.rb +76 -0
  37. data/satorix/CI/deploy/rubygems.rb +77 -0
  38. data/satorix/custom.rb +21 -0
  39. metadata +57 -29
  40. data/.travis.yml +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f677f81f64b5f6c8e17e151b2b2be516cb736ff1
4
- data.tar.gz: 55632c8146b1748a25c56b45c2def07e7f6d5902
2
+ SHA256:
3
+ metadata.gz: 274dbd65366f3a703d2a57b81524f4aa9aa9522b0caa2f2b8158758b20c1861c
4
+ data.tar.gz: 3fb98c60b4e33c30d053d23c6833d39e809c6d9da4785475694b554e974a9699
5
5
  SHA512:
6
- metadata.gz: 571f0d7f89ee278d5d06be705e2cc910cd925cdc3642d23f12e852d0542fcac31222c4abf20a8c5475163a14785db243b0d4a8787f84f0ff308dd9e75ecb4af5
7
- data.tar.gz: 4ed70556edd92e3d2a08a6f08e3e54f85fe569c37134748e5483ce201d5f4956d99e8a2a9c54c5eb9dde8c32dc1179506c510ceb26ddd2882d9ed830329d0724
6
+ metadata.gz: 77f3d4a3a876d26f1d335936e4d613dad6280cbff9367d31672a2b56df16086c074ba066a1b11760a802d9a357491e1275d81bb09473726716dffc47f1798de8
7
+ data.tar.gz: 22aefe7ba2cb3a806b44635e8554762a912283a66cb7f6628a16937e1e3e166ce7437ae38e13471722c2e5cbc196d043d6f1c33886990a5771ac974f0c6c4ada
data/.gitignore CHANGED
@@ -1,9 +1,12 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
7
6
  /pkg/
8
7
  /spec/reports/
9
8
  /tmp/
9
+
10
+ # Ruymine/development files
11
+ .idea
12
+ clean_branches.rb
@@ -0,0 +1,45 @@
1
+ image: 'satorix/base'
2
+
3
+ cache:
4
+ key: "$CI_PROJECT_ID"
5
+ paths:
6
+ - 'tmp/satorix/cache' # To cache buildpack gems between runs.
7
+
8
+
9
+ .satorix: &satorix
10
+ script:
11
+ - gem install satorix --source https://gems.iexposure.com --no-document
12
+ - satorix
13
+
14
+
15
+ # bundler-audit
16
+ # Patch-level verification for Bundler
17
+ # https://github.com/rubysec/bundler-audit
18
+ bundler_audit:
19
+ <<: *satorix
20
+
21
+
22
+ # RSpec
23
+ # Behaviour Driven Development for Ruby
24
+ # http://rspec.info/
25
+ rspec:
26
+ <<: *satorix
27
+
28
+
29
+ # RuboCop
30
+ # A Ruby static code analyzer, based on the community Ruby style guide.
31
+ # https://github.com/bbatsov/rubocop
32
+ rubocop:
33
+ <<: *satorix
34
+ allow_failure: true
35
+
36
+
37
+
38
+ # This is a custom job, defined at satorix/CI/deploy/rubygems.rb
39
+ deploy_to_rubygems:
40
+ stage: deploy
41
+ only:
42
+ - tags
43
+ except:
44
+ - schedules
45
+ <<: *satorix
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
- --format documentation
1
+ --format progress
2
+ --order rand
2
3
  --color
@@ -0,0 +1,11 @@
1
+ Metrics/LineLength:
2
+ Max: 120
3
+
4
+ Style/ModuleFunction:
5
+ Enabled: false
6
+
7
+ Layout/SpaceInsideStringInterpolation:
8
+ EnforcedStyle: space
9
+
10
+ Style/FrozenStringLiteralComment:
11
+ Enabled: false
@@ -0,0 +1 @@
1
+ ruby-2.6.6
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ ruby File.open("#{ File.dirname(__FILE__) }/.ruby-version", &:gets).strip[/ruby-(.+)/i, 1]
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in satorix.gemspec
@@ -0,0 +1,25 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ satorix (1.6.0)
5
+ airbrake-ruby
6
+ bundler (~> 1.13)
7
+ rake
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ airbrake-ruby (2.0.0)
13
+ rake (13.0.1)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ satorix!
20
+
21
+ RUBY VERSION
22
+ ruby 2.6.6p146
23
+
24
+ BUNDLED WITH
25
+ 1.17.3
@@ -0,0 +1 @@
1
+ # This is an empty file, to silence an buildpack warning during testing.
data/README.md CHANGED
@@ -1,3 +1,95 @@
1
1
  # Satorix
2
2
 
3
- By [Internet Exposure](https://www.iexposure.com/)
3
+ By [Satorix](https://www.satorix.com/)
4
+
5
+ [![build](http://gitlab.iexposure.com/satorix/satorix/badges/master/build.svg)](http://gitlab.iexposure.com/satorix/satorix/pipelines)
6
+ [![coverage](http://gitlab.iexposure.com/satorix/satorix/badges/master/coverage.svg)](http://gitlab.iexposure.com/satorix/satorix/pipelines)
7
+
8
+ This gem is a commandline tool used to tie together SCM, CI/CD, and hosting environments.
9
+
10
+ ## Installation
11
+
12
+ Install Satorix from the command line:
13
+
14
+ $ gem install satorix
15
+
16
+ ## Usage
17
+
18
+ Run Satorix from the command line:
19
+
20
+ $ satorix
21
+
22
+ Note: Satorix is currently designed to be run via GitLab CI, not locally.
23
+
24
+ ## Application Preparation
25
+
26
+ To prepare your application:
27
+
28
+ [Satorix Docs](https://www.satorix.com/docs)
29
+
30
+
31
+ ## Development
32
+
33
+ After checking out the repo, run `bin/setup` to install dependencies.
34
+ Then, run `bundle exec rspec spec` to run the tests.
35
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
+
37
+ To install this gem onto your local machine, run `bundle exec rake install`.
38
+
39
+
40
+ ## CI/CD
41
+
42
+ Satorix is used to provide continuous integration and continuous deployment for this application.
43
+
44
+ CI is run against every push.
45
+
46
+ CD is used to automatically build and deploy the gem for the master branch.
47
+
48
+ ## Environment Variables
49
+
50
+ Satorix uses environment variables as an authoritative source for application configuration.
51
+
52
+ Environment variables are named with the scheme of PREFIX_BRANCH_KEY.
53
+
54
+
55
+ ### Application (AEEV)
56
+
57
+ Application-level environment variables are identified by the AEEV prefix (Application Exportable Environment Variable).
58
+
59
+ These environment variables are passed to the Satorix Hosting Evnvironment for consumption by the application. A
60
+ likely use case would be secrets.yml
61
+
62
+ The prefix and branch are removed.
63
+
64
+
65
+ * AEEV_PRODUCTION_SECRET_API_KEY
66
+ * AEEV_STAGING_SECRET_API_KEY
67
+
68
+ The variable name passed to the application in the respective environment would be SECRET_API_KEY
69
+
70
+
71
+ ### Flynn (FLYNN)
72
+
73
+ Environment variables used to configure Flynn are identified by the FLYNN prefix.
74
+
75
+ #### Required
76
+
77
+ * DOMAIN
78
+ * KEY
79
+ * TLSPIN
80
+
81
+ #### Optional
82
+
83
+ * SCALE
84
+ * RESOURCES
85
+
86
+ ### Domain (DDEV, CRT, KEY)
87
+
88
+ Domain information can be configured with environment variables identified by the DOMAIN prefix.
89
+
90
+ * KEY_PRODUCTION_DOMAINDOM
91
+ * CRT_PRODUCTION_DOMAINDOM
92
+ * DDEV_PRODUCTION_DOMAINDOM
93
+ * KEY_PRODUCTION_DEFAULT
94
+ * CRT_PRODUCTION_DEFAULT
95
+
data/Rakefile CHANGED
@@ -1,6 +1,10 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ begin
3
+ require 'rspec/core/rake_task'
3
4
 
4
- RSpec::Core::RakeTask.new(:spec)
5
+ RSpec::Core::RakeTask.new(:spec)
5
6
 
6
- task :default => :spec
7
+ task default: :spec
8
+ rescue LoadError
9
+ # no rspec available
10
+ end
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "satorix"
3
+ require 'bundler/setup'
4
+ require 'satorix'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "satorix"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #Adjust path in case called directly and not through gem
4
+ $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/../lib"
5
+
6
+ require 'satorix'
7
+
8
+ Satorix.go
@@ -1,5 +1,341 @@
1
- require "satorix/version"
1
+ require 'airbrake-ruby'
2
+ require 'English' # http://ruby-doc.org/stdlib-2.0.0/libdoc/English/rdoc/English.html
3
+ require 'satorix/version'
4
+ require 'etc'
2
5
 
3
6
  module Satorix
4
- # Your code goes here...
7
+
8
+ module Shared
9
+ autoload :Console, 'satorix/shared/console'
10
+ end
11
+
12
+ module CI
13
+
14
+ module Deploy
15
+ autoload :Flynn, 'satorix/CI/deploy/flynn'
16
+ end
17
+
18
+ module Shared
19
+ autoload :BuildpackManager, 'satorix/CI/shared/buildpack_manager'
20
+ module BuildpackManager
21
+ autoload :Buildpack, 'satorix/CI/shared/buildpack_manager/buildpack'
22
+ end
23
+ autoload :YarnManager, 'satorix/CI/shared/yarn_manager'
24
+
25
+ module Ruby
26
+ autoload :GemManager, 'satorix/CI/shared/ruby/gem_manager'
27
+ end
28
+ end
29
+
30
+ module Test
31
+ module Python
32
+ autoload :DjangoTest, 'satorix/CI/test/python/django_test'
33
+ autoload :Safety, 'satorix/CI/test/python/safety'
34
+ end
35
+
36
+ module Ruby
37
+ autoload :Brakeman, 'satorix/CI/test/ruby/brakeman'
38
+ autoload :BundlerAudit, 'satorix/CI/test/ruby/bundler_audit'
39
+ autoload :Cucumber, 'satorix/CI/test/ruby/cucumber'
40
+ autoload :RailsTest, 'satorix/CI/test/ruby/rails_test'
41
+ autoload :Rspec, 'satorix/CI/test/ruby/rspec'
42
+ autoload :Rubocop, 'satorix/CI/test/ruby/rubocop'
43
+ end
44
+
45
+ module Shared
46
+ autoload :Database, 'satorix/CI/test/shared/database'
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ include Satorix::Shared::Console
53
+
54
+ extend self
55
+
56
+
57
+ def go
58
+ airbrake_start
59
+ prepare_app_environment
60
+
61
+ begin
62
+ log_header "Executing #{ ci_job_name } script for #{ ci_commit_ref_name }..."
63
+
64
+ execute_as_user 'satorix' do
65
+ Dir.chdir(build_dir)
66
+ unless skip_buildpack?
67
+ Satorix::CI::Shared::BuildpackManager.go
68
+ Dir.chdir(app_dir)
69
+ end
70
+ Satorix::CI::Shared::Ruby::GemManager.go if ruby_gem?
71
+ Satorix::CI::Shared::YarnManager.go if yarn?
72
+
73
+ job_class.go
74
+ end
75
+
76
+ log_header "\nDone executing #{ ci_job_name } script for #{ ci_commit_ref_name }.\n"
77
+ rescue Exception => e
78
+ # TODO: add link to issue tracker
79
+ log 'If you feel this failure was in error, please check the issue tracker for more information.'
80
+ Airbrake.notify(e) if airbrake_configured?
81
+ raise e
82
+ end
83
+ end
84
+
85
+
86
+ def add_user(username)
87
+ unless user_exists?(username)
88
+ run_command(['useradd', '--user-group', '--comment', "'#{ username } user'", '--shell', '/bin/bash', '--home', app_dir, username], quiet: true)
89
+ end
90
+ end
91
+
92
+
93
+ def user_exists?(username)
94
+ Etc.getpwnam(username)
95
+ rescue
96
+ end
97
+
98
+
99
+ def airbrake_configured?
100
+ [airbrake_project_id, airbrake_project_key].all? { |var| !var.empty? }
101
+ end
102
+
103
+
104
+ def airbrake_project_id
105
+ ENV['SATORIX_CI_AIRBRAKE_PROJECT_ID'].to_s
106
+ end
107
+
108
+
109
+ def airbrake_project_key
110
+ ENV['SATORIX_CI_AIRBRAKE_PROJECT_KEY'].to_s
111
+ end
112
+
113
+
114
+ def airbrake_start
115
+ return unless airbrake_configured?
116
+
117
+ Airbrake.configure do |c|
118
+ c.project_id = airbrake_project_id
119
+ c.project_key = airbrake_project_key
120
+ end
121
+
122
+ at_exit do
123
+ Airbrake.notify_sync($ERROR_INFO) if $ERROR_INFO # https://github.com/airbrake/airbrake-ruby#reporting-critical-exceptions
124
+ Airbrake.close # https://github.com/airbrake/airbrake-ruby#airbrakeclose
125
+ end
126
+ end
127
+
128
+
129
+ def custom_loader_relative_path
130
+ File.join 'satorix', 'custom.rb'
131
+ end
132
+
133
+
134
+ def custom_loader_full_path
135
+ File.join build_dir, custom_loader_relative_path
136
+ end
137
+
138
+
139
+ def load_custom
140
+ log_bench "Looking for custom job definitions in #{ custom_loader_relative_path }....." do
141
+ if File.exist? custom_loader_full_path
142
+ log "Loading custom job definitions from #{ custom_loader_relative_path }."
143
+ require custom_loader_full_path
144
+ Satorix::Custom.available_jobs
145
+ else
146
+ # TODO : create this documentation and link to it in the message below
147
+ log 'No custom jobs found.'
148
+ log "You can define custom jobs by adding a #{ custom_loader_relative_path } file to your app root."
149
+ log 'For more information, please refer to https://www.satorix.com/docs/articles/custom_satorix_jobs.'
150
+ {}
151
+ end
152
+ end
153
+ end
154
+
155
+
156
+ def default_jobs
157
+ { deploy: { deploy_with_flynn: Satorix::CI::Deploy::Flynn },
158
+ test: { brakeman: Satorix::CI::Test::Ruby::Brakeman,
159
+ bundler_audit: Satorix::CI::Test::Ruby::BundlerAudit,
160
+ cucumber: Satorix::CI::Test::Ruby::Cucumber,
161
+ django_test: Satorix::CI::Test::Python::DjangoTest,
162
+ rails_test: Satorix::CI::Test::Ruby::RailsTest,
163
+ rspec: Satorix::CI::Test::Ruby::Rspec,
164
+ rubocop: Satorix::CI::Test::Ruby::Rubocop,
165
+ safety: Satorix::CI::Test::Python::Safety } }
166
+ end
167
+
168
+
169
+ def available_jobs
170
+ @_available_jobs ||= load_custom.tap do |jobs|
171
+ default_jobs.each do |stage, job_definitions|
172
+ jobs[stage] ||= {}
173
+ job_definitions.each { |job, target| jobs[stage][job] = target }
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ def ci_job_name
180
+ ENV['CI_JOB_NAME']
181
+ end
182
+
183
+
184
+ def ci_job_stage
185
+ ENV['CI_JOB_STAGE']
186
+ end
187
+
188
+
189
+ def ci_commit_ref_name
190
+ ENV['CI_COMMIT_REF_NAME']
191
+ end
192
+
193
+
194
+ def ci_commit_sha
195
+ ENV['CI_COMMIT_SHA']
196
+ end
197
+
198
+
199
+ def current_branch
200
+ @_current_branch ||= ci_commit_ref_name.to_s.upcase
201
+ end
202
+
203
+
204
+ def job_class
205
+ available_jobs[ci_job_stage.to_sym] && available_jobs[ci_job_stage.to_sym][ci_job_name.to_sym] || begin
206
+ log_error_and_abort "The #{ ci_job_name } job does not exist for the #{ ci_job_stage } stage!"
207
+ end
208
+ end
209
+
210
+
211
+ # https://stackoverflow.com/questions/4548151/run-ruby-block-as-specific-os-user/
212
+ def execute_as_user(user, &block)
213
+ u = (user.is_a? Integer) ? Etc.getpwuid(user) : Etc.getpwnam(user)
214
+
215
+ ENV['HOME'] = Satorix.app_dir
216
+
217
+ reader, writer = IO.pipe
218
+
219
+ Process.fork do
220
+ # the child process won't need to read from the pipe
221
+ reader.close
222
+
223
+ # use primary group ID of target user
224
+ # This needs to be done first as we won't have
225
+ # permission to change our group after changing EUID
226
+ Process.gid = Process.egid = u.gid
227
+
228
+ # set real and effective UIDs to target user
229
+ Process.uid = Process.euid = u.uid
230
+
231
+ # get the result and write it to the IPC pipe
232
+ result = block.call(user)
233
+ Marshal.dump(result, writer)
234
+ writer.close
235
+
236
+ # prevent shutdown hooks from running
237
+ Process.exit!(true)
238
+ end
239
+
240
+ # back to reality... we won't be writing anything
241
+ writer.close
242
+
243
+ # block until there's data to read
244
+ result = Marshal.load(reader)
245
+
246
+ # done with that!
247
+ reader.close
248
+
249
+ # return block result
250
+ result
251
+ rescue EOFError
252
+ log_error 'This job has failed.'
253
+ abort
254
+ end
255
+
256
+
257
+ def paths
258
+ ci_project_dir = ENV['CI_PROJECT_DIR'].to_s
259
+ { buildpacks: '/tmp/buildpacks',
260
+ app_dir: '/tmp/app',
261
+ build_dir: ci_project_dir,
262
+ cache: File.join(ci_project_dir, 'tmp', 'satorix', 'cache'),
263
+ env: '/tmp/env' }
264
+ end
265
+
266
+
267
+ def app_dir
268
+ paths[:app_dir]
269
+ end
270
+
271
+
272
+ def bin_dir
273
+ File.join app_dir, 'bin'
274
+ end
275
+
276
+
277
+ def build_dir
278
+ paths[:build_dir]
279
+ end
280
+
281
+
282
+ def prepare_app_environment
283
+ log_bench 'Preparing app environment...' do
284
+ add_user('satorix')
285
+ setup_directories
286
+ end
287
+ end
288
+
289
+
290
+ def project_name
291
+ ENV['CI_PROJECT_NAME']
292
+ end
293
+
294
+
295
+ def setup_directories
296
+ paths.values.each { |path| FileUtils.mkdir_p path }
297
+ paths.values.each { |path| FileUtils.chown_R 'satorix', 'satorix', path }
298
+ FileUtils.ln_s app_dir, '/app'
299
+ end
300
+
301
+
302
+ def skip_buildpack?
303
+ job_class.respond_to?(:skip_buildpack) && job_class.skip_buildpack
304
+ end
305
+
306
+
307
+ def yarn?
308
+ File.exist? yarn_lock_file
309
+ end
310
+
311
+
312
+ def yarn_lock_file
313
+ File.join(app_dir, 'yarn.lock')
314
+ end
315
+
316
+
317
+ def rails_app?
318
+ # This used to follow the Heroku buildpack, with:
319
+ # File.exist?(File.join(build_dir, 'Gemfile'))
320
+ # It has been adjusted to accommodate non-rails apps that use ruby gems.
321
+ # The config/application.rb file seems to be present in all rails applications.
322
+ # It may be useful to add additional checks, for increased precision, in the future.
323
+ File.exist?(File.join(build_dir, 'config', 'application.rb'))
324
+ end
325
+
326
+
327
+ def django_app?
328
+ # Satorix requires django apps be in a public directory.
329
+ # The manage.py file seems to be present in all django applications.
330
+ # It may be useful to add additional checks, for increased precision, in the future.
331
+ File.exist?(File.join(build_dir, 'public', 'manage.py'))
332
+ end
333
+
334
+
335
+ def ruby_gem?
336
+ # Checks for the presence of a *.gemspec file in the project root.
337
+ # It may be useful to add additional checks, for increased precision, in the future.
338
+ Dir[File.join(build_dir, '*.gemspec')].any?
339
+ end
340
+
5
341
  end