Skip to content

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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.