@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

Write an explicit edge for commit membership in packages

Summary:
Ref T10978. Currently, during commit import, we write an "Audit Not Required" auditor for commits which don't require an audit.

This auditor is used to power the "Commits in this package" query in Owners.

This conflates audits and commit/package membership. I think it might even predate edges. Code needs to dance around this mess and we get the wrong result in some cases, since auditors are now editable.

Instead, write an explicit edge which just says "this commit is part of such-and-such packages". Then use that to run the query. Logical!

I'll issue guidance on this but I'm not migrating it, since it fixes itself going forward and only really affects the UI in Owners.

Test Plan:
- Ran `bin/audit update-owners` with various arguments.
- Viewed packages in web UI, saw them load the proper commits.
- Queried by packages in Diffusion explicitly.
- Clicked the "View All" link in Owners and got to the right search UI.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10978

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

+197 -3
+4
src/__phutil_library_map__.php
··· 635 635 'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php', 636 636 'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php', 637 637 'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php', 638 + 'DiffusionCommitHasPackageEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php', 638 639 'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php', 639 640 'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php', 640 641 'DiffusionCommitHasTaskEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasTaskEdgeType.php', ··· 1891 1892 'PhabricatorAuditTransactionComment' => 'applications/audit/storage/PhabricatorAuditTransactionComment.php', 1892 1893 'PhabricatorAuditTransactionQuery' => 'applications/audit/query/PhabricatorAuditTransactionQuery.php', 1893 1894 'PhabricatorAuditTransactionView' => 'applications/audit/view/PhabricatorAuditTransactionView.php', 1895 + 'PhabricatorAuditUpdateOwnersManagementWorkflow' => 'applications/audit/management/PhabricatorAuditUpdateOwnersManagementWorkflow.php', 1894 1896 'PhabricatorAuthAccountView' => 'applications/auth/view/PhabricatorAuthAccountView.php', 1895 1897 'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php', 1896 1898 'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php', ··· 5343 5345 'DiffusionCommitEditController' => 'DiffusionController', 5344 5346 'DiffusionCommitEditEngine' => 'PhabricatorEditEngine', 5345 5347 'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine', 5348 + 'DiffusionCommitHasPackageEdgeType' => 'PhabricatorEdgeType', 5346 5349 'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType', 5347 5350 'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship', 5348 5351 'DiffusionCommitHasTaskEdgeType' => 'PhabricatorEdgeType', ··· 6786 6789 'PhabricatorAuditTransactionComment' => 'PhabricatorApplicationTransactionComment', 6787 6790 'PhabricatorAuditTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6788 6791 'PhabricatorAuditTransactionView' => 'PhabricatorApplicationTransactionView', 6792 + 'PhabricatorAuditUpdateOwnersManagementWorkflow' => 'PhabricatorAuditManagementWorkflow', 6789 6793 'PhabricatorAuthAccountView' => 'AphrontView', 6790 6794 'PhabricatorAuthApplication' => 'PhabricatorApplication', 6791 6795 'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
+117
src/applications/audit/management/PhabricatorAuditUpdateOwnersManagementWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuditUpdateOwnersManagementWorkflow 4 + extends PhabricatorAuditManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('update-owners') 9 + ->setExamples('**update-owners** ...') 10 + ->setSynopsis(pht('Update package relationships for commits.')) 11 + ->setArguments( 12 + array( 13 + array( 14 + 'name' => 'all', 15 + 'help' => pht('Update all commits in all repositories.'), 16 + ), 17 + array( 18 + 'name' => 'objects', 19 + 'wildcard' => true, 20 + 'help' => pht('Update named commits and repositories.'), 21 + ), 22 + )); 23 + } 24 + 25 + public function execute(PhutilArgumentParser $args) { 26 + $viewer = $this->getViewer(); 27 + 28 + $all = $args->getArg('all'); 29 + $names = $args->getArg('objects'); 30 + 31 + if (!$names && !$all) { 32 + throw new PhutilArgumentUsageException( 33 + pht( 34 + 'Specify "--all" to update everything, or a list of specific '. 35 + 'commits or repositories to update.')); 36 + } else if ($names && $all) { 37 + throw new PhutilArgumentUsageException( 38 + pht( 39 + 'Specify either a list of objects to update or "--all", but not '. 40 + 'both.')); 41 + } 42 + 43 + if ($all) { 44 + $objects = new LiskMigrationIterator(new PhabricatorRepository()); 45 + } else { 46 + $query = id(new PhabricatorObjectQuery()) 47 + ->setViewer($viewer) 48 + ->withNames($names); 49 + 50 + $query->execute(); 51 + 52 + $objects = array(); 53 + 54 + $results = $query->getNamedResults(); 55 + foreach ($names as $name) { 56 + if (!isset($results[$name])) { 57 + throw new PhutilArgumentUsageException( 58 + pht( 59 + 'Object "%s" is not a valid object.', 60 + $name)); 61 + } 62 + 63 + $object = $results[$name]; 64 + if (!($object instanceof PhabricatorRepository) && 65 + !($object instanceof PhabricatorRepositoryCommit)) { 66 + throw new PhutilArgumentUsageException( 67 + pht( 68 + 'Object "%s" is not a valid repository or commit.', 69 + $name)); 70 + } 71 + 72 + $objects[] = $object; 73 + } 74 + } 75 + 76 + foreach ($objects as $object) { 77 + if ($object instanceof PhabricatorRepository) { 78 + $commits = id(new DiffusionCommitQuery()) 79 + ->setViewer($viewer) 80 + ->withRepository($object) 81 + ->execute(); 82 + } else { 83 + $commits = array($object); 84 + } 85 + 86 + foreach ($commits as $commit) { 87 + $repository = $commit->getRepository(); 88 + 89 + $affected_paths = PhabricatorOwnerPathQuery::loadAffectedPaths( 90 + $repository, 91 + $commit, 92 + $viewer); 93 + 94 + $affected_packages = PhabricatorOwnersPackage::loadAffectedPackages( 95 + $repository, 96 + $affected_paths); 97 + 98 + $monograms = mpull($affected_packages, 'getMonogram'); 99 + if ($monograms) { 100 + $monograms = implode(', ', $monograms); 101 + } else { 102 + $monograms = pht('none'); 103 + } 104 + 105 + echo tsprintf( 106 + "%s\n", 107 + pht( 108 + 'Updating "%s" (%s)...', 109 + $commit->getDisplayName(), 110 + $monograms)); 111 + 112 + $commit->writeOwnersEdges(mpull($affected_packages, 'getPHID')); 113 + } 114 + } 115 + } 116 + 117 + }
+10
src/applications/audit/query/PhabricatorCommitSearchEngine.php
··· 45 45 $query->withRepositoryPHIDs($map['repositoryPHIDs']); 46 46 } 47 47 48 + if ($map['packagePHIDs']) { 49 + $query->withPackagePHIDs($map['packagePHIDs']); 50 + } 51 + 48 52 return $query; 49 53 } 50 54 ··· 78 82 ->setConduitKey('repositories') 79 83 ->setAliases(array('repository', 'repositories', 'repositoryPHID')) 80 84 ->setDatasource(new DiffusionRepositoryDatasource()), 85 + id(new PhabricatorSearchDatasourceField()) 86 + ->setLabel(pht('Packages')) 87 + ->setKey('packagePHIDs') 88 + ->setConduitKey('packages') 89 + ->setAliases(array('package', 'packages', 'packagePHID')) 90 + ->setDatasource(new PhabricatorOwnersPackageDatasource()), 81 91 ); 82 92 } 83 93
+8
src/applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php
··· 1 + <?php 2 + 3 + final class DiffusionCommitHasPackageEdgeType 4 + extends PhabricatorEdgeType { 5 + 6 + const EDGECONST = 65; 7 + 8 + }
+30
src/applications/diffusion/query/DiffusionCommitQuery.php
··· 13 13 private $identifierMap; 14 14 private $responsiblePHIDs; 15 15 private $statuses; 16 + private $packagePHIDs; 16 17 17 18 private $needAuditRequests; 18 19 private $auditIDs; ··· 121 122 122 123 public function withResponsiblePHIDs(array $responsible_phids) { 123 124 $this->responsiblePHIDs = $responsible_phids; 125 + return $this; 126 + } 127 + 128 + public function withPackagePHIDs(array $package_phids) { 129 + $this->packagePHIDs = $package_phids; 124 130 return $this; 125 131 } 126 132 ··· 498 504 $this->statuses); 499 505 } 500 506 507 + if ($this->packagePHIDs !== null) { 508 + $where[] = qsprintf( 509 + $conn, 510 + 'package.dst IN (%Ls)', 511 + $this->packagePHIDs); 512 + } 513 + 501 514 return $where; 502 515 } 503 516 ··· 519 532 return (bool)$this->responsiblePHIDs; 520 533 } 521 534 535 + private function shouldJoinOwners() { 536 + return (bool)$this->packagePHIDs; 537 + } 538 + 522 539 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 523 540 $join = parent::buildJoinClauseParts($conn); 524 541 $audit_request = new PhabricatorRepositoryAuditRequest(); ··· 537 554 $audit_request->getTableName()); 538 555 } 539 556 557 + if ($this->shouldJoinOwners()) { 558 + $join[] = qsprintf( 559 + $conn, 560 + 'JOIN %T package ON commit.phid = package.src 561 + AND package.type = %s', 562 + PhabricatorEdgeConfig::TABLE_NAME_EDGE, 563 + DiffusionCommitHasPackageEdgeType::EDGECONST); 564 + } 565 + 540 566 return $join; 541 567 } 542 568 ··· 546 572 } 547 573 548 574 if ($this->shouldJoinAudit()) { 575 + return true; 576 + } 577 + 578 + if ($this->shouldJoinOwners()) { 549 579 return true; 550 580 } 551 581
+3 -3
src/applications/owners/controller/PhabricatorOwnersDetailController.php
··· 68 68 $commit_uri = id(new PhutilURI('/diffusion/commit/')) 69 69 ->setQueryParams( 70 70 array( 71 - 'auditorPHIDs' => $package->getPHID(), 71 + 'package' => $package->getPHID(), 72 72 )); 73 73 74 74 $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; 75 75 76 76 $attention_commits = id(new DiffusionCommitQuery()) 77 77 ->setViewer($request->getUser()) 78 - ->withAuditorPHIDs(array($package->getPHID())) 78 + ->withPackagePHIDs(array($package->getPHID())) 79 79 ->withStatuses( 80 80 array( 81 81 $status_concern, ··· 102 102 103 103 $all_commits = id(new DiffusionCommitQuery()) 104 104 ->setViewer($request->getUser()) 105 - ->withAuditorPHIDs(array($package->getPHID())) 105 + ->withPackagePHIDs(array($package->getPHID())) 106 106 ->needCommitData(true) 107 107 ->needAuditRequests(true) 108 108 ->setLimit(25)
+23
src/applications/repository/storage/PhabricatorRepositoryCommit.php
··· 262 262 return isset($map[$audit->getAuditorPHID()]); 263 263 } 264 264 265 + public function writeOwnersEdges(array $package_phids) { 266 + $src_phid = $this->getPHID(); 267 + $edge_type = DiffusionCommitHasPackageEdgeType::EDGECONST; 268 + 269 + $editor = new PhabricatorEdgeEditor(); 270 + 271 + $dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 272 + $src_phid, 273 + $edge_type); 274 + 275 + foreach ($dst_phids as $dst_phid) { 276 + $editor->removeEdge($src_phid, $edge_type, $dst_phid); 277 + } 278 + 279 + foreach ($package_phids as $package_phid) { 280 + $editor->addEdge($src_phid, $edge_type, $package_phid); 281 + } 282 + 283 + $editor->save(); 284 + 285 + return $this; 286 + } 287 + 265 288 public function getAuditorPHIDsForEdit() { 266 289 $audits = $this->getAudits(); 267 290 return mpull($audits, 'getAuditorPHID');
+2
src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
··· 42 42 $repository, 43 43 $affected_paths); 44 44 45 + $commit->writeOwnersEdges(mpull($affected_packages, 'getPHID')); 46 + 45 47 if (!$affected_packages) { 46 48 return; 47 49 }