ucb_rails_ci 1.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7fd00d015b13a606625896918ba8ba848a39e7c0
4
+ data.tar.gz: c53a1860d8fda5030635caee14840f8ff6c08cc5
5
+ SHA512:
6
+ metadata.gz: 9b25c28b48232f954e30155acbcf71b76a23a602bde491eda844bb40b5ba055f5dd95870e521cb4a2a9aa651ea93044195992a90df811a80c47b2e1f5274a9fc
7
+ data.tar.gz: bae199cd74d50285bdd896e3bb63574dc8fe1197fd90ac5d3db73f634f0562008402b70d798e011180d81ad38809511b1290ccb4974d978b3092f09bcd430029
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
@@ -0,0 +1,102 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ucb_rails_ci (1.0.0)
5
+ builder
6
+ bundler
7
+ github_api
8
+ rake
9
+ travis
10
+ warbler
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ addressable (2.3.7)
16
+ backports (3.6.4)
17
+ builder (3.2.2)
18
+ coderay (1.1.0)
19
+ descendants_tracker (0.0.4)
20
+ thread_safe (~> 0.3, >= 0.3.1)
21
+ ethon (0.7.3)
22
+ ffi (>= 1.3.0)
23
+ faraday (0.9.1)
24
+ multipart-post (>= 1.2, < 3)
25
+ faraday_middleware (0.9.1)
26
+ faraday (>= 0.7.4, < 0.10)
27
+ ffi (1.9.3)
28
+ gh (0.14.0)
29
+ addressable
30
+ backports
31
+ faraday (~> 0.8)
32
+ multi_json (~> 1.0)
33
+ net-http-persistent (>= 2.7)
34
+ net-http-pipeline
35
+ github_api (0.12.2)
36
+ addressable (~> 2.3)
37
+ descendants_tracker (~> 0.0.4)
38
+ faraday (~> 0.8, < 0.10)
39
+ hashie (>= 3.3)
40
+ multi_json (>= 1.7.5, < 2.0)
41
+ nokogiri (~> 1.6.3)
42
+ oauth2
43
+ hashie (3.3.2)
44
+ highline (1.7.0)
45
+ jruby-jars (1.7.17)
46
+ jruby-rack (1.1.18)
47
+ json (1.8.1)
48
+ jwt (1.2.1)
49
+ launchy (2.4.3)
50
+ addressable (~> 2.3)
51
+ method_source (0.8.2)
52
+ mini_portile (0.6.2)
53
+ multi_json (1.10.1)
54
+ multi_xml (0.5.5)
55
+ multipart-post (2.0.0)
56
+ net-http-persistent (2.9.4)
57
+ net-http-pipeline (1.0.1)
58
+ nokogiri (1.6.6.2)
59
+ mini_portile (~> 0.6.0)
60
+ oauth2 (1.0.0)
61
+ faraday (>= 0.8, < 0.10)
62
+ jwt (~> 1.0)
63
+ multi_json (~> 1.3)
64
+ multi_xml (~> 0.5)
65
+ rack (~> 1.2)
66
+ pry (0.9.12.4)
67
+ coderay (~> 1.0)
68
+ method_source (~> 0.8)
69
+ slop (~> 3.4)
70
+ pusher-client (0.6.0)
71
+ json
72
+ websocket (~> 1.0)
73
+ rack (1.6.0)
74
+ rake (10.4.2)
75
+ rubyzip (1.1.7)
76
+ slop (3.4.7)
77
+ thread_safe (0.3.4)
78
+ travis (1.7.5)
79
+ addressable (~> 2.3)
80
+ backports
81
+ faraday (~> 0.9)
82
+ faraday_middleware (~> 0.9, >= 0.9.1)
83
+ gh (~> 0.13)
84
+ highline (~> 1.6)
85
+ launchy (~> 2.1)
86
+ pry (~> 0.9, < 0.10)
87
+ pusher-client (~> 0.4)
88
+ typhoeus (~> 0.6, >= 0.6.8)
89
+ typhoeus (0.7.1)
90
+ ethon (>= 0.7.1)
91
+ warbler (1.4.5)
92
+ jruby-jars (>= 1.5.6, < 2.0)
93
+ jruby-rack (>= 1.1.1, < 1.3)
94
+ rake (>= 0.9.6)
95
+ rubyzip (>= 0.9, < 1.2)
96
+ websocket (1.0.7)
97
+
98
+ PLATFORMS
99
+ ruby
100
+
101
+ DEPENDENCIES
102
+ ucb_rails_ci!
@@ -0,0 +1,49 @@
1
+ Rakefile
2
+ ========
3
+
4
+ Rake commands for command line deployment. Basic usage:
5
+
6
+ ```
7
+ # SVN/jenkins deploys
8
+
9
+ rake deploy APP=<app name> # defaults to trunk
10
+ rake deploy APP=<app name> BRANCH=<branch name>
11
+ rake deploy APP=<app name> TAG=<tag>
12
+
13
+ # Github/Travis-CI deploys
14
+ rake gdeploy APP=<app name> # defaults to master
15
+ rake gdeploy APP=<app name> BRANCH=<branch name>
16
+ rake gdeploy APP=<app name> TAG=<tag>
17
+
18
+ # Restarting
19
+ rake restart APP=<app name>
20
+ ```
21
+
22
+ ucb\_rails\_ci.rake
23
+ =======
24
+
25
+ Automatically included in any project that wants to be deployed via
26
+ Github/Travis. Basic tasks for prepping and precompiling a rails project.
27
+
28
+ Examples:
29
+
30
+ ```bash
31
+ rake ci:setup # any preliminary setup
32
+ rake ci:war # Build a war file for JRuby deployment
33
+ ```
34
+
35
+ ci.rb
36
+ =====
37
+
38
+ Simple scripts to supplement CI build. This applies to jenkins only -- Travis
39
+ uses a different mechanism.
40
+
41
+ Usage:
42
+
43
+ ```
44
+ ci.rb APP_NAME [--run-specs-false] [--compile-assets-flag]
45
+
46
+ --run-specs-flag (defaults is true)
47
+ --compile-assets-flag (defaults is true)
48
+ ```
49
+
@@ -0,0 +1,370 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+ require 'yaml'
4
+ require 'ostruct'
5
+ require 'github_api'
6
+
7
+ sync = true
8
+
9
+ ################################################################################
10
+ # Initialization
11
+ ################################################################################
12
+ task :default => :deploy
13
+
14
+ desc "deploy the application"
15
+
16
+ task :do_deploy => [:symlink_configs, :disable_web, :tomcat_stop,
17
+ :deploy_war, :load_crontab, :tomcat_start, :enable_web]
18
+
19
+ task :deploy => [:parse_args, :svn_export, :do_deploy]
20
+ task :gdeploy => [:parse_args, :github_release_export, :do_deploy]
21
+
22
+ desc "Full restart without deploy"
23
+ task :restart => [:disable_web, :tomcat_stop, :tomcat_start, :enable_web]
24
+
25
+ task :parse_args do
26
+ unless ENV['APP']
27
+ $stderr.puts "Usage: rake APP=<app_name>"
28
+ exit(1)
29
+ end
30
+
31
+ base_dir = ENV['DEPLOY_BASE'] || ENV['HOME']
32
+ config_dir = "#{base_dir}/.deploy_config/#{ENV['APP']}"
33
+ work_dir = "#{base_dir}/.deploy_work"
34
+
35
+ $config = OpenStruct.new(YAML.load_file("#{config_dir}/deploy.yml"))
36
+ $config.config_dir = config_dir
37
+ $config.debug = (ENV['DEBUG'] == 'true')
38
+ $config.app_name = ENV['APP']
39
+ $config.app_user = "app_#{ENV['APP']}"
40
+ $config.tag = ENV['TAG']
41
+ $config.branch = ENV['BRANCH'] || $config.tag || 'master'
42
+ $config.war = ENV['WAR'] || ($config.branch == 'master') ? 'trunk.war' : "#{$config.branch}.war"
43
+ $config.release_url = "#{$config.svn_project_url}/#{$config.war}"
44
+ $config.work_dir = "#{work_dir}/#{$config.app_name}"
45
+
46
+ if $config.github_api_token
47
+ $github = Github.new do |c|
48
+ c.oauth_token = $config.github_api_token
49
+ c.user = 'ucb-ist-eas'
50
+ c.repo = $config.app_name
51
+ end
52
+ end
53
+
54
+ if $config.debug
55
+ puts($config.inspect)
56
+ end
57
+ end
58
+
59
+
60
+ ################################################################################
61
+ # Deploy War
62
+ ################################################################################
63
+ desc "Deploy the war file to tomcat webapps dir"
64
+ task :deploy_war => [:parse_args, :ensure_work_directory] do
65
+ progress "Deploying war to tomcat" do
66
+
67
+ webapps = $config.webapps_dir
68
+ app = $config.app_name
69
+ root = "ROOT"
70
+ app_user = $config.app_user
71
+
72
+ FileUtils.cd($config.work_dir) do
73
+ run_cmd "sudo -u #{app_user} rm -rf #{webapps}/#{root}/*"
74
+ run_cmd "rm -rf #{webapps}/#{root}"
75
+ run_cmd "cp -R . #{webapps}/#{app}"
76
+ run_cmd "mv #{webapps}/#{app} #{webapps}/#{root}"
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+
83
+ ################################################################################
84
+ # Start/Stop Tomcat
85
+ ################################################################################
86
+ desc "stop tomcat application server"
87
+ task :tomcat_stop => [:parse_args] do
88
+ unless $dry_run
89
+ status_cmd = "sudo service tomcat6-#{$config.app_name} status"
90
+ stop_cmd = "sudo service tomcat6-#{$config.app_name} stop"
91
+
92
+ output = `#{status_cmd}`.chomp
93
+ if $?.exitstatus == 3
94
+ puts(output)
95
+ else
96
+ output = `#{stop_cmd}`.chomp
97
+ if $?.exitstatus == 0
98
+ puts(output)
99
+ else
100
+ $stderr.puts(output)
101
+ exit(1)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ desc "start tomcat application server"
108
+ task :tomcat_start => [:parse_args] do
109
+ unless $dry_run
110
+ output = `sudo service tomcat6-#{$config.app_name} start`.chomp
111
+ puts(output)
112
+ end
113
+ end
114
+
115
+
116
+ ################################################################################
117
+ # Checkout Source Code
118
+ ################################################################################
119
+ def copy_yml_configs
120
+ Dir["#{$config.config_dir}/*.yml"].each do |file_path|
121
+ file_name = File.basename(file_path)
122
+ unless file_name == "deploy.yml"
123
+ `cp #{file_path} ./WEB-INF/config/#{file_name}`
124
+ end
125
+ end
126
+ end
127
+
128
+ def configure_web_xml
129
+ web_xml = "WEB-INF/web.xml"
130
+
131
+ lines = File.readlines(web_xml)
132
+ File.open(web_xml, "w") do |f|
133
+ lines.each do |line|
134
+ if line =~ /<param-value>production<\/param-value>/
135
+ f.puts("<param-value>#{$config.rails_env}<\/param-value>")
136
+ elsif line =~ /<param-value>ci<\/param-value>/
137
+ f.puts("<param-value>#{$config.rails_env}<\/param-value>")
138
+ else
139
+ f.puts(line)
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ def symlink_tmp_to_temp
146
+ `ln -s /var/lib/tomcat6-#{$config.app_name}/temp WEB-INF/tmp`
147
+ end
148
+
149
+ def extract_war
150
+ `jar -xvf *.war`
151
+ `rm -rf *.war`
152
+ end
153
+
154
+ desc "Embed and link configs"
155
+ task :symlink_configs => [:ensure_work_directory] do
156
+ progress "Embedding configs and linking directories" do
157
+
158
+ FileUtils.cd($config.work_dir) do
159
+ extract_war
160
+ copy_yml_configs
161
+ configure_web_xml
162
+ symlink_tmp_to_temp
163
+ end
164
+
165
+ end
166
+ end
167
+
168
+ desc "download project from svn"
169
+ task :svn_export => [:parse_args, :svn_war_exists, :ensure_work_directory] do
170
+ progress "Exporting code from SVN (#{$config.release_url})" do
171
+
172
+ app_dir = $config.app_name
173
+
174
+ if File.exists?(app_dir)
175
+ puts "App dir #{app_dir} already exists. Incomplete deploy?"
176
+ exit(1)
177
+ end
178
+
179
+ run_cmd("svn export #{$config.release_url}")
180
+
181
+ end
182
+ end
183
+
184
+ desc "download project from github"
185
+ task :github_release_export => [:parse_args, :ensure_work_directory] do
186
+ if $config.tag.nil?
187
+
188
+ # Look at the list of releases and pick with the highest number matching the branch
189
+ releases = $github.repos.releases.list('ucb-ist-eas', $config.app_name)
190
+ branch_tags = releases.map(&:tag_name).select { |t| t =~ /\A#{$config.branch}-\d+\Z/ }
191
+ branch_tags.sort_by! { |t| t =~ /-(\d+)\Z/; $1.to_i }
192
+
193
+ if branch_tags.empty?
194
+ puts "No tags found for BRANCH=#{$config.branch}. Use TAG variable to specify a particular tag."
195
+ exit 1
196
+ else
197
+ $config.tag = branch_tags.last
198
+ end
199
+ end
200
+
201
+ progress "Exporting war file from GitHub for tag #{$config.tag}" do
202
+
203
+ app_dir = $config.app_name
204
+ orig_war = "#{$config.app_name}.war"
205
+
206
+ cmd = %{github-release download
207
+ -s #{$config.github_api_token}
208
+ -u ucb-ist-eas
209
+ -r #{$config.app_name}
210
+ -t #{$config.tag}
211
+ -n #{orig_war}}
212
+
213
+ cmd.gsub!(/[[:space:]]+/, " ")
214
+
215
+ run_cmd cmd, fail_msg: "Could not download the release. Is the tag right? Is github-release installed?"
216
+
217
+ FileUtils.mv(orig_war, $config.war)
218
+
219
+ end
220
+ end
221
+
222
+ task :svn_war_exists => [:svn_project_exists] do
223
+ run_cmd "svn ls #{$config.release_url} 2>&1",
224
+ fail_msg: "SVN URL not found: #{$config.release_url}"
225
+ end
226
+
227
+ task :svn_project_exists => [:parse_args] do
228
+ run_cmd "svn ls #{$config.svn_project_url} 2>&1",
229
+ fail_msg: "SVN URL not found: #{$config.svn_project_url}"
230
+ end
231
+
232
+ desc "Create work directory if necessary"
233
+ task :ensure_work_directory => [:parse_args] do
234
+ run_cmd "mkdir -p #{$config.work_dir}",
235
+ fail_msg: "Cannot create work directory: #{$config.work_dir}"
236
+
237
+ FileUtils.cd($config.work_dir)
238
+ end
239
+
240
+ ################################################################################
241
+ # Enable/Disable web
242
+ ################################################################################
243
+ desc "remove maintenance file and enable web access to app"
244
+ task :enable_web => [:parse_args] do
245
+ progress "Enabling tomcat proxy, remove maint message" do
246
+
247
+ file = $config.work_dir
248
+ FileUtils.rm_rf(file) if File.exists?(file)
249
+ FileUtils.rm_rf($config.maint_file) if File.exists?($config.maint_file)
250
+
251
+ end
252
+ end
253
+
254
+ desc "display maintenance file and disable web access to app"
255
+ task :disable_web => [:parse_args] do
256
+ progress "Disabling tomcat proxy, display maint message" do
257
+
258
+ maint_start = Time.now.strftime("%m-%d-%Y %H:%M:%S")
259
+ template = ERB.new($template)
260
+ File.open($config.maint_file, "w") do |f|
261
+ f.puts template.result(binding)
262
+ end
263
+
264
+ raise "file is missing" unless File.exists?($config.maint_file)
265
+ end
266
+ end
267
+
268
+ $template = %q{
269
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
270
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
271
+
272
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
273
+
274
+ <head>
275
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
276
+ <title>Site Maintenance</title>
277
+ <style type="text/css">
278
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
279
+ div.dialog {
280
+ width: 35em;
281
+ padding: 0 3em;
282
+ margin: 3em auto 0 auto;
283
+ border: 1px solid #ccc;
284
+ border-right-color: #999;
285
+ border-bottom-color: #999;
286
+ }
287
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
288
+ </style>
289
+ </head>
290
+
291
+ <body>
292
+ <div class="dialog">
293
+ <h1>Site Maintenance</h1>
294
+ <p>
295
+ The site is down for maintenance as of: <strong><%= maint_start %></strong> <br/>
296
+ it should be available shortly.
297
+ </p>
298
+ </div>
299
+ </body>
300
+ </html>
301
+ }
302
+
303
+
304
+ ################################################################################
305
+ # Cron Jobs
306
+ ################################################################################
307
+
308
+ desc "load cron jobs from crontab"
309
+ task :load_crontab do
310
+ crontab_file = "#{$config.app_name}/config/crontab.txt"
311
+ if $config.rails_env == "production" && File.exists?(crontab_file)
312
+ `/usr/bin/crontab -u app_#{$config.app_name} #{crontab_file}`
313
+ end
314
+ end
315
+
316
+ ################################################################################
317
+ # Misc
318
+ ################################################################################
319
+
320
+ desc "curls the ~/app_monitor url and performs a basic health check"
321
+ task :health_check => [:parse_args] do
322
+ progress "Performing health check" do
323
+
324
+ result = `curl -m 10 -s localhost:#{$config.tomcat_port}/app_monitor`.chomp
325
+
326
+ raise result unless result == "OK"
327
+
328
+ end
329
+ end
330
+
331
+ def run_cmd(cmd_str, opts = {})
332
+ IO.popen(cmd_str) { |f|0
333
+ until f.eof?
334
+ str = f.gets
335
+ puts(str) if $debug_mode
336
+ end
337
+ }
338
+
339
+ if opts[:fail_msg] && $?.exitstatus.to_i != 0
340
+ raise(opts[:fail_msg])
341
+ end
342
+ end
343
+
344
+ def progress(msg)
345
+ print "%-59s" % [msg + ":"]
346
+ yield
347
+ puts "[#{" OK ".green}]"
348
+ rescue
349
+ puts "[#{"FAILED".red}]"
350
+ exit(1)
351
+ end
352
+
353
+ class String
354
+ # colorization
355
+ def colorize(color_code)
356
+ "\e[#{color_code}m#{self}\e[0m"
357
+ end
358
+
359
+ def red
360
+ colorize(31)
361
+ end
362
+
363
+ def green
364
+ colorize(32)
365
+ end
366
+ end
367
+
368
+
369
+
370
+
data/ci.rb ADDED
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'fileutils'
5
+ require 'warbler'
6
+
7
+ class CiBuild
8
+ attr_accessor :app_name, :workspace_path, :jenkins_home, :run_specs_flag, :compile_assets_flag, :build_number
9
+
10
+ def initialize(args={})
11
+ @app_name = args.fetch(:app_name)
12
+ @workspace_path = args.fetch(:workspace_path)
13
+ @jenkins_home = args.fetch(:jenkins_home)
14
+
15
+ @run_specs_flag = args.fetch(:run_specs_flag, true)
16
+ @compile_assets_flag = args.fetch(:compile_assets_flag, true)
17
+
18
+ @build_number = args.fetch(:build_number, nil)
19
+ end
20
+
21
+ def run
22
+ Dir.chdir(workspace_path) do
23
+ clean_workspace
24
+ run_bundle_install
25
+ setup_dot_yml_files
26
+
27
+ if run_specs_flag
28
+ setup_db
29
+ run_rspec_suite
30
+ end
31
+
32
+ compile_assets if compile_assets_flag
33
+
34
+ record_build_number
35
+ archive_war_file
36
+ end
37
+ end
38
+
39
+
40
+ private
41
+
42
+ def clean_workspace
43
+ FileUtils.rm_rf("#{workspace_path}/.rvmrc") if File.exists?("#{workspace_path}/.rvmrc")
44
+ end
45
+
46
+ def job_name
47
+ workspace_path =~ /jobs\/(.+)\/workspace$/
48
+ $1
49
+ end
50
+
51
+ def war_archive_url
52
+ "#{war_archive_root}/#{app_name}/"
53
+ end
54
+
55
+ def war_archive_root
56
+ "svn+ssh://svn@code.berkeley.edu/eas-rails/wars"
57
+ end
58
+
59
+ def war_name
60
+ if git_repo?
61
+ git_war_name
62
+ elsif svn_repo?
63
+ svn_war_name
64
+ else
65
+ raise "Unrecognized SCM"
66
+ end
67
+ end
68
+
69
+ def git_repo?
70
+ Dir["#{workspace_path}/.*"].any? { |f| File.basename(f) == ".git" }
71
+ end
72
+
73
+ def svn_repo?
74
+ Dir["#{workspace_path}/.*"].any? { |f| File.basename(f) == ".svn" }
75
+ end
76
+
77
+ def svn_war_name
78
+ ENV['SVN_URL'].split("/").pop
79
+ end
80
+
81
+ def git_war_name
82
+ branch = ENV['GIT_BRANCH']
83
+ (branch == "master") ? "trunk" : branch
84
+ end
85
+
86
+ def default_env_vars
87
+ ["WEBKIT=false", "QMAKE=/usr/bin/qmake-qt47"]
88
+ end
89
+
90
+ def run_cmd(cmd, env_vars = [])
91
+ # TODO: Enable after qt47 is installed
92
+ # env = (default_env_vars + env_vars).join(" ")
93
+
94
+ env = env_vars.join(" ")
95
+ full_cmd = "#{env} #{cmd}"
96
+ $stdout.puts("#{full_cmd} ... ")
97
+
98
+ output = `#{full_cmd} 2>&1`
99
+ $stdout.puts(output)
100
+
101
+ if $?.success?
102
+ $stdout.puts("[OK]")
103
+ else
104
+ $stdout.puts("[FAILED]")
105
+ $stdout.puts($?.exitstatus)
106
+ exit($?.exitstatus)
107
+ end
108
+
109
+ output
110
+ end
111
+
112
+ def run_bundle_install
113
+ run_cmd("bundle install")
114
+ end
115
+
116
+ def setup_dot_yml_files
117
+ create_db_yml
118
+ Dir.glob("config/*.yml.example").each do |file_path|
119
+ file_name = File.basename(file_path).split(".")[0..1].join(".")
120
+ next if file_name == "database.yml"
121
+ dir_name = File.dirname(file_path)
122
+ FileUtils.cp(file_path, File.join(dir_name, file_name))
123
+ end
124
+ end
125
+
126
+ def setup_db
127
+ run_cmd("bundle exec rake db:migrate", ["RAILS_ENV=test"])
128
+ end
129
+
130
+ def create_db_yml
131
+ db_yml_contents = <<-DB
132
+ test: &test
133
+ adapter: sqlite3
134
+ database: #{app_name}
135
+ username: jenkins
136
+ password: jenkins
137
+ host: localhost
138
+
139
+ legacy_test: &legacy_test
140
+ adapter: sqlite3
141
+ database: db/legacy_test.sqlite3
142
+
143
+ ci:
144
+ <<: *test
145
+
146
+ legacy_ci:
147
+ <<: *legacy_test
148
+
149
+ DB
150
+ File.open("config/database.yml", "w") do |f|
151
+ f.write(db_yml_contents)
152
+ end
153
+ end
154
+
155
+ def run_rspec_suite
156
+ run_cmd("bundle exec rspec spec --tag ~js --format RspecJunitFormatter --out results.xml", ["RAILS_ENV=test"])
157
+ end
158
+
159
+ def compile_assets
160
+ cache_compiled_assets
161
+ run_cmd("bundle exec rake assets:precompile", ["RAILS_ENV=ci"])
162
+ end
163
+
164
+ def cache_compiled_assets
165
+ link = "#{jenkins_home}/.jenkins/jobs/#{job_name}/workspace/tmp"
166
+ target = "#{jenkins_home}/tmp/#{job_name}"
167
+
168
+ FileUtils.mkdir(target) unless File.exists?(target)
169
+ FileUtils.rm_rf(link) if File.exists?(link)
170
+ File.symlink(target, link) unless File.symlink?(link)
171
+ end
172
+
173
+ def record_build_number
174
+ if build_number
175
+ path = File.join(workspace_path, 'BUILD')
176
+ File.open(path, "w") do |f|
177
+ puts "Storing build number #{build_number} into #{path}"
178
+ f.write(build_number)
179
+ end
180
+ end
181
+ end
182
+
183
+ def archive_war_file
184
+ create_war
185
+ remove_war if war_checked_in?
186
+ commit_war
187
+ end
188
+
189
+ def create_war
190
+ RakeFileUtils.verbose_flag = true
191
+ Warbler::Task.new('trunk')
192
+
193
+ $stdout.print("Building war file: ")
194
+ Rake::Task['trunk'].invoke()
195
+
196
+ FileUtils.mv("workspace.war", "#{war_name}.war")
197
+ end
198
+
199
+ def war_checked_in?
200
+ wars = run_cmd("svn list #{war_archive_url}").split("\n")
201
+ wars.include?("#{war_name}.war")
202
+ end
203
+
204
+ def remove_war
205
+ run_cmd("svn rm #{war_archive_url}/#{war_name}.war -m 'removing war #{war_name}.war' &")
206
+ run_cmd("sleep 2s")
207
+ end
208
+
209
+ def commit_war
210
+ run_cmd("svn import #{war_name}.war #{war_archive_url}/#{war_name}.war -m 'committing war #{war_name}.war'")
211
+ end
212
+ end
213
+
214
+
215
+ if __FILE__ == $PROGRAM_NAME
216
+ def extract_optional_args(optional_args)
217
+ optional_args.inject({}) do |hash, arg|
218
+ key, val = arg.split("=")
219
+ if val == "false"
220
+ val = false
221
+ elsif val.nil?
222
+ val = true
223
+ else
224
+ val = val
225
+ end
226
+
227
+ key = key[2..-1].gsub("-", "_").to_sym
228
+ hash[key] = val
229
+ hash
230
+ end
231
+ end
232
+
233
+ args = {
234
+ :app_name => ARGV[0],
235
+ :workspace_path => ENV['WORKSPACE'],
236
+ :jenkins_home => ENV['HOME'],
237
+ }
238
+
239
+ optional_args = extract_optional_args(ARGV[1..-1])
240
+
241
+
242
+ p args.merge(optional_args)
243
+ CiBuild.new(args.merge(optional_args)).run
244
+ end
@@ -0,0 +1,3 @@
1
+ module UcbRailsCi
2
+ require 'ucb_rails_ci/railtie' if defined?(Rails)
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'ucb_rails_ci'
2
+ require 'rails'
3
+ module UcbRailsCi
4
+ class Railtie < Rails::Railtie
5
+ railtie_name :ucb_rails_ci
6
+
7
+ rake_tasks do
8
+ load File.join(File.dirname(__FILE__), '..', '..', 'tasks', 'ucb_rails_ci.rake')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,161 @@
1
+
2
+ require 'warbler'
3
+
4
+ namespace :ci do
5
+
6
+ desc "Run CI setup tasks"
7
+ task :setup => %w[ config:copy_defaults ]
8
+
9
+ desc "Run Rspec for CI"
10
+ task :spec => %w[ setup spec ]
11
+
12
+ task :war => %w[ war:archive ]
13
+
14
+ namespace :war do
15
+
16
+ task :archive do
17
+ if war_branch?
18
+ Rake::Task['ci:war:prepare'].invoke
19
+ Rake::Task['ci:war:create'].invoke
20
+ else
21
+ puts "Not creating a war because this is a personal branch (has a / in it)"
22
+ exit 1
23
+ end
24
+ end
25
+
26
+ task :create do
27
+ create_war
28
+ end
29
+
30
+ task :prepare => %w[ assets:precompile ] do
31
+ record_build_number
32
+ end
33
+ end
34
+
35
+ def record_build_number
36
+ if build_number = ENV['TRAVIS_BUILD_NUMBER']
37
+ path = File.join(workspace_path, 'BUILD')
38
+ File.open(path, "w") do |f|
39
+ puts "Storing build number #{build_number} into #{path}"
40
+ f.write(build_number)
41
+ end
42
+ end
43
+ end
44
+
45
+ def create_war
46
+ RakeFileUtils.verbose_flag = true
47
+ Warbler::Task.new(branch)
48
+
49
+ $stdout.print("Building war file: ")
50
+ Rake::Task[branch].invoke()
51
+ end
52
+
53
+ def branch
54
+ branch = ENV['TRAVIS_BRANCH']
55
+
56
+ unless branch
57
+ branch = `git rev-parse --abbrev-ref HEAD`.strip
58
+ end
59
+
60
+ branch = 'trunk' if branch == 'master'
61
+ branch
62
+ end
63
+
64
+ def war_name
65
+ branch
66
+ end
67
+
68
+ def war_branch?
69
+ !branch.include?('/')
70
+ end
71
+
72
+ def workspace_path
73
+ ENV['TRAVIS_BUILD_DIR'] || '.'
74
+ end
75
+
76
+ def app_name
77
+ if ENV['TRAVIS_REPO_SLUG']
78
+ ENV['TRAVIS_REPO_SLUG'].split('/').last
79
+ else
80
+ url = `git config --get remote.origin.url`
81
+ if url =~ /ucb-ist-eas\/(.*)\.git/
82
+ $1
83
+ else
84
+ raise "cannot determine branch"
85
+ end
86
+ end
87
+ end
88
+
89
+ def run_cmd(cmd, env_vars = [])
90
+ # TODO: Enable after qt47 is installed
91
+ # env = (default_env_vars + env_vars).join(" ")
92
+
93
+ env = env_vars.join(" ")
94
+ full_cmd = "#{env} #{cmd}"
95
+ $stdout.puts("#{full_cmd} ... ")
96
+
97
+ output = `#{full_cmd} 2>&1`
98
+ $stdout.puts(output)
99
+
100
+ if $?.success?
101
+ $stdout.puts("[OK]")
102
+ else
103
+ $stdout.puts("[FAILED]")
104
+ $stdout.puts($?.exitstatus)
105
+ exit($?.exitstatus)
106
+ end
107
+
108
+ output
109
+ end
110
+
111
+ end
112
+
113
+ namespace :config do
114
+
115
+ desc "copy .example files into their real locations"
116
+ task :copy_defaults do
117
+ Dir.glob("config/*.yml.example").each do |file_path|
118
+ file_name = File.basename(file_path).split(".")[0..1].join(".")
119
+ dir_name = File.dirname(file_path)
120
+ FileUtils.cp(file_path, File.join(dir_name, file_name))
121
+ end
122
+ end
123
+
124
+ desc "Generate .travis.yml file for use with Travis-CI"
125
+ task :generate_travis_yml do
126
+
127
+ require 'erb'
128
+
129
+ `travis help`
130
+ if $? == 1
131
+ puts "This command requires the travis CLI. Install it with 'gem install travis'"
132
+ exit 1
133
+ end
134
+
135
+ filename = File.join(File.dirname(__FILE__), "..", "templates", "travis.yml.erb")
136
+ input = File.read(filename)
137
+ erb = ERB.new(input)
138
+
139
+ print "App Name: "
140
+ app_name = $stdin.gets.strip
141
+
142
+ print "Github API Key: "
143
+ api_key = $stdin.gets.strip
144
+
145
+ print "Slack Integration Key (optional): "
146
+ slack_key = $stdin.gets.strip
147
+ slack_key = nil if slack_key == ''
148
+
149
+ text = erb.result(binding)
150
+ File.write(Rails.root.join('.travis.yml'), text)
151
+
152
+ `travis encrypt --add deploy.api-key #{api_key}`
153
+
154
+ if slack_key
155
+ `travis encrypt --add notifications.slack #{slack_key}`
156
+ end
157
+
158
+ `travis lint`
159
+
160
+ end
161
+ end
@@ -0,0 +1,36 @@
1
+ branches:
2
+ except:
3
+ - /^.*-\d+$/ # exclude build tags
4
+ - /^.*\/.*+$/ # exclude private branches (with slash)
5
+ language: ruby
6
+ env: SQLITE=true
7
+ rvm:
8
+ - jruby-19mode
9
+ install: bundle install --full-index --without='development production' --path vendor/bundle --jobs=3 --retry=3
10
+ script:
11
+ - bundle exec rake ci:setup
12
+ - bundle exec rake db:migrate
13
+ - bundle exec rake db:test:prepare
14
+ - mkdir -p tmp/cache
15
+ - xvfb-run bundle exec rspec
16
+ cache:
17
+ directories:
18
+ - vendor/bundle
19
+ before_deploy:
20
+ - bundle exec rake ci:war RAILS_ENV=ci
21
+ - git tag $TRAVIS_BRANCH-$TRAVIS_BUILD_NUMBER # remove this if you want to tag manually
22
+ deploy:
23
+ provider: releases
24
+ api-key:
25
+ file: <%= app_name %>.war
26
+ skip_cleanup: true
27
+ on:
28
+ #tags: true # enable if you want to only build via manual tags
29
+ all_branches: true
30
+ # addons:
31
+ # code_climate:
32
+ # repo_token: (insert repo token from code climate)
33
+ <% if slack_key %>
34
+ notifications:
35
+ slack:
36
+ <% end %>
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'ucb_rails_ci'
3
+ s.version = '1.0.1'
4
+ s.summary = "CI and deployment integration for UCB Rails apps"
5
+ s.description = "Tools for enabling Rails apps to be built on Travis and deployed from GitHub"
6
+ s.authors = ["Ken Miller"]
7
+ s.email = 'ken@berkeley.edu'
8
+ s.date = Time.now.utc.strftime("%Y-%m-%d")
9
+ s.files = `git ls-files`.split("\n")
10
+ s.homepage = 'https://github.com/ucb-ist-eas/ucb_rails_ci'
11
+ s.bindir = 'bin'
12
+
13
+ s.add_runtime_dependency 'rake'
14
+ s.add_runtime_dependency 'bundler'
15
+ s.add_runtime_dependency 'builder'
16
+ s.add_runtime_dependency 'warbler'
17
+ s.add_runtime_dependency 'github_api'
18
+ s.add_runtime_dependency 'travis'
19
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ucb_rails_ci
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ken Miller
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: builder
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: warbler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: github_api
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: travis
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Tools for enabling Rails apps to be built on Travis and deployed from
98
+ GitHub
99
+ email: ken@berkeley.edu
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - Gemfile
105
+ - Gemfile.lock
106
+ - README.md
107
+ - Rakefile
108
+ - ci.rb
109
+ - lib/ucb_rails_ci.rb
110
+ - lib/ucb_rails_ci/railtie.rb
111
+ - tasks/ucb_rails_ci.rake
112
+ - templates/travis.yml.erb
113
+ - ucb_rails_ci.gemspec
114
+ homepage: https://github.com/ucb-ist-eas/ucb_rails_ci
115
+ licenses: []
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.2.2
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: CI and deployment integration for UCB Rails apps
137
+ test_files: []