Created
December 2, 2011 11:12
-
-
Save infertux/1422833 to your computer and use it in GitHub Desktop.
EC2 Windows CI hackish scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@echo off | |
REM If you edit this file, make sure to run `unix2dos` to fix those fucking CRLF. | |
REM Let's keep this script as dumb as possible and do all the logic with a true language. | |
REM Basically, this should be no more than a sort of remote control. | |
echo Initializing hook... | |
:begin | |
cd C:\Users\Administrator\Desktop | |
echo Waiting for commands... | |
:wait | |
ping -n 2 127.0.0.1 > nul | |
<nul set /p =. | |
if not exist go goto wait | |
set /p project= <go | |
del go | |
echo. | |
echo Received go signal, running tests for project %project%... | |
del done 2>nul | |
echo. > ci.log | |
echo. > exitcodes | |
cd C:\Users\Administrator\Desktop\%project%\spec\support | |
echo Running tests on IE aka I'm Evil... | |
java -jar JsTestDriver.jar --tests all --port 1234 --reset --browser "C:\Program Files\Internet Explorer\iexplore.exe" >> C:\Users\Administrator\Desktop\ci.log | |
echo %errorlevel% >> C:\Users\Administrator\Desktop\exitcodes | |
echo Running tests on Firefox... | |
java -jar JsTestDriver.jar --tests all --port 1234 --reset --browser "C:\Program Files\Mozilla Firefox\firefox.exe" >> C:\Users\Administrator\Desktop\ci.log | |
echo %errorlevel% >> C:\Users\Administrator\Desktop\exitcodes | |
echo Running tests on Safari... | |
java -jar JsTestDriver.jar --tests all --port 1234 --reset --browser "C:\Program Files\Safari\Safari.exe" >> C:\Users\Administrator\Desktop\ci.log | |
echo %errorlevel% >> C:\Users\Administrator\Desktop\exitcodes | |
echo Running tests on Chrome... | |
java -jar JsTestDriver.jar --tests all --port 1234 --reset --browser "C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe" >> C:\Users\Administrator\Desktop\ci.log | |
echo %errorlevel% >> C:\Users\Administrator\Desktop\exitcodes | |
echo. > C:\Users\Administrator\Desktop\done | |
goto begin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
credentials: | |
access_key_id: <%= @credentials['access_key_id'] %> | |
secret_access_key: <%= @credentials['secret_access_key'] %> | |
ci: | |
tests_timeout: 300 # give it 5 min to run tests on all browsers | |
username: Administrator | |
host: x.x.x.x # elastic IP | |
instance_id: i-xxxxxxxx | |
region: xxxxxxxx | |
local_path: /home/cruise/.cruise/projects | |
remote_path: /cygdrive/c/Users/Administrator/Desktop | |
hook_file: /cygdrive/c/Users/Administrator/AppData/Roaming/Microsoft/Windows/Start\\ Menu/Programs/Startup/ci_hook.bat | |
lock_file: /home/cruise/aws/lock | |
ssh_key: /home/cruise/.ssh/id_rsa | |
ssh_port: xx | |
ssh_params: -i /home/cruise/.ssh/id_rsa -p xx -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -q | |
last_build_file: /home/cruise/aws/last_build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'yaml' | |
require 'fileutils' | |
require 'rubygems' | |
require 'aws-sdk' | |
require 'tzinfo' | |
STDOUT.sync = true | |
STDERR.sync = true | |
def get_instance | |
ec2 = AWS::EC2.new | |
ec2 = ec2.regions[CONFIG['ci']['region']] | |
instance = ec2.instances[CONFIG['ci']['instance_id']] | |
unless instance.exists? | |
raise "Cannot find instance #{CONFIG['ci']['instance_id']}, wrong region?" | |
end | |
instance | |
end | |
def wait_for_status(instance, status, delay = 5) | |
print "Waiting for the instance to be #{status.to_s}..." | |
until instance.status == status | |
sleep delay | |
print '.' | |
end | |
puts | |
end | |
def run_or_raise command | |
# rsync fails sometimes because of disk I/O issues on cheap Amazon drives | |
# so we give it 5 times to succeed | |
5.times do | |
output = `#{command}` | |
return output if $? == 0 | |
sleep 30 | |
end | |
FileUtils.rm CONFIG['ci']['lock_file'] | |
raise "Failed 5 times in a row running: '#{command}'" | |
end | |
def launch_instance | |
instance = get_instance | |
if [:pending, :running].include? instance.status | |
raise "Instance already running (or booting)." | |
end | |
puts "Starting instance..." | |
instance.start | |
wait_for_status instance, :running | |
puts "Instance launched" | |
end | |
def shutdown_instance | |
instance = get_instance | |
return unless :running == instance.status | |
instance.stop | |
end | |
def reboot_instance | |
instance = get_instance | |
raise "Instance is not running." unless :running == instance.status | |
puts "Rebooting instance..." | |
# instance.reboot does NOT work! | |
instance.stop | |
wait_for_status instance, :stopped | |
instance.start | |
wait_for_status instance, :running | |
puts "Instance rebooted" | |
end | |
def check_instance | |
instance = get_instance | |
if instance.status != :running and instance.status != :stopped | |
raise "Don't know what to do, exiting." | |
end | |
unless instance.status == :running | |
launch_instance | |
wait_for_status instance, :running | |
end | |
puts "Associating elastic IP..." | |
if instance.elastic_ip.nil? | |
instance.associate_elastic_ip(CONFIG['ci']['host']) | |
sleep 5 | |
end | |
print "Waiting for Windows' post boot..." | |
begin | |
print '.' | |
`nc -z -w 10 #{CONFIG['ci']['host']} #{CONFIG['ci']['ssh_port']}` | |
end while $? != 0 | |
puts | |
end | |
def upload_hook | |
check_instance | |
puts "Uploading hook..." | |
local_hook_file = "#{File.dirname(__FILE__)}/#{File.basename(CONFIG['ci']['hook_file'])}" | |
output = run_or_raise "rsync -azci -e \"ssh #{CONFIG['ci']['ssh_params']}\" #{local_hook_file} \"#{CONFIG['ci']['username']}@#{CONFIG['ci']['host']}:#{CONFIG['ci']['hook_file']}\"" | |
# This would be the proper way to do it, but I can't restart the new hook this way. | |
# | |
# pid = `ssh #{CONFIG['ci']['ssh_params']}" #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']} ps -W | grep cmd.exe | awk '{print $1}'`.split("\n").map(&:chop) | |
# if pid.count > 1 | |
# raise "Oops, didn't get exactly one PID, I don't how to kill the hook." | |
# else | |
# `ssh #{CONFIG['ci']['ssh_params']}" #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']} /bin/kill -f #{pid.first}` | |
# end | |
if output.chop.length > 0 | |
# Hey, it's Windows, born to be rebooted ;) | |
puts "New hook uploaded, rebooting to take effect..." | |
reboot_instance | |
else | |
puts "Remote hook already up-to-date." | |
end | |
end | |
def run_tests project | |
check_instance | |
puts "Syncing repo..." | |
run_or_raise "rsync -azv --delete --timeout=5 -e \"ssh #{CONFIG['ci']['ssh_params']}\" #{CONFIG['ci']['local_path']}/#{project}/work/ #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']}:#{CONFIG['ci']['remote_path']}/#{project}" | |
puts "Sending the green flag to run tests to Batch handler..." | |
run_or_raise "ssh #{CONFIG['ci']['ssh_params']} #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']} -- echo -n '#{project}' \\> /cygdrive/c/Users/Administrator/Desktop/go" | |
print "Wait for tests to finish..." | |
timeouted = true | |
CONFIG['ci']['tests_timeout'].times do | |
sleep 1 | |
`ssh #{CONFIG['ci']['ssh_params']} #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']} -- test -f /cygdrive/c/Users/Administrator/Desktop/done 2>/dev/null` | |
unless $? == 0 | |
print "." | |
else | |
timeouted = false | |
break | |
end | |
end | |
puts | |
if timeouted | |
puts "Tests timeouted" | |
exit_codes = [1] | |
else | |
puts "Tests finished normally." | |
exit_codes = run_or_raise "ssh #{CONFIG['ci']['ssh_params']} #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']} -- cat /cygdrive/c/Users/Administrator/Desktop/exitcodes" | |
end | |
# Print log for CruiseControl | |
puts run_or_raise "ssh #{CONFIG['ci']['ssh_params']} #{CONFIG['ci']['username']}@#{CONFIG['ci']['host']} -- cat /cygdrive/c/Users/Administrator/Desktop/ci.log" | |
# Return sum of error codes | |
exit_codes.split.inject(0) { |sum, x| sum + x.to_i } rescue 1 | |
end | |
### | |
CONFIG = YAML.load(File.read( | |
File.join(File.dirname(__FILE__), "config.yml") | |
)) | |
AWS.config CONFIG['credentials'] | |
case ARGV[0] | |
when '--run-tests' | |
# Let's go! | |
if File.file? CONFIG['ci']['lock_file'] | |
puts "Another instance of this script is already running!" | |
exit 1 | |
end | |
unless ARGV[1] | |
puts "You must pass the project to build." | |
exit 1 | |
end | |
FileUtils.touch CONFIG['ci']['lock_file'] | |
FileUtils.touch CONFIG['ci']['last_build_file'] | |
upload_hook | |
exit_code = run_tests(ARGV[1]) | |
FileUtils.rm CONFIG['ci']['lock_file'] | |
# Return JsTestDriver exit code to CruiseControl | |
exit exit_code | |
when '--shutdown-instance-if-needed' | |
# Tests are running right now, don't shutdown instance | |
exit 0 if File.file? CONFIG['ci']['lock_file'] | |
right_now = TZInfo::Timezone.get('Europe/London').utc_to_local(Time.new.utc) | |
case right_now.wday | |
when 0, 6 # Sunday, Saturday | |
is_working_time = false | |
else | |
is_working_time = (right_now.hour.between? 10, 18) | |
end | |
if is_working_time | |
# Shutdown if last build was at least one hour ago | |
`find #{File.dirname(CONFIG['ci']['last_build_file'])} -name #{File.basename(CONFIG['ci']['last_build_file'])} -mtime 1` | |
exit 0 if $? == 0 | |
end | |
shutdown_instance | |
exit 0 | |
else | |
puts <<-USAGE | |
You must pass exactly one argument: | |
--run-tests <project> | |
--shutdown-instance-if-needed | |
USAGE | |
end | |
exit 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment