Instantly share code, notes, and snippets.

Embed
What would you like to do?
EC2 Windows CI hackish scripts
@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
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
#!/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