Add support for testing against a specific Xcode version (#609)
* Add support for testing against a specific Xcode version
* Protect against cache poisoning by including the Xcode version and path in the host_platform_remote_properties_override
diff --git a/buildkite/bazelci.py b/buildkite/bazelci.py
index 27c0928..20152f7 100644
--- a/buildkite/bazelci.py
+++ b/buildkite/bazelci.py
@@ -393,6 +393,9 @@
# The platform used for various steps (e.g. stuff that formerly ran on the "pipeline" workers).
DEFAULT_PLATFORM = "ubuntu1804"
+DEFAULT_XCODE_VERSION = "10.2"
+XCODE_VERSION_REGEX = re.compile(r"^\d+\.\d+(\.\d+)?$")
+
ENCRYPTED_SAUCELABS_TOKEN = """
CiQAry63sOlZtTNtuOT5DAOLkum0rGof+DOweppZY1aOWbat8zwSTQAL7Hu+rgHSOr6P4S1cu4YG
/I1BHsWaOANqUgFt6ip9/CUGGJ1qggsPGXPrmhSbSPqNAIAkpxYzabQ3mfSIObxeBmhKg2dlILA/
@@ -641,6 +644,37 @@
tmpdir = tempfile.mkdtemp()
sc_process = None
try:
+ # Activate the correct Xcode version on macOS machines.
+ if platform == "macos":
+ # Get the Xcode version from the config.
+ xcode_version = task_config.get("xcode_version", DEFAULT_XCODE_VERSION)
+ print_collapsed_group("Activating Xcode {}...".format(xcode_version))
+
+ # Ensure it's a valid version number.
+ if not isinstance(xcode_version, str):
+ raise BuildkiteException(
+ "Version number '{}' is not a string. Did you forget to put it in quotes?".format(
+ xcode_version
+ )
+ )
+ if not XCODE_VERSION_REGEX.match(xcode_version):
+ raise BuildkiteException(
+ "Invalid Xcode version format '{}', must match the format X.Y[.Z].".format(
+ xcode_version
+ )
+ )
+
+ # Check that the selected Xcode version is actually installed on the host.
+ xcode_path = "/Applications/Xcode {}.app".format(xcode_version)
+ if not os.path.exists(xcode_path):
+ raise BuildkiteException("Xcode not found at '{}'.".format(xcode_path))
+
+ # Now activate the specified Xcode version and let it install its required components.
+ # The CI machines have a sudoers config that allows the 'buildkite' user to run exactly
+ # these two commands, so don't change them without also modifying the file there.
+ execute_command(["/usr/bin/sudo", "/usr/bin/xcode-select", "--switch", xcode_path])
+ execute_command(["/usr/bin/sudo", "/usr/bin/xcodebuild", "-runFirstLaunch"])
+
# If the CI worker runs Bazelisk, we need to forward all required env variables to the test.
# Otherwise any integration test that invokes Bazel (=Bazelisk in this case) will fail.
test_env_vars = ["LocalAppData"] if platform == "windows" else ["HOME"]
@@ -1029,6 +1063,20 @@
]:
return []
+ platform_cache_key = [platform.encode("utf-8")]
+ if platform == "macos":
+ # macOS version:
+ platform_cache_key.append(subprocess.check_output(["/usr/bin/sw_vers", "-productVersion"]))
+ # Path to Xcode:
+ platform_cache_key.append(subprocess.check_output(["/usr/bin/xcode-select", "-p"]))
+ # Xcode version:
+ platform_cache_key.append(subprocess.check_output(["/usr/bin/xcodebuild", "-version"]))
+
+ platform_cache_digest = hashlib.sha256()
+ for key in platform_cache_key:
+ platform_cache_digest.update(key)
+ platform_cache_digest.update(b":")
+
flags = [
"--remote_timeout=60",
# TODO(ulfjack): figure out how to resolve
@@ -1037,7 +1085,7 @@
"--disk_cache=",
"--remote_max_connections=200",
'--host_platform_remote_properties_override=properties:{name:"platform" value:"%s"}'
- % platform,
+ % platform_cache_digest.hexdigest(),
]
if platform == "macos":