/
builder.py
95 lines (80 loc) · 4.22 KB
/
builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import os, time
import web, pusher
import courier, simplethread
REPO_STORAGE = '/tmp/dudley_repo'
NOW = web.sqlliteral('now()')
pushcloud = pusher.pusher_from_url()
env = web.storage(os.environ)
buildqueue = simplethread.Queue()
def watchdb(db):
while 1:
try: buildqueue.get(timeout=60)
except simplethread.Empty: pass
for job in db.select('jobs', where="builder is null and done='f'"):
simplethread.spawn(lambda: start_build(db, job))
def get_buildserver(db, build_id):
buildservers = db.select('buildservers', where="building is null", limit=1).list()
if buildservers and db.update('buildservers', building=build_id, where="building is null and id=$buildservers[0].id", vars=locals()):
return buildservers[0]
else:
return False
def claim_job(db, job_id, build_id):
return db.update('jobs', builder=build_id, where="id=$job_id and builder is null", vars=locals())
def start_build(db, job):
build_id = db.insert('builds', job_id=job.id, updated_at=NOW)
if not claim_job(db, job.id, build_id):
# someone else is already building this job
db.delete('builds', where="build_id=$build_id", vars=locals())
return False
def update_build_log(text=''):
db.query("UPDATE builds SET log = log || $text, updated_at = now() WHERE id=$build_id", vars=locals())
pushcloud['build_' + str(build_id)].trigger('update', {'text': text, 'build_id': build_id})
sh = courier.Courier(update_build_log)
buildserver = get_buildserver(db, build_id)
if not buildserver:
update_build_log("Couldn't find a free buildserver. You might try adding some more.\n")
update_build_log("Sleeping until we find one...\n")
while not buildserver:
time.sleep(1)
update_build_log()
buildserver = get_buildserver(db, build_id)
try:
result = do_build(job.commit_hash, sh, buildserver)
except:
result = False
db.update('builds', where="id=$build_id", done=True, success=result, vars=locals())
db.update('jobs', where="id=$job.id", done=True, success=result, vars=locals())
db.update('buildservers', where='id=$buildserver.id', building=None, vars=locals())
db.insert('announcements', content="Deploy %s!" % {True: "succeeded", False: "failed"}[result])
def do_build(commit_hash, sh, buildserver):
if not os.path.exists(REPO_STORAGE):
try: os.mkdir('/app/.ssh')
except OSError: pass # directory already exists
file('/app/.ssh/known_hosts', 'a').write("heroku.com,50.19.85.132 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAu8erSx6jh+8ztsfHwkNeFr/SZaSOcvoa8AyMpaerGIPZDB2TKNgNkMSYTLYGDK2ivsqXopo2W7dpQRBIVF80q9mNXy5tbt1WE04gbOBB26Wn2hF4bk3Tu+BNMFbvMjPbkVlC2hcFuQJdH4T2i/dtauyTpJbD/6ExHR9XYVhdhdMs0JsjP/Q5FNoWh2ff9YbZVpDQSTPvusUp4liLjPfa/i0t+2LpNCeWy8Y+V9gUlDWiyYwrfMVI0UwNCZZKHs1Unpc11/4HLitQRtvuk0Ot5qwwBxbmtvCDKZvj1aFBid71/mYdGRPYZMIxq1zgP1acePC1zfTG/lvuQ7d0Pe0kaw==\n")
file('/app/.ssh/id_rsa', 'w').write(env.SSH_PRIVKEY)
sh.run('git', 'clone', env.GIT_URL, REPO_STORAGE)
sh.cd(REPO_STORAGE)
def sensiblepush(count=10):
errcode = sh.run('git', 'push', buildserver.short_name, commit_hash + ':master')
if errcode and count and 'error fetching custom buildpack' in sh.lastoutput.getvalue():
return sensiblepush(count=count-1)
elif errcode and count and 'Illegal instruction' in sh.lastoutput.getvalue():
return sensiblepush(count=count-1)
elif errcode and count and 'Utils::TimeoutError' in sh.lastoutput.getvalue():
return sensiblepush(count=count-1)
else:
return errcode
sh.run('git', 'remote', 'add', buildserver.short_name, buildserver.git_url)
errcode = ( # 0 is success, so we use `or`
sh.run('git', 'pull', 'origin', 'master') or
sh.run('git', 'checkout', commit_hash) or
sensiblepush()
)
return not errcode
if __name__ == "__main__":
import sys
#INSERT INTO buildservers (short_name, git_url) VALUES('sleepy-ocean-9027', 'git@heroku.com:sleepy-ocean-9027.git');
db = web.database()
job = web.storage(commit_hash=sys.argv[1])
job.id = db.insert('jobs', **job)
start_build(db, job)