diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -612,6 +612,7 @@ 'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php', 'DoorkeeperBridge' => 'applications/doorkeeper/bridge/DoorkeeperBridge.php', 'DoorkeeperBridgeAsana' => 'applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php', + 'DoorkeeperBridgeGitHub' => 'applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php', 'DoorkeeperBridgeJIRA' => 'applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php', 'DoorkeeperDAO' => 'applications/doorkeeper/storage/DoorkeeperDAO.php', 'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php', @@ -620,6 +621,7 @@ 'DoorkeeperFeedWorker' => 'applications/doorkeeper/worker/DoorkeeperFeedWorker.php', 'DoorkeeperFeedWorkerAsana' => 'applications/doorkeeper/worker/DoorkeeperFeedWorkerAsana.php', 'DoorkeeperFeedWorkerJIRA' => 'applications/doorkeeper/worker/DoorkeeperFeedWorkerJIRA.php', + 'DoorkeeperGitHubHookController' => 'applications/doorkeeper/controller/DoorkeeperGitHubHookController.php', 'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php', 'DoorkeeperMissingLinkException' => 'applications/doorkeeper/exception/DoorkeeperMissingLinkException.php', 'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php', @@ -3203,6 +3205,7 @@ 'DivinerWorkflow' => 'PhabricatorManagementWorkflow', 'DoorkeeperBridge' => 'Phobject', 'DoorkeeperBridgeAsana' => 'DoorkeeperBridge', + 'DoorkeeperBridgeGitHub' => 'DoorkeeperBridge', 'DoorkeeperBridgeJIRA' => 'DoorkeeperBridge', 'DoorkeeperDAO' => 'PhabricatorLiskDAO', 'DoorkeeperExternalObject' => @@ -3214,6 +3217,7 @@ 'DoorkeeperFeedWorker' => 'FeedPushWorker', 'DoorkeeperFeedWorkerAsana' => 'DoorkeeperFeedWorker', 'DoorkeeperFeedWorkerJIRA' => 'DoorkeeperFeedWorker', + 'DoorkeeperGitHubHookController' => 'PhabricatorController', 'DoorkeeperImportEngine' => 'Phobject', 'DoorkeeperMissingLinkException' => 'Exception', 'DoorkeeperObjectRef' => 'Phobject', diff --git a/src/applications/doorkeeper/application/PhabricatorApplicationDoorkeeper.php b/src/applications/doorkeeper/application/PhabricatorApplicationDoorkeeper.php --- a/src/applications/doorkeeper/application/PhabricatorApplicationDoorkeeper.php +++ b/src/applications/doorkeeper/application/PhabricatorApplicationDoorkeeper.php @@ -25,6 +25,7 @@ return array( '/doorkeeper/' => array( 'tags/' => 'DoorkeeperTagsController', + 'github-hook/' => 'DoorkeeperGitHubHookController', ), ); } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php new file mode 100644 --- /dev/null +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php @@ -0,0 +1,119 @@ +getApplicationType() != self::APPTYPE_GITHUB) { + return false; + } + + $types = array( + self::OBJTYPE_PULL_REQUEST => true, + ); + + return isset($types[$ref->getObjectType()]); + } + + public function pullRefs(array $refs) { + + $id_map = mpull($refs, 'getObjectID', 'getObjectKey'); + + // Object IDs for pull requests are formatted as: + // + // user:repo:pullid + // + // since we need both the user and repo name to retrieve + // data about a pull request. + $data_map = array(); + foreach ($id_map as $key => $id) { + $split = explode(':', $id); + $user = str_replace($split[0], '/', ''); + $repo = str_replace($split[1], '/', ''); + $pull_id = (int)$split[2]; + $data_map[$key] = array( + 'id' => $id, + 'user' => $user, + 'repo' => $repo, + 'pullID' => $pull_id, + 'getURI' => + 'https://api.github.com/repos/'.$user.'/'.$repo.'/pulls/'.$pull_id); + } + + // Query the GitHub public API, retrieving all of the information. + $futures = array(); + foreach ($data_map as $key => $data) { + $futures[$key] = id(new HTTPSFuture($data['getURI'])) + ->setMethod('GET') + ->setTimeout(30); + } + + $results = array(); + $failed = array(); + foreach (Futures($futures) as $key => $future) { + try { + $results[$key] = $future->resolveJSON(); + } catch (Exception $ex) { + if (($ex instanceof HTTPFutureResponseStatus) && + ($ex->getStatusCode() == 404)) { + // This indicates that the object has been deleted (or never existed, + // or isn't visible to the current user) but it's a successful sync of + // an object which isn't visible. + } else { + // This is something else, so consider it a synchronization failure. + phlog($ex); + $failed[$key] = $ex; + } + } + } + + // Set all the associated data. + foreach ($refs as $ref) { + $did_fail = idx($failed, $ref->getObjectKey()); + if ($did_fail) { + // Set at least the key to identify, normally the + // name is the name of the PR. + $ref->setAttribute('name', 'GitHub PR '.$ref->getObjectID()); + $ref->setSyncFailed(true); + continue; + } + + $result = idx($results, $ref->getObjectKey()); + if (!$result) { + continue; + } + + $ref->setIsVisible(true); + $ref->setAttribute('name', $result['title']); + $ref->setAttribute('url', $result['url']); + $ref->setAttribute('diffURL', $result['diff_url']); + $ref->setAttribute('state', $result['state']); + $ref->setAttribute('userLogin', $result['user']['login']); + $ref->setAttribute('body', $result['body']); + $ref->setAttribute('headGitURL', $result['head']['repo']['git_url']); + $ref->setAttribute('headGitSHA', $result['head']['sha']); + $ref->setAttribute('headLabel', $result['head']['label']); + $ref->setAttribute('baseGitURL', $result['base']['repo']['git_url']); + $ref->setAttribute('baseGitSHA', $result['base']['sha']); + $ref->setAttribute('baseLabel', $result['base']['label']); + $ref->setAttribute('merged', $result['merged']); + + // TODO: Does the data belong on the ref attributes or the + // object properties?? + + $obj = $ref->getExternalObject(); + if ($obj->getID()) { + continue; + } + + $obj->setObjectURI($result['url']); + + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + $obj->save(); + unset($unguarded); + } + } + +} diff --git a/src/applications/doorkeeper/controller/DoorkeeperGitHubHookController.php b/src/applications/doorkeeper/controller/DoorkeeperGitHubHookController.php new file mode 100644 --- /dev/null +++ b/src/applications/doorkeeper/controller/DoorkeeperGitHubHookController.php @@ -0,0 +1,37 @@ +getRequest(); + + $is_get = $_SERVER['REQUEST_METHOD'] == 'GET'; + + if ($is_get) { + return id(new AphrontPlainTextResponse()) + ->setContent('GET test'); + } else { + $payload = json_decode($_POST['payload']); + + return id(new AphrontPlainTextResponse()) + ->setContent($_POST['payload']); + } + } + +}