mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Provide a more flexible script for administrative management of audits
Summary: Fixes T3679. This comes up every so often and the old script is extremely broad (nuke everything in a repository). Provide a more surgical tool. Test Plan: Ran a bunch of variations of the script and they all seemed to work OK. Reviewers: btrahan Reviewed By: btrahan CC: aran, staticshock Maniphest Tasks: T3679 Differential Revision: https://secure.phabricator.com/D6678
This commit is contained in:
parent
02ccca4bbd
commit
86989c9f98
7 changed files with 342 additions and 81 deletions
1
bin/audit
Symbolic link
1
bin/audit
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../scripts/setup/manage_audit.php
|
|
@ -1,83 +1,5 @@
|
||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$root = dirname(dirname(dirname(__FILE__)));
|
echo "This script has been replaced with `bin/audit`.\n";
|
||||||
require_once $root.'/scripts/__init_script__.php';
|
exit(1);
|
||||||
|
|
||||||
$args = new PhutilArgumentParser($argv);
|
|
||||||
$args->setTagline('manage open Audit requests');
|
|
||||||
$args->setSynopsis(<<<EOSYNOPSIS
|
|
||||||
**audit.php** __repository_callsign__
|
|
||||||
Close all open audit requests in a repository. This is intended to
|
|
||||||
reset the state of an imported repository which triggered a bunch of
|
|
||||||
spurious audit requests during import.
|
|
||||||
|
|
||||||
EOSYNOPSIS
|
|
||||||
);
|
|
||||||
$args->parseStandardArguments();
|
|
||||||
$args->parse(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'more',
|
|
||||||
'wildcard' => true,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
$more = $args->getArg('more');
|
|
||||||
if (count($more) !== 1) {
|
|
||||||
$args->printHelpAndExit();
|
|
||||||
}
|
|
||||||
$callsign = reset($more);
|
|
||||||
|
|
||||||
$repository = id(new PhabricatorRepository())->loadOneWhere(
|
|
||||||
'callsign = %s',
|
|
||||||
$callsign);
|
|
||||||
if (!$repository) {
|
|
||||||
throw new Exception("No repository exists with callsign '{$callsign}'!");
|
|
||||||
}
|
|
||||||
|
|
||||||
$ok = phutil_console_confirm(
|
|
||||||
'This will reset all open audit requests ("Audit Required" or "Concern '.
|
|
||||||
'Raised") for commits in this repository to "Audit Not Required". This '.
|
|
||||||
'operation destroys information and can not be undone! Are you sure '.
|
|
||||||
'you want to proceed?');
|
|
||||||
if (!$ok) {
|
|
||||||
echo "OK, aborting.\n";
|
|
||||||
die(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Loading commits...\n";
|
|
||||||
$all_commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
|
|
||||||
'repositoryID = %d',
|
|
||||||
$repository->getID());
|
|
||||||
|
|
||||||
echo "Clearing audit requests...\n";
|
|
||||||
|
|
||||||
foreach ($all_commits as $commit) {
|
|
||||||
$query = id(new PhabricatorAuditQuery())
|
|
||||||
->withStatus(PhabricatorAuditQuery::STATUS_OPEN)
|
|
||||||
->withCommitPHIDs(array($commit->getPHID()));
|
|
||||||
$requests = $query->execute();
|
|
||||||
|
|
||||||
echo "Clearing ".$commit->getPHID()."... ";
|
|
||||||
|
|
||||||
if (!$requests) {
|
|
||||||
echo "nothing to do.\n";
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
echo count($requests)." requests to clear";
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($requests as $request) {
|
|
||||||
$request->setAuditStatus(
|
|
||||||
PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED);
|
|
||||||
$request->save();
|
|
||||||
echo ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
$commit->setAuditStatus(PhabricatorAuditCommitStatusConstants::NONE);
|
|
||||||
$commit->save();
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Done.\n";
|
|
||||||
|
|
22
scripts/setup/manage_audit.php
Executable file
22
scripts/setup/manage_audit.php
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$root = dirname(dirname(dirname(__FILE__)));
|
||||||
|
require_once $root.'/scripts/__init_script__.php';
|
||||||
|
|
||||||
|
$args = new PhutilArgumentParser($argv);
|
||||||
|
$args->setTagline('manage audits');
|
||||||
|
$args->setSynopsis(<<<EOSYNOPSIS
|
||||||
|
**audit** __command__ [__options__]
|
||||||
|
Manage Phabricator audits.
|
||||||
|
|
||||||
|
EOSYNOPSIS
|
||||||
|
);
|
||||||
|
$args->parseStandardArguments();
|
||||||
|
|
||||||
|
$workflows = array(
|
||||||
|
new PhabricatorAuditManagementDeleteWorkflow(),
|
||||||
|
new PhutilHelpArgumentWorkflow(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$args->parseWorkflows($workflows);
|
|
@ -862,6 +862,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuditListController' => 'applications/audit/controller/PhabricatorAuditListController.php',
|
'PhabricatorAuditListController' => 'applications/audit/controller/PhabricatorAuditListController.php',
|
||||||
'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php',
|
'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php',
|
||||||
'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php',
|
'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php',
|
||||||
|
'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php',
|
||||||
|
'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php',
|
||||||
'PhabricatorAuditPreviewController' => 'applications/audit/controller/PhabricatorAuditPreviewController.php',
|
'PhabricatorAuditPreviewController' => 'applications/audit/controller/PhabricatorAuditPreviewController.php',
|
||||||
'PhabricatorAuditQuery' => 'applications/audit/query/PhabricatorAuditQuery.php',
|
'PhabricatorAuditQuery' => 'applications/audit/query/PhabricatorAuditQuery.php',
|
||||||
'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php',
|
'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php',
|
||||||
|
@ -2884,6 +2886,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuditListController' => 'PhabricatorAuditController',
|
'PhabricatorAuditListController' => 'PhabricatorAuditController',
|
||||||
'PhabricatorAuditListView' => 'AphrontView',
|
'PhabricatorAuditListView' => 'AphrontView',
|
||||||
'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver',
|
'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||||
|
'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow',
|
||||||
|
'PhabricatorAuditManagementWorkflow' => 'PhutilArgumentWorkflow',
|
||||||
'PhabricatorAuditPreviewController' => 'PhabricatorAuditController',
|
'PhabricatorAuditPreviewController' => 'PhabricatorAuditController',
|
||||||
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
|
'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'PhabricatorAuthAccountView' => 'AphrontView',
|
'PhabricatorAuthAccountView' => 'AphrontView',
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorAuditManagementDeleteWorkflow
|
||||||
|
extends PhabricatorAuditManagementWorkflow {
|
||||||
|
|
||||||
|
public function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('delete')
|
||||||
|
->setExamples('**delete** [--dry-run] ...')
|
||||||
|
->setSynopsis('Delete audit requests matching parameters.')
|
||||||
|
->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'dry-run',
|
||||||
|
'help' => 'Show what would be deleted, but do not actually delete '.
|
||||||
|
'anything.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'users',
|
||||||
|
'param' => 'names',
|
||||||
|
'help' => 'Select only audits by a given list of users.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'repositories',
|
||||||
|
'param' => 'repos',
|
||||||
|
'help' => 'Select only audits in a given list of repositories.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'commits',
|
||||||
|
'param' => 'commits',
|
||||||
|
'help' => 'Select only audits for the given commits.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'min-commit-date',
|
||||||
|
'param' => 'date',
|
||||||
|
'help' => 'Select only audits for commits on or after the given '.
|
||||||
|
'date.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'max-commit-date',
|
||||||
|
'param' => 'date',
|
||||||
|
'help' => 'Select only audits for commits on or before the given '.
|
||||||
|
'date.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'status',
|
||||||
|
'param' => 'status',
|
||||||
|
'help' => 'Select only audits in the given status. By default, '.
|
||||||
|
'only open audits are selected.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'ids',
|
||||||
|
'param' => 'ids',
|
||||||
|
'help' => 'Select only audits with the given IDs.',
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$users = $this->loadUsers($args->getArg('users'));
|
||||||
|
$repos = $this->loadRepos($args->getArg('repositories'));
|
||||||
|
$commits = $this->loadCommits($args->getArg('commits'));
|
||||||
|
$ids = $this->parseList($args->getArg('ids'));
|
||||||
|
|
||||||
|
$status = $args->getArg('status');
|
||||||
|
if (!$status) {
|
||||||
|
$status = PhabricatorAuditQuery::STATUS_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
$min_date = $this->loadDate($args->getArg('min-commit-date'));
|
||||||
|
$max_date = $this->loadDate($args->getArg('max-commit-date'));
|
||||||
|
if ($min_date && $max_date && ($min_date > $max_date)) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
"Specified max date must come after specified min date.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_dry_run = $args->getArg('dry-run');
|
||||||
|
|
||||||
|
$query = id(new PhabricatorAuditQuery())
|
||||||
|
->needCommits(true);
|
||||||
|
|
||||||
|
if ($status) {
|
||||||
|
$query->withStatus($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ids) {
|
||||||
|
$query->withIDs($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($repos) {
|
||||||
|
$query->withRepositoryPHIDs(mpull($repos, 'getPHID'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($users) {
|
||||||
|
$query->withAuditorPHIDs(mpull($users, 'getPHID'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($commits) {
|
||||||
|
$query->withCommitPHIDs(mpull($commits, 'getPHID'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$audits = $query->execute();
|
||||||
|
$commits = $query->getCommits();
|
||||||
|
|
||||||
|
// TODO: AuditQuery is currently not policy-aware and uses an old query
|
||||||
|
// to load commits. Load them in the modern way to get repositories. Remove
|
||||||
|
// this after modernizing PhabricatorAuditQuery.
|
||||||
|
$commits = id(new DiffusionCommitQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(mpull($commits, 'getPHID'))
|
||||||
|
->execute();
|
||||||
|
$commits = mpull($commits, null, 'getPHID');
|
||||||
|
|
||||||
|
foreach ($audits as $key => $audit) {
|
||||||
|
$commit = idx($commits, $audit->getCommitPHID());
|
||||||
|
if (!$commit) {
|
||||||
|
unset($audits[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($min_date && $commit->getEpoch() < $min_date) {
|
||||||
|
unset($audits[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max_date && $commit->getEpoch() > $max_date) {
|
||||||
|
unset($audits[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
|
if (!$audits) {
|
||||||
|
$console->writeErr("%s\n", pht("No audits match the query."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorHandleQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs(mpull($audits, 'getAuditorPHID'))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($audits as $audit) {
|
||||||
|
$commit = idx($commits, $audit->getCommitPHID());
|
||||||
|
|
||||||
|
$console->writeOut(
|
||||||
|
"%s\n",
|
||||||
|
sprintf(
|
||||||
|
"%10d %-16s %-16s %s: %s",
|
||||||
|
$audit->getID(),
|
||||||
|
$handles[$audit->getAuditorPHID()]->getName(),
|
||||||
|
PhabricatorAuditStatusConstants::getStatusName(
|
||||||
|
$audit->getAuditStatus()),
|
||||||
|
$commit->getRepository()->formatCommitName(
|
||||||
|
$commit->getCommitIdentifier()),
|
||||||
|
trim($commit->getSummary())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_dry_run) {
|
||||||
|
$message = pht(
|
||||||
|
'Really delete these %d audit(s)? They will be permanently deleted '.
|
||||||
|
'and can not be recovered.',
|
||||||
|
count($audits));
|
||||||
|
if ($console->confirm($message)) {
|
||||||
|
foreach ($audits as $audit) {
|
||||||
|
$id = $audit->getID();
|
||||||
|
$console->writeOut("%s\n", pht("Deleting audit %d...", $id));
|
||||||
|
$audit->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getViewer() {
|
||||||
|
return PhabricatorUser::getOmnipotentUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadUsers($users) {
|
||||||
|
$users = $this->parseList($users);
|
||||||
|
if (!$users) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$objects = id(new PhabricatorPeopleQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withUsernames($users)
|
||||||
|
->execute();
|
||||||
|
$objects = mpull($objects, null, 'getUsername');
|
||||||
|
|
||||||
|
foreach ($users as $name) {
|
||||||
|
if (empty($objects[$name])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('No such user with username "%s"!', $name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseList($list) {
|
||||||
|
$list = preg_split('/\s*,\s*/', $list);
|
||||||
|
|
||||||
|
foreach ($list as $key => $item) {
|
||||||
|
$list[$key] = trim($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($list as $key => $item) {
|
||||||
|
if (!strlen($item)) {
|
||||||
|
unset($list[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadRepos($callsigns) {
|
||||||
|
$callsigns = $this->parseList($callsigns);
|
||||||
|
if (!$callsigns) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repos = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withCallsigns($callsigns)
|
||||||
|
->execute();
|
||||||
|
$repos = mpull($repos, null, 'getCallsign');
|
||||||
|
|
||||||
|
foreach ($callsigns as $sign) {
|
||||||
|
if (empty($repos[$sign])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('No such repository with callsign "%s"!', $sign));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadDate($date) {
|
||||||
|
if (!$date) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$epoch = strtotime($date);
|
||||||
|
if (!$epoch || $epoch < 1) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Unable to parse date "%s". Use a format like "2000-01-01".',
|
||||||
|
$date));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadCommits($commits) {
|
||||||
|
$names = $this->parseList($commits);
|
||||||
|
if (!$names) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = id(new DiffusionCommitQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withIdentifiers($names);
|
||||||
|
|
||||||
|
$commits = $query->execute();
|
||||||
|
|
||||||
|
$map = $query->getIdentifierMap();
|
||||||
|
foreach ($names as $name) {
|
||||||
|
if (empty($map[$name])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('No such commit "%s"!', $name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $commits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorAuditManagementWorkflow
|
||||||
|
extends PhutilArgumentWorkflow {
|
||||||
|
|
||||||
|
public function isExecutable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
final class PhabricatorAuditQuery {
|
final class PhabricatorAuditQuery {
|
||||||
|
|
||||||
|
private $ids;
|
||||||
private $offset;
|
private $offset;
|
||||||
private $limit;
|
private $limit;
|
||||||
|
|
||||||
|
@ -22,6 +23,11 @@ final class PhabricatorAuditQuery {
|
||||||
|
|
||||||
private $commits;
|
private $commits;
|
||||||
|
|
||||||
|
public function withIDs(array $ids) {
|
||||||
|
$this->ids = $ids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function withCommitPHIDs(array $commit_phids) {
|
public function withCommitPHIDs(array $commit_phids) {
|
||||||
$this->commitPHIDs = $commit_phids;
|
$this->commitPHIDs = $commit_phids;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -156,6 +162,13 @@ final class PhabricatorAuditQuery {
|
||||||
private function buildWhereClause($conn_r) {
|
private function buildWhereClause($conn_r) {
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
|
if ($this->ids) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'req.id IN (%Ld)',
|
||||||
|
$this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->commitPHIDs) {
|
if ($this->commitPHIDs) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
|
@ -212,7 +225,14 @@ final class PhabricatorAuditQuery {
|
||||||
case self::STATUS_ANY:
|
case self::STATUS_ANY:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown status '{$status}'!");
|
$valid = array(
|
||||||
|
self::STATUS_ANY,
|
||||||
|
self::STATUS_OPEN,
|
||||||
|
self::STATUS_CONCERN,
|
||||||
|
);
|
||||||
|
throw new Exception(
|
||||||
|
"Unknown audit status '{$status}'! Valid statuses are: ".
|
||||||
|
implode(', ', $valid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($where) {
|
if ($where) {
|
||||||
|
|
Loading…
Reference in a new issue