satorix 0.0.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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