satorix 0.0.1 → 1.5.3
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/CI/deploy/flynn/environment_variables.rb +123 -0
- data/lib/satorix/CI/deploy/flynn/resources.rb +79 -0
- data/lib/satorix/CI/deploy/flynn/routes.rb +267 -0
- data/lib/satorix/CI/deploy/flynn/scale.rb +52 -0
- data/lib/satorix/CI/deploy/flynn.rb +132 -0
- data/lib/satorix/CI/shared/buildpack_manager/buildpack.rb +159 -0
- data/lib/satorix/CI/shared/buildpack_manager.rb +220 -0
- data/lib/satorix/CI/shared/ruby/gem_manager.rb +80 -0
- data/lib/satorix/CI/shared/yarn_manager.rb +25 -0
- data/lib/satorix/CI/test/python/django_test.rb +38 -0
- data/lib/satorix/CI/test/python/safety.rb +30 -0
- data/lib/satorix/CI/test/ruby/brakeman.rb +35 -0
- data/lib/satorix/CI/test/ruby/bundler_audit.rb +35 -0
- data/lib/satorix/CI/test/ruby/cucumber.rb +29 -0
- data/lib/satorix/CI/test/ruby/rails_test.rb +29 -0
- data/lib/satorix/CI/test/ruby/rspec.rb +29 -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 +157 -0
- data/lib/satorix/version.rb +1 -1
- data/lib/satorix.rb +343 -2
- data/satorix/CI/deploy/ie_gem_server.rb +80 -0
- data/satorix/CI/deploy/rubygems.rb +81 -0
- data/satorix/custom.rb +21 -0
- data/satorix.gemspec +13 -11
- metadata +57 -29
- data/.travis.yml +0 -5
@@ -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,157 @@
|
|
1
|
+
module Satorix
|
2
|
+
module Shared
|
3
|
+
module Console
|
4
|
+
|
5
|
+
|
6
|
+
require 'benchmark'
|
7
|
+
require 'English' # http://ruby-doc.org/stdlib-2.0.0/libdoc/English/rdoc/English.html
|
8
|
+
require 'shellwords'
|
9
|
+
|
10
|
+
|
11
|
+
extend self
|
12
|
+
|
13
|
+
|
14
|
+
def colors
|
15
|
+
Hash.new(39).merge(red: '0;31', light_red: '1;31',
|
16
|
+
green: '0;32', light_green: '1;32',
|
17
|
+
yellow: '0;33', light_yellow: '1;33',
|
18
|
+
blue: '0;34', light_blue: '1;34',
|
19
|
+
magenta: '0;35', light_magenta: '1;35',
|
20
|
+
cyan: '0;36', light_cyan: '1;36',
|
21
|
+
white: '0;37', light_white: '1;37')
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def colorize(text, color = nil)
|
26
|
+
"\033[#{ colors[color] }m#{ text }\033[0m"
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def log(text, color = nil)
|
31
|
+
puts color ? colorize(text, color) : text
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def log_command(text)
|
36
|
+
log text, :cyan
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def log_duration(text)
|
41
|
+
log text, :light_cyan
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def log_header(text)
|
46
|
+
log("\n#{ text }", :light_green)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def log_error(text)
|
51
|
+
log text, :light_red
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def log_error_and_abort(text)
|
56
|
+
log_error text
|
57
|
+
abort text
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def log_bench(message)
|
62
|
+
log_header message
|
63
|
+
result = nil
|
64
|
+
log_duration "Time elapsed: #{ humanize_time Benchmark.realtime { result = yield } }."
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def humanize_time(seconds)
|
70
|
+
return 'less than 1 second' if seconds < 1
|
71
|
+
[[60, :second], [60, :minute], [24, :hour], [1000, :day]].map do |count, name|
|
72
|
+
if seconds > 0
|
73
|
+
seconds, n = seconds.divmod(count)
|
74
|
+
n = n.to_i
|
75
|
+
"#{ n } #{ name }#{ 's' if n > 1 }" if n > 0
|
76
|
+
end
|
77
|
+
end.compact.reverse.join(' ')
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# This method is used *EVERYWHERE*.
|
82
|
+
# Seemingly small changes can have far-ranging and extreme consequences.
|
83
|
+
# Modify only with extreme caution.
|
84
|
+
#
|
85
|
+
# Note: Many applications (like echo and the Flynn CLI) append a newline to their output.
|
86
|
+
# If you are using the command result, it may be desirable to chomp the trailing
|
87
|
+
# newline. It is left to the implementing call to handle this, as it proved
|
88
|
+
# cumbersome to handle in this method in a way that worked perfectly in all cases.
|
89
|
+
def run_command(command, filtered_text: [], quiet: false)
|
90
|
+
command = command.shelljoin if command.is_a?(Array)
|
91
|
+
logged_command = logged_command(command, filtered_text)
|
92
|
+
log_command(logged_command) unless quiet
|
93
|
+
|
94
|
+
''.tap do |output|
|
95
|
+
IO.popen(bash(command)) do |io|
|
96
|
+
until io.eof?
|
97
|
+
line = io.gets
|
98
|
+
puts line unless quiet || line.empty?
|
99
|
+
$stdout.flush
|
100
|
+
output.concat line
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
handle_run_command_error(logged_command, quiet) unless $CHILD_STATUS.success?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# http://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script
|
110
|
+
# TODO : reduce / consolidate?
|
111
|
+
# Find variables changed as a result of sourcing the given file, and update in ENV.
|
112
|
+
def source_env_from(file)
|
113
|
+
bash_source(file).each { |k, v| ENV[k] = v }
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
private ##########################################################################################################
|
118
|
+
|
119
|
+
|
120
|
+
def logged_command(command, filtered_text)
|
121
|
+
filtered_text = [filtered_text].flatten.delete_if { |text| text.nil? || text.strip.empty? }
|
122
|
+
command.gsub(Regexp.union(filtered_text), '[MASKED]')
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def bash(command)
|
127
|
+
"bash -c #{ command.shellescape }"
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
# http://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script
|
132
|
+
# TODO : reduce / consolidate?
|
133
|
+
# Read in the bash environment, after an optional command. Returns Array of key/value pairs.
|
134
|
+
def bash_env(cmd = nil)
|
135
|
+
env_cmd = bash("#{ cmd + ';' if cmd } printenv")
|
136
|
+
env = `#{ env_cmd }`
|
137
|
+
env.split(/\n/).map { |l| l.split(/=/, 2) }
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# http://stackoverflow.com/questions/1197224/source-shell-script-into-environment-within-a-ruby-script
|
142
|
+
# TODO : reduce / consolidate?
|
143
|
+
# Source a given file, and compare environment before and after. Returns Hash of any keys that have changed.
|
144
|
+
def bash_source(file)
|
145
|
+
Hash[bash_env("source #{ File.realpath file }") - bash_env]
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def handle_run_command_error(logged_command, quiet)
|
150
|
+
error_message = "\nAn error has occurred while running the following command:\n#{ logged_command }\n"
|
151
|
+
quiet ? abort : log_error_and_abort(error_message)
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/satorix/version.rb
CHANGED
data/lib/satorix.rb
CHANGED
@@ -1,5 +1,346 @@
|
|
1
|
-
require
|
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
|
-
|
7
|
+
|
8
|
+
|
9
|
+
module Shared
|
10
|
+
autoload :Console, 'satorix/shared/console'
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
module CI
|
15
|
+
|
16
|
+
module Deploy
|
17
|
+
autoload :Flynn, 'satorix/CI/deploy/flynn'
|
18
|
+
end
|
19
|
+
|
20
|
+
module Shared
|
21
|
+
autoload :BuildpackManager, 'satorix/CI/shared/buildpack_manager'
|
22
|
+
module BuildpackManager
|
23
|
+
autoload :Buildpack, 'satorix/CI/shared/buildpack_manager/buildpack'
|
24
|
+
end
|
25
|
+
autoload :YarnManager, 'satorix/CI/shared/yarn_manager'
|
26
|
+
|
27
|
+
module Ruby
|
28
|
+
autoload :GemManager, 'satorix/CI/shared/ruby/gem_manager'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module Test
|
33
|
+
module Python
|
34
|
+
autoload :DjangoTest, 'satorix/CI/test/python/django_test'
|
35
|
+
autoload :Safety, 'satorix/CI/test/python/safety'
|
36
|
+
end
|
37
|
+
|
38
|
+
module Ruby
|
39
|
+
autoload :Brakeman, 'satorix/CI/test/ruby/brakeman'
|
40
|
+
autoload :BundlerAudit, 'satorix/CI/test/ruby/bundler_audit'
|
41
|
+
autoload :Cucumber, 'satorix/CI/test/ruby/cucumber'
|
42
|
+
autoload :RailsTest, 'satorix/CI/test/ruby/rails_test'
|
43
|
+
autoload :Rspec, 'satorix/CI/test/ruby/rspec'
|
44
|
+
autoload :Rubocop, 'satorix/CI/test/ruby/rubocop'
|
45
|
+
end
|
46
|
+
|
47
|
+
module Shared
|
48
|
+
autoload :Database, 'satorix/CI/test/shared/database'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
include Satorix::Shared::Console
|
56
|
+
|
57
|
+
|
58
|
+
extend self
|
59
|
+
|
60
|
+
|
61
|
+
def go
|
62
|
+
airbrake_start
|
63
|
+
prepare_app_environment
|
64
|
+
|
65
|
+
begin
|
66
|
+
log_header "Executing #{ ci_job_name } script for #{ ci_commit_ref_name }..."
|
67
|
+
|
68
|
+
execute_as_user 'satorix' do
|
69
|
+
Dir.chdir(build_dir)
|
70
|
+
unless skip_buildpack?
|
71
|
+
Satorix::CI::Shared::BuildpackManager.go
|
72
|
+
Dir.chdir(app_dir)
|
73
|
+
end
|
74
|
+
Satorix::CI::Shared::Ruby::GemManager.go if ruby_gem?
|
75
|
+
Satorix::CI::Shared::YarnManager.go if yarn?
|
76
|
+
|
77
|
+
job_class.go
|
78
|
+
end
|
79
|
+
|
80
|
+
log_header "\nDone executing #{ ci_job_name } script for #{ ci_commit_ref_name }.\n"
|
81
|
+
rescue Exception => e
|
82
|
+
# TODO: add link to issue tracker
|
83
|
+
log 'If you feel this failure was in error, please check the issue tracker for more information.'
|
84
|
+
Airbrake.notify(e) if airbrake_configured?
|
85
|
+
raise e
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def add_user(username)
|
91
|
+
unless user_exists?(username)
|
92
|
+
run_command ['useradd', '--user-group', '--comment', "'#{ username } user'", '--shell', '/bin/bash', '--home', app_dir, username], quiet: true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def user_exists?(username)
|
98
|
+
Etc.getpwnam(username)
|
99
|
+
rescue
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def airbrake_configured?
|
104
|
+
[airbrake_project_id, airbrake_project_key].all? { |var| !var.empty? }
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def airbrake_project_id
|
109
|
+
ENV['SATORIX_CI_AIRBRAKE_PROJECT_ID'].to_s
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def airbrake_project_key
|
114
|
+
ENV['SATORIX_CI_AIRBRAKE_PROJECT_KEY'].to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def airbrake_start
|
119
|
+
return unless airbrake_configured?
|
120
|
+
|
121
|
+
Airbrake.configure do |c|
|
122
|
+
c.project_id = airbrake_project_id
|
123
|
+
c.project_key = airbrake_project_key
|
124
|
+
end
|
125
|
+
|
126
|
+
at_exit do
|
127
|
+
Airbrake.notify_sync($ERROR_INFO) if $ERROR_INFO # https://github.com/airbrake/airbrake-ruby#reporting-critical-exceptions
|
128
|
+
Airbrake.close # https://github.com/airbrake/airbrake-ruby#airbrakeclose
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def custom_loader_relative_path
|
134
|
+
File.join 'satorix', 'custom.rb'
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def custom_loader_full_path
|
139
|
+
File.join build_dir, custom_loader_relative_path
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def load_custom
|
144
|
+
log_bench "Looking for custom job definitions in #{ custom_loader_relative_path }....." do
|
145
|
+
if File.exist? custom_loader_full_path
|
146
|
+
log "Loading custom job definitions from #{ custom_loader_relative_path }."
|
147
|
+
require custom_loader_full_path
|
148
|
+
Satorix::Custom.available_jobs
|
149
|
+
else
|
150
|
+
# TODO : create this documentation and link to it in the message below
|
151
|
+
log 'No custom jobs found.'
|
152
|
+
log "You can define custom jobs by adding a #{ custom_loader_relative_path } file to your app root."
|
153
|
+
log 'For more information, please refer to https://www.satorix.com/docs/articles/custom_satorix_jobs.'
|
154
|
+
{}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def default_jobs
|
161
|
+
{ deploy: { deploy_with_flynn: Satorix::CI::Deploy::Flynn },
|
162
|
+
test: { brakeman: Satorix::CI::Test::Ruby::Brakeman,
|
163
|
+
bundler_audit: Satorix::CI::Test::Ruby::BundlerAudit,
|
164
|
+
cucumber: Satorix::CI::Test::Ruby::Cucumber,
|
165
|
+
django_test: Satorix::CI::Test::Python::DjangoTest,
|
166
|
+
rails_test: Satorix::CI::Test::Ruby::RailsTest,
|
167
|
+
rspec: Satorix::CI::Test::Ruby::Rspec,
|
168
|
+
rubocop: Satorix::CI::Test::Ruby::Rubocop,
|
169
|
+
safety: Satorix::CI::Test::Python::Safety } }
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def available_jobs
|
174
|
+
@_available_jobs ||= load_custom.tap do |jobs|
|
175
|
+
default_jobs.each do |stage, job_definitions|
|
176
|
+
jobs[stage] ||= {}
|
177
|
+
job_definitions.each { |job, target| jobs[stage][job] = target }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def ci_job_name
|
184
|
+
ENV['CI_JOB_NAME']
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def ci_job_stage
|
189
|
+
ENV['CI_JOB_STAGE']
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def ci_commit_ref_name
|
194
|
+
ENV['CI_COMMIT_REF_NAME']
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
def ci_commit_sha
|
199
|
+
ENV['CI_COMMIT_SHA']
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
def current_branch
|
204
|
+
@_current_branch ||= ci_commit_ref_name.to_s.upcase
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def job_class
|
209
|
+
available_jobs[ci_job_stage.to_sym] && available_jobs[ci_job_stage.to_sym][ci_job_name.to_sym] || begin
|
210
|
+
log_error_and_abort "The #{ ci_job_name } job does not exist for the #{ ci_job_stage } stage!"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
# https://stackoverflow.com/questions/4548151/run-ruby-block-as-specific-os-user/
|
216
|
+
def execute_as_user(user, &block)
|
217
|
+
u = (user.is_a? Integer) ? Etc.getpwuid(user) : Etc.getpwnam(user)
|
218
|
+
|
219
|
+
ENV['HOME'] = Satorix.app_dir
|
220
|
+
|
221
|
+
reader, writer = IO.pipe
|
222
|
+
|
223
|
+
Process.fork do
|
224
|
+
# the child process won't need to read from the pipe
|
225
|
+
reader.close
|
226
|
+
|
227
|
+
# use primary group ID of target user
|
228
|
+
# This needs to be done first as we won't have
|
229
|
+
# permission to change our group after changing EUID
|
230
|
+
Process.gid = Process.egid = u.gid
|
231
|
+
|
232
|
+
# set real and effective UIDs to target user
|
233
|
+
Process.uid = Process.euid = u.uid
|
234
|
+
|
235
|
+
# get the result and write it to the IPC pipe
|
236
|
+
result = block.call(user)
|
237
|
+
Marshal.dump(result, writer)
|
238
|
+
writer.close
|
239
|
+
|
240
|
+
# prevent shutdown hooks from running
|
241
|
+
Process.exit!(true)
|
242
|
+
end
|
243
|
+
|
244
|
+
# back to reality... we won't be writing anything
|
245
|
+
writer.close
|
246
|
+
|
247
|
+
# block until there's data to read
|
248
|
+
result = Marshal.load(reader)
|
249
|
+
|
250
|
+
# done with that!
|
251
|
+
reader.close
|
252
|
+
|
253
|
+
# return block result
|
254
|
+
result
|
255
|
+
rescue EOFError
|
256
|
+
log_error 'This job has failed.'
|
257
|
+
abort
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
def paths
|
262
|
+
ci_project_dir = ENV['CI_PROJECT_DIR'].to_s
|
263
|
+
{ buildpacks: '/tmp/buildpacks',
|
264
|
+
app_dir: '/tmp/app',
|
265
|
+
build_dir: ci_project_dir,
|
266
|
+
cache: File.join(ci_project_dir, 'tmp', 'satorix', 'cache'),
|
267
|
+
env: '/tmp/env' }
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
def app_dir
|
272
|
+
paths[:app_dir]
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
def bin_dir
|
277
|
+
File.join app_dir, 'bin'
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
def build_dir
|
282
|
+
paths[:build_dir]
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
def prepare_app_environment
|
287
|
+
log_bench 'Preparing app environment...' do
|
288
|
+
add_user('satorix')
|
289
|
+
setup_directories
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
def project_name
|
295
|
+
ENV['CI_PROJECT_NAME']
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
def setup_directories
|
300
|
+
paths.values.each { |path| FileUtils.mkdir_p path }
|
301
|
+
paths.values.each { |path| FileUtils.chown_R 'satorix', 'satorix', path }
|
302
|
+
FileUtils.ln_s app_dir, '/app'
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
def skip_buildpack?
|
307
|
+
job_class.respond_to?(:skip_buildpack) && job_class.skip_buildpack
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
def yarn?
|
312
|
+
File.exist? yarn_lock_file
|
313
|
+
end
|
314
|
+
|
315
|
+
|
316
|
+
def yarn_lock_file
|
317
|
+
File.join(app_dir, 'yarn.lock')
|
318
|
+
end
|
319
|
+
|
320
|
+
|
321
|
+
def rails_app?
|
322
|
+
# This used to follow the Heroku buildpack, with:
|
323
|
+
# File.exist?(File.join(build_dir, 'Gemfile'))
|
324
|
+
# It has been adjusted to accommodate non-rails apps that use ruby gems.
|
325
|
+
# The config/application.rb file seems to be present in all rails applications.
|
326
|
+
# It may be useful to add additional checks, for increased precision, in the future.
|
327
|
+
File.exist?(File.join(build_dir, 'config', 'application.rb'))
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
def django_app?
|
332
|
+
# Satorix requires django apps be in a public directory.
|
333
|
+
# The manage.py file seems to be present in all django applications.
|
334
|
+
# It may be useful to add additional checks, for increased precision, in the future.
|
335
|
+
File.exist?(File.join(build_dir, 'public', 'manage.py'))
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
def ruby_gem?
|
340
|
+
# Checks for the presence of a *.gemspec file in the project root.
|
341
|
+
# It may be useful to add additional checks, for increased precision, in the future.
|
342
|
+
Dir[File.join(build_dir, '*.gemspec')].any?
|
343
|
+
end
|
344
|
+
|
345
|
+
|
5
346
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Satorix
|
2
|
+
module CI
|
3
|
+
module Deploy
|
4
|
+
module IeGemServer
|
5
|
+
|
6
|
+
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
|
10
|
+
include Satorix::Shared::Console
|
11
|
+
|
12
|
+
|
13
|
+
extend self
|
14
|
+
|
15
|
+
|
16
|
+
def go
|
17
|
+
log_bench('Installing the geminabox gem...') { install_geminabox_gem }
|
18
|
+
log_bench('Preparing gem build directory...') { prepare_gem_build_directory }
|
19
|
+
log_bench('Building gem...') { build_gem }
|
20
|
+
built_gems.each { |gem| log_bench("Publishing #{ File.basename gem }...") { publish_gem gem } }
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def build_gem
|
25
|
+
run_command 'rake build'
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def built_gems
|
30
|
+
Dir.glob(File.join(gem_build_directory, '*.gem')).select { |e| File.file? e }
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def gem_build_directory
|
35
|
+
File.join Satorix.app_dir, 'pkg'
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def ie_gem_server_host
|
40
|
+
"https://#{ ie_gem_server_user_name }:#{ ie_gem_server_password }@gems.iexposure.com"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def install_geminabox_gem
|
45
|
+
run_command "gem install geminabox --source https://gems.iexposure.com --no-document --bindir #{ Satorix.bin_dir }"
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def prepare_gem_build_directory
|
50
|
+
run_command "rm -rf #{ gem_build_directory }", quiet: true
|
51
|
+
FileUtils.mkdir_p gem_build_directory
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def publish_gem(gem)
|
56
|
+
run_command "gem inabox #{ gem } --host #{ ie_gem_server_host }",
|
57
|
+
filtered_text: [ie_gem_server_user_name, ie_gem_server_password]
|
58
|
+
rescue RuntimeError
|
59
|
+
# To prevent the display of an ugly stacktrace.
|
60
|
+
abort "\nGem was not published!"
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
private ########################################################################################################
|
65
|
+
|
66
|
+
|
67
|
+
def ie_gem_server_password
|
68
|
+
ENV['IE_GEM_SERVER_PASSWORD']
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def ie_gem_server_user_name
|
73
|
+
ENV['IE_GEM_SERVER_USER_NAME']
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|