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
@@ -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