Run bazel builds in premerge
if user is a member of "bazel_build" https://reviews.llvm.org/project/view/107/ or modified /utils/bazel/* For #328
This commit is contained in:
parent
f7058aaa51
commit
6c2410440e
9 changed files with 110 additions and 22 deletions
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
# Run prune as git will start to complain about
|
# Run prune as git will start to complain "warning: There are too many unreachable loose objects; run 'git prune' to remove them.".
|
||||||
|
# That happens as we routinely drop old branches.
|
||||||
git prune
|
git prune
|
||||||
if [ -f ".git/gc.log" ]; then
|
if [ -f ".git/gc.log" ]; then
|
||||||
echo ".git/gc.log exist"
|
echo ".git/gc.log exist"
|
||||||
|
|
|
@ -192,7 +192,18 @@ Most commonly used are:
|
||||||
- `ph_log_level` ("DEBUG", "INFO", "WARNING" (default) or "ERROR"): log level for build scripts.
|
- `ph_log_level` ("DEBUG", "INFO", "WARNING" (default) or "ERROR"): log level for build scripts.
|
||||||
- `ph_linux_agents`, `ph_windows_agents`: custom JSON constraints on agents. For example, you might put one machine to a custom queue if it's errornous and send jobs to it with `ph_windows_agents={"queue": "custom"}`.
|
- `ph_linux_agents`, `ph_windows_agents`: custom JSON constraints on agents. For example, you might put one machine to a custom queue if it's errornous and send jobs to it with `ph_windows_agents={"queue": "custom"}`.
|
||||||
- `ph_skip_linux`, `ph_skip_windows` (if set to any value): skip build on this OS.
|
- `ph_skip_linux`, `ph_skip_windows` (if set to any value): skip build on this OS.
|
||||||
- `ph_skip_generated`: don't run custom steps generated from within llvm-project.
|
- `ph_skip_generated`: don't run custom steps generated from within llvm-project.
|
||||||
|
|
||||||
|
While trying a new patch for premerge scripts it's typical to start a new build by copying "ph_"
|
||||||
|
env variables from one of the recent builds and appending
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ph_dry_run_report=yes
|
||||||
|
ph_skip_windows=yes
|
||||||
|
ph_skip_generated=yes
|
||||||
|
ph_scripts_refspec="<branch name>"
|
||||||
|
ph_log_level=DEBUG
|
||||||
|
```
|
||||||
|
|
||||||
## Update HTTP auth credentials
|
## Update HTTP auth credentials
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,8 @@ class ChooseProjects:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_changed_files(patch_str: str = None) -> Set[str]:
|
def get_changed_files(patch_str: str = None) -> Set[str]:
|
||||||
"""get list of changed files from the patch or from STDIN."""
|
"""get list of changed files from the patch or from STDIN.
|
||||||
|
e.g. ['compiler-rt/lib/tsan/CMakeLists.txt']"""
|
||||||
if patch_str is None:
|
if patch_str is None:
|
||||||
patch_str = sys.stdin
|
patch_str = sys.stdin
|
||||||
patch = PatchSet(patch_str)
|
patch = PatchSet(patch_str)
|
||||||
|
|
|
@ -219,7 +219,7 @@ class ApplyPatch:
|
||||||
|
|
||||||
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
||||||
def get_diff(self, diff_id: int):
|
def get_diff(self, diff_id: int):
|
||||||
"""Get a diff from Phabricator based on it's diff id."""
|
"""Get a diff from Phabricator based on its diff id."""
|
||||||
return self.phab.differential.getdiff(diff_id=diff_id)
|
return self.phab.differential.getdiff(diff_id=diff_id)
|
||||||
|
|
||||||
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
||||||
|
|
|
@ -23,11 +23,13 @@ import argparse
|
||||||
|
|
||||||
import backoff
|
import backoff
|
||||||
from phabricator import Phabricator
|
from phabricator import Phabricator
|
||||||
|
from benedict import benedict
|
||||||
|
|
||||||
|
|
||||||
class PhabTalk:
|
class PhabTalk:
|
||||||
"""Talk to Phabricator to upload build results.
|
"""Talk to Phabricator to upload build results.
|
||||||
See https://secure.phabricator.com/conduit/method/harbormaster.sendmessage/
|
See https://secure.phabricator.com/conduit/method/harbormaster.sendmessage/
|
||||||
|
You might want to use it as it provides retries on most of the calls.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, token: Optional[str], host: Optional[str] = 'https://reviews.llvm.org/api/',
|
def __init__(self, token: Optional[str], host: Optional[str] = 'https://reviews.llvm.org/api/',
|
||||||
|
@ -47,6 +49,11 @@ class PhabTalk:
|
||||||
result = self._phab.differential.querydiffs(ids=[diff])
|
result = self._phab.differential.querydiffs(ids=[diff])
|
||||||
return 'D' + result[diff]['revisionID']
|
return 'D' + result[diff]['revisionID']
|
||||||
|
|
||||||
|
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
||||||
|
def get_diff(self, diff_id: int):
|
||||||
|
"""Get a diff from Phabricator based on its diff id."""
|
||||||
|
return self._phab.differential.getdiff(diff_id=diff_id)
|
||||||
|
|
||||||
def comment_on_diff(self, diff_id: str, text: str):
|
def comment_on_diff(self, diff_id: str, text: str):
|
||||||
"""Add a comment to a differential based on the diff_id"""
|
"""Add a comment to a differential based on the diff_id"""
|
||||||
logging.info('Sending comment to diff {}:'.format(diff_id))
|
logging.info('Sending comment to diff {}:'.format(diff_id))
|
||||||
|
@ -144,6 +151,27 @@ class PhabTalk:
|
||||||
return
|
return
|
||||||
self.create_artifact(phid, str(uuid.uuid4()), 'uri', {'uri': url, 'ui.external': True, 'name': name})
|
self.create_artifact(phid, str(uuid.uuid4()), 'uri', {'uri': url, 'ui.external': True, 'name': name})
|
||||||
|
|
||||||
|
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
||||||
|
def user_projects(self, user_phid: str) -> List[str]:
|
||||||
|
"""Returns slugs of all projects user has a membership."""
|
||||||
|
projects = benedict(self._phab.project.search(constraints={'members': [user_phid]}))
|
||||||
|
slugs = []
|
||||||
|
for p in projects.get('data', []):
|
||||||
|
slug = benedict(p).get('fields.slug')
|
||||||
|
if slug:
|
||||||
|
slugs.append(p['fields']['slug'])
|
||||||
|
return slugs
|
||||||
|
|
||||||
|
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
||||||
|
def get_revision(self, revision_id: int):
|
||||||
|
"""Get a revision from Phabricator based on its revision id."""
|
||||||
|
return self._phab.differential.query(ids=[revision_id])[0]
|
||||||
|
|
||||||
|
@backoff.on_exception(backoff.expo, Exception, max_tries=5, logger='', factor=3)
|
||||||
|
def get_diff(self, diff_id: int):
|
||||||
|
"""Get a diff from Phabricator based on its diff id."""
|
||||||
|
return self._phab.differential.getdiff(diff_id=diff_id)
|
||||||
|
|
||||||
|
|
||||||
class Step:
|
class Step:
|
||||||
def __init__(self, name=''):
|
def __init__(self, name=''):
|
||||||
|
|
|
@ -14,19 +14,31 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
from buildkite_utils import set_metadata, BuildkiteApi
|
from buildkite_utils import set_metadata, BuildkiteApi
|
||||||
|
from phabtalk.phabtalk import PhabTalk
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
diff_id = os.getenv("ph_buildable_diff")
|
diff_id = os.getenv("ph_buildable_diff")
|
||||||
|
revision_id = os.getenv("ph_buildable_revision")
|
||||||
log_level = os.getenv('ph_log_level', 'INFO')
|
log_level = os.getenv('ph_log_level', 'INFO')
|
||||||
base_commit = os.getenv('ph_base_commit', 'auto')
|
base_commit = os.getenv('ph_base_commit', 'auto')
|
||||||
run_build = os.getenv('ph_skip_build') is None
|
run_build = os.getenv('ph_skip_build') is None
|
||||||
trigger = os.getenv('ph_trigger_pipeline')
|
trigger = os.getenv('ph_trigger_pipeline')
|
||||||
logging.basicConfig(level=log_level, format='%(levelname)-7s %(message)s')
|
logging.basicConfig(level=log_level, format='%(levelname)-7s %(message)s')
|
||||||
|
|
||||||
|
phabtalk = PhabTalk(os.getenv('CONDUIT_TOKEN'), dry_run_updates=(os.getenv('ph_dry_run_report') is not None))
|
||||||
|
rev = phabtalk.get_revision(int(revision_id))
|
||||||
|
user_id = rev.get('authorPHID')
|
||||||
|
logging.debug(f'authorPHID {user_id}')
|
||||||
|
if user_id is None:
|
||||||
|
logging.error('cannot find author of the revision')
|
||||||
|
sys.exit(1)
|
||||||
|
projects = phabtalk.user_projects(user_id)
|
||||||
|
logging.info(f'user projects: {", ".join(projects)}')
|
||||||
# Cancel any existing builds.
|
# Cancel any existing builds.
|
||||||
# Do this before setting own 'ph_buildable_revision'.
|
# Do this before setting own 'ph_buildable_revision'.
|
||||||
try:
|
try:
|
||||||
|
@ -43,8 +55,17 @@ if __name__ == '__main__':
|
||||||
if trigger is None:
|
if trigger is None:
|
||||||
trigger = 'premerge-checks'
|
trigger = 'premerge-checks'
|
||||||
|
|
||||||
steps = []
|
env = {
|
||||||
steps.append({
|
'ph_scripts_refspec': '${BUILDKITE_BRANCH}',
|
||||||
|
'ph_user_project_slugs': ",".join(projects),
|
||||||
|
# TODO: those two are for "apply_patch.sh". Maybe just look for "ph_" env in patch_diff.py?
|
||||||
|
'LOG_LEVEL': log_level,
|
||||||
|
'BASE_COMMIT': base_commit,
|
||||||
|
}
|
||||||
|
for e in os.environ:
|
||||||
|
if e.startswith('ph_'):
|
||||||
|
env[e] = os.getenv(e)
|
||||||
|
steps = [{
|
||||||
'label': 'create branch',
|
'label': 'create branch',
|
||||||
'key': 'create-branch',
|
'key': 'create-branch',
|
||||||
'commands': [
|
'commands': [
|
||||||
|
@ -53,11 +74,8 @@ if __name__ == '__main__':
|
||||||
],
|
],
|
||||||
'agents': {'queue': 'service'},
|
'agents': {'queue': 'service'},
|
||||||
'timeout_in_minutes': 20,
|
'timeout_in_minutes': 20,
|
||||||
'env': {
|
'env': env
|
||||||
'LOG_LEVEL': log_level,
|
}]
|
||||||
'BASE_COMMIT': base_commit,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if run_build:
|
if run_build:
|
||||||
trigger_build_step = {
|
trigger_build_step = {
|
||||||
'trigger': trigger,
|
'trigger': trigger,
|
||||||
|
@ -66,14 +84,8 @@ if __name__ == '__main__':
|
||||||
'depends_on': 'create-branch',
|
'depends_on': 'create-branch',
|
||||||
'build': {
|
'build': {
|
||||||
'branch': f'phab-diff-{diff_id}',
|
'branch': f'phab-diff-{diff_id}',
|
||||||
'env': {},
|
'env': env,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for e in os.environ:
|
|
||||||
if e.startswith('ph_'):
|
|
||||||
trigger_build_step['build']['env'][e] = os.getenv(e)
|
|
||||||
# Set scripts source from the current build if it's not yet defined.
|
|
||||||
if 'ph_scripts_refspec' not in trigger_build_step['build']['env']:
|
|
||||||
trigger_build_step['build']['env']['ph_scripts_refspec'] = '${BUILDKITE_BRANCH}'
|
|
||||||
steps.append(trigger_build_step)
|
steps.append(trigger_build_step)
|
||||||
print(yaml.dump({'steps': steps}))
|
print(yaml.dump({'steps': steps}))
|
||||||
|
|
|
@ -19,7 +19,7 @@ import os
|
||||||
from buildkite_utils import annotate, feedback_url, set_metadata
|
from buildkite_utils import annotate, feedback_url, set_metadata
|
||||||
from choose_projects import ChooseProjects
|
from choose_projects import ChooseProjects
|
||||||
import git
|
import git
|
||||||
from steps import generic_linux, generic_windows, from_shell_output, checkout_scripts
|
from steps import generic_linux, generic_windows, from_shell_output, checkout_scripts, bazel
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
steps_generators = [
|
steps_generators = [
|
||||||
|
@ -73,6 +73,7 @@ if __name__ == '__main__':
|
||||||
if os.getenv('ph_skip_generated') is None:
|
if os.getenv('ph_skip_generated') is None:
|
||||||
for gen in steps_generators:
|
for gen in steps_generators:
|
||||||
steps.extend(from_shell_output(gen))
|
steps.extend(from_shell_output(gen))
|
||||||
|
steps.extend(bazel(modified_files))
|
||||||
|
|
||||||
if phid is None:
|
if phid is None:
|
||||||
logging.warning('ph_target_phid is not specified. Skipping "Report" step')
|
logging.warning('ph_target_phid is not specified. Skipping "Report" step')
|
||||||
|
|
|
@ -17,7 +17,7 @@ import io
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import List
|
from typing import List, Set
|
||||||
|
|
||||||
from exec_utils import watch_shell
|
from exec_utils import watch_shell
|
||||||
import yaml
|
import yaml
|
||||||
|
@ -75,6 +75,40 @@ def generic_linux(projects: str, check_diff: bool) -> List:
|
||||||
return [linux_buld_step]
|
return [linux_buld_step]
|
||||||
|
|
||||||
|
|
||||||
|
def bazel(modified_files: Set[str]) -> List:
|
||||||
|
if os.getenv('ph_skip_bazel') is not None:
|
||||||
|
logging.info('bazel build is skipped as "ph_skip_bazel" is set')
|
||||||
|
return []
|
||||||
|
updated_build = any(s.startswith('utils/bazel/') for s in modified_files)
|
||||||
|
if updated_build:
|
||||||
|
logging.info('files in utils/bazel/ modified, will trigger bazel build')
|
||||||
|
else:
|
||||||
|
user_projects = os.getenv('ph_user_project_slugs', '').split(',')
|
||||||
|
if 'bazel_build' not in user_projects:
|
||||||
|
logging.info('bazel build is skipped as "bazel_build" is not listed in user projects and no files in '
|
||||||
|
'utils/bazel/ are modified')
|
||||||
|
return []
|
||||||
|
agents = {'queue': 'llvm-bazel'}
|
||||||
|
t = os.getenv('ph_bazel_agents')
|
||||||
|
if t is not None:
|
||||||
|
agents = json.loads(t)
|
||||||
|
return [{
|
||||||
|
'label': ':bazel: bazel',
|
||||||
|
'key': 'bazel',
|
||||||
|
'commands': [
|
||||||
|
'set -eu',
|
||||||
|
'cd utils/bazel',
|
||||||
|
'bazel query //... + @llvm-project//... | xargs bazel test --config=generic_clang --config=rbe --test_output=errors --test_tag_filters=-nobuildkite --build_tag_filters=-nobuildkite',
|
||||||
|
],
|
||||||
|
'agents': agents,
|
||||||
|
'timeout_in_minutes': 120,
|
||||||
|
'retry': {'automatic': [
|
||||||
|
{'exit_status': -1, 'limit': 2}, # Agent lost
|
||||||
|
{'exit_status': 255, 'limit': 2}, # Forced agent shutdown
|
||||||
|
]},
|
||||||
|
}]
|
||||||
|
|
||||||
|
|
||||||
def generic_windows(projects: str) -> List:
|
def generic_windows(projects: str) -> List:
|
||||||
if os.getenv('ph_skip_windows') is not None:
|
if os.getenv('ph_skip_windows') is not None:
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -75,7 +75,7 @@ if __name__ == '__main__':
|
||||||
for i, job in enumerate(build.get('jobs', [])):
|
for i, job in enumerate(build.get('jobs', [])):
|
||||||
job = benedict(job)
|
job = benedict(job)
|
||||||
job_web_url = job.get('web_url', os.getenv('BUILDKITE_BUILD_URL', ''))
|
job_web_url = job.get('web_url', os.getenv('BUILDKITE_BUILD_URL', ''))
|
||||||
logging.info(f'{job.get("id")} state {job.get("state")}')
|
logging.info(f'{job.get("id")} {job.get("name")} state {job.get("state")}')
|
||||||
job_state = job.get('state')
|
job_state = job.get('state')
|
||||||
if job.get('type') == 'waiter':
|
if job.get('type') == 'waiter':
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in a new issue