Add support for Sauce Labs
diff --git a/buildkite/bazelci.py b/buildkite/bazelci.py
index b120bdf..e9dda65 100644
--- a/buildkite/bazelci.py
+++ b/buildkite/bazelci.py
@@ -31,7 +31,9 @@
 import subprocess
 import sys
 import tempfile
+import time
 import urllib.request
+import uuid
 import yaml
 from github3 import login
 from urllib.request import url2pathname
@@ -352,6 +354,9 @@
     test_only = test_only or "build_targets" not in config
     if build_only and test_only:
         raise BuildkiteException("build_only and test_only cannot be true at the same time")
+
+    tmpdir = tempfile.mkdtemp()
+    sc_process = None
     try:
         if git_repository:
             clone_git_repository(git_repository, platform)
@@ -361,8 +366,6 @@
         if is_pull_request() and not is_trusted_author(github_user_for_pull_request(), git_repository):
             update_pull_request_verification_status(git_repository, commit, state="success")
 
-        tmpdir = tempfile.mkdtemp()
-
         if use_but:
             print_collapsed_group(":gcloud: Downloading Bazel Under Test")
             bazel_binary = download_bazel_binary(tmpdir, platform)
@@ -375,6 +378,25 @@
             execute_shell_commands(config.get("shell_commands", None))
         execute_bazel_run(bazel_binary, config.get("run_targets", None))
 
+        if config.get("sauce", None):
+            print_collapsed_group(":saucelabs: Starting Sauce Connect Proxy")
+            os.environ["SAUCE_USERNAME"] = "bazel_rules_webtesting"
+            os.environ["SAUCE_ACCESS_KEY"] = fetch_saucelabs_token()
+            os.environ["TUNNEL_ID"] = str(uuid.uuid4())
+            os.environ["BUILD_TAG"] = str(uuid.uuid4())
+            readyfile = os.path.join(tmpdir, "sc_is_ready")
+            if platform == "windows":
+                cmd = ["sauce-connect.exe", "/i", os.environ["TUNNEL_ID"], "/f", readyfile]
+            else:
+                cmd = ["sc", "-i", os.environ["TUNNEL_ID"], "-f", readyfile]
+            sc_process = execute_command_background(cmd)
+            wait_start = time.time()
+            while not os.path.exists(readyfile):
+                if time.time() - wait_start > 30:
+                    raise BuildkiteException("Sauce Connect Proxy is still not ready after 30 seconds, aborting!")
+                time.sleep(1)
+            print("Sauce Connect Proxy is ready, continuing...")
+
         if not test_only:
             build_bep_file = os.path.join(tmpdir, "build_bep.json")
             if is_pull_request():
@@ -431,6 +453,12 @@
 
             upload_test_logs(test_bep_file, tmpdir)
     finally:
+        if sc_process:
+            sc_process.terminate()
+            try:
+                sc_process.wait(timeout=10)
+            except subprocess.TimeoutExpired:
+                sc_process.kill()
         if tmpdir:
             shutil.rmtree(tmpdir)
 
@@ -443,6 +471,7 @@
 
 
 __github_token__ = None
+__saucelabs_token__ = None
 
 
 def fetch_github_token():
@@ -460,6 +489,21 @@
         os.remove("github-token.enc")
 
 
+def fetch_saucelabs_token():
+    global __saucelabs_token__
+    if __saucelabs_token__:
+        return __saucelabs_token__
+    try:
+        execute_command(
+            [gsutil_command(), "cp", "gs://bazel-encrypted-secrets/saucelabs-access-key.enc", "saucelabs-access-key.enc"])
+        __saucelabs_token__ = subprocess.check_output([gcloud_command(), "kms", "decrypt", "--location", "global", "--keyring", "buildkite",
+                                                       "--key", "saucelabs-access-key", "--ciphertext-file", "saucelabs-access-key.enc",
+                                                       "--plaintext-file", "-"], env=os.environ).decode("utf-8").strip()
+        return __saucelabs_token__
+    finally:
+        os.remove("saucelabs-access-key.enc")
+
+
 def owner_repository_from_url(git_repository):
     m = re.search(r"/([^/]+)/([^/]+)\.git$", git_repository)
     owner = m.group(1)
@@ -739,7 +783,7 @@
         "--tls_enabled=true",
         "--google_default_credentials"
     ]
-    
+
     # Enable BES / Build Results reporting.
     flags += [
         "--bes_backend=buildeventservice.googleapis.com",
@@ -747,7 +791,7 @@
         "--bes_timeout=360s",
         "--project_id=bazel-public"
     ]
-    
+
     if not accept_cached:
         flags += ["--noremote_accept_cached"]
 
@@ -773,7 +817,7 @@
         "--host_platform=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:rbe_ubuntu1604",
         "--platforms=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:rbe_ubuntu1604",
     ]
-    
+
     return flags
 
 
@@ -900,6 +944,12 @@
     return subprocess.run(args, shell=shell, check=fail_if_nonzero, env=os.environ).returncode
 
 
+def execute_command_background(args):
+    eprint(" ".join(args))
+    #return subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=os.environ)
+    return subprocess.Popen(args, env=os.environ)
+
+
 def is_trusted_author(github_user, git_repository):
     if not github_user or not git_repository:
         raise BuildkiteException("github_user and git_repository must be set.")