8b36ff5888
- moved linux agents to a new node pool (basically renamed old one) - removed some out of date scripts or moved them to playlists - removed shell scrips invoked from jenkins - minor docs updates Some python classes methods are not used anymore and will be cleaned up separately.
201 lines
6.7 KiB
Python
Executable file
201 lines
6.7 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# Copyright 2019 Google LLC
|
|
#
|
|
# Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# https://llvm.org/LICENSE.txt
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""
|
|
Interactions with Phabricator.
|
|
"""
|
|
|
|
import socket
|
|
import time
|
|
import uuid
|
|
from typing import Optional, List, Dict
|
|
from phabricator import Phabricator
|
|
|
|
|
|
class PhabTalk:
|
|
"""Talk to Phabricator to upload build results.
|
|
See https://secure.phabricator.com/conduit/method/harbormaster.sendmessage/
|
|
"""
|
|
|
|
def __init__(self, token: Optional[str], host: Optional[str], dryrun: bool):
|
|
self._phab = None # type: Optional[Phabricator]
|
|
if not dryrun:
|
|
self._phab = Phabricator(token=token, host=host)
|
|
_try_call(self._phab.update_interfaces)
|
|
|
|
@property
|
|
def dryrun(self):
|
|
return self._phab is None
|
|
|
|
def get_revision_id(self, diff: str) -> Optional[str]:
|
|
"""Get the revision ID for a diff from Phabricator."""
|
|
if self.dryrun:
|
|
return None
|
|
|
|
result = self._phab.differential.querydiffs(ids=[diff])
|
|
return 'D' + result[diff]['revisionID']
|
|
|
|
def comment_on_diff(self, diff_id: str, text: str):
|
|
"""Add a comment to a differential based on the diff_id"""
|
|
print('Sending comment to diff {}:'.format(diff_id))
|
|
print(text)
|
|
revision_id = self.get_revision_id(diff_id)
|
|
if revision_id is not None:
|
|
self._comment_on_revision(revision_id, text)
|
|
|
|
def _comment_on_revision(self, revision: str, text: str):
|
|
"""Add comment on a differential based on the revision id."""
|
|
|
|
transactions = [{
|
|
'type': 'comment',
|
|
'value': text
|
|
}]
|
|
|
|
if self.dryrun:
|
|
print('differential.revision.edit =================')
|
|
print('Transactions: {}'.format(transactions))
|
|
return
|
|
|
|
# API details at
|
|
# https://secure.phabricator.com/conduit/method/differential.revision.edit/
|
|
self._phab.differential.revision.edit(objectIdentifier=revision,
|
|
transactions=transactions)
|
|
print('Uploaded comment to Revision D{}:{}'.format(revision, text))
|
|
|
|
def update_build_status(self, diff_id: str, phid: str, working: bool, success: bool, lint: {} = {}, unit: [] = []):
|
|
"""Submit collected report to Phabricator.
|
|
"""
|
|
|
|
result_type = 'pass'
|
|
if working:
|
|
result_type = 'working'
|
|
elif not success:
|
|
result_type = 'fail'
|
|
|
|
# Group lint messages by file and line.
|
|
lint_messages = []
|
|
for v in lint.values():
|
|
path = ''
|
|
line = 0
|
|
descriptions = []
|
|
for e in v:
|
|
path = e['path']
|
|
line = e['line']
|
|
descriptions.append('{}: {}'.format(e['name'], e['description']))
|
|
lint_message = {
|
|
'name': 'Pre-merge checks',
|
|
'severity': 'warning',
|
|
'code': 'llvm-premerge-checks',
|
|
'path': path,
|
|
'line': line,
|
|
'description': '\n'.join(descriptions),
|
|
}
|
|
lint_messages.append(lint_message)
|
|
|
|
if self.dryrun:
|
|
print('harbormaster.sendmessage =================')
|
|
print('type: {}'.format(result_type))
|
|
print('unit: {}'.format(unit))
|
|
print('lint: {}'.format(lint_messages))
|
|
return
|
|
|
|
_try_call(lambda: self._phab.harbormaster.sendmessage(
|
|
buildTargetPHID=phid,
|
|
type=result_type,
|
|
unit=unit,
|
|
lint=lint_messages))
|
|
print('Uploaded build status {}, {} test results and {} lint results'.format(
|
|
result_type, len(unit), len(lint_messages)))
|
|
|
|
# TODO: deprecate
|
|
def add_artifact(self, phid: str, file: str, name: str, results_url: str):
|
|
artifactKey = str(uuid.uuid4())
|
|
artifactType = 'uri'
|
|
artifactData = {'uri': '{}/{}'.format(results_url, file),
|
|
'ui.external': True,
|
|
'name': name}
|
|
self.create_artifact(phid, artifactKey, artifactType, artifactData)
|
|
print('Created artifact "{}"'.format(name))
|
|
|
|
def create_artifact(self, phid, artifact_key, artifact_type, artifact_data):
|
|
if self.dryrun:
|
|
print('harbormaster.createartifact =================')
|
|
print('artifactKey: {}'.format(artifact_key))
|
|
print('artifactType: {}'.format(artifact_type))
|
|
print('artifactData: {}'.format(artifact_data))
|
|
return
|
|
_try_call(lambda: self._phab.harbormaster.createartifact(
|
|
buildTargetPHID=phid,
|
|
artifactKey=artifact_key,
|
|
artifactType=artifact_type,
|
|
artifactData=artifact_data))
|
|
|
|
|
|
class Step:
|
|
def __init__(self):
|
|
self.name = ''
|
|
self.success = True
|
|
self.duration = 0.0
|
|
self.messages = []
|
|
self.reproduce_commands = []
|
|
|
|
def set_status_from_exit_code(self, exit_code: int):
|
|
if exit_code != 0:
|
|
self.success = False
|
|
|
|
|
|
class Report:
|
|
def __init__(self):
|
|
self.os = ''
|
|
self.name = ''
|
|
self.comments = []
|
|
self.success = True
|
|
self.working = False
|
|
self.unit = [] # type: List
|
|
self.lint = {}
|
|
self.test_stats = {
|
|
'pass': 0,
|
|
'fail': 0,
|
|
'skip': 0
|
|
} # type: Dict[str, int]
|
|
self.steps = [] # type: List[Step]
|
|
self.artifacts = [] # type: List
|
|
|
|
def __str__(self):
|
|
return str(self.__dict__)
|
|
|
|
def add_lint(self, m):
|
|
key = '{}:{}'.format(m['path'], m['line'])
|
|
if key not in self.lint:
|
|
self.lint[key] = []
|
|
self.lint[key].append(m)
|
|
|
|
def add_artifact(self, dir: str, file: str, name: str):
|
|
self.artifacts.append({'dir': dir, 'file': file, 'name': name})
|
|
|
|
|
|
def _try_call(call):
|
|
"""Tries to call function several times retrying on socked.timeout."""
|
|
c = 0
|
|
while True:
|
|
try:
|
|
call()
|
|
except socket.timeout as e:
|
|
c += 1
|
|
if c > 5:
|
|
print('Connection to Pharicator failed, giving up: {}'.format(e))
|
|
raise
|
|
print('Connection to Pharicator failed, retrying: {}'.format(e))
|
|
time.sleep(c * 10)
|
|
break
|