@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a basic pull event log for debugging repository cloning

Summary:
Ref T10228. This is currently quite limited:

- No UI.
- No SSH support.

My primary goal is to debug the issue in T10228. In the long run we can expand this to be a bit fancier.

Test Plan:
Made various valid and invalid clones, got sucess responses and not-so-successful responses, viewed the log table for general corresponding messages and broad sanity.

Ran GC via `bin/phd debug trigger`, no issues.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10228

Differential Revision: https://secure.phabricator.com/D15127

+371
+14
resources/sql/autopatches/20160128.repo.1.pull.sql
··· 1 + CREATE TABLE {$NAMESPACE}_repository.repository_pullevent ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + repositoryPHID VARBINARY(64), 5 + epoch INT UNSIGNED NOT NULL, 6 + pullerPHID VARBINARY(64), 7 + remoteAddress INT UNSIGNED, 8 + remoteProtocol VARCHAR(32) COLLATE {$COLLATE_TEXT}, 9 + resultType VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, 10 + resultCode INT UNSIGNED NOT NULL, 11 + properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, 12 + KEY `key_repository` (repositoryPHID), 13 + KEY `key_epoch` (epoch) 14 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+11
src/__phutil_library_map__.php
··· 688 688 'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php', 689 689 'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php', 690 690 'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php', 691 + 'DiffusionPullEventGarbageCollector' => 'applications/diffusion/DiffusionPullEventGarbageCollector.php', 691 692 'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php', 692 693 'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php', 693 694 'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php', ··· 3028 3029 'PhabricatorRepositoryMirrorQuery' => 'applications/repository/query/PhabricatorRepositoryMirrorQuery.php', 3029 3030 'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php', 3030 3031 'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php', 3032 + 'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php', 3033 + 'PhabricatorRepositoryPullEventPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php', 3034 + 'PhabricatorRepositoryPullEventQuery' => 'applications/repository/query/PhabricatorRepositoryPullEventQuery.php', 3031 3035 'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php', 3032 3036 'PhabricatorRepositoryPushEvent' => 'applications/repository/storage/PhabricatorRepositoryPushEvent.php', 3033 3037 'PhabricatorRepositoryPushEventPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php', ··· 4708 4712 'DiffusionPreCommitRefRepositoryHeraldField' => 'DiffusionPreCommitRefHeraldField', 4709 4713 'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'DiffusionPreCommitRefHeraldField', 4710 4714 'DiffusionPreCommitRefTypeHeraldField' => 'DiffusionPreCommitRefHeraldField', 4715 + 'DiffusionPullEventGarbageCollector' => 'PhabricatorGarbageCollector', 4711 4716 'DiffusionPushCapability' => 'PhabricatorPolicyCapability', 4712 4717 'DiffusionPushEventViewController' => 'DiffusionPushLogController', 4713 4718 'DiffusionPushLogController' => 'DiffusionController', ··· 7476 7481 'PhabricatorRepositoryMirrorQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7477 7482 'PhabricatorRepositoryParsedChange' => 'Phobject', 7478 7483 'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine', 7484 + 'PhabricatorRepositoryPullEvent' => array( 7485 + 'PhabricatorRepositoryDAO', 7486 + 'PhabricatorPolicyInterface', 7487 + ), 7488 + 'PhabricatorRepositoryPullEventPHIDType' => 'PhabricatorPHIDType', 7489 + 'PhabricatorRepositoryPullEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7479 7490 'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorDaemon', 7480 7491 'PhabricatorRepositoryPushEvent' => array( 7481 7492 'PhabricatorRepositoryDAO',
+29
src/applications/diffusion/DiffusionPullEventGarbageCollector.php
··· 1 + <?php 2 + 3 + final class DiffusionPullEventGarbageCollector 4 + extends PhabricatorGarbageCollector { 5 + 6 + const COLLECTORCONST = 'diffusion.pull'; 7 + 8 + public function getCollectorName() { 9 + return pht('Repository Pull Events'); 10 + } 11 + 12 + public function getDefaultRetentionPolicy() { 13 + return phutil_units('30 days in seconds'); 14 + } 15 + 16 + protected function collectGarbage() { 17 + $table = new PhabricatorRepositoryPullEvent(); 18 + $conn_w = $table->establishConnection('w'); 19 + 20 + queryfx( 21 + $conn_w, 22 + 'DELETE FROM %T WHERE epoch < %d LIMIT 100', 23 + $table->getTableName(), 24 + $this->getGarbageEpoch()); 25 + 26 + return ($conn_w->getAffectedRows() == 100); 27 + } 28 + 29 + }
+94
src/applications/diffusion/controller/DiffusionServeController.php
··· 2 2 3 3 final class DiffusionServeController extends DiffusionController { 4 4 5 + private $serviceViewer; 6 + private $serviceRepository; 7 + 8 + public function setServiceViewer(PhabricatorUser $viewer) { 9 + $this->serviceViewer = $viewer; 10 + return $this; 11 + } 12 + 13 + public function getServiceViewer() { 14 + return $this->serviceViewer; 15 + } 16 + 17 + public function setServiceRepository(PhabricatorRepository $repository) { 18 + $this->serviceRepository = $repository; 19 + return $this; 20 + } 21 + 22 + public function getServiceRepository() { 23 + return $this->serviceRepository; 24 + } 25 + 5 26 public function isVCSRequest(AphrontRequest $request) { 6 27 $identifier = $this->getRepositoryIdentifierFromRequest($request); 7 28 if ($identifier === null) { ··· 45 66 } 46 67 47 68 public function handleRequest(AphrontRequest $request) { 69 + $service_exception = null; 70 + $response = null; 71 + 72 + try { 73 + $response = $this->serveRequest($request); 74 + } catch (Exception $ex) { 75 + $service_exception = $ex; 76 + } 77 + 78 + try { 79 + $remote_addr = $request->getRemoteAddr(); 80 + $remote_addr = ip2long($remote_addr); 81 + 82 + $pull_event = id(new PhabricatorRepositoryPullEvent()) 83 + ->setEpoch(PhabricatorTime::getNow()) 84 + ->setRemoteAddress($remote_addr) 85 + ->setRemoteProtocol('http'); 86 + 87 + if ($response) { 88 + $pull_event 89 + ->setResultType('wild') 90 + ->setResultCode($response->getHTTPResponseCode()); 91 + 92 + if ($response instanceof PhabricatorVCSResponse) { 93 + $pull_event->setProperties( 94 + array( 95 + 'response.message' => $response->getMessage(), 96 + )); 97 + } 98 + } else { 99 + $pull_event 100 + ->setResultType('exception') 101 + ->setResultCode(500) 102 + ->setProperties( 103 + array( 104 + 'exception.class' => $ex->getClass(), 105 + 'exception.message' => $ex->getMessage(), 106 + )); 107 + } 108 + 109 + $viewer = $this->getServiceViewer(); 110 + if ($viewer) { 111 + $pull_event->setPullerPHID($viewer->getPHID()); 112 + } 113 + 114 + $repository = $this->getServiceRepository(); 115 + if ($repository) { 116 + $pull_event->setRepositoryPHID($repository->getPHID()); 117 + } 118 + 119 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 120 + $pull_event->save(); 121 + unset($unguarded); 122 + 123 + } catch (Exception $ex) { 124 + if ($service_exception) { 125 + throw $service_exception; 126 + } 127 + throw $ex; 128 + } 129 + 130 + if ($service_exception) { 131 + throw $service_exception; 132 + } 133 + 134 + return $response; 135 + } 136 + 137 + private function serveRequest(AphrontRequest $request) { 48 138 $identifier = $this->getRepositoryIdentifierFromRequest($request); 49 139 50 140 // If authentication credentials have been provided, try to find a user ··· 64 154 // being "not logged in". 65 155 $viewer = new PhabricatorUser(); 66 156 } 157 + 158 + $this->setServiceViewer($viewer); 67 159 68 160 $allow_public = PhabricatorEnv::getEnvConfig('policy.allow-public'); 69 161 $allow_auth = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth'); ··· 110 202 } 111 203 } 112 204 } 205 + 206 + $this->setServiceRepository($repository); 113 207 114 208 if (!$repository->isTracked()) { 115 209 return new PhabricatorVCSResponse(
+39
src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryPullEventPHIDType extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'PULE'; 6 + 7 + public function getTypeName() { 8 + return pht('Pull Event'); 9 + } 10 + 11 + public function newObject() { 12 + return new PhabricatorRepositoryPullEvent(); 13 + } 14 + 15 + public function getPHIDTypeApplicationClass() { 16 + return 'PhabricatorDiffusionApplication'; 17 + } 18 + 19 + protected function buildQueryForObjects( 20 + PhabricatorObjectQuery $query, 21 + array $phids) { 22 + 23 + return id(new PhabricatorRepositoryPullEventQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + foreach ($handles as $phid => $handle) { 33 + $event = $objects[$phid]; 34 + 35 + $handle->setName(pht('Pull Event %d', $event->getID())); 36 + } 37 + } 38 + 39 + }
+97
src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryPullEventQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $repositoryPHIDs; 9 + private $pullerPHIDs; 10 + 11 + public function withIDs(array $ids) { 12 + $this->ids = $ids; 13 + return $this; 14 + } 15 + 16 + public function withPHIDs(array $phids) { 17 + $this->phids = $phids; 18 + return $this; 19 + } 20 + 21 + public function withRepositoryPHIDs(array $repository_phids) { 22 + $this->repositoryPHIDs = $repository_phids; 23 + return $this; 24 + } 25 + 26 + public function withPullerPHIDs(array $puller_phids) { 27 + $this->pullerPHIDs = $puller_phids; 28 + return $this; 29 + } 30 + 31 + public function newResultObject() { 32 + return new PhabricatorRepositoryPullEvent(); 33 + } 34 + 35 + protected function loadPage() { 36 + return $this->loadStandardPage($this->newResultObject()); 37 + } 38 + 39 + protected function willFilterPage(array $events) { 40 + $repository_phids = mpull($events, 'getRepositoryPHID'); 41 + $repositories = id(new PhabricatorRepositoryQuery()) 42 + ->setViewer($this->getViewer()) 43 + ->withPHIDs($repository_phids) 44 + ->execute(); 45 + $repositories = mpull($repositories, null, 'getPHID'); 46 + 47 + foreach ($events as $key => $event) { 48 + $phid = $event->getRepositoryPHID(); 49 + if (empty($repositories[$phid])) { 50 + unset($events[$key]); 51 + continue; 52 + } 53 + $event->attachRepository($repositories[$phid]); 54 + } 55 + 56 + return $events; 57 + } 58 + 59 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 60 + $where = parent::buildWhereClauseParts($conn); 61 + 62 + if ($this->ids !== null) { 63 + $where[] = qsprintf( 64 + $conn, 65 + 'id IN (%Ld)', 66 + $this->ids); 67 + } 68 + 69 + if ($this->phids !== null) { 70 + $where[] = qsprintf( 71 + $conn, 72 + 'phid IN (%Ls)', 73 + $this->phids); 74 + } 75 + 76 + if ($this->repositoryPHIDs !== null) { 77 + $where[] = qsprintf( 78 + $conn, 79 + 'repositoryPHID IN (%Ls)', 80 + $this->repositoryPHIDs); 81 + } 82 + 83 + if ($this->pullerPHIDs !== null) { 84 + $where[] = qsprintf( 85 + $conn, 86 + 'pullerPHID in (%Ls)', 87 + $this->pullerPHIDs); 88 + } 89 + 90 + return $where; 91 + } 92 + 93 + public function getQueryApplicationClass() { 94 + return 'PhabricatorDiffusionApplication'; 95 + } 96 + 97 + }
+87
src/applications/repository/storage/PhabricatorRepositoryPullEvent.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryPullEvent 4 + extends PhabricatorRepositoryDAO 5 + implements PhabricatorPolicyInterface { 6 + 7 + protected $repositoryPHID; 8 + protected $epoch; 9 + protected $pullerPHID; 10 + protected $remoteAddress; 11 + protected $remoteProtocol; 12 + protected $resultType; 13 + protected $resultCode; 14 + protected $properties; 15 + 16 + private $repository = self::ATTACHABLE; 17 + 18 + public static function initializeNewEvent(PhabricatorUser $viewer) { 19 + return id(new PhabricatorRepositoryPushEvent()) 20 + ->setPusherPHID($viewer->getPHID()); 21 + } 22 + 23 + protected function getConfiguration() { 24 + return array( 25 + self::CONFIG_AUX_PHID => true, 26 + self::CONFIG_TIMESTAMPS => false, 27 + self::CONFIG_SERIALIZATION => array( 28 + 'properties' => self::SERIALIZATION_JSON, 29 + ), 30 + self::CONFIG_COLUMN_SCHEMA => array( 31 + 'repositoryPHID' => 'phid?', 32 + 'pullerPHID' => 'phid?', 33 + 'remoteAddress' => 'uint32?', 34 + 'remoteProtocol' => 'text32?', 35 + 'resultType' => 'text32', 36 + 'resultCode' => 'uint32', 37 + ), 38 + self::CONFIG_KEY_SCHEMA => array( 39 + 'key_repository' => array( 40 + 'columns' => array('repositoryPHID'), 41 + ), 42 + 'key_epoch' => array( 43 + 'columns' => array('epoch'), 44 + ), 45 + ), 46 + ) + parent::getConfiguration(); 47 + } 48 + 49 + public function generatePHID() { 50 + return PhabricatorPHID::generateNewPHID( 51 + PhabricatorRepositoryPullEventPHIDType::TYPECONST); 52 + } 53 + 54 + public function attachRepository(PhabricatorRepository $repository) { 55 + $this->repository = $repository; 56 + return $this; 57 + } 58 + 59 + public function getRepository() { 60 + return $this->assertAttached($this->repository); 61 + } 62 + 63 + 64 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 65 + 66 + 67 + public function getCapabilities() { 68 + return array( 69 + PhabricatorPolicyCapability::CAN_VIEW, 70 + ); 71 + } 72 + 73 + public function getPolicy($capability) { 74 + return $this->getRepository()->getPolicy($capability); 75 + } 76 + 77 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 78 + return $this->getRepository()->hasAutomaticCapability($capability, $viewer); 79 + } 80 + 81 + public function describeAutomaticCapability($capability) { 82 + return pht( 83 + "A repository's pull events are visible to users who can see the ". 84 + "repository."); 85 + } 86 + 87 + }