mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Add a rough "bin/repository unpublish" workflow to attempt to cleanup improperly published repositories
Summary: Ref T13114. See PHI514. This makes some attempt to undo the damage caused by incorrectly publishing a repository. Don't run this. Test Plan: Yikes. Maniphest Tasks: T13114 Differential Revision: https://secure.phabricator.com/D19271
This commit is contained in:
parent
9fbf4ee58c
commit
66392e5b8b
3 changed files with 308 additions and 2 deletions
|
@ -4020,6 +4020,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRepositoryManagementRefsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php',
|
||||
'PhabricatorRepositoryManagementReparseWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php',
|
||||
'PhabricatorRepositoryManagementThawWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementThawWorkflow.php',
|
||||
'PhabricatorRepositoryManagementUnpublishWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementUnpublishWorkflow.php',
|
||||
'PhabricatorRepositoryManagementUpdateWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php',
|
||||
'PhabricatorRepositoryManagementWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementWorkflow.php',
|
||||
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryMercurialCommitChangeParserWorker.php',
|
||||
|
@ -8563,7 +8564,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorMarkupInterface',
|
||||
),
|
||||
'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
|
||||
'PhabricatorFeedStoryData' => array(
|
||||
'PhabricatorFeedDAO',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO',
|
||||
'PhabricatorFeedStoryPublisher' => 'Phobject',
|
||||
'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
|
||||
|
@ -9814,6 +9818,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRepositoryManagementRefsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementReparseWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementThawWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementUnpublishWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementUpdateWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||
'PhabricatorRepositoryManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFeedStoryData extends PhabricatorFeedDAO {
|
||||
final class PhabricatorFeedStoryData
|
||||
extends PhabricatorFeedDAO
|
||||
implements PhabricatorDestructibleInterface {
|
||||
|
||||
protected $phid;
|
||||
|
||||
|
@ -66,4 +68,30 @@ final class PhabricatorFeedStoryData extends PhabricatorFeedDAO {
|
|||
return idx($this->storyData, $key, $default);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function destroyObjectPermanently(
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
|
||||
$this->openTransaction();
|
||||
$conn = $this->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'DELETE FROM %T WHERE chronologicalKey = %s',
|
||||
id(new PhabricatorFeedStoryNotification())->getTableName(),
|
||||
$this->getChronologicalKey());
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'DELETE FROM %T WHERE chronologicalKey = %s',
|
||||
id(new PhabricatorFeedStoryReference())->getTableName(),
|
||||
$this->getChronologicalKey());
|
||||
|
||||
$this->delete();
|
||||
$this->saveTransaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorRepositoryManagementUnpublishWorkflow
|
||||
extends PhabricatorRepositoryManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('unpublish')
|
||||
->setExamples(
|
||||
'**unpublish** [__options__] __repository__')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Unpublish all feed stories and notifications that a repository '.
|
||||
'has generated. Keep expectations low; can not rewind time.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht('Do not prompt for confirmation.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'dry-run',
|
||||
'help' => pht('Do not perform any writes.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'repositories',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
$is_force = $args->getArg('force');
|
||||
$is_dry_run = $args->getArg('dry-run');
|
||||
|
||||
$repositories = $this->loadLocalRepositories($args, 'repositories');
|
||||
if (count($repositories) !== 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify exactly one repository to unpublish.'));
|
||||
}
|
||||
$repository = head($repositories);
|
||||
|
||||
if (!$is_force) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'This script will unpublish all feed stories and notifications '.
|
||||
'which a repository generated during import. This action can not '.
|
||||
'be undone.'));
|
||||
|
||||
$prompt = pht(
|
||||
'Permanently unpublish "%s"?',
|
||||
$repository->getDisplayName());
|
||||
if (!phutil_console_confirm($prompt)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('User aborted workflow.'));
|
||||
}
|
||||
}
|
||||
|
||||
$commits = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer)
|
||||
->withRepositoryPHIDs(array($repository->getPHID()))
|
||||
->execute();
|
||||
|
||||
echo pht("Will unpublish %s commits.\n", count($commits));
|
||||
|
||||
foreach ($commits as $commit) {
|
||||
$this->unpublishCommit($commit, $is_dry_run);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function unpublishCommit(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
$is_dry_run) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Unpublishing commit "%s".',
|
||||
$commit->getMonogram()));
|
||||
|
||||
$stories = id(new PhabricatorFeedQuery())
|
||||
->setViewer($viewer)
|
||||
->withFilterPHIDs(array($commit->getPHID()))
|
||||
->execute();
|
||||
|
||||
if ($stories) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Found %s feed storie(s).',
|
||||
count($stories)));
|
||||
|
||||
if (!$is_dry_run) {
|
||||
$engine = new PhabricatorDestructionEngine();
|
||||
foreach ($stories as $story) {
|
||||
$story_data = $story->getStoryData();
|
||||
$engine->destroyObject($story_data);
|
||||
}
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Destroyed %s feed storie(s).',
|
||||
count($stories)));
|
||||
}
|
||||
}
|
||||
|
||||
$edge_types = array(
|
||||
PhabricatorObjectMentionsObjectEdgeType::EDGECONST => true,
|
||||
DiffusionCommitHasTaskEdgeType::EDGECONST => true,
|
||||
DiffusionCommitHasRevisionEdgeType::EDGECONST => true,
|
||||
DiffusionCommitRevertsCommitEdgeType::EDGECONST => true,
|
||||
);
|
||||
|
||||
$query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(array($commit->getPHID()))
|
||||
->withEdgeTypes(array_keys($edge_types));
|
||||
$edges = $query->execute();
|
||||
|
||||
foreach ($edges[$commit->getPHID()] as $type => $edge_list) {
|
||||
foreach ($edge_list as $edge) {
|
||||
$dst = $edge['dst'];
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Commit "%s" has edge of type "%s" to object "%s".',
|
||||
$commit->getMonogram(),
|
||||
$type,
|
||||
$dst));
|
||||
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($dst))
|
||||
->executeOne();
|
||||
if ($object) {
|
||||
if ($object instanceof PhabricatorApplicationTransactionInterface) {
|
||||
$this->unpublishEdgeTransaction(
|
||||
$commit,
|
||||
$type,
|
||||
$object,
|
||||
$is_dry_run);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function unpublishEdgeTransaction(
|
||||
$src,
|
||||
$type,
|
||||
PhabricatorApplicationTransactionInterface $dst,
|
||||
$is_dry_run) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$query = PhabricatorApplicationTransactionQuery::newQueryForObject($dst)
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($dst->getPHID()));
|
||||
|
||||
$xactions = id(clone $query)
|
||||
->withTransactionTypes(
|
||||
array(
|
||||
PhabricatorTransactions::TYPE_EDGE,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$type_obj = PhabricatorEdgeType::getByConstant($type);
|
||||
$inverse_type = $type_obj->getInverseEdgeConstant();
|
||||
|
||||
$engine = new PhabricatorDestructionEngine();
|
||||
foreach ($xactions as $xaction) {
|
||||
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||
if ($edge_type != $inverse_type) {
|
||||
// Some other type of edge was edited.
|
||||
continue;
|
||||
}
|
||||
|
||||
$record = PhabricatorEdgeChangeRecord::newFromTransaction($xaction);
|
||||
$changed = $record->getChangedPHIDs();
|
||||
if ($changed !== array($src->getPHID())) {
|
||||
// Affected objects were not just the object we're unpublishing.
|
||||
continue;
|
||||
}
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Found edge transaction "%s" on object "%s" for type "%s".',
|
||||
$xaction->getPHID(),
|
||||
$dst->getPHID(),
|
||||
$type));
|
||||
|
||||
if (!$is_dry_run) {
|
||||
$engine->destroyObject($xaction);
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Destroyed transaction "%s" on object "%s".',
|
||||
$xaction->getPHID(),
|
||||
$dst->getPHID()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === DiffusionCommitHasTaskEdgeType::EDGECONST) {
|
||||
$xactions = id(clone $query)
|
||||
->withTransactionTypes(
|
||||
array(
|
||||
ManiphestTaskStatusTransaction::TRANSACTIONTYPE,
|
||||
))
|
||||
->execute();
|
||||
|
||||
if ($xactions) {
|
||||
foreach ($xactions as $xaction) {
|
||||
$metadata = $xaction->getMetadata();
|
||||
if (idx($metadata, 'commitPHID') === $src->getPHID()) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'MANUAL Task "%s" was likely closed improperly by "%s".',
|
||||
$dst->getMonogram(),
|
||||
$src->getMonogram()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === DiffusionCommitHasRevisionEdgeType::EDGECONST) {
|
||||
$xactions = id(clone $query)
|
||||
->withTransactionTypes(
|
||||
array(
|
||||
DifferentialRevisionCloseTransaction::TRANSACTIONTYPE,
|
||||
))
|
||||
->execute();
|
||||
|
||||
if ($xactions) {
|
||||
foreach ($xactions as $xaction) {
|
||||
$metadata = $xaction->getMetadata();
|
||||
if (idx($metadata, 'isCommitClose')) {
|
||||
if (idx($metadata, 'commitPHID') === $src->getPHID()) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'MANUAL Revision "%s" was likely closed improperly by "%s".',
|
||||
$dst->getMonogram(),
|
||||
$src->getMonogram()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$is_dry_run) {
|
||||
id(new PhabricatorEdgeEditor())
|
||||
->removeEdge($src->getPHID(), $type, $dst->getPHID())
|
||||
->save();
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Destroyed edge of type "%s" between "%s" and "%s".',
|
||||
$type,
|
||||
$src->getPHID(),
|
||||
$dst->getPHID()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue