satorix 0.0.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +4 -1
- data/.gitlab-ci.yml +45 -0
- data/.rspec +2 -1
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +25 -0
- data/Procfile +1 -0
- data/README.md +93 -1
- data/Rakefile +8 -4
- data/bin/console +3 -3
- data/bin/satorix +8 -0
- data/lib/satorix.rb +338 -2
- data/lib/satorix/CI/deploy/flynn.rb +129 -0
- data/lib/satorix/CI/deploy/flynn/environment_variables.rb +121 -0
- data/lib/satorix/CI/deploy/flynn/resources.rb +77 -0
- data/lib/satorix/CI/deploy/flynn/routes.rb +266 -0
- data/lib/satorix/CI/deploy/flynn/scale.rb +52 -0
- data/lib/satorix/CI/shared/buildpack_manager.rb +213 -0
- data/lib/satorix/CI/shared/buildpack_manager/buildpack.rb +153 -0
- data/lib/satorix/CI/shared/ruby/gem_manager.rb +79 -0
- data/lib/satorix/CI/shared/yarn_manager.rb +22 -0
- data/lib/satorix/CI/test/python/django_test.rb +35 -0
- data/lib/satorix/CI/test/python/safety.rb +27 -0
- data/lib/satorix/CI/test/ruby/brakeman.rb +32 -0
- data/lib/satorix/CI/test/ruby/bundler_audit.rb +32 -0
- data/lib/satorix/CI/test/ruby/cucumber.rb +26 -0
- data/lib/satorix/CI/test/ruby/rails_test.rb +26 -0
- data/lib/satorix/CI/test/ruby/rspec.rb +26 -0
- data/lib/satorix/CI/test/ruby/rubocop.rb +98 -0
- data/lib/satorix/CI/test/shared/database.rb +74 -0
- data/lib/satorix/shared/console.rb +180 -0
- data/lib/satorix/version.rb +1 -1
- data/satorix.gemspec +13 -11
- data/satorix/CI/deploy/ie_gem_server.rb +76 -0
- data/satorix/CI/deploy/rubygems.rb +77 -0
- data/satorix/custom.rb +21 -0
- metadata +57 -29
- data/.travis.yml +0 -5
@@ -0,0 +1,52 @@
|
|
1
|
+
module Satorix
|
2
|
+
module CI
|
3
|
+
module Deploy
|
4
|
+
module Flynn
|
5
|
+
module Scale
|
6
|
+
|
7
|
+
def adjust_scale
|
8
|
+
cached_scale_options_to_set = scale_options_to_set
|
9
|
+
log "No scale specified in #{ defined_scale_key }. Displaying current scale:" if cached_scale_options_to_set.empty?
|
10
|
+
fc_scale cached_scale_options_to_set
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def current_scale
|
15
|
+
scale_string_to_hash fc_scale
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def defined_scale
|
20
|
+
scale_string_to_hash ENV[defined_scale_key].to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def defined_scale_key
|
25
|
+
"FLYNN_#{ current_branch }_SCALE"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def fc_scale(scale_options_to_set = nil)
|
30
|
+
command = %w[flynn scale].concat(scale_options_to_set)
|
31
|
+
|
32
|
+
run_command(command)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def scale_options_to_set
|
37
|
+
[].tap do |scale|
|
38
|
+
defined_scale.each { |job, workers| scale << "#{ job }=#{ workers }" }
|
39
|
+
(current_scale.keys - defined_scale.keys).each { |job| scale << "#{ job }=0" } unless defined_scale.empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def scale_string_to_hash(scale_string)
|
45
|
+
{}.tap { |jobs| scale_string.split.map { |x| x.partition('=') }.each { |x| jobs[x.first.to_sym] = x.last } }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Satorix
|
2
|
+
module CI
|
3
|
+
module Shared
|
4
|
+
|
5
|
+
# This code was inspired by:
|
6
|
+
# herokuish - https://github.com/gliderlabs/herokuish
|
7
|
+
# flynn - https://github.com/flynn/flynn/blob/master/slugbuilder/builder/build.sh
|
8
|
+
module BuildpackManager
|
9
|
+
|
10
|
+
require 'fileutils'
|
11
|
+
|
12
|
+
include Satorix::Shared::Console
|
13
|
+
|
14
|
+
extend self
|
15
|
+
|
16
|
+
|
17
|
+
def go
|
18
|
+
abort_if_skip_buildpack
|
19
|
+
prepare_buildpack_environment
|
20
|
+
run_buildpacks
|
21
|
+
load_dot_release_file
|
22
|
+
switch_context_to_new_test_slug
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def active_buildpack_list
|
27
|
+
custom_buildpack_list || stack_buildpack_list
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def active_buildpacks
|
32
|
+
active_buildpack_list.split.map { |buildpack| Buildpack.new buildpack }
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def cedar_14?
|
37
|
+
stack_version == 'cedar-14'
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def copy_app_dir
|
42
|
+
FileUtils.cp_r File.join(Satorix.build_dir, '/.'), Satorix.app_dir, remove_destination: true
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def custom_buildpack_list
|
47
|
+
IO.binread(files[:dot_buildpacks]).strip if custom_buildpacks?
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def custom_buildpacks?
|
52
|
+
File.exist? files[:dot_buildpacks]
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def default_buildpack_list
|
57
|
+
# When updating, pull from the most recent nightly release listed at https://releases.flynn.io/.
|
58
|
+
# Below was taken from https://github.com/flynn/flynn/blob/v20190814.0/slugbuilder/builder/buildpacks.txt
|
59
|
+
<<-THESE_ARE_THE_BUILDPACKS_OFFICIALLY_SUPPORTED_BY_FLYNN.strip
|
60
|
+
https://github.com/heroku/heroku-buildpack-multi.git#ed950773
|
61
|
+
https://github.com/cloudfoundry/staticfile-buildpack.git#e482e8f2
|
62
|
+
https://github.com/heroku/heroku-buildpack-ruby.git#4ca71a9d
|
63
|
+
https://github.com/heroku/heroku-buildpack-nodejs.git#a4fb9419
|
64
|
+
https://github.com/heroku/heroku-buildpack-clojure.git#5858bad3
|
65
|
+
https://github.com/heroku/heroku-buildpack-python.git#9dcabe24
|
66
|
+
https://github.com/heroku/heroku-buildpack-java.git#354d2a79
|
67
|
+
https://github.com/heroku/heroku-buildpack-gradle.git#b89c8c38
|
68
|
+
https://github.com/heroku/heroku-buildpack-scala.git#41c296d4
|
69
|
+
https://github.com/heroku/heroku-buildpack-php.git#62a691bf
|
70
|
+
https://github.com/heroku/heroku-buildpack-go.git#6f80fd9c
|
71
|
+
THESE_ARE_THE_BUILDPACKS_OFFICIALLY_SUPPORTED_BY_FLYNN
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def default_cedar_14_buildpack_list
|
76
|
+
# When updating, pull from the most recent stable release listed at https://releases.flynn.io/.
|
77
|
+
# Below was taken from https://github.com/flynn/flynn/blob/v20170321.0/slugbuilder/builder/buildpacks.txt
|
78
|
+
<<-THESE_ARE_THE_BUILDPACKS_OFFICIALLY_SUPPORTED_BY_FLYNN.strip
|
79
|
+
https://github.com/heroku/heroku-buildpack-multi.git#ed950773
|
80
|
+
https://github.com/cloudfoundry/staticfile-buildpack.git#206728f9
|
81
|
+
https://github.com/heroku/heroku-buildpack-ruby.git#6988832f
|
82
|
+
https://github.com/heroku/heroku-buildpack-nodejs.git#9b8a98d8
|
83
|
+
https://github.com/heroku/heroku-buildpack-clojure.git#8768a8ff
|
84
|
+
https://github.com/heroku/heroku-buildpack-python.git#cafd4182
|
85
|
+
https://github.com/heroku/heroku-buildpack-java.git#8c34efe9
|
86
|
+
https://github.com/heroku/heroku-buildpack-gradle.git#13fa1fe7
|
87
|
+
https://github.com/heroku/heroku-buildpack-scala.git#dd0dd806
|
88
|
+
https://github.com/heroku/heroku-buildpack-play.git#cc5e6166
|
89
|
+
https://github.com/heroku/heroku-buildpack-php.git#e0499a7f
|
90
|
+
https://github.com/heroku/heroku-buildpack-go.git#bd1acfe5
|
91
|
+
THESE_ARE_THE_BUILDPACKS_OFFICIALLY_SUPPORTED_BY_FLYNN
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def files
|
96
|
+
build_dir = Satorix.build_dir
|
97
|
+
{ dot_buildpacks: File.join(build_dir, '.buildpacks'),
|
98
|
+
dot_env: File.join(build_dir, '.env'),
|
99
|
+
dot_release: File.join(build_dir, '.release') }
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def load_dot_release_file
|
104
|
+
# TODO : Currently not required. Maybe should be implemented for maximum compatibility?
|
105
|
+
log_error_and_abort "Unhandled file '#{ files[:dot_release] }'." if File.exists? files[:dot_release]
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def lsb_release
|
110
|
+
run_command(%w[lsb_release --release --short], quiet: true).chomp
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def prepare_buildpack_environment
|
115
|
+
log_bench 'Preparing buildpack environment...' do
|
116
|
+
set_buildpack_expectations
|
117
|
+
process_dot_env_file
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def process_dot_env_file
|
123
|
+
# TODO : Currently not required. Maybe should be implemented for maximum compatibility?
|
124
|
+
log_error_and_abort "Unhandled file '#{ files[:dot_env] }'" if File.exists? files[:dot_env]
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def load_env
|
129
|
+
# TODO : Currently not required. Maybe should be implemented for maximum compatibility?
|
130
|
+
log_error_and_abort 'Unhandled load_env' unless (Dir.entries(Satorix.paths[:env]) - %w(. ..)).empty?
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def load_profile
|
135
|
+
profile_locations.each do |profile_location|
|
136
|
+
Dir.glob(profile_location) do |shell_file|
|
137
|
+
# Uncomment below to debug profile loading
|
138
|
+
# run_command(['cat', shell_file])
|
139
|
+
source_env_from shell_file
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def profile_locations
|
146
|
+
[File.join('/etc/profile.d', '*.sh'),
|
147
|
+
File.join(Satorix.app_dir, '.profile.d', '*.sh')]
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def run_buildpacks
|
152
|
+
buildpacks = []
|
153
|
+
|
154
|
+
log_bench('Detecting application type...') do
|
155
|
+
active_buildpacks.each(&:ensure_correctness)
|
156
|
+
buildpacks = active_buildpacks.keep_if(&:detected?)
|
157
|
+
|
158
|
+
if buildpacks.empty?
|
159
|
+
log_error_and_abort 'Unable to select a buildpack!'
|
160
|
+
elsif buildpacks.length > 1 && !custom_buildpacks?
|
161
|
+
log_error("\n\nWarning: Multiple default buildpacks reported the ability to handle this app."\
|
162
|
+
" The first buildpack in the list above will be used.\n\n")
|
163
|
+
buildpacks = buildpacks.first(1)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
buildpacks.each(&:compile)
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def set_buildpack_expectations
|
172
|
+
ENV['APP_DIR'] = Satorix.app_dir
|
173
|
+
ENV['REQUEST_ID'] = "commit-#{ Satorix.ci_commit_sha }"
|
174
|
+
ENV['STACK'] = stack_version
|
175
|
+
ENV['DATABASE_URL'] = Satorix::CI::Test::Shared::Database.url
|
176
|
+
ENV['CURL_CONNECT_TIMEOUT'] = '30'
|
177
|
+
ENV['CURL_TIMEOUT'] = '600'
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def abort_if_skip_buildpack
|
182
|
+
if ENV['SKIP_BUILDPACK']
|
183
|
+
log_error_and_abort("\n\nDEPRECATED: Skipping buildpack is no longer supported, please update your .gitlab-ci.yml.\n\n")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def stack_version
|
189
|
+
if lsb_release == '14.04'
|
190
|
+
return 'cedar-14'
|
191
|
+
else
|
192
|
+
return 'heroku-18'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
def stack_buildpack_list
|
198
|
+
cedar_14? ? default_cedar_14_buildpack_list : default_buildpack_list
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
def switch_context_to_new_test_slug
|
203
|
+
log_bench 'Switching context to the newly built application...' do
|
204
|
+
copy_app_dir
|
205
|
+
load_env
|
206
|
+
load_profile
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Satorix
|
2
|
+
module CI
|
3
|
+
module Shared
|
4
|
+
module BuildpackManager
|
5
|
+
|
6
|
+
class Buildpack
|
7
|
+
|
8
|
+
require 'fileutils'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
include Satorix::Shared::Console
|
12
|
+
|
13
|
+
attr_accessor :commit_sha_short,
|
14
|
+
:url
|
15
|
+
|
16
|
+
|
17
|
+
def go
|
18
|
+
log_error_and_abort 'Buildpack.go should not be called directly - use BuildpackManager.go.'
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def initialize(initialization_url)
|
23
|
+
self.url, self.commit_sha_short = initialization_url.to_s.strip.split('#')
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def compile
|
28
|
+
log_bench "Building application using the #{ name } buildpack..." do
|
29
|
+
run_command([compile_binary_path,
|
30
|
+
Satorix.build_dir,
|
31
|
+
Satorix.paths[:cache],
|
32
|
+
Satorix.paths[:env]])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def compile_binary_path
|
38
|
+
bin_path = File.join(path, 'bin')
|
39
|
+
test_compile_path = File.join(bin_path, 'test-compile')
|
40
|
+
compile_path = File.join(bin_path, 'compile')
|
41
|
+
File.exist?(test_compile_path) ? test_compile_path : compile_path
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def detected?
|
46
|
+
if BuildpackManager.custom_buildpacks?
|
47
|
+
log "Custom Buildpack: #{ name }."
|
48
|
+
else
|
49
|
+
command = [File.join(path, 'bin', 'detect'), Satorix.build_dir]
|
50
|
+
buildpack_name = run_command(command, quiet: true).chomp
|
51
|
+
log "Detected Framework: #{ buildpack_name }."
|
52
|
+
end
|
53
|
+
true
|
54
|
+
rescue SystemExit
|
55
|
+
# By design, buildpacks return a non-zero exit code
|
56
|
+
# (which raises a SystemExit exception) when not detected.
|
57
|
+
# This is not particularly exceptional, so we just return false.
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def name
|
63
|
+
File.basename(URI.parse(url).path, ".git")
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def path
|
68
|
+
File.join Satorix.paths[:buildpacks], name
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def ensure_correctness
|
73
|
+
unless correct_version?
|
74
|
+
delete!
|
75
|
+
checkout
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def delete!
|
81
|
+
FileUtils.rm_rf path
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def exist?
|
86
|
+
Dir.exist? path
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def checkout
|
91
|
+
log "Downloading Buildpack: #{ url }."
|
92
|
+
commit_sha_short ? checkout_specific_version : checkout_newest_version
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def checkout_specific_version
|
97
|
+
run_command(['git', 'clone', '--quiet', '--no-checkout', url, path], quiet: true)
|
98
|
+
Dir.chdir(path) { run_command(['git', 'checkout', '--quiet', commit_sha_short], quiet: true) }
|
99
|
+
Dir.chdir(path) { run_command(%w[git submodule update --init --recursive], quiet: true) }
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def checkout_newest_version
|
104
|
+
run_command(['git', 'clone', '--quiet', '--recursive', '--depth', '1', url, path], quiet: true)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def correct_version?
|
109
|
+
exist? &&
|
110
|
+
commit_sha_short &&
|
111
|
+
commit_sha_short == current_commit_sha_short_on_disk
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def commit_sha_length
|
116
|
+
commit_sha_short ? commit_sha_short.to_s.length : 7
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def satorix_distrib_sha_version
|
121
|
+
IO.binread(satorix_distrib_sha_path).strip if satorix_distrib_sha?
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def satorix_distrib_sha?
|
126
|
+
File.exist? satorix_distrib_sha_path
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def satorix_distrib_sha_path
|
131
|
+
File.join(path, '.distrib-sha')
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def git_sha_short
|
136
|
+
Dir.chdir(path) do
|
137
|
+
# Also works: `git show -s --format=%h`.strip
|
138
|
+
run_command(['git', 'rev-parse', "--short=#{ commit_sha_length }", 'HEAD'], quiet: true).strip
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def current_commit_sha_short_on_disk
|
144
|
+
return '' unless exist?
|
145
|
+
satorix_distrib_sha_version || git_sha_short
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Satorix
|
2
|
+
module CI
|
3
|
+
module Shared
|
4
|
+
module Ruby
|
5
|
+
module GemManager
|
6
|
+
|
7
|
+
include Satorix::Shared::Console
|
8
|
+
|
9
|
+
extend self
|
10
|
+
|
11
|
+
|
12
|
+
def go(quiet = true)
|
13
|
+
if Satorix.ci_job_stage == 'deploy'
|
14
|
+
prepare_gem_for_deployment(quiet)
|
15
|
+
else
|
16
|
+
prepare_gem_environment_for_test(quiet)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def prepare_gem_for_deployment(quiet = true)
|
22
|
+
log_bench 'Preparing gem for deployment...' do
|
23
|
+
# Development dependencies should not be included in the Gemfile.lock
|
24
|
+
# otherwise they will become dependencies of the implementing application
|
25
|
+
remove_development_dependencies_from_gemspec(quiet)
|
26
|
+
regenerate_gemfile_lock(quiet)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def prepare_gem_environment_for_test(quiet = true)
|
32
|
+
log_bench 'Preparing gem environment...' do
|
33
|
+
restore_missing_gems(quiet)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def restore_missing_gems(quiet = true)
|
39
|
+
# The buildpack incorrectly removes gems that are included using add_development_dependency.
|
40
|
+
# These gems must remain available for testing.
|
41
|
+
run_command(%w[bundle install --with development], quiet: quiet)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def remove_development_dependencies_from_gemspec(quiet = true)
|
46
|
+
Dir[File.join(Satorix.app_dir, '*.gemspec')].each do |file|
|
47
|
+
log File.read(file) unless quiet
|
48
|
+
|
49
|
+
backup_file_name = "#{ file }.satorix.old"
|
50
|
+
FileUtils.mv(file, backup_file_name)
|
51
|
+
|
52
|
+
File.open(file, 'w') do |modified_file|
|
53
|
+
File.foreach(backup_file_name) do |line|
|
54
|
+
modified_file.puts line unless line =~ /\.add_development_dependency/
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
File.delete(backup_file_name)
|
59
|
+
|
60
|
+
log File.read(file) unless quiet
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def regenerate_gemfile_lock(quiet = true)
|
67
|
+
run_command(%w[bundle config --local frozen false], quiet: quiet)
|
68
|
+
run_command(%w[bundle install], quiet: quiet)
|
69
|
+
log "\n\n#{ File.read(File.join(Satorix.app_dir, 'Gemfile.lock')) }" unless quiet
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|