From 07f9393c071d45d5bceec44bedf6280a18b5721d Mon Sep 17 00:00:00 2001
From: Sebastian Wilzbach <seb@wilzba.ch>
Date: Mon, 20 Feb 2017 02:00:10 +0100
Subject: [PATCH 1/3] Add CI Payloads

---
 .../circleci_project_github_dlang_dmd_2827    | 483 ++++++++++++++++
 .../circleci_project_github_dlang_phobos_1778 | 534 ++++++++++++++++++
 ...c_9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450 |  11 +
 ...c_feb55b1f448c28dfb72ce409f8b1994f097dddb5 |  11 +
 .../github_repos_dlang_dmd_pulls_6324_reviews |   3 +
 ...s_eb53933e2d0989f6da3edf8581bb3de4acac9f0e | 134 +++++
 .../projecttester_job_dmd_trigger_974         |  13 +
 .../projecttester_job_phobos_trigger_343      |  13 +
 .../travis_repos_dlang_dmd_builds_203056613   |   1 +
 9 files changed, 1203 insertions(+)
 create mode 100644 data/payloads/circleci_project_github_dlang_dmd_2827
 create mode 100644 data/payloads/circleci_project_github_dlang_phobos_1778
 create mode 100644 data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450
 create mode 100644 data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_feb55b1f448c28dfb72ce409f8b1994f097dddb5
 create mode 100644 data/payloads/github_repos_dlang_dmd_pulls_6324_reviews
 create mode 100644 data/payloads/github_repos_dlang_dmd_status_eb53933e2d0989f6da3edf8581bb3de4acac9f0e
 create mode 100644 data/payloads/projecttester_job_dmd_trigger_974
 create mode 100644 data/payloads/projecttester_job_phobos_trigger_343
 create mode 100644 data/payloads/travis_repos_dlang_dmd_builds_203056613

diff --git a/data/payloads/circleci_project_github_dlang_dmd_2827 b/data/payloads/circleci_project_github_dlang_dmd_2827
new file mode 100644
index 0000000..35fa20d
--- /dev/null
+++ b/data/payloads/circleci_project_github_dlang_dmd_2827
@@ -0,0 +1,483 @@
+{
+  "compare" : null,
+  "previous_successful_build" : {
+    "build_num" : 2826,
+    "status" : "success",
+    "build_time_millis" : 680713
+  },
+  "build_parameters" : null,
+  "oss" : true,
+  "all_commit_details_truncated" : false,
+  "committer_date" : "2017-02-19T00:47:51Z",
+  "steps" : [ {
+    "name" : "Starting the build",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Starting the build",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:48:55.197Z",
+      "type" : "infrastructure",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/60721088526a14aa37be8a85-dlang-dmd-0-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=b0d2622e4011a94e44d09252202331ae5599c9737168baf93b0b737426655e32",
+      "start_time" : "2017-02-19T00:48:51.429Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 0,
+      "run_time_millis" : 3768,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Start container",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Start container",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:02.246Z",
+      "source" : "config",
+      "type" : "infrastructure",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/70721088526a14aa77be8a85-dlang-dmd-1-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=17fdba6a06b448d6eae45faa70c06d6244ba037a4c0ab2a7ea442256e4f55e74",
+      "start_time" : "2017-02-19T00:48:55.206Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 1,
+      "run_time_millis" : 7040,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Enable SSH",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Enable SSH",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:03.907Z",
+      "type" : "infrastructure",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/80721088526a14aae7be8a85-dlang-dmd-2-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=d247ac240fdb5d29f28b4de039c261e82ad9383398d9d176d9bb03210736bcf8",
+      "start_time" : "2017-02-19T00:49:02.254Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 2,
+      "run_time_millis" : 1653,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Restore source cache",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Restore source cache",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:06.292Z",
+      "source" : "cache",
+      "type" : "checkout",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/90721088526a14aaf7be8a85-dlang-dmd-3-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=f78a7285f71cd619c3bb00d56eb6e2154bec0864ee65e9db2b4e65415e497ae2",
+      "start_time" : "2017-02-19T00:49:03.915Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 3,
+      "run_time_millis" : 2377,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Checkout using CircleCI fork PR checkout user key: 72:d2:fd:18:4c:f5:15:b1:ab:47:44:65:9d:9b:08:0a",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Checkout using CircleCI fork PR checkout user key: 72:d2:fd:18:4c:f5:15:b1:ab:47:44:65:9d:9b:08:0a",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:09.485Z",
+      "source" : "config",
+      "type" : "checkout",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/a0721088526a14aa28be8a85-dlang-dmd-4-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=12c112e388f72461a416c7c8ae15c78335d386bec18f91f07018e610a7c031d1",
+      "start_time" : "2017-02-19T00:49:06.301Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 4,
+      "run_time_millis" : 3184,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Configure the build",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Configure the build",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:12.936Z",
+      "source" : "cache",
+      "type" : "machine",
+      "truncation_len" : null,
+      "start_time" : "2017-02-19T00:49:09.493Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 5,
+      "run_time_millis" : 3443,
+      "has_output" : false
+    } ]
+  }, {
+    "name" : "Suppressing export of environment variables  fork PR builds",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Suppressing export of environment variables  fork PR builds",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:12.944Z",
+      "source" : "db",
+      "type" : "machine",
+      "truncation_len" : null,
+      "start_time" : "2017-02-19T00:49:12.943Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 6,
+      "run_time_millis" : 1,
+      "has_output" : false
+    } ]
+  }, {
+    "name" : "Restore cache",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Restore cache",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:20.153Z",
+      "source" : "cache",
+      "type" : "machine",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/d0721088526a14aa88be8a85-dlang-dmd-7-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=e618b2e25e2b9f5667ee863cadb8279e8cbf4c06286a47470585846c192d15e2",
+      "start_time" : "2017-02-19T00:49:12.950Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 7,
+      "run_time_millis" : 7203,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh install-deps",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh install-deps",
+      "bash_command" : "./circleci.sh install-deps",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:21.398Z",
+      "source" : "config",
+      "type" : "dependencies",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/e0721088526a14aa09be8a85-dlang-dmd-8-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=a9168d99914f07cd8e9259e0fa425525b587a1b9793cbda46a1b51f66fe40fd4",
+      "start_time" : "2017-02-19T00:49:20.166Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 8,
+      "run_time_millis" : 1232,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Save cache",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Save cache",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T00:49:24.572Z",
+      "source" : "cache",
+      "type" : "database",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/f0721088526a14aa19be8a85-dlang-dmd-9-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=1f8ca9517fab06baa9962030590c77b420dbd2508e1307e06ca03eafea3e62f8",
+      "start_time" : "2017-02-19T00:49:21.405Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 9,
+      "run_time_millis" : 3167,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh coverage",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : true,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh coverage",
+      "bash_command" : "./circleci.sh coverage",
+      "status" : "timedout",
+      "timedout" : true,
+      "continue" : null,
+      "end_time" : "2017-02-19T01:00:05.670Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/01721088526a14aa49be8a85-dlang-dmd-10-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=ad0df1b952c38e9471865820906be451bb21c5354fdb2a47a22797172ab66deb",
+      "start_time" : "2017-02-19T00:49:24.580Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 10,
+      "run_time_millis" : 641090,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "rm -rf test/runnable/extra-files",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "rm -rf test/runnable/extra-files",
+      "bash_command" : "rm -rf test/runnable/extra-files",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T01:00:06.612Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "start_time" : "2017-02-19T01:00:05.686Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 11,
+      "run_time_millis" : 926,
+      "has_output" : false
+    } ]
+  }, {
+    "name" : "bash <(curl -s https://codecov.io/bash)",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "bash <(curl -s https://codecov.io/bash)",
+      "bash_command" : "bash <(curl -s https://codecov.io/bash)",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T01:00:07.647Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/5e721088526a14aa61ee8a85-dlang-dmd-12-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=b83020e0f21e53fa818292bf72880b12f90f1be006a50e05a062f8fe4dde81d3",
+      "start_time" : "2017-02-19T01:00:06.618Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 12,
+      "run_time_millis" : 1029,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Collect test metadata",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Collect test metadata",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T01:00:09.413Z",
+      "type" : "teardown",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/7e721088526a14aa71ee8a85-dlang-dmd-13-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=748b248a9982f561a2997eaf63dd54e941c6312e66bbb516c34eb7dc12894913",
+      "start_time" : "2017-02-19T01:00:07.654Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 13,
+      "run_time_millis" : 1759,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Collect artifacts",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Collect artifacts",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T01:00:16.272Z",
+      "type" : "teardown",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/9e721088526a14aa91ee8a85-dlang-dmd-14-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T005748Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=8fecdf913a98dcc45516aef7c582841e6d574cf3a437903ff0e33a400ce25eab",
+      "start_time" : "2017-02-19T01:00:09.422Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 14,
+      "run_time_millis" : 6850,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Disable SSH",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Disable SSH",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-19T01:00:16.285Z",
+      "type" : "teardown",
+      "truncation_len" : null,
+      "start_time" : "2017-02-19T01:00:16.281Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 17,
+      "run_time_millis" : 4,
+      "has_output" : false
+    } ]
+  } ],
+  "body" : "",
+  "usage_queued_at" : "2017-02-19T00:47:24.323Z",
+  "fail_reason" : null,
+  "retry_of" : null,
+  "reponame" : "dmd",
+  "ssh_users" : [ ],
+  "build_url" : "https://circleci.com/gh/dlang/dmd/2827",
+  "parallel" : 1,
+  "failed" : null,
+  "branch" : "pull/6552",
+  "username" : "dlang",
+  "author_date" : "2017-02-19T00:47:51Z",
+  "why" : "github",
+  "user" : {
+    "is_user" : true,
+    "login" : "WalterBright",
+    "avatar_url" : "https://avatars.githubusercontent.com/u/568298?v=3",
+    "name" : "Walter Bright",
+    "vcs_type" : "github",
+    "id" : 568298
+  },
+  "vcs_revision" : "378ec2f7616ec7ca4554c5381b45561473b0c218",
+  "owners" : [ "WalterBright" ],
+  "vcs_tag" : null,
+  "pull_requests" : [ {
+    "head_sha" : "378ec2f7616ec7ca4554c5381b45561473b0c218",
+    "url" : "https://github.com/dlang/dmd/pull/6552"
+  } ],
+  "build_num" : 2827,
+  "infrastructure_fail" : false,
+  "committer_email" : "walter@walterbright.com",
+  "previous" : null,
+  "status" : "timedout",
+  "committer_name" : "Walter Bright",
+  "retries" : null,
+  "subject" : "todt.d: replace use of abytes with toStringSymbol()",
+  "vcs_type" : "github",
+  "timedout" : true,
+  "dont_build" : null,
+  "lifecycle" : "finished",
+  "no_dependency_cache" : false,
+  "stop_time" : "2017-02-19T01:00:16.492Z",
+  "ssh_disabled" : false,
+  "build_time_millis" : 685569,
+  "circle_yml" : {
+    "string" : "dependencies:\n  pre:\n    - ./circleci.sh install-deps\n  cache_directories:\n    - \"~/dlang\"\n\ntest:\n  override:\n    - ./circleci.sh coverage:\n        parallel: true\n\n  post:\n    # CodeCov gets confused by lst files which it can't matched\n    - rm -rf test/runnable/extra-files\n    - bash <(curl -s https://codecov.io/bash)\n\ngeneral:\n  branches:\n    ignore:\n      - dmd-1.x\n"
+  },
+  "messages" : [ ],
+  "is_first_green_build" : false,
+  "job_name" : null,
+  "start_time" : "2017-02-19T00:48:50.923Z",
+  "canceler" : null,
+  "all_commit_details" : [ {
+    "committer_date" : "2017-02-19T00:47:51Z",
+    "body" : "",
+    "author_date" : "2017-02-19T00:47:51Z",
+    "committer_email" : "walter@walterbright.com",
+    "commit" : "378ec2f7616ec7ca4554c5381b45561473b0c218",
+    "committer_login" : "WalterBright",
+    "committer_name" : "Walter Bright",
+    "subject" : "todt.d: replace use of abytes with toStringSymbol()",
+    "commit_url" : "https://github.com/dlang/dmd/commit/378ec2f7616ec7ca4554c5381b45561473b0c218",
+    "author_login" : "WalterBright",
+    "author_name" : "Walter Bright",
+    "author_email" : "walter@walterbright.com"
+  } ],
+  "outcome" : "timedout",
+  "vcs_url" : "https://github.com/dlang/dmd",
+  "author_name" : "Walter Bright",
+  "node" : [ {
+    "public_ip_addr" : "54.211.139.201",
+    "port" : 64703,
+    "username" : "ubuntu",
+    "image_id" : "s3://lxc-images/circletar-1741-cc586-20160424T232626Z",
+    "ssh_enabled" : null
+  } ],
+  "queued_at" : "2017-02-19T00:48:50.895Z",
+  "canceled" : false,
+  "author_email" : "walter@walterbright.com"
+}
\ No newline at end of file
diff --git a/data/payloads/circleci_project_github_dlang_phobos_1778 b/data/payloads/circleci_project_github_dlang_phobos_1778
new file mode 100644
index 0000000..c422979
--- /dev/null
+++ b/data/payloads/circleci_project_github_dlang_phobos_1778
@@ -0,0 +1,534 @@
+{
+  "compare" : null,
+  "previous_successful_build" : {
+    "build_num" : 1767,
+    "status" : "success",
+    "build_time_millis" : 521777
+  },
+  "build_parameters" : null,
+  "oss" : true,
+  "all_commit_details_truncated" : false,
+  "committer_date" : "2017-02-18T02:47:25Z",
+  "steps" : [ {
+    "name" : "Starting the build",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Starting the build",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:35.897Z",
+      "type" : "infrastructure",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/dfbc80af324879992c5b7a85-dlang-phobos-0-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=219cc983fb65b6cc8b2a7a7b7f35383f547da64ec5dd5745c892fea2a9245780",
+      "start_time" : "2017-02-18T02:47:30.573Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 0,
+      "run_time_millis" : 5324,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Start container",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Start container",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:43.285Z",
+      "source" : "config",
+      "type" : "infrastructure",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/efbc80af324879997c5b7a85-dlang-phobos-1-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=59550b2c136b50676ec874c04aea6be45db6fa234d59304dc8043f363e55f6a6",
+      "start_time" : "2017-02-18T02:47:35.903Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 1,
+      "run_time_millis" : 7382,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Enable SSH",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Enable SSH",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:45.205Z",
+      "type" : "infrastructure",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/ffbc80af32487999fc5b7a85-dlang-phobos-2-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=2fbb599e06314303c64f697b6af61c0b6b07e06fd6d6522dd6afc5685c57d0f4",
+      "start_time" : "2017-02-18T02:47:43.290Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 2,
+      "run_time_millis" : 1915,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Restore source cache",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Restore source cache",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:48.040Z",
+      "source" : "cache",
+      "type" : "checkout",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/00cc80af324879991d5b7a85-dlang-phobos-3-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=b7e0d7a5f31863745fd4bda0f5a2450d15ae770fe5da5d36c87259f2a8c2a637",
+      "start_time" : "2017-02-18T02:47:45.210Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 3,
+      "run_time_millis" : 2830,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Checkout using CircleCI fork PR checkout user key: 72:d2:fd:18:4c:f5:15:b1:ab:47:44:65:9d:9b:08:0a",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Checkout using CircleCI fork PR checkout user key: 72:d2:fd:18:4c:f5:15:b1:ab:47:44:65:9d:9b:08:0a",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:51.624Z",
+      "source" : "config",
+      "type" : "checkout",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/10cc80af324879994d5b7a85-dlang-phobos-4-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=6d3bc62e0d1b7222ce10b0ac6b21c1f65782639214ce7b4a1838099a4566c294",
+      "start_time" : "2017-02-18T02:47:48.050Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 4,
+      "run_time_millis" : 3574,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Configure the build",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Configure the build",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:55.053Z",
+      "source" : "cache",
+      "type" : "machine",
+      "truncation_len" : null,
+      "start_time" : "2017-02-18T02:47:51.629Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 5,
+      "run_time_millis" : 3424,
+      "has_output" : false
+    } ]
+  }, {
+    "name" : "Suppressing export of environment variables  fork PR builds",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Suppressing export of environment variables  fork PR builds",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:47:55.062Z",
+      "source" : "db",
+      "type" : "machine",
+      "truncation_len" : null,
+      "start_time" : "2017-02-18T02:47:55.061Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 6,
+      "run_time_millis" : 1,
+      "has_output" : false
+    } ]
+  }, {
+    "name" : "Restore cache",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Restore cache",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:48:01.517Z",
+      "source" : "cache",
+      "type" : "machine",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/40cc80af32487999bd5b7a85-dlang-phobos-7-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=5d8d4b64167f0728d326140440625c932c2fa652949a8b38919230dc09619426",
+      "start_time" : "2017-02-18T02:47:55.066Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 7,
+      "run_time_millis" : 6451,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh install-deps",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh install-deps",
+      "bash_command" : "./circleci.sh install-deps",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:48:03.189Z",
+      "source" : "config",
+      "type" : "dependencies",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/50cc80af324879991e5b7a85-dlang-phobos-8-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=12d3eb7a83a3019b5310e6b1e2cfaa4b12b19dd58081858ad3c1f908736fe454",
+      "start_time" : "2017-02-18T02:48:01.527Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 8,
+      "run_time_millis" : 1662,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Save cache",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Save cache",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:48:06.605Z",
+      "source" : "cache",
+      "type" : "database",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/60cc80af324879993e5b7a85-dlang-phobos-9-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=5da80f63c346d36b8f7b89963fe02ef0d73effe52403ea7158bcd62cbf919fff",
+      "start_time" : "2017-02-18T02:48:03.193Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 9,
+      "run_time_millis" : 3412,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh style",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh style",
+      "bash_command" : "./circleci.sh style",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:48:37.347Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/70cc80af324879996e5b7a85-dlang-phobos-10-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=f6361244f3167e9f410c1d7d3564424d379a40c4ae3ae563456e036da28cca0b",
+      "start_time" : "2017-02-18T02:48:06.614Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 10,
+      "run_time_millis" : 30733,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh setup-repos",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh setup-repos",
+      "bash_command" : "./circleci.sh setup-repos",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:49:00.428Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/80cc80af32487999506b7a85-dlang-phobos-11-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=c84f0960a42e2f7fa6cb38b6c831b39b09abe1c566f1ba8ec14d330323eea7d4",
+      "start_time" : "2017-02-18T02:48:37.360Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 11,
+      "run_time_millis" : 23068,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh publictests",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh publictests",
+      "bash_command" : "./circleci.sh publictests",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:50:51.342Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/90cc80af32487999c16b7a85-dlang-phobos-12-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=c59922004858df5210665dfb4e12d97b9ba9039c67889af083e6e0012d852de1",
+      "start_time" : "2017-02-18T02:49:00.434Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 12,
+      "run_time_millis" : 110908,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "./circleci.sh coverage",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "./circleci.sh coverage",
+      "bash_command" : "./circleci.sh coverage",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:55:52.311Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/f0cc80af32487999b86b7a85-dlang-phobos-13-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=663b4a0e7102c5753cebe8281b83dd067bd1e041d6650ea711859fd45f9e5791",
+      "start_time" : "2017-02-18T02:50:51.360Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 13,
+      "run_time_millis" : 300951,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "bash <(curl -s https://codecov.io/bash)",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : false,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "bash <(curl -s https://codecov.io/bash)",
+      "bash_command" : "bash <(curl -s https://codecov.io/bash)",
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:55:59.411Z",
+      "source" : "config",
+      "type" : "test",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/65cc80af324879998b7b7a85-dlang-phobos-14-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=0cc5e4ac863e10818808a03e27815e61a6bc7cc551e6dc6c76898780b5fd89a2",
+      "start_time" : "2017-02-18T02:55:52.327Z",
+      "exit_code" : 0,
+      "canceled" : null,
+      "step" : 14,
+      "run_time_millis" : 7084,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Collect test metadata",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Collect test metadata",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:56:01.442Z",
+      "type" : "teardown",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/75cc80af32487999fb7b7a85-dlang-phobos-15-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=d00bc536b2d2310d96f74a696c17c643d31de7d594bfac5d69006348bf70aeda",
+      "start_time" : "2017-02-18T02:55:59.417Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 15,
+      "run_time_millis" : 2025,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Collect artifacts",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Collect artifacts",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:56:09.027Z",
+      "type" : "teardown",
+      "truncation_len" : null,
+      "output_url" : "https://circle-production-action-output.s3.amazonaws.com/85cc80af324879991c7b7a85-dlang-phobos-16-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170220T003856Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20170220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=36eaec6c7ab332ed1bccd35348137092e6338d6f8d672bbdecaa8bf7734ff243",
+      "start_time" : "2017-02-18T02:56:01.447Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 16,
+      "run_time_millis" : 7580,
+      "has_output" : true
+    } ]
+  }, {
+    "name" : "Disable SSH",
+    "actions" : [ {
+      "truncated" : false,
+      "index" : 0,
+      "parallel" : true,
+      "failed" : null,
+      "infrastructure_fail" : null,
+      "name" : "Disable SSH",
+      "bash_command" : null,
+      "status" : "success",
+      "timedout" : null,
+      "continue" : null,
+      "end_time" : "2017-02-18T02:56:09.035Z",
+      "type" : "teardown",
+      "truncation_len" : null,
+      "start_time" : "2017-02-18T02:56:09.031Z",
+      "exit_code" : null,
+      "canceled" : null,
+      "step" : 19,
+      "run_time_millis" : 4,
+      "has_output" : false
+    } ]
+  } ],
+  "body" : "",
+  "usage_queued_at" : "2017-02-18T02:43:58.178Z",
+  "fail_reason" : null,
+  "retry_of" : null,
+  "reponame" : "phobos",
+  "ssh_users" : [ ],
+  "build_url" : "https://circleci.com/gh/dlang/phobos/1778",
+  "parallel" : 1,
+  "failed" : null,
+  "branch" : "pull/5151",
+  "username" : "dlang",
+  "author_date" : "2017-02-18T02:47:25Z",
+  "why" : "github",
+  "user" : {
+    "is_user" : true,
+    "login" : "wilzbach",
+    "avatar_url" : "https://avatars.githubusercontent.com/u/4370550?v=3",
+    "name" : "Sebastian Wilzbach",
+    "vcs_type" : "github",
+    "id" : 4370550
+  },
+  "vcs_revision" : "c652bce17d242c71875d99e379e37259101ea9fd",
+  "owners" : [ "wilzbach" ],
+  "vcs_tag" : null,
+  "pull_requests" : [ {
+    "head_sha" : "c652bce17d242c71875d99e379e37259101ea9fd",
+    "url" : "https://github.com/dlang/phobos/pull/5151"
+  } ],
+  "build_num" : 1778,
+  "infrastructure_fail" : false,
+  "committer_email" : "seb@wilzba.ch",
+  "previous" : null,
+  "status" : "success",
+  "committer_name" : "Sebastian Wilzbach",
+  "retries" : null,
+  "subject" : "[Static if] replace overload constraints with static if (sorting.d)",
+  "vcs_type" : "github",
+  "timedout" : false,
+  "dont_build" : null,
+  "lifecycle" : "finished",
+  "no_dependency_cache" : false,
+  "stop_time" : "2017-02-18T02:56:09.384Z",
+  "ssh_disabled" : false,
+  "build_time_millis" : 519668,
+  "circle_yml" : {
+    "string" : "dependencies:\n  pre:\n    - ./circleci.sh install-deps\n  cache_directories:\n    - \"~/dlang\"\n\ntest:\n  override:\n    - ./circleci.sh style\n    - ./circleci.sh setup-repos\n    - ./circleci.sh publictests\n    - ./circleci.sh coverage:\n        parallel: true\n        timeout: 1200\n\n  post:\n    - bash <(curl -s https://codecov.io/bash)\n"
+  },
+  "messages" : [ ],
+  "is_first_green_build" : false,
+  "job_name" : null,
+  "start_time" : "2017-02-18T02:47:29.716Z",
+  "canceler" : null,
+  "all_commit_details" : [ {
+    "committer_date" : "2017-02-18T02:47:25Z",
+    "body" : "",
+    "author_date" : "2017-02-18T02:47:25Z",
+    "committer_email" : "seb@wilzba.ch",
+    "commit" : "c652bce17d242c71875d99e379e37259101ea9fd",
+    "committer_login" : "wilzbach",
+    "committer_name" : "Sebastian Wilzbach",
+    "subject" : "[Static if] replace overload constraints with static if (sorting.d)",
+    "commit_url" : "https://github.com/dlang/phobos/commit/c652bce17d242c71875d99e379e37259101ea9fd",
+    "author_login" : "wilzbach",
+    "author_name" : "Sebastian Wilzbach",
+    "author_email" : "seb@wilzba.ch"
+  } ],
+  "outcome" : "success",
+  "vcs_url" : "https://github.com/dlang/phobos",
+  "author_name" : "Sebastian Wilzbach",
+  "node" : [ {
+    "public_ip_addr" : "52.14.133.93",
+    "port" : 64786,
+    "username" : "ubuntu",
+    "image_id" : "s3://lxc-images-us-east-2/circletar-1741-cc586-20160424T232626Z",
+    "ssh_enabled" : null
+  } ],
+  "queued_at" : "2017-02-18T02:47:29.677Z",
+  "canceled" : false,
+  "author_email" : "seb@wilzba.ch"
+}
\ No newline at end of file
diff --git a/data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450 b/data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450
new file mode 100644
index 0000000..e6a911b
--- /dev/null
+++ b/data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450
@@ -0,0 +1,11 @@
+<!doctype html>
+
+<link rel="stylesheet" href="/static/style.css" />
+
+<title>Test result - DAutoTest</title>
+
+<body>
+<h1>Test result</h1>
+
+<table><tr><td>Component</td><td>dmd</td></tr><tr><td>Pull request</td><td><a href="https://github.com/dlang/dmd/pull/6553">#6553</a></td></tr><tr><td>Base result</td><td><a href="../!base/">View</a></td></tr><tr><td>Status</td><td>success</td></tr><tr><td>Details</td><td>Documentation OK (no changes)</td></tr><tr><td>Build log</td><td><a href="build.log">View</a></td></tr><tr><td>Files</td><td><a href="file/web/index.html">Main page</a> &middot; <a href="file/web/phobos-prerelease/index.html">Phobos</a> &middot; <a href="file/web/library-prerelease/index.html">DDox</a> &middot; <a href="file/web/">All files</a></td></tr><tr><td>Changes</td><td><table class="changes"><tr><td>web/dlangspec.mobi</td><td>(binary file)</td><td><a href="../!base/file/web/dlangspec.mobi">Old</a> <a href="file/web/dlangspec.mobi">New</a> <a href="diff/web/dlangspec.mobi">Diff</a></td></tr><tr><td>web/dlangspec.pdf</td><td>(binary file)</td><td><a href="../!base/file/web/dlangspec.pdf">Old</a> <a href="file/web/dlangspec.pdf">New</a> <a href="diff/web/dlangspec.pdf">Diff</a></td></tr></table></td></tr></table>
+</body>
diff --git a/data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_feb55b1f448c28dfb72ce409f8b1994f097dddb5 b/data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_feb55b1f448c28dfb72ce409f8b1994f097dddb5
new file mode 100644
index 0000000..e0135c4
--- /dev/null
+++ b/data/payloads/dtest_results_86959530a962d84f01679c0aa45dd2c9714cc6ac_feb55b1f448c28dfb72ce409f8b1994f097dddb5
@@ -0,0 +1,11 @@
+<!doctype html>
+
+<link rel="stylesheet" href="/static/style.css" />
+
+<title>Test result - DAutoTest</title>
+
+<body>
+<h1>Test result</h1>
+
+<table><tr><td>Component</td><td>phobos</td></tr><tr><td>Pull request</td><td><a href="https://github.com/dlang/phobos/pull/5131">#5131</a></td></tr><tr><td>Base result</td><td><a href="../!base/">View</a></td></tr><tr><td>Status</td><td>error</td></tr><tr><td>Details</td><td>Merge failed</td></tr><tr><td>Build log</td><td><a href="build.log">View</a></td></tr><tr><td>Files</td><td><a href="file/web/index.html">Main page</a> &middot; <a href="file/web/phobos-prerelease/index.html">Phobos</a> &middot; <a href="file/web/library-prerelease/index.html">DDox</a> &middot; <a href="file/web/">All files</a></td></tr></table>
+</body>
diff --git a/data/payloads/github_repos_dlang_dmd_pulls_6324_reviews b/data/payloads/github_repos_dlang_dmd_pulls_6324_reviews
new file mode 100644
index 0000000..41b42e6
--- /dev/null
+++ b/data/payloads/github_repos_dlang_dmd_pulls_6324_reviews
@@ -0,0 +1,3 @@
+[
+
+]
diff --git a/data/payloads/github_repos_dlang_dmd_status_eb53933e2d0989f6da3edf8581bb3de4acac9f0e b/data/payloads/github_repos_dlang_dmd_status_eb53933e2d0989f6da3edf8581bb3de4acac9f0e
new file mode 100644
index 0000000..0e73d40
--- /dev/null
+++ b/data/payloads/github_repos_dlang_dmd_status_eb53933e2d0989f6da3edf8581bb3de4acac9f0e
@@ -0,0 +1,134 @@
+{
+  "state": "pending",
+  "statuses": [
+    {
+      "url": "https://api.github.com/repos/dlang/dmd/statuses/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "id": 913326948,
+      "state": "success",
+      "description": "Your tests passed on CircleCI!",
+      "target_url": "https://circleci.com/gh/dlang/dmd/1864?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link",
+      "context": "ci/circleci",
+      "created_at": "2016-12-15T15:30:11Z",
+      "updated_at": "2016-12-15T15:30:11Z"
+    },
+    {
+      "url": "https://api.github.com/repos/dlang/dmd/statuses/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "id": 913343183,
+      "state": "success",
+      "description": "86.501% (+0.001%) compared to b7d84f8",
+      "target_url": "https://codecov.io/gh/dlang/dmd/compare/b7d84f8c7358d4bdf5518392bcf99831c09faf3b...eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "context": "codecov/project",
+      "created_at": "2016-12-15T15:36:42Z",
+      "updated_at": "2016-12-15T15:36:42Z"
+    },
+    {
+      "url": "https://api.github.com/repos/dlang/dmd/statuses/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "id": 913343256,
+      "state": "success",
+      "description": "100% of diff hit (target 86.500%)",
+      "target_url": "https://codecov.io/gh/dlang/dmd/compare/b7d84f8c7358d4bdf5518392bcf99831c09faf3b...eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "context": "codecov/patch",
+      "created_at": "2016-12-15T15:36:44Z",
+      "updated_at": "2016-12-15T15:36:44Z"
+    },
+    {
+      "url": "https://api.github.com/repos/dlang/dmd/statuses/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "id": 913453137,
+      "state": "success",
+      "description": "The Travis CI build passed",
+      "target_url": "https://travis-ci.org/dlang/dmd/builds/184269264",
+      "context": "continuous-integration/travis-ci/pr",
+      "created_at": "2016-12-15T16:20:51Z",
+      "updated_at": "2016-12-15T16:20:51Z"
+    },
+    {
+      "url": "https://api.github.com/repos/dlang/dmd/statuses/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "id": 928992984,
+      "state": "success",
+      "description": "Documentation OK (no changes)",
+      "target_url": "http://dtest.thecybershadow.net/results/b4d532b2ba83497292a119cb8fcefd3889e04d78/eb53933e2d0989f6da3edf8581bb3de4acac9f0e/",
+      "context": "CyberShadow/DAutoTest",
+      "created_at": "2016-12-26T16:38:15Z",
+      "updated_at": "2016-12-26T16:38:15Z"
+    },
+    {
+      "url": "https://api.github.com/repos/dlang/dmd/statuses/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+      "id": 929015869,
+      "state": "pending",
+      "description": "Pass: 4, Pending: 6",
+      "target_url": "https://auto-tester.puremagic.com/pull-history.ghtml?projectid=1&repoid=1&pullid=6324",
+      "context": "auto-tester",
+      "created_at": "2016-12-26T17:16:42Z",
+      "updated_at": "2016-12-26T17:16:42Z"
+    }
+  ],
+  "sha": "eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+  "total_count": 6,
+  "repository": {
+    "id": 1257070,
+    "name": "dmd",
+    "full_name": "dlang/dmd",
+    "owner": {
+      "login": "dlang",
+      "id": 565913,
+      "avatar_url": "https://avatars.githubusercontent.com/u/565913?v=3",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/dlang",
+      "html_url": "https://github.com/dlang",
+      "followers_url": "https://api.github.com/users/dlang/followers",
+      "following_url": "https://api.github.com/users/dlang/following{/other_user}",
+      "gists_url": "https://api.github.com/users/dlang/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/dlang/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/dlang/subscriptions",
+      "organizations_url": "https://api.github.com/users/dlang/orgs",
+      "repos_url": "https://api.github.com/users/dlang/repos",
+      "events_url": "https://api.github.com/users/dlang/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/dlang/received_events",
+      "type": "Organization",
+      "site_admin": false
+    },
+    "private": false,
+    "html_url": "https://github.com/dlang/dmd",
+    "description": "dmd D Programming Language compiler",
+    "fork": false,
+    "url": "https://api.github.com/repos/dlang/dmd",
+    "forks_url": "https://api.github.com/repos/dlang/dmd/forks",
+    "keys_url": "https://api.github.com/repos/dlang/dmd/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/dlang/dmd/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/dlang/dmd/teams",
+    "hooks_url": "https://api.github.com/repos/dlang/dmd/hooks",
+    "issue_events_url": "https://api.github.com/repos/dlang/dmd/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/dlang/dmd/events",
+    "assignees_url": "https://api.github.com/repos/dlang/dmd/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/dlang/dmd/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/dlang/dmd/tags",
+    "blobs_url": "https://api.github.com/repos/dlang/dmd/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/dlang/dmd/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/dlang/dmd/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/dlang/dmd/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/dlang/dmd/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/dlang/dmd/languages",
+    "stargazers_url": "https://api.github.com/repos/dlang/dmd/stargazers",
+    "contributors_url": "https://api.github.com/repos/dlang/dmd/contributors",
+    "subscribers_url": "https://api.github.com/repos/dlang/dmd/subscribers",
+    "subscription_url": "https://api.github.com/repos/dlang/dmd/subscription",
+    "commits_url": "https://api.github.com/repos/dlang/dmd/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/dlang/dmd/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/dlang/dmd/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/dlang/dmd/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/dlang/dmd/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/dlang/dmd/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/dlang/dmd/merges",
+    "archive_url": "https://api.github.com/repos/dlang/dmd/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/dlang/dmd/downloads",
+    "issues_url": "https://api.github.com/repos/dlang/dmd/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/dlang/dmd/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/dlang/dmd/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/dlang/dmd/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/dlang/dmd/labels{/name}",
+    "releases_url": "https://api.github.com/repos/dlang/dmd/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/dlang/dmd/deployments"
+  },
+  "commit_url": "https://api.github.com/repos/dlang/dmd/commits/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+  "url": "https://api.github.com/repos/dlang/dmd/commits/eb53933e2d0989f6da3edf8581bb3de4acac9f0e/status"
+}
diff --git a/data/payloads/projecttester_job_dmd_trigger_974 b/data/payloads/projecttester_job_dmd_trigger_974
new file mode 100644
index 0000000..9033b5a
--- /dev/null
+++ b/data/payloads/projecttester_job_dmd_trigger_974
@@ -0,0 +1,13 @@
+
+
+
+
+  
+  <!DOCTYPE html><html><head resURL="/static/d741984f" data-rooturl="" data-resurl="/static/d741984f">
+    
+
+    <title>dmd_trigger #974 [Jenkins]</title><link rel="stylesheet" href="/static/d741984f/css/layout-common.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/css/style.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/css/color.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/css/responsive-grid.css" type="text/css" /><link rel="shortcut icon" href="/static/d741984f/favicon.ico" type="image/vnd.microsoft.icon" /><link color="black" rel="mask-icon" href="/images/mask-icon.svg" /><script>var isRunAsTest=false; var rootURL=""; var resURL="/static/d741984f";</script><script src="/static/d741984f/scripts/prototype.js" type="text/javascript"></script><script src="/static/d741984f/scripts/behavior.js" type="text/javascript"></script><script src='/adjuncts/d741984f/org/kohsuke/stapler/bind.js' type='text/javascript'></script><script src="/static/d741984f/scripts/yui/yahoo/yahoo-min.js"></script><script src="/static/d741984f/scripts/yui/dom/dom-min.js"></script><script src="/static/d741984f/scripts/yui/event/event-min.js"></script><script src="/static/d741984f/scripts/yui/animation/animation-min.js"></script><script src="/static/d741984f/scripts/yui/dragdrop/dragdrop-min.js"></script><script src="/static/d741984f/scripts/yui/container/container-min.js"></script><script src="/static/d741984f/scripts/yui/connection/connection-min.js"></script><script src="/static/d741984f/scripts/yui/datasource/datasource-min.js"></script><script src="/static/d741984f/scripts/yui/autocomplete/autocomplete-min.js"></script><script src="/static/d741984f/scripts/yui/menu/menu-min.js"></script><script src="/static/d741984f/scripts/yui/element/element-min.js"></script><script src="/static/d741984f/scripts/yui/button/button-min.js"></script><script src="/static/d741984f/scripts/yui/storage/storage-min.js"></script><script src="/static/d741984f/scripts/hudson-behavior.js" type="text/javascript"></script><script src="/static/d741984f/scripts/sortable.js" type="text/javascript"></script><script>crumb.init("", "");</script><link rel="stylesheet" href="/static/d741984f/scripts/yui/container/assets/container.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/assets/skins/sam/skin.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/container/assets/skins/sam/container.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/button/assets/skins/sam/button.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/menu/assets/skins/sam/menu.css" type="text/css" /><link rel="search" href="/opensearch.xml" type="application/opensearchdescription+xml" title="Jenkins" /><meta name="ROBOTS" content="INDEX,NOFOLLOW" /><script src="/static/d741984f/jsbundles/page-init.js" type="text/javascript"></script></head><body data-model-type="hudson.model.FreeStyleBuild" id="jenkins" class="yui-skin-sam jenkins-2.26 two-column" data-version="2.26"><a href="#skip2content" class="skiplink">Skip to content</a><div id="page-head"><div id="header"><div class="logo"><a id="jenkins-home-link" href="/"><img src="/static/d741984f/images/headshot.png" alt="title" id="jenkins-head-icon" /><img src="/static/d741984f/images/title.png" alt="title" width="139" id="jenkins-name-icon" height="34" /></a></div><div class="login"> <a href="/login?from=%2Fjob%2Fdmd_trigger%2F974%2F"><b>log in</b></a></div><div class="searchbox hidden-xs"><form method="get" name="search" action="/job/dmd_trigger/974/search/" style="position:relative;" class="no-json"><div id="search-box-minWidth"></div><div id="search-box-sizer"></div><div id="searchform"><input name="q" placeholder="search" id="search-box" class="has-default-text" /> <a href="http://wiki.jenkins-ci.org/display/JENKINS/Search+Box"><img src="/static/d741984f/images/16x16/help.png" style="width: 16px; height: 16px; " class="icon-help icon-sm" /></a><div id="search-box-completion"></div><script>createSearchBox("/job/dmd_trigger/974/search/");</script></div></form></div></div><div id="breadcrumbBar"><tr id="top-nav"><td id="left-top-nav" colspan="2"><link rel='stylesheet' href='/adjuncts/d741984f/lib/layout/breadcrumbs.css' type='text/css' /><script src='/adjuncts/d741984f/lib/layout/breadcrumbs.js' type='text/javascript'></script><div class="top-sticker noedge"><div class="top-sticker-inner"><div id="right-top-nav"><div id="right-top-nav"><div class="smallfont"><a href="?auto_refresh=true">ENABLE AUTO REFRESH</a></div></div></div><ul id="breadcrumbs"><li class="item"><a href="/" class="model-link inside">Jenkins</a></li><li href="/" class="children"></li><li class="item"><a href="/job/dmd_trigger/" class="model-link inside">dmd_trigger</a></li><li href="/job/dmd_trigger/" class="children"></li><li class="item"><a href="/job/dmd_trigger/974/" class="model-link inside">#974</a></li><li class="separator"></li></ul><div id="breadcrumb-menu-target"></div></div></div></td></tr></div></div><div id="page-body" class="clear"><div id="side-panel"><div id="tasks"><div class="task"><a href="/job/dmd_trigger/" class="task-icon-link"><img src="/static/d741984f/images/24x24/up.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-up icon-md" /></a> <a href="/job/dmd_trigger/" class="task-link">Back to Project</a></div><div class="task"><a href="/job/dmd_trigger/974/" class="task-icon-link"><img src="/static/d741984f/images/24x24/search.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-search icon-md" /></a> <a href="/job/dmd_trigger/974/" class="task-link"><b>Status</b></a><div class="subtasks"></div></div><div class="task"><a href="/job/dmd_trigger/974/changes" class="task-icon-link"><img src="/static/d741984f/images/24x24/notepad.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-notepad icon-md" /></a> <a href="/job/dmd_trigger/974/changes" class="task-link">Changes</a></div><div class="task"><a href="/job/dmd_trigger/974/console" class="task-icon-link"><img src="/static/d741984f/images/24x24/terminal.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-terminal icon-md" /></a> <a href="/job/dmd_trigger/974/console" class="task-link">Console Output</a></div><div class="task"><a href="/job/dmd_trigger/974/configure" class="task-icon-link"><img src="/static/d741984f/images/24x24/notepad.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-notepad icon-md" /></a> <a href="/job/dmd_trigger/974/configure" class="task-link">View Build Information</a></div><div class="task"><a href="/job/dmd_trigger/974/parameters" class="task-icon-link"><img src="/static/d741984f/images/24x24/document-properties.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-document-properties icon-md" /></a> <a href="/job/dmd_trigger/974/parameters" class="task-link">Parameters</a></div><div class="task"><a href="/job/dmd_trigger/973/" class="task-icon-link"><img src="/static/d741984f/images/24x24/previous.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-previous icon-md" /></a> <a href="/job/dmd_trigger/973/" class="task-link">Previous Build</a></div><div class="task"><a href="/job/dmd_trigger/975/" class="task-icon-link"><img src="/static/d741984f/images/24x24/next.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-next icon-md" /></a> <a href="/job/dmd_trigger/975/" class="task-link">Next Build</a></div></div></div><div id="main-panel"><a name="skip2content"></a><div style="float:right; background-color:white; z-index: 1; position:relative; margin-left: 1em"><div style="margin-top:1em">Started 23 hr ago</div><div>
+            Took <a href="/job/dmd_trigger/buildTimeTrend">48 sec</a></div></div><h1 class="build-caption page-headline"><img src="/static/d741984f/images/48x48/red.png" alt="Failed" tooltip="Failed" style="width: 48px; height: 48px; " class="icon-red icon-xlg" />
+        Build #974
+        (Feb 19, 2017 1:49:04 AM)
+      </h1><div><div id="description"><div><a title="todt.d: replace use of abytes with toStringSymbol()" href="https://github.com/dlang/dmd/pull/6552">PR #6552</a>: todt.d: replace use of abyt...</div></div></div><table style="margin-top: 1em; margin-left:1em;"><tr><td><img src="/static/d741984f/images/48x48/notepad.png" style="width: 48px; height: 48px; " class="icon-notepad icon-xlg" /></td><td style="vertical-align:middle">No changes.</td></tr><tr><td><img src="/static/d741984f/images/48x48/orange-square.png" style="width: 48px; height: 48px; " class="icon-orange-square icon-xlg" /></td><td style="vertical-align:middle"><p><span>GitHub pull request #6552 of commit 378ec2f7616ec7ca4554c5381b45561473b0c218, no merge conflicts.</span></p></td></tr><h2>Subproject Builds</h2><ul style="list-style-type: none;"><li><a href="/job/DPL/" class="model-link">DPL</a><a href="/job/DPL/1480/" class="model-link"><img src="/static/d741984f/images/16x16/red.png" alt="Failed" height="16" width="16" />#1480</a></li></ul></table></div></div><footer><div class="container-fluid"><div class="row"><div class="col-md-6" id="footer"></div><div class="col-md-18"><span class="page_generated">Page generated: Feb 20, 2017 1:15:46 AM CET</span><span class="rest_api"><a href="api/">REST API</a></span><span class="jenkins_ver"><a href="http://jenkins-ci.org/">Jenkins ver. 2.26</a></span></div></div></div></footer></body></html>
\ No newline at end of file
diff --git a/data/payloads/projecttester_job_phobos_trigger_343 b/data/payloads/projecttester_job_phobos_trigger_343
new file mode 100644
index 0000000..18ec1f0
--- /dev/null
+++ b/data/payloads/projecttester_job_phobos_trigger_343
@@ -0,0 +1,13 @@
+
+
+
+
+  
+  <!DOCTYPE html><html><head resURL="/static/d741984f" data-rooturl="" data-resurl="/static/d741984f">
+    
+
+    <title>phobos_trigger #343 [Jenkins]</title><link rel="stylesheet" href="/static/d741984f/css/layout-common.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/css/style.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/css/color.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/css/responsive-grid.css" type="text/css" /><link rel="shortcut icon" href="/static/d741984f/favicon.ico" type="image/vnd.microsoft.icon" /><link color="black" rel="mask-icon" href="/images/mask-icon.svg" /><script>var isRunAsTest=false; var rootURL=""; var resURL="/static/d741984f";</script><script src="/static/d741984f/scripts/prototype.js" type="text/javascript"></script><script src="/static/d741984f/scripts/behavior.js" type="text/javascript"></script><script src='/adjuncts/d741984f/org/kohsuke/stapler/bind.js' type='text/javascript'></script><script src="/static/d741984f/scripts/yui/yahoo/yahoo-min.js"></script><script src="/static/d741984f/scripts/yui/dom/dom-min.js"></script><script src="/static/d741984f/scripts/yui/event/event-min.js"></script><script src="/static/d741984f/scripts/yui/animation/animation-min.js"></script><script src="/static/d741984f/scripts/yui/dragdrop/dragdrop-min.js"></script><script src="/static/d741984f/scripts/yui/container/container-min.js"></script><script src="/static/d741984f/scripts/yui/connection/connection-min.js"></script><script src="/static/d741984f/scripts/yui/datasource/datasource-min.js"></script><script src="/static/d741984f/scripts/yui/autocomplete/autocomplete-min.js"></script><script src="/static/d741984f/scripts/yui/menu/menu-min.js"></script><script src="/static/d741984f/scripts/yui/element/element-min.js"></script><script src="/static/d741984f/scripts/yui/button/button-min.js"></script><script src="/static/d741984f/scripts/yui/storage/storage-min.js"></script><script src="/static/d741984f/scripts/hudson-behavior.js" type="text/javascript"></script><script src="/static/d741984f/scripts/sortable.js" type="text/javascript"></script><script>crumb.init("", "");</script><link rel="stylesheet" href="/static/d741984f/scripts/yui/container/assets/container.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/assets/skins/sam/skin.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/container/assets/skins/sam/container.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/button/assets/skins/sam/button.css" type="text/css" /><link rel="stylesheet" href="/static/d741984f/scripts/yui/menu/assets/skins/sam/menu.css" type="text/css" /><link rel="search" href="/opensearch.xml" type="application/opensearchdescription+xml" title="Jenkins" /><meta name="ROBOTS" content="INDEX,NOFOLLOW" /><script src="/static/d741984f/jsbundles/page-init.js" type="text/javascript"></script></head><body data-model-type="hudson.model.FreeStyleBuild" id="jenkins" class="yui-skin-sam jenkins-2.26 two-column" data-version="2.26"><a href="#skip2content" class="skiplink">Skip to content</a><div id="page-head"><div id="header"><div class="logo"><a id="jenkins-home-link" href="/"><img src="/static/d741984f/images/headshot.png" alt="title" id="jenkins-head-icon" /><img src="/static/d741984f/images/title.png" alt="title" width="139" id="jenkins-name-icon" height="34" /></a></div><div class="login"> <a href="/login?from=%2Fjob%2Fphobos_trigger%2F343%2F"><b>log in</b></a></div><div class="searchbox hidden-xs"><form method="get" name="search" action="/job/phobos_trigger/343/search/" style="position:relative;" class="no-json"><div id="search-box-minWidth"></div><div id="search-box-sizer"></div><div id="searchform"><input name="q" placeholder="search" id="search-box" class="has-default-text" /> <a href="http://wiki.jenkins-ci.org/display/JENKINS/Search+Box"><img src="/static/d741984f/images/16x16/help.png" style="width: 16px; height: 16px; " class="icon-help icon-sm" /></a><div id="search-box-completion"></div><script>createSearchBox("/job/phobos_trigger/343/search/");</script></div></form></div></div><div id="breadcrumbBar"><tr id="top-nav"><td id="left-top-nav" colspan="2"><link rel='stylesheet' href='/adjuncts/d741984f/lib/layout/breadcrumbs.css' type='text/css' /><script src='/adjuncts/d741984f/lib/layout/breadcrumbs.js' type='text/javascript'></script><div class="top-sticker noedge"><div class="top-sticker-inner"><div id="right-top-nav"><div id="right-top-nav"><div class="smallfont"><a href="?auto_refresh=true">ENABLE AUTO REFRESH</a></div></div></div><ul id="breadcrumbs"><li class="item"><a href="/" class="model-link inside">Jenkins</a></li><li href="/" class="children"></li><li class="item"><a href="/job/phobos_trigger/" class="model-link inside">phobos_trigger</a></li><li href="/job/phobos_trigger/" class="children"></li><li class="item"><a href="/job/phobos_trigger/343/" class="model-link inside">#343</a></li><li class="separator"></li></ul><div id="breadcrumb-menu-target"></div></div></div></td></tr></div></div><div id="page-body" class="clear"><div id="side-panel"><div id="tasks"><div class="task"><a href="/job/phobos_trigger/" class="task-icon-link"><img src="/static/d741984f/images/24x24/up.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-up icon-md" /></a> <a href="/job/phobos_trigger/" class="task-link">Back to Project</a></div><div class="task"><a href="/job/phobos_trigger/343/" class="task-icon-link"><img src="/static/d741984f/images/24x24/search.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-search icon-md" /></a> <a href="/job/phobos_trigger/343/" class="task-link"><b>Status</b></a><div class="subtasks"></div></div><div class="task"><a href="/job/phobos_trigger/343/changes" class="task-icon-link"><img src="/static/d741984f/images/24x24/notepad.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-notepad icon-md" /></a> <a href="/job/phobos_trigger/343/changes" class="task-link">Changes</a></div><div class="task"><a href="/job/phobos_trigger/343/console" class="task-icon-link"><img src="/static/d741984f/images/24x24/terminal.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-terminal icon-md" /></a> <a href="/job/phobos_trigger/343/console" class="task-link">Console Output</a></div><div class="task"><a href="/job/phobos_trigger/343/configure" class="task-icon-link"><img src="/static/d741984f/images/24x24/notepad.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-notepad icon-md" /></a> <a href="/job/phobos_trigger/343/configure" class="task-link">View Build Information</a></div><div class="task"><a href="/job/phobos_trigger/343/parameters" class="task-icon-link"><img src="/static/d741984f/images/24x24/document-properties.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-document-properties icon-md" /></a> <a href="/job/phobos_trigger/343/parameters" class="task-link">Parameters</a></div><div class="task"><a href="/job/phobos_trigger/342/" class="task-icon-link"><img src="/static/d741984f/images/24x24/previous.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-previous icon-md" /></a> <a href="/job/phobos_trigger/342/" class="task-link">Previous Build</a></div><div class="task"><a href="/job/phobos_trigger/344/" class="task-icon-link"><img src="/static/d741984f/images/24x24/next.png" style="width: 24px; height: 24px; width: 24px; height: 24px; margin: 2px;" class="icon-next icon-md" /></a> <a href="/job/phobos_trigger/344/" class="task-link">Next Build</a></div></div></div><div id="main-panel"><a name="skip2content"></a><div style="float:right; background-color:white; z-index: 1; position:relative; margin-left: 1em"><div style="margin-top:1em">Started 1 day 20 hr ago</div><div>
+            Took <a href="/job/phobos_trigger/buildTimeTrend">21 min</a></div></div><h1 class="build-caption page-headline"><img src="/static/d741984f/images/48x48/red.png" alt="Failed" tooltip="Failed" style="width: 48px; height: 48px; " class="icon-red icon-xlg" />
+        Build #343
+        (Feb 18, 2017 4:30:05 AM)
+      </h1><div><div id="description"><div><a title="[Static if] replace overload constraints with static if (sorting.d)" href="https://github.com/dlang/phobos/pull/5151">PR #5151</a>: [Static if] replace overloa...</div></div></div><table style="margin-top: 1em; margin-left:1em;"><tr><td><img src="/static/d741984f/images/48x48/notepad.png" style="width: 48px; height: 48px; " class="icon-notepad icon-xlg" /></td><td style="vertical-align:middle">No changes.</td></tr><tr><td><img src="/static/d741984f/images/48x48/orange-square.png" style="width: 48px; height: 48px; " class="icon-orange-square icon-xlg" /></td><td style="vertical-align:middle"><p><span>GitHub pull request #5151 of commit c652bce17d242c71875d99e379e37259101ea9fd, no merge conflicts.</span></p></td></tr><h2>Subproject Builds</h2><ul style="list-style-type: none;"><li><a href="/job/DPL/" class="model-link">DPL</a><a href="/job/DPL/1465/" class="model-link"><img src="/static/d741984f/images/16x16/red.png" alt="Failed" height="16" width="16" />#1465</a></li></ul></table></div></div><footer><div class="container-fluid"><div class="row"><div class="col-md-6" id="footer"></div><div class="col-md-18"><span class="page_generated">Page generated: Feb 20, 2017 1:29:19 AM CET</span><span class="rest_api"><a href="api/">REST API</a></span><span class="jenkins_ver"><a href="http://jenkins-ci.org/">Jenkins ver. 2.26</a></span></div></div></div></footer></body></html>
\ No newline at end of file
diff --git a/data/payloads/travis_repos_dlang_dmd_builds_203056613 b/data/payloads/travis_repos_dlang_dmd_builds_203056613
new file mode 100644
index 0000000..b168381
--- /dev/null
+++ b/data/payloads/travis_repos_dlang_dmd_builds_203056613
@@ -0,0 +1 @@
+{"build":{"id":203056613,"repository_id":318556,"commit_id":58164361,"number":"7903","event_type":"pull_request","pull_request":true,"pull_request_title":"todt.d: replace use of abytes with toStringSymbol()","pull_request_number":6552,"config":{"language":"d","d":["dmd","gdc","ldc"],"script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":["MODEL=32","MODEL=64"],"matrix":{"include":[{"os":"osx","d":"dmd","env":"MODEL=32","dist":"precise"},{"os":"osx","d":"dmd","env":"MODEL=64","dist":"precise"}],"exclude":[{"d":"gdc","env":"MODEL=32"}]},".result":"configured","group":"stable","dist":"precise"},"state":"errored","started_at":"2017-02-19T00:49:00Z","finished_at":"2017-02-19T01:22:42Z","duration":3878,"job_ids":[203056614,203056615,203056616,203056619,203056620,203056622,203056623]},"commit":{"id":58164361,"sha":"9b58ff577eeadc52c2138dba52cdf63844a58e6f","branch":"master","branch_is_default":true,"message":"todt.d: replace use of abytes with toStringSymbol()","committed_at":"2017-02-19T00:47:51Z","author_name":"Walter Bright","author_email":"walter@walterbright.com","committer_name":"Walter Bright","committer_email":"walter@walterbright.com","compare_url":"https://github.com/dlang/dmd/pull/6552"},"jobs":[{"id":203056614,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696963,"state":"passed","number":"7903.1","config":{"language":"d","d":"dmd","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=32",".result":"configured","group":"stable","dist":"precise","os":"linux"},"started_at":"2017-02-19T00:49:20Z","finished_at":"2017-02-19T01:02:57Z","queue":"builds.docker","allow_failure":false,"tags":null,"annotation_ids":[]},{"id":203056615,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696964,"state":"failed","number":"7903.2","config":{"language":"d","d":"dmd","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=64",".result":"configured","group":"stable","dist":"precise","os":"linux"},"started_at":"2017-02-19T00:49:03Z","finished_at":"2017-02-19T00:50:58Z","queue":"builds.docker","allow_failure":false,"tags":null,"annotation_ids":[]},{"id":203056616,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696965,"state":"failed","number":"7903.3","config":{"language":"d","d":"gdc","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=64",".result":"configured","group":"stable","dist":"precise","os":"linux"},"started_at":"2017-02-19T00:49:00Z","finished_at":"2017-02-19T00:51:08Z","queue":"builds.docker","allow_failure":false,"tags":null,"annotation_ids":[]},{"id":203056619,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696966,"state":"passed","number":"7903.4","config":{"language":"d","d":"ldc","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=32",".result":"configured","group":"stable","dist":"precise","os":"linux"},"started_at":"2017-02-19T00:49:01Z","finished_at":"2017-02-19T01:02:52Z","queue":"builds.docker","allow_failure":false,"tags":null,"annotation_ids":[]},{"id":203056620,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696968,"state":"failed","number":"7903.5","config":{"language":"d","d":"ldc","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=64",".result":"configured","group":"stable","dist":"precise","os":"linux"},"started_at":"2017-02-19T00:49:03Z","finished_at":"2017-02-19T00:50:57Z","queue":"builds.docker","allow_failure":false,"tags":null,"annotation_ids":[]},{"id":203056622,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696969,"state":"passed","number":"7903.6","config":{"language":"d","d":"dmd","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=32",".result":"configured","os":"osx"},"started_at":"2017-02-19T00:52:25Z","finished_at":"2017-02-19T01:22:42Z","queue":"builds.macstadium6","allow_failure":false,"tags":null,"annotation_ids":[]},{"id":203056623,"repository_id":318556,"build_id":203056613,"commit_id":58164361,"log_id":148696971,"state":"errored","number":"7903.7","config":{"language":"d","d":"dmd","script":"./travis.sh","sudo":false,"addons":{"apt":{"packages":["g++-multilib","gdb","libcurl3-gnutls:i386"]}},"env":"MODEL=64",".result":"configured","os":"osx"},"started_at":"2017-02-19T00:52:13Z","finished_at":"2017-02-19T00:53:09Z","queue":"builds.macstadium6","allow_failure":false,"tags":null,"annotation_ids":[]}],"annotations":[]}
\ No newline at end of file

From 81815dbbbf2577efddf54a10e94c8af33f66ec44 Mon Sep 17 00:00:00 2001
From: Sebastian Wilzbach <seb@wilzba.ch>
Date: Mon, 20 Feb 2017 02:00:57 +0100
Subject: [PATCH 2/3] Add CI PR Recognition: first steps

---
 source/dlangbot/app.d    |   6 ++
 source/dlangbot/ci.d     | 169 +++++++++++++++++++++++++++++++++++++++
 source/dlangbot/github.d |  72 +++++++++++++++++
 source/dlangbot/travis.d |  36 ++++++---
 test/ci.d                |  73 +++++++++++++++++
 test/status.d            |   1 +
 test/utils.d             |  26 +++++-
 7 files changed, 370 insertions(+), 13 deletions(-)
 create mode 100644 source/dlangbot/ci.d
 create mode 100644 test/ci.d

diff --git a/source/dlangbot/app.d b/source/dlangbot/app.d
index 281b001..36c54d4 100644
--- a/source/dlangbot/app.d
+++ b/source/dlangbot/app.d
@@ -4,6 +4,7 @@ import dlangbot.bugzilla, dlangbot.github, dlangbot.travis, dlangbot.trello,
        dlangbot.utils;
 
 public import dlangbot.bugzilla : bugzillaURL;
+public import dlangbot.ci       : dTestAPI, circleCiAPI, projectTesterAPI;
 public import dlangbot.github   : githubAPIURL, githubAuth, hookSecret;
 public import dlangbot.travis   : travisAPIURL;
 public import dlangbot.trello   : trelloAPIURL, trelloAuth, trelloSecret;
@@ -20,6 +21,7 @@ import vibe.stream.operations : readAllUTF8;
 
 bool runAsync = true;
 bool runTrello = true;
+bool runPRReview = true;
 
 Duration timeBetweenFullPRChecks = 5.minutes; // this should never be larger 30 mins on heroku
 Throttler!(typeof(&searchForAutoMergePrs)) prThrottler;
@@ -135,7 +137,11 @@ void githubHook(HTTPServerRequest req, HTTPServerResponse res)
         string state = json["state"].get!string;
         // no need to trigger the checker for failure/pending
         if (state == "success")
+        {
             prThrottler(repoSlug);
+            if (runPRReview)
+                checkPRForReviewNeed(repoSlug, json);
+        }
 
         return res.writeBody("handled");
     case "pull_request":
diff --git a/source/dlangbot/ci.d b/source/dlangbot/ci.d
new file mode 100644
index 0000000..123804a
--- /dev/null
+++ b/source/dlangbot/ci.d
@@ -0,0 +1,169 @@
+module dlangbot.ci;
+
+import dlangbot.github;
+
+import vibe.core.log;
+import vibe.http.client : HTTPClientRequest, requestHTTP;
+
+import std.conv : to;
+import std.format : format;
+import std.regex : matchFirst, regex;
+import std.exception;
+import std.variant : Nullable;
+
+// list of used APIs (overwritten by the test suite)
+string dTestAPI = "http://dtest.dlang.io";
+string circleCiAPI = "https://circleci.com/api/v1.1";
+string projectTesterAPI = "https://ci.dawg.eu";
+
+// only since 2.073
+auto nullable(T)(T t) {  return Nullable!T(t); }
+
+/**
+There's no way to get the PR number from a GitHub status event (or other API
+endpoints).
+Hence we have to check the sender for this information.
+*/
+Nullable!uint getPRForStatus(string repoSlug, string url, string context)
+{
+    Nullable!uint prNumber;
+
+    try {
+        logDebug("getPRNumber (repo: %s, ci: %s)", repoSlug, context);
+        switch (context) {
+            case "auto-tester":
+                prNumber = checkAutoTester(url);
+                break;
+            case "ci/circleci":
+                prNumber = checkCircleCi(url);
+                break;
+            case "continuous-integration/travis-ci/pr":
+                prNumber = checkTravisCi(url);
+                break;
+            case "CyberShadow/DAutoTest":
+                prNumber = checkDTest(url);
+                break;
+            case "Project Tester":
+                prNumber = checkProjectTester(url);
+                break;
+            // CodeCov provides no way atm
+            default:
+        }
+    } catch (Exception e) {
+        logDebug("PR number for: %s (by CI: %s) couldn't be detected", repoSlug, context);
+        logDebug("Exception", e);
+    }
+
+    return prNumber;
+}
+
+class PRDetectionException : Exception
+{
+    this()
+    {
+        super("Failure to detected PR number");
+    }
+}
+
+Nullable!uint checkCircleCi(string url)
+{
+    import std.algorithm.iteration : splitter;
+    import std.array : array;
+    import std.range : back, retro;
+
+    // https://circleci.com/gh/dlang/dmd/2827?utm_campaign=v...
+    static circleCiRe = regex(`circleci.com/gh/(.*)/([0-9]+)`);
+    Nullable!uint pr;
+
+    auto m = url.matchFirst(circleCiRe);
+    enforce(!m.empty);
+
+    string repoSlug = m[1];
+    ulong buildNumber = m[2].to!ulong;
+
+    auto resp = requestHTTP("%s/project/github/%s/%d"
+                .format(circleCiAPI, repoSlug, buildNumber)).readJson;
+    if (auto prs = resp["pull_requests"][])
+    {
+        pr = prs[0]["url"].get!string
+                            .splitter("/")
+                            .array // TODO: splitter is not bidirectional
+                                   //  https://issues.dlang.org/show_bug.cgi?id=17047
+                            .back.to!uint;
+    }
+    // branch in upstream
+    return pr;
+}
+
+
+Nullable!uint checkTravisCi(string url)
+{
+    import dlangbot.travis : getPRNumber;
+
+    // https://travis-ci.org/dlang/dmd/builds/203056613
+    static travisCiRe = regex(`travis-ci.org/(.*)/builds/([0-9]+)`);
+    Nullable!uint pr;
+
+    auto m = url.matchFirst(travisCiRe);
+    enforce(!m.empty);
+
+    string repoSlug = m[1];
+    ulong buildNumber = m[2].to!ulong;
+
+    return getPRNumber(repoSlug, buildNumber);
+}
+
+// tests PRs only
+auto checkAutoTester(string url)
+{
+    // https://auto-tester.puremagic.com/pull-history.ghtml?projectid=1&repoid=1&pullid=6552
+    static autoTesterRe = regex(`pullid=([0-9]+)`);
+
+    auto m = url.matchFirst(autoTesterRe);
+    enforce(!m.empty);
+    return m[1].to!uint.nullable;
+}
+
+// tests PRs only
+auto checkDTest(string url)
+{
+    import vibe.stream.operations : readAllUTF8;
+
+    // http://dtest.dlang.io/results/f3f364ddcf96e98d1a6566b04b130c3f8b37a25f/378ec2f7616ec7ca4554c5381b45561473b0c218/
+    static dTestRe = regex(`results/([0-9a-f]+)/([0-9a-f]+)`);
+    static dTestReText = regex(`<tr>.*Pull request.*<a href=".*\/pull\/([0-9]+)"`);
+
+    // to enable testing: don't use link directly
+    auto shas = url.matchFirst(dTestRe);
+    enforce(!shas.empty);
+    string headSha = shas[1]; // = PR
+    string baseSha= shas[2];  // e.g upstream/master
+
+    auto m = requestHTTP("%s/results/%s/%s/".format(dTestAPI, headSha, baseSha))
+            .bodyReader
+            .readAllUTF8
+            .matchFirst(dTestReText);
+
+    enforce(!m.empty);
+    return m[1].to!uint.nullable;
+}
+
+// tests PRs only ?
+Nullable!uint checkProjectTester(string url)
+{
+    import vibe.stream.operations : readAllUTF8;
+    import vibe.inet.url : URL;
+
+    // 1: repoSlug, 2: pr
+    static projectTesterReText = `href="https:\/\/github[.]com\/(.*)\/pull\/([0-9]+)`;
+
+    auto uri = URL(url);
+
+    auto m = requestHTTP("%s%s".format(projectTesterAPI, uri.path))
+            .bodyReader
+            .readAllUTF8
+            .matchFirst(projectTesterReText);
+
+    enforce(!m.empty, "Project tester detection failed");
+    return m[2].to!uint.nullable;
+}
diff --git a/source/dlangbot/github.d b/source/dlangbot/github.d
index bb388cf..94e4d4a 100644
--- a/source/dlangbot/github.d
+++ b/source/dlangbot/github.d
@@ -143,6 +143,14 @@ struct PullRequest
     string commitsURL() const { return "%s/repos/%s/pulls/%d/commits".format(githubAPIURL, repoSlug, number); }
     string eventsURL() const { return "%s/repos/%s/issues/%d/events".format(githubAPIURL, repoSlug, number); }
     string htmlURL() const { return "https://github.com/%s/pull/%d".format(repoSlug, number); }
+
+    static PullRequest fetch(string repoSlug, uint number)
+    {
+        return ghGetRequest("%s/repos/%s/pulls/%d"
+                            .format(githubAPIURL, repoSlug, number))
+                .readJson
+                .deserializeJson!PullRequest;
+    }
 }
 
 alias LabelsAndCommits = Tuple!(Json[], "labels", Json[], "commits");
@@ -322,3 +330,67 @@ void checkTitleForLabels(in ref PullRequest pr)
     if (mappedLabels.length)
         pr.addLabels(mappedLabels);
 }
+
+auto getPassingCiCount(string repoSlug, string sha)
+{
+    auto json = ghGetRequest("%s/repos/%s/status/%s"
+                            .format(githubAPIURL, repoSlug, sha))
+                .readJson["statuses"][];
+    return json.filter!((e){
+         if (e["state"] == "success")
+             switch (e["context"].get!string) {
+                 case "auto-tester":
+                 case "CyberShadow/DAutoTest":
+                 case "continuous-integration/travis-ci/pr":
+                 case "ci/circleci":
+                     return true;
+                 default:
+                     return false;
+             }
+         return false;
+    }).walkLength;
+}
+
+/**
+Marks a PR as reviewable if
+- there hasn't been a review yet
+- there is at least one successful CI
+*/
+void checkPRForReviewNeed(string repoSlug, Json statusPayload)
+{
+    import dlangbot.ci : getPRForStatus;
+
+    import std.stdio;
+    auto passingCi = getPassingCiCount(repoSlug, statusPayload["sha"].get!string);
+
+    auto prNumber = getPRForStatus(repoSlug,
+                                   statusPayload["target_url"].get!string,
+                                   statusPayload["context"].get!string);
+
+    if (!prNumber.isNull)
+    {
+        PullRequest pr = {number: prNumber};
+        pr.base.repo.fullName = repoSlug;
+        logInfo("repo(%s): found a valid PR number: %d", repoSlug, prNumber);
+        auto reviewsURL = "%s/repos/%s/pulls/%d/reviews"
+                          .format(githubAPIURL, repoSlug, prNumber);
+        auto reviews = requestHTTP(reviewsURL, (scope req) {
+                // custom media type is required during preview period:
+                // preview review api: https://developer.github.com/changes/2016-12-14-reviews-api
+                req.headers["Accept"] = "application/vnd.github.black-cat-preview+json";
+                req.headers["Authorization"] = githubAuth;
+            })
+            .readJson[];
+
+        if (reviews.length == 0 && passingCi >= 2)
+        {
+            logInfo("repo(%s): No review found", repoSlug);
+            // do the cool stuff here
+            pr.addLabels(["needs review"]);
+        }
+        else if (reviews.length > 0)
+        {
+            pr.removeLabel("needs review");
+        }
+    }
+}
diff --git a/source/dlangbot/travis.d b/source/dlangbot/travis.d
index 03cbc91..1f50d91 100644
--- a/source/dlangbot/travis.d
+++ b/source/dlangbot/travis.d
@@ -4,22 +4,31 @@ string travisAPIURL = "https://api.travis-ci.org";
 string travisAuth;
 
 import vibe.core.log;
+import vibe.http.client : requestHTTP, HTTPClientRequest;
+import vibe.http.common : HTTPMethod;
+
+import std.functional : toDelegate;
+import std.format : format;
+import std.variant : Nullable;
 
 //==============================================================================
 // Dedup Travis-CI builds
 //==============================================================================
 
+void travisAuthReq(scope HTTPClientRequest req)
+{
+    req.headers["Accept"] = "application/vnd.travis-ci.2+json";
+    req.headers["Authorization"] = travisAuth;
+}
+
 void cancelBuild(size_t buildId)
 {
-    import std.format : format;
-    import vibe.http.client : requestHTTP;
-    import vibe.http.common : HTTPMethod;
     import vibe.stream.operations : readAllUTF8;
 
     auto url = "%s/builds/%s/cancel".format(travisAPIURL, buildId);
     requestHTTP(url, (scope req) {
-        req.headers["Authorization"] = travisAuth;
         req.method = HTTPMethod.POST;
+        travisAuthReq(req);
     }, (scope res) {
         if (res.statusCode / 100 == 2)
             logInfo("Canceled Build %s\n", buildId);
@@ -32,9 +41,7 @@ void cancelBuild(size_t buildId)
 void dedupTravisBuilds(string action, string repoSlug, uint pullRequestNumber)
 {
     import std.algorithm.iteration : filter;
-    import std.format : format;
     import std.range : drop;
-    import vibe.http.client : requestHTTP;
 
     if (action != "synchronize" && action != "merged")
         return;
@@ -49,10 +56,7 @@ void dedupTravisBuilds(string action, string repoSlug, uint pullRequestNumber)
     }
 
     auto url = "%s/repos/%s/builds?event_type=pull_request".format(travisAPIURL, repoSlug);
-    auto activeBuildsForPR = requestHTTP(url, (scope req) {
-            req.headers["Authorization"] = travisAuth;
-            req.headers["Accept"] = "application/vnd.travis-ci.2+json";
-        })
+    auto activeBuildsForPR = requestHTTP(url, (&travisAuthReq).toDelegate)
         .readJson["builds"][]
         .filter!(b => activeState(b["state"].get!string))
         .filter!(b => b["pull_request_number"].get!uint == pullRequestNumber);
@@ -63,4 +67,16 @@ void dedupTravisBuilds(string action, string repoSlug, uint pullRequestNumber)
         cancelBuild(b["id"].get!size_t);
 }
 
+Nullable!uint getPRNumber(string repoSlug, ulong buildId)
+{
+    Nullable!uint pr;
+
+    auto url = "%s/repos/%s/builds/%s".format(travisAPIURL, repoSlug, buildId);
+    auto res = requestHTTP(url, (&travisAuthReq).toDelegate)
+                .readJson["build"];
 
+    if (res["event_type"] == "pull_request")
+        pr = res["pull_request_number"].get!uint;
+
+    return pr;
+}
diff --git a/test/ci.d b/test/ci.d
new file mode 100644
index 0000000..4a40b2d
--- /dev/null
+++ b/test/ci.d
@@ -0,0 +1,73 @@
+import utils;
+
+// manual test
+unittest
+{
+    import std.typecons : Tuple;
+
+    alias Entry = Tuple!(string, "repoSlug", int, "pr", string, "context", string, "url");
+
+    auto urls = [
+        Entry("dlang/phobos", 5151, "Project Tester", "https://ci.dawg.eu/job/phobos_trigger/343/"),
+        Entry("dlang/phobos", 5131, "CyberShadow/DAutoTest", "http://dtest.dlang.io/results/86959530a962d84f01679c0aa45dd2c9714cc6ac/feb55b1f448c28dfb72ce409f8b1994f097dddb5/"),
+        Entry("dlang/phobos", 5151, "auto-tester", "https://auto-tester.puremagic.com/pull-history.ghtml?projectid=1&repoid=3&pullid=5151"),
+        Entry("dlang/phobos", 5151, "ci/circleci", "https://circleci.com/gh/dlang/phobos/1778?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"),
+        Entry("dlang/phobos", -1, "codecov/patch ", "https://codecov.io/gh/dlang/phobos/compare/395ae88ac75ee0c03f6475ad4a5b073cfaf4e084...c652bce17d242c71875d99e379e37259101ea9fd"),
+        Entry("dlang/phobos", -1, "codecov/project", "https://codecov.io/gh/dlang/phobos/compare/395ae88ac75ee0c03f6475ad4a5b073cfaf4e084...c652bce17d242c71875d99e379e37259101ea9fd"),
+        Entry("dlang/dmd", 6552, "continuous-integration/travis-ci/pr", "https://travis-ci.org/dlang/dmd/builds/203056613"),
+        Entry("dlang/dmd", 6553, "CyberShadow/DAutoTest", "http://dtest.dlang.io/results/86959530a962d84f01679c0aa45dd2c9714cc6ac/9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450/"),
+        Entry("dlang/dmd", 6552, "Project Tester", "https://ci.dawg.eu/job/dmd_trigger/974/"),
+        Entry("dlang/dmd", 6552, "auto-tester", "https://auto-tester.puremagic.com/pull-history.ghtml?projectid=1&repoid=1&pullid=6552"),
+        Entry("dlang/dmd", 6552, "ci/circleci", "https://circleci.com/gh/dlang/dmd/2827?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"),
+    ];
+
+    setAPIExpectations(
+        "/projecttester/job/phobos_trigger/343/",
+        "/dtest/results/86959530a962d84f01679c0aa45dd2c9714cc6ac/feb55b1f448c28dfb72ce409f8b1994f097dddb5/",
+        "/circleci/project/github/dlang/phobos/1778",
+        "/travis/repos/dlang/dmd/builds/203056613",
+        "/dtest/results/86959530a962d84f01679c0aa45dd2c9714cc6ac/9f9369c96a0b8b8e4844c6d8d6f3fae9391a1450/",
+        "/projecttester/job/dmd_trigger/974/",
+        "/circleci/project/github/dlang/dmd/2827",
+    );
+
+    import dlangbot.ci : getPRForStatus;
+    foreach (i, e; urls)
+    {
+        auto pr = getPRForStatus(e.repoSlug, e.url, e.context);
+        if (e.pr >= 0)
+            assert(pr == e.pr);
+    }
+}
+
+// send hooks
+unittest
+{
+    runPRReview = true;
+    scope(exit) runPRReview = false;
+
+    setAPIExpectations(
+        "/github/repos/dlang/dmd/issues?state=open&labels=auto-merge", (ref Json j) {
+            j = Json.emptyArray;
+        },
+        "/github/repos/dlang/dmd/issues?state=open&labels=auto-merge-squash", (ref Json j) {
+            j = Json.emptyArray;
+        },
+        "/github/repos/dlang/dmd/status/eb53933e2d0989f6da3edf8581bb3de4acac9f0e",
+        "/github/repos/dlang/dmd/pulls/6324/reviews",
+        "/github/repos/dlang/dmd/issues/6324/labels",
+        (scope HTTPServerRequest req, scope HTTPServerResponse res){
+            assert(req.method == HTTPMethod.POST);
+            assert(req.json[].length == 1);
+            assert(req.json[0] == "needs review");
+            res.statusCode = 200;
+            res.writeVoidBody;
+        }
+    );
+
+    postGitHubHook("dlang_dmd_status_6324.json", "status",
+        (ref Json j, scope HTTPClientRequest req){
+            j["state"] = "success";
+        }
+    );
+}
diff --git a/test/status.d b/test/status.d
index 92f9e6e..7164795 100644
--- a/test/status.d
+++ b/test/status.d
@@ -3,6 +3,7 @@ import utils;
 // send pending status event -> no action
 unittest
 {
+    prThrottler.reset;
     setAPIExpectations();
 
     postGitHubHook("dlang_dmd_status_6324.json", "status");
diff --git a/test/utils.d b/test/utils.d
index 30a02be..ac576ba 100644
--- a/test/utils.d
+++ b/test/utils.d
@@ -11,6 +11,8 @@ public import vibe.http.server : HTTPServerRequest, HTTPServerResponse;
 public import std.functional : toDelegate;
 public import vibe.data.json : deserializeJson, Json;
 public import std.datetime : SysTime;
+public import std.stdio : writeln, writefln;
+public import std.format : format;
 
 // existing dlang bot comment -> update comment
 
@@ -65,6 +67,12 @@ void startFakeAPIServer()
     trelloAPIURL = fakeAPIServerURL ~ "/trello";
     travisAPIURL = fakeAPIServerURL ~ "/travis";
     bugzillaURL = fakeAPIServerURL ~ "/bugzilla";
+    dTestAPI = fakeAPIServerURL ~ "/dtest";
+    circleCiAPI = fakeAPIServerURL ~ "/circleci";
+    projectTesterAPI = fakeAPIServerURL ~ "/projecttester";
+
+    // only enable this for the specific CI tests (don't spam the other tests)
+    runPRReview = false;
 }
 
 // serves saved GitHub API payloads
@@ -95,6 +103,10 @@ auto payloadServer(scope HTTPServerRequest req, scope HTTPServerResponse res)
         return expectation.reqHandler(req, res);
 
     string filePath = buildPath(payloadDir, req.requestURL[1 .. $].replace("/", "_"));
+    // remove trailing slashes
+    if (filePath[$ - 1] == '_')
+       filePath = filePath.dropBackOne;
+
     if (!filePath.exists)
     {
         assert(0, "Please create payload: " ~ filePath);
@@ -112,9 +124,12 @@ auto payloadServer(scope HTTPServerRequest req, scope HTTPServerResponse res)
             if (expectation.jsonHandler !is null)
                 expectation.jsonHandler(payloadJson);
 
-            payload = payloadJson.toString;
+            return res.writeJsonBody(payloadJson);
+        }
+        else
+        {
+            return res.writeBody(payload);
         }
-        return res.writeBody(payload);
     }
 }
 
@@ -159,10 +174,15 @@ struct APIExpectation
 APIExpectation[] apiExpectations;
 
 void setAPIExpectations(Args...)(Args args)
+{
+    apiExpectations.length = 0;
+    addAPIExpectations(args);
+}
+
+void addAPIExpectations(Args...)(Args args)
 {
     import std.functional : toDelegate;
     import std.traits :  Parameters;
-    apiExpectations.length = 0;
     foreach (i, arg; args)
     {
         static if (is(Args[i] : string))

From db88bde6091b3247948082041e6d2d35800569e4 Mon Sep 17 00:00:00 2001
From: Sebastian Wilzbach <seb@wilzba.ch>
Date: Sun, 26 Feb 2017 11:58:06 +0100
Subject: [PATCH 3/3] LDC fix: Add prThrottler.reset

---
 test/ci.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/ci.d b/test/ci.d
index 4a40b2d..14367e5 100644
--- a/test/ci.d
+++ b/test/ci.d
@@ -43,6 +43,7 @@ unittest
 // send hooks
 unittest
 {
+    prThrottler.reset;
     runPRReview = true;
     scope(exit) runPRReview = false;