def start(self): """ detach from the parent, decouple everything, and rebind """ log.info("Starting roundabout...") _fork(1) and _decouple() _fork(2) and self.__rebind() self.__write_pid_file(os.getpid()) log.info("Done")
def reload(self): """Sleep for configured time, the descendent class should then reload the data for the job. """ sleeptime = int(self.config["ci"].get("job_reload_sleep", 30)) log.info("Job not complete, sleeping for %s seconds..." % sleeptime) time.sleep(sleeptime)
def merge(self, branch, squash=None, message=None): """ Merge the passed in branch with HEAD """ log.info("merging %s into %s" % (branch, self.repo.active_branch.name)) merge_cmd = ('git', 'merge', branch) try: if squash: squash_cmd = tuple(list(merge_cmd) + ['--squash']) self.repo.git.execute(squash_cmd) if not message: try: with open(self.clonepath + "/.git/SQUASH_MSG") as msg: message = msg.read() except IOError: message = "Squash merge %s into %s" % (branch, self.repo.active_branch.name) commit_cmd = ['git', 'commit', '-m'] commit_cmd.append(message) return self.repo.git.execute(tuple(commit_cmd)) else: return self.repo.git.execute(merge_cmd) except git.exc.GitCommandError, e: # If there's a merge failure reset and raise. self.repo.head.reset(working_tree=True) raise GitException(e)
def comment(self, message): """ Add a comment to the specified issue. Returns a dict representation of the comment. """ log.info("commenting on %s: %s" % (self.number, message)) return self.client.github.issues.comment( self.client.config["github"]["repo"], self.number, message)
def spawn(cls, branch, config, opener=None): """ Create and return a paramaterized Job based on the CI config. """ ci_class = get_ci_class(config["ci"]["class"]) log.info("Starting %s job" % config["ci"]["class"]) return ci_class.spawn(branch, config, opener)
def close(self, message): """ Add a comment to this pull request and close it. Returns the closed Issue. """ self.comment(message) log.info("Closing %s" % self.html_url) return self.client.github.issues.close( self.client.config["github"]["repo"], self.number)
def spawn(cls, branch, config, opener=None): """ Create and return a paramaterized build of the current job """ _job = cls(config, opener=opener) log.info("Building: %s for %s" % (_job.config["ci"]["job"], branch)) if _job.req(_job.build_endpoint % (_job.config["ci"]["base_url"], _job.config["ci"]["job"], branch)): build_id = _job.properties["nextBuildNumber"] while True: # Keep trying until we return something. try: _job.build = [b for b in _job.builds if build_id == b.number][0] log.info("Build URL: %s" % _job.url) return _job except IndexError: time.sleep(1)
def stop(self): """ Read the pidfile for the process ID of the daemon and kill it """ try: log.info("Stopping roundabout...") with open(self.pidfile, 'r') as fd: pid = fd.read() try: os.kill(int(pid), signal.SIGTERM) log.info("Done...") except ValueError: log.info("Couldn't stop roundabout...") return None except (IOError, OSError): log.info("Couldn't stop roundabout...") return None
def update_config(config_file): """ Update the configuration from yaml to json """ if not config_file: raise RuntimeError("You didn't specify a configuration file") try: bak_file = config_file + '-bak' log.info("moving %s to %s" % (config_file, bak_file)) os.rename(config_file, bak_file) with open(bak_file) as bak: log.info("Opening %s" % bak_file) with open(config_file, "w") as out: log.info("Writing %s" % config_file) json.dump(yaml.load(bak), out, indent=4) except yaml.YAMLError: log.info("Couldn't read a yaml file, so nothing needed to be done")
finally: sys.exit(0) def do_post_merge_tasks(config, github): try: pull_requests = github.pull_requests pull_requests = [(u, p) for u, p in pull_requests.items() if p.looks_good_to_a_human(github.approvers)] except RuntimeError, e: log.error("Unexpected response from github:\n %s" % str(e)) pull_requests = [] if not pull_requests: log.info("No work to do, sleeping.") sleeptime = int(config["default"].get("poll_sleep", 30)) time.sleep(sleeptime) return for url, pull_request in pull_requests: log.info("processing %s" % url) repo = git_client.Git(remote_name=pull_request.remote_name, remote_url=pull_request.remote_url, remote_branch=pull_request.remote_branch, config=config) # Create a remote, fetch it, checkout the branch with repo as git: log.info("Cloning to %s" % repo.clonepath)
def push(self, branch, remote='origin', remote_branch=None): """ Push the branch up to the remote """ if remote_branch: branch = "%s:%s" % (branch, remote_branch) log.info("pushing %s to %s" % (branch, remote)) return self.repo.remote(remote).push(branch)
def run(config): """ Run roundabout forever or until you kill it. """ while True: github = roundabout.github.client.Client(config) try: pull_requests = github.pull_requests pull_requests = [(u, p) for u, p in pull_requests.items() if p.lgtm(github.approvers)] except RuntimeError, e: log.error("Unexpected response from github:\n %s" % str(e)) pull_requests = [] if not pull_requests: log.info("No work to do, sleeping.") sleeptime = int(config["default"].get("poll_sleep", 30)) time.sleep(sleeptime) continue for url, pull_request in pull_requests: log.info("processing %s" % url) repo = git_client.Git(remote_name=pull_request.remote_name, remote_url=pull_request.remote_url, remote_branch=pull_request.remote_branch, config=config) # Create a remote, fetch it, checkout the branch with repo as git: log.info("Cloning to %s" % repo.clonepath) # Ensure we're on the requested branch for the pull_request. base_branch = pull_request.base_branch git.branch(base_branch).checkout() try: git.merge(git.remote_branch, squash=config["git"].get("squash_merges")) except git_client.GitException, e: pull_request.close(git_client.MERGE_FAIL_MSG % e) continue if config["pylint"]: py_res = pylint.Pylint(config["pylint"]["modules"], config=config, path=repo.clonepath) if not py_res: pull_request.close( pylint.PYLINT_FAIL_MSG % (py_res.previous_score, py_res.current_score)) continue # push up a test branch git.push(base_branch, remote_branch=git.local_branch_name) with ci.job.Job.spawn(git.local_branch_name, config) as job: while not job.complete: job.reload() if job: # Successful build, good coverage, and clean pylint. git.push(base_branch) pull_request.close(git_client.BUILD_SUCCESS_MSG) else: pull_request.close(git_client.BUILD_FAIL_MSG % job.url)