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
@@ -0,0 +1,22 @@
1
+ module Satorix
2
+ module CI
3
+ module Shared
4
+
5
+ module YarnManager
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Installing yarn dependencies...') { run_command(%w[yarn install]) }
14
+ end
15
+
16
+
17
+ private
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Python
5
+ module DjangoTest
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Python version...') { run_command(%w[python --version]) }
14
+ log_bench('Running tests...') { run_tests }
15
+ end
16
+
17
+
18
+ def run_tests
19
+ run_command([manage, 'collectstatic'])
20
+ run_command([manage, 'test', 'public'])
21
+ end
22
+
23
+
24
+ private
25
+
26
+
27
+ def manage
28
+ File.join(Satorix.app_dir, 'public', 'manage.py')
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Python
5
+ module Safety
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Python version...') { run_command(%w[python --version]) }
14
+ log_bench('Installing Safety...') { run_command(%w[pip install safety]) }
15
+ log_bench('Auditing requirements.txt...') { run_scan }
16
+ end
17
+
18
+
19
+ def run_scan
20
+ run_command(%w[safety check -r requirements.txt])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Ruby
5
+ module Brakeman
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Ruby version...') { run_command(%w[ruby -v]) }
14
+ log_bench('Installing Brakeman...') { install_gem }
15
+ log_bench('Running Brakeman scan...') { run_scan }
16
+ end
17
+
18
+
19
+ def install_gem
20
+ run_command(['gem', 'install', 'brakeman', '--no-document', '--bindir', Satorix.bin_dir])
21
+ end
22
+
23
+
24
+ def run_scan
25
+ run_command(['brakeman', '-z', '--table-width', '1200', '--exit-on-error', '--path', Satorix.app_dir])
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Ruby
5
+ module BundlerAudit
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Ruby version...') { run_command(%w[ruby -v]) }
14
+ log_bench('Installing bundler-audit...') { install_gem }
15
+ log_bench('Auditing Gemfile.lock...') { run_scan }
16
+ end
17
+
18
+
19
+ def install_gem
20
+ run_command(['gem', 'install', 'bundler-audit', '--no-document', '--bindir', Satorix.bin_dir])
21
+ end
22
+
23
+
24
+ def run_scan
25
+ run_command(%w[bundle-audit check --update])
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Ruby
5
+ module Cucumber
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Ruby version...') { run_command(%w[ruby -v]) }
14
+ log_bench('Running Cucumber...') { run_cucumber }
15
+ end
16
+
17
+
18
+ def run_cucumber
19
+ run_command(%w[bundle exec cucumber])
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Ruby
5
+ module RailsTest
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Ruby version...') { run_command(%w[ruby -v]) }
14
+ log_bench('Running Rails test...') { run_scan }
15
+ end
16
+
17
+
18
+ def run_scan
19
+ run_command(%w[rails test])
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Ruby
5
+ module Rspec
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Ruby version...') { run_command(%w[ruby -v]) }
14
+ log_bench('Running specs...') { run_specs }
15
+ end
16
+
17
+
18
+ def run_specs
19
+ run_command(%w[rspec spec])
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,98 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Ruby
5
+ module Rubocop
6
+
7
+ include Satorix::Shared::Console
8
+
9
+ extend self
10
+
11
+
12
+ def go
13
+ log_bench('Displaying current Ruby version...') { run_command(%w[ruby -v]) }
14
+ log_bench('Installing Rubocop...') { install_gems }
15
+ log_bench('Configuring Rubocop...') { create_rubocop_config }
16
+ log_bench('Running Rubocop inspection...') { run_scan }
17
+ end
18
+
19
+
20
+ def install_gems
21
+ run_command(['gem', 'install', 'rubocop', '--no-document', '--bindir', Satorix.bin_dir])
22
+ run_command(['gem', 'install', 'rubocop-performance', '--no-document', '--bindir', Satorix.bin_dir])
23
+ run_command(['gem', 'install', 'rubocop-rails_config', '--no-document', '--bindir', Satorix.bin_dir]) if Satorix.rails_app?
24
+ end
25
+
26
+
27
+ def create_rubocop_config
28
+ if config_exist?
29
+ log 'Using existing .rubocop.yml file from project.'
30
+ else
31
+ log 'A .rubocop.yml file was not found, generating a default configuration file for this project.'
32
+ content = Satorix.rails_app? ? config_content_rails : config_content_default
33
+ save_rubocop_config content
34
+ end
35
+ log 'For more information, please refer to https://www.satorix.com/docs/articles/app_using_ruby_on_rails#rubocop.'
36
+ end
37
+
38
+
39
+ def run_scan
40
+ command = %w[rubocop --require rubocop-performance --display-cop-names --extra-details --display-style-guide --parallel]
41
+ command.concat(%w[--require rubocop-rails]) if Satorix.rails_app?
42
+
43
+ run_command(command)
44
+ end
45
+
46
+
47
+ private
48
+
49
+
50
+ def rubocop_config_file
51
+ File.join(Satorix.app_dir, '.rubocop.yml')
52
+ end
53
+
54
+
55
+ def save_rubocop_config(config)
56
+ File.open(rubocop_config_file, 'w') { |f| f.write(config) }
57
+ end
58
+
59
+
60
+ def config_exist?
61
+ File.exist? rubocop_config_file
62
+ end
63
+
64
+
65
+ def ruby_version
66
+ version = run_command(%w[bundle platform --ruby], quiet: true)
67
+ version.match(/\d+\.\d+\.\d+/)[0]
68
+ end
69
+
70
+
71
+ def config_content_default
72
+ <<~CONTENT
73
+ AllCops:
74
+ TargetRubyVersion: #{ ruby_version }
75
+ Exclude:
76
+ - '**/tmp/**/*'
77
+ - '**/templates/**/*'
78
+ - '**/vendor/**/*'
79
+ CONTENT
80
+ end
81
+
82
+
83
+ def config_content_rails
84
+ <<~CONTENT
85
+ inherit_gem:
86
+ rubocop-rails_config:
87
+ - config/rails.yml
88
+
89
+ AllCops:
90
+ TargetRubyVersion: #{ ruby_version }
91
+ CONTENT
92
+ end
93
+
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,74 @@
1
+ module Satorix
2
+ module CI
3
+ module Test
4
+ module Shared
5
+ module Database
6
+
7
+
8
+ include Satorix::Shared::Console
9
+
10
+
11
+ extend self
12
+
13
+
14
+ def host
15
+ ENV['DB_HOST'].to_s
16
+ end
17
+
18
+
19
+ def name
20
+ name_key.empty? ? '' : ENV[name_key].to_s
21
+ end
22
+
23
+
24
+ def name_key
25
+ {
26
+ mariadb: 'MYSQL_DATABASE',
27
+ mysql: 'MYSQL_DATABASE',
28
+ postgres: 'POSTGRES_DB'
29
+ }[host.to_sym].to_s
30
+ end
31
+
32
+
33
+ def password
34
+ password_key.empty? ? '' : ENV[password_key].to_s
35
+ end
36
+
37
+
38
+ def password_key
39
+ {
40
+ mariadb: 'MYSQL_ROOT_PASSWORD',
41
+ mysql: 'MYSQL_ROOT_PASSWORD',
42
+ postgres: 'POSTGRES_PASSWORD'
43
+ }[host.to_sym].to_s
44
+ end
45
+
46
+
47
+ def url
48
+ unset = [host, user, password, name].select(&:empty?)
49
+ if unset.empty?
50
+ "#{ host }://#{ user }:#{ password }@#{ host }/#{ name }"
51
+ else
52
+ log 'No database has been configured.'
53
+ log 'If you would like to use a database for this job, ensure that your gitlab-config.yml is configured.'
54
+ log 'Most databases require you to specify a type, host, database, username, and password.'
55
+ nil
56
+ end
57
+ end
58
+
59
+
60
+ def user
61
+ (ENV[user_key] || 'root').to_s
62
+ end
63
+
64
+
65
+ def user_key
66
+ "#{ host.upcase }_USER"
67
+ end
68
+
69
+
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,180 @@
1
+ module Satorix
2
+ module Shared
3
+ module Console
4
+
5
+ require 'benchmark'
6
+ require 'English' # http://ruby-doc.org/stdlib-2.0.0/libdoc/English/rdoc/English.html
7
+ require 'shellwords'
8
+
9
+ extend self
10
+
11
+
12
+ def colorize(text, color = nil)
13
+ "\033[#{ colors[color] }m#{ text }\033[0m"
14
+ end
15
+
16
+
17
+ def colors
18
+ Hash.new(39).merge(red: '0;31', light_red: '1;31',
19
+ green: '0;32', light_green: '1;32',
20
+ yellow: '0;33', light_yellow: '1;33',
21
+ blue: '0;34', light_blue: '1;34',
22
+ magenta: '0;35', light_magenta: '1;35',
23
+ cyan: '0;36', light_cyan: '1;36',
24
+ white: '0;37', light_white: '1;37')
25
+ end
26
+
27
+
28
+ def humanize_time(seconds)
29
+ return 'less than 1 second' if seconds < 1
30
+ [[60, :second], [60, :minute], [24, :hour], [1000, :day]].map do |count, name|
31
+ if seconds > 0
32
+ seconds, n = seconds.divmod(count)
33
+ n = n.to_i
34
+ "#{ n } #{ name }#{ 's' if n > 1 }" if n > 0
35
+ end
36
+ end.compact.reverse.join(' ')
37
+ end
38
+
39
+
40
+ def log(text, color = nil)
41
+ puts color ? colorize(text, color) : text
42
+ end
43
+
44
+
45
+ def log_command(text)
46
+ log text, :cyan
47
+ end
48
+
49
+
50
+ def log_duration(text)
51
+ log text, :light_cyan
52
+ end
53
+
54
+
55
+ def log_header(text)
56
+ log("\n#{ text }", :light_green)
57
+ end
58
+
59
+
60
+ def log_error(text)
61
+ log text, :light_red
62
+ end
63
+
64
+
65
+ def log_error_and_abort(text)
66
+ log_error text
67
+ abort text
68
+ end
69
+
70
+
71
+ def log_bench(message)
72
+ log_header message
73
+ result = nil
74
+ log_duration "Time elapsed: #{ humanize_time Benchmark.realtime { result = yield } }."
75
+ result
76
+ end
77
+
78
+
79
+ # The preferred way to use this method is by passing an array containing the command.
80
+ # This will ensure that items are properly escaped.
81
+ # It is also possible to use nested arrays. See tests for example use cases.
82
+ #
83
+ # If you want to see how your string command translates into an arry, you can use shellsplit method.
84
+ #
85
+ # For example:
86
+ # 'ruby -v'.shellsplit
87
+ # => ["ruby", "-v"]
88
+ #
89
+ # If, for some reason, you need to supply a string that is already escaped or should not be escaped, it is
90
+ # possible to supply a string argument instead of an array. If you do this, you will need to supply
91
+ # an additional parameter of string_verified_safe, to ensure that the operation is deliberate.
92
+ def run_command(command, filtered_text: [], quiet: false, string_verified_safe: false)
93
+ # This method is used *EVERYWHERE*.
94
+ # Seemingly small changes can have far-ranging and extreme consequences.
95
+ # Modify only with extreme caution.
96
+ #
97
+ # Note: Many applications (like echo and the Flynn CLI) append a newline to their output.
98
+ # If you are using the command result, it may be desirable to chomp the trailing
99
+ # newline. It is left to the implementing call to handle this, as it proved
100
+ # cumbersome to handle in this method in a way that worked perfectly in all cases.
101
+ raise('Potentially unsafe or problematic command. Please refer to the method documentation for more information.') if command.is_a?(String) && !string_verified_safe
102
+
103
+ command = normalized_command(command)
104
+ logged_command = logged_command(command, filtered_text)
105
+ log_command(logged_command) unless quiet
106
+
107
+ ''.tap do |output|
108
+ IO.popen(bash(command)) do |io|
109
+ until io.eof?
110
+ line = io.gets
111
+ puts line unless quiet || line.empty?
112
+ $stdout.flush
113
+ output.concat line
114
+ end
115
+ end
116
+
117
+ handle_run_command_error(logged_command, quiet) unless $CHILD_STATUS.success?
118
+ end
119
+ end
120
+
121
+
122
+ # http://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script
123
+ # TODO : reduce / consolidate?
124
+ # Find variables changed as a result of sourcing the given file, and update in ENV.
125
+ def source_env_from(file)
126
+ bash_source(file).each { |k, v| ENV[k] = v }
127
+ end
128
+
129
+
130
+ private
131
+
132
+
133
+ def bash(command)
134
+ ['bash -c', escaped_command(command), '2>&1'].join(' ')
135
+ end
136
+
137
+
138
+ def logged_command(command, filtered_text)
139
+ filtered_text = [filtered_text].flatten.delete_if { |text| text.nil? || text.strip.empty? }
140
+ unescaped_command = command.shellsplit.join(' ')
141
+ unescaped_command.gsub(Regexp.union(filtered_text.sort_by(&:length).reverse), '[MASKED]')
142
+ end
143
+
144
+
145
+ def escaped_command(command)
146
+ command.shellescape
147
+ end
148
+
149
+
150
+ def normalized_command(command)
151
+ command.is_a?(Array) ? command.flatten.shelljoin : command
152
+ end
153
+
154
+
155
+ def handle_run_command_error(logged_command, quiet)
156
+ error_message = "\nAn error has occurred while running the following command:\n#{ logged_command }\n"
157
+ quiet ? abort : log_error_and_abort(error_message)
158
+ end
159
+
160
+
161
+ # http://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script
162
+ # TODO : reduce / consolidate?
163
+ # Read in the bash environment, after an optional command. Returns Array of key/value pairs.
164
+ def bash_env(cmd = nil)
165
+ env_cmd = bash("#{ cmd + ';' if cmd } printenv")
166
+ env = `#{ env_cmd }`
167
+ env.split(/\n/).map { |l| l.split(/=/, 2) }
168
+ end
169
+
170
+
171
+ # http://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script
172
+ # TODO : reduce / consolidate?
173
+ # Source a given file, and compare environment before and after. Returns Hash of any keys that have changed.
174
+ def bash_source(file)
175
+ Hash[bash_env("source #{ File.realpath file }") - bash_env]
176
+ end
177
+
178
+ end
179
+ end
180
+ end