add pull request support
diff --git a/buildkite/bazelci.py b/buildkite/bazelci.py
index 95c568f..4229c68 100644
--- a/buildkite/bazelci.py
+++ b/buildkite/bazelci.py
@@ -37,6 +37,7 @@
 # Initialize the random number generator.
 random.seed()
 
+
 class BuildkiteException(Exception):
     """
     Raised whenever something goes wrong and we should exit with an error.
@@ -59,6 +60,12 @@
     pass
 
 
+class BazelTestFailedException(Exception):
+    """
+    Raised when a Bazel build fails.
+    """
+    pass
+
 def eprint(*args, **kwargs):
     """
     Print to stderr and flush (just in case).
@@ -289,6 +296,7 @@
     fail_pipeline = False
     tmpdir = None
     bazel_binary = "bazel"
+    commit = os.getenv("BUILDKITE_COMMIT")
     try:
         if git_repository:
             clone_git_repository(git_repository, platform)
@@ -301,24 +309,44 @@
         execute_shell_commands(config.get("shell_commands", None))
         execute_bazel_run(bazel_binary, config.get("run_targets", None))
         if not test_only:
-            execute_bazel_build(bazel_binary, platform, config.get("build_flags", []),
-                                config.get("build_targets", None))
-            if save_but:
-                upload_bazel_binary()
-        if not build_only:
-            bep_file = os.path.join(tmpdir, "build_event_json_file.json")
+            build_bep_file = os.path.join(tmpdir, "build_bep.json")
+            if is_pull_request():
+                update_pull_request_build_status(git_repository, commit, "pending", None)
             try:
-                execute_bazel_test(bazel_binary, platform, config.get("test_flags", []),
-                                   config.get("test_targets", None), bep_file)
-            except BazelTestFailedException:
+                execute_bazel_build(bazel_binary, platform, config.get("build_flags", []),
+                                    config.get("build_targets", None), build_bep_file)
+                if is_pull_request():
+                    invocation_id = bes_invocation_id(build_bep_file)
+                    update_pull_request_build_status(git_repository, commit, "success", invocation_id)
+                if save_but:
+                    upload_bazel_binary()
+            except BazelBuildFailedException:
+                if is_pull_request()
+                    invocation_id = bes_invocation_id(build_bep_file)
+                    update_pull_request_build_status(git_repository, commit, "failure", invocation_id)
                 fail_pipeline = True
-            print_test_summary(bep_file)
+        if not fail_pipeline and not build_only:
+            test_bep_file = os.path.join(tmpdir, "test_bep.json")
+            try:
+                if is_pull_request():
+                    update_pull_request_test_status(git_repository, commit, "pending", None, 0, 0, 0)
+                execute_bazel_test(bazel_binary, platform, config.get("test_flags", []),
+                                   config.get("test_targets", None), test_bep_file)
+                if is_pull_request():
+                    invocation_id = bes_invocation_id(test_bep_file)
+                    update_pull_request_test_status(git_repository, commit, "success", invocation_id, 0, 0, 0)
+            except BazelTestFailedException:
+                if is_pull_request():
+                    invocation_id = bes_invocation_id(test_bep_file)
+                    update_pull_request_test_status(git_repository, commit, "success", invocation_id, 1, 1, 1)
+                fail_pipeline = True
+            print_test_summary(test_bep_file)
 
             # Fail the pipeline if there were any flaky tests.
-            if has_flaky_tests(bep_file):
+            if has_flaky_tests(test_bep_file):
                 fail_pipeline = True
 
-            upload_test_logs(bep_file, tmpdir)
+            upload_test_logs(test_bep_file, tmpdir)
     finally:
         if tmpdir:
             shutil.rmtree(tmpdir)
@@ -328,6 +356,71 @@
         raise BuildkiteException("At least one test failed or was flaky.")
 
 
+def fetch_github_token():
+    execute_command(
+        ["gsutil", "cp", "gs://bazel-encrypted-secrets/github-token.enc", "github-token.enc"])
+    return subprocess.check_output(["gcloud", "kms", "decrypt", "--location", "global", "--keyring", "buildkite",
+                                    "--key", "github-token", "--ciphertext-file", "github-token.enc",
+                                    "--plaintext-file", "-"]).decode("utf-8").strip()
+
+
+def owner_repository_from_url(git_repository):
+    m = re.search(r"/([^/]+)/([^/]+)\.git$", repository_url)
+    owner = m.group(1)
+    repository = m.group(2)
+    return (owner, repository)
+
+
+def update_pull_request_status(git_repository, commit, state, invocation_id, description, context):
+    gh = login(token=fetch_github_token())
+    owner, repo = owner_repository_from_url(git_repository)
+    repo = gh.repository(owner=owner, repository=repo)
+    results_url = "https://source.cloud.google.com/results/invocations/" + invocation_id
+    repo.create_status(sha=commit, state=state, target_url=results_url, description=description, context=context)
+
+
+def update_pull_request_build_status(git_repository, commit, state, invocation_id):
+    description = ""
+    if state == "pending":
+        description = "Running ..."
+    elif state == "failure":
+        description = "Failed"
+    elif state == "success":
+        description = "Succeeded"
+    update_pull_request_status(git_repository, commit, state, invocation_id, description, "bazel build")
+
+
+def update_pull_request_test_status(repository_url, commit, state, invocation_id, failed, timed_out,
+                                    flaky):
+    description = ""
+    if failed == 0 and timed_out == 0 and flaky == 0:
+        description = "All Tests Passed"
+    else:
+        description = "{0} tests failed, {1} tests timed out, {2} tests are flaky".format(failed, timed_out, flaky)
+    update_pull_request_status(repository_url, commit, state, invocation_id, description, "bazel test")
+
+
+def is_pull_request():
+    third_party_repo = pos.getenv("BUILDKITE_PULL_REQUEST_REPO", "")
+    return len(third_party_repo) > 0
+
+
+def bes_invocation_id(bep_file):
+    targets = []
+    raw_data = ""
+    with open(bep_file) as f:
+        raw_data = f.read()
+    decoder = json.JSONDecoder()
+
+    pos = 0
+    while pos < len(raw_data):
+        bep_obj, size = decoder.raw_decode(raw_data[pos:])
+        if "started" in bep_obj:
+            return bep_obj["started"]["uuid"]
+        pos += size + 1
+    return None
+
+
 def show_image(url, alt):
     eprint("\033]1338;url='\"{0}\"';alt='\"{1}\"'\a\n".format(url, alt))
 
@@ -449,17 +542,22 @@
                 return True
     return False
 
-def execute_bazel_build(bazel_binary, platform, flags, targets):
+
+def execute_bazel_build(bazel_binary, platform, flags, targets, bep_file):
     if not targets:
         return
     print_expanded_group("Build")
     num_jobs = str(multiprocessing.cpu_count())
     common_flags = ["--show_progress_rate_limit=5", "--curses=yes", "--color=yes", "--keep_going",
-                    "--jobs=" + num_jobs]
+                    "--jobs=" + num_jobs], "--build_event_json_file=" + bep_file,
+                    "--experimental_build_event_json_file_path_conversion=false"]
     caching_flags = []
     if not remote_enabled(flags):
         caching_flags = remote_caching_flags(platform)
-    execute_command([bazel_binary, "build"] + common_flags + caching_flags + flags + targets)
+    try:
+        execute_command([bazel_binary, "build"] + common_flags + caching_flags + flags + targets)
+    except subprocess.CalledProcessError as e:
+        raise BazelBuildFailedException("bazel build failed with exit code {}".format(e.returncode))
 
 
 def execute_bazel_test(bazel_binary, platform, flags, targets, bep_file):
@@ -866,5 +964,6 @@
         return 1
     return 0
 
+
 if __name__ == "__main__":
     sys.exit(main())