UNPKG

4.31 kBPlain TextView Raw
1import os
2import sys
3import json
4import time
5import logging
6
7try:
8 import urllib.request as urllib2
9except ImportError:
10 import urllib2
11
12log = logging.getLogger("travis.leader")
13log.addHandler(logging.StreamHandler())
14log.setLevel(logging.INFO)
15
16TRAVIS_JOB_NUMBER = 'TRAVIS_JOB_NUMBER'
17TRAVIS_BUILD_ID = 'TRAVIS_BUILD_ID'
18POLLING_INTERVAL = 'LEADER_POLLING_INTERVAL'
19GITHUB_TOKEN = 'GITHUB_TOKEN'
20
21
22# Travis API entry point, there are at least https://api.travis-ci.com and https://api.travis-ci.org
23travis_entry = sys.argv[1] if len(sys.argv) > 1 else 'https://api.travis-ci.org'
24
25build_id = os.getenv(TRAVIS_BUILD_ID)
26polling_interval = int(os.getenv(POLLING_INTERVAL, '5'))
27gh_token = os.getenv(GITHUB_TOKEN)
28
29# assume, first job is the leader
30is_leader = lambda job_number: job_number.endswith('.1')
31
32job_number = os.getenv(TRAVIS_JOB_NUMBER)
33
34if not job_number:
35 # seems even for builds with only one job, this won't get here
36 log.fatal("Don't use defining leader for build without matrix")
37 exit(1)
38elif is_leader(job_number):
39 log.info("This is a leader")
40else:
41 # since python is subprocess, env variables are exported back via file
42 with open(".to_export_back", "w") as export_var:
43 export_var.write("BUILD_MINION=YES")
44 log.info("This is a minion")
45 exit(0)
46
47
48class MatrixElement(object):
49 def __init__(self, json_raw):
50 self.is_finished = json_raw['finished_at'] is not None
51 self.is_succeeded = json_raw['result'] == 0
52 self.number = json_raw['number']
53 self.is_leader = is_leader(self.number)
54
55
56def matrix_snapshot(travis_token):
57 """
58 :return: Matrix List
59 """
60 headers = {'content-type': 'application/json', 'Authorization': 'token {}'.format(travis_token)}
61 req = urllib2.Request("{0}/builds/{1}".format(travis_entry, build_id), headers=headers)
62 response = urllib2.urlopen(req).read()
63 raw_json = json.loads(response)
64 matrix_without_leader = [MatrixElement(job) for job in raw_json["matrix"] if not is_leader(job['number'])]
65 return matrix_without_leader
66
67
68def wait_others_to_finish(travis_token):
69 def others_finished():
70 """
71 Dumps others to finish
72 Leader cannot finish, it is working now
73 :return: tuple(True or False, List of not finished jobs)
74 """
75 snapshot = matrix_snapshot(travis_token)
76 finished = [job.is_finished for job in snapshot if not job.is_leader]
77 return reduce(lambda a, b: a and b, finished), [job.number for job in snapshot if
78 not job.is_leader and not job.is_finished]
79
80 while True:
81 finished, waiting_list = others_finished()
82 if finished:
83 break
84 log.info("Leader waits for minions {0}...".format(waiting_list)) # just in case do not get "silence timeout"
85 time.sleep(polling_interval)
86
87
88def get_token():
89 assert gh_token, 'GITHUB_TOKEN is not set'
90 data = {"github_token": gh_token}
91 headers = {'content-type': 'application/json'}
92
93 req = urllib2.Request("{0}/auth/github".format(travis_entry), json.dumps(data), headers)
94 response = urllib2.urlopen(req).read()
95 travis_token = json.loads(response).get('access_token')
96
97 return travis_token
98
99
100try:
101 token = get_token()
102 wait_others_to_finish(token)
103
104 final_snapshot = matrix_snapshot(token)
105 log.info("Final Results: {0}".format([(e.number, e.is_succeeded) for e in final_snapshot]))
106
107 BUILD_AGGREGATE_STATUS = 'BUILD_AGGREGATE_STATUS'
108 others_snapshot = [el for el in final_snapshot if not el.is_leader]
109 if reduce(lambda a, b: a and b, [e.is_succeeded for e in others_snapshot]):
110 os.environ[BUILD_AGGREGATE_STATUS] = "others_succeeded"
111 elif reduce(lambda a, b: a and b, [not e.is_succeeded for e in others_snapshot]):
112 log.error("Others Failed")
113 os.environ[BUILD_AGGREGATE_STATUS] = "others_failed"
114 else:
115 log.warn("Others Unknown")
116 os.environ[BUILD_AGGREGATE_STATUS] = "unknown"
117 # since python is subprocess, env variables are exported back via file
118 with open(".to_export_back", "w") as export_var:
119 export_var.write("BUILD_LEADER=YES {0}={1}".format(BUILD_AGGREGATE_STATUS, os.environ[BUILD_AGGREGATE_STATUS]))
120
121except Exception as e:
122 log.fatal(e)