blob: 4827b829ce3ed02c059b5ea6a05648a482aacb75 [file] [log] [blame]
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01001#!/usr/bin/env python3
2#
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003# 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 Buchgraber0b6a39d2018-02-17 00:45:14 +010017import argparse
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +010018import base64
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010019import codecs
Florian Weikert78e67de2024-02-06 12:58:41 +010020import collections
21import concurrent.futures
Yun Peng5a1a9442022-01-11 14:26:48 +010022import copy
Jakob Buchgraber12807052018-02-25 17:04:56 +010023import datetime
Philipp Wollermann2b4ee9f2021-02-11 16:32:35 +010024from glob import glob
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +010025import hashlib
Yun Peng5a1a9442022-01-11 14:26:48 +010026import itertools
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010027import json
Jakob Buchgraber6db0f262018-02-17 15:45:54 +010028import multiprocessing
Philipp Wollermann0a04cf32018-02-21 17:07:22 +010029import os
Philipp Wollermanndcaddd92018-02-21 14:13:43 +010030import os.path
Florian Weikertda94a102022-10-21 12:24:37 +020031import platform as platform_module
Jakob Buchgraber257693b2018-02-20 00:03:56 +010032import random
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010033import re
Yun Peng9337bb32020-02-28 13:31:29 +010034import requests
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010035import shutil
Philipp Wollermanndcaddd92018-02-21 14:13:43 +010036import stat
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010037import subprocess
38import sys
Florian Weikert5e70d9d2023-05-08 19:20:23 +020039import tarfile
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010040import tempfile
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +020041import threading
Philipp Wollermanne1318eb2018-08-13 15:08:01 +020042import time
Florian Weikert78e67de2024-02-06 12:58:41 +010043from typing import Sequence
Philipp Wollermannce986af2019-07-18 14:46:05 +020044import urllib.error
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +010045import urllib.request
Jakob Buchgraber25bb50f2018-02-22 18:06:21 +010046import yaml
Philipp Wollermanndcaddd92018-02-21 14:13:43 +010047
48# Initialize the random number generator.
49random.seed()
50
Philipp Wollermanne67eec42019-05-24 15:18:20 +020051BUILDKITE_ORG = os.environ["BUILDKITE_ORGANIZATION_SLUG"]
Florian Weikertc2745512021-02-17 16:13:55 +010052THIS_IS_PRODUCTION = BUILDKITE_ORG == "bazel"
Philipp Wollermanne67eec42019-05-24 15:18:20 +020053THIS_IS_TESTING = BUILDKITE_ORG == "bazel-testing"
54THIS_IS_TRUSTED = BUILDKITE_ORG == "bazel-trusted"
55THIS_IS_SPARTA = True
56
57CLOUD_PROJECT = "bazel-public" if THIS_IS_TRUSTED else "bazel-untrusted"
58
59GITHUB_BRANCH = {"bazel": "master", "bazel-trusted": "master", "bazel-testing": "testing"}[
60 BUILDKITE_ORG
61]
62
Florian Weikerte1ca35d2024-01-25 13:46:16 +010063SCRIPT_URL = "https://raw.githubusercontent.com/bazelbuild/continuous-integration/{}/buildkite/bazelci.py".format(
64 GITHUB_BRANCH
Philipp Wollermannc05ac682019-01-19 12:37:28 +010065)
Jakob Buchgraber95e3d572018-02-21 18:48:49 +010066
Philipp Wollermanne67eec42019-05-24 15:18:20 +020067AGGREGATE_INCOMPATIBLE_TEST_RESULT_URL = "https://raw.githubusercontent.com/bazelbuild/continuous-integration/{}/buildkite/aggregate_incompatible_flags_test_result.py?{}".format(
68 GITHUB_BRANCH, int(time.time())
69)
70
71EMERGENCY_FILE_URL = "https://raw.githubusercontent.com/bazelbuild/continuous-integration/{}/buildkite/emergency.yml?{}".format(
72 GITHUB_BRANCH, int(time.time())
73)
74
75FLAKY_TESTS_BUCKET = {
76 "bazel-testing": "gs://bazel-testing-buildkite-stats/flaky-tests-bep/",
77 "bazel-trusted": "gs://bazel-buildkite-stats/flaky-tests-bep/",
78 "bazel": "gs://bazel-buildkite-stats/flaky-tests-bep/",
79}[BUILDKITE_ORG]
80
Chi Wangb2b65682020-08-27 10:36:15 +080081KZIPS_BUCKET = {
82 "bazel-testing": "gs://bazel-kzips-testing/",
83 "bazel-trusted": "gs://bazel-kzips/",
84 "bazel": "gs://bazel-kzips/",
85}[BUILDKITE_ORG]
86
Florian Weikert78e67de2024-02-06 12:58:41 +010087# We don't collect logs in the trusted org
88LOG_BUCKET = {
89 "bazel-testing": "https://storage.googleapis.com/bazel-testing-buildkite-artifacts",
90 "bazel": "https://storage.googleapis.com/bazel-untrusted-buildkite-artifacts",
91}[BUILDKITE_ORG]
92
Florian Weikert797787b2019-12-19 15:33:07 +010093# Projects can opt out of receiving GitHub issues from --notify by adding `"do_not_notify": True` to their respective downstream entry.
Philipp Wollermanne67eec42019-05-24 15:18:20 +020094DOWNSTREAM_PROJECTS_PRODUCTION = {
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +010095 "Android Studio Plugin": {
96 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +010097 "file_config": ".bazelci/android-studio.yml",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +010098 "pipeline_slug": "android-studio-plugin",
99 },
Mai Hussien0731b4d2022-06-28 11:16:07 -0700100 "Android Studio Plugin Google": {
101 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100102 "file_config": ".bazelci/android-studio.yml",
Mai Hussien0731b4d2022-06-28 11:16:07 -0700103 "pipeline_slug": "android-studio-plugin-google",
104 },
Yun Peng996efad2018-11-27 17:19:44 +0100105 "Android Testing": {
106 "git_repository": "https://github.com/googlesamples/android-testing.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100107 "file_config": "bazelci/buildkite-pipeline.yml",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100108 "pipeline_slug": "android-testing",
Yun Peng9b1d3432021-12-07 10:40:45 +0100109 "disabled_reason": "https://github.com/android/testing-samples/issues/417",
Yun Peng996efad2018-11-27 17:19:44 +0100110 },
Yun Peng8910fa32019-01-03 08:58:16 +0100111 "Bazel": {
112 "git_repository": "https://github.com/bazelbuild/bazel.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100113 "file_config": ".bazelci/postsubmit.yml",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100114 "pipeline_slug": "bazel-bazel",
Yun Peng8910fa32019-01-03 08:58:16 +0100115 },
Tobias Werthd848eca2019-05-14 15:08:35 +0200116 "Bazel Bench": {
117 "git_repository": "https://github.com/bazelbuild/bazel-bench.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100118 "file_config": ".bazelci/postsubmit.yml",
Tobias Werthd848eca2019-05-14 15:08:35 +0200119 "pipeline_slug": "bazel-bench",
120 },
Philipp Wollermannfefcbf42019-05-28 14:28:40 +0200121 "Bazel Codelabs": {
122 "git_repository": "https://github.com/bazelbuild/codelabs.git",
Philipp Wollermannfefcbf42019-05-28 14:28:40 +0200123 "pipeline_slug": "bazel-codelabs",
Yun Peng88d80ae2020-11-19 16:23:49 +0100124 "disabled_reason": "https://github.com/bazelbuild/codelabs/issues/38",
Philipp Wollermannfefcbf42019-05-28 14:28:40 +0200125 },
Jinfce9b302019-08-08 15:18:26 -0400126 "Bazel Examples": {
127 "git_repository": "https://github.com/bazelbuild/examples.git",
Jinfce9b302019-08-08 15:18:26 -0400128 "pipeline_slug": "bazel-bazel-examples",
129 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100130 "Bazel Remote Cache": {
131 "git_repository": "https://github.com/buchgr/bazel-remote.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100132 "pipeline_slug": "bazel-remote-cache",
Yun Peng996efad2018-11-27 17:19:44 +0100133 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100134 "Bazel skylib": {
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200135 "git_repository": "https://github.com/bazelbuild/bazel-skylib.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100136 "pipeline_slug": "bazel-skylib",
Yun Peng667750b2020-02-20 14:06:43 +0100137 "owned_by_bazel": True,
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200138 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100139 "Bazel toolchains": {
140 "git_repository": "https://github.com/bazelbuild/bazel-toolchains.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100141 "pipeline_slug": "bazel-toolchains",
142 },
143 "Bazel watcher": {
144 "git_repository": "https://github.com/bazelbuild/bazel-watcher.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100145 "pipeline_slug": "bazel-watcher",
Florian Weikertecf091c2023-04-28 10:22:23 +0200146 "disabled_reason": "https://github.com/bazelbuild/bazel-watcher/issues/590",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100147 },
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200148 "Bazelisk": {
149 "git_repository": "https://github.com/bazelbuild/bazelisk.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100150 "file_config": ".bazelci/config.yml",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200151 "pipeline_slug": "bazelisk",
152 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100153 "Buildfarm": {
154 "git_repository": "https://github.com/bazelbuild/bazel-buildfarm.git",
Philipp Wollermann89e5d882021-09-09 12:44:29 +0200155 "pipeline_slug": "buildfarm-farmer",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100156 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100157 "Buildtools": {
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200158 "git_repository": "https://github.com/bazelbuild/buildtools.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100159 "pipeline_slug": "buildtools",
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200160 },
Yun Peng39a42582018-11-09 10:59:47 +0100161 "CLion Plugin": {
162 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100163 "file_config": ".bazelci/clion.yml",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100164 "pipeline_slug": "clion-plugin",
Yun Peng39a42582018-11-09 10:59:47 +0100165 },
Mai Hussien0731b4d2022-06-28 11:16:07 -0700166 "CLion Plugin Google": {
167 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100168 "file_config": ".bazelci/clion.yml",
Mai Hussien0731b4d2022-06-28 11:16:07 -0700169 "pipeline_slug": "clion-plugin-google",
170 },
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200171 "Cartographer": {
172 "git_repository": "https://github.com/googlecartographer/cartographer.git",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200173 "pipeline_slug": "cartographer",
Xùdōng Yáng51788892023-11-09 23:41:55 +0100174 "disabled_reason": "https://github.com/cartographer-project/cartographer/issues/1938#issuecomment-1804795113",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200175 },
Philipp Wollermannee850782019-02-05 22:56:04 +0100176 "Cloud Robotics Core": {
Stefan Sauerb4dd3f92019-02-05 22:44:28 +0100177 "git_repository": "https://github.com/googlecloudrobotics/core.git",
Philipp Wollermanndd8cf922021-10-01 15:43:33 +0200178 "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/pipelines/cloud-robotics.yml",
Stefan Sauerb4dd3f92019-02-05 22:44:28 +0100179 "pipeline_slug": "cloud-robotics-core",
180 },
Keith Smiley3b0ba602019-05-15 04:42:19 -0700181 "Envoy": {
182 "git_repository": "https://github.com/envoyproxy/envoy.git",
Keith Smiley3b0ba602019-05-15 04:42:19 -0700183 "pipeline_slug": "envoy",
Gowroji Sunilb5da6c22023-11-10 16:07:40 +0530184 "disabled_reason": "https://github.com/envoyproxy/envoy/issues/29723",
Keith Smiley3b0ba602019-05-15 04:42:19 -0700185 },
Florian Weikert1fe28b72019-07-02 12:47:55 +0200186 "FlatBuffers": {
187 "git_repository": "https://github.com/google/flatbuffers.git",
Florian Weikert1fe28b72019-07-02 12:47:55 +0200188 "pipeline_slug": "flatbuffers",
Gowroji Sunil7192ed02023-10-05 18:06:02 +0530189 "disabled_reason": "https://github.com/google/flatbuffers/issues/7992",
Florian Weikert1fe28b72019-07-02 12:47:55 +0200190 },
Philipp Wollermannf3750fa2019-05-21 17:11:59 +0200191 "Flogger": {
192 "git_repository": "https://github.com/google/flogger.git",
Philipp Wollermanndd8cf922021-10-01 15:43:33 +0200193 "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/pipelines/flogger.yml",
Philipp Wollermannf3750fa2019-05-21 17:11:59 +0200194 "pipeline_slug": "flogger",
Gowroji Sunil3358b922024-01-25 18:17:17 +0530195 "disabled_reason": "https://github.com/bazelbuild/continuous-integration/issues/1588#issuecomment-1874630563",
Philipp Wollermannf3750fa2019-05-21 17:11:59 +0200196 },
Marcel Hlopkoc8840772018-10-23 12:51:46 +0200197 "Gerrit": {
198 "git_repository": "https://gerrit.googlesource.com/gerrit.git",
Philipp Wollermanndd8cf922021-10-01 15:43:33 +0200199 "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/pipelines/gerrit.yml",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100200 "pipeline_slug": "gerrit",
Marcel Hlopkoc8840772018-10-23 12:51:46 +0200201 },
Yun Pengd6622022018-11-05 13:10:26 +0100202 "Google Logging": {
203 "git_repository": "https://github.com/google/glog.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100204 "pipeline_slug": "google-logging",
Yun Pengd6622022018-11-05 13:10:26 +0100205 },
Yun Peng9586db52018-11-02 10:48:40 +0100206 "IntelliJ Plugin": {
207 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100208 "file_config": ".bazelci/intellij.yml",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100209 "pipeline_slug": "intellij-plugin",
Yun Peng9586db52018-11-02 10:48:40 +0100210 },
Mai Hussien0731b4d2022-06-28 11:16:07 -0700211 "IntelliJ Plugin Google": {
212 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100213 "file_config": ".bazelci/intellij.yml",
Mai Hussien0731b4d2022-06-28 11:16:07 -0700214 "pipeline_slug": "intellij-plugin-google",
215 },
216 "IntelliJ UE Plugin": {
217 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100218 "file_config": ".bazelci/intellij-ue.yml",
Mai Hussien0731b4d2022-06-28 11:16:07 -0700219 "pipeline_slug": "intellij-ue-plugin",
220 },
221 "IntelliJ UE Plugin Google": {
222 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100223 "file_config": ".bazelci/intellij-ue.yml",
Mai Hussien0731b4d2022-06-28 11:16:07 -0700224 "pipeline_slug": "intellij-ue-plugin-google",
225 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100226 "IntelliJ Plugin Aspect": {
227 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100228 "file_config": ".bazelci/aspect.yml",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100229 "pipeline_slug": "intellij-plugin-aspect",
230 },
Mai Hussien0731b4d2022-06-28 11:16:07 -0700231 "IntelliJ Plugin Aspect Google": {
232 "git_repository": "https://github.com/bazelbuild/intellij.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100233 "file_config": ".bazelci/aspect.yml",
Mai Hussien0731b4d2022-06-28 11:16:07 -0700234 "pipeline_slug": "intellij-plugin-aspect-google",
235 },
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200236 "Kythe": {
237 "git_repository": "https://github.com/kythe/kythe.git",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200238 "pipeline_slug": "kythe",
239 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100240 "Protobuf": {
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200241 "git_repository": "https://github.com/google/protobuf.git",
Philipp Wollermanndd8cf922021-10-01 15:43:33 +0200242 "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/pipelines/protobuf.yml",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100243 "pipeline_slug": "protobuf",
Yun Peng667750b2020-02-20 14:06:43 +0100244 "owned_by_bazel": True,
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200245 },
Laurent Le Brunf6326d62020-07-28 18:24:10 +0200246 "Stardoc": {
247 "git_repository": "https://github.com/bazelbuild/stardoc.git",
Laurent Le Brunf6326d62020-07-28 18:24:10 +0200248 "pipeline_slug": "stardoc",
Yun Peng667750b2020-02-20 14:06:43 +0100249 "owned_by_bazel": True,
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200250 },
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200251 "TensorFlow": {
252 "git_repository": "https://github.com/tensorflow/tensorflow.git",
Philipp Wollermanndd8cf922021-10-01 15:43:33 +0200253 "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/pipelines/tensorflow.yml",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200254 "pipeline_slug": "tensorflow",
Yun Pengab700d52023-05-08 13:16:44 +0200255 "disabled_reason": "https://github.com/tensorflow/tensorflow/issues/60508",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200256 },
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200257 "re2": {
258 "git_repository": "https://github.com/google/re2.git",
Philipp Wollermanndd8cf922021-10-01 15:43:33 +0200259 "http_config": "https://raw.githubusercontent.com/bazelbuild/continuous-integration/master/pipelines/re2.yml",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200260 "pipeline_slug": "re2",
261 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100262 "rules_android": {
263 "git_repository": "https://github.com/bazelbuild/rules_android.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100264 "pipeline_slug": "rules-android",
Gowroji Sunilec5c8a92024-02-05 14:47:38 +0530265 "disabled_reason": "https://github.com/bazelbuild/rules_android/issues/187",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100266 },
Keith Smiley2e3d7c82022-09-09 00:47:51 -0700267 "rules_android_ndk": {
Yun Peng687ad6b2022-09-15 11:00:02 +0200268 "git_repository": "https://github.com/bazelbuild/rules_android_ndk.git",
Keith Smiley2e3d7c82022-09-09 00:47:51 -0700269 "pipeline_slug": "rules-android-ndk",
270 },
Yun Peng809f27b2018-11-13 10:15:39 +0100271 "rules_apple": {
272 "git_repository": "https://github.com/bazelbuild/rules_apple.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100273 "pipeline_slug": "rules-apple-darwin",
Yun Peng809f27b2018-11-13 10:15:39 +0100274 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100275 "rules_cc": {
276 "git_repository": "https://github.com/bazelbuild/rules_cc.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100277 "pipeline_slug": "rules-cc",
Yun Peng667750b2020-02-20 14:06:43 +0100278 "owned_by_bazel": True,
Gowroji Sunil3358b922024-01-25 18:17:17 +0530279 "disabled_reason": "https://github.com/bazelbuild/rules_cc/issues/190",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100280 },
Marcel Hlopko340dfd22018-10-19 11:33:01 +0200281 "rules_closure": {
282 "git_repository": "https://github.com/bazelbuild/rules_closure.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100283 "pipeline_slug": "rules-closure-closure-compiler",
Yun Peng667750b2020-02-20 14:06:43 +0100284 "owned_by_bazel": True,
Marcel Hlopko340dfd22018-10-19 11:33:01 +0200285 },
Florian Weikert5569c112021-04-01 14:04:33 +0200286 "rules_dotnet": {
287 "git_repository": "https://github.com/bazelbuild/rules_dotnet.git",
Florian Weikert5569c112021-04-01 14:04:33 +0200288 "pipeline_slug": "rules-dotnet-edge",
289 },
Yun Peng996efad2018-11-27 17:19:44 +0100290 "rules_foreign_cc": {
291 "git_repository": "https://github.com/bazelbuild/rules_foreign_cc.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100292 "file_config": ".bazelci/config.yaml",
Yun Peng996efad2018-11-27 17:19:44 +0100293 "pipeline_slug": "rules-foreign-cc",
Yun Peng667750b2020-02-20 14:06:43 +0100294 "owned_by_bazel": True,
Yun Peng996efad2018-11-27 17:19:44 +0100295 },
Xindb02c012018-11-07 14:10:54 -0500296 "rules_go": {
297 "git_repository": "https://github.com/bazelbuild/rules_go.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100298 "pipeline_slug": "rules-go-golang",
Yun Pengb7247ff2018-11-15 13:52:39 +0100299 },
Yun Peng7deea572018-11-05 10:47:45 +0100300 "rules_groovy": {
Yun Peng996efad2018-11-27 17:19:44 +0100301 "git_repository": "https://github.com/bazelbuild/rules_groovy.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100302 "pipeline_slug": "rules-groovy",
Yun Peng996efad2018-11-27 17:19:44 +0100303 },
304 "rules_gwt": {
305 "git_repository": "https://github.com/bazelbuild/rules_gwt.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100306 "pipeline_slug": "rules-gwt",
Xùdōng Yáng99b86b32021-08-18 23:42:05 +1000307 "disabled_reason": "https://github.com/bazelbuild/continuous-integration/issues/1202",
Philipp Wollermann2a160432019-09-19 15:57:28 +0200308 },
Florian Weikertff6444e2019-09-16 16:08:57 +0200309 "rules_haskell": {
310 "git_repository": "https://github.com/tweag/rules_haskell.git",
Florian Weikertff6444e2019-09-16 16:08:57 +0200311 "pipeline_slug": "rules-haskell-haskell",
Philipp Wollermann2a160432019-09-19 15:57:28 +0200312 },
Yun Peng996efad2018-11-27 17:19:44 +0100313 "rules_jsonnet": {
314 "git_repository": "https://github.com/bazelbuild/rules_jsonnet.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100315 "pipeline_slug": "rules-jsonnet",
Gowroji Sunile2b6b062024-01-29 18:35:59 +0530316 "disabled_reason": "https://github.com/bazelbuild/rules_jsonnet/issues/173",
Yun Peng996efad2018-11-27 17:19:44 +0100317 },
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200318 "rules_jvm_external": {
319 "git_repository": "https://github.com/bazelbuild/rules_jvm_external.git",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200320 "pipeline_slug": "rules-jvm-external",
Yun Peng667750b2020-02-20 14:06:43 +0100321 "owned_by_bazel": True,
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200322 },
323 "rules_jvm_external - examples": {
324 "git_repository": "https://github.com/bazelbuild/rules_jvm_external.git",
Yun Pengfb86f672023-11-10 16:18:55 +0100325 "file_config": ".bazelci/examples.yml",
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200326 "pipeline_slug": "rules-jvm-external-examples",
Yun Peng667750b2020-02-20 14:06:43 +0100327 "owned_by_bazel": True,
Philipp Wollermann1dc76992019-05-28 16:42:51 +0200328 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100329 "rules_kotlin": {
330 "git_repository": "https://github.com/bazelbuild/rules_kotlin.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100331 "pipeline_slug": "rules-kotlin-kotlin",
332 },
Yun Penga5650e12018-11-14 10:16:06 +0100333 "rules_nodejs": {
334 "git_repository": "https://github.com/bazelbuild/rules_nodejs.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100335 "pipeline_slug": "rules-nodejs-nodejs",
Yun Penga5650e12018-11-14 10:16:06 +0100336 },
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200337 "rules_perl": {
338 "git_repository": "https://github.com/bazelbuild/rules_perl.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100339 "pipeline_slug": "rules-perl",
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200340 },
Yannic6110b3c2019-08-12 15:09:37 +0000341 "rules_proto": {
342 "git_repository": "https://github.com/bazelbuild/rules_proto.git",
Yannic6110b3c2019-08-12 15:09:37 +0000343 "pipeline_slug": "rules-proto",
Yun Peng667750b2020-02-20 14:06:43 +0100344 "owned_by_bazel": True,
Yannic6110b3c2019-08-12 15:09:37 +0000345 },
Yun Peng3d5a8a62018-11-19 11:42:01 +0100346 "rules_python": {
347 "git_repository": "https://github.com/bazelbuild/rules_python.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100348 "pipeline_slug": "rules-python-python",
Yun Peng667750b2020-02-20 14:06:43 +0100349 "owned_by_bazel": True,
Yun Peng3d5a8a62018-11-19 11:42:01 +0100350 },
Xindb02c012018-11-07 14:10:54 -0500351 "rules_rust": {
352 "git_repository": "https://github.com/bazelbuild/rules_rust.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100353 "pipeline_slug": "rules-rust-rustlang",
Xindb02c012018-11-07 14:10:54 -0500354 },
Yun Pengca62fff2018-10-31 11:22:03 +0100355 "rules_sass": {
356 "git_repository": "https://github.com/bazelbuild/rules_sass.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100357 "pipeline_slug": "rules-sass",
Gowroji Sunil13187912024-01-23 15:47:06 +0530358 "disabled_reason": "https://github.com/bazelbuild/rules_sass/issues/153",
Yun Pengca62fff2018-10-31 11:22:03 +0100359 },
Xindb02c012018-11-07 14:10:54 -0500360 "rules_scala": {
361 "git_repository": "https://github.com/bazelbuild/rules_scala.git",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100362 "pipeline_slug": "rules-scala-scala",
Xùdōng Yáng79deb042022-09-15 12:26:36 +0200363 "disabled_reason": "waiting on https://github.com/bazelbuild/rules_scala/pull/1422",
Xindb02c012018-11-07 14:10:54 -0500364 },
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100365 "rules_swift": {
366 "git_repository": "https://github.com/bazelbuild/rules_swift.git",
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100367 "pipeline_slug": "rules-swift-swift",
368 },
Richard Levasseura9690c62023-03-06 08:39:36 -0800369 "rules_testing": {
370 "git_repository": "https://github.com/bazelbuild/rules_testing.git",
Richard Levasseura9690c62023-03-06 08:39:36 -0800371 "pipeline_slug": "rules-testing",
372 "owned_by_bazel": True,
373 },
Yun Peng996efad2018-11-27 17:19:44 +0100374 "rules_webtesting": {
375 "git_repository": "https://github.com/bazelbuild/rules_webtesting.git",
Yun Peng996efad2018-11-27 17:19:44 +0100376 "pipeline_slug": "rules-webtesting-saucelabs",
Yun Peng996efad2018-11-27 17:19:44 +0100377 },
Philipp Wollermann389acd82019-05-21 17:41:48 +0200378 "upb": {
379 "git_repository": "https://github.com/protocolbuffers/upb.git",
Philipp Wollermann389acd82019-05-21 17:41:48 +0200380 "pipeline_slug": "upb",
Yun Pengab700d52023-05-08 13:16:44 +0200381 "disabled_reason": "https://github.com/protocolbuffers/upb/issues/1290",
Philipp Wollermann389acd82019-05-21 17:41:48 +0200382 },
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200383}
384
Philipp Wollermanne67eec42019-05-24 15:18:20 +0200385DOWNSTREAM_PROJECTS_TESTING = {
Philipp Wollermannbed211d2019-06-07 11:38:59 +0200386 "Bazel": DOWNSTREAM_PROJECTS_PRODUCTION["Bazel"],
387 "Bazelisk": DOWNSTREAM_PROJECTS_PRODUCTION["Bazelisk"],
Philipp Wollermannbed211d2019-06-07 11:38:59 +0200388 "rules_go": DOWNSTREAM_PROJECTS_PRODUCTION["rules_go"],
389 "rules_groovy": DOWNSTREAM_PROJECTS_PRODUCTION["rules_groovy"],
390 "rules_kotlin": DOWNSTREAM_PROJECTS_PRODUCTION["rules_kotlin"],
391 "rules_nodejs": DOWNSTREAM_PROJECTS_PRODUCTION["rules_nodejs"],
392 "rules_rust": DOWNSTREAM_PROJECTS_PRODUCTION["rules_rust"],
393 "rules_scala": DOWNSTREAM_PROJECTS_PRODUCTION["rules_scala"],
Philipp Wollermanne67eec42019-05-24 15:18:20 +0200394}
395
396DOWNSTREAM_PROJECTS = {
397 "bazel-testing": DOWNSTREAM_PROJECTS_TESTING,
398 "bazel-trusted": {},
399 "bazel": DOWNSTREAM_PROJECTS_PRODUCTION,
400}[BUILDKITE_ORG]
Philipp Wollermann6dd7aa32019-02-05 22:42:15 +0100401
Philipp Wollermann81a88412019-07-12 10:34:33 +0200402DOCKER_REGISTRY_PREFIX = {
403 "bazel-testing": "bazel-public/testing",
404 "bazel-trusted": "bazel-public",
405 "bazel": "bazel-public",
406}[BUILDKITE_ORG]
407
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200408# A map containing all supported platform names as keys, with the values being
409# the platform name in a human readable format, and a the buildkite-agent's
410# working directory.
411PLATFORMS = {
Philipp Wollermanneffcd6e2019-06-21 18:30:34 +0200412 "centos7": {
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200413 "name": "CentOS 7 (OpenJDK 8, gcc 4.8.5)",
414 "emoji-name": ":centos: 7 (OpenJDK 8, gcc 4.8.5)",
Philipp Wollermann28978712021-10-21 19:09:29 +0200415 "publish_binary": [],
Philipp Wollermann5d6765d2020-02-17 17:12:02 +0100416 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/centos7-java8",
Philipp Wollermanneffcd6e2019-06-21 18:30:34 +0200417 "python": "python3.6",
418 },
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200419 "centos7_java11": {
420 "name": "CentOS 7 (OpenJDK 11, gcc 4.8.5)",
421 "emoji-name": ":centos: 7 (OpenJDK 11, gcc 4.8.5)",
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200422 "publish_binary": [],
423 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/centos7-java11",
424 "python": "python3.6",
425 },
426 "centos7_java11_devtoolset10": {
427 "name": "CentOS 7 (OpenJDK 11, gcc 10.2.1)",
428 "emoji-name": ":centos: 7 (OpenJDK 11, gcc 10.2.1)",
Philipp Wollermann28978712021-10-21 19:09:29 +0200429 "publish_binary": ["ubuntu1404", "centos7", "linux"],
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200430 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/centos7-java11-devtoolset10",
431 "python": "python3.6",
432 },
Philipp Wollermann67fc3712019-06-12 15:39:21 +0200433 "debian10": {
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200434 "name": "Debian 10 Buster (OpenJDK 11, gcc 8.3.0)",
435 "emoji-name": ":debian: 10 Buster (OpenJDK 11, gcc 8.3.0)",
Philipp Wollermann67fc3712019-06-12 15:39:21 +0200436 "publish_binary": [],
Philipp Wollermann5d6765d2020-02-17 17:12:02 +0100437 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/debian10-java11",
Philipp Wollermann67fc3712019-06-12 15:39:21 +0200438 "python": "python3.7",
439 },
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200440 "debian11": {
441 "name": "Debian 11 Bullseye (OpenJDK 17, gcc 10.2.1)",
Zhongpeng Lin1377b622023-07-04 09:19:08 -0700442 "emoji-name": ":debian: 11 Bullseye (OpenJDK 17, gcc 10.2.1)",
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200443 "publish_binary": [],
444 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/debian11-java17",
445 "python": "python3.9",
446 },
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200447 "ubuntu1604": {
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200448 "name": "Ubuntu 16.04 LTS (OpenJDK 8, gcc 5.4.0)",
449 "emoji-name": ":ubuntu: 16.04 LTS (OpenJDK 8, gcc 5.4.0)",
Philipp Wollermann64047082021-10-21 21:26:25 +0200450 "publish_binary": [],
Philipp Wollermann5d6765d2020-02-17 17:12:02 +0100451 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/ubuntu1604-java8",
Philipp Wollermann57b32682019-05-18 22:09:27 +0200452 "python": "python3.6",
Philipp Wollermann438ec242018-09-05 14:39:24 +0200453 },
454 "ubuntu1804": {
Florian Weikertd02ea362022-09-15 13:51:17 +0200455 "name": "Ubuntu 18.04 LTS (OpenJDK 11, gcc 7.5.0)",
456 "emoji-name": ":ubuntu: 18.04 LTS (OpenJDK 11, gcc 7.5.0)",
Yun Peng539b17f2023-10-18 10:36:58 +0200457 "publish_binary": [],
Philipp Wollermann5d6765d2020-02-17 17:12:02 +0100458 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/ubuntu1804-java11",
Philipp Wollermann57b32682019-05-18 22:09:27 +0200459 "python": "python3.6",
Philipp Wollermann438ec242018-09-05 14:39:24 +0200460 },
Mostyn Bramley-Mooreab4599e2020-06-23 20:31:01 +0200461 "ubuntu2004": {
Florian Weikertd02ea362022-09-15 13:51:17 +0200462 "name": "Ubuntu 20.04 LTS (OpenJDK 11, gcc 9.4.0)",
463 "emoji-name": ":ubuntu: 20.04 LTS (OpenJDK 11, gcc 9.4.0)",
Philipp Wollermann55f72ac2020-09-21 22:22:05 +0200464 "publish_binary": [],
Mostyn Bramley-Mooreab4599e2020-06-23 20:31:01 +0200465 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/ubuntu2004-java11",
466 "python": "python3.8",
467 },
Yun Peng07dafc52022-03-16 13:23:35 +0100468 "ubuntu2004_arm64": {
Florian Weikertd02ea362022-09-15 13:51:17 +0200469 "name": "Ubuntu 20.04 LTS ARM64 (OpenJDK 11, gcc 9.4.0)",
470 "emoji-name": ":ubuntu: 20.04 LTS ARM64 (OpenJDK 11, gcc 9.4.0)",
Yun Peng07dafc52022-03-16 13:23:35 +0100471 "publish_binary": [],
472 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/ubuntu2004-java11",
473 "python": "python3.8",
474 "queue": "arm64",
475 # TODO: Re-enable always-pull if we also publish docker containers for Linux ARM64
476 "always-pull": False,
477 },
Chi Wang6357efe2020-08-25 16:23:38 +0800478 "kythe_ubuntu2004": {
Florian Weikertd02ea362022-09-15 13:51:17 +0200479 "name": "Kythe (Ubuntu 20.04 LTS, OpenJDK 11, gcc 9.4.0)",
480 "emoji-name": "Kythe (:ubuntu: 20.04 LTS, OpenJDK 11, gcc 9.4.0)",
Chi Wang6357efe2020-08-25 16:23:38 +0800481 "publish_binary": [],
482 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/ubuntu2004-java11-kythe",
483 "python": "python3.8",
484 },
Florian Weikertdcc75202022-09-06 16:34:04 +0200485 "ubuntu2204": {
486 "name": "Ubuntu 22.04 (OpenJDK 17, gcc 11.2.0)",
487 "emoji-name": ":ubuntu: 22.04 (OpenJDK 17, gcc 11.2.0)",
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200488 "publish_binary": [],
Florian Weikertdcc75202022-09-06 16:34:04 +0200489 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/ubuntu2204-java17",
Philipp Wollermannb6a399a2021-10-22 07:57:26 +0200490 "python": "python3",
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200491 },
David Ostrovskyba4478f2023-05-08 16:24:04 +0200492 "fedora39": {
493 "name": "Fedora 39 (OpenJDK 17, gcc 13.1.1)",
494 "emoji-name": ":fedora: 39 (OpenJDK 17, gcc 13.1.1)",
David Ostrovskyba4478f2023-05-08 16:24:04 +0200495 "publish_binary": [],
496 "docker-image": f"gcr.io/{DOCKER_REGISTRY_PREFIX}/fedora39-java17",
497 "python": "python3",
498 },
Philipp Wollermann438ec242018-09-05 14:39:24 +0200499 "macos": {
Philipp Wollermann5e3d09f2021-10-21 01:03:01 +0200500 "name": "macOS (OpenJDK 11, Xcode)",
501 "emoji-name": ":darwin: (OpenJDK 11, Xcode)",
Philipp Wollermann783d1672019-06-06 13:35:30 +0200502 "publish_binary": ["macos"],
Philipp Wollermann7a185322019-05-18 22:15:48 +0200503 "queue": "macos",
Philipp Wollermann89d36492021-02-16 11:59:09 +0100504 "python": "python3",
Philipp Wollermann438ec242018-09-05 14:39:24 +0200505 },
Yun Peng3cfc36f2023-12-07 18:39:34 +0100506 "macos_v2": {
507 "name": "macOS (OpenJDK 11, Xcode)",
508 "emoji-name": ":darwin: (OpenJDK 11, Xcode)",
509 "publish_binary": [],
510 "queue": "macos_v2",
511 "python": "python3",
512 },
Florian Weikerta87e6f32023-04-14 19:58:32 +0200513 "macos_qa": {
514 "name": "macOS QA (OpenJDK 11, Xcode)",
515 "emoji-name": ":darwin: :fire_extinguisher: (OpenJDK 11, Xcode)",
Florian Weikerta87e6f32023-04-14 19:58:32 +0200516 "publish_binary": [],
517 "queue": "macos_qa",
518 "python": "python3",
519 },
Florian Weikertf4ad0922023-07-10 20:26:12 +0200520 "macos_arm64_qa": {
521 "name": "macOS arm64 QA (OpenJDK 8, Xcode)",
522 "emoji-name": ":darwin: arm64 :fire_extinguisher: (OpenJDK 8, Xcode)",
Florian Weikertf4ad0922023-07-10 20:26:12 +0200523 "publish_binary": [],
524 "queue": "macos_arm64_qa",
525 "python": "python3",
526 },
Yun Peng46d43912021-04-21 09:49:53 +0200527 "macos_arm64": {
Philipp Wollermann9af2b432021-10-20 22:37:17 +0200528 "name": "macOS arm64 (OpenJDK 8, Xcode)",
529 "emoji-name": ":darwin: arm64 (OpenJDK 8, Xcode)",
Yun Peng83f32772021-04-21 11:22:35 +0200530 "publish_binary": ["macos_arm64"],
Yun Pengab922132022-11-18 20:28:02 +0100531 "queue": "macos_arm64",
Yun Peng46d43912021-04-21 09:49:53 +0200532 "python": "python3",
533 },
Yun Peng3cfc36f2023-12-07 18:39:34 +0100534 "macos_arm64_v2": {
535 "name": "macOS arm64 (OpenJDK 8, Xcode)",
536 "emoji-name": ":darwin: arm64 (OpenJDK 8, Xcode)",
537 "publish_binary": [],
538 "queue": "macos_arm64_v2",
539 "python": "python3",
540 },
Philipp Wollermann438ec242018-09-05 14:39:24 +0200541 "windows": {
Yun Peng7dd25b52023-11-07 15:04:50 +0100542 "name": "Windows (OpenJDK 11, VS2022)",
543 "emoji-name": ":windows: (OpenJDK 11, VS2022)",
Philipp Wollermann783d1672019-06-06 13:35:30 +0200544 "publish_binary": ["windows"],
Philipp Wollermann7a185322019-05-18 22:15:48 +0200545 "queue": "windows",
Philipp Wollermann57b32682019-05-18 22:09:27 +0200546 "python": "python.exe",
Philipp Wollermann438ec242018-09-05 14:39:24 +0200547 },
Yun Peng6a7a6702022-02-02 15:02:47 +0100548 "windows_arm64": {
Yun Peng7dd25b52023-11-07 15:04:50 +0100549 "name": "Windows ARM64 (OpenJDK 11, VS2022)",
550 "emoji-name": ":windows: arm64 (OpenJDK 11, VS2022)",
Yun Penge024eb72023-07-25 10:18:39 +0200551 "publish_binary": ["windows_arm64"],
Yun Peng6a7a6702022-02-02 15:02:47 +0100552 # TODO(pcloudy): Switch to windows_arm64 queue when Windows ARM64 machines are available,
553 # current we just use x86_64 machines to do cross compile.
554 "queue": "windows",
555 "python": "python.exe",
Florian Weikert7f21ca42022-02-02 17:35:23 +0100556 },
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200557}
558
Chi Wang5a88d962023-10-19 14:44:04 +0200559# Generate rbe_ubuntu* platforms based on ubuntu* platforms.
560for platform, platform_dict in PLATFORMS.copy().items():
561 if platform.startswith("ubuntu"):
562 rbe_platform_dict = copy.deepcopy(platform_dict)
563 rbe_platform_dict["name"] = "RBE {}".format(platform_dict["name"])
564 rbe_platform_dict["emoji-name"] = "RBE {}".format(platform_dict["emoji-name"])
565 PLATFORMS["rbe_{}".format(platform)] = rbe_platform_dict
566
Philipp Wollermannfce92bf2019-05-22 15:14:32 +0200567BUILDIFIER_DOCKER_IMAGE = "gcr.io/bazel-public/buildifier"
Florian Weikertf20ae6f2019-01-16 14:32:09 +0100568
Philipp Wollermann1403d2c2019-01-10 13:15:51 +0100569# The platform used for various steps (e.g. stuff that formerly ran on the "pipeline" workers).
570DEFAULT_PLATFORM = "ubuntu1804"
571
Philipp Wollermannf4aabb72019-06-25 15:59:00 +0200572# In order to test that "the one Linux binary" that we build for our official releases actually
573# works on all Linux distributions that we test on, we use the Linux binary built on our official
574# release platform for all Linux downstream tests.
Yun Pengdae5a7d2022-01-11 16:08:58 +0100575LINUX_BINARY_PLATFORM = "centos7_java11_devtoolset10"
Philipp Wollermannf4aabb72019-06-25 15:59:00 +0200576
Philipp Wollermann380f1e62019-04-12 16:45:27 +0200577XCODE_VERSION_REGEX = re.compile(r"^\d+\.\d+(\.\d+)?$")
Florian Weikertdb832a02020-11-19 19:14:48 +0100578XCODE_VERSION_OVERRIDES = {"10.2.1": "10.3", "11.2": "11.2.1", "11.3": "11.3.1"}
Philipp Wollermann380f1e62019-04-12 16:45:27 +0200579
Florian Weikertc8642af2019-02-03 23:58:51 +0100580BUILD_LABEL_PATTERN = re.compile(r"^Build label: (\S+)$", re.MULTILINE)
581
Florian Weikertde96a6f2019-03-07 14:57:50 +0100582BUILDIFIER_STEP_NAME = "Buildifier"
583
Florian Weikert5f5d3cb2019-04-15 15:36:27 +0200584SKIP_TASKS_ENV_VAR = "CI_SKIP_TASKS"
585
Florian Weikertecf091c2023-04-28 10:22:23 +0200586# TODO: change to USE_BAZEL_DIFF once the feature has been tested in QA
Florian Weikert7012a522023-05-04 16:49:40 +0200587USE_BAZEL_DIFF_ENV_VAR = "USE_BAZEL_DIFF"
588
Florian Weikert77d20062023-05-08 16:32:48 +0200589BAZEL_DIFF_ANNOTATION_CTX = "'diff'"
590
Florian Weikert7012a522023-05-04 16:49:40 +0200591# TODO(fweikert): Install bazel-diff on the Docker images and on the Mac machines
592BAZEL_DIFF_URL = (
593 "https://github.com/Tinder/bazel-diff/releases/download/4.5.0/bazel-diff_deploy.jar"
594)
Florian Weikertecf091c2023-04-28 10:22:23 +0200595
596AUTO_DIFFBASE_VALUES = frozenset(["1", "true", "auto"])
597
Florian Weikerte417f9f2023-05-05 17:33:46 +0200598# Always run all test targets if any of the paths here are modified by the current commit.
599# Values can be directory paths (with a trailing slash) or file paths.
600DISABLE_BAZEL_DIFF_IF_MODIFIED = (".bazelci/", ".bazelversion")
601
Florian Weikertecf091c2023-04-28 10:22:23 +0200602COMMIT_RE = re.compile(r"[0-9a-z]{40}")
603
Philipp Wollermannce986af2019-07-18 14:46:05 +0200604CONFIG_FILE_EXTENSIONS = {".yml", ".yaml"}
Florian Weikert778251c2019-04-25 15:14:44 +0200605
Chi Wang6357efe2020-08-25 16:23:38 +0800606KYTHE_DIR = "/usr/local/kythe"
607
608INDEX_UPLOAD_POLICY_ALWAYS = "Always"
609
610INDEX_UPLOAD_POLICY_IF_BUILD_SUCCESS = "IfBuildSuccess"
611
612INDEX_UPLOAD_POLICY_NEVER = "Never"
Florian Weikert13215a82019-05-10 12:42:21 +0200613
Yun Peng686f4592022-01-17 15:38:48 +0100614# The maximum number of tasks allowed in one pipeline yaml config file.
615# This is to prevent accidentally creating too many tasks with the martix testing feature.
Yun Peng68e8a4c2022-06-15 18:31:41 +0200616MAX_TASK_NUMBER = 80
Yun Peng686f4592022-01-17 15:38:48 +0100617
Florian Weikert8cba2be2023-07-12 17:39:56 +0200618LAB_AGENT_PATTERNS = [
619 re.compile(r"^bk-imacpro-\d+$"),
620 re.compile(r"^bk-(trusted|testing)-macpro-\d+$"),
621 re.compile(r"^bk-(trusted-)?macstudio-\d+$"),
622]
623
Florian Weikert78e67de2024-02-06 12:58:41 +0100624_TEST_BEP_FILE = "test_bep.json"
625_SHARD_RE = re.compile(r"(.+) \(shard (\d+)\)")
626
Florian Weikertdb832a02020-11-19 19:14:48 +0100627
Philipp Wollermanndcaddd92018-02-21 14:13:43 +0100628class BuildkiteException(Exception):
629 """
630 Raised whenever something goes wrong and we should exit with an error.
631 """
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100632
Philipp Wollermanndcaddd92018-02-21 14:13:43 +0100633 pass
634
635
Yun Pengd433d4f2022-12-09 10:53:15 +0100636class BuildkiteInfraException(Exception):
637 """
638 Raised whenever something goes wrong with the CI infra and we should immediately exit with an error.
639 """
640
641 pass
642
643
Philipp Wollermanndcaddd92018-02-21 14:13:43 +0100644class BinaryUploadRaceException(Exception):
645 """
646 Raised when try_publish_binaries wasn't able to publish a set of binaries,
647 because the generation of the current file didn't match the expected value.
648 """
Philipp Wollermanncd5694c2019-01-03 14:53:04 +0100649
Philipp Wollermanndcaddd92018-02-21 14:13:43 +0100650 pass
651
652
Florian Weikerta0e74592019-03-07 11:56:12 +0100653class BuildkiteClient(object):
Florian Weikerta0e74592019-03-07 11:56:12 +0100654 _ENCRYPTED_BUILDKITE_API_TOKEN = """
655CiQA4DEB9ldzC+E39KomywtqXfaQ86hhulgeDsicds2BuvbCYzsSUAAqwcvXZPh9IMWlwWh94J2F
656exosKKaWB0tSRJiPKnv2NPDfEqGul0ZwVjtWeASpugwxxKeLhFhPMcgHMPfndH6j2GEIY6nkKRbP
657uwoRMCwe
658""".strip()
659
Philipp Wollermanne67eec42019-05-24 15:18:20 +0200660 _ENCRYPTED_BUILDKITE_API_TESTING_TOKEN = """
661CiQAMTBkWjL1C+F5oon3+cC1vmum5+c1y5+96WQY44p0Lxd0PeASUQAy7iU0c6E3W5EOSFYfD5fA
662MWy/SHaMno1NQSUa4xDOl5yc2kizrtxPPVkX4x9pLNuGUY/xwAn2n1DdiUdWZNWlY1bX2C4ex65e
663P9w8kNhEbw==
664""".strip()
665
Florian Weikertde96a6f2019-03-07 14:57:50 +0100666 _BUILD_STATUS_URL_TEMPLATE = (
667 "https://api.buildkite.com/v2/organizations/{}/pipelines/{}/builds/{}"
668 )
Florian Weikerta0e74592019-03-07 11:56:12 +0100669
Florian Weikertdb832a02020-11-19 19:14:48 +0100670 _NEW_BUILD_URL_TEMPLATE = "https://api.buildkite.com/v2/organizations/{}/pipelines/{}/builds"
Yun Peng9337bb32020-02-28 13:31:29 +0100671
672 _RETRY_JOB_URL_TEMPLATE = (
673 "https://api.buildkite.com/v2/organizations/{}/pipelines/{}/builds/{}/jobs/{}/retry"
674 )
675
Florian Weikertb3439b32022-11-09 11:05:16 +0100676 _PIPELINE_INFO_URL_TEMPLATE = "https://api.buildkite.com/v2/organizations/{}/pipelines/{}"
Mai Hussienfe58c062022-07-07 01:29:46 -0700677
Florian Weikerta0e74592019-03-07 11:56:12 +0100678 def __init__(self, org, pipeline):
679 self._org = org
680 self._pipeline = pipeline
681 self._token = self._get_buildkite_token()
682
683 def _get_buildkite_token(self):
Florian Weikert849afb22019-12-14 12:22:29 -0800684 return decrypt_token(
685 encrypted_token=self._ENCRYPTED_BUILDKITE_API_TESTING_TOKEN
686 if THIS_IS_TESTING
687 else self._ENCRYPTED_BUILDKITE_API_TOKEN,
688 kms_key="buildkite-testing-api-token"
689 if THIS_IS_TESTING
690 else "buildkite-untrusted-api-token",
Florian Weikerta0e74592019-03-07 11:56:12 +0100691 )
692
Florian Weikertdb832a02020-11-19 19:14:48 +0100693 def _open_url(self, url, params=[]):
Florian Weikert60661912019-12-18 15:17:10 +0100694 try:
Yun Peng9337bb32020-02-28 13:31:29 +0100695 params_str = "".join("&{}={}".format(k, v) for k, v in params)
Florian Weikert60661912019-12-18 15:17:10 +0100696 return (
Yun Peng9337bb32020-02-28 13:31:29 +0100697 urllib.request.urlopen("{}?access_token={}{}".format(url, self._token, params_str))
Florian Weikert60661912019-12-18 15:17:10 +0100698 .read()
Yun Pengdbedc122020-02-28 13:32:04 +0100699 .decode("utf-8", "ignore")
Florian Weikert60661912019-12-18 15:17:10 +0100700 )
701 except urllib.error.HTTPError as ex:
702 raise BuildkiteException("Failed to open {}: {} - {}".format(url, ex.code, ex.reason))
Florian Weikerta0e74592019-03-07 11:56:12 +0100703
Mai Hussienfe58c062022-07-07 01:29:46 -0700704 def get_pipeline_info(self):
705 """Get details for a pipeline given its organization slug
706 and pipeline slug.
707 See https://buildkite.com/docs/apis/rest-api/pipelines#get-a-pipeline
708
709 Returns
710 -------
711 dict
712 the metadata for the pipeline
713 """
714 url = self._PIPELINE_INFO_URL_TEMPLATE.format(self._org, self._pipeline)
715 output = self._open_url(url)
716 return json.loads(output)
717
Florian Weikerta0e74592019-03-07 11:56:12 +0100718 def get_build_info(self, build_number):
Yun Peng9337bb32020-02-28 13:31:29 +0100719 """Get build info for a pipeline with a given build number
720 See https://buildkite.com/docs/apis/rest-api/builds#get-a-build
721
722 Parameters
723 ----------
724 build_number : the build number
725
726 Returns
727 -------
728 dict
729 the metadata for the build
730 """
Florian Weikerta0e74592019-03-07 11:56:12 +0100731 url = self._BUILD_STATUS_URL_TEMPLATE.format(self._org, self._pipeline, build_number)
732 output = self._open_url(url)
733 return json.loads(output)
734
Yun Peng9337bb32020-02-28 13:31:29 +0100735 def get_build_info_list(self, params):
736 """Get a list of build infos for this pipeline
737 See https://buildkite.com/docs/apis/rest-api/builds#list-builds-for-a-pipeline
738
739 Parameters
740 ----------
741 params : the parameters to filter the result
742
743 Returns
744 -------
745 list of dict
746 the metadata for a list of builds
747 """
748 url = self._BUILD_STATUS_URL_TEMPLATE.format(self._org, self._pipeline, "")
749 output = self._open_url(url, params)
750 return json.loads(output)
751
Florian Weikerta0e74592019-03-07 11:56:12 +0100752 def get_build_log(self, job):
753 return self._open_url(job["raw_log_url"])
754
Yun Peng9337bb32020-02-28 13:31:29 +0100755 @staticmethod
756 def _check_response(response, expected_status_code):
757 if response.status_code != expected_status_code:
758 eprint("Exit code:", response.status_code)
759 eprint("Response:\n", response.text)
760 response.raise_for_status()
761
Florian Weikertdb832a02020-11-19 19:14:48 +0100762 def trigger_new_build(self, commit, message=None, env={}):
Yun Peng9337bb32020-02-28 13:31:29 +0100763 """Trigger a new build at a given commit and return the build metadata.
764 See https://buildkite.com/docs/apis/rest-api/builds#create-a-build
765
766 Parameters
767 ----------
768 commit : the commit we want to build at
769 message : the message we should as the build titile
770 env : (optional) the environment variables to set
771
772 Returns
773 -------
774 dict
775 the metadata for the build
776 """
Salma Samyf54c75b2022-11-04 16:28:18 +0200777 pipeline_info = self.get_pipeline_info()
778 if not pipeline_info:
779 raise BuildkiteException(f"Cannot find pipeline info for pipeline {self._pipeline}.")
780
Yun Peng9337bb32020-02-28 13:31:29 +0100781 url = self._NEW_BUILD_URL_TEMPLATE.format(self._org, self._pipeline)
782 data = {
783 "commit": commit,
Salma Samyf54c75b2022-11-04 16:28:18 +0200784 "branch": pipeline_info.get("default_branch") or "master",
Yun Peng9337bb32020-02-28 13:31:29 +0100785 "message": message if message else f"Trigger build at {commit}",
786 "env": env,
Yun Peng867b5a52022-01-11 13:59:41 +0100787 "ignore_pipeline_branch_filters": "true",
Yun Peng9337bb32020-02-28 13:31:29 +0100788 }
Florian Weikertdb832a02020-11-19 19:14:48 +0100789 response = requests.post(url + "?access_token=" + self._token, json=data)
Yun Peng9337bb32020-02-28 13:31:29 +0100790 BuildkiteClient._check_response(response, requests.codes.created)
791 return json.loads(response.text)
792
Yun Peng9337bb32020-02-28 13:31:29 +0100793 def trigger_job_retry(self, build_number, job_id):
794 """Trigger a job retry and return the job metadata.
795 See https://buildkite.com/docs/apis/rest-api/jobs#retry-a-job
796
797 Parameters
798 ----------
799 build_number : the number of the build we want to retry
800 job_id : the id of the job we want to retry
801
802 Returns
803 -------
804 dict
805 the metadata for the job
806 """
807 url = self._RETRY_JOB_URL_TEMPLATE.format(self._org, self._pipeline, build_number, job_id)
808 response = requests.put(url + "?access_token=" + self._token)
809 BuildkiteClient._check_response(response, requests.codes.ok)
810 return json.loads(response.text)
811
Yun Peng9337bb32020-02-28 13:31:29 +0100812 def wait_job_to_finish(self, build_number, job_id, interval_time=30, logger=None):
813 """Wait a job to finish and return the job metadata
814
815 Parameters
816 ----------
817 build_number : the number of the build we want to wait
818 job_id : the id of the job we want to wait
819 interval_time : (optional) the interval time to check the build status, default to 30s
820 logger : (optional) a logger to report progress
821
822 Returns
823 -------
824 dict
825 the latest metadata for the job
826 """
827 t = 0
828 build_info = self.get_build_info(build_number)
829 while True:
830 for job in build_info["jobs"]:
831 if job["id"] == job_id:
832 state = job["state"]
833 if state != "scheduled" and state != "running" and state != "assigned":
834 return job
835 break
836 else:
Florian Weikertdb832a02020-11-19 19:14:48 +0100837 raise BuildkiteException(
838 f"job id {job_id} doesn't exist in build " + build_info["web_url"]
839 )
Yun Peng9337bb32020-02-28 13:31:29 +0100840 url = build_info["web_url"]
841 if logger:
842 logger.log(f"Waiting for {url}, waited {t} seconds...")
843 time.sleep(interval_time)
844 t += interval_time
845 build_info = self.get_build_info(build_number)
846
Yun Peng9337bb32020-02-28 13:31:29 +0100847 def wait_build_to_finish(self, build_number, interval_time=30, logger=None):
848 """Wait a build to finish and return the build metadata
849
850 Parameters
851 ----------
852 build_number : the number of the build we want to wait
853 interval_time : (optional) the interval time to check the build status, default to 30s
854 logger : (optional) a logger to report progress
855
856 Returns
857 -------
858 dict
859 the latest metadata for the build
860 """
861 t = 0
862 build_info = self.get_build_info(build_number)
863 while build_info["state"] == "scheduled" or build_info["state"] == "running":
864 url = build_info["web_url"]
865 if logger:
866 logger.log(f"Waiting for {url}, waited {t} seconds...")
867 time.sleep(interval_time)
868 t += interval_time
869 build_info = self.get_build_info(build_number)
870 return build_info
871
872
Chi Wang5e3998f2023-05-10 11:51:02 +0000873def decrypt_token(encrypted_token, kms_key, project="bazel-untrusted"):
Florian Weikert849afb22019-12-14 12:22:29 -0800874 return (
875 subprocess.check_output(
876 [
877 gcloud_command(),
878 "kms",
879 "decrypt",
880 "--project",
Chi Wang5e3998f2023-05-10 11:51:02 +0000881 project,
Florian Weikert849afb22019-12-14 12:22:29 -0800882 "--location",
883 "global",
884 "--keyring",
885 "buildkite",
886 "--key",
887 kms_key,
888 "--ciphertext-file",
889 "-",
890 "--plaintext-file",
891 "-",
892 ],
893 input=base64.b64decode(encrypted_token),
894 env=os.environ,
895 )
896 .decode("utf-8")
897 .strip()
898 )
899
900
Philipp Wollermanndcaddd92018-02-21 14:13:43 +0100901def eprint(*args, **kwargs):
902 """
903 Print to stderr and flush (just in case).
904 """
905 print(*args, flush=True, file=sys.stderr, **kwargs)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +0100906
907
Jakob Buchgraber9f153542018-02-27 10:56:04 +0100908def is_windows():
Jakob Buchgraber09048fa2018-02-27 11:39:39 +0100909 return os.name == "nt"
Jakob Buchgraber9f153542018-02-27 10:56:04 +0100910
Jakob Buchgrabere6de16b2018-02-28 12:42:12 +0100911
Florian Weikerta6110a92022-10-20 01:16:09 +0200912def is_mac():
Florian Weikertda94a102022-10-21 12:24:37 +0200913 return platform_module.system() == "Darwin"
Florian Weikerta6110a92022-10-20 01:16:09 +0200914
915
Florian Weikert8cba2be2023-07-12 17:39:56 +0200916def is_lab_machine():
917 agent = os.getenv("BUILDKITE_AGENT_NAME")
918 return any(p.match(agent) for p in LAB_AGENT_PATTERNS)
919
920
Florian Weikert04ac4292023-11-21 17:04:54 +0100921def is_ipv6_mac():
922 return is_mac() and not is_lab_machine()
923
924
Jakob Buchgraber9f153542018-02-27 10:56:04 +0100925def gsutil_command():
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200926 return "gsutil.cmd" if is_windows() else "gsutil"
Jakob Buchgraber9f153542018-02-27 10:56:04 +0100927
Jakob Buchgrabere6de16b2018-02-28 12:42:12 +0100928
Jakob Buchgraber9f153542018-02-27 10:56:04 +0100929def gcloud_command():
Philipp Wollermann2409c6e2018-08-07 07:37:54 +0200930 return "gcloud.cmd" if is_windows() else "gcloud"
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +0100931
Jakob Buchgrabere6de16b2018-02-28 12:42:12 +0100932
Yun Peng5a1a9442022-01-11 14:26:48 +0100933def match_matrix_attr_pattern(s):
934 return re.match("^\${{\s*(\w+)\s*}}$", s)
935
936
937def get_matrix_attributes(task):
938 """Get unexpanded matrix attributes from the given task.
939
940 If a value of field matches "${{<name>}}", then <name> is a wanted matrix attribute.
941 eg. platform: ${{ platform }}
942 """
943 attributes = []
944 for key, value in task.items():
945 if type(value) is str:
946 res = match_matrix_attr_pattern(value)
947 if res:
948 attributes.append(res.groups()[0])
949 return list(set(attributes))
950
951
952def get_combinations(matrix, attributes):
953 """Given a matrix and the wanted attributes, return all possible combinations.
954
955 eg.
956 With matrix = {'a': [1, 2], 'b': [1], 'c': [1]},
957 if attributes = ['a', 'b'], then returns [[('a', 1), ('b', 1)], [('a', 2), ('b', 1)]]
958 if attributes = ['b', 'c'], then returns [[('b', 1), ('c', 1)]]
959 if attributes = ['c'], then returns [[('c', 1)]]
960 """
Yun Peng2b59cde2023-05-08 15:44:38 +0200961 # Sort the attributes to make the output deterministic.
962 attributes.sort()
Yun Peng5a1a9442022-01-11 14:26:48 +0100963 for attr in attributes:
964 if attr not in matrix:
965 raise BuildkiteException("${{ %s }} is not defined in `matrix` section." % attr)
966 pairs = [[(attr, value) for value in matrix[attr]] for attr in attributes]
Yun Peng237309f2023-04-25 15:30:43 +0200967 return sorted(itertools.product(*pairs))
Yun Peng5a1a9442022-01-11 14:26:48 +0100968
969
970def get_expanded_task(task, combination):
971 """Expand a task with the given combination of values of attributes."""
972 combination = dict(combination)
973 expanded_task = copy.deepcopy(task)
974 for key, value in task.items():
975 if type(value) is str:
976 res = match_matrix_attr_pattern(value)
977 if res:
978 attr = res.groups()[0]
979 expanded_task[key] = combination[attr]
980 return expanded_task
981
982
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +0100983def fetch_configs(http_url, file_config):
Philipp Wollermanndb024862018-02-19 17:16:56 +0100984 """
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +0100985 If specified fetches the build configuration from file_config or http_url, else tries to
Jakob Buchgraber25bb50f2018-02-22 18:06:21 +0100986 read it from .bazelci/presubmit.yml.
Philipp Wollermann598b4a42018-02-19 17:03:36 +0100987 Returns the json configuration as a python data structure.
Philipp Wollermanndb024862018-02-19 17:16:56 +0100988 """
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +0100989 if file_config is not None and http_url is not None:
990 raise BuildkiteException("file_config and http_url cannot be set at the same time")
991
Florian Weikertc8b3ed22019-05-31 16:14:12 +0200992 return load_config(http_url, file_config)
993
994
Yun Pengfde48582022-01-11 14:37:47 +0100995def expand_task_config(config):
Yun Peng5a1a9442022-01-11 14:26:48 +0100996 # Expand tasks that uses attributes defined in the matrix section.
997 # The original task definition expands to multiple tasks for each possible combination.
998 tasks_to_expand = []
999 expanded_tasks = {}
1000 matrix = config.pop("matrix", {})
1001 for key, value in matrix.items():
1002 if type(key) is not str or type(value) is not list:
1003 raise BuildkiteException("Expect `matrix` is a map of str -> list")
1004
1005 for task in config["tasks"]:
1006 attributes = get_matrix_attributes(config["tasks"][task])
1007 if attributes:
1008 tasks_to_expand.append(task)
1009 count = 1
1010 for combination in get_combinations(matrix, attributes):
1011 expanded_task_name = "%s_config_%.2d" % (task, count)
1012 count += 1
Florian Weikert7f21ca42022-02-02 17:35:23 +01001013 expanded_tasks[expanded_task_name] = get_expanded_task(
1014 config["tasks"][task], combination
1015 )
Yun Peng5a1a9442022-01-11 14:26:48 +01001016
1017 for task in tasks_to_expand:
1018 config["tasks"].pop(task)
1019 config["tasks"].update(expanded_tasks)
1020
Yun Pengfde48582022-01-11 14:37:47 +01001021
1022def load_config(http_url, file_config, allow_imports=True):
1023 if http_url:
1024 config = load_remote_yaml_file(http_url)
1025 else:
1026 file_config = file_config or ".bazelci/presubmit.yml"
1027 with open(file_config, "r") as fd:
1028 config = yaml.safe_load(fd)
1029
1030 # Legacy mode means that there is exactly one task per platform (e.g. ubuntu1604_nojdk),
1031 # which means that we can get away with using the platform name as task ID.
1032 # No other updates are needed since get_platform_for_task() falls back to using the
1033 # task ID as platform if there is no explicit "platforms" field.
1034 if "platforms" in config:
1035 config["tasks"] = config.pop("platforms")
1036
1037 if "tasks" not in config:
1038 config["tasks"] = {}
1039
1040 expand_task_config(config)
1041
Florian Weikertc8b3ed22019-05-31 16:14:12 +02001042 imports = config.pop("imports", None)
1043 if imports:
1044 if not allow_imports:
1045 raise BuildkiteException("Nested imports are not allowed")
1046
1047 for i in imports:
1048 imported_tasks = load_imported_tasks(i, http_url, file_config)
1049 config["tasks"].update(imported_tasks)
1050
Yun Peng686f4592022-01-17 15:38:48 +01001051 if len(config["tasks"]) > MAX_TASK_NUMBER:
Florian Weikert7f21ca42022-02-02 17:35:23 +01001052 raise BuildkiteException(
1053 "The number of tasks in one config file is limited to %s!" % MAX_TASK_NUMBER
1054 )
Yun Peng686f4592022-01-17 15:38:48 +01001055
Florian Weikert843d7a02019-02-03 17:24:50 +01001056 return config
1057
1058
Florian Weikert13215a82019-05-10 12:42:21 +02001059def load_remote_yaml_file(http_url):
1060 with urllib.request.urlopen(http_url) as resp:
1061 reader = codecs.getreader("utf-8")
Philipp Wollermannd00107e2019-05-18 23:50:59 +02001062 return yaml.safe_load(reader(resp))
Florian Weikert13215a82019-05-10 12:42:21 +02001063
1064
Florian Weikertc8b3ed22019-05-31 16:14:12 +02001065def load_imported_tasks(import_name, http_url, file_config):
1066 if "/" in import_name:
1067 raise BuildkiteException("Invalid import '%s'" % import_name)
1068
1069 old_path = http_url or file_config
1070 new_path = "%s%s" % (old_path[: old_path.rfind("/") + 1], import_name)
1071 if http_url:
1072 http_url = new_path
1073 else:
1074 file_config = new_path
1075
1076 imported_config = load_config(http_url=http_url, file_config=file_config, allow_imports=False)
1077
1078 namespace = import_name.partition(".")[0]
1079 tasks = {}
1080 for task_name, task_config in imported_config["tasks"].items():
Florian Weikert61f29b82019-08-12 16:56:56 +02001081 fix_imported_task_platform(task_name, task_config)
1082 fix_imported_task_name(namespace, task_config)
1083 fix_imported_task_working_directory(namespace, task_config)
Florian Weikertc8b3ed22019-05-31 16:14:12 +02001084 tasks["%s_%s" % (namespace, task_name)] = task_config
1085
1086 return tasks
1087
1088
Florian Weikert61f29b82019-08-12 16:56:56 +02001089def fix_imported_task_platform(task_name, task_config):
1090 if "platform" not in task_config:
1091 task_config["platform"] = task_name
1092
1093
1094def fix_imported_task_name(namespace, task_config):
1095 old_name = task_config.get("name")
1096 task_config["name"] = "%s (%s)" % (namespace, old_name) if old_name else namespace
1097
1098
1099def fix_imported_task_working_directory(namespace, task_config):
1100 old_dir = task_config.get("working_directory")
1101 task_config["working_directory"] = os.path.join(namespace, old_dir) if old_dir else namespace
1102
1103
Jakob Buchgraber3120f7a2018-02-18 13:28:02 +01001104def print_collapsed_group(name):
Jakob Buchgraber5c9b13d2018-02-21 22:28:14 +01001105 eprint("\n\n--- {0}\n\n".format(name))
Jakob Buchgraber3120f7a2018-02-18 13:28:02 +01001106
Jakob Buchgraber9c83de72018-02-18 15:32:44 +01001107
Jakob Buchgraber3120f7a2018-02-18 13:28:02 +01001108def print_expanded_group(name):
Jakob Buchgraber5c9b13d2018-02-21 22:28:14 +01001109 eprint("\n\n+++ {0}\n\n".format(name))
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001110
Yun Peng9b1d3432021-12-07 10:40:45 +01001111
Philipp Wollermannc8b86ac2021-10-25 17:52:07 +02001112def is_trueish(s):
1113 return str(s).lower() in ["true", "1", "t", "y", "yes"]
1114
Jakob Buchgraber9c83de72018-02-18 15:32:44 +01001115
Yun Peng8975c6b2019-02-28 11:55:55 +01001116def use_bazelisk_migrate():
1117 """
1118 If USE_BAZELISK_MIGRATE is set, we use `bazelisk --migrate` to test incompatible flags.
1119 """
Philipp Wollermannc8b86ac2021-10-25 17:52:07 +02001120 return is_trueish(os.environ.get("USE_BAZELISK_MIGRATE"))
Yun Peng8975c6b2019-02-28 11:55:55 +01001121
Yun Peng6a6d29d2023-11-09 14:24:06 +01001122
1123def is_downstream_pipeline():
1124 """
1125 Return true if BAZELCI_DOWNSTREAM_PIPELINE is set
1126 """
1127 return is_trueish(os.environ.get("BAZELCI_DOWNSTREAM_PIPELINE"))
1128
1129
John Cater2f0aee22023-10-19 05:34:33 -04001130def local_run_only():
1131 """
1132 If BAZELCI_LOCAL_RUN is set, run bazelci in local-only mode, with no attempt
1133 to use remote cache/execution, and no attempt to upload test results. Only
1134 compatible with `bazelci.py runner`, not other subcommands.
1135 """
1136 return is_trueish(os.environ.get("BAZELCI_LOCAL_RUN", "false"))
1137
Yun Peng8975c6b2019-02-28 11:55:55 +01001138
1139def bazelisk_flags():
1140 return ["--migrate"] if use_bazelisk_migrate() else []
1141
1142
Chi Wang54595a22021-06-11 17:49:58 +08001143def calculate_flags(task_config, task_config_key, action_key, tmpdir, test_env_vars):
Chi Wang6357efe2020-08-25 16:23:38 +08001144 include_json_profile = task_config.get("include_json_profile", [])
Chi Wang54595a22021-06-11 17:49:58 +08001145 capture_corrupted_outputs = task_config.get("capture_corrupted_outputs", [])
Chi Wang6357efe2020-08-25 16:23:38 +08001146
1147 json_profile_flags = []
1148 json_profile_out = None
Chi Wang54595a22021-06-11 17:49:58 +08001149 if action_key in include_json_profile:
1150 json_profile_out = os.path.join(tmpdir, "{}.profile.gz".format(action_key))
Tobias Werthc22e4c42021-10-08 12:03:14 +02001151 json_profile_flags = ["--profile={}".format(json_profile_out)]
1152
Chi Wang54595a22021-06-11 17:49:58 +08001153 capture_corrupted_outputs_flags = []
1154 capture_corrupted_outputs_dir = None
1155 if action_key in capture_corrupted_outputs:
Philipp Wollermannf436e742021-08-11 11:06:55 +02001156 capture_corrupted_outputs_dir = os.path.join(
1157 tmpdir, "{}_corrupted_outputs".format(action_key)
1158 )
1159 capture_corrupted_outputs_flags = [
1160 "--experimental_remote_capture_corrupted_outputs={}".format(
1161 capture_corrupted_outputs_dir
1162 )
1163 ]
Chi Wang6357efe2020-08-25 16:23:38 +08001164
Alexandre Rostovtsev42bbeb52023-08-24 04:08:04 -04001165 flags = list(task_config.get(task_config_key, []))
Chi Wang6357efe2020-08-25 16:23:38 +08001166 flags += json_profile_flags
Chi Wang54595a22021-06-11 17:49:58 +08001167 flags += capture_corrupted_outputs_flags
Chi Wang6357efe2020-08-25 16:23:38 +08001168 # We have to add --test_env flags to `build`, too, otherwise Bazel
1169 # discards its analysis cache between `build` and `test`.
1170 if test_env_vars:
1171 flags += ["--test_env={}".format(v) for v in test_env_vars]
1172
Chi Wang54595a22021-06-11 17:49:58 +08001173 return flags, json_profile_out, capture_corrupted_outputs_dir
Chi Wang6357efe2020-08-25 16:23:38 +08001174
1175
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001176def execute_commands(
Florian Weikertc8642af2019-02-03 23:58:51 +01001177 task_config,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001178 platform,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001179 use_bazel_at_commit,
1180 use_but,
1181 save_but,
Yun Peng4d1d6542019-01-17 18:30:33 +01001182 needs_clean,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001183 build_only,
1184 test_only,
1185 monitor_flaky_tests,
Florian Weikertc8642af2019-02-03 23:58:51 +01001186 bazel_version=None,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001187):
Yun Pengc85cd0e2022-09-02 10:44:29 +02001188 if use_bazelisk_migrate():
1189 # If we are testing incompatible flags with Bazelisk,
1190 # use Bazel@last_green if USE_BAZEL_VERSION env var is not set explicitly.
1191 if "USE_BAZEL_VERSION" not in os.environ:
1192 bazel_version = "last_green"
1193
1194 # Override use_but in case we are in the downstream pipeline so that it doesn't try to
1195 # download Bazel built from previous jobs.
1196 use_but = False
1197
1198 # Set BAZELISK_INCOMPATIBLE_FLAGS to tell Bazelisk which flags to test.
1199 os.environ["BAZELISK_INCOMPATIBLE_FLAGS"] = ",".join(fetch_incompatible_flags().keys())
1200
Florian Weikert13215a82019-05-10 12:42:21 +02001201 if not bazel_version:
1202 # The last good version of Bazel can be specified in an emergency file.
1203 # However, we only use last_good_bazel for pipelines that do not
1204 # explicitly specify a version of Bazel.
1205 try:
1206 emergency_settings = load_remote_yaml_file(EMERGENCY_FILE_URL)
1207 bazel_version = emergency_settings.get("last_good_bazel")
1208 except urllib.error.HTTPError:
1209 # Ignore this error. The Setup step will have already complained about
1210 # it by showing an error message.
1211 pass
Yun Pengf50f7b72019-02-28 19:09:52 +01001212
Jakob Buchgraberfb95a2f2018-02-22 11:46:25 +01001213 if build_only and test_only:
1214 raise BuildkiteException("build_only and test_only cannot be true at the same time")
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02001215
Florian Weikertd7cb83d2023-02-24 19:51:52 +01001216 if use_but:
1217 if use_bazel_at_commit:
1218 raise BuildkiteException("use_bazel_at_commit cannot be set when use_but is true")
1219
1220 print_collapsed_group(":printer: Printing task config for downstream job...")
Florian Weikert7d894782023-02-24 20:28:14 +01001221 eprint(json.dumps(task_config, indent=2))
Yun Peng20d45602018-10-18 13:27:05 +02001222
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02001223 tmpdir = tempfile.mkdtemp()
1224 sc_process = None
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001225 try:
Florian Weikertda94a102022-10-21 12:24:37 +02001226 if is_mac():
Florian Weikertee84c5c2019-05-28 11:21:51 +02001227 activate_xcode(task_config)
Philipp Wollermann380f1e62019-04-12 16:45:27 +02001228
Florian Weikert4ee0bed2019-02-21 18:03:00 +01001229 # If the CI worker runs Bazelisk, we need to forward all required env variables to the test.
1230 # Otherwise any integration test that invokes Bazel (=Bazelisk in this case) will fail.
Marcel Hlopko198328b2019-02-25 09:19:55 +01001231 test_env_vars = ["LocalAppData"] if platform == "windows" else ["HOME"]
Florian Weikertc12580c2021-07-13 17:09:25 +02001232
1233 # CI should have its own user agent so that we can remove it from Bazel download statistics.
1234 os.environ["BAZELISK_USER_AGENT"] = "Bazelisk/BazelCI"
1235 test_env_vars.append("BAZELISK_USER_AGENT")
1236
Florian Weikertd5b7fd22023-12-19 18:25:35 +01001237 # Avoid "Network is unreachable" errors in IPv6-only environments
Florian Weikert78e67de2024-02-06 12:58:41 +01001238 for e in ("JAVA_TOOL_OPTIONS", "SSL_CERT_FILE"):
1239 if os.getenv(e):
1240 test_env_vars.append(e)
Florian Weikert07afd382023-12-08 14:43:07 +01001241
Philipp Wollermannf4aabb72019-06-25 15:59:00 +02001242 # We use one binary for all Linux platforms (because we also just release one binary for all
1243 # Linux versions and we have to ensure that it works on all of them).
Florian Weikertda94a102022-10-21 12:24:37 +02001244 binary_platform = platform if is_mac() or is_windows() else LINUX_BINARY_PLATFORM
Philipp Wollermannf4aabb72019-06-25 15:59:00 +02001245
Yun Pengeec2cd92022-07-14 14:44:30 +02001246 bazel_binary = "bazel"
Yun Peng20d45602018-10-18 13:27:05 +02001247 if use_bazel_at_commit:
1248 print_collapsed_group(":gcloud: Downloading Bazel built at " + use_bazel_at_commit)
Yun Pengdae5a7d2022-01-11 16:08:58 +01001249 # Linux binaries are published under platform name "centos7"
1250 if binary_platform == LINUX_BINARY_PLATFORM:
1251 binary_platform = "centos7"
Yun Pengeec2cd92022-07-14 14:44:30 +02001252 os.environ["USE_BAZEL_VERSION"] = download_bazel_binary_at_commit(
Philipp Wollermannf4aabb72019-06-25 15:59:00 +02001253 tmpdir, binary_platform, use_bazel_at_commit
1254 )
Yun Pengeec2cd92022-07-14 14:44:30 +02001255 print_collapsed_group(":bazel: Using Bazel at " + os.environ["USE_BAZEL_VERSION"])
Philipp Wollermann639c0452019-01-03 11:23:54 +01001256 elif use_but:
Jakob Buchgraber92755d72018-02-22 15:33:37 +01001257 print_collapsed_group(":gcloud: Downloading Bazel Under Test")
Yun Pengeec2cd92022-07-14 14:44:30 +02001258 os.environ["USE_BAZEL_VERSION"] = download_bazel_binary(tmpdir, binary_platform)
1259 print_collapsed_group(":bazel: Using Bazel at " + os.environ["USE_BAZEL_VERSION"])
Philipp Wollermann639c0452019-01-03 11:23:54 +01001260 else:
Yun Pengeec2cd92022-07-14 14:44:30 +02001261 print_collapsed_group(":bazel: Using Bazel version " + bazel_version)
Florian Weikertc8642af2019-02-03 23:58:51 +01001262 if bazel_version:
Florian Weikertc8642af2019-02-03 23:58:51 +01001263 os.environ["USE_BAZEL_VERSION"] = bazel_version
Philipp Wollermann87b45252020-01-22 12:43:42 +01001264 if "USE_BAZEL_VERSION" in os.environ and not task_config.get(
1265 "skip_use_bazel_version_for_test", False
1266 ):
Yun Pengf0a66e22019-10-14 12:45:42 +02001267 # This will only work if the bazel binary in $PATH is actually a bazelisk binary
1268 # (https://github.com/bazelbuild/bazelisk).
1269 test_env_vars.append("USE_BAZEL_VERSION")
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001270
Philipp Wollermann5c7ea412019-05-24 15:26:57 +02001271 for key, value in task_config.get("environment", {}).items():
Philipp Wollermann4ad4aac2019-05-24 15:23:09 +02001272 # We have to explicitly convert the value to a string, because sometimes YAML tries to
1273 # be smart and converts strings like "true" and "false" to booleans.
Philipp Wollermann7df2d0d2021-08-11 13:26:04 +02001274 os.environ[key] = os.path.expandvars(str(value))
Philipp Wollermann213ac9d2019-02-06 11:50:05 +01001275
Yun Peng366f04c2020-08-10 16:55:58 +02001276 # Set BAZELISK_SHUTDOWN to 1 when we use bazelisk --migrate on Windows.
1277 # This is a workaround for https://github.com/bazelbuild/continuous-integration/issues/1012
1278 if use_bazelisk_migrate() and platform == "windows":
1279 os.environ["BAZELISK_SHUTDOWN"] = "1"
1280
Florian Weikert9b805ce2023-05-10 18:04:03 +02001281 def PrepareRepoInCwd(print_cmd_groups, initial_setup=False):
Florian Weikert5e70d9d2023-05-08 19:20:23 +02001282 # Allow the config to override the current working directory.
1283 requested_working_dir = task_config.get("working_directory")
1284 if requested_working_dir:
1285 if os.path.isabs(requested_working_dir):
1286 raise BuildkiteException(
1287 f"working_directory must be relative to the repository root, "
1288 "but was {requested_working_dir}"
1289 )
1290
1291 full_requested_working_dir = os.path.abspath(requested_working_dir)
1292 if not os.path.isdir(full_requested_working_dir):
1293 raise BuildkiteException(
1294 f"{full_requested_working_dir} does not exist or is not a directory"
1295 )
1296
1297 os.chdir(full_requested_working_dir)
1298
Florian Weikertb97801e2023-05-10 19:17:08 +02001299 # Dirty workaround for #1660
1300 if initial_setup:
1301 # Set OUTPUT_BASE environment variable
1302 os.environ["OUTPUT_BASE"] = get_output_base(bazel_binary)
Florian Weikert9b805ce2023-05-10 18:04:03 +02001303
Florian Weikertb97801e2023-05-10 19:17:08 +02001304 cmd_exec_func = (
1305 execute_batch_commands if platform == "windows" else execute_shell_commands
1306 )
1307 cmd_exec_func(task_config.get("setup", None))
Yun Peng28a9a532023-05-10 14:19:46 +02001308
Florian Weikert5e70d9d2023-05-08 19:20:23 +02001309 if platform == "windows":
Florian Weikert4141ce92023-05-09 09:45:55 +02001310 execute_batch_commands(task_config.get("batch_commands", None), print_cmd_groups)
Florian Weikert5e70d9d2023-05-08 19:20:23 +02001311 else:
Florian Weikert4141ce92023-05-09 09:45:55 +02001312 execute_shell_commands(task_config.get("shell_commands", None), print_cmd_groups)
Florian Weikert5e70d9d2023-05-08 19:20:23 +02001313
Florian Weikert9b805ce2023-05-10 18:04:03 +02001314 PrepareRepoInCwd(True, initial_setup=True)
Florian Weikerte72d68a2019-03-08 18:56:33 +01001315
Florian Weikertc8642af2019-02-03 23:58:51 +01001316 bazel_version = print_bazel_version_info(bazel_binary, platform)
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001317
Yun Penga5a1ee02018-12-05 15:00:58 +01001318 print_environment_variables_info()
1319
Yun Pengc85cd0e2022-09-02 10:44:29 +02001320 execute_bazel_run(bazel_binary, platform, task_config.get("run_targets", None))
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001321
Yun Peng4d1d6542019-01-17 18:30:33 +01001322 if needs_clean:
Yun Pengea0359e2019-01-17 15:37:47 +01001323 execute_bazel_clean(bazel_binary, platform)
1324
Yun Pengfb86f672023-11-10 16:18:55 +01001325 # we need the commit here in order to calculate the correct targets.
1326 git_commit = os.getenv("BUILDKITE_COMMIT")
Florian Weikert25e92c42023-04-28 10:56:46 +02001327 if not git_commit:
1328 raise BuildkiteInfraException("Unable to determine Git commit for this build")
1329
Florian Weikert7dd9f662023-05-04 15:05:42 +02001330 test_flags, json_profile_out_test, capture_corrupted_outputs_dir_test = calculate_flags(
1331 task_config, "test_flags", "test", tmpdir, test_env_vars
1332 )
1333
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01001334 build_targets, test_targets, coverage_targets, index_targets = calculate_targets(
Florian Weikert7dd9f662023-05-04 15:05:42 +02001335 task_config,
1336 bazel_binary,
1337 build_only,
1338 test_only,
Florian Weikert5e70d9d2023-05-08 19:20:23 +02001339 os.getcwd(),
1340 PrepareRepoInCwd,
Florian Weikert7dd9f662023-05-04 15:05:42 +02001341 git_commit,
1342 test_flags,
Florian Weikert736d06e2019-05-08 13:16:42 +02001343 )
Florian Weikert736d06e2019-05-08 13:16:42 +02001344
1345 if build_targets:
Philipp Wollermannf436e742021-08-11 11:06:55 +02001346 (
1347 build_flags,
1348 json_profile_out_build,
1349 capture_corrupted_outputs_dir_build,
1350 ) = calculate_flags(task_config, "build_flags", "build", tmpdir, test_env_vars)
joeleba76887952019-05-16 15:22:17 +02001351 try:
Yun Pengc8bb9e92021-07-28 11:56:53 +02001352 release_name = get_release_name_from_branch_name()
joeleba76887952019-05-16 15:22:17 +02001353 execute_bazel_build(
1354 bazel_version,
1355 bazel_binary,
1356 platform,
Philipp Wollermannf436e742021-08-11 11:06:55 +02001357 build_flags
1358 + (
1359 ["--stamp", "--embed_label=%s" % release_name]
1360 if save_but and release_name
1361 else []
1362 ),
joeleba76887952019-05-16 15:22:17 +02001363 build_targets,
1364 None,
joeleba76887952019-05-16 15:22:17 +02001365 )
1366 if save_but:
1367 upload_bazel_binary(platform)
1368 finally:
Chi Wang6357efe2020-08-25 16:23:38 +08001369 if json_profile_out_build:
Philipp Wollermann92cf51e2019-05-16 15:31:11 +02001370 upload_json_profile(json_profile_out_build, tmpdir)
Chi Wang54595a22021-06-11 17:49:58 +08001371 if capture_corrupted_outputs_dir_build:
1372 upload_corrupted_outputs(capture_corrupted_outputs_dir_build, tmpdir)
Philipp Wollermann639c0452019-01-03 11:23:54 +01001373
Florian Weikert736d06e2019-05-08 13:16:42 +02001374 if test_targets:
Florian Weikert4901c662019-02-26 13:20:11 +01001375 if not is_windows():
1376 # On platforms that support sandboxing (Linux, MacOS) we have
1377 # to allow access to Bazelisk's cache directory.
1378 # However, the flag requires the directory to exist,
1379 # so we create it here in order to not crash when a test
1380 # does not invoke Bazelisk.
Florian Weikertda94a102022-10-21 12:24:37 +02001381 bazelisk_cache_dir = get_bazelisk_cache_directory()
Florian Weikert4901c662019-02-26 13:20:11 +01001382 os.makedirs(bazelisk_cache_dir, mode=0o755, exist_ok=True)
1383 test_flags.append("--sandbox_writable_path={}".format(bazelisk_cache_dir))
Florian Weikert5b890332019-02-25 14:57:43 +01001384
Chi Wang5e3998f2023-05-10 11:51:02 +00001385 # Set BUILDKITE_ANALYTICS_TOKEN so that bazelci-agent can upload test results to Test Analytics
1386 if "ENCRYPTED_BUILDKITE_ANALYTICS_TOKEN" in os.environ:
1387 if THIS_IS_TESTING:
1388 kms_key = "buildkite-testing-api-token"
1389 project = "bazel-untrusted"
1390 elif THIS_IS_TRUSTED:
1391 kms_key = "buildkite-trusted-api-token"
1392 project = "bazel-public"
1393 else:
1394 kms_key = "buildkite-untrusted-api-token"
1395 project = "bazel-untrusted"
1396 os.environ["BUILDKITE_ANALYTICS_TOKEN"] = decrypt_token(
1397 encrypted_token=os.environ["ENCRYPTED_BUILDKITE_ANALYTICS_TOKEN"],
1398 kms_key=kms_key,
1399 project=project,
1400 )
1401
Florian Weikert78e67de2024-02-06 12:58:41 +01001402 test_bep_file = os.path.join(tmpdir, _TEST_BEP_FILE)
1403 with concurrent.futures.ThreadPoolExecutor() as executor:
1404 future = executor.submit(
1405 upload_test_logs_from_bep, test_bep_file, tmpdir, monitor_flaky_tests
1406 )
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +02001407 try:
1408 execute_bazel_test(
1409 bazel_version,
1410 bazel_binary,
1411 platform,
1412 test_flags,
1413 test_targets,
1414 test_bep_file,
1415 monitor_flaky_tests,
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +02001416 )
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +02001417 finally:
Chi Wang6357efe2020-08-25 16:23:38 +08001418 if json_profile_out_test:
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +02001419 upload_json_profile(json_profile_out_test, tmpdir)
Chi Wang54595a22021-06-11 17:49:58 +08001420 if capture_corrupted_outputs_dir_test:
1421 upload_corrupted_outputs(capture_corrupted_outputs_dir_test, tmpdir)
Florian Weikert78e67de2024-02-06 12:58:41 +01001422
1423 _ = future.result()
1424 # TODO: print results
Chi Wang6357efe2020-08-25 16:23:38 +08001425
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01001426 if coverage_targets:
Florian Weikert7f21ca42022-02-02 17:35:23 +01001427 (
1428 coverage_flags,
1429 json_profile_out_coverage,
1430 capture_corrupted_outputs_dir_coverage,
1431 ) = calculate_flags(task_config, "coverage_flags", "coverage", tmpdir, test_env_vars)
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01001432 try:
1433 execute_bazel_coverage(
1434 bazel_version,
1435 bazel_binary,
1436 platform,
1437 coverage_flags,
1438 coverage_targets,
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01001439 )
1440 finally:
1441 if json_profile_out_coverage:
1442 upload_json_profile(json_profile_out_coverage, tmpdir)
1443 if capture_corrupted_outputs_dir_coverage:
1444 upload_corrupted_outputs(capture_corrupted_outputs_dir_coverage, tmpdir)
1445
Chi Wang6357efe2020-08-25 16:23:38 +08001446 if index_targets:
Philipp Wollermannf436e742021-08-11 11:06:55 +02001447 (
1448 index_flags,
1449 json_profile_out_index,
1450 capture_corrupted_outputs_dir_index,
1451 ) = calculate_flags(task_config, "index_flags", "index", tmpdir, test_env_vars)
Chi Wang6357efe2020-08-25 16:23:38 +08001452 index_upload_policy = task_config.get("index_upload_policy", "IfBuildSuccess")
Chi Wangb2b65682020-08-27 10:36:15 +08001453 index_upload_gcs = task_config.get("index_upload_gcs", False)
Chi Wang6357efe2020-08-25 16:23:38 +08001454
1455 try:
Florian Weikertdb832a02020-11-19 19:14:48 +01001456 should_upload_kzip = (
1457 True if index_upload_policy == INDEX_UPLOAD_POLICY_ALWAYS else False
1458 )
Chi Wang6357efe2020-08-25 16:23:38 +08001459 try:
1460 execute_bazel_build_with_kythe(
1461 bazel_version,
1462 bazel_binary,
1463 platform,
1464 index_flags,
1465 index_targets,
1466 None,
Chi Wang6357efe2020-08-25 16:23:38 +08001467 )
1468
1469 if index_upload_policy == INDEX_UPLOAD_POLICY_IF_BUILD_SUCCESS:
1470 should_upload_kzip = True
1471 except subprocess.CalledProcessError as e:
1472 # If not running with Always policy, raise the build error.
1473 if index_upload_policy != INDEX_UPLOAD_POLICY_ALWAYS:
1474 handle_bazel_failure(e, "build")
1475
Philipp Wollermanna038b002021-05-05 22:04:38 +02001476 if should_upload_kzip and not is_pull_request():
Chi Wang6357efe2020-08-25 16:23:38 +08001477 try:
Chi Wangb2b65682020-08-27 10:36:15 +08001478 merge_and_upload_kythe_kzip(platform, index_upload_gcs)
Chi Wang6357efe2020-08-25 16:23:38 +08001479 except subprocess.CalledProcessError:
1480 raise BuildkiteException("Failed to upload kythe kzip")
1481 finally:
1482 if json_profile_out_index:
1483 upload_json_profile(json_profile_out_index, tmpdir)
Chi Wang54595a22021-06-11 17:49:58 +08001484 if capture_corrupted_outputs_dir_index:
1485 upload_corrupted_outputs(capture_corrupted_outputs_dir_index, tmpdir)
Chi Wang6357efe2020-08-25 16:23:38 +08001486
UebelAndre2e56c362023-06-13 04:38:15 -07001487 if platform == "windows":
Florian Weikertc89d6142023-11-20 14:50:35 +01001488 execute_batch_commands(
1489 task_config.get("post_batch_commands", None),
1490 True,
1491 ":batch: Post Processing (Batch Commands)",
1492 )
UebelAndre2e56c362023-06-13 04:38:15 -07001493 else:
Florian Weikertc89d6142023-11-20 14:50:35 +01001494 execute_shell_commands(
1495 task_config.get("post_shell_commands", None),
1496 True,
1497 ":bash: Post Processing (Shell Commands)",
1498 )
UebelAndre2e56c362023-06-13 04:38:15 -07001499
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001500 finally:
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +02001501 terminate_background_process(sc_process)
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001502 if tmpdir:
1503 shutil.rmtree(tmpdir)
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01001504
Philipp Wollermann3c8b8512019-07-16 15:28:03 +02001505
Florian Weikertee84c5c2019-05-28 11:21:51 +02001506def activate_xcode(task_config):
Florian Weikertcee5d1d2023-05-17 00:23:03 +02001507 supported_versions = sorted(
1508 # Stripping "Xcode" prefix and ".app" suffix from e.g. "Xcode12.0.1.app" leaves just the version number.
1509 [os.path.basename(x)[5:-4] for x in glob("/Applications/Xcode*.app")],
Florian Weikertcee5d1d2023-05-17 00:23:03 +02001510 )
Florian Weikertb649c502023-11-20 18:00:35 +01001511 if not supported_versions:
1512 raise BuildkiteInfraException("Could not find a valid Xcode installation.")
Florian Weikertcee5d1d2023-05-17 00:23:03 +02001513
Florian Weikertb649c502023-11-20 18:00:35 +01001514 # Get the Xcode version from the config. Can be None.
1515 wanted_xcode_version = get_requested_xcode_version(task_config)
Florian Weikertee84c5c2019-05-28 11:21:51 +02001516
Philipp Wollermann06e56972020-01-29 14:46:41 +01001517 # This is used to replace e.g. 11.2 with 11.2.1 without having to update all configs.
Philipp Wollermann2b4ee9f2021-02-11 16:32:35 +01001518 xcode_version = XCODE_VERSION_OVERRIDES.get(wanted_xcode_version, wanted_xcode_version)
1519
Florian Weikertb649c502023-11-20 18:00:35 +01001520 # Default to the oldest installed version if no version was requested
1521 # or the requested version is not installed.
Philipp Wollermann2b4ee9f2021-02-11 16:32:35 +01001522 if xcode_version not in supported_versions:
Florian Weikertb649c502023-11-20 18:00:35 +01001523 xcode_version = supported_versions[0]
James Leitch3ac0ac12023-12-04 04:25:26 -08001524
Florian Weikertb649c502023-11-20 18:00:35 +01001525 if not wanted_xcode_version or wanted_xcode_version == xcode_version:
1526 print_collapsed_group(":xcode: Activating Xcode {}...".format(xcode_version))
1527 else:
Philipp Wollermann2b4ee9f2021-02-11 16:32:35 +01001528 print_collapsed_group(
1529 ":xcode: Fixed Xcode version: {} -> {}...".format(wanted_xcode_version, xcode_version)
1530 )
1531 lines = [
Florian Weikert9d5e4c02021-05-27 15:10:20 +02001532 "Your selected Xcode version {} was not available on the machine.".format(
1533 wanted_xcode_version
1534 ),
Philipp Wollermann2b4ee9f2021-02-11 16:32:35 +01001535 "Bazel CI automatically picked a fallback version: {}.".format(xcode_version),
1536 "Available versions are: {}.".format(supported_versions),
1537 ]
Florian Weikert9d5e4c02021-05-27 15:10:20 +02001538 execute_command(
1539 [
1540 "buildkite-agent",
1541 "annotate",
1542 "--style=warning",
1543 "\n".join(lines),
1544 "--context",
1545 "ctx-xcode_version_fixed",
1546 ]
1547 )
Philipp Wollermann06e56972020-01-29 14:46:41 +01001548
Florian Weikertee84c5c2019-05-28 11:21:51 +02001549 # Now activate the specified Xcode version and let it install its required components.
1550 # The CI machines have a sudoers config that allows the 'buildkite' user to run exactly
1551 # these two commands, so don't change them without also modifying the file there.
Florian Weikertb649c502023-11-20 18:00:35 +01001552 xcode_path = "/Applications/Xcode{}.app".format(xcode_version)
Florian Weikertee84c5c2019-05-28 11:21:51 +02001553 execute_command(["/usr/bin/sudo", "/usr/bin/xcode-select", "--switch", xcode_path])
1554 execute_command(["/usr/bin/sudo", "/usr/bin/xcodebuild", "-runFirstLaunch"])
1555
1556
Florian Weikertb649c502023-11-20 18:00:35 +01001557def get_requested_xcode_version(task_config):
1558 wanted_xcode_version = task_config.get("xcode_version")
1559 if not wanted_xcode_version:
1560 return None
1561
1562 # Ensure it's a valid version number.
1563 if not isinstance(wanted_xcode_version, str):
1564 raise BuildkiteException(
1565 "Version number '{}' is not a string. Did you forget to put it in quotes?".format(
1566 wanted_xcode_version
1567 )
1568 )
1569 if not XCODE_VERSION_REGEX.match(wanted_xcode_version):
1570 raise BuildkiteException(
1571 "Invalid Xcode version format '{}', must match the format X.Y[.Z].".format(
1572 wanted_xcode_version
1573 )
1574 )
1575
1576 return wanted_xcode_version
1577
1578
Florian Weikertda94a102022-10-21 12:24:37 +02001579def get_bazelisk_cache_directory():
Florian Weikert4901c662019-02-26 13:20:11 +01001580 # The path relies on the behavior of Go's os.UserCacheDir()
1581 # and of the Go version of Bazelisk.
Florian Weikertda94a102022-10-21 12:24:37 +02001582 cache_dir = "Library/Caches" if is_mac() else ".cache"
Philipp Wollermannce986af2019-07-18 14:46:05 +02001583 return os.path.join(os.environ.get("HOME"), cache_dir, "bazelisk")
Florian Weikert4901c662019-02-26 13:20:11 +01001584
Florian Weikert5b890332019-02-25 14:57:43 +01001585
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02001586def current_branch_is_main_branch():
Mai Hussien0731b4d2022-06-28 11:16:07 -07001587 return os.getenv("BUILDKITE_BRANCH") in ("master", "stable", "main", "google")
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02001588
1589
Yun Peng92c0ef82021-07-26 10:41:21 +02001590def get_release_name_from_branch_name():
Yun Peng98948362022-11-08 16:14:20 +01001591 # TODO(pcloudy): Find a better way to do this
1592 if os.getenv("BUILDKITE_PIPELINE_SLUG") == "publish-bazel-binaries":
1593 return None
Yun Peng26e01ff2021-07-26 12:05:53 +02001594 res = re.match(r"release-(\d+\.\d+\.\d+(rc\d+)?).*", os.getenv("BUILDKITE_BRANCH"))
Yun Peng98948362022-11-08 16:14:20 +01001595 return res.group(1) if res else None
Yun Peng92c0ef82021-07-26 10:41:21 +02001596
1597
Jakob Buchgraber95e3d572018-02-21 18:48:49 +01001598def is_pull_request():
Jakob Buchgraber67761d32018-02-21 19:00:21 +01001599 third_party_repo = os.getenv("BUILDKITE_PULL_REQUEST_REPO", "")
Jakob Buchgraber95e3d572018-02-21 18:48:49 +01001600 return len(third_party_repo) > 0
1601
1602
Yun Penge3cf12d2018-12-05 15:01:09 +01001603def print_bazel_version_info(bazel_binary, platform):
Jakob Buchgraber99c4bbb2018-02-22 11:59:31 +01001604 print_collapsed_group(":information_source: Bazel Info")
Philipp Wollermannf13804b2019-02-05 21:08:30 +01001605 version_output = execute_command_and_get_output(
Florian Weikertb3439b32022-11-09 11:05:16 +01001606 [bazel_binary] + common_startup_flags() + ["--nosystem_rc", "--nohome_rc", "version"]
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001607 )
1608 execute_command(
Florian Weikertb3439b32022-11-09 11:05:16 +01001609 [bazel_binary] + common_startup_flags() + ["--nosystem_rc", "--nohome_rc", "info"]
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001610 )
Jakob Buchgraber7e690a72018-02-18 13:22:15 +01001611
Florian Weikertc8642af2019-02-03 23:58:51 +01001612 match = BUILD_LABEL_PATTERN.search(version_output)
1613 return match.group(1) if match else "unreleased binary"
1614
Jakob Buchgraber7e690a72018-02-18 13:22:15 +01001615
Yun Penga5a1ee02018-12-05 15:00:58 +01001616def print_environment_variables_info():
1617 print_collapsed_group(":information_source: Environment Variables")
1618 for key, value in os.environ.items():
1619 eprint("%s=(%s)" % (key, value))
1620
1621
Jakob Buchgraber426399e2018-03-20 19:45:46 +01001622def upload_bazel_binary(platform):
John Cater2f0aee22023-10-19 05:34:33 -04001623 if local_run_only():
1624 return
Jakob Buchgraber7d1d3bb2018-02-21 22:38:22 +01001625 print_collapsed_group(":gcloud: Uploading Bazel Under Test")
Jakob Buchgraber426399e2018-03-20 19:45:46 +01001626 if platform == "windows":
Philipp Wollermann10183212020-02-04 21:54:14 +01001627 binary_dir = r"bazel-bin\src"
1628 binary_name = r"bazel.exe"
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001629 binary_nojdk_name = r"bazel_nojdk.exe"
Philipp Wollermann10183212020-02-04 21:54:14 +01001630 else:
1631 binary_dir = "bazel-bin/src"
1632 binary_name = "bazel"
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001633 binary_nojdk_name = "bazel_nojdk"
Philipp Wollermann10183212020-02-04 21:54:14 +01001634 execute_command(["buildkite-agent", "artifact", "upload", binary_name], cwd=binary_dir)
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001635 execute_command(["buildkite-agent", "artifact", "upload", binary_nojdk_name], cwd=binary_dir)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001636
1637
Chi Wangb2b65682020-08-27 10:36:15 +08001638def merge_and_upload_kythe_kzip(platform, index_upload_gcs):
John Cater2f0aee22023-10-19 05:34:33 -04001639 if local_run_only():
1640 return
Chi Wang6357efe2020-08-25 16:23:38 +08001641 print_collapsed_group(":gcloud: Uploading kythe kzip")
1642
Philipp Wollermann2b4ee9f2021-02-11 16:32:35 +01001643 kzips = glob("bazel-out/*/extra_actions/**/*.kzip", recursive=True)
Chi Wang6357efe2020-08-25 16:23:38 +08001644
Chi Wang6357efe2020-08-25 16:23:38 +08001645 build_number = os.getenv("BUILDKITE_BUILD_NUMBER")
Chi Wangb2b65682020-08-27 10:36:15 +08001646 git_commit = os.getenv("BUILDKITE_COMMIT")
1647 final_kzip_name = "{}-{}-{}.kzip".format(build_number, platform, git_commit)
Chi Wang6357efe2020-08-25 16:23:38 +08001648
Chi Wangb2b65682020-08-27 10:36:15 +08001649 execute_command([f"{KYTHE_DIR}/tools/kzip", "merge", "--output", final_kzip_name] + kzips)
Chi Wang6357efe2020-08-25 16:23:38 +08001650 execute_command(["buildkite-agent", "artifact", "upload", final_kzip_name])
1651
Chi Wangb2b65682020-08-27 10:36:15 +08001652 if index_upload_gcs:
1653 pipeline = os.getenv("BUILDKITE_PIPELINE_SLUG")
Chi Wang08aea1c2022-03-21 18:04:52 +08001654 branch = os.getenv("BUILDKITE_BRANCH")
1655 destination = KZIPS_BUCKET + pipeline + "/" + branch + "/" + final_kzip_name
Chi Wangb2b65682020-08-27 10:36:15 +08001656 print("Uploading to GCS {}".format(destination))
Florian Weikertdb832a02020-11-19 19:14:48 +01001657 execute_command([gsutil_command(), "cp", final_kzip_name, destination])
Chi Wangb2b65682020-08-27 10:36:15 +08001658
Chi Wang6357efe2020-08-25 16:23:38 +08001659
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001660def download_binary(dest_dir, platform, binary_name):
Philipp Wollermannc52e26a2019-05-18 22:10:47 +02001661 source_step = create_label(platform, "Bazel", build_only=True)
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001662 execute_command(
Philipp Wollermann10183212020-02-04 21:54:14 +01001663 ["buildkite-agent", "artifact", "download", binary_name, dest_dir, "--step", source_step]
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001664 )
Philipp Wollermann10183212020-02-04 21:54:14 +01001665 bazel_binary_path = os.path.join(dest_dir, binary_name)
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001666 st = os.stat(bazel_binary_path)
1667 os.chmod(bazel_binary_path, st.st_mode | stat.S_IEXEC)
1668 return bazel_binary_path
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001669
1670
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001671def download_bazel_binary(dest_dir, platform):
1672 binary_name = "bazel.exe" if platform == "windows" else "bazel"
1673 return download_binary(dest_dir, platform, binary_name)
1674
1675
1676def download_bazel_nojdk_binary(dest_dir, platform):
1677 binary_name = "bazel_nojdk.exe" if platform == "windows" else "bazel_nojdk"
1678 return download_binary(dest_dir, platform, binary_name)
1679
1680
Yun Pengd433d4f2022-12-09 10:53:15 +01001681def download_binary_at_commit(bazel_git_commit, bazel_binary_url, bazel_binary_path):
Yun Peng02312732019-01-17 18:17:05 +01001682 try:
Florian Weikertdb832a02020-11-19 19:14:48 +01001683 execute_command([gsutil_command(), "cp", bazel_binary_url, bazel_binary_path])
Yun Peng02312732019-01-17 18:17:05 +01001684 except subprocess.CalledProcessError as e:
Yun Pengd433d4f2022-12-09 10:53:15 +01001685 raise BuildkiteInfraException(
Philipp Wollermannc05ac682019-01-19 12:37:28 +01001686 "Failed to download Bazel binary at %s, error message:\n%s" % (bazel_git_commit, str(e))
1687 )
Yun Peng20d45602018-10-18 13:27:05 +02001688 st = os.stat(bazel_binary_path)
1689 os.chmod(bazel_binary_path, st.st_mode | stat.S_IEXEC)
1690 return bazel_binary_path
1691
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001692
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001693def download_bazel_binary_at_commit(dest_dir, platform, bazel_git_commit):
1694 url = bazelci_builds_gs_url(platform, bazel_git_commit)
1695 path = os.path.join(dest_dir, "bazel.exe" if platform == "windows" else "bazel")
Yun Pengd433d4f2022-12-09 10:53:15 +01001696 return download_binary_at_commit(bazel_git_commit, url, path)
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001697
1698
1699def download_bazel_nojdk_binary_at_commit(dest_dir, platform, bazel_git_commit):
1700 url = bazelci_builds_nojdk_gs_url(platform, bazel_git_commit)
1701 path = os.path.join(dest_dir, "bazel_nojdk.exe" if platform == "windows" else "bazel_nojdk")
Yun Pengd433d4f2022-12-09 10:53:15 +01001702 return download_binary_at_commit(bazel_git_commit, url, path)
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001703
Florian Weikert7f21ca42022-02-02 17:35:23 +01001704
Chi Wangd61eb972023-01-18 11:36:18 +01001705def download_bazelci_agent(dest_dir):
Chi Wangaf6d5b12023-05-10 11:48:05 +00001706 repo = "bazelbuild/continuous-integration"
Chi Wang4412ef12023-05-10 16:29:22 +00001707 if THIS_IS_TESTING and "BAZELCI_AGENT_REPO" in os.environ:
Chi Wangaf6d5b12023-05-10 11:48:05 +00001708 repo = os.environ["BAZELCI_AGENT_REPO"]
Chi Wang8939b692023-06-19 10:14:54 +00001709 version = "0.2.2"
Chi Wang4412ef12023-05-10 16:29:22 +00001710 if THIS_IS_TESTING and "BAZELCI_AGENT_VERSION" in os.environ:
Chi Wangaf6d5b12023-05-10 11:48:05 +00001711 version = os.environ["BAZELCI_AGENT_VERSION"]
Chi Wangd279d582021-09-29 10:59:06 +08001712 postfix = ""
Florian Weikertda94a102022-10-21 12:24:37 +02001713 if is_windows():
Chi Wangd279d582021-09-29 10:59:06 +08001714 postfix = "x86_64-pc-windows-msvc.exe"
Florian Weikertda94a102022-10-21 12:24:37 +02001715 elif is_mac():
Florian Weikertb3439b32022-11-09 11:05:16 +01001716 if platform_module.machine() == "arm64":
Chi Wang74ad6ca2022-10-21 11:20:36 +02001717 postfix = "aarch64-apple-darwin"
1718 else:
1719 postfix = "x86_64-apple-darwin"
Chi Wangd279d582021-09-29 10:59:06 +08001720 else:
Chi Wangaac1ead2023-01-17 11:10:48 +01001721 if platform_module.machine() == "aarch64":
1722 postfix = "aarch64-unknown-linux-musl"
1723 else:
1724 postfix = "x86_64-unknown-linux-musl"
Chi Wangd279d582021-09-29 10:59:06 +08001725
1726 name = "bazelci-agent-{}-{}".format(version, postfix)
Florian Weikert9b805ce2023-05-10 18:04:03 +02001727 url = "https://github.com/{}/releases/download/agent-{}/{}".format(repo, version, name)
Florian Weikertda94a102022-10-21 12:24:37 +02001728 path = os.path.join(dest_dir, "bazelci-agent.exe" if is_windows() else "bazelci-agent")
Chi Wangd279d582021-09-29 10:59:06 +08001729 execute_command(["curl", "-sSL", url, "-o", path])
1730 st = os.stat(path)
1731 os.chmod(path, st.st_mode | stat.S_IEXEC)
1732 return path
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04001733
Florian Weikert7f21ca42022-02-02 17:35:23 +01001734
Florian Weikertda94a102022-10-21 12:24:37 +02001735def get_mirror_root():
1736 if is_mac():
1737 return "/usr/local/var/bazelbuild/"
1738 elif is_windows():
1739 return "c:\\buildkite\\bazelbuild\\"
joeleba7050d842019-05-23 17:03:31 +02001740
Florian Weikertda94a102022-10-21 12:24:37 +02001741 return "/var/lib/bazelbuild/"
joeleba7050d842019-05-23 17:03:31 +02001742
Florian Weikertc89d6142023-11-20 14:50:35 +01001743
Yun Pengfb86f672023-11-10 16:18:55 +01001744def get_repositories_root():
1745 """A root directory for preparing the reposioty to be tested."""
1746 repo_root = "/var/lib/buildkite-agent/builds/${BUILDKITE_AGENT_NAME}/${BUILDKITE_ORGANIZATION_SLUG}-org-repo-root"
1747 if is_mac():
1748 repo_root = "/Users/buildkite/builds/${BUILDKITE_AGENT_NAME}/${BUILDKITE_ORGANIZATION_SLUG}-org-repo-root"
1749 elif is_windows():
1750 repo_root = "c:/b/${BUILDKITE_AGENT_NAME}/${BUILDKITE_ORGANIZATION_SLUG}-org-repo-root"
1751 # If this is a Windows machine with a local SSD, the build directory is on drive D.
1752 if os.path.exists("d:/b"):
1753 repo_root = repo_root.replace("c:/b/", "d:/b/")
joeleba7050d842019-05-23 17:03:31 +02001754
Yun Pengfb86f672023-11-10 16:18:55 +01001755 repo_root = os.path.expandvars(repo_root)
1756
1757 if not os.path.exists(repo_root):
1758 os.makedirs(repo_root)
1759 return repo_root
1760
Yun Pengfb86f672023-11-10 16:18:55 +01001761
Florian Weikertc89d6142023-11-20 14:50:35 +01001762def clone_git_repository(git_repository, git_commit=None, suppress_stdout=False):
Yun Pengfb86f672023-11-10 16:18:55 +01001763 def execute_git_command(args):
1764 execute_command(args, print_output=not suppress_stdout, suppress_stdout=suppress_stdout)
1765
1766 root = get_repositories_root()
Philipp Wollermannff39ef52018-02-21 14:18:52 +01001767 project_name = re.search(r"/([^/]+)\.git$", git_repository).group(1)
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001768 clone_path = os.path.join(root, project_name)
Yun Pengfb86f672023-11-10 16:18:55 +01001769 if not suppress_stdout:
1770 print_collapsed_group(
1771 "Fetching %s sources at %s" % (project_name, git_commit if git_commit else "HEAD")
1772 )
Philipp Wollermann438ec242018-09-05 14:39:24 +02001773
Florian Weikertda94a102022-10-21 12:24:37 +02001774 mirror_path = get_mirror_root() + re.sub(r"[^0-9A-Za-z]", "-", git_repository)
Philipp Wollermannea128282019-05-08 11:56:14 +02001775
Philipp Wollermann414703d2018-08-28 16:40:38 +02001776 if not os.path.exists(clone_path):
Philipp Wollermann62f4a032019-05-08 17:44:14 +02001777 if os.path.exists(mirror_path):
Yun Pengfb86f672023-11-10 16:18:55 +01001778 execute_git_command(
Philipp Wollermann62f4a032019-05-08 17:44:14 +02001779 ["git", "clone", "-v", "--reference", mirror_path, git_repository, clone_path]
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001780 )
Philipp Wollermannd4cd0d82018-05-01 09:56:24 +02001781 else:
Yun Pengfb86f672023-11-10 16:18:55 +01001782 execute_git_command(["git", "clone", "-v", git_repository, clone_path])
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001783
Philipp Wollermann414703d2018-08-28 16:40:38 +02001784 os.chdir(clone_path)
Yun Pengfb86f672023-11-10 16:18:55 +01001785 execute_git_command(["git", "remote", "set-url", "origin", git_repository])
1786 execute_git_command(["git", "clean", "-fdqx"])
1787 execute_git_command(["git", "submodule", "foreach", "--recursive", "git clean -fdqx"])
1788 execute_git_command(["git", "fetch", "origin"])
Yun Peng376d2b32018-11-29 10:24:54 +01001789 if git_commit:
1790 # sync to a specific commit of this repository
Yun Pengfb86f672023-11-10 16:18:55 +01001791 execute_git_command(["git", "reset", git_commit, "--hard"])
Yun Peng376d2b32018-11-29 10:24:54 +01001792 else:
1793 # sync to the latest commit of HEAD. Unlikely git pull this also works after a force push.
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01001794 remote_head = (
1795 subprocess.check_output(["git", "symbolic-ref", "refs/remotes/origin/HEAD"])
1796 .decode("utf-8")
1797 .rstrip()
1798 )
Yun Pengfb86f672023-11-10 16:18:55 +01001799 execute_git_command(["git", "reset", remote_head, "--hard"])
1800 execute_git_command(["git", "submodule", "sync", "--recursive"])
1801 execute_git_command(["git", "submodule", "update", "--init", "--recursive", "--force"])
1802 execute_git_command(["git", "submodule", "foreach", "--recursive", "git reset --hard"])
1803 execute_git_command(["git", "clean", "-fdqx"])
1804 execute_git_command(["git", "submodule", "foreach", "--recursive", "git clean -fdqx"])
Yun Peng20d45602018-10-18 13:27:05 +02001805 return clone_path
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001806
Philipp Wollermann438ec242018-09-05 14:39:24 +02001807
Florian Weikertc89d6142023-11-20 14:50:35 +01001808def execute_batch_commands(
1809 commands, print_group=True, group_message=":batch: Setup (Batch Commands)"
1810):
Yun Penga935a542018-05-18 15:08:53 +02001811 if not commands:
1812 return
Florian Weikert4141ce92023-05-09 09:45:55 +02001813
1814 if print_group:
UebelAndre2e56c362023-06-13 04:38:15 -07001815 print_collapsed_group(group_message)
Florian Weikert4141ce92023-05-09 09:45:55 +02001816
Yun Penga935a542018-05-18 15:08:53 +02001817 batch_commands = "&".join(commands)
Jakob Buchgraber4a824412018-06-22 12:56:10 +02001818 return subprocess.run(batch_commands, shell=True, check=True, env=os.environ).returncode
Yun Penga935a542018-05-18 15:08:53 +02001819
Philipp Wollermann414703d2018-08-28 16:40:38 +02001820
Florian Weikertc89d6142023-11-20 14:50:35 +01001821def execute_shell_commands(
1822 commands, print_group=True, group_message=":bash: Setup (Shell Commands)"
1823):
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001824 if not commands:
1825 return
Florian Weikert4141ce92023-05-09 09:45:55 +02001826
1827 if print_group:
UebelAndre2e56c362023-06-13 04:38:15 -07001828 print_collapsed_group(group_message)
Florian Weikert4141ce92023-05-09 09:45:55 +02001829
mostynb14440912020-03-17 17:11:47 +01001830 shell_command = "\n".join(["set -e"] + commands)
Philipp Wollermann3e1a7712018-02-19 17:34:24 +01001831 execute_command([shell_command], shell=True)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001832
1833
Yun Peng0a6a98a2019-03-06 13:07:54 +01001834def handle_bazel_failure(exception, action):
1835 msg = "bazel {0} failed with exit code {1}".format(action, exception.returncode)
1836 if use_bazelisk_migrate():
1837 print_collapsed_group(msg)
1838 else:
1839 raise BuildkiteException(msg)
1840
1841
Yun Pengc85cd0e2022-09-02 10:44:29 +02001842def execute_bazel_run(bazel_binary, platform, targets):
Philipp Wollermann598b4a42018-02-19 17:03:36 +01001843 if not targets:
1844 return
1845 print_collapsed_group("Setup (Run Targets)")
1846 for target in targets:
Yun Peng0a6a98a2019-03-06 13:07:54 +01001847 try:
1848 execute_command(
1849 [bazel_binary]
1850 + bazelisk_flags()
Florian Weikertda94a102022-10-21 12:24:37 +02001851 + common_startup_flags()
Yun Peng0a6a98a2019-03-06 13:07:54 +01001852 + ["run"]
1853 + common_build_flags(None, platform)
Yun Peng0a6a98a2019-03-06 13:07:54 +01001854 + [target]
1855 )
1856 except subprocess.CalledProcessError as e:
1857 handle_bazel_failure(e, "run")
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01001858
1859
Philipp Wollermannc4752e42022-02-08 16:35:39 +01001860def remote_caching_flags(platform, accept_cached=True):
Philipp Wollermanne67eec42019-05-24 15:18:20 +02001861 # Only enable caching for untrusted and testing builds.
Florian Weikert8cba2be2023-07-12 17:39:56 +02001862 if CLOUD_PROJECT != "bazel-untrusted":
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001863 return []
Yun Peng07dafc52022-03-16 13:23:35 +01001864 # We don't enable remote caching on the Linux ARM64 machine since it doesn't have access to GCS.
1865 if platform == "ubuntu2004_arm64":
1866 return []
1867
Florian Weikert09012952023-07-12 14:07:39 +02001868 platform_cache_key = [
1869 BUILDKITE_ORG.encode("utf-8"),
1870 # Whenever the remote cache was known to have been poisoned increase the number below
Yun Peng9229e012023-08-03 14:27:56 +02001871 "cache-poisoning-20230803".encode("utf-8"),
Florian Weikert09012952023-07-12 14:07:39 +02001872 platform.encode("utf-8"),
1873 ]
1874
Florian Weikertda94a102022-10-21 12:24:37 +02001875 if is_mac():
Philipp Wollermannfce92bf2019-05-22 15:14:32 +02001876 platform_cache_key += [
Philipp Wollermannef89d2f2019-04-18 15:52:24 +02001877 # macOS version:
1878 subprocess.check_output(["/usr/bin/sw_vers", "-productVersion"]),
1879 # Path to Xcode:
1880 subprocess.check_output(["/usr/bin/xcode-select", "-p"]),
1881 # Xcode version:
1882 subprocess.check_output(["/usr/bin/xcodebuild", "-version"]),
1883 ]
Yun Peng1ce55be2023-08-11 16:19:26 +02001884
Florian Weikert8cba2be2023-07-12 17:39:56 +02001885 if is_mac() and is_lab_machine():
1886 # Use a local cache server for our physical macOS machines in the lab.
Yun Peng67fc1d72023-12-20 14:15:30 +01001887 flags = ["--remote_cache=http://[2a00:79e1:abc:8602:a28c:fdff:fed0:ec39]"]
Philipp Wollermannef89d2f2019-04-18 15:52:24 +02001888 else:
Philipp Wollermanne74da4e2019-06-07 11:31:02 +02001889 # Use RBE for caching builds running on GCE.
Florian Weikert7eb3a742023-11-14 15:00:32 +01001890 protocol = "grpcs://" if is_mac() else ""
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02001891 flags = [
1892 "--google_default_credentials",
Florian Weikert7eb3a742023-11-14 15:00:32 +01001893 "--remote_cache={}remotebuildexecution.googleapis.com".format(protocol),
Philipp Wollermanne74da4e2019-06-07 11:31:02 +02001894 "--remote_instance_name=projects/{}/instances/default_instance".format(CLOUD_PROJECT),
Florian Weikert8cba2be2023-07-12 17:39:56 +02001895 # Enable BES / Build Results reporting.
Chi Wang466876d2023-06-12 14:09:20 +00001896 "--bes_backend=buildeventservice.googleapis.com",
1897 "--bes_timeout=360s",
Florian Weikert736073b2023-11-22 15:10:20 +01001898 "--project_id=bazel-untrusted",
Chi Wang466876d2023-06-12 14:09:20 +00001899 ]
Philipp Wollermann380f1e62019-04-12 16:45:27 +02001900
1901 platform_cache_digest = hashlib.sha256()
1902 for key in platform_cache_key:
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02001903 eprint("Adding to platform cache key: {}".format(key))
Philipp Wollermann380f1e62019-04-12 16:45:27 +02001904 platform_cache_digest.update(key)
1905 platform_cache_digest.update(b":")
1906
Florian Weikerte8693be2024-01-12 19:34:14 +01001907 remote_timeout = 3600 if is_ipv6_mac() else 60
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02001908 flags += [
Florian Weikert04ac4292023-11-21 17:04:54 +01001909 f"--remote_timeout={remote_timeout}",
Philipp Wollermann639c0452019-01-03 11:23:54 +01001910 "--remote_max_connections=200",
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02001911 '--remote_default_platform_properties=properties:{name:"cache-silo-key" value:"%s"}'
1912 % platform_cache_digest.hexdigest(),
Yun Peng5985f742023-04-17 11:32:42 +02001913 "--remote_download_toplevel",
Philipp Wollermann639c0452019-01-03 11:23:54 +01001914 ]
Jakob Buchgraber4f1d2712018-02-20 10:22:47 +01001915
Yun Pengb2fbce32022-02-04 12:56:34 +01001916 if not accept_cached:
1917 flags += ["--noremote_accept_cached"]
1918
Philipp Wollermannd96d8fa2019-01-11 14:37:47 +01001919 return flags
1920
Jakob Buchgraber4f1d2712018-02-20 10:22:47 +01001921
Jakob Buchgraberb4342cd2018-02-20 16:35:07 +01001922def remote_enabled(flags):
1923 # Detect if the project configuration enabled its own remote caching / execution.
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001924 remote_flags = ["--remote_executor", "--remote_cache", "--remote_http_cache"]
Jakob Buchgraberb4342cd2018-02-20 16:35:07 +01001925 for flag in flags:
1926 for remote_flag in remote_flags:
1927 if flag.startswith(remote_flag):
1928 return True
1929 return False
1930
Jakob Buchgraber95e3d572018-02-21 18:48:49 +01001931
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001932def concurrent_jobs(platform):
1933 return "75" if platform.startswith("rbe_") else str(multiprocessing.cpu_count())
Jakob Buchgraber51a83662018-02-22 19:49:24 +01001934
1935
Philipp Wollermann3e28d3b2018-02-23 23:19:37 +01001936def concurrent_test_jobs(platform):
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001937 if platform.startswith("rbe_"):
1938 return "75"
Florian Weikertda94a102022-10-21 12:24:37 +02001939 elif is_windows():
Jakob Buchgrabere3ccda32018-06-22 23:29:48 +02001940 return "8"
Florian Weikertda94a102022-10-21 12:24:37 +02001941 elif is_mac() and THIS_IS_TESTING:
Philipp Wollermann85877cb2021-07-02 19:20:33 +02001942 return "4"
Florian Weikertda94a102022-10-21 12:24:37 +02001943 elif is_mac():
Florian Weikertecf091c2023-04-28 10:22:23 +02001944 # TODO(twerth): This is an experiment, remove.
1945 return str(int(multiprocessing.cpu_count() / 2))
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001946 return "12"
Philipp Wollermann3e28d3b2018-02-23 23:19:37 +01001947
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001948
Florian Weikertda94a102022-10-21 12:24:37 +02001949def common_startup_flags():
1950 if is_windows():
Philipp Wollermannd5ab3d92020-02-05 16:55:13 +01001951 if os.path.exists("D:/b"):
1952 # This machine has a local SSD mounted as drive D.
1953 return ["--output_user_root=D:/b"]
1954 else:
1955 # This machine uses its PD-SSD as the build directory.
1956 return ["--output_user_root=C:/b"]
Florian Weikert04ac4292023-11-21 17:04:54 +01001957 elif is_ipv6_mac():
1958 return ["--host_jvm_args=-Djava.net.preferIPv6Addresses=true"]
Philipp Wollermannd5ab3d92020-02-05 16:55:13 +01001959 return []
Yun Peng58977d62018-11-16 12:19:20 +01001960
1961
1962def common_build_flags(bep_file, platform):
Yun Peng088cc932018-11-16 12:11:46 +01001963 flags = [
Yun Pengf51e7842018-11-16 11:35:43 +01001964 "--show_progress_rate_limit=5",
1965 "--curses=yes",
1966 "--color=yes",
Philipp Wollermannd99414c2019-05-28 17:26:09 +02001967 "--terminal_columns=143",
Philipp Wollermann4c8391e2019-05-28 18:03:35 +02001968 "--show_timestamps",
Yun Pengf51e7842018-11-16 11:35:43 +01001969 "--verbose_failures",
Yun Pengf51e7842018-11-16 11:35:43 +01001970 "--jobs=" + concurrent_jobs(platform),
Yun Pengf51e7842018-11-16 11:35:43 +01001971 "--announce_rc",
Philipp Wollermannb97f9102019-04-16 18:05:56 +02001972 "--experimental_repository_cache_hardlinks",
Philipp Wollermannef89d2f2019-04-18 15:52:24 +02001973 # Some projects set --disk_cache in their project-specific bazelrc, which we never want on
1974 # CI, so let's just disable it explicitly.
1975 "--disk_cache=",
Yun Peng088cc932018-11-16 12:11:46 +01001976 ]
Philipp Wollermann639c0452019-01-03 11:23:54 +01001977
Florian Weikertda94a102022-10-21 12:24:37 +02001978 if is_windows():
Philipp Wollermannb97f9102019-04-16 18:05:56 +02001979 pass
Florian Weikertda94a102022-10-21 12:24:37 +02001980 elif is_mac():
Philipp Wollermannb97f9102019-04-16 18:05:56 +02001981 flags += [
1982 "--sandbox_writable_path=/var/tmp/_bazel_buildkite/cache/repos/v1",
1983 "--test_env=REPOSITORY_CACHE=/var/tmp/_bazel_buildkite/cache/repos/v1",
1984 ]
1985 else:
Philipp Wollermann639c0452019-01-03 11:23:54 +01001986 flags += ["--sandbox_tmpfs_path=/tmp"]
1987
Yun Peng088cc932018-11-16 12:11:46 +01001988 if bep_file:
Philipp Wollermann639c0452019-01-03 11:23:54 +01001989 flags += [
1990 "--experimental_build_event_json_file_path_conversion=false",
1991 "--build_event_json_file=" + bep_file,
1992 ]
1993
Florian Weikert04ac4292023-11-21 17:04:54 +01001994 if is_ipv6_mac():
1995 flags += ["--jvmopt=-Djava.net.preferIPv6Addresses"]
1996
Yun Peng088cc932018-11-16 12:11:46 +01001997 return flags
Philipp Wollermann94bd9e32018-04-30 15:32:28 +02001998
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02001999
Yun Pengb7247ff2018-11-15 13:52:39 +01002000def rbe_flags(original_flags, accept_cached):
Philipp Wollermannbcfd9da2018-08-09 15:31:18 +02002001 # Enable remote execution via RBE.
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002002 flags = [
2003 "--remote_executor=remotebuildexecution.googleapis.com",
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01002004 "--remote_instance_name=projects/bazel-untrusted/instances/default_instance",
Philipp Wollermann2df93482021-06-16 04:39:49 +02002005 "--remote_timeout=3600",
Philipp Wollermann57dadb82020-02-17 14:32:24 +01002006 "--incompatible_strict_action_env",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002007 "--google_default_credentials",
Philipp Wollermann57dadb82020-02-17 14:32:24 +01002008 "--toolchain_resolution_debug",
Yun Peng5985f742023-04-17 11:32:42 +02002009 "--remote_download_toplevel",
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002010 ]
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02002011
Philipp Wollermannbcfd9da2018-08-09 15:31:18 +02002012 # Enable BES / Build Results reporting.
2013 flags += [
2014 "--bes_backend=buildeventservice.googleapis.com",
Philipp Wollermannbcfd9da2018-08-09 15:31:18 +02002015 "--bes_timeout=360s",
Florian Weikert736073b2023-11-22 15:10:20 +01002016 "--project_id=bazel-untrusted",
Philipp Wollermannbcfd9da2018-08-09 15:31:18 +02002017 ]
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02002018
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002019 if not accept_cached:
2020 flags += ["--noremote_accept_cached"]
Philipp Wollermannbcfd9da2018-08-09 15:31:18 +02002021
Nicolas Lopez36996222019-05-28 12:21:28 -04002022 # Adapted from https://github.com/bazelbuild/bazel-toolchains/blob/master/bazelrc/.bazelrc
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002023 flags += [
Nicolas Lopez36996222019-05-28 12:21:28 -04002024 # These should NOT longer need to be modified.
2025 # All that is needed is updating the @bazel_toolchains repo pin
2026 # in projects' WORKSPACE files.
Xindb02c012018-11-07 14:10:54 -05002027 #
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002028 # Toolchain related flags to append at the end of your .bazelrc file.
Nicolas Lopez36996222019-05-28 12:21:28 -04002029 "--host_javabase=@buildkite_config//java:jdk",
2030 "--javabase=@buildkite_config//java:jdk",
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002031 "--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8",
2032 "--java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8",
Nicolas Lopez36996222019-05-28 12:21:28 -04002033 "--crosstool_top=@buildkite_config//cc:toolchain",
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002034 "--action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002035 ]
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02002036
Yun Pengb7247ff2018-11-15 13:52:39 +01002037 # Platform flags:
2038 # The toolchain container used for execution is defined in the target indicated
2039 # by "extra_execution_platforms", "host_platform" and "platforms".
Xin03a88ab2019-03-11 19:18:50 -04002040 # If you are using your own toolchain container, you need to create a platform
2041 # target with "constraint_values" that allow for the toolchain specified with
2042 # "extra_toolchains" to be selected (given constraints defined in
2043 # "exec_compatible_with").
Yun Pengb7247ff2018-11-15 13:52:39 +01002044 # More about platforms: https://docs.bazel.build/versions/master/platforms.html
2045 # Don't add platform flags if they are specified already.
2046 platform_flags = {
Nicolas Lopez36996222019-05-28 12:21:28 -04002047 "--extra_toolchains": "@buildkite_config//config:cc-toolchain",
2048 "--extra_execution_platforms": "@buildkite_config//config:platform",
2049 "--host_platform": "@buildkite_config//config:platform",
2050 "--platforms": "@buildkite_config//config:platform",
Yun Pengb7247ff2018-11-15 13:52:39 +01002051 }
Yun Peng67ab5062018-11-15 13:58:15 +01002052 for platform_flag, value in list(platform_flags.items()):
Yun Pengb7247ff2018-11-15 13:52:39 +01002053 found = False
2054 for original_flag in original_flags:
2055 if original_flag.startswith(platform_flag):
2056 found = True
2057 break
2058 if not found:
2059 flags += [platform_flag + "=" + value]
2060
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002061 return flags
2062
2063
Yun Peng28a9a532023-05-10 14:19:46 +02002064def get_output_base(bazel_binary):
Yun Peng2b6de292022-02-10 14:28:06 +01002065 return execute_command_and_get_output(
Florian Weikertda94a102022-10-21 12:24:37 +02002066 [bazel_binary] + common_startup_flags() + ["info", "output_base"],
Yun Peng2b6de292022-02-10 14:28:06 +01002067 print_output=False,
2068 ).strip()
2069
2070
Florian Weikertb3439b32022-11-09 11:05:16 +01002071def compute_flags(platform, flags, bep_file, bazel_binary, enable_remote_cache=False):
Yun Peng58977d62018-11-16 12:19:20 +01002072 aggregated_flags = common_build_flags(bep_file, platform)
John Cater2f0aee22023-10-19 05:34:33 -04002073
2074 if not local_run_only():
2075 if not remote_enabled(flags):
2076 if platform.startswith("rbe_"):
2077 aggregated_flags += rbe_flags(flags, accept_cached=enable_remote_cache)
2078 else:
Florian Weikertc89d6142023-11-20 14:50:35 +01002079 aggregated_flags += remote_caching_flags(
2080 platform, accept_cached=enable_remote_cache
2081 )
Yun Peng75786552018-11-13 15:23:30 +01002082 aggregated_flags += flags
Yun Peng53598002018-12-03 10:42:02 +01002083
Philipp Wollermann87b45252020-01-22 12:43:42 +01002084 for i, flag in enumerate(aggregated_flags):
2085 if "$HOME" in flag:
Florian Weikerta6110a92022-10-20 01:16:09 +02002086 if is_windows():
Philipp Wollermannd5ab3d92020-02-05 16:55:13 +01002087 if os.path.exists("D:/"):
2088 home = "D:"
2089 else:
2090 home = "C:/b"
Florian Weikerta6110a92022-10-20 01:16:09 +02002091 elif is_mac():
Philipp Wollermannd5ab3d92020-02-05 16:55:13 +01002092 home = "/Users/buildkite"
2093 else:
2094 home = "/var/lib/buildkite-agent"
Philipp Wollermann87b45252020-01-22 12:43:42 +01002095 aggregated_flags[i] = flag.replace("$HOME", home)
2096 if "$OUTPUT_BASE" in flag:
Yun Peng28a9a532023-05-10 14:19:46 +02002097 output_base = get_output_base(bazel_binary)
Philipp Wollermann87b45252020-01-22 12:43:42 +01002098 aggregated_flags[i] = flag.replace("$OUTPUT_BASE", output_base)
2099
Yun Pengd6443e22023-11-13 12:56:29 +01002100 if is_downstream_pipeline():
2101 # If we are in a downstream pipeline, turn off the lockfile update since changing Bazel version could affect the lockfile.
2102 aggregated_flags.append("--lockfile_mode=off")
Yun Pengd6443e22023-11-13 12:56:29 +01002103
Florian Weikert24a4b482018-11-30 19:05:38 +01002104 return aggregated_flags
Yun Peng53598002018-12-03 10:42:02 +01002105
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002106
Yun Pengea0359e2019-01-17 15:37:47 +01002107def execute_bazel_clean(bazel_binary, platform):
2108 print_expanded_group(":bazel: Clean")
2109
2110 try:
Florian Weikertda94a102022-10-21 12:24:37 +02002111 execute_command([bazel_binary] + common_startup_flags() + ["clean", "--expunge"])
Yun Pengea0359e2019-01-17 15:37:47 +01002112 except subprocess.CalledProcessError as e:
2113 raise BuildkiteException("bazel clean failed with exit code {}".format(e.returncode))
2114
2115
Chi Wang6357efe2020-08-25 16:23:38 +08002116def kythe_startup_flags():
2117 return [f"--bazelrc={KYTHE_DIR}/extractors.bazelrc"]
2118
2119
2120def kythe_build_flags():
Philipp Wollermannf61a20b2021-05-05 22:04:20 +02002121 return [
2122 "--experimental_convenience_symlinks=normal",
Florian Weikert9d5e4c02021-05-27 15:10:20 +02002123 f"--override_repository=kythe_release={KYTHE_DIR}",
Philipp Wollermannf61a20b2021-05-05 22:04:20 +02002124 ]
Chi Wang6357efe2020-08-25 16:23:38 +08002125
2126
Florian Weikertb3439b32022-11-09 11:05:16 +01002127def execute_bazel_build(bazel_version, bazel_binary, platform, flags, targets, bep_file):
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02002128 print_collapsed_group(":bazel: Computing flags for build step")
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002129 aggregated_flags = compute_flags(
Yun Peng8975c6b2019-02-28 11:55:55 +01002130 platform,
2131 flags,
Yun Peng8975c6b2019-02-28 11:55:55 +01002132 bep_file,
Philipp Wollermann87b45252020-01-22 12:43:42 +01002133 bazel_binary,
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002134 enable_remote_cache=True,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002135 )
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02002136
2137 print_expanded_group(":bazel: Build ({})".format(bazel_version))
Jakob Buchgraber95e3d572018-02-21 18:48:49 +01002138 try:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002139 execute_command(
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002140 [bazel_binary]
2141 + bazelisk_flags()
Florian Weikertda94a102022-10-21 12:24:37 +02002142 + common_startup_flags()
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002143 + ["build"]
2144 + aggregated_flags
Philipp Wollermann2a160432019-09-19 15:57:28 +02002145 + ["--"]
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002146 + targets
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002147 )
Jakob Buchgraber95e3d572018-02-21 18:48:49 +01002148 except subprocess.CalledProcessError as e:
Yun Peng0a6a98a2019-03-06 13:07:54 +01002149 handle_bazel_failure(e, "build")
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002150
2151
Florian Weikertb3439b32022-11-09 11:05:16 +01002152def execute_bazel_build_with_kythe(bazel_version, bazel_binary, platform, flags, targets, bep_file):
Chi Wang6357efe2020-08-25 16:23:38 +08002153 print_collapsed_group(":bazel: Computing flags for build step")
2154 aggregated_flags = compute_flags(
2155 platform,
2156 flags,
Chi Wang6357efe2020-08-25 16:23:38 +08002157 bep_file,
2158 bazel_binary,
2159 enable_remote_cache=False,
2160 )
2161
2162 print_expanded_group(":bazel: Build ({})".format(bazel_version))
2163
2164 execute_command(
2165 [bazel_binary]
2166 + bazelisk_flags()
Florian Weikertda94a102022-10-21 12:24:37 +02002167 + common_startup_flags()
Chi Wang6357efe2020-08-25 16:23:38 +08002168 + kythe_startup_flags()
2169 + ["build"]
2170 + kythe_build_flags()
2171 + aggregated_flags
2172 + ["--"]
2173 + targets
2174 )
2175
2176
Florian Weikert7dd9f662023-05-04 15:05:42 +02002177def calculate_targets(
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002178 task_config,
2179 bazel_binary,
2180 build_only,
2181 test_only,
2182 workspace_dir,
2183 ws_setup_func,
2184 git_commit,
2185 test_flags,
Florian Weikert7dd9f662023-05-04 15:05:42 +02002186):
Florian Weikerte417f9f2023-05-05 17:33:46 +02002187 print_collapsed_group(":dart: Calculating targets")
2188
Alexandre Rostovtsev42bbeb52023-08-24 04:08:04 -04002189 build_targets = [] if test_only else list(task_config.get("build_targets", []))
2190 test_targets = [] if build_only else list(task_config.get("test_targets", []))
Florian Weikertc89d6142023-11-20 14:50:35 +01002191 coverage_targets = (
2192 [] if (build_only or test_only) else list(task_config.get("coverage_targets", []))
2193 )
Alexandre Rostovtsev42bbeb52023-08-24 04:08:04 -04002194 index_targets = [] if (build_only or test_only) else list(task_config.get("index_targets", []))
Chi Wang6357efe2020-08-25 16:23:38 +08002195
Florian Weikertdb832a02020-11-19 19:14:48 +01002196 index_targets_query = (
2197 None if (build_only or test_only) else task_config.get("index_targets_query", None)
2198 )
Chi Wang6357efe2020-08-25 16:23:38 +08002199 if index_targets_query:
2200 output = execute_command_and_get_output(
2201 [bazel_binary]
Florian Weikertda94a102022-10-21 12:24:37 +02002202 + common_startup_flags()
Yun Pengeb435bb2023-11-17 10:37:42 +01002203 + ["--nosystem_rc", "--nohome_rc", "query", "--lockfile_mode=off", index_targets_query],
Chi Wang6357efe2020-08-25 16:23:38 +08002204 print_output=False,
2205 )
2206 index_targets += output.strip().split("\n")
Florian Weikert736d06e2019-05-08 13:16:42 +02002207
Philipp Wollermann2a160432019-09-19 15:57:28 +02002208 # Remove the "--" argument splitter from the list that some configs explicitly
2209 # include. We'll add it back again later where needed.
2210 build_targets = [x.strip() for x in build_targets if x.strip() != "--"]
2211 test_targets = [x.strip() for x in test_targets if x.strip() != "--"]
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01002212 coverage_targets = [x.strip() for x in coverage_targets if x.strip() != "--"]
Chi Wang6357efe2020-08-25 16:23:38 +08002213 index_targets = [x.strip() for x in index_targets if x.strip() != "--"]
Philipp Wollermann2a160432019-09-19 15:57:28 +02002214
Florian Weikertdcbbdf32023-05-02 11:58:53 +02002215 diffbase = os.getenv(USE_BAZEL_DIFF_ENV_VAR, "").lower()
2216 shard_id = int(os.getenv("BUILDKITE_PARALLEL_JOB", "-1"))
2217 shard_count = int(os.getenv("BUILDKITE_PARALLEL_JOB_COUNT", "-1"))
2218 sharding_enabled = shard_id > -1 and shard_count > -1
2219
Yun Pengfb86f672023-11-10 16:18:55 +01002220 use_bazel_diff = diffbase and can_use_bazel_diff(git_commit)
Florian Weikerte417f9f2023-05-05 17:33:46 +02002221
Florian Weikertdcbbdf32023-05-02 11:58:53 +02002222 # Skip target expansion if we don't need to calculate test targets
Florian Weikerte417f9f2023-05-05 17:33:46 +02002223 if not use_bazel_diff and not sharding_enabled:
Florian Weikertdcbbdf32023-05-02 11:58:53 +02002224 return build_targets, test_targets, coverage_targets, index_targets
2225
2226 # TODO(#1614): Fix target expansion
Florian Weikert7dd9f662023-05-04 15:05:42 +02002227 expanded_test_targets = expand_test_target_patterns(bazel_binary, test_targets, test_flags)
Florian Weikertecf091c2023-04-28 10:22:23 +02002228
Florian Weikertecf091c2023-04-28 10:22:23 +02002229 actual_test_targets = (
2230 filter_unchanged_targets(
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002231 expanded_test_targets,
2232 workspace_dir,
2233 ws_setup_func,
2234 bazel_binary,
2235 diffbase,
2236 git_commit,
Florian Weikertecf091c2023-04-28 10:22:23 +02002237 )
Florian Weikerte417f9f2023-05-05 17:33:46 +02002238 if use_bazel_diff
Florian Weikertecf091c2023-04-28 10:22:23 +02002239 else expanded_test_targets
2240 )
2241
Florian Weikertdcbbdf32023-05-02 11:58:53 +02002242 if sharding_enabled:
Florian Weikert736d06e2019-05-08 13:16:42 +02002243 print_collapsed_group(
2244 ":female-detective: Calculating targets for shard {}/{}".format(
2245 shard_id + 1, shard_count
2246 )
2247 )
Florian Weikertecf091c2023-04-28 10:22:23 +02002248 actual_test_targets = get_targets_for_shard(actual_test_targets, shard_id, shard_count)
Florian Weikert736d06e2019-05-08 13:16:42 +02002249
Florian Weikertecf091c2023-04-28 10:22:23 +02002250 return build_targets, actual_test_targets, coverage_targets, index_targets
Florian Weikert736d06e2019-05-08 13:16:42 +02002251
2252
Yun Pengfb86f672023-11-10 16:18:55 +01002253def can_use_bazel_diff(git_commit):
Florian Weikerte417f9f2023-05-05 17:33:46 +02002254 matched_files = []
Yun Pengfb86f672023-11-10 16:18:55 +01002255 for f in get_modified_files(git_commit):
Florian Weikerte417f9f2023-05-05 17:33:46 +02002256 for d in DISABLE_BAZEL_DIFF_IF_MODIFIED:
2257 if d.endswith("/") and f.startswith(d) or f == d:
2258 matched_files.append(f)
2259
2260 if matched_files:
2261 eprint(
2262 "Cannot enable bazel-diff since the following files were modified:\n\t{}".format(
2263 "\n\t".join(sorted(matched_files))
2264 )
2265 )
2266
2267 return not matched_files
2268
2269
Florian Weikert7dd9f662023-05-04 15:05:42 +02002270def expand_test_target_patterns(bazel_binary, test_targets, test_flags):
2271 if not test_targets:
2272 return []
Florian Weikert736d06e2019-05-08 13:16:42 +02002273
Florian Weikert7dd9f662023-05-04 15:05:42 +02002274 print_collapsed_group(":ninja: Resolving test targets via bazel query")
Philipp Wollermann2a160432019-09-19 15:57:28 +02002275
Florian Weikert736d06e2019-05-08 13:16:42 +02002276 output = execute_command_and_get_output(
2277 [bazel_binary]
Florian Weikertda94a102022-10-21 12:24:37 +02002278 + common_startup_flags()
Florian Weikert736d06e2019-05-08 13:16:42 +02002279 + [
Yun Peng8e8cdcf2022-03-16 13:08:15 +01002280 "--nosystem_rc",
2281 "--nohome_rc",
Florian Weikert5242a592023-05-04 19:19:28 +02002282 "cquery" if os.getenv("EXP_USE_CQUERY") else "query",
Yun Pengeb435bb2023-11-17 10:37:42 +01002283 "--lockfile_mode=off",
Florian Weikert7dd9f662023-05-04 15:05:42 +02002284 get_test_query(test_targets, test_flags),
Florian Weikert736d06e2019-05-08 13:16:42 +02002285 ],
2286 print_output=False,
Philipp Wollermann54719fe2021-07-02 21:54:08 +02002287 ).strip()
2288 return output.split("\n") if output else []
Florian Weikert736d06e2019-05-08 13:16:42 +02002289
2290
Florian Weikert7dd9f662023-05-04 15:05:42 +02002291def get_test_query(test_targets, test_flags):
2292 included_targets, excluded_targets = partition_list(test_targets)
2293
2294 def FormatTargetList(targets):
2295 return " ".join("'{}'".format(t) for t in targets)
2296
Florian Weikert38a99212023-05-04 15:12:41 +02002297 query = "let t = tests(set({})) in $t".format(FormatTargetList(included_targets))
Florian Weikert7dd9f662023-05-04 15:05:42 +02002298
2299 if excluded_targets:
2300 query += " except tests(set({}))".format(FormatTargetList(excluded_targets))
2301
2302 included_tags, excluded_tags = get_test_tags(test_flags)
2303
2304 for t in excluded_tags:
Florian Weikert38a99212023-05-04 15:12:41 +02002305 query += " except attr('tags', '\\b{}\\b', $t)".format(t)
Florian Weikert7dd9f662023-05-04 15:05:42 +02002306
2307 if included_tags:
Florian Weikert38a99212023-05-04 15:12:41 +02002308 parts = ["attr('tags', '\\b{}\\b', $tt)".format(t) for t in included_tags]
Florian Weikert7dd9f662023-05-04 15:05:42 +02002309 query = "let tt = {} in {}".format(query, " union ".join(parts))
2310
2311 return query
2312
2313
2314def get_test_tags(test_flags):
2315 wanted_prefix = "--test_tag_filters="
2316
2317 for f in test_flags:
2318 if not f.startswith(wanted_prefix):
2319 continue
2320
Florian Weikertc833a7c2023-05-05 21:26:36 +02002321 tags = removeprefix(f, wanted_prefix).split(",")
Florian Weikert7dd9f662023-05-04 15:05:42 +02002322 include, exclude = partition_list(tags)
2323
2324 # Skip tests tagged as "manual" by default, unless explicitly requested
2325 manual_tag = "manual"
2326 if manual_tag not in include and manual_tag not in exclude:
2327 exclude.append(manual_tag)
2328
2329 return include, exclude
2330
Yun Peng01e617e2023-05-04 16:03:46 +02002331 return [], ["manual"]
Florian Weikert7dd9f662023-05-04 15:05:42 +02002332
2333
Florian Weikertc833a7c2023-05-05 21:26:36 +02002334def removeprefix(s, prefix):
2335 def rp(p):
2336 if s.startswith(p):
Florian Weikertb0fcf902023-05-05 22:01:50 +02002337 return s[len(p) :]
Florian Weikertc833a7c2023-05-05 21:26:36 +02002338 return s
2339
2340 func = getattr(s, "removeprefix", rp)
2341 return func(prefix)
2342
2343
Florian Weikertecf091c2023-04-28 10:22:23 +02002344def filter_unchanged_targets(
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002345 expanded_test_targets, workspace_dir, ws_setup_func, bazel_binary, diffbase, git_commit
Florian Weikertecf091c2023-04-28 10:22:23 +02002346):
2347 print_collapsed_group(
2348 f":scissors: Filtering targets that haven't been affected since {diffbase}"
2349 )
2350
2351 tmpdir = tempfile.mkdtemp()
Florian Weikertecf091c2023-04-28 10:22:23 +02002352 try:
Florian Weikertb0fcf902023-05-05 22:01:50 +02002353 resolved_diffbase = resolve_diffbase(diffbase)
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002354 eprint(f"Resolved diffbase to {resolved_diffbase}")
Florian Weikertb0fcf902023-05-05 22:01:50 +02002355
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002356 eprint("Cloning comparison repository...")
2357 diffbase_archive_url = get_commit_archive_url(resolved_diffbase)
2358 local_archive_path = download_file(diffbase_archive_url, tmpdir, "repo.tar.gz")
2359 diffbase_repo_dir = os.path.join(tmpdir, resolved_diffbase)
2360 extract_archive(local_archive_path, diffbase_repo_dir)
2361
2362 eprint("Setting up comparison repository...")
2363 os.chdir(diffbase_repo_dir)
Florian Weikert4141ce92023-05-09 09:45:55 +02002364 ws_setup_func(False)
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002365
2366 eprint(f"Downloading bazel-diff to {tmpdir}")
2367 bazel_diff_path = download_file(BAZEL_DIFF_URL, tmpdir, "bazel-diff.jar")
Florian Weikertb0fcf902023-05-05 22:01:50 +02002368 eprint(f"Running bazel-diff for {resolved_diffbase} and {git_commit}")
2369
Florian Weikertecf091c2023-04-28 10:22:23 +02002370 affected_targets = run_bazel_diff(
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002371 bazel_diff_path, diffbase_repo_dir, workspace_dir, bazel_binary, tmpdir
Florian Weikertecf091c2023-04-28 10:22:23 +02002372 )
Florian Weikertb0fcf902023-05-05 22:01:50 +02002373 except (BuildkiteException, BuildkiteInfraException) as ex:
2374 try:
2375 execute_command(
2376 [
2377 "buildkite-agent",
2378 "annotate",
2379 "--style=warning",
2380 "--context",
2381 "'diff_failed'",
2382 "This build runs all test targets even though `{}` is set "
Florian Weikert9029ccf2023-05-08 17:07:04 +02002383 "since bazel-diff failed with an error:\n```\n{}\n```".format(
Florian Weikertb0fcf902023-05-05 22:01:50 +02002384 USE_BAZEL_DIFF_ENV_VAR, ex
2385 ),
2386 ]
2387 )
Florian Weikert77d20062023-05-08 16:32:48 +02002388 execute_command(
2389 ["buildkite-agent", "annotation", "remove", "--context", BAZEL_DIFF_ANNOTATION_CTX]
2390 )
Florian Weikertb0fcf902023-05-05 22:01:50 +02002391 finally:
2392 return expanded_test_targets
Florian Weikertecf091c2023-04-28 10:22:23 +02002393 finally:
Florian Weikerte747d4f2023-05-09 09:43:47 +02002394 try:
2395 shutil.rmtree(tmpdir)
2396 except:
2397 pass
2398
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002399 os.chdir(workspace_dir)
Florian Weikertecf091c2023-04-28 10:22:23 +02002400
Florian Weikertaa0488f2023-05-03 15:06:13 +02002401 config_target_set = set(expanded_test_targets)
2402 remaining_targets = list(config_target_set.intersection(affected_targets))
2403 if len(remaining_targets) < len(expanded_test_targets):
2404 print_collapsed_group(
2405 ":scissors: Successfully reduced test targets from {} to {}".format(
2406 len(expanded_test_targets), len(remaining_targets)
2407 )
2408 )
2409
2410 skipped_targets = sorted(config_target_set.difference(remaining_targets))
2411 eprint("Skipped targets:\n\t{}".format("\n\t".join(skipped_targets)))
2412
Florian Weikertecf091c2023-04-28 10:22:23 +02002413 execute_command(
2414 [
2415 "buildkite-agent",
2416 "annotate",
2417 "--style=info",
Florian Weikert20151ab2023-05-04 20:32:36 +02002418 "--context",
Florian Weikert77d20062023-05-08 16:32:48 +02002419 BAZEL_DIFF_ANNOTATION_CTX,
Florian Weikert76736e92023-04-28 11:23:27 +02002420 "This run only contains test targets that have been changed since "
Florian Weikertb0fcf902023-05-05 22:01:50 +02002421 "{} due to the `{}` env variable".format(resolved_diffbase, USE_BAZEL_DIFF_ENV_VAR),
Florian Weikertecf091c2023-04-28 10:22:23 +02002422 ]
2423 )
2424
Florian Weikertaa0488f2023-05-03 15:06:13 +02002425 return remaining_targets
Florian Weikertecf091c2023-04-28 10:22:23 +02002426
2427
2428def resolve_diffbase(diffbase):
2429 if diffbase in AUTO_DIFFBASE_VALUES:
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002430 return resolve_revision("HEAD^")
Florian Weikertecf091c2023-04-28 10:22:23 +02002431 elif COMMIT_RE.fullmatch(diffbase):
2432 return diffbase
2433
2434 raise BuildkiteException(
Florian Weikertb0fcf902023-05-05 22:01:50 +02002435 "Invalid value '{}' for `{}` env variable. Must be a Git commit hash or one of {}".format(
Florian Weikertecf091c2023-04-28 10:22:23 +02002436 diffbase, ", ".join(AUTO_DIFFBASE_VALUES)
2437 )
2438 )
2439
2440
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002441def get_commit_archive_url(resolved_diffbase):
2442 repo_url = os.getenv("BUILDKITE_REPO", "")
2443 prefix = "+" if "googlesource" in repo_url else ""
2444 return repo_url.replace(".git", "/{}archive/{}.tar.gz".format(prefix, resolved_diffbase))
2445
2446
2447def extract_archive(archive_url, dest_dir):
2448 if not os.path.isdir(dest_dir):
2449 os.mkdir(dest_dir)
2450
Florian Weikertecf091c2023-04-28 10:22:23 +02002451 try:
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002452 with tarfile.open(archive_url, mode="r:gz") as archive:
2453 archive.extractall(dest_dir)
2454 except tarfile.TarError as ex:
2455 raise BuildkiteInfraException("Failed to extract repository archive: {}".format(ex)) from ex
2456
2457
2458def download_file(url, dest_dir, dest_filename):
2459 local_path = os.path.join(dest_dir, dest_filename)
2460 try:
Florian Weikertdfd8db42023-05-09 15:41:30 +02002461 execute_command(["curl", "-sSL", url, "-o", local_path], capture_stderr=True)
Florian Weikertecf091c2023-04-28 10:22:23 +02002462 except subprocess.CalledProcessError as ex:
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002463 raise BuildkiteInfraException("Failed to download {}: {}\n{}".format(url, ex, ex.stderr))
Florian Weikertecf091c2023-04-28 10:22:23 +02002464 return local_path
2465
2466
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002467def run_bazel_diff(bazel_diff_path, old_workspace_dir, new_workspace_dir, bazel_binary, data_dir):
Florian Weikertecf091c2023-04-28 10:22:23 +02002468 before_json = os.path.join(data_dir, "before.json")
2469 after_json = os.path.join(data_dir, "after.json")
2470 targets_file = os.path.join(data_dir, "targets.txt")
2471
2472 try:
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002473 for repo_dir, json_path in (
2474 (old_workspace_dir, before_json),
2475 (new_workspace_dir, after_json),
2476 ):
Florian Weikertecf091c2023-04-28 10:22:23 +02002477 execute_command(
2478 [
2479 "java",
2480 "-jar",
2481 bazel_diff_path,
2482 "generate-hashes",
2483 "-w",
Florian Weikert5e70d9d2023-05-08 19:20:23 +02002484 repo_dir,
Florian Weikertecf091c2023-04-28 10:22:23 +02002485 "-b",
2486 bazel_binary,
Yun Peng83e4a7f2024-01-25 10:59:00 +01002487 "--bazelCommandOptions=--lockfile_mode=off",
Florian Weikertecf091c2023-04-28 10:22:23 +02002488 json_path,
Florian Weikertdfd8db42023-05-09 15:41:30 +02002489 ],
2490 capture_stderr=True,
Florian Weikertecf091c2023-04-28 10:22:23 +02002491 )
2492
2493 execute_command(
2494 [
2495 "java",
2496 "-jar",
2497 bazel_diff_path,
2498 "get-impacted-targets",
2499 "-sh",
2500 before_json,
2501 "-fh",
2502 after_json,
2503 "-o",
2504 targets_file,
Florian Weikertdfd8db42023-05-09 15:41:30 +02002505 ],
2506 capture_stderr=True,
Florian Weikertecf091c2023-04-28 10:22:23 +02002507 )
2508 except subprocess.CalledProcessError as ex:
Florian Weikertb0fcf902023-05-05 22:01:50 +02002509 raise BuildkiteInfraException("Failed to run bazel-diff: {}\n{}".format(ex, ex.stderr))
Florian Weikertecf091c2023-04-28 10:22:23 +02002510
2511 with open(targets_file, "rt") as f:
2512 contents = f.read()
2513
2514 return contents.split("\n")
2515
2516
Florian Weikert7dd9f662023-05-04 15:05:42 +02002517def partition_list(items):
2518 included, excluded = [], []
2519 for i in items:
2520 if i.startswith("-"):
2521 excluded.append(i[1:])
Florian Weikert736d06e2019-05-08 13:16:42 +02002522 else:
Florian Weikert7dd9f662023-05-04 15:05:42 +02002523 included.append(i)
Florian Weikert736d06e2019-05-08 13:16:42 +02002524
Florian Weikert7dd9f662023-05-04 15:05:42 +02002525 return included, excluded
Florian Weikert736d06e2019-05-08 13:16:42 +02002526
2527
Philipp Wollermann87b45252020-01-22 12:43:42 +01002528def get_targets_for_shard(test_targets, shard_id, shard_count):
Florian Weikert736d06e2019-05-08 13:16:42 +02002529 # TODO(fweikert): implement a more sophisticated algorithm
Philipp Wollermann87b45252020-01-22 12:43:42 +01002530 return sorted(test_targets)[shard_id::shard_count]
Florian Weikert736d06e2019-05-08 13:16:42 +02002531
2532
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002533def execute_bazel_test(
Florian Weikertc8642af2019-02-03 23:58:51 +01002534 bazel_version,
2535 bazel_binary,
2536 platform,
2537 flags,
2538 targets,
2539 bep_file,
2540 monitor_flaky_tests,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002541):
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002542 aggregated_flags = [
2543 "--flaky_test_attempts=3",
2544 "--build_tests_only",
Tobias Werth08a93832023-04-25 16:29:52 +02002545 "--local_test_jobs=" + concurrent_test_jobs(platform),
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002546 ]
Yun Peng6a6d29d2023-11-09 14:24:06 +01002547
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002548 # Don't enable remote caching if the user enabled remote execution / caching themselves
Jakob Buchgraberc340f582018-06-22 13:48:33 +02002549 # or flaky test monitoring is enabled, as remote caching makes tests look less flaky than
2550 # they are.
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02002551 print_collapsed_group(":bazel: Computing flags for test step")
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002552 aggregated_flags += compute_flags(
Yun Peng8975c6b2019-02-28 11:55:55 +01002553 platform,
2554 flags,
Yun Peng8975c6b2019-02-28 11:55:55 +01002555 bep_file,
Philipp Wollermann87b45252020-01-22 12:43:42 +01002556 bazel_binary,
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002557 enable_remote_cache=not monitor_flaky_tests,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002558 )
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002559
Philipp Wollermannbda4b7d2019-05-16 20:04:17 +02002560 print_expanded_group(":bazel: Test ({})".format(bazel_version))
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01002561 try:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002562 execute_command(
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002563 [bazel_binary]
2564 + bazelisk_flags()
Florian Weikertda94a102022-10-21 12:24:37 +02002565 + common_startup_flags()
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002566 + ["test"]
2567 + aggregated_flags
Philipp Wollermann2a160432019-09-19 15:57:28 +02002568 + ["--"]
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002569 + targets
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002570 )
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01002571 except subprocess.CalledProcessError as e:
Yun Peng0a6a98a2019-03-06 13:07:54 +01002572 handle_bazel_failure(e, "test")
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002573
Florian Weikert7f21ca42022-02-02 17:35:23 +01002574
Florian Weikertb3439b32022-11-09 11:05:16 +01002575def execute_bazel_coverage(bazel_version, bazel_binary, platform, flags, targets):
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01002576 aggregated_flags = [
2577 "--build_tests_only",
Tobias Werth08a93832023-04-25 16:29:52 +02002578 "--local_test_jobs=" + concurrent_test_jobs(platform),
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01002579 ]
2580 print_collapsed_group(":bazel: Computing flags for coverage step")
2581 aggregated_flags += compute_flags(
2582 platform,
2583 flags,
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01002584 None,
2585 bazel_binary,
2586 enable_remote_cache=True,
2587 )
2588
2589 print_expanded_group(":bazel: Coverage ({})".format(bazel_version))
2590 try:
2591 execute_command(
2592 [bazel_binary]
2593 + bazelisk_flags()
Florian Weikertda94a102022-10-21 12:24:37 +02002594 + common_startup_flags()
Xavier Bonaventura3755ea02022-02-02 13:56:54 +01002595 + ["coverage"]
2596 + aggregated_flags
2597 + ["--"]
2598 + targets
2599 )
2600 except subprocess.CalledProcessError as e:
2601 handle_bazel_failure(e, "coverage")
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002602
Florian Weikert7f21ca42022-02-02 17:35:23 +01002603
Florian Weikertda94a102022-10-21 12:24:37 +02002604def upload_test_logs_from_bep(bep_file, tmpdir, monitor_flaky_tests):
John Cater2f0aee22023-10-19 05:34:33 -04002605 if local_run_only():
2606 return
Florian Weikert78e67de2024-02-06 12:58:41 +01002607
Chi Wangd61eb972023-01-18 11:36:18 +01002608 bazelci_agent_binary = download_bazelci_agent(tmpdir)
Chi Wangd279d582021-09-29 10:59:06 +08002609 execute_command(
Florian Weikert7f21ca42022-02-02 17:35:23 +01002610 [
2611 bazelci_agent_binary,
2612 "artifact",
2613 "upload",
Florian Weikert78e67de2024-02-06 12:58:41 +01002614 "--debug", # Force BEP upload for non-flaky failures
Florian Weikert7f21ca42022-02-02 17:35:23 +01002615 "--delay=5",
2616 "--mode=buildkite",
2617 "--build_event_json_file={}".format(bep_file),
2618 ]
Chi Wangd279d582021-09-29 10:59:06 +08002619 + (["--monitor_flaky_tests"] if monitor_flaky_tests else [])
2620 )
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002621
Philipp Wollermann3c8b8512019-07-16 15:28:03 +02002622
joeleba76887952019-05-16 15:22:17 +02002623def upload_json_profile(json_profile_path, tmpdir):
John Cater2f0aee22023-10-19 05:34:33 -04002624 if local_run_only():
2625 return
joeleba76887952019-05-16 15:22:17 +02002626 if not os.path.exists(json_profile_path):
2627 return
2628 print_collapsed_group(":gcloud: Uploading JSON Profile")
Philipp Wollermann92cf51e2019-05-16 15:31:11 +02002629 execute_command(["buildkite-agent", "artifact", "upload", json_profile_path], cwd=tmpdir)
joeleba76887952019-05-16 15:22:17 +02002630
Philipp Wollermannf436e742021-08-11 11:06:55 +02002631
Chi Wang54595a22021-06-11 17:49:58 +08002632def upload_corrupted_outputs(capture_corrupted_outputs_dir, tmpdir):
John Cater2f0aee22023-10-19 05:34:33 -04002633 if local_run_only():
2634 return
Chi Wang54595a22021-06-11 17:49:58 +08002635 if not os.path.exists(capture_corrupted_outputs_dir):
2636 return
2637 print_collapsed_group(":gcloud: Uploading corrupted outputs")
Philipp Wollermannf436e742021-08-11 11:06:55 +02002638 execute_command(
2639 ["buildkite-agent", "artifact", "upload", "{}/**/*".format(capture_corrupted_outputs_dir)],
2640 cwd=tmpdir,
2641 )
2642
Philipp Wollermann5b00a702019-07-18 11:21:38 +02002643
Philipp Wollermannaf35abf2019-05-22 17:52:01 +02002644def execute_command_and_get_output(args, shell=False, fail_if_nonzero=True, print_output=True):
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01002645 eprint(" ".join(args))
Florian Weikertc8642af2019-02-03 23:58:51 +01002646 process = subprocess.run(
2647 args,
2648 shell=shell,
2649 check=fail_if_nonzero,
2650 env=os.environ,
Florian Weikert9029ccf2023-05-08 17:07:04 +02002651 stdout=subprocess.PIPE, # We cannot use capture_output since some workers run Python <3.7
Philipp Wollermannf13804b2019-02-05 21:08:30 +01002652 errors="replace",
Florian Weikertc8642af2019-02-03 23:58:51 +01002653 universal_newlines=True,
2654 )
Florian Weikert736d06e2019-05-08 13:16:42 +02002655 if print_output:
2656 eprint(process.stdout)
2657
Florian Weikertc8642af2019-02-03 23:58:51 +01002658 return process.stdout
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002659
2660
Florian Weikertdfd8db42023-05-09 15:41:30 +02002661def execute_command(
Florian Weikertc89d6142023-11-20 14:50:35 +01002662 args,
2663 shell=False,
2664 fail_if_nonzero=True,
2665 cwd=None,
2666 print_output=True,
2667 capture_stderr=False,
2668 suppress_stdout=False,
Florian Weikertdfd8db42023-05-09 15:41:30 +02002669):
Yun Peng9337bb32020-02-28 13:31:29 +01002670 if print_output:
2671 eprint(" ".join(args))
Philipp Wollermann92cf51e2019-05-16 15:31:11 +02002672 return subprocess.run(
Florian Weikertb0fcf902023-05-05 22:01:50 +02002673 args,
2674 shell=shell,
2675 check=fail_if_nonzero,
2676 env=os.environ,
2677 cwd=cwd,
2678 errors="replace",
Yun Pengfb86f672023-11-10 16:18:55 +01002679 stdout=subprocess.DEVNULL
2680 if suppress_stdout
Yun Penge7dbd5a2023-11-13 19:00:29 +01002681 else None, # suppress_stdout=True when we don't want the output to be printed
Florian Weikertdfd8db42023-05-09 15:41:30 +02002682 stderr=subprocess.PIPE
2683 if capture_stderr
2684 else None, # capture_stderr=True when we want exceptions to contain stderr
Philipp Wollermann92cf51e2019-05-16 15:31:11 +02002685 ).returncode
Philipp Wollermannf13804b2019-02-05 21:08:30 +01002686
2687
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02002688def execute_command_background(args):
2689 eprint(" ".join(args))
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02002690 return subprocess.Popen(args, env=os.environ)
2691
Philipp Wollermann3c8b8512019-07-16 15:28:03 +02002692
Jakob Buchgraber2b63a7f2019-07-16 15:27:34 +02002693def terminate_background_process(process):
2694 if process:
2695 process.terminate()
2696 try:
2697 process.wait(timeout=10)
2698 except subprocess.TimeoutExpired:
2699 process.kill()
Philipp Wollermanne1318eb2018-08-13 15:08:01 +02002700
Philipp Wollermann3c8b8512019-07-16 15:28:03 +02002701
mai93b49bad72021-05-06 00:50:34 +02002702def create_step(label, commands, platform, shards=1, soft_fail=None):
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01002703 if "docker-image" in PLATFORMS[platform]:
Florian Weikert736d06e2019-05-08 13:16:42 +02002704 step = create_docker_step(
Florian Weikertb3439b32022-11-09 11:05:16 +01002705 label,
2706 image=PLATFORMS[platform]["docker-image"],
2707 commands=commands,
2708 queue=PLATFORMS[platform].get("queue", "default"),
2709 always_pull=PLATFORMS[platform].get("always-pull", True),
Philipp Wollermannc05ac682019-01-19 12:37:28 +01002710 )
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01002711 else:
Philipp Wollermannf3750fa2019-05-21 17:11:59 +02002712 step = {
2713 "label": label,
2714 "command": commands,
2715 "agents": {"queue": PLATFORMS[platform]["queue"]},
2716 }
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01002717
Florian Weikert736d06e2019-05-08 13:16:42 +02002718 if shards > 1:
2719 step["label"] += " (shard %n)"
2720 step["parallelism"] = shards
2721
mai93b49bad72021-05-06 00:50:34 +02002722 if soft_fail is not None:
2723 step["soft_fail"] = soft_fail
2724
Philipp Wollermann5b2f3fc2019-05-18 22:36:17 +02002725 # Enforce a global 8 hour job timeout.
2726 step["timeout_in_minutes"] = 8 * 60
2727
2728 # Automatically retry when an agent got lost (usually due to an infra flake).
Philipp Wollermannf22bba32019-07-18 11:22:50 +02002729 step["retry"] = {
2730 "automatic": [
2731 {"exit_status": -1, "limit": 3}, # Buildkite internal "agent lost" exit code
2732 {"exit_status": 137, "limit": 3}, # SIGKILL
2733 {"exit_status": 143, "limit": 3}, # SIGTERM
2734 ]
2735 }
Philipp Wollermann5b2f3fc2019-05-18 22:36:17 +02002736
Florian Weikert736d06e2019-05-08 13:16:42 +02002737 return step
2738
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01002739
Florian Weikertb3439b32022-11-09 11:05:16 +01002740def create_docker_step(
2741 label, image, commands=None, additional_env_vars=None, queue="default", always_pull=True
2742):
Philipp Wollermann0e051dd2019-05-16 11:37:52 +02002743 env = ["ANDROID_HOME", "ANDROID_NDK_HOME", "BUILDKITE_ARTIFACT_UPLOAD_DESTINATION"]
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002744 if additional_env_vars:
2745 env += ["{}={}".format(k, v) for k, v in additional_env_vars.items()]
2746
Philipp Wollermannc05ac682019-01-19 12:37:28 +01002747 step = {
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002748 "label": label,
2749 "command": commands,
Yun Peng07dafc52022-03-16 13:23:35 +01002750 "agents": {"queue": queue},
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002751 "plugins": {
Philipp Wollermann9fa03542021-07-01 23:27:53 +02002752 "docker#v3.8.0": {
Yun Peng07dafc52022-03-16 13:23:35 +01002753 "always-pull": always_pull,
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002754 "environment": env,
Philipp Wollermannc05ac682019-01-19 12:37:28 +01002755 "image": image,
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002756 "network": "host",
2757 "privileged": True,
2758 "propagate-environment": True,
Philipp Wollermann7aa95492019-05-18 22:03:24 +02002759 "propagate-uid-gid": True,
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002760 "volumes": [
Philipp Wollermann7aa95492019-05-18 22:03:24 +02002761 "/etc/group:/etc/group:ro",
2762 "/etc/passwd:/etc/passwd:ro",
Philipp Wollermann3dc99b32021-10-12 00:34:30 +02002763 "/etc/shadow:/etc/shadow:ro",
Philipp Wollermann562ef1d2021-10-20 22:15:29 +02002764 "/opt/android-ndk-r15c:/opt/android-ndk-r15c:ro",
Yun Peng72874212022-09-13 19:08:03 +02002765 "/opt/android-ndk-r25b:/opt/android-ndk-r25b:ro",
Philipp Wollermann562ef1d2021-10-20 22:15:29 +02002766 "/opt/android-sdk-linux:/opt/android-sdk-linux:ro",
Philipp Wollermann7aa95492019-05-18 22:03:24 +02002767 "/var/lib/buildkite-agent:/var/lib/buildkite-agent",
Philipp Wollermann338db4a2019-05-18 11:21:04 +02002768 "/var/lib/gitmirrors:/var/lib/gitmirrors:ro",
Philipp Wollermanna65944a2020-02-03 12:45:22 +01002769 "/var/run/docker.sock:/var/run/docker.sock",
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002770 ],
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002771 }
2772 },
2773 }
Philipp Wollermannc05ac682019-01-19 12:37:28 +01002774 if not step["command"]:
2775 del step["command"]
2776 return step
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002777
2778
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002779def print_project_pipeline(
Florian Weikertf20ae6f2019-01-16 14:32:09 +01002780 configs,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002781 project_name,
2782 http_config,
2783 file_config,
2784 git_repository,
2785 monitor_flaky_tests,
2786 use_but,
Florian Weikert60661912019-12-18 15:17:10 +01002787 notify,
Florian Weikert78e67de2024-02-06 12:58:41 +01002788 print_shard_summary,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002789):
Florian Weikert843d7a02019-02-03 17:24:50 +01002790 task_configs = configs.get("tasks", None)
2791 if not task_configs:
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02002792 raise BuildkiteException("{0} pipeline configuration is empty.".format(project_name))
2793
Jakob Buchgraberaa2af382018-02-21 19:56:54 +01002794 pipeline_steps = []
mai93f2e116c2020-10-19 09:33:14 +02002795 # If the repository is hosted on Git-on-borg, we show the link to the commit Gerrit review
2796 buildkite_repo = os.getenv("BUILDKITE_REPO")
2797 if is_git_on_borg_repo(buildkite_repo):
2798 show_gerrit_review_link(buildkite_repo, pipeline_steps)
2799
Florian Weikert5f5d3cb2019-04-15 15:36:27 +02002800 task_configs = filter_tasks_that_should_be_skipped(task_configs, pipeline_steps)
Jakob Buchgraberff2bdad2018-02-25 13:06:30 +01002801
Florian Weikert85208912019-03-07 17:08:39 +01002802 buildifier_config = configs.get("buildifier")
Philipp Wollermanndac65512019-02-05 22:14:10 +01002803 # Skip Buildifier when we test downstream projects.
Yun Peng6a6d29d2023-11-09 14:24:06 +01002804 if buildifier_config and not is_downstream_pipeline():
Florian Weikert85208912019-03-07 17:08:39 +01002805 buildifier_env_vars = {}
2806 if isinstance(buildifier_config, str):
2807 # Simple format:
2808 # ---
2809 # buildifier: latest
Philipp Wollermann22538e72021-10-02 09:43:28 +02002810 buildifier_env_vars["BUILDIFIER_VERSION"] = buildifier_config
Florian Weikert85208912019-03-07 17:08:39 +01002811 else:
2812 # Advanced format:
2813 # ---
2814 # buildifier:
2815 # version: latest
2816 # warnings: all
Philipp Wollermann22538e72021-10-02 09:43:28 +02002817 if "version" in buildifier_config:
2818 buildifier_env_vars["BUILDIFIER_VERSION"] = buildifier_config["version"]
2819 if "warnings" in buildifier_config:
2820 buildifier_env_vars["BUILDIFIER_WARNINGS"] = buildifier_config["warnings"]
Florian Weikert85208912019-03-07 17:08:39 +01002821
2822 if not buildifier_env_vars:
2823 raise BuildkiteException(
2824 'Invalid buildifier configuration entry "{}"'.format(buildifier_config)
2825 )
2826
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002827 pipeline_steps.append(
2828 create_docker_step(
Florian Weikertde96a6f2019-03-07 14:57:50 +01002829 BUILDIFIER_STEP_NAME,
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002830 image=BUILDIFIER_DOCKER_IMAGE,
Florian Weikert85208912019-03-07 17:08:39 +01002831 additional_env_vars=buildifier_env_vars,
Florian Weikert29cb7ec2019-03-07 14:52:18 +01002832 )
2833 )
Philipp Wollermannc05ac682019-01-19 12:37:28 +01002834
Philipp Wollermanndac65512019-02-05 22:14:10 +01002835 # In Bazel Downstream Project pipelines, we should test the project at the last green commit.
Yun Pengfb86f672023-11-10 16:18:55 +01002836 git_commit = get_last_green_commit(project_name) if is_downstream_pipeline() else None
Philipp Wollermanndac65512019-02-05 22:14:10 +01002837
Florian Weikert854fd852019-06-04 16:44:19 +02002838 config_hashes = set()
Yun Penge6b09902023-03-31 17:15:58 +02002839 skipped_downstream_tasks = []
Florian Weikert78e67de2024-02-06 12:58:41 +01002840 has_sharded_task = False
Florian Weikert843d7a02019-02-03 17:24:50 +01002841 for task, task_config in task_configs.items():
Florian Weikertdb832a02020-11-19 19:14:48 +01002842 platform = get_platform_for_task(task, task_config)
2843 task_name = task_config.get("name")
mai93b49bad72021-05-06 00:50:34 +02002844 soft_fail = task_config.get("soft_fail")
Florian Weikertdb832a02020-11-19 19:14:48 +01002845
Florian Weikert854fd852019-06-04 16:44:19 +02002846 # We override the Bazel version in downstream pipelines. This means that two tasks that
2847 # only differ in the value of their explicit "bazel" field will be identical in the
2848 # downstream pipeline, thus leading to duplicate work.
2849 # Consequently, we filter those duplicate tasks here.
Yun Peng6a6d29d2023-11-09 14:24:06 +01002850 if is_downstream_pipeline():
Florian Weikert854fd852019-06-04 16:44:19 +02002851 h = hash_task_config(task, task_config)
2852 if h in config_hashes:
Yun Penge6b09902023-03-31 17:15:58 +02002853 skipped_downstream_tasks.append(
Yun Peng9d905fb2023-04-24 14:13:26 +02002854 "{}: {}".format(
Florian Weikertb3439b32022-11-09 11:05:16 +01002855 create_label(platform, project_name, task_name=task_name),
Yun Penge6b09902023-03-31 17:15:58 +02002856 "The same task already exists after ignoring bazel version.",
Florian Weikertb3439b32022-11-09 11:05:16 +01002857 )
2858 )
Florian Weikert11d31432023-02-27 11:33:23 +01002859 continue
2860
Florian Weikert854fd852019-06-04 16:44:19 +02002861 config_hashes.add(h)
2862
Yun Penge6b09902023-03-31 17:15:58 +02002863 # Skip tasks with `skip_in_bazel_downstream_pipeline` specified.
2864 skipped_reason = task_config.get("skip_in_bazel_downstream_pipeline", "")
2865 if skipped_reason:
2866 skipped_downstream_tasks.append(
Yun Peng9d905fb2023-04-24 14:13:26 +02002867 "{}: {}".format(
Yun Penge6b09902023-03-31 17:15:58 +02002868 create_label(platform, project_name, task_name=task_name),
2869 skipped_reason,
2870 )
2871 )
2872 continue
2873
Florian Weikert736d06e2019-05-08 13:16:42 +02002874 shards = task_config.get("shards", "1")
2875 try:
2876 shards = int(shards)
2877 except ValueError:
2878 raise BuildkiteException("Task {} has invalid shard value '{}'".format(task, shards))
2879
Florian Weikert78e67de2024-02-06 12:58:41 +01002880 if shards > 1:
2881 has_sharded_task = True
2882
Florian Weikertd646a2d2023-12-20 18:29:17 +01002883 step = runner_step(
2884 platform=platform,
2885 task=task,
2886 task_name=task_name,
2887 project_name=project_name,
2888 http_config=http_config,
2889 file_config=file_config,
2890 git_repository=git_repository,
2891 git_commit=git_commit,
2892 monitor_flaky_tests=monitor_flaky_tests,
2893 use_but=use_but,
2894 shards=shards,
2895 soft_fail=soft_fail,
2896 )
2897 pipeline_steps.append(step)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002898
Yun Penge6b09902023-03-31 17:15:58 +02002899 if skipped_downstream_tasks:
2900 lines = ["\n- {}".format(s) for s in skipped_downstream_tasks]
Florian Weikertdb832a02020-11-19 19:14:48 +01002901 commands = [
Yun Pengee010872023-04-24 14:48:19 +02002902 "buildkite-agent meta-data exists 'has-skipped-annotation' || buildkite-agent annotate --style=info 'The following tasks were skipped:\n' --context 'ctx-skipped_downstream_tasks'",
2903 "buildkite-agent meta-data set 'has-skipped-annotation' 'true'",
Yun Penge6b09902023-03-31 17:15:58 +02002904 "buildkite-agent annotate --style=info '{}' --append --context 'ctx-skipped_downstream_tasks'".format(
Xùdōng Yáng045c9812021-08-18 01:42:35 +10002905 "".join(lines)
2906 ),
Florian Weikertdb832a02020-11-19 19:14:48 +01002907 ]
2908 pipeline_steps.append(
2909 create_step(
Yun Penge6b09902023-03-31 17:15:58 +02002910 label=":pipeline: Print information about skipped tasks",
Florian Weikertdb832a02020-11-19 19:14:48 +01002911 commands=commands,
2912 platform=DEFAULT_PLATFORM,
2913 )
2914 )
2915
Yun Peng996efad2018-11-27 17:19:44 +01002916 pipeline_slug = os.getenv("BUILDKITE_PIPELINE_SLUG")
2917 all_downstream_pipeline_slugs = []
2918 for _, config in DOWNSTREAM_PROJECTS.items():
2919 all_downstream_pipeline_slugs.append(config["pipeline_slug"])
Florian Weikert78e67de2024-02-06 12:58:41 +01002920
Ivo List23ce48d2020-11-18 13:15:34 +01002921 # We update last green commit in the following cases:
2922 # 1. This job runs on master, stable or main branch (could be a custom build launched manually)
2923 # 2. We intend to run the same job in downstream with Bazel@HEAD (eg. google-bazel-presubmit)
2924 # 3. This job is not:
2925 # - a GitHub pull request
2926 # - uses a custom built Bazel binary (in Bazel Downstream Projects pipeline)
2927 # - testing incompatible flags
2928 # - running `bazelisk --migrate` in a non-downstream pipeline
Florian Weikert78e67de2024-02-06 12:58:41 +01002929 should_update_last_green = (
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02002930 current_branch_is_main_branch()
Ivo List23ce48d2020-11-18 13:15:34 +01002931 and pipeline_slug in all_downstream_pipeline_slugs
Yun Pengc85cd0e2022-09-02 10:44:29 +02002932 and not (is_pull_request() or use_but or use_bazelisk_migrate())
Florian Weikert78e67de2024-02-06 12:58:41 +01002933 )
2934
2935 actually_print_shard_summary = has_sharded_task and print_shard_summary
2936
2937 if should_update_last_green or actually_print_shard_summary:
2938 pipeline_steps.append({"wait": None, "continue_on_failure": True})
2939
2940 if should_update_last_green:
Florian Weikertde96a6f2019-03-07 14:57:50 +01002941 # We need to call "Try Update Last Green Commit" even if there are failures,
2942 # since we don't want a failing Buildifier step to block the update of
2943 # the last green commit for this project.
2944 # try_update_last_green_commit() ensures that we don't update the commit
2945 # if any build or test steps fail.
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002946 pipeline_steps.append(
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01002947 create_step(
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01002948 label="Try Update Last Green Commit",
2949 commands=[
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002950 fetch_bazelcipy_command(),
Philipp Wollermann57b32682019-05-18 22:09:27 +02002951 PLATFORMS[DEFAULT_PLATFORM]["python"]
2952 + " bazelci.py try_update_last_green_commit",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002953 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02002954 platform=DEFAULT_PLATFORM,
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01002955 )
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002956 )
Yun Peng43239b02018-11-23 13:57:34 +01002957
Yun Pengb3c65c22023-12-11 12:02:12 +01002958 if "validate_config" in configs and not is_downstream_pipeline():
Florian Weikertc89d6142023-11-20 14:50:35 +01002959 pipeline_steps += create_config_validation_steps(
2960 git_commit or os.getenv("BUILDKITE_COMMIT")
2961 )
Florian Weikert778251c2019-04-25 15:14:44 +02002962
Yun Peng6a6d29d2023-11-09 14:24:06 +01002963 if use_bazelisk_migrate() and not is_downstream_pipeline():
Florian Weikert09813a02019-10-26 19:34:33 +02002964 # Print results of bazelisk --migrate in project pipelines that explicitly set
2965 # the USE_BAZELISK_MIGRATE env var, but that are not being run as part of a
2966 # downstream pipeline.
2967 number = os.getenv("BUILDKITE_BUILD_NUMBER")
Florian Weikert60661912019-12-18 15:17:10 +01002968 pipeline_steps += get_steps_for_aggregating_migration_results(number, notify)
Florian Weikert09813a02019-10-26 19:34:33 +02002969
Florian Weikert78e67de2024-02-06 12:58:41 +01002970 if actually_print_shard_summary:
2971 pipeline_steps.append(
2972 create_step(
2973 label="Print Test Summary for Shards",
2974 commands=[
2975 fetch_bazelcipy_command(),
2976 PLATFORMS[DEFAULT_PLATFORM]["python"] + " bazelci.py print_shard_summary",
2977 ],
2978 platform=DEFAULT_PLATFORM,
2979 )
2980 )
2981
Yun Peng6a6d29d2023-11-09 14:24:06 +01002982 print_pipeline_steps(pipeline_steps, handle_emergencies=not is_downstream_pipeline())
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01002983
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01002984
mai93f2e116c2020-10-19 09:33:14 +02002985def show_gerrit_review_link(git_repository, pipeline_steps):
2986 host = re.search(r"https://(.+?)\.googlesource", git_repository).group(1)
2987 if not host:
2988 raise BuildkiteException("Couldn't get host name from %s" % git_repository)
Florian Weikertdb832a02020-11-19 19:14:48 +01002989 text = "The transformed code used in this pipeline can be found under https://{}-review.googlesource.com/q/{}".format(
2990 host, os.getenv("BUILDKITE_COMMIT")
2991 )
Florian Weikertc6cc6172023-05-04 20:20:55 +02002992 commands = ["buildkite-agent annotate --style=info --context 'gerrit' '{}'".format(text)]
mai93f2e116c2020-10-19 09:33:14 +02002993 pipeline_steps.append(
2994 create_step(
2995 label=":pipeline: Print information about Gerrit Review Link",
2996 commands=commands,
2997 platform=DEFAULT_PLATFORM,
2998 )
2999 )
3000
3001
3002def is_git_on_borg_repo(git_repository):
3003 return git_repository and "googlesource.com" in git_repository
3004
3005
Florian Weikert854fd852019-06-04 16:44:19 +02003006def hash_task_config(task_name, task_config):
3007 # Two task configs c1 and c2 have the same hash iff they lead to two functionally identical jobs
3008 # in the downstream pipeline. This function discards the "bazel" field (since it's being
Philipp Wollermannce986af2019-07-18 14:46:05 +02003009 # overridden) and the "name" field (since it has no effect on the actual work).
Florian Weikert854fd852019-06-04 16:44:19 +02003010 # Moreover, it adds an explicit "platform" field if that's missing.
3011 cpy = task_config.copy()
3012 cpy.pop("bazel", None)
3013 cpy.pop("name", None)
3014 if "platform" not in cpy:
3015 cpy["platform"] = task_name
3016
3017 m = hashlib.md5()
Florian Weikertb3439b32022-11-09 11:05:16 +01003018 # Technically we should sort cpy[key] if it's a list of entries
3019 # whose order does not matter (e.g. targets).
3020 # However, this seems to be overkill for the current use cases.
Florian Weikert854fd852019-06-04 16:44:19 +02003021 for key in sorted(cpy):
Florian Weikert8186c392019-06-05 12:53:39 +02003022 value = "%s:%s;" % (key, cpy[key])
3023 m.update(value.encode("utf-8"))
Florian Weikert854fd852019-06-04 16:44:19 +02003024
3025 return m.digest()
3026
3027
Florian Weikert843d7a02019-02-03 17:24:50 +01003028def get_platform_for_task(task, task_config):
3029 # Most pipeline configurations have exactly one task per platform, which makes it
3030 # convenient to use the platform name as task ID. Consequently, we use the
3031 # task ID as platform if there is no explicit "platform" field.
3032 return task_config.get("platform", task)
3033
3034
Yun Pengfb86f672023-11-10 16:18:55 +01003035def create_config_validation_steps(git_commit):
Florian Weikertf52f91a2019-05-08 15:19:30 +02003036 config_files = [
Philipp Wollermann67225ec2021-08-11 11:12:51 +02003037 path
Yun Pengfb86f672023-11-10 16:18:55 +01003038 for path in get_modified_files(git_commit)
Philipp Wollermann67225ec2021-08-11 11:12:51 +02003039 if path.startswith(".bazelci/") and os.path.splitext(path)[1] in CONFIG_FILE_EXTENSIONS
Florian Weikertf52f91a2019-05-08 15:19:30 +02003040 ]
Florian Weikertf52f91a2019-05-08 15:19:30 +02003041 return [
3042 create_step(
3043 label=":cop: Validate {}".format(f),
3044 commands=[
3045 fetch_bazelcipy_command(),
3046 "{} bazelci.py project_pipeline --file_config={}".format(
Philipp Wollermann57b32682019-05-18 22:09:27 +02003047 PLATFORMS[DEFAULT_PLATFORM]["python"], f
Florian Weikertf52f91a2019-05-08 15:19:30 +02003048 ),
3049 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003050 platform=DEFAULT_PLATFORM,
Florian Weikertf52f91a2019-05-08 15:19:30 +02003051 )
3052 for f in config_files
3053 ]
3054
3055
Yun Pengfb86f672023-11-10 16:18:55 +01003056def get_modified_files(git_commit):
Florian Weikerte417f9f2023-05-05 17:33:46 +02003057 output = execute_command_and_get_output(
Yun Pengfb86f672023-11-10 16:18:55 +01003058 ["git", "diff-tree", "--no-commit-id", "--name-only", "-r", git_commit]
Florian Weikerte417f9f2023-05-05 17:33:46 +02003059 )
3060 return output.split("\n")
3061
3062
Florian Weikertd79dc502019-05-13 09:51:05 +02003063def print_pipeline_steps(pipeline_steps, handle_emergencies=True):
3064 if handle_emergencies:
3065 emergency_step = create_emergency_announcement_step_if_necessary()
3066 if emergency_step:
3067 pipeline_steps.insert(0, emergency_step)
Florian Weikert13215a82019-05-10 12:42:21 +02003068
3069 print(yaml.dump({"steps": pipeline_steps}))
3070
3071
3072def create_emergency_announcement_step_if_necessary():
3073 style = "error"
3074 message, issue_url, last_good_bazel = None, None, None
3075 try:
3076 emergency_settings = load_remote_yaml_file(EMERGENCY_FILE_URL)
3077 message = emergency_settings.get("message")
3078 issue_url = emergency_settings.get("issue_url")
3079 last_good_bazel = emergency_settings.get("last_good_bazel")
3080 except urllib.error.HTTPError as ex:
3081 message = str(ex)
3082 style = "warning"
3083
3084 if not any([message, issue_url, last_good_bazel]):
3085 return
3086
3087 text = '<span class="h1">:rotating_light: Emergency :rotating_light:</span>\n'
3088 if message:
3089 text += "- {}\n".format(message)
3090 if issue_url:
3091 text += '- Please check this <a href="{}">issue</a> for more details.\n'.format(issue_url)
3092 if last_good_bazel:
3093 text += (
3094 "- Default Bazel version is *{}*, "
3095 "unless the pipeline configuration specifies an explicit version."
3096 ).format(last_good_bazel)
3097
3098 return create_step(
3099 label=":rotating_light: Emergency :rotating_light:",
Philipp Wollermann7590b962019-05-16 11:35:03 +02003100 commands=[
3101 'buildkite-agent annotate --append --style={} --context "omg" "{}"'.format(style, text)
3102 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003103 platform=DEFAULT_PLATFORM,
Florian Weikert13215a82019-05-10 12:42:21 +02003104 )
3105
3106
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003107def runner_step(
3108 platform,
Florian Weikert843d7a02019-02-03 17:24:50 +01003109 task,
3110 task_name=None,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003111 project_name=None,
3112 http_config=None,
3113 file_config=None,
3114 git_repository=None,
3115 git_commit=None,
3116 monitor_flaky_tests=False,
3117 use_but=False,
Florian Weikert736d06e2019-05-08 13:16:42 +02003118 shards=1,
mai93b49bad72021-05-06 00:50:34 +02003119 soft_fail=None,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003120):
Philipp Wollermann57b32682019-05-18 22:09:27 +02003121 command = PLATFORMS[platform]["python"] + " bazelci.py runner --task=" + task
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003122 if http_config:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003123 command += " --http_config=" + http_config
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01003124 if file_config:
3125 command += " --file_config=" + file_config
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003126 if git_repository:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003127 command += " --git_repository=" + git_repository
Yun Peng376d2b32018-11-29 10:24:54 +01003128 if git_commit:
3129 command += " --git_commit=" + git_commit
Jakob Buchgraberc340f582018-06-22 13:48:33 +02003130 if monitor_flaky_tests:
3131 command += " --monitor_flaky_tests"
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003132 if use_but:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003133 command += " --use_but"
Florian Weikert843d7a02019-02-03 17:24:50 +01003134 label = create_label(platform, project_name, task_name=task_name)
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01003135 return create_step(
Florian Weikert9d5e4c02021-05-27 15:10:20 +02003136 label=label,
3137 commands=[fetch_bazelcipy_command(), command],
3138 platform=platform,
3139 shards=shards,
3140 soft_fail=soft_fail,
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01003141 )
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003142
3143
3144def fetch_bazelcipy_command():
Florian Weikerte1ca35d2024-01-25 13:46:16 +01003145 return "curl -sS {0}?{1} -o bazelci.py".format(SCRIPT_URL, int(time.time()))
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003146
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003147
Yun Peng8975c6b2019-02-28 11:55:55 +01003148def fetch_aggregate_incompatible_flags_test_result_command():
3149 return "curl -sS {0} -o aggregate_incompatible_flags_test_result.py".format(
Philipp Wollermanne67eec42019-05-24 15:18:20 +02003150 AGGREGATE_INCOMPATIBLE_TEST_RESULT_URL
Yun Peng8975c6b2019-02-28 11:55:55 +01003151 )
3152
3153
Florian Weikertb3439b32022-11-09 11:05:16 +01003154def upload_project_pipeline_step(project_name, git_repository, http_config, file_config):
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003155 pipeline_command = (
3156 '{0} bazelci.py project_pipeline --project_name="{1}" ' + "--git_repository={2}"
Philipp Wollermann57b32682019-05-18 22:09:27 +02003157 ).format(PLATFORMS[DEFAULT_PLATFORM]["python"], project_name, git_repository)
Yun Pengc85cd0e2022-09-02 10:44:29 +02003158 pipeline_command += " --use_but"
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003159 if http_config:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003160 pipeline_command += " --http_config=" + http_config
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01003161 if file_config:
3162 pipeline_command += " --file_config=" + file_config
Chi Wangf1f20362022-02-01 13:44:45 +01003163 pipeline_command += " | tee /dev/tty | buildkite-agent pipeline upload"
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003164
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01003165 return create_step(
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003166 label="Setup {0}".format(project_name),
3167 commands=[fetch_bazelcipy_command(), pipeline_command],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003168 platform=DEFAULT_PLATFORM,
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003169 )
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003170
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003171
Florian Weikert843d7a02019-02-03 17:24:50 +01003172def create_label(platform, project_name, build_only=False, test_only=False, task_name=None):
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003173 if build_only and test_only:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003174 raise BuildkiteException("build_only and test_only cannot be true at the same time")
Florian Weikert843d7a02019-02-03 17:24:50 +01003175 platform_display_name = PLATFORMS[platform]["emoji-name"]
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003176
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003177 if build_only:
3178 label = "Build "
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003179 elif test_only:
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003180 label = "Test "
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003181 else:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003182 label = ""
3183
Florian Weikert843d7a02019-02-03 17:24:50 +01003184 platform_label = (
3185 "{0} on {1}".format(task_name, platform_display_name)
3186 if task_name
3187 else platform_display_name
3188 )
3189
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003190 if project_name:
Florian Weikert3808e8b2023-11-13 19:52:09 +01003191 # Update get_project_name_from_job in bazel_auto_sheriff.py if you change
3192 # the expected format of "Project Foo (Task bar on OS)"
Florian Weikert843d7a02019-02-03 17:24:50 +01003193 label += "{0} ({1})".format(project_name, platform_label)
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003194 else:
Florian Weikert843d7a02019-02-03 17:24:50 +01003195 label += platform_label
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003196
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003197 return label
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003198
3199
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003200def bazel_build_step(
Florian Weikert843d7a02019-02-03 17:24:50 +01003201 task,
3202 platform,
3203 project_name,
3204 http_config=None,
3205 file_config=None,
3206 build_only=False,
3207 test_only=False,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003208):
Philipp Wollermann57b32682019-05-18 22:09:27 +02003209 pipeline_command = PLATFORMS[platform]["python"] + " bazelci.py runner"
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003210 if build_only:
Philipp Wollermannc52e26a2019-05-18 22:10:47 +02003211 pipeline_command += " --build_only --save_but"
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003212 if test_only:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003213 pipeline_command += " --test_only"
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003214 if http_config:
Philipp Wollermannf6be4662018-02-21 14:48:28 +01003215 pipeline_command += " --http_config=" + http_config
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01003216 if file_config:
3217 pipeline_command += " --file_config=" + file_config
Florian Weikert843d7a02019-02-03 17:24:50 +01003218 pipeline_command += " --task=" + task
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003219
Yun Pengbe4e2eb2022-09-16 11:42:50 +02003220 step = create_step(
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003221 label=create_label(platform, project_name, build_only, test_only),
3222 commands=[fetch_bazelcipy_command(), pipeline_command],
3223 platform=platform,
3224 )
Yun Pengbe4e2eb2022-09-16 11:42:50 +02003225 # Always try to automatically retry the bazel build step, this will make
3226 # the publish bazel binaries pipeline more reliable.
3227 step["retry"] = {
3228 "automatic": [
3229 {"exit_status": "*", "limit": 3},
3230 ]
3231 }
3232 return step
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003233
3234
Florian Weikert5f5d3cb2019-04-15 15:36:27 +02003235def filter_tasks_that_should_be_skipped(task_configs, pipeline_steps):
3236 skip_tasks = get_skip_tasks()
3237 if not skip_tasks:
Florian Weikert5f5d3cb2019-04-15 15:36:27 +02003238 return task_configs
3239
3240 actually_skipped = []
3241 skip_tasks = set(skip_tasks)
3242 for task in list(task_configs.keys()):
3243 if task in skip_tasks:
3244 actually_skipped.append(task)
3245 del task_configs[task]
3246 skip_tasks.remove(task)
3247
3248 if not task_configs:
3249 raise BuildkiteException(
3250 "Nothing to do since all tasks in the configuration should be skipped."
3251 )
3252
3253 annotations = []
3254 if actually_skipped:
3255 annotations.append(
3256 ("info", "Skipping the following task(s): {}".format(", ".join(actually_skipped)))
3257 )
3258
3259 if skip_tasks:
3260 annotations.append(
3261 (
3262 "warning",
3263 (
3264 "The following tasks should have been skipped, "
3265 "but were not part of the configuration: {}"
3266 ).format(", ".join(skip_tasks)),
3267 )
3268 )
3269
3270 if annotations:
3271 print_skip_task_annotations(annotations, pipeline_steps)
3272
3273 return task_configs
3274
3275
3276def get_skip_tasks():
3277 value = os.getenv(SKIP_TASKS_ENV_VAR, "")
3278 return [v for v in value.split(",") if v]
3279
3280
3281def print_skip_task_annotations(annotations, pipeline_steps):
3282 commands = [
3283 "buildkite-agent annotate --style={} '{}' --context 'ctx-{}'".format(s, t, hash(t))
3284 for s, t in annotations
3285 ]
3286 pipeline_steps.append(
Philipp Wollermann7a185322019-05-18 22:15:48 +02003287 create_step(
3288 label=":pipeline: Print information about skipped tasks",
3289 commands=commands,
3290 platform=DEFAULT_PLATFORM,
3291 )
Florian Weikert5f5d3cb2019-04-15 15:36:27 +02003292 )
3293
3294
Florian Weikert843d7a02019-02-03 17:24:50 +01003295def print_bazel_publish_binaries_pipeline(task_configs, http_config, file_config):
3296 if not task_configs:
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003297 raise BuildkiteException("Bazel publish binaries pipeline configuration is empty.")
3298
Florian Weikert5f5d3cb2019-04-15 15:36:27 +02003299 pipeline_steps = []
3300 task_configs = filter_tasks_that_should_be_skipped(task_configs, pipeline_steps)
3301
Florian Weikert843d7a02019-02-03 17:24:50 +01003302 platforms = [get_platform_for_task(t, tc) for t, tc in task_configs.items()]
Philipp Wollermann783d1672019-06-06 13:35:30 +02003303
3304 # These are the platforms that the bazel_publish_binaries.yml config is actually building.
3305 configured_platforms = set(filter(should_publish_binaries_for_platform, platforms))
Philipp Wollermanna2ea5d82018-08-27 14:12:10 +02003306
Philipp Wollermann783d1672019-06-06 13:35:30 +02003307 # These are the platforms that we want to build and publish according to this script.
3308 expected_platforms = set(filter(should_publish_binaries_for_platform, PLATFORMS))
Florian Weikert843d7a02019-02-03 17:24:50 +01003309
Philipp Wollermann30f314d2021-06-11 10:51:39 +02003310 # We can skip this check if we're not on the main branch, because then we're probably
3311 # building a one-off custom debugging binary anyway.
Florian Weikert7f21ca42022-02-02 17:35:23 +01003312 if current_branch_is_main_branch():
3313 missing = expected_platforms.difference(configured_platforms)
3314 if missing:
3315 raise BuildkiteException(
3316 (
3317 "Bazel publish binaries pipeline needs to build Bazel for every commit on all publish_binary-enabled platforms. "
3318 "Please add jobs for the missing platform(s) to the pipeline config: {}".format(
3319 ", ".join(missing)
3320 )
3321 )
3322 )
Jakob Buchgraber08e8e402018-03-20 19:22:07 +01003323
Yun Pengd352b6d2018-10-17 13:28:39 +02003324 # Build Bazel
Florian Weikert843d7a02019-02-03 17:24:50 +01003325 for task, task_config in task_configs.items():
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003326 pipeline_steps.append(
Florian Weikert843d7a02019-02-03 17:24:50 +01003327 bazel_build_step(
3328 task,
3329 get_platform_for_task(task, task_config),
3330 "Bazel",
3331 http_config,
3332 file_config,
3333 build_only=True,
3334 )
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003335 )
Jakob Buchgraber4631a032018-03-22 17:12:46 +01003336
Yun Peng1ce55be2023-08-11 16:19:26 +02003337 pipeline_steps.append({"wait": None, "continue_on_failure": True})
Jakob Buchgraber9d6ca8a2018-03-22 17:30:09 +01003338
Yun Pengc2dd6522018-10-17 12:58:35 +02003339 # If all builds succeed, publish the Bazel binaries to GCS.
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003340 pipeline_steps.append(
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01003341 create_step(
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003342 label="Publish Bazel Binaries",
Philipp Wollermann57b32682019-05-18 22:09:27 +02003343 commands=[
3344 fetch_bazelcipy_command(),
3345 PLATFORMS[DEFAULT_PLATFORM]["python"] + " bazelci.py publish_binaries",
3346 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003347 platform=DEFAULT_PLATFORM,
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003348 )
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003349 )
Jakob Buchgraber08e8e402018-03-20 19:22:07 +01003350
Florian Weikert13215a82019-05-10 12:42:21 +02003351 print_pipeline_steps(pipeline_steps)
Jakob Buchgraber08e8e402018-03-20 19:22:07 +01003352
3353
Florian Weikert843d7a02019-02-03 17:24:50 +01003354def should_publish_binaries_for_platform(platform):
3355 if platform not in PLATFORMS:
3356 raise BuildkiteException("Unknown platform '{}'".format(platform))
3357
3358 return PLATFORMS[platform]["publish_binary"]
3359
3360
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01003361def print_disabled_projects_info_box_step():
3362 info_text = ["Downstream testing is disabled for the following projects :sadpanda:"]
3363 for project, config in DOWNSTREAM_PROJECTS.items():
3364 disabled_reason = config.get("disabled_reason", None)
3365 if disabled_reason:
3366 info_text.append("* **%s**: %s" % (project, disabled_reason))
3367
3368 if len(info_text) == 1:
3369 return None
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01003370 return create_step(
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003371 label=":sadpanda:",
3372 commands=[
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003373 'buildkite-agent annotate --append --style=info "\n' + "\n".join(info_text) + '\n"'
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01003374 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003375 platform=DEFAULT_PLATFORM,
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003376 )
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01003377
Yun Peng6528e652019-01-02 14:41:07 +01003378
3379def print_incompatible_flags_info_box_step(incompatible_flags_map):
3380 info_text = ["Build and test with the following incompatible flags:"]
3381
3382 for flag in incompatible_flags_map:
3383 info_text.append("* **%s**: %s" % (flag, incompatible_flags_map[flag]))
3384
3385 if len(info_text) == 1:
3386 return None
Philipp Wollermannc43d3cf2019-01-10 13:24:15 +01003387 return create_step(
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003388 label="Incompatible flags info",
3389 commands=[
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003390 'buildkite-agent annotate --append --style=info "\n' + "\n".join(info_text) + '\n"'
Yun Peng6528e652019-01-02 14:41:07 +01003391 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003392 platform=DEFAULT_PLATFORM,
Philipp Wollermann1403d2c2019-01-10 13:15:51 +01003393 )
Yun Peng6528e652019-01-02 14:41:07 +01003394
3395
Yun Peng7d302f62019-01-10 16:56:15 +01003396def fetch_incompatible_flags():
Yun Peng6528e652019-01-02 14:41:07 +01003397 """
Yun Pengc85cd0e2022-09-02 10:44:29 +02003398 Return a list of incompatible flags to be tested. The key is the flag name and the value is its Github URL.
Yun Peng6528e652019-01-02 14:41:07 +01003399 """
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003400 output = subprocess.check_output(
3401 [
Yun Pengc85cd0e2022-09-02 10:44:29 +02003402 # Query for open issues with "incompatible-change" and "migration-ready" label.
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003403 "curl",
Yun Pengc85cd0e2022-09-02 10:44:29 +02003404 "https://api.github.com/search/issues?per_page=100&q=repo:bazelbuild/bazel+label:incompatible-change+label:migration-ready+state:open",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003405 ]
3406 ).decode("utf-8")
Yun Peng6528e652019-01-02 14:41:07 +01003407 issue_info = json.loads(output)
3408
Yun Pengc85cd0e2022-09-02 10:44:29 +02003409 FLAG_PATTERN = re.compile(r"^--[a-z][a-z0-9_]*$")
3410 incompatible_flags = {}
Yun Peng6528e652019-01-02 14:41:07 +01003411 for issue in issue_info["items"]:
Yun Peng6528e652019-01-02 14:41:07 +01003412 name = "--" + issue["title"].split(":")[0]
3413 url = issue["html_url"]
Yun Pengc85cd0e2022-09-02 10:44:29 +02003414 if FLAG_PATTERN.match(name):
Yun Peng6528e652019-01-02 14:41:07 +01003415 incompatible_flags[name] = url
3416 else:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003417 eprint(
Philipp Wollermann639c0452019-01-03 11:23:54 +01003418 f"{name} is not recognized as an incompatible flag, please modify the issue title "
3419 f'of {url} to "<incompatible flag name (without --)>:..."'
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003420 )
Yun Peng6528e652019-01-02 14:41:07 +01003421
Yun Pengc85cd0e2022-09-02 10:44:29 +02003422 # If INCOMPATIBLE_FLAGS is set manually, we test those flags, try to keep the URL info if possible.
3423 if "INCOMPATIBLE_FLAGS" in os.environ:
3424 given_incompatible_flags = {}
3425 for flag in os.environ["INCOMPATIBLE_FLAGS"].split(","):
3426 given_incompatible_flags[flag] = incompatible_flags.get(flag, "")
3427 return given_incompatible_flags
3428
Yun Peng6528e652019-01-02 14:41:07 +01003429 return incompatible_flags
3430
3431
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003432def print_bazel_downstream_pipeline(
Yun Pengc85cd0e2022-09-02 10:44:29 +02003433 task_configs, http_config, file_config, test_disabled_projects, notify
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003434):
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003435 pipeline_steps = []
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003436
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01003437 info_box_step = print_disabled_projects_info_box_step()
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01003438 if info_box_step is not None:
3439 pipeline_steps.append(info_box_step)
3440
Yun Pengc85cd0e2022-09-02 10:44:29 +02003441 if not use_bazelisk_migrate():
3442 if not task_configs:
3443 raise BuildkiteException("Bazel downstream pipeline configuration is empty.")
Florian Weikert843d7a02019-02-03 17:24:50 +01003444 for task, task_config in task_configs.items():
Yun Peng5599ca22019-01-16 12:32:41 +01003445 pipeline_steps.append(
Florian Weikert843d7a02019-02-03 17:24:50 +01003446 bazel_build_step(
3447 task,
3448 get_platform_for_task(task, task_config),
3449 "Bazel",
3450 http_config,
3451 file_config,
3452 build_only=True,
3453 )
Yun Peng5599ca22019-01-16 12:32:41 +01003454 )
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003455
Yun Peng5599ca22019-01-16 12:32:41 +01003456 pipeline_steps.append("wait")
Yun Pengc85cd0e2022-09-02 10:44:29 +02003457 else:
Yun Peng7d302f62019-01-10 16:56:15 +01003458 incompatible_flags_map = fetch_incompatible_flags()
Yun Peng3c1d7d12020-06-30 14:58:34 +02003459 if not incompatible_flags_map:
Florian Weikert9d5e4c02021-05-27 15:10:20 +02003460 step = create_step(
3461 label="No Incompatible flags info",
3462 commands=[
Florian Weikert42738ac2021-05-27 15:54:14 +02003463 'buildkite-agent annotate --style=error "No incompatible flag issue is found on github for current version of Bazel." --context "noinc"'
Florian Weikert9d5e4c02021-05-27 15:10:20 +02003464 ],
3465 platform=DEFAULT_PLATFORM,
Florian Weikertdb832a02020-11-19 19:14:48 +01003466 )
Florian Weikert9d5e4c02021-05-27 15:10:20 +02003467 pipeline_steps.append(step)
3468 print_pipeline_steps(pipeline_steps)
3469 return
3470
Yun Peng6528e652019-01-02 14:41:07 +01003471 info_box_step = print_incompatible_flags_info_box_step(incompatible_flags_map)
3472 if info_box_step is not None:
3473 pipeline_steps.append(info_box_step)
Yun Peng7a539ef2018-11-30 15:07:24 +01003474
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003475 for project, config in DOWNSTREAM_PROJECTS.items():
Yun Peng996efad2018-11-27 17:19:44 +01003476 disabled_reason = config.get("disabled_reason", None)
Yun Pengfb759fa2018-12-13 11:35:39 +01003477 # If test_disabled_projects is true, we add configs for disabled projects.
Florian Weikert7b3f17e2019-03-14 13:52:42 +01003478 # If test_disabled_projects is false, we add configs for not disabled projects.
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003479 if (test_disabled_projects and disabled_reason) or (
3480 not test_disabled_projects and not disabled_reason
3481 ):
Yun Peng996efad2018-11-27 17:19:44 +01003482 pipeline_steps.append(
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003483 upload_project_pipeline_step(
3484 project_name=project,
3485 git_repository=config["git_repository"],
3486 http_config=config.get("http_config", None),
3487 file_config=config.get("file_config", None),
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003488 )
3489 )
Yun Peng71c2d9f2022-01-05 10:33:51 +01003490
Yun Pengc85cd0e2022-09-02 10:44:29 +02003491 if use_bazelisk_migrate():
Yun Peng002eab92018-12-17 18:28:14 +01003492 current_build_number = os.environ.get("BUILDKITE_BUILD_NUMBER", None)
3493 if not current_build_number:
3494 raise BuildkiteException("Not running inside Buildkite")
Yun Pengc85cd0e2022-09-02 10:44:29 +02003495
Florian Weikertb3439b32022-11-09 11:05:16 +01003496 pipeline_steps += get_steps_for_aggregating_migration_results(current_build_number, notify)
Yun Peng002eab92018-12-17 18:28:14 +01003497
Florian Weikert2896edb2019-04-04 16:12:47 +02003498 if (
3499 not test_disabled_projects
Yun Pengc85cd0e2022-09-02 10:44:29 +02003500 and not use_bazelisk_migrate()
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02003501 and current_branch_is_main_branch()
Florian Weikert2896edb2019-04-04 16:12:47 +02003502 ):
Florian Weikert35906542019-04-01 11:53:53 +02003503 # Only update the last green downstream commit in the regular Bazel@HEAD + Downstream pipeline.
3504 pipeline_steps.append("wait")
3505 pipeline_steps.append(
3506 create_step(
3507 label="Try Update Last Green Downstream Commit",
3508 commands=[
3509 fetch_bazelcipy_command(),
Philipp Wollermann57b32682019-05-18 22:09:27 +02003510 PLATFORMS[DEFAULT_PLATFORM]["python"]
3511 + " bazelci.py try_update_last_green_downstream_commit",
Florian Weikert35906542019-04-01 11:53:53 +02003512 ],
Philipp Wollermann7a185322019-05-18 22:15:48 +02003513 platform=DEFAULT_PLATFORM,
Florian Weikert35906542019-04-01 11:53:53 +02003514 )
3515 )
3516
Florian Weikert13215a82019-05-10 12:42:21 +02003517 print_pipeline_steps(pipeline_steps)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01003518
3519
Florian Weikert60661912019-12-18 15:17:10 +01003520def get_steps_for_aggregating_migration_results(current_build_number, notify):
Florian Weikert09813a02019-10-26 19:34:33 +02003521 parts = [
3522 PLATFORMS[DEFAULT_PLATFORM]["python"],
3523 "aggregate_incompatible_flags_test_result.py",
3524 "--build_number=%s" % current_build_number,
Florian Weikert09813a02019-10-26 19:34:33 +02003525 ]
Florian Weikert60661912019-12-18 15:17:10 +01003526 if notify:
3527 parts.append("--notify")
Florian Weikert09813a02019-10-26 19:34:33 +02003528 return [
3529 {"wait": "~", "continue_on_failure": "true"},
3530 create_step(
3531 label="Aggregate incompatible flags test result",
3532 commands=[
3533 fetch_bazelcipy_command(),
3534 fetch_aggregate_incompatible_flags_test_result_command(),
3535 " ".join(parts),
3536 ],
3537 platform=DEFAULT_PLATFORM,
3538 ),
3539 ]
3540
3541
Yun Pengc2dd6522018-10-17 12:58:35 +02003542def bazelci_builds_download_url(platform, git_commit):
Philipp Wollermanne67eec42019-05-24 15:18:20 +02003543 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-builds"
3544 return "https://storage.googleapis.com/{}/artifacts/{}/{}/bazel".format(
3545 bucket_name, platform, git_commit
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003546 )
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003547
3548
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04003549def bazelci_builds_nojdk_download_url(platform, git_commit):
3550 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-builds"
3551 return "https://storage.googleapis.com/{}/artifacts/{}/{}/bazel_nojdk".format(
3552 bucket_name, platform, git_commit
3553 )
3554
3555
Yun Peng20d45602018-10-18 13:27:05 +02003556def bazelci_builds_gs_url(platform, git_commit):
Philipp Wollermanne67eec42019-05-24 15:18:20 +02003557 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-builds"
3558 return "gs://{}/artifacts/{}/{}/bazel".format(bucket_name, platform, git_commit)
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003559
3560
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04003561def bazelci_builds_nojdk_gs_url(platform, git_commit):
3562 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-builds"
3563 return "gs://{}/artifacts/{}/{}/bazel_nojdk".format(bucket_name, platform, git_commit)
3564
3565
mai93f04f9482020-10-20 17:22:30 +02003566def bazelci_latest_build_metadata_url():
Philipp Wollermanne67eec42019-05-24 15:18:20 +02003567 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-builds"
3568 return "gs://{}/metadata/latest.json".format(bucket_name)
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003569
3570
mai93f04f9482020-10-20 17:22:30 +02003571def bazelci_builds_metadata_url(git_commit):
3572 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-builds"
3573 return "gs://{}/metadata/{}.json".format(bucket_name, git_commit)
3574
3575
Yun Peng996efad2018-11-27 17:19:44 +01003576def bazelci_last_green_commit_url(git_repository, pipeline_slug):
Philipp Wollermanne67eec42019-05-24 15:18:20 +02003577 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-untrusted-builds"
3578 return "gs://{}/last_green_commit/{}/{}".format(
3579 bucket_name, git_repository[len("https://") :], pipeline_slug
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003580 )
Yun Pengafe67d42018-11-23 17:06:43 +01003581
3582
Florian Weikert35906542019-04-01 11:53:53 +02003583def bazelci_last_green_downstream_commit_url():
Philipp Wollermanne67eec42019-05-24 15:18:20 +02003584 bucket_name = "bazel-testing-builds" if THIS_IS_TESTING else "bazel-untrusted-builds"
3585 return "gs://{}/last_green_commit/downstream_pipeline".format(bucket_name)
Florian Weikert35906542019-04-01 11:53:53 +02003586
3587
Yun Pengfb86f672023-11-10 16:18:55 +01003588def get_last_green_commit_by_url(last_green_commit_url):
Yun Peng61a448f2018-11-23 17:11:46 +01003589 try:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003590 return (
3591 subprocess.check_output(
3592 [gsutil_command(), "cat", last_green_commit_url], env=os.environ
3593 )
3594 .decode("utf-8")
3595 .strip()
3596 )
Yun Peng61a448f2018-11-23 17:11:46 +01003597 except subprocess.CalledProcessError:
3598 return None
Yun Peng43239b02018-11-23 13:57:34 +01003599
3600
Yun Pengfb86f672023-11-10 16:18:55 +01003601def get_last_green_commit(project_name):
3602 last_green_commit_url = bazelci_last_green_commit_url(
Florian Weikertc89d6142023-11-20 14:50:35 +01003603 DOWNSTREAM_PROJECTS[project_name]["git_repository"],
3604 DOWNSTREAM_PROJECTS[project_name]["pipeline_slug"],
Yun Pengfb86f672023-11-10 16:18:55 +01003605 )
3606 return get_last_green_commit_by_url(last_green_commit_url)
3607
3608
Yun Peng358cd882018-11-29 10:25:18 +01003609def try_update_last_green_commit():
Florian Weikertde96a6f2019-03-07 14:57:50 +01003610 org_slug = os.getenv("BUILDKITE_ORGANIZATION_SLUG")
Yun Peng358cd882018-11-29 10:25:18 +01003611 pipeline_slug = os.getenv("BUILDKITE_PIPELINE_SLUG")
Florian Weikertde96a6f2019-03-07 14:57:50 +01003612 build_number = os.getenv("BUILDKITE_BUILD_NUMBER")
3613 current_job_id = os.getenv("BUILDKITE_JOB_ID")
3614
3615 client = BuildkiteClient(org=org_slug, pipeline=pipeline_slug)
3616 build_info = client.get_build_info(build_number)
3617
mai9302a609c2021-05-20 10:36:46 +02003618 # Find any failing steps other than Buildifier and steps with soft_fail enabled then "try update last green".
Philipp Wollermannce986af2019-07-18 14:46:05 +02003619 def has_failed(job):
Florian Weikertbd40a272019-03-08 10:20:18 +01003620 state = job.get("state")
3621 # Ignore steps that don't have a state (like "wait").
Florian Weikertde96a6f2019-03-07 14:57:50 +01003622 return (
Florian Weikert35906542019-04-01 11:53:53 +02003623 state is not None
3624 and state != "passed"
mai9302a609c2021-05-20 10:36:46 +02003625 and not job.get("soft_failed")
Florian Weikertde96a6f2019-03-07 14:57:50 +01003626 and job["id"] != current_job_id
3627 and job["name"] != BUILDIFIER_STEP_NAME
3628 )
3629
Philipp Wollermannce986af2019-07-18 14:46:05 +02003630 failing_jobs = [j["name"] for j in build_info["jobs"] if has_failed(j)]
Florian Weikertde96a6f2019-03-07 14:57:50 +01003631 if failing_jobs:
3632 raise BuildkiteException(
3633 "Cannot update last green commit due to {} failing step(s): {}".format(
3634 len(failing_jobs), ", ".join(failing_jobs)
3635 )
3636 )
3637
Yun Peng358cd882018-11-29 10:25:18 +01003638 git_repository = os.getenv("BUILDKITE_REPO")
Florian Weikert35906542019-04-01 11:53:53 +02003639 last_green_commit_url = bazelci_last_green_commit_url(git_repository, pipeline_slug)
3640 update_last_green_commit_if_newer(last_green_commit_url)
3641
3642
3643def update_last_green_commit_if_newer(last_green_commit_url):
Yun Pengfb86f672023-11-10 16:18:55 +01003644 last_green_commit = get_last_green_commit_by_url(last_green_commit_url)
Florian Weikert5e70d9d2023-05-08 19:20:23 +02003645 current_commit = resolve_revision("HEAD")
Yun Peng358cd882018-11-29 10:25:18 +01003646 if last_green_commit:
Jakob Buchgraber7c7ceee2019-10-28 10:28:58 +01003647 success = False
3648 try:
3649 execute_command(["git", "fetch", "-v", "origin", last_green_commit])
3650 success = True
3651 except subprocess.CalledProcessError:
3652 # If there was an error fetching the commit it typically means
3653 # that the commit does not exist anymore - due to a force push. In
3654 # order to recover from that assume that the current commit is the
3655 # newest commit.
3656 result = [current_commit]
3657 finally:
3658 if success:
3659 result = (
3660 subprocess.check_output(
3661 ["git", "rev-list", "%s..%s" % (last_green_commit, current_commit)]
3662 )
3663 .decode("utf-8")
3664 .strip()
3665 )
Philipp Wollermannce986af2019-07-18 14:46:05 +02003666 else:
3667 result = None
Yun Peng358cd882018-11-29 10:25:18 +01003668
Philipp Wollermann639c0452019-01-03 11:23:54 +01003669 # If current_commit is newer that last_green_commit, `git rev-list A..B` will output a bunch of
3670 # commits, otherwise the output should be empty.
Yun Peng358cd882018-11-29 10:25:18 +01003671 if not last_green_commit or result:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003672 execute_command(
Philipp Wollermann76a7eac2020-02-17 18:29:52 +01003673 [
3674 "echo %s | %s -h 'Cache-Control: no-store' cp - %s"
3675 % (current_commit, gsutil_command(), last_green_commit_url)
3676 ],
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003677 shell=True,
3678 )
Yun Peng358cd882018-11-29 10:25:18 +01003679 else:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003680 eprint(
3681 "Updating abandoned: last green commit (%s) is not older than current commit (%s)."
3682 % (last_green_commit, current_commit)
3683 )
3684
Yun Peng358cd882018-11-29 10:25:18 +01003685
Florian Weikert5e70d9d2023-05-08 19:20:23 +02003686def resolve_revision(rev):
3687 return subprocess.check_output(["git", "rev-parse", rev]).decode("utf-8").strip()
3688
3689
Florian Weikert35906542019-04-01 11:53:53 +02003690def try_update_last_green_downstream_commit():
3691 last_green_commit_url = bazelci_last_green_downstream_commit_url()
3692 update_last_green_commit_if_newer(last_green_commit_url)
3693
3694
Jakob Buchgraber76381e02018-02-19 16:19:56 +01003695def latest_generation_and_build_number():
Philipp Wollermannce986af2019-07-18 14:46:05 +02003696 generation = None
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003697 output = None
Philipp Wollermannce986af2019-07-18 14:46:05 +02003698 for attempt in range(5):
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003699 output = subprocess.check_output(
mai93f04f9482020-10-20 17:22:30 +02003700 [gsutil_command(), "stat", bazelci_latest_build_metadata_url()], env=os.environ
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003701 )
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003702 match = re.search("Generation:[ ]*([0-9]+)", output.decode("utf-8"))
3703 if not match:
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003704 raise BuildkiteException("Couldn't parse generation. gsutil output format changed?")
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003705 generation = match.group(1)
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003706
Philipp Wollermannff39ef52018-02-21 14:18:52 +01003707 match = re.search(r"Hash \(md5\):[ ]*([^\s]+)", output.decode("utf-8"))
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003708 if not match:
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003709 raise BuildkiteException("Couldn't parse md5 hash. gsutil output format changed?")
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003710 expected_md5hash = base64.b64decode(match.group(1))
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003711
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003712 output = subprocess.check_output(
mai93f04f9482020-10-20 17:22:30 +02003713 [gsutil_command(), "cat", bazelci_latest_build_metadata_url()], env=os.environ
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01003714 )
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003715 hasher = hashlib.md5()
3716 hasher.update(output)
3717 actual_md5hash = hasher.digest()
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003718
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003719 if expected_md5hash == actual_md5hash:
3720 break
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003721 info = json.loads(output.decode("utf-8"))
Philipp Wollermannce986af2019-07-18 14:46:05 +02003722 return generation, info["build_number"]
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003723
Jakob Buchgraber699aece2018-02-19 12:49:30 +01003724
Jakob Buchgraber88083fd2018-02-18 17:23:35 +01003725def sha256_hexdigest(filename):
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003726 sha256 = hashlib.sha256()
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02003727 with open(filename, "rb") as f:
3728 for block in iter(lambda: f.read(65536), b""):
Philipp Wollermann598b4a42018-02-19 17:03:36 +01003729 sha256.update(block)
3730 return sha256.hexdigest()
Jakob Buchgraber699aece2018-02-19 12:49:30 +01003731
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01003732
Florian Weikert78e67de2024-02-06 12:58:41 +01003733def print_shard_summary():
3734 tmpdir = tempfile.mkdtemp()
3735 try:
3736 print_collapsed_group("Fetching test artifacts...")
3737 all_test_artifacts = get_artifacts_for_failing_tests()
3738 print_collapsed_group("Dwonloading & parsing BEP files...")
3739 for base_task, current_test_artifacts in all_test_artifacts.items():
3740 failures = []
3741 for test_artifact in current_test_artifacts:
3742 local_bep_path = test_artifact.download_bep(tmpdir)
3743 if not local_bep_path:
3744 # TODO: propagate errors
3745 continue
3746
3747 for test_execution in parse_bep(local_bep_path):
3748 if test_execution.overall_status == "PASSED":
3749 continue
3750
3751 failures.append(test_execution.Format(test_artifact.job_id))
3752
3753 if failures:
3754 message = "\n".join(failures)
3755 execute_command(
3756 [
3757 "buildkite-agent",
3758 "annotate",
3759 "--style=error",
3760 f"**{base_task} Failures**\n\n{message}",
3761 "--context",
3762 f"{base_task}",
3763 ]
3764 )
3765 finally:
3766 shutil.rmtree(tmpdir)
3767
3768
3769def get_log_path_for_label(label, shard, total_shards, attempt, total_attempts):
3770 parts = [label.lstrip("/").replace(":", "/")]
3771 if total_shards > 1:
3772 parts.append(f"shard_{shard}_of_{total_shards}")
3773 if total_attempts > 1:
3774 parts.append(f"test_attempts/attempt_{attempt}.log")
3775 else:
3776 parts.append("test.log")
3777
3778 return "/".join(parts)
3779
3780
3781def get_artifacts_for_failing_tests():
3782 org_slug = os.getenv("BUILDKITE_ORGANIZATION_SLUG")
3783 pipeline_slug = os.getenv("BUILDKITE_PIPELINE_SLUG")
3784 build_number = os.getenv("BUILDKITE_BUILD_NUMBER")
3785
3786 client = BuildkiteClient(org=org_slug, pipeline=pipeline_slug)
3787 build_info = client.get_build_info(build_number)
3788
3789 paths = collections.defaultdict(list)
3790 for job in build_info["jobs"]:
3791 if job.get("state") in (None, "passed"):
3792 continue
3793
3794 # This is a bit hacky, but saves us one API request per job (to check for BUILDKITE_PARALLEL_JOB)
3795 match = _SHARD_RE.search(job.get("name", ""))
3796 if not match:
3797 continue
3798
3799 relative_bep_path, relative_log_paths = get_test_file_paths(job["id"])
3800 # TODO: show build failures in the annotation, too?
3801 if not relative_bep_path:
3802 continue
3803
3804 base_task = match.group(1)
3805 ta = TestArtifacts(
3806 job_id=job["id"],
3807 relative_bep_path=relative_bep_path,
3808 relative_log_paths=relative_log_paths,
3809 )
3810 paths[base_task].append(ta)
3811
3812 return paths
3813
3814
3815class TestArtifacts:
3816 def __init__(self, job_id, relative_bep_path, relative_log_paths) -> None:
3817 self.job_id = job_id
3818 self.relative_bep_path = relative_bep_path
3819 self.relative_log_paths = relative_log_paths
3820
3821 def download_bep(self, dest_dir: str) -> str:
3822 job_dir = os.path.join(dest_dir, self.job_id)
3823 os.makedirs(job_dir)
3824
3825 try:
3826 execute_command(
3827 [
3828 "buildkite-agent",
3829 "artifact",
3830 "download",
3831 f"*/{_TEST_BEP_FILE}",
3832 job_dir,
3833 "--step",
3834 self.job_id,
3835 ]
3836 )
3837 except:
3838 # TODO: handle exception
3839 return None
3840
3841 return os.path.join(job_dir, self.relative_bep_path)
3842
3843
3844def get_test_file_paths(job_id):
3845 bep_path = None
3846 log_paths = []
3847
3848 output = execute_command_and_get_output(
3849 [
3850 "buildkite-agent",
3851 "artifact",
3852 "search",
3853 "*",
3854 "--step",
3855 job_id,
3856 ],
3857 fail_if_nonzero=False,
3858 ).strip()
3859
3860 if not output or "no matches found" in output:
3861 return None, []
3862
3863 for line in output.split("\n"):
3864 parts = line.split(" ")
3865 # Expected format:
3866 # JOB_ID FILE_PATH TIMESTAMP
3867 if len(parts) != 3:
3868 continue
3869
3870 path = parts[1]
3871 if path.endswith(_TEST_BEP_FILE):
3872 bep_path = path
3873 elif path.endswith(".log"):
3874 log_paths.append(path)
3875
3876 return bep_path, log_paths
3877
3878
3879def format_millis(millis):
3880 def fmt(ms):
3881 return "{:.1f}s".format(ms / 1000)
3882
3883 if len(millis) == 1:
3884 return fmt(millis[0])
3885
3886 total = sum(millis)
3887 return f"{fmt(total)} ({' + '.join(fmt(ms) for ms in millis)})"
3888
3889
3890def format_test_status(status):
3891 cls = {"PASSED": "green", "FLAKY": "purple"}.get(status, "red")
3892 return f"<span class='{cls}'>{status}</span>"
3893
3894
3895# TODO here and below: use @dataclasses.dataclass(frozen=True) once Python has been updated on Docker machines
3896class TestAttempt:
3897 def __init__(self, number, status, millis) -> None:
3898 self.number = number
3899 self.status = status
3900 self.millis = millis
3901
3902
3903class TestShard:
3904 def __init__(self, number, attempts) -> None:
3905 self.number = number
3906 self.attempts = attempts
3907
3908 def _get_detailed_overall_status(self):
3909 counter = collections.Counter([a.status for a in self.attempts])
3910 passed = counter["PASSED"]
3911 no_attempts = len(self.attempts)
3912 if passed == no_attempts:
3913 return "PASSED", no_attempts, no_attempts
3914 elif passed and passed < no_attempts:
3915 return "FLAKY", no_attempts - passed, no_attempts
3916 elif counter["FAILED"]:
3917 return "FAILED", counter["FAILED"], no_attempts
3918
3919 [(status, count)] = counter.most_common(1)
3920 return status, count, no_attempts
3921
3922 def get_details(self):
3923 overall, bad_runs, total_runs = self._get_detailed_overall_status()
3924 qualifier = "" if not bad_runs else f"{bad_runs} out of "
3925 return overall, (
3926 f"in {qualifier}{total_runs} runs over {format_millis(self.attempt_millis)}"
3927 )
3928
3929 @property
3930 def overall_status(self):
3931 return self._get_detailed_overall_status()[0]
3932
3933 @property
3934 def attempt_millis(self):
3935 return [a.millis for a in self.attempts]
3936
3937
3938class TestExecution:
3939 def __init__(self, label, shards) -> None:
3940 self.label = label
3941 self.shards = shards
3942
3943 @property
3944 def overall_status(self):
3945 status_set = set(s.overall_status for s in self.shards)
3946 if len(status_set) > 1:
3947 for status in (
3948 "FAILED",
3949 "TIMEOUT",
3950 "NO_STATUS",
3951 "INCOMPLETE",
3952 "REMOTE_FAILURE",
3953 "FAILED_TO_BUILD",
3954 "PASSED",
3955 ):
3956 if status in status_set:
3957 return status
3958
3959 return next(iter(status_set))
3960
3961 @property
3962 def critical_path(self):
3963 max_millis = 0
3964 path = None
3965
3966 for s in self.shards:
3967 duration_millis = sum(s.attempt_millis)
3968 if duration_millis > max_millis:
3969 max_millis = duration_millis
3970 path = s.attempt_millis
3971
3972 return format_millis(path)
3973
3974 def Format(self, job_id: str) -> str:
3975 def get_log_url_for_shard(s):
3976 local_log_path = get_log_path_for_label(
3977 self.label,
3978 s.number,
3979 len(self.shards),
3980 1,
3981 len(s.attempts),
3982 )
3983 # TODO: check in relative_log_paths if log really exists?
3984 return os.path.join(LOG_BUCKET, job_id, local_log_path)
3985
3986 def format_shard(s):
Florian Weikert051b05f2024-02-06 13:08:29 +01003987 overall, statistics = s.get_details()
Florian Weikert78e67de2024-02-06 12:58:41 +01003988 return (
Florian Weikert051b05f2024-02-06 13:08:29 +01003989 f"{format_test_status(overall)} {statistics}: [log]({get_log_url_for_shard(s)})"
Florian Weikert78e67de2024-02-06 12:58:41 +01003990 )
3991
3992 failing_shards = [s for s in self.shards if s.overall_status != "PASSED"]
3993 if len(failing_shards) == 1:
3994 [shard] = failing_shards
3995 # TODO: show log links for failing attempts > 1?
3996 return f"- {self.label} {format_shard(shard)}"
3997
3998 shard_info = "".join(
3999 f" - Shard {s.number}/{len(self.shards)}: {format_shard(s)}" for s in failing_shards
4000 )
4001 return f"- {self.label}\n{shard_info}"
4002
4003
4004def parse_bep(path):
4005 data = collections.defaultdict(dict)
4006 for test, shard, attempt, status, millis in get_test_results_from_bep(path):
4007 ta = TestAttempt(number=attempt, status=status, millis=millis)
4008 if shard not in data[test]:
4009 data[test][shard] = []
4010
4011 data[test][shard].append(ta)
4012
4013 tests = []
4014 for test, attempts_per_shard in data.items():
4015 shards = [
4016 TestShard(number=shard, attempts=attempts_per_shard[shard])
4017 for shard in sorted(attempts_per_shard.keys())
4018 ]
4019 tests.append(TestExecution(label=test, shards=shards))
4020
4021 return tests
4022
4023
4024def get_test_results_from_bep(path):
4025 with open(path, "rt") as f:
4026 for line in f:
4027 if "testResult" not in line:
4028 continue
4029
4030 data = json.loads(line)
4031 meta = data.get("id").get("testResult")
4032 if not meta:
4033 continue
4034
4035 yield (
4036 meta["label"],
4037 meta["shard"],
4038 meta["attempt"],
4039 data["testResult"]["status"],
4040 int(data["testResult"]["testAttemptDurationMillis"]),
4041 )
4042
4043
Philipp Wollermann02955272019-04-18 18:00:48 +02004044def upload_bazel_binaries():
4045 """
4046 Uploads all Bazel binaries to a deterministic URL based on the current Git commit.
4047
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004048 Returns maps of platform names to sha256 hashes of the corresponding bazel and bazel_nojdk binaries.
Philipp Wollermann02955272019-04-18 18:00:48 +02004049 """
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004050 bazel_hashes = {}
4051 bazel_nojdk_hashes = {}
Yun Peng1ce55be2023-08-11 16:19:26 +02004052 error = None
Philipp Wollermannbdd4bf92019-06-06 14:43:50 +02004053 for platform_name, platform in PLATFORMS.items():
Philipp Wollermann783d1672019-06-06 13:35:30 +02004054 if not should_publish_binaries_for_platform(platform_name):
4055 continue
Jakob Buchgraberb13a9a82018-03-27 18:37:09 +02004056 tmpdir = tempfile.mkdtemp()
4057 try:
Philipp Wollermann783d1672019-06-06 13:35:30 +02004058 bazel_binary_path = download_bazel_binary(tmpdir, platform_name)
4059 # One platform that we build on can generate binaries for multiple platforms, e.g.
Philipp Wollermannf4aabb72019-06-25 15:59:00 +02004060 # the centos7 platform generates binaries for the "centos7" platform, but also
Philipp Wollermann783d1672019-06-06 13:35:30 +02004061 # for the generic "linux" platform.
4062 for target_platform_name in platform["publish_binary"]:
4063 execute_command(
4064 [
4065 gsutil_command(),
4066 "cp",
4067 bazel_binary_path,
4068 bazelci_builds_gs_url(target_platform_name, os.environ["BUILDKITE_COMMIT"]),
4069 ]
4070 )
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004071 bazel_hashes[target_platform_name] = sha256_hexdigest(bazel_binary_path)
4072
4073 # Also publish bazel_nojdk binaries.
4074 bazel_nojdk_binary_path = download_bazel_nojdk_binary(tmpdir, platform_name)
4075 for target_platform_name in platform["publish_binary"]:
4076 execute_command(
4077 [
4078 gsutil_command(),
4079 "cp",
4080 bazel_nojdk_binary_path,
Florian Weikertdb832a02020-11-19 19:14:48 +01004081 bazelci_builds_nojdk_gs_url(
4082 target_platform_name, os.environ["BUILDKITE_COMMIT"]
4083 ),
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004084 ]
4085 )
4086 bazel_nojdk_hashes[target_platform_name] = sha256_hexdigest(bazel_nojdk_binary_path)
Philipp Wollermann30f314d2021-06-11 10:51:39 +02004087 except subprocess.CalledProcessError as e:
Yun Peng1ce55be2023-08-11 16:19:26 +02004088 eprint(
4089 "Failured to download and publish Bazel binary for platform {}: {}".format(
4090 platform_name, e
Philipp Wollermann30f314d2021-06-11 10:51:39 +02004091 )
Yun Peng1ce55be2023-08-11 16:19:26 +02004092 )
4093 error = e
Jakob Buchgraberb13a9a82018-03-27 18:37:09 +02004094 finally:
4095 shutil.rmtree(tmpdir)
Yun Peng1ce55be2023-08-11 16:19:26 +02004096 # If we're not on the main branch, we're probably building a custom one-off binary and
4097 # ignore failures for individual platforms (it's possible that we didn't build binaries
4098 # for all platforms).
4099 if error and current_branch_is_main_branch():
4100 raise error
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004101 return bazel_hashes, bazel_nojdk_hashes
Philipp Wollermann02955272019-04-18 18:00:48 +02004102
4103
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004104def try_publish_binaries(bazel_hashes, bazel_nojdk_hashes, build_number, expected_generation):
Philipp Wollermann02955272019-04-18 18:00:48 +02004105 """
4106 Uploads the info.json file that contains information about the latest Bazel commit that was
4107 successfully built on CI.
4108 """
4109 now = datetime.datetime.now()
4110 git_commit = os.environ["BUILDKITE_COMMIT"]
4111 info = {
4112 "build_number": build_number,
4113 "build_time": now.strftime("%d-%m-%Y %H:%M"),
4114 "git_commit": git_commit,
4115 "platforms": {},
4116 }
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004117 for platform, sha256 in bazel_hashes.items():
Philipp Wollermann02955272019-04-18 18:00:48 +02004118 info["platforms"][platform] = {
4119 "url": bazelci_builds_download_url(platform, git_commit),
Philipp Wollermann783d1672019-06-06 13:35:30 +02004120 "sha256": sha256,
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004121 "nojdk_url": bazelci_builds_nojdk_download_url(platform, git_commit),
4122 "nojdk_sha256": bazel_nojdk_hashes[platform],
Philipp Wollermann02955272019-04-18 18:00:48 +02004123 }
Jakob Buchgraberb13a9a82018-03-27 18:37:09 +02004124 tmpdir = tempfile.mkdtemp()
4125 try:
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004126 info_file = os.path.join(tmpdir, "info.json")
4127 with open(info_file, mode="w", encoding="utf-8") as fp:
Jakob Buchgraber609a20e2018-02-25 17:06:51 +01004128 json.dump(info, fp, indent=2, sort_keys=True)
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004129
4130 try:
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004131 execute_command(
4132 [
4133 gsutil_command(),
4134 "-h",
4135 "x-goog-if-generation-match:" + expected_generation,
4136 "-h",
4137 "Content-Type:application/json",
4138 "cp",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004139 info_file,
mai93f04f9482020-10-20 17:22:30 +02004140 bazelci_latest_build_metadata_url(),
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004141 ]
4142 )
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004143 except subprocess.CalledProcessError:
4144 raise BinaryUploadRaceException()
mai93f04f9482020-10-20 17:22:30 +02004145
4146 execute_command(
4147 [
4148 gsutil_command(),
4149 "cp",
4150 bazelci_latest_build_metadata_url(),
4151 bazelci_builds_metadata_url(git_commit),
4152 ]
4153 )
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004154 finally:
Philipp Wollermann3e1a7712018-02-19 17:34:24 +01004155 shutil.rmtree(tmpdir)
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01004156
4157
Jakob Buchgraber76381e02018-02-19 16:19:56 +01004158def publish_binaries():
Philipp Wollermanndb024862018-02-19 17:16:56 +01004159 """
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004160 Publish Bazel binaries to GCS.
Philipp Wollermanndb024862018-02-19 17:16:56 +01004161 """
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004162 current_build_number = os.environ.get("BUILDKITE_BUILD_NUMBER", None)
4163 if not current_build_number:
4164 raise BuildkiteException("Not running inside Buildkite")
4165 current_build_number = int(current_build_number)
4166
Philipp Wollermann02955272019-04-18 18:00:48 +02004167 # Upload the Bazel binaries for this commit.
Rupert Shuttleworth7f3a91e2020-08-22 07:31:47 -04004168 bazel_hashes, bazel_nojdk_hashes = upload_bazel_binaries()
Philipp Wollermann02955272019-04-18 18:00:48 +02004169
4170 # Try to update the info.json with data about our build. This will fail (expectedly) if we're
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02004171 # not the latest build. Only do this if we're building binaries from the main branch to avoid
4172 # accidentally publishing a custom debug build as the "latest" Bazel binary.
4173 if current_branch_is_main_branch():
4174 for _ in range(5):
4175 latest_generation, latest_build_number = latest_generation_and_build_number()
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01004176
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02004177 if current_build_number <= latest_build_number:
4178 eprint(
4179 (
4180 "Current build '{0}' is not newer than latest published '{1}'. "
4181 + "Skipping publishing of binaries."
4182 ).format(current_build_number, latest_build_number)
4183 )
4184 break
4185
4186 try:
4187 try_publish_binaries(
4188 bazel_hashes, bazel_nojdk_hashes, current_build_number, latest_generation
4189 )
4190 except BinaryUploadRaceException:
4191 # Retry.
4192 continue
4193
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004194 eprint(
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02004195 "Successfully updated '{0}' to binaries from build {1}.".format(
4196 bazelci_latest_build_metadata_url(), current_build_number
4197 )
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004198 )
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004199 break
Philipp Wollermann1b5ecdc2021-06-10 21:52:55 +02004200 else:
4201 raise BuildkiteException("Could not publish binaries, ran out of attempts.")
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004202
Philipp Wollermann3c8b8512019-07-16 15:28:03 +02004203
Philipp Wollermann639c0452019-01-03 11:23:54 +01004204# This is so that multiline python strings are represented as YAML
4205# block strings.
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01004206def str_presenter(dumper, data):
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004207 if len(data.splitlines()) > 1: # check for multiline string
4208 return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
4209 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
4210
Jakob Buchgraberd20ffeb2018-02-18 03:16:43 +01004211
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004212def main(argv=None):
4213 if argv is None:
Yun Peng20d45602018-10-18 13:27:05 +02004214 argv = sys.argv[1:]
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004215
Jakob Buchgraber9952a3b2018-12-06 15:38:51 +01004216 yaml.add_representer(str, str_presenter)
4217
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02004218 parser = argparse.ArgumentParser(description="Bazel Continuous Integration Script")
Florian Weikert944209b2019-05-10 12:41:48 +02004219 parser.add_argument("--script", type=str)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01004220
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004221 subparsers = parser.add_subparsers(dest="subparsers_name")
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004222
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004223 bazel_publish_binaries_pipeline = subparsers.add_parser("bazel_publish_binaries_pipeline")
4224 bazel_publish_binaries_pipeline.add_argument("--file_config", type=str)
Jakob Buchgraber08e8e402018-03-20 19:22:07 +01004225 bazel_publish_binaries_pipeline.add_argument("--http_config", type=str)
4226 bazel_publish_binaries_pipeline.add_argument("--git_repository", type=str)
4227
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004228 bazel_downstream_pipeline = subparsers.add_parser("bazel_downstream_pipeline")
4229 bazel_downstream_pipeline.add_argument("--file_config", type=str)
4230 bazel_downstream_pipeline.add_argument("--http_config", type=str)
4231 bazel_downstream_pipeline.add_argument("--git_repository", type=str)
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004232 bazel_downstream_pipeline.add_argument(
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004233 "--test_disabled_projects", type=bool, nargs="?", const=True
4234 )
Florian Weikert60661912019-12-18 15:17:10 +01004235 bazel_downstream_pipeline.add_argument("--notify", type=bool, nargs="?", const=True)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01004236
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004237 project_pipeline = subparsers.add_parser("project_pipeline")
4238 project_pipeline.add_argument("--project_name", type=str)
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004239 project_pipeline.add_argument("--file_config", type=str)
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004240 project_pipeline.add_argument("--http_config", type=str)
4241 project_pipeline.add_argument("--git_repository", type=str)
Jakob Buchgraber66ba4fe2018-06-22 15:04:14 +02004242 project_pipeline.add_argument("--monitor_flaky_tests", type=bool, nargs="?", const=True)
Philipp Wollermann2409c6e2018-08-07 07:37:54 +02004243 project_pipeline.add_argument("--use_but", type=bool, nargs="?", const=True)
Florian Weikert60661912019-12-18 15:17:10 +01004244 project_pipeline.add_argument("--notify", type=bool, nargs="?", const=True)
Florian Weikert78e67de2024-02-06 12:58:41 +01004245 project_pipeline.add_argument("--print_shard_summary", type=bool, nargs="?", const=True)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01004246
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004247 runner = subparsers.add_parser("runner")
Florian Weikert843d7a02019-02-03 17:24:50 +01004248 runner.add_argument("--task", action="store", type=str, default="")
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004249 runner.add_argument("--file_config", type=str)
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004250 runner.add_argument("--http_config", type=str)
4251 runner.add_argument("--git_repository", type=str)
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004252 runner.add_argument(
4253 "--git_commit", type=str, help="Reset the git repository to this commit after cloning it"
4254 )
4255 runner.add_argument(
Yun Peng5012a862021-09-16 16:35:43 +02004256 "--repo_location",
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004257 type=str,
4258 help="Use an existing repository instead of cloning from github",
4259 )
4260 runner.add_argument(
Dan Halperinefda1192019-01-16 00:34:09 -08004261 "--use_bazel_at_commit", type=str, help="Use Bazel binary built at a specific commit"
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004262 )
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004263 runner.add_argument("--use_but", type=bool, nargs="?", const=True)
4264 runner.add_argument("--save_but", type=bool, nargs="?", const=True)
Yun Peng4d1d6542019-01-17 18:30:33 +01004265 runner.add_argument("--needs_clean", type=bool, nargs="?", const=True)
Philipp Wollermann598b4a42018-02-19 17:03:36 +01004266 runner.add_argument("--build_only", type=bool, nargs="?", const=True)
4267 runner.add_argument("--test_only", type=bool, nargs="?", const=True)
Jakob Buchgraber66ba4fe2018-06-22 15:04:14 +02004268 runner.add_argument("--monitor_flaky_tests", type=bool, nargs="?", const=True)
Jakob Buchgraberc340f582018-06-22 13:48:33 +02004269
Philipp Wollermannce986af2019-07-18 14:46:05 +02004270 subparsers.add_parser("publish_binaries")
4271 subparsers.add_parser("try_update_last_green_commit")
4272 subparsers.add_parser("try_update_last_green_downstream_commit")
Florian Weikert78e67de2024-02-06 12:58:41 +01004273 subparsers.add_parser("print_shard_summary")
Yun Peng358cd882018-11-29 10:25:18 +01004274
Yun Peng20d45602018-10-18 13:27:05 +02004275 args = parser.parse_args(argv)
Jakob Buchgraber0b6a39d2018-02-17 00:45:14 +01004276
Florian Weikert944209b2019-05-10 12:41:48 +02004277 if args.script:
4278 global SCRIPT_URL
4279 SCRIPT_URL = args.script
4280
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004281 try:
Jakob Buchgraber08e8e402018-03-20 19:22:07 +01004282 if args.subparsers_name == "bazel_publish_binaries_pipeline":
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004283 configs = fetch_configs(args.http_config, args.file_config)
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004284 print_bazel_publish_binaries_pipeline(
Florian Weikert843d7a02019-02-03 17:24:50 +01004285 task_configs=configs.get("tasks", None),
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004286 http_config=args.http_config,
4287 file_config=args.file_config,
4288 )
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004289 elif args.subparsers_name == "bazel_downstream_pipeline":
Yun Pengc85cd0e2022-09-02 10:44:29 +02004290 # If USE_BAZELISK_MIGRATE is true, we don't need to fetch task configs for Bazel
4291 # since we use Bazelisk to fetch Bazel binaries.
Florian Weikertb3439b32022-11-09 11:05:16 +01004292 configs = (
4293 {} if use_bazelisk_migrate() else fetch_configs(args.http_config, args.file_config)
4294 )
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004295 print_bazel_downstream_pipeline(
Florian Weikert843d7a02019-02-03 17:24:50 +01004296 task_configs=configs.get("tasks", None),
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004297 http_config=args.http_config,
4298 file_config=args.file_config,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004299 test_disabled_projects=args.test_disabled_projects,
Florian Weikert60661912019-12-18 15:17:10 +01004300 notify=args.notify,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004301 )
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004302 elif args.subparsers_name == "project_pipeline":
Yun Pengfb86f672023-11-10 16:18:55 +01004303 # Fetch the repo in case we need to use file_config.
4304 if args.git_repository:
Florian Weikertc89d6142023-11-20 14:50:35 +01004305 git_commit = (
4306 get_last_green_commit(args.project_name) if is_downstream_pipeline() else None
4307 )
Yun Pengfb86f672023-11-10 16:18:55 +01004308 clone_git_repository(args.git_repository, git_commit, suppress_stdout=True)
4309
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004310 configs = fetch_configs(args.http_config, args.file_config)
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004311 print_project_pipeline(
Florian Weikertf20ae6f2019-01-16 14:32:09 +01004312 configs=configs,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004313 project_name=args.project_name,
4314 http_config=args.http_config,
4315 file_config=args.file_config,
4316 git_repository=args.git_repository,
4317 monitor_flaky_tests=args.monitor_flaky_tests,
4318 use_but=args.use_but,
Florian Weikert60661912019-12-18 15:17:10 +01004319 notify=args.notify,
Florian Weikert78e67de2024-02-06 12:58:41 +01004320 print_shard_summary=args.print_shard_summary,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004321 )
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004322 elif args.subparsers_name == "runner":
Yun Pengfb86f672023-11-10 16:18:55 +01004323 # Fetch the repo in case we need to use file_config.
4324 if args.repo_location:
4325 os.chdir(args.repo_location)
4326 elif args.git_repository:
4327 clone_git_repository(args.git_repository, args.git_commit)
4328
Jakob Buchgraberc57d4ad2018-03-22 12:33:17 +01004329 configs = fetch_configs(args.http_config, args.file_config)
Florian Weikert843d7a02019-02-03 17:24:50 +01004330 tasks = configs.get("tasks", {})
4331 task_config = tasks.get(args.task)
4332 if not task_config:
4333 raise BuildkiteException(
4334 "No such task '{}' in configuration. Available: {}".format(
4335 args.task, ", ".join(tasks)
4336 )
4337 )
4338
Chi Wangdb965412023-05-10 09:00:46 +00004339 os.environ["BAZELCI_TASK"] = args.task
4340
Florian Weikert843d7a02019-02-03 17:24:50 +01004341 platform = get_platform_for_task(args.task, task_config)
4342
Yun Pengdb76f842021-08-30 18:39:38 +02004343 # The value of `BUILDKITE_MESSAGE` defaults to the commit message, which can be too large
4344 # on Windows, therefore we truncate the value to 1000 characters.
4345 # See https://github.com/bazelbuild/continuous-integration/issues/1218
4346 if "BUILDKITE_MESSAGE" in os.environ:
4347 os.environ["BUILDKITE_MESSAGE"] = os.environ["BUILDKITE_MESSAGE"][:1000]
4348
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004349 execute_commands(
Florian Weikertc8642af2019-02-03 23:58:51 +01004350 task_config=task_config,
Florian Weikert843d7a02019-02-03 17:24:50 +01004351 platform=platform,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004352 use_bazel_at_commit=args.use_bazel_at_commit,
4353 use_but=args.use_but,
4354 save_but=args.save_but,
Yun Peng4d1d6542019-01-17 18:30:33 +01004355 needs_clean=args.needs_clean,
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004356 build_only=args.build_only,
4357 test_only=args.test_only,
4358 monitor_flaky_tests=args.monitor_flaky_tests,
Florian Weikertc8642af2019-02-03 23:58:51 +01004359 bazel_version=task_config.get("bazel") or configs.get("bazel"),
Philipp Wollermanncd5694c2019-01-03 14:53:04 +01004360 )
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004361 elif args.subparsers_name == "publish_binaries":
4362 publish_binaries()
Yun Peng358cd882018-11-29 10:25:18 +01004363 elif args.subparsers_name == "try_update_last_green_commit":
Florian Weikert35906542019-04-01 11:53:53 +02004364 # Update the last green commit of a project pipeline
Yun Peng358cd882018-11-29 10:25:18 +01004365 try_update_last_green_commit()
Florian Weikert35906542019-04-01 11:53:53 +02004366 elif args.subparsers_name == "try_update_last_green_downstream_commit":
4367 # Update the last green commit of the downstream pipeline
4368 try_update_last_green_downstream_commit()
Florian Weikert78e67de2024-02-06 12:58:41 +01004369 elif args.subparsers_name == "print_shard_summary":
4370 print_shard_summary()
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004371 else:
4372 parser.print_help()
4373 return 2
4374 except BuildkiteException as e:
4375 eprint(str(e))
4376 return 1
4377 return 0
4378
Jakob Buchgraber95e3d572018-02-21 18:48:49 +01004379
Philipp Wollermanndcaddd92018-02-21 14:13:43 +01004380if __name__ == "__main__":
4381 sys.exit(main())