def getProjectId(self, sourcestamp): # retrieve project id via cache url = giturlparse(sourcestamp['repository']) if url is None: defer.returnValue(None) project_full_name = u"%s/%s" % (url.owner, url.repo) project_full_name = unicode2NativeString(project_full_name) # gitlab needs project name to be fully url quoted to get the project id project_full_name = urlquote_plus(project_full_name) if project_full_name not in self.project_ids: response = yield self._http.get('/api/v3/projects/%s' % (project_full_name)) proj = yield response.json() if response.code not in (200, ): log.msg( 'Unknown (or hidden) gitlab project' '{repo}: {message}'.format( repo=project_full_name, **proj)) defer.returnValue(None) self.project_ids[project_full_name] = proj['id'] defer.returnValue(self.project_ids[project_full_name])
def create_changes(self, new_logentries): changes = [] for el in new_logentries: revision = text_type(el.getAttribute("revision")) revlink = u'' if self.revlinktmpl and revision: revlink = self.revlinktmpl % urlquote_plus(revision) revlink = text_type(revlink) log.msg("Adding change revision %s" % (revision,)) author = self._get_text(el, "author") comments = self._get_text(el, "msg") # there is a "date" field, but it provides localtime in the # repository's timezone, whereas we care about buildmaster's # localtime (since this will get used to position the boxes on # the Waterfall display, etc). So ignore the date field, and # addChange will fill in with the current time branches = {} try: pathlist = el.getElementsByTagName("paths")[0] except IndexError: # weird, we got an empty revision log.msg("ignoring commit with no paths") continue for p in pathlist.getElementsByTagName("path"): kind = p.getAttribute("kind") action = p.getAttribute("action") path = u"".join([t.data for t in p.childNodes]) if path.startswith("/"): path = path[1:] if kind == "dir" and not path.endswith("/"): path += "/" where = self._transform_path(path) # if 'where' is None, the file was outside any project that # we care about and we should ignore it if where: branch = where.get("branch", None) filename = where["path"] if branch not in branches: branches[branch] = { 'files': [], 'number_of_directories': 0} if filename == "": # root directory of branch branches[branch]['files'].append(filename) branches[branch]['number_of_directories'] += 1 elif filename.endswith("/"): # subdirectory of branch branches[branch]['files'].append(filename[:-1]) branches[branch]['number_of_directories'] += 1 else: branches[branch]['files'].append(filename) if "action" not in branches[branch]: branches[branch]['action'] = action for key in ("repository", "project", "codebase"): if key in where: branches[branch][key] = where[key] for branch in branches: action = branches[branch]['action'] files = branches[branch]['files'] number_of_directories_changed = branches[ branch]['number_of_directories'] number_of_files_changed = len(files) if (action == u'D' and number_of_directories_changed == 1 and number_of_files_changed == 1 and files[0] == ''): log.msg("Ignoring deletion of branch '%s'" % branch) else: chdict = dict( author=author, # weakly assume filenames are utf-8 files=[bytes2unicode(f, 'utf-8', 'replace') for f in files], comments=comments, revision=revision, branch=util.bytes2unicode(branch), revlink=revlink, category=self.category, repository=util.bytes2unicode( branches[branch].get('repository', self.repourl)), project=util.bytes2unicode( branches[branch].get('project', self.project)), codebase=util.bytes2unicode( branches[branch].get('codebase', None))) changes.append(chdict) return changes
def create_changes(self, new_logentries): changes = [] for el in new_logentries: revision = text_type(el.getAttribute("revision")) revlink = u'' if self.revlinktmpl and revision: revlink = self.revlinktmpl % urlquote_plus(revision) revlink = text_type(revlink) log.msg("Adding change revision %s" % (revision, )) author = self._get_text(el, "author") comments = self._get_text(el, "msg") # there is a "date" field, but it provides localtime in the # repository's timezone, whereas we care about buildmaster's # localtime (since this will get used to position the boxes on # the Waterfall display, etc). So ignore the date field, and # addChange will fill in with the current time branches = {} try: pathlist = el.getElementsByTagName("paths")[0] except IndexError: # weird, we got an empty revision log.msg("ignoring commit with no paths") continue for p in pathlist.getElementsByTagName("path"): kind = p.getAttribute("kind") action = p.getAttribute("action") path = "".join([t.data for t in p.childNodes]) path = path.encode("ascii") if path.startswith("/"): path = path[1:] if kind == "dir" and not path.endswith("/"): path += "/" where = self._transform_path(path) # if 'where' is None, the file was outside any project that # we care about and we should ignore it if where: branch = where.get("branch", None) filename = where["path"] if branch not in branches: branches[branch] = { 'files': [], 'number_of_directories': 0 } if filename == "": # root directory of branch branches[branch]['files'].append(filename) branches[branch]['number_of_directories'] += 1 elif filename.endswith("/"): # subdirectory of branch branches[branch]['files'].append(filename[:-1]) branches[branch]['number_of_directories'] += 1 else: branches[branch]['files'].append(filename) if "action" not in branches[branch]: branches[branch]['action'] = action for key in ("repository", "project", "codebase"): if key in where: branches[branch][key] = where[key] for branch in branches: action = branches[branch]['action'] files = branches[branch]['files'] number_of_directories_changed = branches[branch][ 'number_of_directories'] number_of_files_changed = len(files) if action == u'D' and number_of_directories_changed == 1 and number_of_files_changed == 1 and files[ 0] == '': log.msg("Ignoring deletion of branch '%s'" % branch) else: chdict = dict( author=author, # weakly assume filenames are utf-8 files=[f.decode('utf-8', 'replace') for f in files], comments=comments, revision=revision, branch=util.ascii2unicode(branch), revlink=revlink, category=self.category, repository=util.ascii2unicode(branches[branch].get( 'repository', self.repourl)), project=util.ascii2unicode(branches[branch].get( 'project', self.project)), codebase=util.ascii2unicode(branches[branch].get( 'codebase', None))) changes.append(chdict) return changes
def send(self, build): props = Properties.fromDict(build['properties']) if build['complete']: state = { SUCCESS: 'success', WARNINGS: 'success', FAILURE: 'failed', SKIPPED: 'success', EXCEPTION: 'error', RETRY: 'pending', CANCELLED: 'error' }.get(build['results'], 'error') description = yield props.render(self.endDescription) else: state = 'running' description = yield props.render(self.startDescription) context = yield props.render(self.context) sourcestamps = build['buildset']['sourcestamps'] project = sourcestamps[0]['project'] for ss in sourcestamps: try: repo = ss['repository'].split('/')[-2:] repoOwner = repo[0] if repo[1].endswith(".git"): repoName = '.'.join(repo[1].split('.')[:-1]) else: repoName = repo[1] # default to master if not found branch = ss.get('branch', 'master') break except Exception: pass if project and len(project.split('/')) == 2: repoOwner, repoName = project.split('/') m = re.match(".*:(.*)", repoOwner) if m is not None: repoOwner = m.group(1) # retrieve project id via cache project_full_name = u"%s/%s" % (repoOwner, repoName) project_full_name = unicode2NativeString(project_full_name) # gitlab needs project name to be fully url quoted to get the project id project_full_name = urlquote_plus(project_full_name) if project_full_name not in self.project_ids: proj = yield self._http.get('/api/v3/projects/%s' % (project_full_name)) proj = yield proj.json() self.project_ids[project_full_name] = proj['id'] proj_id = self.project_ids[project_full_name] for sourcestamp in sourcestamps: sha = sourcestamp['revision'] try: branch = unicode2NativeString(branch) sha = unicode2NativeString(sha) state = unicode2NativeString(state) target_url = unicode2NativeString(build['url']) context = unicode2NativeString(context) description = unicode2NativeString(description) res = yield self.createStatus(project_id=proj_id, branch=branch, sha=sha, state=state, target_url=target_url, context=context, description=description) if res.code not in (200, 201, 204): message = yield res.json() message = message.get('message', 'unspecified error') log.msg( 'Could not send status "{state}" for ' '{repoOwner}/{repoName} at {sha}: {message}'.format( state=state, repoOwner=repoOwner, repoName=repoName, sha=sha, message=message)) elif self.verbose: log.msg('Status "{state}" sent for ' '{repoOwner}/{repoName} at {sha}.'.format( state=state, repoOwner=repoOwner, repoName=repoName, sha=sha)) except Exception as e: log.err( e, 'Fail to send status "{state}" for ' '{repoOwner}/{repoName} at {sha}'.format( state=state, repoOwner=repoOwner, repoName=repoName, sha=sha))