Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 3 | # Copyright 2018 The Bazel Authors. All rights reserved. |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 17 | import argparse |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 18 | import base64 |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 19 | import codecs |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 20 | import hashlib |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 21 | import json |
Jakob Buchgraber | 6db0f26 | 2018-02-17 15:45:54 +0100 | [diff] [blame] | 22 | import multiprocessing |
Philipp Wollermann | 0a04cf3 | 2018-02-21 17:07:22 +0100 | [diff] [blame] | 23 | import os |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 24 | import os.path |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 25 | import random |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 26 | import re |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 27 | from shutil import copyfile |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 28 | import shutil |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 29 | import stat |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 30 | import subprocess |
| 31 | import sys |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 32 | import tempfile |
| 33 | import urllib.request |
Philipp Wollermann | c030f2e | 2018-02-21 17:02:19 +0100 | [diff] [blame] | 34 | from urllib.request import url2pathname |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 35 | from urllib.parse import urlparse |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 36 | |
| 37 | # Initialize the random number generator. |
| 38 | random.seed() |
| 39 | |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 40 | |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 41 | class BuildkiteException(Exception): |
| 42 | """ |
| 43 | Raised whenever something goes wrong and we should exit with an error. |
| 44 | """ |
| 45 | pass |
| 46 | |
| 47 | |
| 48 | class BinaryUploadRaceException(Exception): |
| 49 | """ |
| 50 | Raised when try_publish_binaries wasn't able to publish a set of binaries, |
| 51 | because the generation of the current file didn't match the expected value. |
| 52 | """ |
| 53 | pass |
| 54 | |
| 55 | |
| 56 | class BazelTestFailedException(Exception): |
| 57 | """ |
| 58 | Raised when a Bazel test fails. |
| 59 | """ |
| 60 | pass |
| 61 | |
| 62 | |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 63 | class BazelTestFailedException(Exception): |
| 64 | """ |
| 65 | Raised when a Bazel build fails. |
| 66 | """ |
| 67 | pass |
| 68 | |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 69 | def eprint(*args, **kwargs): |
| 70 | """ |
| 71 | Print to stderr and flush (just in case). |
| 72 | """ |
| 73 | print(*args, flush=True, file=sys.stderr, **kwargs) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 74 | |
| 75 | |
| 76 | def downstream_projects(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 77 | return { |
Jakob Buchgraber | f462ff9 | 2018-02-20 17:34:09 +0100 | [diff] [blame] | 78 | "Bazel Remote Execution": { |
| 79 | "git_repository": "https://github.com/bazelbuild/bazel.git", |
| 80 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/bazel-remote-execution-postsubmit.json" |
| 81 | }, |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 82 | "BUILD_file_generator": { |
| 83 | "git_repository": "https://github.com/bazelbuild/BUILD_file_generator.git", |
| 84 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/BUILD_file_generator-postsubmit.json" |
| 85 | }, |
| 86 | "bazel-toolchains": { |
| 87 | "git_repository": "https://github.com/bazelbuild/bazel-toolchains.git", |
| 88 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/bazel-toolchains-postsubmit.json" |
| 89 | }, |
| 90 | "buildtools": { |
| 91 | "git_repository": "https://github.com/bazelbuild/buildtools.git", |
| 92 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/buildtools-postsubmit.json" |
| 93 | }, |
| 94 | "CLion Plugin": { |
| 95 | "git_repository": "https://github.com/bazelbuild/intellij.git", |
| 96 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/clion-postsubmit.json" |
| 97 | }, |
| 98 | "Eclipse Plugin": { |
| 99 | "git_repository": "https://github.com/bazelbuild/eclipse.git", |
| 100 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/eclipse-postsubmit.json" |
| 101 | }, |
| 102 | "Gerrit": { |
| 103 | "git_repository": "https://gerrit.googlesource.com/gerrit.git", |
| 104 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/gerrit-postsubmit.json" |
| 105 | }, |
| 106 | "Google Logging": { |
| 107 | "git_repository": "https://github.com/google/glog.git", |
| 108 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/glog-postsubmit.json" |
| 109 | }, |
| 110 | "IntelliJ Plugin": { |
| 111 | "git_repository": "https://github.com/bazelbuild/intellij.git", |
| 112 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/intellij-postsubmit.json" |
| 113 | }, |
| 114 | "migration-tooling": { |
| 115 | "git_repository": "https://github.com/bazelbuild/migration-tooling.git", |
| 116 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/migration-tooling-postsubmit.json" |
| 117 | }, |
| 118 | "protobuf": { |
| 119 | "git_repository": "https://github.com/google/protobuf.git", |
| 120 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/protobuf-postsubmit.json" |
| 121 | }, |
| 122 | "re2": { |
| 123 | "git_repository": "https://github.com/google/re2.git", |
| 124 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/re2-postsubmit.json" |
| 125 | }, |
| 126 | "rules_appengine": { |
| 127 | "git_repository": "https://github.com/bazelbuild/rules_appengine.git", |
| 128 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_appengine-postsubmit.json" |
| 129 | }, |
| 130 | "rules_closure": { |
| 131 | "git_repository": "https://github.com/bazelbuild/rules_closure.git", |
| 132 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_closure-postsubmit.json" |
| 133 | }, |
| 134 | "rules_d": { |
| 135 | "git_repository": "https://github.com/bazelbuild/rules_d.git", |
| 136 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_d-postsubmit.json" |
| 137 | }, |
| 138 | "rules_go": { |
| 139 | "git_repository": "https://github.com/bazelbuild/rules_go.git", |
| 140 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_go-postsubmit.json" |
| 141 | }, |
| 142 | "rules_groovy": { |
| 143 | "git_repository": "https://github.com/bazelbuild/rules_groovy.git", |
| 144 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_groovy-postsubmit.json" |
| 145 | }, |
| 146 | "rules_gwt": { |
| 147 | "git_repository": "https://github.com/bazelbuild/rules_gwt.git", |
| 148 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_gwt-postsubmit.json" |
| 149 | }, |
| 150 | "rules_jsonnet": { |
| 151 | "git_repository": "https://github.com/bazelbuild/rules_jsonnet.git", |
| 152 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_jsonnet-postsubmit.json" |
| 153 | }, |
| 154 | "rules_k8s": { |
| 155 | "git_repository": "https://github.com/bazelbuild/rules_k8s.git", |
| 156 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_k8s-postsubmit.json" |
| 157 | }, |
| 158 | "rules_nodejs": { |
| 159 | "git_repository": "https://github.com/bazelbuild/rules_nodejs.git", |
| 160 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_nodejs-postsubmit.json" |
| 161 | }, |
| 162 | "rules_perl": { |
| 163 | "git_repository": "https://github.com/bazelbuild/rules_perl.git", |
| 164 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_perl-postsubmit.json" |
| 165 | }, |
| 166 | "rules_python": { |
| 167 | "git_repository": "https://github.com/bazelbuild/rules_python.git", |
| 168 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_python-postsubmit.json" |
| 169 | }, |
| 170 | "rules_rust": { |
| 171 | "git_repository": "https://github.com/bazelbuild/rules_rust.git", |
| 172 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_rust-postsubmit.json" |
| 173 | }, |
| 174 | "rules_sass": { |
| 175 | "git_repository": "https://github.com/bazelbuild/rules_sass.git", |
| 176 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_sass-postsubmit.json" |
| 177 | }, |
| 178 | "rules_scala": { |
| 179 | "git_repository": "https://github.com/bazelbuild/rules_scala.git", |
| 180 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_scala-postsubmit.json" |
| 181 | }, |
| 182 | "rules_typescript": { |
| 183 | "git_repository": "https://github.com/bazelbuild/rules_typescript.git", |
| 184 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_typescript-postsubmit.json" |
| 185 | }, |
| 186 | # Enable once is resolved: https://github.com/bazelbuild/continuous-integration/issues/191 |
| 187 | # "rules_webtesting": { |
| 188 | # "git_repository": "https://github.com/bazelbuild/rules_webtesting.git", |
| 189 | # "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/rules_webtesting-postsubmit.json" |
| 190 | # }, |
| 191 | "skydoc": { |
| 192 | "git_repository": "https://github.com/bazelbuild/skydoc.git", |
| 193 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/skydoc-postsubmit.json" |
| 194 | }, |
| 195 | "subpar": { |
| 196 | "git_repository": "https://github.com/google/subpar.git", |
| 197 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/subpar-postsubmit.json" |
| 198 | }, |
| 199 | "TensorFlow": { |
| 200 | "git_repository": "https://github.com/tensorflow/tensorflow.git", |
| 201 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/tensorflow-postsubmit.json" |
| 202 | }, |
| 203 | "TensorFlow Serving": { |
| 204 | "git_repository": "https://github.com/tensorflow/serving.git", |
| 205 | "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/pipelines/tensorflow-serving-postsubmit.json" |
| 206 | } |
| 207 | } |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 208 | |
| 209 | |
Philipp Wollermann | bcedf9b | 2018-02-19 18:07:44 +0100 | [diff] [blame] | 210 | def python_binary(platform=None): |
| 211 | if platform == "windows": |
| 212 | return "python.exe" |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 213 | return "python3.6" |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 214 | |
| 215 | |
| 216 | def bazelcipy_url(): |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 217 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 218 | URL to the latest version of this script. |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 219 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 220 | return "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/buildkite/bazelci.py" |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 221 | |
| 222 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 223 | def platforms_info(): |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 224 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 225 | Returns a map containing all supported platform names as keys, with the |
| 226 | values being the platform name in a human readable format, and a the |
| 227 | buildkite-agent's working directory. |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 228 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 229 | return { |
| 230 | "ubuntu1404": { |
| 231 | "name": "Ubuntu 14.04", |
Philipp Wollermann | 1c03636 | 2018-02-19 17:16:31 +0100 | [diff] [blame] | 232 | "agent-directory": "/var/lib/buildkite-agent/builds/${BUILDKITE_AGENT_NAME}" |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 233 | }, |
| 234 | "ubuntu1604": { |
| 235 | "name": "Ubuntu 16.04", |
Philipp Wollermann | 1c03636 | 2018-02-19 17:16:31 +0100 | [diff] [blame] | 236 | "agent-directory": "/var/lib/buildkite-agent/builds/${BUILDKITE_AGENT_NAME}" |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 237 | }, |
| 238 | "macos": { |
| 239 | "name": "macOS", |
Philipp Wollermann | 1c03636 | 2018-02-19 17:16:31 +0100 | [diff] [blame] | 240 | "agent-directory": "/usr/local/var/buildkite-agent/builds/${BUILDKITE_AGENT_NAME}" |
| 241 | }, |
| 242 | "windows": { |
| 243 | "name": "Windows", |
| 244 | "agent-directory": "d:/build/${BUILDKITE_AGENT_NAME}", |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 245 | } |
| 246 | } |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 247 | |
Jakob Buchgraber | 6db0f26 | 2018-02-17 15:45:54 +0100 | [diff] [blame] | 248 | |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 249 | def flaky_test_meme_url(): |
Jakob Buchgraber | f910746 | 2018-02-20 00:18:39 +0100 | [diff] [blame] | 250 | urls = ["https://storage.googleapis.com/bazel-buildkite-memes/flaky_tests_1.jpg", |
Jakob Buchgraber | ad6692e | 2018-02-20 11:12:00 +0100 | [diff] [blame] | 251 | "https://storage.googleapis.com/bazel-buildkite-memes/flaky_tests_2.jpg"] |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 252 | return random.choice(urls) |
| 253 | |
| 254 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 255 | def downstream_projects_root(platform): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 256 | downstream_projects_dir = os.path.expandvars( |
| 257 | "${BUILDKITE_ORGANIZATION_SLUG}-downstream-projects") |
| 258 | path = os.path.join(agent_directory(platform), downstream_projects_dir) |
| 259 | if not os.path.exists(path): |
| 260 | os.makedirs(path) |
| 261 | return path |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 262 | |
| 263 | |
| 264 | def agent_directory(platform): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 265 | return os.path.expandvars(platforms_info()[platform]["agent-directory"]) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 266 | |
| 267 | |
| 268 | def supported_platforms(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 269 | return set(platforms_info().keys()) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 270 | |
| 271 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 272 | def fetch_configs(http_url): |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 273 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 274 | If specified fetches the build configuration from http_url, else tries to |
| 275 | read it from .bazelci/config.json. |
| 276 | Returns the json configuration as a python data structure. |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 277 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 278 | if http_url is None: |
| 279 | with open(".bazelci/config.json", "r") as fd: |
| 280 | return json.load(fd) |
| 281 | with urllib.request.urlopen(http_url) as resp: |
| 282 | reader = codecs.getreader("utf-8") |
| 283 | return json.load(reader(resp)) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 284 | |
Jakob Buchgraber | 9c83de7 | 2018-02-18 15:32:44 +0100 | [diff] [blame] | 285 | |
Jakob Buchgraber | 3120f7a | 2018-02-18 13:28:02 +0100 | [diff] [blame] | 286 | def print_collapsed_group(name): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 287 | eprint("\n--- {0}\n".format(name)) |
Jakob Buchgraber | 3120f7a | 2018-02-18 13:28:02 +0100 | [diff] [blame] | 288 | |
Jakob Buchgraber | 9c83de7 | 2018-02-18 15:32:44 +0100 | [diff] [blame] | 289 | |
Jakob Buchgraber | 3120f7a | 2018-02-18 13:28:02 +0100 | [diff] [blame] | 290 | def print_expanded_group(name): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 291 | eprint("\n+++ {0}\n".format(name)) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 292 | |
Jakob Buchgraber | 9c83de7 | 2018-02-18 15:32:44 +0100 | [diff] [blame] | 293 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 294 | def execute_commands(config, platform, git_repository, use_but, save_but, |
| 295 | build_only, test_only): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 296 | fail_pipeline = False |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 297 | tmpdir = None |
| 298 | bazel_binary = "bazel" |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 299 | commit = os.getenv("BUILDKITE_COMMIT") |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 300 | try: |
| 301 | if git_repository: |
| 302 | clone_git_repository(git_repository, platform) |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 303 | cleanup() |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 304 | tmpdir = tempfile.mkdtemp() |
| 305 | if use_but: |
| 306 | print_collapsed_group("Downloading Bazel under test") |
| 307 | bazel_binary = download_bazel_binary(tmpdir, platform) |
| 308 | print_bazel_version_info(bazel_binary) |
| 309 | execute_shell_commands(config.get("shell_commands", None)) |
| 310 | execute_bazel_run(bazel_binary, config.get("run_targets", None)) |
| 311 | if not test_only: |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 312 | build_bep_file = os.path.join(tmpdir, "build_bep.json") |
| 313 | if is_pull_request(): |
| 314 | update_pull_request_build_status(git_repository, commit, "pending", None) |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 315 | try: |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 316 | execute_bazel_build(bazel_binary, platform, config.get("build_flags", []), |
| 317 | config.get("build_targets", None), build_bep_file) |
| 318 | if is_pull_request(): |
| 319 | invocation_id = bes_invocation_id(build_bep_file) |
| 320 | update_pull_request_build_status(git_repository, commit, "success", invocation_id) |
| 321 | if save_but: |
| 322 | upload_bazel_binary() |
| 323 | except BazelBuildFailedException: |
| 324 | if is_pull_request() |
| 325 | invocation_id = bes_invocation_id(build_bep_file) |
| 326 | update_pull_request_build_status(git_repository, commit, "failure", invocation_id) |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 327 | fail_pipeline = True |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 328 | if not fail_pipeline and not build_only: |
| 329 | test_bep_file = os.path.join(tmpdir, "test_bep.json") |
| 330 | try: |
| 331 | if is_pull_request(): |
| 332 | update_pull_request_test_status(git_repository, commit, "pending", None, 0, 0, 0) |
| 333 | execute_bazel_test(bazel_binary, platform, config.get("test_flags", []), |
| 334 | config.get("test_targets", None), test_bep_file) |
| 335 | if is_pull_request(): |
| 336 | invocation_id = bes_invocation_id(test_bep_file) |
| 337 | update_pull_request_test_status(git_repository, commit, "success", invocation_id, 0, 0, 0) |
| 338 | except BazelTestFailedException: |
| 339 | if is_pull_request(): |
| 340 | invocation_id = bes_invocation_id(test_bep_file) |
| 341 | update_pull_request_test_status(git_repository, commit, "success", invocation_id, 1, 1, 1) |
| 342 | fail_pipeline = True |
| 343 | print_test_summary(test_bep_file) |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 344 | |
| 345 | # Fail the pipeline if there were any flaky tests. |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 346 | if has_flaky_tests(test_bep_file): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 347 | fail_pipeline = True |
| 348 | |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 349 | upload_test_logs(test_bep_file, tmpdir) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 350 | finally: |
| 351 | if tmpdir: |
| 352 | shutil.rmtree(tmpdir) |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 353 | cleanup() |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 354 | |
| 355 | if fail_pipeline: |
| 356 | raise BuildkiteException("At least one test failed or was flaky.") |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 357 | |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 358 | |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 359 | def fetch_github_token(): |
| 360 | execute_command( |
| 361 | ["gsutil", "cp", "gs://bazel-encrypted-secrets/github-token.enc", "github-token.enc"]) |
| 362 | return subprocess.check_output(["gcloud", "kms", "decrypt", "--location", "global", "--keyring", "buildkite", |
| 363 | "--key", "github-token", "--ciphertext-file", "github-token.enc", |
| 364 | "--plaintext-file", "-"]).decode("utf-8").strip() |
| 365 | |
| 366 | |
| 367 | def owner_repository_from_url(git_repository): |
| 368 | m = re.search(r"/([^/]+)/([^/]+)\.git$", repository_url) |
| 369 | owner = m.group(1) |
| 370 | repository = m.group(2) |
| 371 | return (owner, repository) |
| 372 | |
| 373 | |
| 374 | def update_pull_request_status(git_repository, commit, state, invocation_id, description, context): |
| 375 | gh = login(token=fetch_github_token()) |
| 376 | owner, repo = owner_repository_from_url(git_repository) |
| 377 | repo = gh.repository(owner=owner, repository=repo) |
| 378 | results_url = "https://source.cloud.google.com/results/invocations/" + invocation_id |
| 379 | repo.create_status(sha=commit, state=state, target_url=results_url, description=description, context=context) |
| 380 | |
| 381 | |
| 382 | def update_pull_request_build_status(git_repository, commit, state, invocation_id): |
| 383 | description = "" |
| 384 | if state == "pending": |
| 385 | description = "Running ..." |
| 386 | elif state == "failure": |
| 387 | description = "Failed" |
| 388 | elif state == "success": |
| 389 | description = "Succeeded" |
| 390 | update_pull_request_status(git_repository, commit, state, invocation_id, description, "bazel build") |
| 391 | |
| 392 | |
| 393 | def update_pull_request_test_status(repository_url, commit, state, invocation_id, failed, timed_out, |
| 394 | flaky): |
| 395 | description = "" |
| 396 | if failed == 0 and timed_out == 0 and flaky == 0: |
| 397 | description = "All Tests Passed" |
| 398 | else: |
| 399 | description = "{0} tests failed, {1} tests timed out, {2} tests are flaky".format(failed, timed_out, flaky) |
| 400 | update_pull_request_status(repository_url, commit, state, invocation_id, description, "bazel test") |
| 401 | |
| 402 | |
| 403 | def is_pull_request(): |
| 404 | third_party_repo = pos.getenv("BUILDKITE_PULL_REQUEST_REPO", "") |
| 405 | return len(third_party_repo) > 0 |
| 406 | |
| 407 | |
| 408 | def bes_invocation_id(bep_file): |
| 409 | targets = [] |
| 410 | raw_data = "" |
| 411 | with open(bep_file) as f: |
| 412 | raw_data = f.read() |
| 413 | decoder = json.JSONDecoder() |
| 414 | |
| 415 | pos = 0 |
| 416 | while pos < len(raw_data): |
| 417 | bep_obj, size = decoder.raw_decode(raw_data[pos:]) |
| 418 | if "started" in bep_obj: |
| 419 | return bep_obj["started"]["uuid"] |
| 420 | pos += size + 1 |
| 421 | return None |
| 422 | |
| 423 | |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 424 | def show_image(url, alt): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 425 | eprint("\033]1338;url='\"{0}\"';alt='\"{1}\"'\a\n".format(url, alt)) |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 426 | |
| 427 | |
Jakob Buchgraber | ca19f78 | 2018-02-19 22:40:43 +0100 | [diff] [blame] | 428 | def print_test_summary(bep_file): |
| 429 | failed = test_logs_for_status(bep_file, status="FAILED") |
| 430 | if failed: |
| 431 | print_expanded_group("Failed Tests") |
| 432 | for label, _ in failed: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 433 | eprint(label) |
Jakob Buchgraber | ca19f78 | 2018-02-19 22:40:43 +0100 | [diff] [blame] | 434 | timed_out = test_logs_for_status(bep_file, status="TIMEOUT") |
Jakob Buchgraber | 039d9ed | 2018-02-20 15:30:05 +0100 | [diff] [blame] | 435 | if timed_out: |
Jakob Buchgraber | ca19f78 | 2018-02-19 22:40:43 +0100 | [diff] [blame] | 436 | print_expanded_group("Timed out Tests") |
Jakob Buchgraber | 622d652 | 2018-02-20 00:05:53 +0100 | [diff] [blame] | 437 | for label, _ in timed_out: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 438 | eprint(label) |
Jakob Buchgraber | ca19f78 | 2018-02-19 22:40:43 +0100 | [diff] [blame] | 439 | flaky = test_logs_for_status(bep_file, status="FLAKY") |
| 440 | if flaky: |
| 441 | print_expanded_group("Flaky Tests") |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 442 | show_image(flaky_test_meme_url(), "Flaky Tests") |
Jakob Buchgraber | 622d652 | 2018-02-20 00:05:53 +0100 | [diff] [blame] | 443 | for label, _ in flaky: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 444 | eprint(label) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 445 | |
Jakob Buchgraber | 257693b | 2018-02-20 00:03:56 +0100 | [diff] [blame] | 446 | |
Jakob Buchgraber | 02e0722 | 2018-02-19 15:05:56 +0100 | [diff] [blame] | 447 | def has_flaky_tests(bep_file): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 448 | return len(test_logs_for_status(bep_file, status="FLAKY")) > 0 |
Jakob Buchgraber | 02e0722 | 2018-02-19 15:05:56 +0100 | [diff] [blame] | 449 | |
| 450 | |
Jakob Buchgraber | 7e690a7 | 2018-02-18 13:22:15 +0100 | [diff] [blame] | 451 | def print_bazel_version_info(bazel_binary): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 452 | print_collapsed_group("Bazel Info") |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 453 | execute_command([bazel_binary, "version"]) |
| 454 | execute_command([bazel_binary, "info"]) |
Jakob Buchgraber | 7e690a7 | 2018-02-18 13:22:15 +0100 | [diff] [blame] | 455 | |
| 456 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 457 | def upload_bazel_binary(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 458 | print_collapsed_group("Uploading Bazel under test") |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 459 | execute_command(["buildkite-agent", "artifact", "upload", "bazel-bin/src/bazel"]) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 460 | |
| 461 | |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 462 | def download_bazel_binary(dest_dir, platform): |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 463 | source_step = create_label(platform, "Bazel", build_only=True) |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 464 | execute_command(["buildkite-agent", "artifact", "download", |
| 465 | "bazel-bin/src/bazel", dest_dir, "--step", source_step]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 466 | bazel_binary_path = os.path.join(dest_dir, "bazel-bin/src/bazel") |
| 467 | st = os.stat(bazel_binary_path) |
| 468 | os.chmod(bazel_binary_path, st.st_mode | stat.S_IEXEC) |
| 469 | return bazel_binary_path |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 470 | |
| 471 | |
| 472 | def clone_git_repository(git_repository, platform): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 473 | root = downstream_projects_root(platform) |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 474 | project_name = re.search(r"/([^/]+)\.git$", git_repository).group(1) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 475 | clone_path = os.path.join(root, project_name) |
| 476 | print_collapsed_group("Fetching " + project_name + " sources") |
| 477 | if os.path.exists(clone_path): |
| 478 | os.chdir(clone_path) |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 479 | execute_command(["git", "remote", "set-url", "origin", git_repository]) |
| 480 | execute_command(["git", "clean", "-fdqx"]) |
| 481 | execute_command(["git", "submodule", "foreach", "--recursive", "git", "clean", "-fdqx"]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 482 | # sync to the latest commit of HEAD. Unlikely git pull this also works after |
| 483 | # a force push. |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 484 | execute_command(["git", "fetch", "origin"]) |
| 485 | remote_head = subprocess.check_output(["git", "symbolic-ref", "refs/remotes/origin/HEAD"]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 486 | remote_head = remote_head.decode("utf-8") |
| 487 | remote_head = remote_head.rstrip() |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 488 | execute_command(["git", "reset", remote_head, "--hard"]) |
| 489 | execute_command(["git", "submodule", "sync", "--recursive"]) |
| 490 | execute_command(["git", "submodule", "update", "--init", "--recursive", "--force"]) |
| 491 | execute_command(["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"]) |
| 492 | execute_command(["git", "clean", "-fdqx"]) |
| 493 | execute_command(["git", "submodule", "foreach", "--recursive", "git", "clean", "-fdqx"]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 494 | else: |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 495 | execute_command(["git", "clone", "--recurse-submodules", git_repository, clone_path]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 496 | os.chdir(clone_path) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 497 | |
| 498 | |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 499 | def cleanup(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 500 | print_collapsed_group("Cleanup") |
| 501 | if os.path.exists("WORKSPACE"): |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 502 | execute_command(["bazel", "clean", "--expunge"]) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 503 | |
Jakob Buchgraber | fd1eaca | 2018-02-17 17:27:14 +0100 | [diff] [blame] | 504 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 505 | def execute_shell_commands(commands): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 506 | if not commands: |
| 507 | return |
| 508 | print_collapsed_group("Setup (Shell Commands)") |
| 509 | shell_command = "\n".join(commands) |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 510 | execute_command([shell_command], shell=True) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 511 | |
| 512 | |
| 513 | def execute_bazel_run(bazel_binary, targets): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 514 | if not targets: |
| 515 | return |
| 516 | print_collapsed_group("Setup (Run Targets)") |
| 517 | for target in targets: |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 518 | execute_command([bazel_binary, "run", "--curses=yes", |
Jakob Buchgraber | 9965953 | 2018-02-19 17:38:50 +0100 | [diff] [blame] | 519 | "--color=yes", "--verbose_failures", target]) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 520 | |
| 521 | |
Jakob Buchgraber | 4f1d271 | 2018-02-20 10:22:47 +0100 | [diff] [blame] | 522 | def remote_caching_flags(platform): |
Jakob Buchgraber | 3af5293 | 2018-02-20 20:28:24 +0100 | [diff] [blame] | 523 | common_flags = ["--bes_backend=buildeventservice.googleapis.com", "--bes_best_effort=false", |
Jakob Buchgraber | cfb03e4 | 2018-02-20 20:32:36 +0100 | [diff] [blame] | 524 | "--bes_timeout=10s", "--tls_enabled", "--project_id=bazel-public", |
Jakob Buchgraber | 3af5293 | 2018-02-20 20:28:24 +0100 | [diff] [blame] | 525 | "--remote_instance_name=projects/bazel-public", |
Jakob Buchgraber | 039d9ed | 2018-02-20 15:30:05 +0100 | [diff] [blame] | 526 | "--experimental_remote_spawn_cache", |
Jakob Buchgraber | 848b88e | 2018-02-21 12:57:45 +0100 | [diff] [blame] | 527 | "--remote_timeout=10", "--remote_cache=remotebuildexecution.googleapis.com", |
Jakob Buchgraber | 3af5293 | 2018-02-20 20:28:24 +0100 | [diff] [blame] | 528 | "--experimental_remote_platform_override=properties:{name:\"platform\" value:\"" + platform + "\"}"] |
Jakob Buchgraber | c38a719 | 2018-02-20 12:56:52 +0100 | [diff] [blame] | 529 | if platform in ["ubuntu1404", "ubuntu1604"]: |
| 530 | return common_flags + ["--google_default_credentials"] |
| 531 | elif platform == "macos": |
| 532 | return common_flags + ["--google_credentials=/Users/ci/GoogleDrive/bazel-public-e29b1f995cb1.json"] |
Jakob Buchgraber | 4f1d271 | 2018-02-20 10:22:47 +0100 | [diff] [blame] | 533 | return [] |
| 534 | |
| 535 | |
Jakob Buchgraber | b4342cd | 2018-02-20 16:35:07 +0100 | [diff] [blame] | 536 | def remote_enabled(flags): |
| 537 | # Detect if the project configuration enabled its own remote caching / execution. |
| 538 | remote_flags = ["--remote_executor", "--remote_cache", "--remote_http_cache"] |
| 539 | for flag in flags: |
| 540 | for remote_flag in remote_flags: |
| 541 | if flag.startswith(remote_flag): |
| 542 | return True |
| 543 | return False |
| 544 | |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 545 | |
| 546 | def execute_bazel_build(bazel_binary, platform, flags, targets, bep_file): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 547 | if not targets: |
| 548 | return |
| 549 | print_expanded_group("Build") |
| 550 | num_jobs = str(multiprocessing.cpu_count()) |
Jakob Buchgraber | ad6692e | 2018-02-20 11:12:00 +0100 | [diff] [blame] | 551 | common_flags = ["--show_progress_rate_limit=5", "--curses=yes", "--color=yes", "--keep_going", |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 552 | "--jobs=" + num_jobs], "--build_event_json_file=" + bep_file, |
| 553 | "--experimental_build_event_json_file_path_conversion=false"] |
Jakob Buchgraber | b4342cd | 2018-02-20 16:35:07 +0100 | [diff] [blame] | 554 | caching_flags = [] |
| 555 | if not remote_enabled(flags): |
| 556 | caching_flags = remote_caching_flags(platform) |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 557 | try: |
| 558 | execute_command([bazel_binary, "build"] + common_flags + caching_flags + flags + targets) |
| 559 | except subprocess.CalledProcessError as e: |
| 560 | raise BazelBuildFailedException("bazel build failed with exit code {}".format(e.returncode)) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 561 | |
| 562 | |
Jakob Buchgraber | d220429 | 2018-02-20 10:25:40 +0100 | [diff] [blame] | 563 | def execute_bazel_test(bazel_binary, platform, flags, targets, bep_file): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 564 | if not targets: |
Philipp Wollermann | 556cf1e | 2018-02-21 15:02:34 +0100 | [diff] [blame] | 565 | return |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 566 | print_expanded_group("Test") |
| 567 | num_jobs = str(multiprocessing.cpu_count()) |
Jakob Buchgraber | 77175c7 | 2018-02-20 15:32:07 +0100 | [diff] [blame] | 568 | common_flags = ["--show_progress_rate_limit=5", "--curses=yes", "--color=yes", "--keep_going", |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 569 | "--flaky_test_attempts=3", "--build_tests_only", |
| 570 | "--jobs=" + num_jobs, "--local_test_jobs=" + num_jobs, |
Jakob Buchgraber | 77595f1 | 2018-02-21 11:44:18 +0100 | [diff] [blame] | 571 | "--build_event_json_file=" + bep_file, |
| 572 | "--experimental_build_event_json_file_path_conversion=false"] |
Jakob Buchgraber | b4342cd | 2018-02-20 16:35:07 +0100 | [diff] [blame] | 573 | caching_flags = [] |
| 574 | if not remote_enabled(flags): |
| 575 | caching_flags = remote_caching_flags(platform) |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 576 | try: |
| 577 | execute_command([bazel_binary, "test"] + common_flags + caching_flags + flags + targets) |
| 578 | except subprocess.CalledProcessError as e: |
| 579 | raise BazelTestFailedException("bazel test failed with exit code {}".format(e.returncode)) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 580 | |
| 581 | |
Jakob Buchgraber | 02e0722 | 2018-02-19 15:05:56 +0100 | [diff] [blame] | 582 | def upload_test_logs(bep_file, tmpdir): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 583 | if not os.path.exists(bep_file): |
| 584 | return |
| 585 | test_logs = test_logs_to_upload(bep_file, tmpdir) |
| 586 | if test_logs: |
| 587 | cwd = os.getcwd() |
| 588 | try: |
| 589 | os.chdir(tmpdir) |
| 590 | print_collapsed_group("Uploading test logs") |
Jakob Buchgraber | 175b521 | 2018-02-19 21:18:21 +0100 | [diff] [blame] | 591 | execute_command(["buildkite-agent", "artifact", "upload", "*/**/*.log"]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 592 | finally: |
| 593 | os.chdir(cwd) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 594 | |
| 595 | |
Jakob Buchgraber | 02e0722 | 2018-02-19 15:05:56 +0100 | [diff] [blame] | 596 | def test_logs_to_upload(bep_file, tmpdir): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 597 | failed = test_logs_for_status(bep_file, status="FAILED") |
| 598 | timed_out = test_logs_for_status(bep_file, status="TIMEOUT") |
| 599 | flaky = test_logs_for_status(bep_file, status="FLAKY") |
| 600 | # Rename the test.log files to the target that created them |
| 601 | # so that it's easy to associate test.log and target. |
| 602 | new_paths = [] |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 603 | for label, test_logs in failed + timed_out + flaky: |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 604 | attempt = 0 |
| 605 | if len(test_logs) > 1: |
| 606 | attempt = 1 |
| 607 | for test_log in test_logs: |
Jakob Buchgraber | 070ef5f | 2018-02-20 17:57:14 +0100 | [diff] [blame] | 608 | try: |
| 609 | new_path = test_label_to_path(tmpdir, label, attempt) |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 610 | eprint("new_path: " + new_path) |
Jakob Buchgraber | 070ef5f | 2018-02-20 17:57:14 +0100 | [diff] [blame] | 611 | os.makedirs(os.path.dirname(new_path), exist_ok=True) |
Jakob Buchgraber | 070ef5f | 2018-02-20 17:57:14 +0100 | [diff] [blame] | 612 | copyfile(test_log, new_path) |
Jakob Buchgraber | 070ef5f | 2018-02-20 17:57:14 +0100 | [diff] [blame] | 613 | new_paths.append(new_path) |
Philipp Wollermann | c030f2e | 2018-02-21 17:02:19 +0100 | [diff] [blame] | 614 | attempt += 1 |
Jakob Buchgraber | 070ef5f | 2018-02-20 17:57:14 +0100 | [diff] [blame] | 615 | except IOError as err: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 616 | # Log error and ignore. |
| 617 | eprint(err) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 618 | return new_paths |
Jakob Buchgraber | 699aece | 2018-02-19 12:49:30 +0100 | [diff] [blame] | 619 | |
| 620 | |
Jakob Buchgraber | 02e0722 | 2018-02-19 15:05:56 +0100 | [diff] [blame] | 621 | def test_label_to_path(tmpdir, label, attempt): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 622 | # remove leading // |
| 623 | path = label[2:] |
Philipp Wollermann | c030f2e | 2018-02-21 17:02:19 +0100 | [diff] [blame] | 624 | path = path.replace("/", os.sep) |
| 625 | path = path.replace(":", os.sep) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 626 | if attempt == 0: |
| 627 | path = os.path.join(path, "test.log") |
| 628 | else: |
| 629 | path = os.path.join(path, "attempt_" + str(attempt) + ".log") |
| 630 | return os.path.join(tmpdir, path) |
Jakob Buchgraber | 699aece | 2018-02-19 12:49:30 +0100 | [diff] [blame] | 631 | |
| 632 | |
Jakob Buchgraber | 02e0722 | 2018-02-19 15:05:56 +0100 | [diff] [blame] | 633 | def test_logs_for_status(bep_file, status): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 634 | targets = [] |
| 635 | raw_data = "" |
| 636 | with open(bep_file) as f: |
| 637 | raw_data = f.read() |
| 638 | decoder = json.JSONDecoder() |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 639 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 640 | pos = 0 |
| 641 | while pos < len(raw_data): |
| 642 | bep_obj, size = decoder.raw_decode(raw_data[pos:]) |
Jakob Buchgraber | 45e3863 | 2018-02-19 17:27:08 +0100 | [diff] [blame] | 643 | if "testSummary" in bep_obj: |
| 644 | test_target = bep_obj["id"]["testSummary"]["label"] |
| 645 | test_status = bep_obj["testSummary"]["overallStatus"] |
| 646 | if test_status == status: |
| 647 | outputs = bep_obj["testSummary"]["failed"] |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 648 | test_logs = [] |
| 649 | for output in outputs: |
Philipp Wollermann | c030f2e | 2018-02-21 17:02:19 +0100 | [diff] [blame] | 650 | test_logs.append(url2pathname(urlparse(output["uri"]).path)) |
Jakob Buchgraber | 45e3863 | 2018-02-19 17:27:08 +0100 | [diff] [blame] | 651 | targets.append((test_target, test_logs)) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 652 | pos += size + 1 |
| 653 | return targets |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 654 | |
| 655 | |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 656 | def execute_command(args, shell=False, fail_if_nonzero=True): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 657 | eprint(" ".join(args)) |
| 658 | return subprocess.run(args, shell=shell, check=fail_if_nonzero).returncode |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 659 | |
| 660 | |
| 661 | def print_project_pipeline(platform_configs, project_name, http_config, |
| 662 | git_repository, use_but): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 663 | pipeline_steps = [] |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 664 | for platform, _ in platform_configs.items(): |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 665 | step = runner_step(platform, project_name, http_config, git_repository, use_but) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 666 | pipeline_steps.append(step) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 667 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 668 | print_pipeline(pipeline_steps) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 669 | |
| 670 | |
| 671 | def runner_step(platform, project_name=None, http_config=None, |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 672 | git_repository=None, use_but=False): |
Philipp Wollermann | bcedf9b | 2018-02-19 18:07:44 +0100 | [diff] [blame] | 673 | command = python_binary(platform) + " bazelci.py runner --platform=" + platform |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 674 | if http_config: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 675 | command += " --http_config=" + http_config |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 676 | if git_repository: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 677 | command += " --git_repository=" + git_repository |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 678 | if use_but: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 679 | command += " --use_but" |
| 680 | label = create_label(platform, project_name) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 681 | return """ |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 682 | - label: \"{0}\" |
| 683 | command: \"{1}\\n{2}\" |
| 684 | agents: |
| 685 | - \"os={3}\"""".format(label, fetch_bazelcipy_command(), command, platform) |
| 686 | |
| 687 | |
| 688 | def print_pipeline(steps): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 689 | print("steps:") |
| 690 | for step in steps: |
| 691 | print(step) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 692 | |
| 693 | |
| 694 | def wait_step(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 695 | return """ |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 696 | - wait""" |
| 697 | |
| 698 | |
| 699 | def http_config_flag(http_config): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 700 | if http_config is not None: |
| 701 | return "--http_config=" + http_config |
| 702 | return "" |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 703 | |
| 704 | |
| 705 | def fetch_bazelcipy_command(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 706 | return "curl -s {0} -o bazelci.py".format(bazelcipy_url()) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 707 | |
| 708 | |
| 709 | def upload_project_pipeline_step(project_name, git_repository, http_config): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 710 | pipeline_command = ("{0} bazelci.py project_pipeline --project_name=\\\"{1}\\\" " + |
| 711 | "--use_but --git_repository={2}").format(python_binary(), project_name, |
| 712 | git_repository) |
| 713 | if http_config: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 714 | pipeline_command += " --http_config=" + http_config |
| 715 | pipeline_command += " | buildkite-agent pipeline upload" |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 716 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 717 | return """ |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 718 | - label: \"Setup {0}\" |
| 719 | command: \"{1}\\n{2}\" |
| 720 | agents: |
| 721 | - \"pipeline=true\"""".format(project_name, fetch_bazelcipy_command(), |
| 722 | pipeline_command) |
| 723 | |
| 724 | |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 725 | def create_label(platform, project_name, build_only=False, test_only=False): |
| 726 | if build_only and test_only: |
| 727 | raise BuildkiteException("build_only and test_only cannot be true at the same time") |
| 728 | platform_name = platforms_info()[platform]["name"] |
| 729 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 730 | if build_only: |
| 731 | label = "Build " |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 732 | elif test_only: |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 733 | label = "Test " |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 734 | else: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 735 | label = "" |
| 736 | |
| 737 | if project_name: |
| 738 | label += "{0} ({1})".format(project_name, platform_name) |
| 739 | else: |
| 740 | label += platform_name |
| 741 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 742 | return label |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 743 | |
| 744 | |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 745 | def bazel_build_step(platform, project_name, http_config=None, build_only=False, test_only=False): |
Philipp Wollermann | bcedf9b | 2018-02-19 18:07:44 +0100 | [diff] [blame] | 746 | pipeline_command = python_binary(platform) + " bazelci.py runner" |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 747 | if build_only: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 748 | pipeline_command += " --build_only --save_but" |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 749 | if test_only: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 750 | pipeline_command += " --test_only" |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 751 | if http_config: |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 752 | pipeline_command += " --http_config=" + http_config |
| 753 | label = create_label(platform, project_name, build_only, test_only) |
| 754 | pipeline_command += " --platform=" + platform |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 755 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 756 | return """ |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 757 | - label: \"{0}\" |
| 758 | command: \"{1}\\n{2}\" |
| 759 | agents: |
| 760 | - \"os={3}\"""".format(label, fetch_bazelcipy_command(), |
| 761 | pipeline_command, platform) |
| 762 | |
| 763 | |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 764 | def publish_bazel_binaries_step(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 765 | command = python_binary() + " bazelci.py publish_binaries" |
| 766 | return """ |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 767 | - label: \"Publish Bazel Binaries\" |
| 768 | command: \"{0}\\n{1}\" |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 769 | agents: |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 770 | - \"pipeline=true\"""".format(fetch_bazelcipy_command(), command) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 771 | |
| 772 | |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 773 | def print_bazel_postsubmit_pipeline(configs, http_config): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 774 | if not configs: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 775 | raise BuildkiteException("Bazel postsubmit pipeline configuration is empty.") |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 776 | if set(configs.keys()) != set(supported_platforms()): |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 777 | raise BuildkiteException("Bazel postsubmit pipeline needs to build Bazel on all supported platforms.") |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 778 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 779 | pipeline_steps = [] |
| 780 | for platform, config in configs.items(): |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 781 | pipeline_steps.append(bazel_build_step(platform, "Bazel", http_config, build_only=True)) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 782 | pipeline_steps.append(wait_step()) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 783 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 784 | # todo move this to the end with a wait step. |
| 785 | pipeline_steps.append(publish_bazel_binaries_step()) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 786 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 787 | for platform, config in configs.items(): |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 788 | pipeline_steps.append(bazel_build_step(platform, "Bazel", http_config, test_only=True)) |
| 789 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 790 | for project, config in downstream_projects().items(): |
| 791 | git_repository = config["git_repository"] |
| 792 | http_config = config.get("http_config", None) |
| 793 | pipeline_steps.append(upload_project_pipeline_step(project, |
| 794 | git_repository, http_config)) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 795 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 796 | print_pipeline(pipeline_steps) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 797 | |
| 798 | |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 799 | def bazelci_builds_download_url(platform, build_number): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 800 | return "https://storage.googleapis.com/bazel-builds/artifacts/{0}/{1}/bazel".format(platform, build_number) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 801 | |
| 802 | |
| 803 | def bazelci_builds_upload_url(platform, build_number): |
Jakob Buchgraber | 2a2304f | 2018-02-19 21:29:44 +0100 | [diff] [blame] | 804 | return "gs://bazel-builds/artifacts/{0}/{1}/bazel".format(platform, build_number) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 805 | |
| 806 | |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 807 | def bazelci_builds_metadata_url(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 808 | return "gs://bazel-builds/metadata/latest_fully_tested.json" |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 809 | |
| 810 | |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 811 | def latest_generation_and_build_number(): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 812 | output = None |
| 813 | attempt = 0 |
| 814 | while attempt < 5: |
| 815 | output = subprocess.check_output( |
| 816 | ["gsutil", "stat", bazelci_builds_metadata_url()]) |
| 817 | match = re.search("Generation:[ ]*([0-9]+)", output.decode("utf-8")) |
| 818 | if not match: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 819 | raise BuildkiteException("Couldn't parse generation. gsutil output format changed?") |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 820 | generation = match.group(1) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 821 | |
Philipp Wollermann | ff39ef5 | 2018-02-21 14:18:52 +0100 | [diff] [blame] | 822 | match = re.search(r"Hash \(md5\):[ ]*([^\s]+)", output.decode("utf-8")) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 823 | if not match: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 824 | raise BuildkiteException("Couldn't parse md5 hash. gsutil output format changed?") |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 825 | expected_md5hash = base64.b64decode(match.group(1)) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 826 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 827 | output = subprocess.check_output( |
| 828 | ["gsutil", "cat", bazelci_builds_metadata_url()]) |
| 829 | hasher = hashlib.md5() |
| 830 | hasher.update(output) |
| 831 | actual_md5hash = hasher.digest() |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 832 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 833 | if expected_md5hash == actual_md5hash: |
| 834 | break |
Philipp Wollermann | f6be466 | 2018-02-21 14:48:28 +0100 | [diff] [blame] | 835 | attempt += 1 |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 836 | info = json.loads(output.decode("utf-8")) |
| 837 | return (generation, info["build_number"]) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 838 | |
Jakob Buchgraber | 699aece | 2018-02-19 12:49:30 +0100 | [diff] [blame] | 839 | |
Jakob Buchgraber | 88083fd | 2018-02-18 17:23:35 +0100 | [diff] [blame] | 840 | def sha256_hexdigest(filename): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 841 | sha256 = hashlib.sha256() |
| 842 | with open(filename, 'rb') as f: |
| 843 | for block in iter(lambda: f.read(65536), b''): |
| 844 | sha256.update(block) |
| 845 | return sha256.hexdigest() |
Jakob Buchgraber | 699aece | 2018-02-19 12:49:30 +0100 | [diff] [blame] | 846 | |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 847 | |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 848 | def try_publish_binaries(build_number, expected_generation): |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 849 | tmpdir = tempfile.mkdtemp() |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 850 | try: |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 851 | info = { |
| 852 | "build_number": build_number, |
| 853 | "git_commit": os.environ["BUILDKITE_COMMIT"], |
| 854 | "platforms": {} |
| 855 | } |
| 856 | for platform in supported_platforms(): |
| 857 | bazel_binary_path = download_bazel_binary(tmpdir, platform) |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 858 | execute_command(["gsutil", "cp", "-a", "public-read", bazel_binary_path, |
| 859 | bazelci_builds_upload_url(platform, build_number)]) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 860 | info["platforms"][platform] = { |
| 861 | "url": bazelci_builds_download_url(platform, build_number), |
| 862 | "sha256": sha256_hexdigest(bazel_binary_path), |
| 863 | } |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 864 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 865 | info_file = os.path.join(tmpdir, "info.json") |
| 866 | with open(info_file, mode="w", encoding="utf-8") as fp: |
| 867 | json.dump(info, fp) |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 868 | |
| 869 | try: |
| 870 | execute_command([ |
| 871 | "gsutil", |
| 872 | "-h", "x-goog-if-generation-match:" + expected_generation, |
| 873 | "-h", "Content-Type:application/json", |
| 874 | "cp", "-a", "public-read", |
| 875 | info_file, bazelci_builds_metadata_url()]) |
| 876 | except subprocess.CalledProcessError: |
| 877 | raise BinaryUploadRaceException() |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 878 | finally: |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 879 | shutil.rmtree(tmpdir) |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 880 | |
| 881 | |
Jakob Buchgraber | 76381e0 | 2018-02-19 16:19:56 +0100 | [diff] [blame] | 882 | def publish_binaries(): |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 883 | """ |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 884 | Publish Bazel binaries to GCS. |
Philipp Wollermann | db02486 | 2018-02-19 17:16:56 +0100 | [diff] [blame] | 885 | """ |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 886 | current_build_number = os.environ.get("BUILDKITE_BUILD_NUMBER", None) |
| 887 | if not current_build_number: |
| 888 | raise BuildkiteException("Not running inside Buildkite") |
| 889 | current_build_number = int(current_build_number) |
| 890 | |
| 891 | for _ in range(5): |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 892 | latest_generation, latest_build_number = latest_generation_and_build_number() |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 893 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 894 | if current_build_number <= latest_build_number: |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 895 | eprint(("Current build '{0}' is not newer than latest published '{1}'. " + |
| 896 | "Skipping publishing of binaries.").format(current_build_number, |
| 897 | latest_build_number)) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 898 | break |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 899 | |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 900 | try: |
| 901 | try_publish_binaries(current_build_number, latest_generation) |
| 902 | except BinaryUploadRaceException: |
| 903 | # Retry. |
| 904 | continue |
| 905 | |
| 906 | eprint("Successfully updated '{0}' to binaries from build {1}." |
| 907 | .format(bazelci_builds_metadata_url(), current_build_number)) |
| 908 | break |
| 909 | else: |
| 910 | raise BuildkiteException("Could not publish binaries, ran out of attempts.") |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 911 | |
| 912 | |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 913 | def main(argv=None): |
| 914 | if argv is None: |
| 915 | argv = sys.argv |
| 916 | |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 917 | parser = argparse.ArgumentParser(description='Bazel Continuous Integration Script') |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 918 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 919 | subparsers = parser.add_subparsers(dest="subparsers_name") |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 920 | |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 921 | bazel_postsubmit_pipeline = subparsers.add_parser("bazel_postsubmit_pipeline") |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 922 | bazel_postsubmit_pipeline.add_argument("--http_config", type=str) |
| 923 | bazel_postsubmit_pipeline.add_argument("--git_repository", type=str) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 924 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 925 | project_pipeline = subparsers.add_parser("project_pipeline") |
| 926 | project_pipeline.add_argument("--project_name", type=str) |
| 927 | project_pipeline.add_argument("--http_config", type=str) |
| 928 | project_pipeline.add_argument("--git_repository", type=str) |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 929 | project_pipeline.add_argument("--use_but", type=bool, nargs="?", const=True) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 930 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 931 | runner = subparsers.add_parser("runner") |
Philipp Wollermann | 3e1a771 | 2018-02-19 17:34:24 +0100 | [diff] [blame] | 932 | runner.add_argument("--platform", action="store", choices=list(supported_platforms())) |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 933 | runner.add_argument("--http_config", type=str) |
| 934 | runner.add_argument("--git_repository", type=str) |
| 935 | runner.add_argument("--use_but", type=bool, nargs="?", const=True) |
| 936 | runner.add_argument("--save_but", type=bool, nargs="?", const=True) |
| 937 | runner.add_argument("--build_only", type=bool, nargs="?", const=True) |
| 938 | runner.add_argument("--test_only", type=bool, nargs="?", const=True) |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 939 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 940 | runner = subparsers.add_parser("publish_binaries") |
Jakob Buchgraber | d20ffeb | 2018-02-18 03:16:43 +0100 | [diff] [blame] | 941 | |
Philipp Wollermann | 598b4a4 | 2018-02-19 17:03:36 +0100 | [diff] [blame] | 942 | args = parser.parse_args() |
Jakob Buchgraber | 0b6a39d | 2018-02-17 00:45:14 +0100 | [diff] [blame] | 943 | |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 944 | try: |
| 945 | if args.subparsers_name == "bazel_postsubmit_pipeline": |
| 946 | configs = fetch_configs(args.http_config) |
| 947 | print_bazel_postsubmit_pipeline(configs.get("platforms", None), args.http_config) |
| 948 | elif args.subparsers_name == "project_pipeline": |
| 949 | configs = fetch_configs(args.http_config) |
| 950 | print_project_pipeline(configs.get("platforms", None), args.project_name, |
| 951 | args.http_config, args.git_repository, args.use_but) |
| 952 | elif args.subparsers_name == "runner": |
| 953 | configs = fetch_configs(args.http_config) |
| 954 | execute_commands(configs.get("platforms", None)[args.platform], |
| 955 | args.platform, args.git_repository, args.use_but, args.save_but, |
| 956 | args.build_only, args.test_only) |
| 957 | elif args.subparsers_name == "publish_binaries": |
| 958 | publish_binaries() |
| 959 | else: |
| 960 | parser.print_help() |
| 961 | return 2 |
| 962 | except BuildkiteException as e: |
| 963 | eprint(str(e)) |
| 964 | return 1 |
| 965 | return 0 |
| 966 | |
Jakob Buchgraber | 95e3d57 | 2018-02-21 18:48:49 +0100 | [diff] [blame^] | 967 | |
Philipp Wollermann | dcaddd9 | 2018-02-21 14:13:43 +0100 | [diff] [blame] | 968 | if __name__ == "__main__": |
| 969 | sys.exit(main()) |