Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/almanac/management/AlmanacManagementLockWorkflow.php b/src/applications/almanac/management/AlmanacManagementLockWorkflow.php
index 332e0ab1f5..60783d1bc5 100644
--- a/src/applications/almanac/management/AlmanacManagementLockWorkflow.php
+++ b/src/applications/almanac/management/AlmanacManagementLockWorkflow.php
@@ -1,49 +1,49 @@
<?php
final class AlmanacManagementLockWorkflow
extends AlmanacManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('lock')
->setSynopsis(pht('Lock a service to prevent it from being edited.'))
->setArguments(
array(
array(
'name' => 'services',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$services = $this->loadServices($args->getArg('services'));
if (!$services) {
throw new PhutilArgumentUsageException(
pht('Specify at least one service to lock.'));
}
foreach ($services as $service) {
if ($service->getIsLocked()) {
throw new PhutilArgumentUsageException(
pht(
'Service "%s" is already locked!',
$service->getName()));
}
}
foreach ($services as $service) {
$this->updateServiceLock($service, true);
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('LOCKED'),
pht('Service "%s" was locked.', $service->getName()));
}
return 0;
}
}
diff --git a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
index e0dd214462..022cbc645d 100644
--- a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
+++ b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
@@ -1,190 +1,190 @@
<?php
final class AlmanacManagementRegisterWorkflow
extends AlmanacManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('register')
->setSynopsis(pht('Register this host as an Almanac device.'))
->setArguments(
array(
array(
'name' => 'device',
'param' => 'name',
'help' => pht('Almanac device name to register.'),
),
array(
'name' => 'private-key',
'param' => 'key',
'help' => pht('Path to a private key for the host.'),
),
array(
'name' => 'allow-key-reuse',
'help' => pht(
'Register even if another host is already registered with this '.
'keypair.'),
),
array(
'name' => 'force',
'help' => pht(
'Register this host even if keys already exist.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$device_name = $args->getArg('device');
if (!strlen($device_name)) {
throw new PhutilArgumentUsageException(
pht('Specify a device with --device.'));
}
$device = id(new AlmanacDeviceQuery())
->setViewer($this->getViewer())
->withNames(array($device_name))
->executeOne();
if (!$device) {
throw new PhutilArgumentUsageException(
pht('No such device "%s" exists!', $device_name));
}
$private_key_path = $args->getArg('private-key');
if (!strlen($private_key_path)) {
throw new PhutilArgumentUsageException(
pht('Specify a private key with --private-key.'));
}
if (!Filesystem::pathExists($private_key_path)) {
throw new PhutilArgumentUsageException(
pht('Private key "%s" does not exist!', $private_key_path));
}
$raw_private_key = Filesystem::readFile($private_key_path);
$phd_user = PhabricatorEnv::getEnvConfig('phd.user');
if (!$phd_user) {
throw new PhutilArgumentUsageException(
pht(
'Config option "phd.user" is not set. You must set this option '.
'so the private key can be stored with the correct permissions.'));
}
$tmp = new TempFile();
list($err) = exec_manual('chown %s %s', $phd_user, $tmp);
if ($err) {
throw new PhutilArgumentUsageException(
pht(
'Unable to change ownership of a file to daemon user "%s". Run '.
'this command as %s or root.',
$phd_user,
$phd_user));
}
$stored_public_path = AlmanacKeys::getKeyPath('device.pub');
$stored_private_path = AlmanacKeys::getKeyPath('device.key');
if (!$args->getArg('force')) {
if (Filesystem::pathExists($stored_public_path)) {
throw new PhutilArgumentUsageException(
pht(
'This host already has a registered public key ("%s"). '.
'Remove this key before registering the host, or use '.
'--force to overwrite it.',
Filesystem::readablePath($stored_public_path)));
}
if (Filesystem::pathExists($stored_private_path)) {
throw new PhutilArgumentUsageException(
pht(
'This host already has a registered private key ("%s"). '.
'Remove this key before registering the host, or use '.
'--force to overwrite it.',
Filesystem::readablePath($stored_private_path)));
}
}
list($raw_public_key) = execx('ssh-keygen -y -f %s', $private_key_path);
$key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);
$public_key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer($this->getViewer())
->withKeys(array($key_object))
->executeOne();
if ($public_key) {
if ($public_key->getObjectPHID() !== $device->getPHID()) {
throw new PhutilArgumentUsageException(
pht(
'The public key corresponding to the given private key is '.
'already associated with an object other than the specified '.
'device. You can not use a single private key to identify '.
'multiple devices or users.'));
} else if (!$public_key->getIsTrusted()) {
throw new PhutilArgumentUsageException(
pht(
'The public key corresponding to the given private key is '.
'already associated with the device, but is not trusted. '.
'Registering this key would trust the other entities which '.
'hold it. Use a unique key, or explicitly enable trust for the '.
'current key.'));
} else if (!$args->getArg('allow-key-reuse')) {
throw new PhutilArgumentUsageException(
pht(
'The public key corresponding to the given private key is '.
'already associated with the device. If you do not want to '.
'use a unique key, use --allow-key-reuse to permit '.
'reassociation.'));
}
} else {
$public_key = id(new PhabricatorAuthSSHKey())
->setObjectPHID($device->getPHID())
->attachObject($device)
->setName($device->getSSHKeyDefaultName())
->setKeyType($key_object->getType())
->setKeyBody($key_object->getBody())
->setKeyComment(pht('Registered'))
->setIsTrusted(1);
}
$console->writeOut(
"%s\n",
pht('Installing public key...'));
$tmp_public = new TempFile();
Filesystem::changePermissions($tmp_public, 0600);
execx('chown %s %s', $phd_user, $tmp_public);
Filesystem::writeFile($tmp_public, $raw_public_key);
execx('mv -f %s %s', $tmp_public, $stored_public_path);
$console->writeOut(
"%s\n",
pht('Installing private key...'));
$tmp_private = new TempFile();
Filesystem::changePermissions($tmp_private, 0600);
execx('chown %s %s', $phd_user, $tmp_private);
Filesystem::writeFile($tmp_private, $raw_private_key);
execx('mv -f %s %s', $tmp_private, $stored_private_path);
if (!$public_key->getID()) {
$console->writeOut(
"%s\n",
pht('Registering device key...'));
$public_key->save();
}
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('HOST REGISTERED'),
pht(
'This host has been registered as "%s" and a trusted keypair '.
'has been installed.',
$device_name));
}
}
diff --git a/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php b/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php
index 952d9c71e5..81ece51b72 100644
--- a/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php
+++ b/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php
@@ -1,85 +1,85 @@
<?php
final class AlmanacManagementTrustKeyWorkflow
extends AlmanacManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('trust-key')
->setSynopsis(pht('Mark a public key as trusted.'))
->setArguments(
array(
array(
'name' => 'id',
'param' => 'id',
'help' => pht('ID of the key to trust.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$id = $args->getArg('id');
if (!$id) {
throw new PhutilArgumentUsageException(
pht('Specify a public key to trust with --id.'));
}
$key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer($this->getViewer())
->withIDs(array($id))
->executeOne();
if (!$key) {
throw new PhutilArgumentUsageException(
pht('No public key exists with ID "%s".', $id));
}
if ($key->getIsTrusted()) {
throw new PhutilArgumentUsageException(
pht('Public key with ID %s is already trusted.', $id));
}
if (!($key->getObject() instanceof AlmanacDevice)) {
throw new PhutilArgumentUsageException(
pht('You can only trust keys associated with Almanac devices.'));
}
$handle = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs(array($key->getObject()->getPHID()))
->executeOne();
$console->writeOut(
"**<bg:red> %s </bg>**\n\n%s\n\n%s\n\n%s",
pht('IMPORTANT!'),
phutil_console_wrap(
pht(
'Trusting a public key gives anyone holding the corresponding '.
'private key complete, unrestricted access to all data in '.
'Phabricator. The private key will be able to sign requests that '.
'skip policy and security checks.')),
phutil_console_wrap(
pht(
'This is an advanced feature which should normally be used only '.
'when building a Phabricator cluster. This feature is very '.
'dangerous if misused.')),
pht('This key is associated with device "%s".', $handle->getName()));
$prompt = pht(
'Really trust this key?');
if (!phutil_console_confirm($prompt)) {
throw new PhutilArgumentUsageException(
pht('User aborted workflow.'));
}
$key->setIsTrusted(1);
$key->save();
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('TRUSTED'),
pht('Key %s has been marked as trusted.', $id));
}
}
diff --git a/src/applications/almanac/management/AlmanacManagementUnlockWorkflow.php b/src/applications/almanac/management/AlmanacManagementUnlockWorkflow.php
index 64f4090334..da8ba3dcc7 100644
--- a/src/applications/almanac/management/AlmanacManagementUnlockWorkflow.php
+++ b/src/applications/almanac/management/AlmanacManagementUnlockWorkflow.php
@@ -1,49 +1,49 @@
<?php
final class AlmanacManagementUnlockWorkflow
extends AlmanacManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('unlock')
->setSynopsis(pht('Unlock a service to allow it to be edited.'))
->setArguments(
array(
array(
'name' => 'services',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$services = $this->loadServices($args->getArg('services'));
if (!$services) {
throw new PhutilArgumentUsageException(
pht('Specify at least one service to unlock.'));
}
foreach ($services as $service) {
if (!$service->getIsLocked()) {
throw new PhutilArgumentUsageException(
pht(
'Service "%s" is not locked!',
$service->getName()));
}
}
foreach ($services as $service) {
$this->updateServiceLock($service, false);
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('UNLOCKED'),
pht('Service "%s" was unlocked.', $service->getName()));
}
return 0;
}
}
diff --git a/src/applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php b/src/applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php
index 0c489605f0..6ad427eeae 100644
--- a/src/applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php
+++ b/src/applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php
@@ -1,52 +1,52 @@
<?php
final class AlmanacManagementUntrustKeyWorkflow
extends AlmanacManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('untrust-key')
->setSynopsis(pht('Revoke trust of a public key.'))
->setArguments(
array(
array(
'name' => 'id',
'param' => 'id',
'help' => pht('ID of the key to revoke trust for.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$id = $args->getArg('id');
if (!$id) {
throw new PhutilArgumentUsageException(
pht('Specify a public key to revoke trust for with --id.'));
}
$key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer($this->getViewer())
->withIDs(array($id))
->executeOne();
if (!$key) {
throw new PhutilArgumentUsageException(
pht('No public key exists with ID "%s".', $id));
}
if (!$key->getIsTrusted()) {
throw new PhutilArgumentUsageException(
pht('Public key with ID %s is not trusted.', $id));
}
$key->setIsTrusted(0);
$key->save();
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('TRUST REVOKED'),
pht('Trust has been revoked for public key %s.', $id));
}
}
diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php
index 269301fddc..2fda59e6fe 100644
--- a/src/applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php
+++ b/src/applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php
@@ -1,24 +1,24 @@
<?php
final class PhabricatorAphlictManagementDebugWorkflow
extends PhabricatorAphlictManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
parent::didConstruct();
$this
->setName('debug')
->setSynopsis(
pht(
'Start the notifications server in the foreground and print large '.
'volumes of diagnostic information to the console.'));
}
public function execute(PhutilArgumentParser $args) {
parent::execute($args);
$this->setDebug(true);
$this->willLaunch();
return $this->launch();
}
}
diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php
index 8d96ecd988..55d97a9ac5 100644
--- a/src/applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php
+++ b/src/applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php
@@ -1,23 +1,23 @@
<?php
final class PhabricatorAphlictManagementRestartWorkflow
extends PhabricatorAphlictManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
parent::didConstruct();
$this
->setName('restart')
->setSynopsis(pht('Stop, then start the notifications server.'));
}
public function execute(PhutilArgumentParser $args) {
parent::execute($args);
$err = $this->executeStopCommand();
if ($err) {
return $err;
}
return $this->executeStartCommand();
}
}
diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php
index 3e1ab8a135..4217ac5903 100644
--- a/src/applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php
+++ b/src/applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php
@@ -1,18 +1,18 @@
<?php
final class PhabricatorAphlictManagementStartWorkflow
extends PhabricatorAphlictManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
parent::didConstruct();
$this
->setName('start')
->setSynopsis(pht('Start the notifications server.'));
}
public function execute(PhutilArgumentParser $args) {
parent::execute($args);
return $this->executeStartCommand();
}
}
diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementStatusWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementStatusWorkflow.php
index 18c4cd4812..145a5fcff2 100644
--- a/src/applications/aphlict/management/PhabricatorAphlictManagementStatusWorkflow.php
+++ b/src/applications/aphlict/management/PhabricatorAphlictManagementStatusWorkflow.php
@@ -1,26 +1,26 @@
<?php
final class PhabricatorAphlictManagementStatusWorkflow
extends PhabricatorAphlictManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('status')
->setSynopsis(pht('Show the status of the notifications server.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$pid = $this->getPID();
if (!$pid) {
$console->writeErr(pht("Aphlict is not running.\n"));
return 1;
}
$console->writeOut(pht("Aphlict (%s) is running.\n", $pid));
return 0;
}
}
diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementStopWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementStopWorkflow.php
index f369783eda..f685afbaab 100644
--- a/src/applications/aphlict/management/PhabricatorAphlictManagementStopWorkflow.php
+++ b/src/applications/aphlict/management/PhabricatorAphlictManagementStopWorkflow.php
@@ -1,17 +1,17 @@
<?php
final class PhabricatorAphlictManagementStopWorkflow
extends PhabricatorAphlictManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('stop')
->setSynopsis(pht('Stop the notifications server.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
return $this->executeStopCommand();
}
}
diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php
index 3766ed22e8..03ae4bd30b 100644
--- a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php
+++ b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php
@@ -1,300 +1,300 @@
<?php
abstract class PhabricatorAphlictManagementWorkflow
extends PhabricatorManagementWorkflow {
private $debug = false;
private $clientHost;
private $clientPort;
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setArguments(
array(
array(
'name' => 'client-host',
'param' => 'hostname',
'help' => pht('Hostname to bind to for the client server.'),
),
array(
'name' => 'client-port',
'param' => 'port',
'help' => pht('Port to bind to for the client server.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$this->clientHost = $args->getArg('client-host');
$this->clientPort = $args->getArg('client-port');
return 0;
}
final public function getPIDPath() {
return PhabricatorEnv::getEnvConfig('notification.pidfile');
}
final public function getLogPath() {
$path = PhabricatorEnv::getEnvConfig('notification.log');
try {
$dir = dirname($path);
if (!Filesystem::pathExists($dir)) {
Filesystem::createDirectory($dir, 0755, true);
}
} catch (FilesystemException $ex) {
throw new Exception(
pht(
"Failed to create '%s'. You should manually create this directory.",
$dir));
}
return $path;
}
final public function getPID() {
$pid = null;
if (Filesystem::pathExists($this->getPIDPath())) {
$pid = (int)Filesystem::readFile($this->getPIDPath());
}
return $pid;
}
final public function cleanup($signo = '?') {
global $g_future;
if ($g_future) {
$g_future->resolveKill();
$g_future = null;
}
Filesystem::remove($this->getPIDPath());
exit(1);
}
protected final function setDebug($debug) {
$this->debug = $debug;
}
public static function requireExtensions() {
self::mustHaveExtension('pcntl');
self::mustHaveExtension('posix');
}
private static function mustHaveExtension($ext) {
if (!extension_loaded($ext)) {
echo "ERROR: The PHP extension '{$ext}' is not installed. You must ".
"install it to run aphlict on this machine.\n";
exit(1);
}
$extension = new ReflectionExtension($ext);
foreach ($extension->getFunctions() as $function) {
$function = $function->name;
if (!function_exists($function)) {
echo "ERROR: The PHP function {$function}() is disabled. You must ".
"enable it to run aphlict on this machine.\n";
exit(1);
}
}
}
final protected function willLaunch() {
$console = PhutilConsole::getConsole();
$pid = $this->getPID();
if ($pid) {
throw new PhutilArgumentUsageException(
pht(
'Unable to start notifications server because it is already '.
'running. Use `aphlict restart` to restart it.'));
}
if (posix_getuid() == 0) {
throw new PhutilArgumentUsageException(
pht(
// TODO: Update this message after a while.
'The notification server should not be run as root. It no '.
'longer requires access to privileged ports.'));
}
// Make sure we can write to the PID file.
if (!$this->debug) {
Filesystem::writeFile($this->getPIDPath(), '');
}
// First, start the server in configuration test mode with --test. This
// will let us error explicitly if there are missing modules, before we
// fork and lose access to the console.
$test_argv = $this->getServerArgv();
$test_argv[] = '--test=true';
execx(
'%s %s %Ls',
$this->getNodeBinary(),
$this->getAphlictScriptPath(),
$test_argv);
}
private function getServerArgv() {
$ssl_key = PhabricatorEnv::getEnvConfig('notification.ssl-key');
$ssl_cert = PhabricatorEnv::getEnvConfig('notification.ssl-cert');
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
$server_uri = new PhutilURI($server_uri);
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
$client_uri = new PhutilURI($client_uri);
$log = $this->getLogPath();
$server_argv = array();
$server_argv[] = '--client-port='.coalesce(
$this->clientPort,
$client_uri->getPort());
$server_argv[] = '--admin-port='.$server_uri->getPort();
$server_argv[] = '--admin-host='.$server_uri->getDomain();
if ($ssl_key) {
$server_argv[] = '--ssl-key='.$ssl_key;
}
if ($ssl_cert) {
$server_argv[] = '--ssl-cert='.$ssl_cert;
}
$server_argv[] = '--log='.$log;
if ($this->clientHost) {
$server_argv[] = '--client-host='.$this->clientHost;
}
return $server_argv;
}
private function getAphlictScriptPath() {
$root = dirname(phutil_get_library_root('phabricator'));
return $root.'/support/aphlict/server/aphlict_server.js';
}
final protected function launch() {
$console = PhutilConsole::getConsole();
if ($this->debug) {
$console->writeOut(pht("Starting Aphlict server in foreground...\n"));
} else {
Filesystem::writeFile($this->getPIDPath(), getmypid());
}
$command = csprintf(
'%s %s %Ls',
$this->getNodeBinary(),
$this->getAphlictScriptPath(),
$this->getServerArgv());
if (!$this->debug) {
declare(ticks = 1);
pcntl_signal(SIGINT, array($this, 'cleanup'));
pcntl_signal(SIGTERM, array($this, 'cleanup'));
}
register_shutdown_function(array($this, 'cleanup'));
if ($this->debug) {
$console->writeOut("Launching server:\n\n $ ".$command."\n\n");
$err = phutil_passthru('%C', $command);
$console->writeOut(">>> Server exited!\n");
exit($err);
} else {
while (true) {
global $g_future;
$g_future = new ExecFuture('exec %C', $command);
$g_future->resolve();
// If the server exited, wait a couple of seconds and restart it.
unset($g_future);
sleep(2);
}
}
}
/* -( Commands )----------------------------------------------------------- */
final protected function executeStartCommand() {
$console = PhutilConsole::getConsole();
$this->willLaunch();
$pid = pcntl_fork();
if ($pid < 0) {
throw new Exception('Failed to fork()!');
} else if ($pid) {
$console->writeErr(pht("Aphlict Server started.\n"));
exit(0);
}
// When we fork, the child process will inherit its parent's set of open
// file descriptors. If the parent process of bin/aphlict is waiting for
// bin/aphlict's file descriptors to close, it will be stuck waiting on
// the daemonized process. (This happens if e.g. bin/aphlict is started
// in another script using passthru().)
fclose(STDOUT);
fclose(STDERR);
$this->launch();
return 0;
}
final protected function executeStopCommand() {
$console = PhutilConsole::getConsole();
$pid = $this->getPID();
if (!$pid) {
$console->writeErr(pht("Aphlict is not running.\n"));
return 0;
}
$console->writeErr(pht("Stopping Aphlict Server (%s)...\n", $pid));
posix_kill($pid, SIGINT);
$start = time();
do {
if (!PhabricatorDaemonReference::isProcessRunning($pid)) {
$console->writeOut(
"%s\n",
pht('Aphlict Server (%s) exited normally.', $pid));
$pid = null;
break;
}
usleep(100000);
} while (time() < $start + 5);
if ($pid) {
$console->writeErr(pht('Sending %s a SIGKILL.', $pid)."\n");
posix_kill($pid, SIGKILL);
unset($pid);
}
Filesystem::remove($this->getPIDPath());
return 0;
}
private function getNodeBinary() {
if (Filesystem::binaryExists('nodejs')) {
return 'nodejs';
}
if (Filesystem::binaryExists('node')) {
return 'node';
}
throw new PhutilArgumentUsageException(
pht(
'No `nodejs` or `node` binary was found in $PATH. You must install '.
'Node.js to start the Aphlict server.'));
}
}
diff --git a/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php b/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php
index 9700c38b26..2d9438dd4e 100644
--- a/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php
+++ b/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php
@@ -1,283 +1,283 @@
<?php
final class PhabricatorAuditManagementDeleteWorkflow
extends PhabricatorAuditManagementWorkflow {
- public function didConstruct() {
+ protected 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 = DiffusionCommitQuery::AUDIT_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 DiffusionCommitQuery())
->setViewer($this->getViewer())
->needAuditRequests(true);
if ($status) {
$query->withAuditStatus($status);
}
$id_map = array();
if ($ids) {
$id_map = array_fuse($ids);
$query->withAuditIDs($ids);
}
if ($repos) {
$query->withRepositoryIDs(mpull($repos, 'getID'));
}
$auditor_map = array();
if ($users) {
$auditor_map = array_fuse(mpull($users, 'getPHID'));
$query->withAuditorPHIDs($auditor_map);
}
if ($commits) {
$query->withPHIDs(mpull($commits, 'getPHID'));
}
$commits = $query->execute();
$commits = mpull($commits, null, 'getPHID');
$audits = array();
foreach ($commits as $commit) {
$commit_audits = $commit->getAudits();
foreach ($commit_audits as $key => $audit) {
if ($id_map && empty($id_map[$audit->getID()])) {
unset($commit_audits[$key]);
continue;
}
if ($auditor_map && empty($auditor_map[$audit->getAuditorPHID()])) {
unset($commit_audits[$key]);
continue;
}
if ($min_date && $commit->getEpoch() < $min_date) {
unset($commit_audits[$key]);
continue;
}
if ($max_date && $commit->getEpoch() > $max_date) {
unset($commit_audits[$key]);
continue;
}
}
$audits[] = $commit_audits;
}
$audits = array_mergev($audits);
$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 = $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 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;
}
}
diff --git a/src/applications/celerity/management/CelerityManagementMapWorkflow.php b/src/applications/celerity/management/CelerityManagementMapWorkflow.php
index be844137e2..525a079f53 100644
--- a/src/applications/celerity/management/CelerityManagementMapWorkflow.php
+++ b/src/applications/celerity/management/CelerityManagementMapWorkflow.php
@@ -1,56 +1,56 @@
<?php
final class CelerityManagementMapWorkflow
extends CelerityManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('map')
->setExamples('**map** [options]')
->setSynopsis(pht('Rebuild static resource maps.'))
->setArguments(
array());
}
public function execute(PhutilArgumentParser $args) {
$resources_map = CelerityPhysicalResources::getAll();
$this->log(
pht(
'Rebuilding %d resource source(s).',
new PhutilNumber(count($resources_map))));
foreach ($resources_map as $name => $resources) {
$this->rebuildResources($resources);
}
$this->log(pht('Done.'));
return 0;
}
/**
* Rebuild the resource map for a resource source.
*
* @param CelerityPhysicalResources Resource source to rebuild.
* @return void
*/
private function rebuildResources(CelerityPhysicalResources $resources) {
$this->log(
pht(
'Rebuilding resource source "%s" (%s)...',
$resources->getName(),
get_class($resources)));
id(new CelerityResourceMapGenerator($resources))
->setDebug(true)
->generate()
->write();
}
protected function log($message) {
$console = PhutilConsole::getConsole();
$console->writeErr("%s\n", $message);
}
}
diff --git a/src/applications/conduit/ssh/ConduitSSHWorkflow.php b/src/applications/conduit/ssh/ConduitSSHWorkflow.php
index 17da348e12..bbc809bca6 100644
--- a/src/applications/conduit/ssh/ConduitSSHWorkflow.php
+++ b/src/applications/conduit/ssh/ConduitSSHWorkflow.php
@@ -1,83 +1,83 @@
<?php
final class ConduitSSHWorkflow extends PhabricatorSSHWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this->setName('conduit');
$this->setArguments(
array(
array(
'name' => 'method',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$time_start = microtime(true);
$methodv = $args->getArg('method');
if (!$methodv) {
throw new Exception('No Conduit method provided.');
} else if (count($methodv) > 1) {
throw new Exception('Too many Conduit methods provided.');
}
$method = head($methodv);
$json = $this->readAllInput();
$raw_params = json_decode($json, true);
if (!is_array($raw_params)) {
throw new Exception('Invalid JSON input.');
}
$params = idx($raw_params, 'params', '[]');
$params = json_decode($params, true);
$metadata = idx($params, '__conduit__', array());
unset($params['__conduit__']);
$call = null;
$error_code = null;
$error_info = null;
try {
$call = new ConduitCall($method, $params);
$call->setUser($this->getUser());
$result = $call->execute();
} catch (ConduitException $ex) {
$result = null;
$error_code = $ex->getMessage();
if ($ex->getErrorDescription()) {
$error_info = $ex->getErrorDescription();
} else if ($call) {
$error_info = $call->getErrorDescription($error_code);
}
}
$response = id(new ConduitAPIResponse())
->setResult($result)
->setErrorCode($error_code)
->setErrorInfo($error_info);
$json_out = json_encode($response->toDictionary());
$json_out = $json_out."\n";
$this->getIOChannel()->write($json_out);
// NOTE: Flush here so we can get an accurate result for the duration,
// if the response is large and the receiver is slow to read it.
$this->getIOChannel()->flush();
$time_end = microtime(true);
$connection_id = idx($metadata, 'connectionID');
$log = id(new PhabricatorConduitMethodCallLog())
->setCallerPHID($this->getUser()->getPHID())
->setConnectionID($connection_id)
->setMethod($method)
->setError((string)$error_code)
->setDuration(1000000 * ($time_end - $time_start))
->save();
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php
index ffc3b10938..8f8a2424ae 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementDebugWorkflow.php
@@ -1,49 +1,49 @@
<?php
final class PhabricatorDaemonManagementDebugWorkflow
extends PhabricatorDaemonManagementWorkflow {
public function shouldParsePartial() {
return true;
}
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('debug')
->setExamples('**debug** __daemon__')
->setSynopsis(
pht(
'Start __daemon__ in the foreground and print large volumes of '.
'diagnostic information to the console.'))
->setArguments(
array(
array(
'name' => 'argv',
'wildcard' => true,
),
array(
'name' => 'as-current-user',
'help' => 'Run the daemon as the current user '.
'instead of the configured phd.user',
),
));
}
public function execute(PhutilArgumentParser $args) {
$argv = $args->getArg('argv');
$run_as_current_user = $args->getArg('as-current-user');
if (!$argv) {
throw new PhutilArgumentUsageException(
pht('You must specify which daemon to debug.'));
}
$daemon_class = array_shift($argv);
return $this->launchDaemon(
$daemon_class,
$argv,
$is_debug = true,
$run_as_current_user);
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementLaunchWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementLaunchWorkflow.php
index 8fcfd6ccd8..03e8e405bf 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementLaunchWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementLaunchWorkflow.php
@@ -1,57 +1,57 @@
<?php
final class PhabricatorDaemonManagementLaunchWorkflow
extends PhabricatorDaemonManagementWorkflow {
public function shouldParsePartial() {
return true;
}
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('launch')
->setExamples('**launch** [n] __daemon__ [options]')
->setSynopsis(pht(
'Start a specific __daemon__, or __n__ copies of a specific '.
'__daemon__.'))
->setArguments(
array(
array(
'name' => 'argv',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$argv = $args->getArg('argv');
$daemon_count = 1;
if ($argv) {
if (is_numeric(head($argv))) {
$daemon_count = array_shift($argv);
}
if ($daemon_count < 1) {
throw new PhutilArgumentUsageException(
pht('You must launch at least one daemon.'));
}
}
if (!$argv) {
throw new PhutilArgumentUsageException(
pht('You must specify which daemon to launch.'));
}
$daemon_class = array_shift($argv);
$this->willLaunchDaemons();
for ($ii = 0; $ii < $daemon_count; $ii++) {
$this->launchDaemon($daemon_class, $argv, $is_debug = false);
}
return 0;
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementListWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementListWorkflow.php
index 5f9ce9d95f..4598d7ffee 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementListWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementListWorkflow.php
@@ -1,31 +1,31 @@
<?php
final class PhabricatorDaemonManagementListWorkflow
extends PhabricatorDaemonManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('list')
->setSynopsis(pht('Show a list of available daemons.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$symbols = $this->loadAvailableDaemonClasses();
$symbols = igroup($symbols, 'library');
foreach ($symbols as $library => $symbol_list) {
$console->writeOut(pht('Daemons in library __%s__:', $library)."\n");
foreach ($symbol_list as $symbol) {
$console->writeOut(" %s\n", $symbol['name']);
}
$console->writeOut("\n");
}
return 0;
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementLogWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementLogWorkflow.php
index 0701678ad1..b32c1439f6 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementLogWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementLogWorkflow.php
@@ -1,102 +1,102 @@
<?php
final class PhabricatorDaemonManagementLogWorkflow
extends PhabricatorDaemonManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('log')
->setExamples('**log** [__options__]')
->setSynopsis(
pht(
'Print the logs for all daemons, or some daemon(s) identified by '.
'ID. You can get the ID for a daemon from the Daemon Console in '.
'the web interface.'))
->setArguments(
array(
array(
'name' => 'id',
'param' => 'id',
'help' => 'Show logs for daemon(s) with given ID(s).',
'repeat' => true,
),
array(
'name' => 'limit',
'param' => 'N',
'default' => 100,
'help' => 'Show a specific number of log messages '.
'(default 100).',
),
));
}
public function execute(PhutilArgumentParser $args) {
$query = id(new PhabricatorDaemonLogQuery())
->setViewer($this->getViewer())
->setAllowStatusWrites(true);
$ids = $args->getArg('id');
if ($ids) {
$query->withIDs($ids);
}
$daemons = $query->execute();
if (!$daemons) {
if ($ids) {
throw new PhutilArgumentUsageException(
pht('No daemon(s) with id(s) "%s" exist!', implode(', ', $ids)));
} else {
throw new PhutilArgumentUsageException(
pht('No daemons are running.'));
}
}
$console = PhutilConsole::getConsole();
$limit = $args->getArg('limit');
$logs = id(new PhabricatorDaemonLogEvent())->loadAllWhere(
'logID IN (%Ld) ORDER BY id DESC LIMIT %d',
mpull($daemons, 'getID'),
$limit);
$logs = array_reverse($logs);
$lines = array();
foreach ($logs as $log) {
$text_lines = phutil_split_lines($log->getMessage(), $retain = false);
foreach ($text_lines as $line) {
$lines[] = array(
'id' => $log->getLogID(),
'type' => $log->getLogType(),
'date' => $log->getEpoch(),
'data' => $line,
);
}
}
// Each log message may be several lines. Limit the number of lines we
// output so that `--limit 123` means "show 123 lines", which is the most
// easily understandable behavior.
$lines = array_slice($lines, -$limit);
foreach ($lines as $line) {
$id = $line['id'];
$type = $line['type'];
$data = $line['data'];
$date = date('r', $line['date']);
$console->writeOut(
"%s\n",
sprintf(
'Daemon %d %s [%s] %s',
$id,
$type,
$date,
$data));
}
return 0;
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php
index 0196f47722..66886eb01e 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php
@@ -1,41 +1,41 @@
<?php
final class PhabricatorDaemonManagementRestartWorkflow
extends PhabricatorDaemonManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('restart')
->setSynopsis(
pht(
'Stop, then start the standard daemon loadout.'))
->setArguments(
array(
array(
'name' => 'graceful',
'param' => 'seconds',
'help' => pht(
'Grace period for daemons to attempt a clean shutdown, in '.
'seconds. Defaults to __15__ seconds.'),
'default' => 15,
),
array(
'name' => 'force',
'help' => pht(
'Also stop running processes that look like daemons but do '.
'not have corresponding PID files.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$graceful = $args->getArg('graceful');
$force = $args->getArg('force');
$err = $this->executeStopCommand(array(), $graceful, $force);
if ($err) {
return $err;
}
return $this->executeStartCommand();
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php
index 7dc5908612..4fe526d365 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php
@@ -1,29 +1,29 @@
<?php
final class PhabricatorDaemonManagementStartWorkflow
extends PhabricatorDaemonManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('start')
->setSynopsis(
pht(
'Start the standard configured collection of Phabricator daemons. '.
'This is appropriate for most installs. Use **phd launch** to '.
'customize which daemons are launched.'))
->setArguments(
array(
array(
'name' => 'keep-leases',
'help' => pht(
'By default, **phd start** will free all task leases held by '.
'the daemons. With this flag, this step will be skipped.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
return $this->executeStartCommand($args->getArg('keep-leases'));
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php
index 036effc2a8..5af583b468 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php
@@ -1,99 +1,99 @@
<?php
final class PhabricatorDaemonManagementStatusWorkflow
extends PhabricatorDaemonManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('status')
->setSynopsis(pht('Show status of running daemons.'))
->setArguments(
array(
array(
'name' => 'local',
'help' => pht('Show only local daemons.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
if ($args->getArg('local')) {
$daemons = $this->loadRunningDaemons();
} else {
$daemons = $this->loadAllRunningDaemons();
}
if (!$daemons) {
$console->writeErr(
"%s\n",
pht('There are no running Phabricator daemons.'));
return 1;
}
$status = 0;
$table = id(new PhutilConsoleTable())
->addColumns(array(
'id' => array(
'title' => 'ID',
),
'host' => array(
'title' => 'Host',
),
'pid' => array(
'title' => 'PID',
),
'started' => array(
'title' => 'Started',
),
'daemon' => array(
'title' => 'Daemon',
),
'argv' => array(
'title' => 'Arguments',
),
));
foreach ($daemons as $daemon) {
if ($daemon instanceof PhabricatorDaemonLog) {
$table->addRow(array(
'id' => $daemon->getID(),
'host' => $daemon->getHost(),
'pid' => $daemon->getPID(),
'started' => date('M j Y, g:i:s A', $daemon->getDateCreated()),
'daemon' => $daemon->getDaemon(),
'argv' => csprintf('%LR', $daemon->getExplicitArgv()),
));
} else if ($daemon instanceof PhabricatorDaemonReference) {
$name = $daemon->getName();
if (!$daemon->isRunning()) {
$daemon->updateStatus(PhabricatorDaemonLog::STATUS_DEAD);
$status = 2;
$name = '<DEAD> '.$name;
}
$daemon_log = $daemon->getDaemonLog();
$id = null;
if ($daemon_log) {
$id = $daemon_log->getID();
}
$table->addRow(array(
'id' => $id,
'host' => 'localhost',
'pid' => $daemon->getPID(),
'started' => $daemon->getEpochStarted()
? date('M j Y, g:i:s A', $daemon->getEpochStarted())
: null,
'daemon' => $name,
'argv' => csprintf('%LR', $daemon->getArgv()),
));
}
}
$table->draw();
}
}
diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php
index 209a6cf964..e40437e306 100644
--- a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php
+++ b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php
@@ -1,43 +1,43 @@
<?php
final class PhabricatorDaemonManagementStopWorkflow
extends PhabricatorDaemonManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('stop')
->setSynopsis(
pht(
'Stop all running daemons, or specific daemons identified by PIDs. '.
'Use **phd status** to find PIDs.'))
->setArguments(
array(
array(
'name' => 'graceful',
'param' => 'seconds',
'help' => pht(
'Grace period for daemons to attempt a clean shutdown, in '.
'seconds. Defaults to __15__ seconds.'),
'default' => 15,
),
array(
'name' => 'force',
'help' => pht(
'Also stop running processes that look like daemons but do '.
'not have corresponding PID files.'),
),
array(
'name' => 'pids',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$pids = $args->getArg('pids');
$graceful = $args->getArg('graceful');
$force = $args->getArg('force');
return $this->executeStopCommand($pids, $graceful, $force);
}
}
diff --git a/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
index 894b1ca088..358cb97d35 100644
--- a/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionGitReceivePackSSHWorkflow.php
@@ -1,45 +1,45 @@
<?php
final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this->setName('git-receive-pack');
$this->setArguments(
array(
array(
'name' => 'dir',
'wildcard' => true,
),
));
}
protected function executeRepositoryOperations() {
$args = $this->getArgs();
$path = head($args->getArg('dir'));
$repository = $this->loadRepository($path);
// This is a write, and must have write access.
$this->requireWriteAccess();
$command = csprintf('git-receive-pack %s', $repository->getLocalPath());
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
$future = id(new ExecFuture('%C', $command))
->setEnv($this->getEnvironment());
$err = $this->newPassthruCommand()
->setIOChannel($this->getIOChannel())
->setCommandChannelFromExecFuture($future)
->execute();
if (!$err) {
$repository->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
PhabricatorRepositoryStatusMessage::CODE_OKAY);
$this->waitForGitClient();
}
return $err;
}
}
diff --git a/src/applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php
index cd64367a76..8934c4ac9b 100644
--- a/src/applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionGitUploadPackSSHWorkflow.php
@@ -1,39 +1,39 @@
<?php
final class DiffusionGitUploadPackSSHWorkflow extends DiffusionGitSSHWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this->setName('git-upload-pack');
$this->setArguments(
array(
array(
'name' => 'dir',
'wildcard' => true,
),
));
}
protected function executeRepositoryOperations() {
$args = $this->getArgs();
$path = head($args->getArg('dir'));
$repository = $this->loadRepository($path);
$command = csprintf('git-upload-pack -- %s', $repository->getLocalPath());
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
$future = id(new ExecFuture('%C', $command))
->setEnv($this->getEnvironment());
$err = $this->newPassthruCommand()
->setIOChannel($this->getIOChannel())
->setCommandChannelFromExecFuture($future)
->execute();
if (!$err) {
$this->waitForGitClient();
}
return $err;
}
}
diff --git a/src/applications/diffusion/ssh/DiffusionMercurialServeSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionMercurialServeSSHWorkflow.php
index da2d72a0df..cbd1d1a752 100644
--- a/src/applications/diffusion/ssh/DiffusionMercurialServeSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionMercurialServeSSHWorkflow.php
@@ -1,105 +1,105 @@
<?php
final class DiffusionMercurialServeSSHWorkflow
extends DiffusionMercurialSSHWorkflow {
protected $didSeeWrite;
- public function didConstruct() {
+ protected function didConstruct() {
$this->setName('hg');
$this->setArguments(
array(
array(
'name' => 'repository',
'short' => 'R',
'param' => 'repo',
),
array(
'name' => 'stdio',
),
array(
'name' => 'command',
'wildcard' => true,
),
));
}
protected function executeRepositoryOperations() {
$args = $this->getArgs();
$path = $args->getArg('repository');
$repository = $this->loadRepository($path);
$args = $this->getArgs();
if (!$args->getArg('stdio')) {
throw new Exception('Expected `hg ... --stdio`!');
}
if ($args->getArg('command') !== array('serve')) {
throw new Exception('Expected `hg ... serve`!');
}
$command = csprintf('hg -R %s serve --stdio', $repository->getLocalPath());
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
$future = id(new ExecFuture('%C', $command))
->setEnv($this->getEnvironment());
$io_channel = $this->getIOChannel();
$protocol_channel = new DiffusionMercurialWireClientSSHProtocolChannel(
$io_channel);
$err = id($this->newPassthruCommand())
->setIOChannel($protocol_channel)
->setCommandChannelFromExecFuture($future)
->setWillWriteCallback(array($this, 'willWriteMessageCallback'))
->execute();
// TODO: It's apparently technically possible to communicate errors to
// Mercurial over SSH by writing a special "\n<error>\n-\n" string. However,
// my attempt to implement that resulted in Mercurial closing the socket and
// then hanging, without showing the error. This might be an issue on our
// side (we need to close our half of the socket?), or maybe the code
// for this in Mercurial doesn't actually work, or maybe something else
// is afoot. At some point, we should look into doing this more cleanly.
// For now, when we, e.g., reject writes for policy reasons, the user will
// see "abort: unexpected response: empty string" after the diagnostically
// useful, e.g., "remote: This repository is read-only over SSH." message.
if (!$err && $this->didSeeWrite) {
$repository->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
PhabricatorRepositoryStatusMessage::CODE_OKAY);
}
return $err;
}
public function willWriteMessageCallback(
PhabricatorSSHPassthruCommand $command,
$message) {
$command = $message['command'];
// Check if this is a readonly command.
$is_readonly = false;
if ($command == 'batch') {
$cmds = idx($message['arguments'], 'cmds');
if (DiffusionMercurialWireProtocol::isReadOnlyBatchCommand($cmds)) {
$is_readonly = true;
}
} else if (DiffusionMercurialWireProtocol::isReadOnlyCommand($command)) {
$is_readonly = true;
}
if (!$is_readonly) {
$this->requireWriteAccess();
$this->didSeeWrite = true;
}
// If we're good, return the raw message data.
return $message['raw'];
}
}
diff --git a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php
index d800dafbce..2d3a3280ea 100644
--- a/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php
+++ b/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php
@@ -1,273 +1,273 @@
<?php
/**
* This protocol has a good spec here:
*
* http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_ra_svn/protocol
*
*/
final class DiffusionSubversionServeSSHWorkflow
extends DiffusionSubversionSSHWorkflow {
private $didSeeWrite;
private $inProtocol;
private $outProtocol;
private $inSeenGreeting;
private $outPhaseCount = 0;
private $internalBaseURI;
private $externalBaseURI;
- public function didConstruct() {
+ protected function didConstruct() {
$this->setName('svnserve');
$this->setArguments(
array(
array(
'name' => 'tunnel',
'short' => 't',
),
));
}
protected function executeRepositoryOperations() {
$args = $this->getArgs();
if (!$args->getArg('tunnel')) {
throw new Exception('Expected `svnserve -t`!');
}
$command = csprintf(
'svnserve -t --tunnel-user=%s',
$this->getUser()->getUsername());
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
$future = new ExecFuture('%C', $command);
$this->inProtocol = new DiffusionSubversionWireProtocol();
$this->outProtocol = new DiffusionSubversionWireProtocol();
$err = id($this->newPassthruCommand())
->setIOChannel($this->getIOChannel())
->setCommandChannelFromExecFuture($future)
->setWillWriteCallback(array($this, 'willWriteMessageCallback'))
->setWillReadCallback(array($this, 'willReadMessageCallback'))
->execute();
if (!$err && $this->didSeeWrite) {
$this->getRepository()->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
PhabricatorRepositoryStatusMessage::CODE_OKAY);
}
return $err;
}
public function willWriteMessageCallback(
PhabricatorSSHPassthruCommand $command,
$message) {
$proto = $this->inProtocol;
$messages = $proto->writeData($message);
$result = array();
foreach ($messages as $message) {
$message_raw = $message['raw'];
$struct = $message['structure'];
if (!$this->inSeenGreeting) {
$this->inSeenGreeting = true;
// The first message the client sends looks like:
//
// ( version ( cap1 ... ) url ... )
//
// We want to grab the URL, load the repository, make sure it exists and
// is accessible, and then replace it with the location of the
// repository on disk.
$uri = $struct[2]['value'];
$struct[2]['value'] = $this->makeInternalURI($uri);
$message_raw = $proto->serializeStruct($struct);
} else if (isset($struct[0]) && $struct[0]['type'] == 'word') {
if (!$proto->isReadOnlyCommand($struct)) {
$this->didSeeWrite = true;
$this->requireWriteAccess($struct[0]['value']);
}
// Several other commands also pass in URLs. We need to translate
// all of these into the internal representation; this also makes sure
// they're valid and accessible.
switch ($struct[0]['value']) {
case 'reparent':
// ( reparent ( url ) )
$struct[1]['value'][0]['value'] = $this->makeInternalURI(
$struct[1]['value'][0]['value']);
$message_raw = $proto->serializeStruct($struct);
break;
case 'switch':
// ( switch ( ( rev ) target recurse url ... ) )
$struct[1]['value'][3]['value'] = $this->makeInternalURI(
$struct[1]['value'][3]['value']);
$message_raw = $proto->serializeStruct($struct);
break;
case 'diff':
// ( diff ( ( rev ) target recurse ignore-ancestry url ... ) )
$struct[1]['value'][4]['value'] = $this->makeInternalURI(
$struct[1]['value'][4]['value']);
$message_raw = $proto->serializeStruct($struct);
break;
case 'add-file':
// ( add-file ( path dir-token file-token [ copy-path copy-rev ] ) )
if (isset($struct[1]['value'][3]['value'][0]['value'])) {
$copy_from = $struct[1]['value'][3]['value'][0]['value'];
$copy_from = $this->makeInternalURI($copy_from);
$struct[1]['value'][3]['value'][0]['value'] = $copy_from;
}
$message_raw = $proto->serializeStruct($struct);
break;
}
}
$result[] = $message_raw;
}
if (!$result) {
return null;
}
return implode('', $result);
}
public function willReadMessageCallback(
PhabricatorSSHPassthruCommand $command,
$message) {
$proto = $this->outProtocol;
$messages = $proto->writeData($message);
$result = array();
foreach ($messages as $message) {
$message_raw = $message['raw'];
$struct = $message['structure'];
if (isset($struct[0]) && ($struct[0]['type'] == 'word')) {
if ($struct[0]['value'] == 'success') {
switch ($this->outPhaseCount) {
case 0:
// This is the "greeting", which announces capabilities.
break;
case 1:
// This responds to the client greeting, and announces auth.
break;
case 2:
// This responds to auth, which should be trivial over SSH.
break;
case 3:
// This contains the URI of the repository. We need to edit it;
// if it does not match what the client requested it will reject
// the response.
$struct[1]['value'][1]['value'] = $this->makeExternalURI(
$struct[1]['value'][1]['value']);
$message_raw = $proto->serializeStruct($struct);
break;
default:
// We don't care about other protocol frames.
break;
}
$this->outPhaseCount++;
} else if ($struct[0]['value'] == 'failure') {
// Find any error messages which include the internal URI, and
// replace the text with the external URI.
foreach ($struct[1]['value'] as $key => $error) {
$code = $error['value'][0]['value'];
$message = $error['value'][1]['value'];
$message = str_replace(
$this->internalBaseURI,
$this->externalBaseURI,
$message);
// Derp derp derp derp derp. The structure looks like this:
// ( failure ( ( code message ... ) ... ) )
$struct[1]['value'][$key]['value'][1]['value'] = $message;
}
$message_raw = $proto->serializeStruct($struct);
}
}
$result[] = $message_raw;
}
if (!$result) {
return null;
}
return implode('', $result);
}
private function makeInternalURI($uri_string) {
$uri = new PhutilURI($uri_string);
$proto = $uri->getProtocol();
if ($proto !== 'svn+ssh') {
throw new Exception(
pht(
'Protocol for URI "%s" MUST be "svn+ssh".',
$uri_string));
}
$path = $uri->getPath();
// Subversion presumably deals with this, but make sure there's nothing
// skethcy going on with the URI.
if (preg_match('(/\\.\\./)', $path)) {
throw new Exception(
pht(
'String "/../" is invalid in path specification "%s".',
$uri_string));
}
$repository = $this->loadRepository($path);
$path = preg_replace(
'(^/diffusion/[A-Z]+)',
rtrim($repository->getLocalPath(), '/'),
$path);
if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) {
$path = rtrim($path, '/');
}
$uri->setPath($path);
// If this is happening during the handshake, these are the base URIs for
// the request.
if ($this->externalBaseURI === null) {
$pre = (string)id(clone $uri)->setPath('');
$this->externalBaseURI = $pre.'/diffusion/'.$repository->getCallsign();
$this->internalBaseURI = $pre.rtrim($repository->getLocalPath(), '/');
}
return (string)$uri;
}
private function makeExternalURI($uri) {
$internal = $this->internalBaseURI;
$external = $this->externalBaseURI;
if (strncmp($uri, $internal, strlen($internal)) === 0) {
$uri = $external.substr($uri, strlen($internal));
}
return $uri;
}
}
diff --git a/src/applications/diviner/workflow/DivinerAtomizeWorkflow.php b/src/applications/diviner/workflow/DivinerAtomizeWorkflow.php
index 2694eb9a04..08c20a005c 100644
--- a/src/applications/diviner/workflow/DivinerAtomizeWorkflow.php
+++ b/src/applications/diviner/workflow/DivinerAtomizeWorkflow.php
@@ -1,137 +1,137 @@
<?php
final class DivinerAtomizeWorkflow extends DivinerWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('atomize')
->setSynopsis(pht('Build atoms from source.'))
->setArguments(
array(
array(
'name' => 'atomizer',
'param' => 'class',
'help' => pht('Specify a subclass of DivinerAtomizer.'),
),
array(
'name' => 'book',
'param' => 'path',
'help' => pht('Path to a Diviner book configuration.'),
),
array(
'name' => 'files',
'wildcard' => true,
),
array(
'name' => 'ugly',
'help' => pht('Produce ugly (but faster) output.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$this->readBookConfiguration($args->getArg('book'));
$console = PhutilConsole::getConsole();
$atomizer_class = $args->getArg('atomizer');
if (!$atomizer_class) {
throw new Exception('Specify an atomizer class with --atomizer.');
}
$symbols = id(new PhutilSymbolLoader())
->setName($atomizer_class)
->setConcreteOnly(true)
->setAncestorClass('DivinerAtomizer')
->selectAndLoadSymbols();
if (!$symbols) {
throw new Exception(
"Atomizer class '{$atomizer_class}' must be a concrete subclass of ".
"DivinerAtomizer.");
}
$atomizer = newv($atomizer_class, array());
$files = $args->getArg('files');
if (!$files) {
throw new Exception('Specify one or more files to atomize.');
}
$file_atomizer = new DivinerFileAtomizer();
foreach (array($atomizer, $file_atomizer) as $configure) {
$configure->setBook($this->getConfig('name'));
}
$group_rules = array();
foreach ($this->getConfig('groups', array()) as $group => $spec) {
$include = (array)idx($spec, 'include', array());
foreach ($include as $pattern) {
$group_rules[$pattern] = $group;
}
}
$all_atoms = array();
$context = array(
'group' => null,
);
foreach ($files as $file) {
$abs_path = Filesystem::resolvePath($file, $this->getConfig('root'));
$data = Filesystem::readFile($abs_path);
if (!$this->shouldAtomizeFile($file, $data)) {
$console->writeLog("Skipping %s...\n", $file);
continue;
} else {
$console->writeLog("Atomizing %s...\n", $file);
}
$context['group'] = null;
foreach ($group_rules as $rule => $group) {
if (preg_match($rule, $file)) {
$context['group'] = $group;
break;
}
}
$file_atoms = $file_atomizer->atomize($file, $data, $context);
$all_atoms[] = $file_atoms;
if (count($file_atoms) !== 1) {
throw new Exception('Expected exactly one atom from file atomizer.');
}
$file_atom = head($file_atoms);
$atoms = $atomizer->atomize($file, $data, $context);
foreach ($atoms as $atom) {
if (!$atom->hasParent()) {
$file_atom->addChild($atom);
}
}
$all_atoms[] = $atoms;
}
$all_atoms = array_mergev($all_atoms);
$all_atoms = mpull($all_atoms, 'toDictionary');
$all_atoms = ipull($all_atoms, null, 'hash');
if ($args->getArg('ugly')) {
$json = json_encode($all_atoms);
} else {
$json_encoder = new PhutilJSON();
$json = $json_encoder->encodeFormatted($all_atoms);
}
$console->writeOut('%s', $json);
return 0;
}
private function shouldAtomizeFile($file_name, $file_data) {
return (strpos($file_data, '@'.'undivinable') === false);
}
}
diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php
index bc799af303..cc942e4df0 100644
--- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php
+++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php
@@ -1,531 +1,531 @@
<?php
final class DivinerGenerateWorkflow extends DivinerWorkflow {
private $atomCache;
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('generate')
->setSynopsis(pht('Generate documentation.'))
->setArguments(
array(
array(
'name' => 'clean',
'help' => 'Clear the caches before generating documentation.',
),
array(
'name' => 'book',
'param' => 'path',
'help' => 'Path to a Diviner book configuration.',
),
));
}
protected function getAtomCache() {
if (!$this->atomCache) {
$book_root = $this->getConfig('root');
$book_name = $this->getConfig('name');
$cache_directory = $book_root.'/.divinercache/'.$book_name;
$this->atomCache = new DivinerAtomCache($cache_directory);
}
return $this->atomCache;
}
protected function log($message) {
$console = PhutilConsole::getConsole();
$console->writeErr($message."\n");
}
public function execute(PhutilArgumentParser $args) {
$book = $args->getArg('book');
if ($book) {
$books = array($book);
} else {
$cwd = getcwd();
$this->log(pht('FINDING DOCUMENTATION BOOKS'));
$books = id(new FileFinder($cwd))
->withType('f')
->withSuffix('book')
->find();
if (!$books) {
throw new PhutilArgumentUsageException(
pht(
"There are no Diviner '.book' files anywhere beneath the ".
"current directory. Use '--book <book>' to specify a ".
"documentation book to generate."));
} else {
$this->log(pht('Found %s book(s).', new PhutilNumber(count($books))));
}
}
foreach ($books as $book) {
$short_name = basename($book);
$this->log(pht('Generating book "%s"...', $short_name));
$this->generateBook($book, $args);
$this->log(pht('Completed generation of "%s".', $short_name)."\n");
}
}
private function generateBook($book, PhutilArgumentParser $args) {
$this->atomCache = null;
$this->readBookConfiguration($book);
if ($args->getArg('clean')) {
$this->log(pht('CLEARING CACHES'));
$this->getAtomCache()->delete();
$this->log(pht('Done.')."\n");
}
// The major challenge of documentation generation is one of dependency
// management. When regenerating documentation, we want to do the smallest
// amount of work we can, so that regenerating documentation after minor
// changes is quick.
//
// ATOM CACHE
//
// In the first stage, we find all the direct changes to source code since
// the last run. This stage relies on two data structures:
//
// - File Hash Map: map<file_hash, node_hash>
// - Atom Map: map<node_hash, true>
//
// First, we hash all the source files in the project to detect any which
// have changed since the previous run (i.e., their hash is not present in
// the File Hash Map). If a file's content hash appears in the map, it has
// not changed, so we don't need to reparse it.
//
// We break the contents of each file into "atoms", which represent a unit
// of source code (like a function, method, class or file). Each atom has a
// "node hash" based on the content of the atom: if a function definition
// changes, the node hash of the atom changes too. The primary output of
// the atom cache is a list of node hashes which exist in the project. This
// is the Atom Map. The node hash depends only on the definition of the atom
// and the atomizer implementation. It ends with an "N", for "node".
//
// (We need the Atom Map in addition to the File Hash Map because each file
// may have several atoms in it (e.g., multiple functions, or a class and
// its methods). The File Hash Map contains an exhaustive list of all atoms
// with type "file", but not child atoms of those top-level atoms.)
//
// GRAPH CACHE
//
// We now know which atoms exist, and can compare the Atom Map to some
// existing cache to figure out what has changed. However, this isn't
// sufficient to figure out which documentation actually needs to be
// regnerated, because atoms depend on other atoms. For example, if "B
// extends A" and the definition for A changes, we need to regenerate the
// documentation in B. Similarly, if X links to Y and Y changes, we should
// regenerate X. (In both these cases, the documentation for the connected
// atom may not acutally change, but in some cases it will, and the extra
// work we need to do is generally very small compared to the size of the
// project.)
//
// To figure out which other nodes have changed, we compute a "graph hash"
// for each node. This hash combines the "node hash" with the node hashes
// of connected nodes. Our primary output is a list of graph hashes, which
// a documentation generator can use to easily determine what work needs
// to be done by comparing the list with a list of cached graph hashes,
// then generating documentation for new hashes and deleting documentation
// for missing hashes. The graph hash ends with a "G", for "graph".
//
// In this stage, we rely on three data structures:
//
// - Symbol Map: map<node_hash, symbol_hash>
// - Edge Map: map<node_hash, list<symbol_hash>>
// - Graph Map: map<node_hash, graph_hash>
//
// Calculating the graph hash requires several steps, because we need to
// figure out which nodes an atom is attached to. The atom contains symbolic
// references to other nodes by name (e.g., "extends SomeClass") in the form
// of DivinerAtomRefs. We can also build a symbolic reference for any atom
// from the atom itself. Each DivinerAtomRef generates a symbol hash,
// which ends with an "S", for "symbol".
//
// First, we update the symbol map. We remove (and mark dirty) any symbols
// associated with node hashes which no longer exist (e.g., old/dead nodes).
// Second, we add (and mark dirty) any symbols associated with new nodes.
// We also add edges defined by new nodes to the graph.
//
// We initialize a list of dirty nodes to the list of new nodes, then
// find all nodes connected to dirty symbols and add them to the dirty
// node list. This list now contains every node with a new or changed
// graph hash.
//
// We walk the dirty list and compute the new graph hashes, adding them
// to the graph hash map. This Graph Map can then be passed to an actual
// documentation generator, which can compare the graph hashes to a list
// of already-generated graph hashes and easily assess which documents need
// to be regenerated and which can be deleted.
$this->buildAtomCache();
$this->buildGraphCache();
$this->publishDocumentation($args->getArg('clean'));
}
/* -( Atom Cache )--------------------------------------------------------- */
private function buildAtomCache() {
$this->log(pht('BUILDING ATOM CACHE'));
$file_hashes = $this->findFilesInProject();
$this->log(pht('Found %d file(s) in project.', count($file_hashes)));
$this->deleteDeadAtoms($file_hashes);
$atomize = $this->getFilesToAtomize($file_hashes);
$this->log(pht('Found %d unatomized, uncached file(s).', count($atomize)));
$file_atomizers = $this->getAtomizersForFiles($atomize);
$this->log(pht('Found %d file(s) to atomize.', count($file_atomizers)));
$futures = $this->buildAtomizerFutures($file_atomizers);
$this->log(pht('Atomizing %d file(s).', count($file_atomizers)));
if ($futures) {
$this->resolveAtomizerFutures($futures, $file_hashes);
$this->log(pht('Atomization complete.'));
} else {
$this->log(pht('Atom cache is up to date, no files to atomize.'));
}
$this->log(pht('Writing atom cache.'));
$this->getAtomCache()->saveAtoms();
$this->log(pht('Done.')."\n");
}
private function getAtomizersForFiles(array $files) {
$rules = $this->getRules();
$exclude = $this->getExclude();
$atomizers = array();
foreach ($files as $file) {
foreach ($exclude as $pattern) {
if (preg_match($pattern, $file)) {
continue 2;
}
}
foreach ($rules as $rule => $atomizer) {
$ok = preg_match($rule, $file);
if ($ok === false) {
throw new Exception(
"Rule '{$rule}' is not a valid regular expression.");
}
if ($ok) {
$atomizers[$file] = $atomizer;
continue;
}
}
}
return $atomizers;
}
private function getRules() {
$rules = $this->getConfig('rules', array(
'/\\.diviner$/' => 'DivinerArticleAtomizer',
'/\\.php$/' => 'DivinerPHPAtomizer',
));
return $rules;
}
private function getExclude() {
$exclude = (array)$this->getConfig('exclude', array());
return $exclude;
}
private function findFilesInProject() {
$raw_hashes = id(new FileFinder($this->getConfig('root')))
->excludePath('*/.*')
->withType('f')
->setGenerateChecksums(true)
->find();
$version = $this->getDivinerAtomWorldVersion();
$file_hashes = array();
foreach ($raw_hashes as $file => $md5_hash) {
$rel_file = Filesystem::readablePath($file, $this->getConfig('root'));
// We want the hash to change if the file moves or Diviner gets updated,
// not just if the file content changes. Derive a hash from everything
// we care about.
$file_hashes[$rel_file] = md5("{$rel_file}\0{$md5_hash}\0{$version}").'F';
}
return $file_hashes;
}
private function deleteDeadAtoms(array $file_hashes) {
$atom_cache = $this->getAtomCache();
$hash_to_file = array_flip($file_hashes);
foreach ($atom_cache->getFileHashMap() as $hash => $atom) {
if (empty($hash_to_file[$hash])) {
$atom_cache->deleteFileHash($hash);
}
}
}
private function getFilesToAtomize(array $file_hashes) {
$atom_cache = $this->getAtomCache();
$atomize = array();
foreach ($file_hashes as $file => $hash) {
if (!$atom_cache->fileHashExists($hash)) {
$atomize[] = $file;
}
}
return $atomize;
}
private function buildAtomizerFutures(array $file_atomizers) {
$atomizers = array();
foreach ($file_atomizers as $file => $atomizer) {
$atomizers[$atomizer][] = $file;
}
$root = dirname(phutil_get_library_root('phabricator'));
$config_root = $this->getConfig('root');
$bar = id(new PhutilConsoleProgressBar())
->setTotal(count($file_atomizers));
$futures = array();
foreach ($atomizers as $class => $files) {
foreach (array_chunk($files, 32) as $chunk) {
$future = new ExecFuture(
'%s atomize --ugly --book %s --atomizer %s -- %Ls',
$root.'/bin/diviner',
$this->getBookConfigPath(),
$class,
$chunk);
$future->setCWD($config_root);
$futures[] = $future;
$bar->update(count($chunk));
}
}
$bar->done();
return $futures;
}
private function resolveAtomizerFutures(array $futures, array $file_hashes) {
assert_instances_of($futures, 'Future');
$atom_cache = $this->getAtomCache();
$bar = id(new PhutilConsoleProgressBar())
->setTotal(count($futures));
$futures = id(new FutureIterator($futures))
->limit(4);
foreach ($futures as $key => $future) {
try {
$atoms = $future->resolveJSON();
foreach ($atoms as $atom) {
if ($atom['type'] == DivinerAtom::TYPE_FILE) {
$file_hash = $file_hashes[$atom['file']];
$atom_cache->addFileHash($file_hash, $atom['hash']);
}
$atom_cache->addAtom($atom);
}
} catch (Exception $e) {
phlog($e);
}
$bar->update(1);
}
$bar->done();
}
/**
* Get a global version number, which changes whenever any atom or atomizer
* implementation changes in a way which is not backward-compatible.
*/
private function getDivinerAtomWorldVersion() {
$version = array();
$version['atom'] = DivinerAtom::getAtomSerializationVersion();
$version['rules'] = $this->getRules();
$atomizers = id(new PhutilSymbolLoader())
->setAncestorClass('DivinerAtomizer')
->setConcreteOnly(true)
->selectAndLoadSymbols();
$atomizer_versions = array();
foreach ($atomizers as $atomizer) {
$atomizer_versions[$atomizer['name']] = call_user_func(
array(
$atomizer['name'],
'getAtomizerVersion',
));
}
ksort($atomizer_versions);
$version['atomizers'] = $atomizer_versions;
return md5(serialize($version));
}
/* -( Graph Cache )-------------------------------------------------------- */
private function buildGraphCache() {
$this->log(pht('BUILDING GRAPH CACHE'));
$atom_cache = $this->getAtomCache();
$symbol_map = $atom_cache->getSymbolMap();
$atoms = $atom_cache->getAtomMap();
$dirty_symbols = array();
$dirty_nhashes = array();
$del_atoms = array_diff_key($symbol_map, $atoms);
$this->log(pht('Found %d obsolete atom(s) in graph.', count($del_atoms)));
foreach ($del_atoms as $nhash => $shash) {
$atom_cache->deleteSymbol($nhash);
$dirty_symbols[$shash] = true;
$atom_cache->deleteEdges($nhash);
$atom_cache->deleteGraph($nhash);
}
$new_atoms = array_diff_key($atoms, $symbol_map);
$this->log(pht('Found %d new atom(s) in graph.', count($new_atoms)));
foreach ($new_atoms as $nhash => $ignored) {
$shash = $this->computeSymbolHash($nhash);
$atom_cache->addSymbol($nhash, $shash);
$dirty_symbols[$shash] = true;
$atom_cache->addEdges(
$nhash,
$this->getEdges($nhash));
$dirty_nhashes[$nhash] = true;
}
$this->log(pht('Propagating changes through the graph.'));
// Find all the nodes which point at a dirty node, and dirty them. Then
// find all the nodes which point at those nodes and dirty them, and so
// on. (This is slightly overkill since we probably don't need to propagate
// dirtiness across documentation "links" between symbols, but we do want
// to propagate it across "extends", and we suffer only a little bit of
// collateral damage by over-dirtying as long as the documentation isn't
// too well-connected.)
$symbol_stack = array_keys($dirty_symbols);
while ($symbol_stack) {
$symbol_hash = array_pop($symbol_stack);
foreach ($atom_cache->getEdgesWithDestination($symbol_hash) as $edge) {
$dirty_nhashes[$edge] = true;
$src_hash = $this->computeSymbolHash($edge);
if (empty($dirty_symbols[$src_hash])) {
$dirty_symbols[$src_hash] = true;
$symbol_stack[] = $src_hash;
}
}
}
$this->log(pht('Found %d affected atoms.', count($dirty_nhashes)));
foreach ($dirty_nhashes as $nhash => $ignored) {
$atom_cache->addGraph($nhash, $this->computeGraphHash($nhash));
}
$this->log(pht('Writing graph cache.'));
$atom_cache->saveGraph();
$atom_cache->saveEdges();
$atom_cache->saveSymbols();
$this->log(pht('Done.')."\n");
}
private function computeSymbolHash($node_hash) {
$atom_cache = $this->getAtomCache();
$atom = $atom_cache->getAtom($node_hash);
if (!$atom) {
throw new Exception("No such atom with node hash '{$node_hash}'!");
}
$ref = DivinerAtomRef::newFromDictionary($atom['ref']);
return $ref->toHash();
}
private function getEdges($node_hash) {
$atom_cache = $this->getAtomCache();
$atom = $atom_cache->getAtom($node_hash);
$refs = array();
// Make the atom depend on its own symbol, so that all atoms with the same
// symbol are dirtied (e.g., if a codebase defines the function "f()"
// several times, all of them should be dirtied when one is dirtied).
$refs[DivinerAtomRef::newFromDictionary($atom)->toHash()] = true;
foreach (array_merge($atom['extends'], $atom['links']) as $ref_dict) {
$ref = DivinerAtomRef::newFromDictionary($ref_dict);
if ($ref->getBook() == $atom['book']) {
$refs[$ref->toHash()] = true;
}
}
return array_keys($refs);
}
private function computeGraphHash($node_hash) {
$atom_cache = $this->getAtomCache();
$atom = $atom_cache->getAtom($node_hash);
$edges = $this->getEdges($node_hash);
sort($edges);
$inputs = array(
'atomHash' => $atom['hash'],
'edges' => $edges,
);
return md5(serialize($inputs)).'G';
}
private function publishDocumentation($clean) {
$atom_cache = $this->getAtomCache();
$graph_map = $atom_cache->getGraphMap();
$this->log(pht('PUBLISHING DOCUMENTATION'));
$publisher = new DivinerLivePublisher();
$publisher->setDropCaches($clean);
$publisher->setConfig($this->getAllConfig());
$publisher->setAtomCache($atom_cache);
$publisher->setRenderer(new DivinerDefaultRenderer());
$publisher->publishAtoms(array_values($graph_map));
$this->log(pht('Done.'));
}
}
diff --git a/src/applications/drydock/management/DrydockManagementCloseWorkflow.php b/src/applications/drydock/management/DrydockManagementCloseWorkflow.php
index 774a94b02b..550b441e85 100644
--- a/src/applications/drydock/management/DrydockManagementCloseWorkflow.php
+++ b/src/applications/drydock/management/DrydockManagementCloseWorkflow.php
@@ -1,49 +1,49 @@
<?php
final class DrydockManagementCloseWorkflow
extends DrydockManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('close')
->setSynopsis('Close a resource.')
->setArguments(
array(
array(
'name' => 'ids',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$ids = $args->getArg('ids');
if (!$ids) {
throw new PhutilArgumentUsageException(
'Specify one or more resource IDs to close.');
}
$viewer = $this->getViewer();
$resources = id(new DrydockResourceQuery())
->setViewer($viewer)
->withIDs($ids)
->execute();
foreach ($ids as $id) {
$resource = idx($resources, $id);
if (!$resource) {
$console->writeErr("Resource %d does not exist!\n", $id);
} else if ($resource->getStatus() != DrydockResourceStatus::STATUS_OPEN) {
$console->writeErr("Resource %d is not 'open'!\n", $id);
} else {
$resource->closeResource();
$console->writeErr("Closed resource %d.\n", $id);
}
}
}
}
diff --git a/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php b/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
index eae8e1921e..937a714ea0 100644
--- a/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
+++ b/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
@@ -1,77 +1,77 @@
<?php
final class DrydockManagementCreateResourceWorkflow
extends DrydockManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('create-resource')
->setSynopsis('Create a resource manually.')
->setArguments(
array(
array(
'name' => 'name',
'param' => 'resource_name',
'help' => 'Resource name.',
),
array(
'name' => 'blueprint',
'param' => 'blueprint_id',
'help' => 'Blueprint ID.',
),
array(
'name' => 'attributes',
'param' => 'name=value,...',
'help' => 'Resource attributes.',
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$resource_name = $args->getArg('name');
if (!$resource_name) {
throw new PhutilArgumentUsageException(
'Specify a resource name with `--name`.');
}
$blueprint_id = $args->getArg('blueprint');
if (!$blueprint_id) {
throw new PhutilArgumentUsageException(
'Specify a blueprint ID with `--blueprint`.');
}
$attributes = $args->getArg('attributes');
if ($attributes) {
$options = new PhutilSimpleOptions();
$options->setCaseSensitive(true);
$attributes = $options->parse($attributes);
}
$viewer = $this->getViewer();
$blueprint = id(new DrydockBlueprintQuery())
->setViewer($viewer)
->withIDs(array($blueprint_id))
->executeOne();
if (!$blueprint) {
throw new PhutilArgumentUsageException(
'Specified blueprint does not exist.');
}
$resource = id(new DrydockResource())
->setBlueprintPHID($blueprint->getPHID())
->setType($blueprint->getImplementation()->getType())
->setName($resource_name)
->setStatus(DrydockResourceStatus::STATUS_OPEN);
if ($attributes) {
$resource->setAttributes($attributes);
}
$resource->save();
$console->writeOut("Created Resource %s\n", $resource->getID());
return 0;
}
}
diff --git a/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php b/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php
index 9d910ca2ea..529376fe00 100644
--- a/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php
+++ b/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php
@@ -1,56 +1,56 @@
<?php
final class DrydockManagementLeaseWorkflow
extends DrydockManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('lease')
->setSynopsis('Lease a resource.')
->setArguments(
array(
array(
'name' => 'type',
'param' => 'resource_type',
'help' => 'Resource type.',
),
array(
'name' => 'attributes',
'param' => 'name=value,...',
'help' => 'Resource specficiation.',
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$resource_type = $args->getArg('type');
if (!$resource_type) {
throw new PhutilArgumentUsageException(
'Specify a resource type with `--type`.');
}
$attributes = $args->getArg('attributes');
if ($attributes) {
$options = new PhutilSimpleOptions();
$options->setCaseSensitive(true);
$attributes = $options->parse($attributes);
}
PhabricatorWorker::setRunAllTasksInProcess(true);
$lease = id(new DrydockLease())
->setResourceType($resource_type);
if ($attributes) {
$lease->setAttributes($attributes);
}
$lease
->queueForActivation()
->waitUntilActive();
$console->writeOut("Acquired Lease %s\n", $lease->getID());
return 0;
}
}
diff --git a/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
index 544fe9bdd0..b59204695c 100644
--- a/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
+++ b/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
@@ -1,52 +1,52 @@
<?php
final class DrydockManagementReleaseWorkflow
extends DrydockManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('release')
->setSynopsis('Release a lease.')
->setArguments(
array(
array(
'name' => 'ids',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$ids = $args->getArg('ids');
if (!$ids) {
throw new PhutilArgumentUsageException(
'Specify one or more lease IDs to release.');
}
$viewer = $this->getViewer();
$leases = id(new DrydockLeaseQuery())
->setViewer($viewer)
->withIDs($ids)
->execute();
foreach ($ids as $id) {
$lease = idx($leases, $id);
if (!$lease) {
$console->writeErr("Lease %d does not exist!\n", $id);
} else if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
$console->writeErr("Lease %d is not 'active'!\n", $id);
} else {
$resource = $lease->getResource();
$blueprint = $resource->getBlueprint();
$blueprint->releaseLease($resource, $lease);
$console->writeErr("Released lease %d.\n", $id);
}
}
}
}
diff --git a/src/applications/fact/management/PhabricatorFactManagementAnalyzeWorkflow.php b/src/applications/fact/management/PhabricatorFactManagementAnalyzeWorkflow.php
index 2494ec4fea..461f87379f 100644
--- a/src/applications/fact/management/PhabricatorFactManagementAnalyzeWorkflow.php
+++ b/src/applications/fact/management/PhabricatorFactManagementAnalyzeWorkflow.php
@@ -1,68 +1,68 @@
<?php
final class PhabricatorFactManagementAnalyzeWorkflow
extends PhabricatorFactManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('analyze')
->setSynopsis(pht('Manually invoke fact analyzers.'))
->setArguments(
array(
array(
'name' => 'iterator',
'param' => 'name',
'repeat' => true,
'help' => 'Process only iterator __name__.',
),
array(
'name' => 'all',
'help' => 'Analyze from the beginning, ignoring cursors.',
),
array(
'name' => 'skip-aggregates',
'help' => 'Skip analysis of aggreate facts.',
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$daemon = new PhabricatorFactDaemon(array());
$daemon->setVerbose(true);
$daemon->setEngines(PhabricatorFactEngine::loadAllEngines());
$iterators = PhabricatorFactDaemon::getAllApplicationIterators();
$selected = $args->getArg('iterator');
if ($selected) {
$use = array();
foreach ($selected as $iterator_name) {
if (isset($iterators[$iterator_name])) {
$use[$iterator_name] = $iterators[$iterator_name];
} else {
$console->writeErr(
"%s\n",
pht("Iterator '%s' does not exist.", $iterator_name));
}
}
$iterators = $use;
}
foreach ($iterators as $iterator_name => $iterator) {
if ($args->getArg('all')) {
$daemon->processIterator($iterator);
} else {
$daemon->processIteratorWithCursor($iterator_name, $iterator);
}
}
if (!$args->getArg('skip-aggregates')) {
$daemon->processAggregates();
}
return 0;
}
}
diff --git a/src/applications/fact/management/PhabricatorFactManagementCursorsWorkflow.php b/src/applications/fact/management/PhabricatorFactManagementCursorsWorkflow.php
index c5f9b4a59c..590967bd32 100644
--- a/src/applications/fact/management/PhabricatorFactManagementCursorsWorkflow.php
+++ b/src/applications/fact/management/PhabricatorFactManagementCursorsWorkflow.php
@@ -1,66 +1,66 @@
<?php
final class PhabricatorFactManagementCursorsWorkflow
extends PhabricatorFactManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('cursors')
->setSynopsis(pht('Show a list of fact iterators and cursors.'))
->setExamples(
"**cursors**\n".
"**cursors** --reset __cursor__")
->setArguments(
array(
array(
'name' => 'reset',
'param' => 'cursor',
'repeat' => true,
'help' => 'Reset cursor __cursor__.',
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$reset = $args->getArg('reset');
if ($reset) {
foreach ($reset as $name) {
$cursor = id(new PhabricatorFactCursor())->loadOneWhere(
'name = %s',
$name);
if ($cursor) {
$console->writeOut("%s\n", pht('Resetting cursor %s...', $name));
$cursor->delete();
} else {
$console->writeErr(
"%s\n",
pht('Cursor %s does not exist or is already reset.', $name));
}
}
return 0;
}
$iterator_map = PhabricatorFactDaemon::getAllApplicationIterators();
if (!$iterator_map) {
$console->writeErr("%s\n", pht('No cursors.'));
return 0;
}
$cursors = id(new PhabricatorFactCursor())->loadAllWhere(
'name IN (%Ls)',
array_keys($iterator_map));
$cursors = mpull($cursors, 'getPosition', 'getName');
foreach ($iterator_map as $iterator_name => $iterator) {
$console->writeOut(
"%s (%s)\n",
$iterator_name,
idx($cursors, $iterator_name, 'start'));
}
return 0;
}
}
diff --git a/src/applications/fact/management/PhabricatorFactManagementDestroyWorkflow.php b/src/applications/fact/management/PhabricatorFactManagementDestroyWorkflow.php
index d533d9f055..8a2e5f1ed8 100644
--- a/src/applications/fact/management/PhabricatorFactManagementDestroyWorkflow.php
+++ b/src/applications/fact/management/PhabricatorFactManagementDestroyWorkflow.php
@@ -1,43 +1,43 @@
<?php
final class PhabricatorFactManagementDestroyWorkflow
extends PhabricatorFactManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('destroy')
->setSynopsis(pht('Destroy all facts.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$question = pht(
'Really destroy all facts? They will need to be rebuilt through '.
'analysis, which may take some time.');
$ok = $console->confirm($question, $default = false);
if (!$ok) {
return 1;
}
$tables = array();
$tables[] = new PhabricatorFactRaw();
$tables[] = new PhabricatorFactAggregate();
foreach ($tables as $table) {
$conn = $table->establishConnection('w');
$name = $table->getTableName();
$console->writeOut("%s\n", pht("Destroying table '%s'...", $name));
queryfx(
$conn,
'TRUNCATE TABLE %T',
$name);
}
$console->writeOut("%s\n", pht('Done.'));
}
}
diff --git a/src/applications/fact/management/PhabricatorFactManagementListWorkflow.php b/src/applications/fact/management/PhabricatorFactManagementListWorkflow.php
index a1644e0121..ec8377fc07 100644
--- a/src/applications/fact/management/PhabricatorFactManagementListWorkflow.php
+++ b/src/applications/fact/management/PhabricatorFactManagementListWorkflow.php
@@ -1,24 +1,24 @@
<?php
final class PhabricatorFactManagementListWorkflow
extends PhabricatorFactManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('list')
->setSynopsis(pht('Show a list of fact engines.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$engines = PhabricatorFactEngine::loadAllEngines();
foreach ($engines as $engine) {
$console->writeOut("%s\n", get_class($engine));
}
return 0;
}
}
diff --git a/src/applications/fact/management/PhabricatorFactManagementStatusWorkflow.php b/src/applications/fact/management/PhabricatorFactManagementStatusWorkflow.php
index 0f3c289cbd..604a40b6ee 100644
--- a/src/applications/fact/management/PhabricatorFactManagementStatusWorkflow.php
+++ b/src/applications/fact/management/PhabricatorFactManagementStatusWorkflow.php
@@ -1,47 +1,47 @@
<?php
final class PhabricatorFactManagementStatusWorkflow
extends PhabricatorFactManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('status')
->setSynopsis(pht('Show status of fact data.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$map = array(
'raw' => new PhabricatorFactRaw(),
'agg' => new PhabricatorFactAggregate(),
);
foreach ($map as $type => $table) {
$conn = $table->establishConnection('r');
$name = $table->getTableName();
$row = queryfx_one(
$conn,
'SELECT COUNT(*) N FROM %T',
$name);
$n = $row['N'];
switch ($type) {
case 'raw':
$desc = pht('There are %d raw fact(s) in storage.', $n);
break;
case 'agg':
$desc = pht('There are %d aggregate fact(s) in storage.', $n);
break;
}
$console->writeOut("%s\n", $desc);
}
return 0;
}
}
diff --git a/src/applications/files/management/PhabricatorFilesManagementCompactWorkflow.php b/src/applications/files/management/PhabricatorFilesManagementCompactWorkflow.php
index 63f44228fe..b885f2613b 100644
--- a/src/applications/files/management/PhabricatorFilesManagementCompactWorkflow.php
+++ b/src/applications/files/management/PhabricatorFilesManagementCompactWorkflow.php
@@ -1,133 +1,133 @@
<?php
final class PhabricatorFilesManagementCompactWorkflow
extends PhabricatorFilesManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('compact')
->setSynopsis(
pht(
'Merge identical files to share the same storage. In some cases, '.
'this can repair files with missing data.'))
->setArguments(
array(
array(
'name' => 'dry-run',
'help' => pht('Show what would be compacted.'),
),
array(
'name' => 'all',
'help' => pht('Compact all files.'),
),
array(
'name' => 'names',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$iterator = $this->buildIterator($args);
if (!$iterator) {
throw new PhutilArgumentUsageException(
pht(
'Either specify a list of files to compact, or use `--all` '.
'to compact all files.'));
}
$is_dry_run = $args->getArg('dry-run');
foreach ($iterator as $file) {
$monogram = $file->getMonogram();
$hash = $file->getContentHash();
if (!$hash) {
$console->writeOut(
"%s\n",
pht('%s: No content hash.', $monogram));
continue;
}
// Find other files with the same content hash. We're going to point
// them at the data for this file.
$similar_files = id(new PhabricatorFile())->loadAllWhere(
'contentHash = %s AND id != %d AND
(storageEngine != %s OR storageHandle != %s)',
$hash,
$file->getID(),
$file->getStorageEngine(),
$file->getStorageHandle());
if (!$similar_files) {
$console->writeOut(
"%s\n",
pht('%s: No other files with the same content hash.', $monogram));
continue;
}
// Only compact files into this one if we can load the data. This
// prevents us from breaking working files if we're missing some data.
try {
$data = $file->loadFileData();
} catch (Exception $ex) {
$data = null;
}
if ($data === null) {
$console->writeOut(
"%s\n",
pht(
'%s: Unable to load file data; declining to compact.',
$monogram));
continue;
}
foreach ($similar_files as $similar_file) {
if ($is_dry_run) {
$console->writeOut(
"%s\n",
pht(
'%s: Would compact storage with %s.',
$monogram,
$similar_file->getMonogram()));
continue;
}
$console->writeOut(
"%s\n",
pht(
'%s: Compacting storage with %s.',
$monogram,
$similar_file->getMonogram()));
$old_instance = null;
try {
$old_instance = $similar_file->instantiateStorageEngine();
$old_engine = $similar_file->getStorageEngine();
$old_handle = $similar_file->getStorageHandle();
} catch (Exception $ex) {
// If the old stuff is busted, we just won't try to delete the
// old data.
phlog($ex);
}
$similar_file
->setStorageEngine($file->getStorageEngine())
->setStorageHandle($file->getStorageHandle())
->save();
if ($old_instance) {
$similar_file->deleteFileDataIfUnused(
$old_instance,
$old_engine,
$old_handle);
}
}
}
return 0;
}
}
diff --git a/src/applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php b/src/applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php
index 366eb5fbe4..579fd7e3ba 100644
--- a/src/applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php
+++ b/src/applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php
@@ -1,30 +1,30 @@
<?php
final class PhabricatorFilesManagementEnginesWorkflow
extends PhabricatorFilesManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('engines')
->setSynopsis('List available storage engines.')
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$engines = PhabricatorFile::buildAllEngines();
if (!$engines) {
throw new Exception('No storage engines are available.');
}
foreach ($engines as $engine) {
$console->writeOut(
"%s\n",
$engine->getEngineIdentifier());
}
return 0;
}
}
diff --git a/src/applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php b/src/applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php
index 85eca269bf..5201465be4 100644
--- a/src/applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php
+++ b/src/applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php
@@ -1,106 +1,106 @@
<?php
final class PhabricatorFilesManagementMigrateWorkflow
extends PhabricatorFilesManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('migrate')
->setSynopsis('Migrate files between storage engines.')
->setArguments(
array(
array(
'name' => 'engine',
'param' => 'storage_engine',
'help' => 'Migrate to the named storage engine.',
),
array(
'name' => 'dry-run',
'help' => 'Show what would be migrated.',
),
array(
'name' => 'all',
'help' => 'Migrate all files.',
),
array(
'name' => 'names',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$engine_id = $args->getArg('engine');
if (!$engine_id) {
throw new PhutilArgumentUsageException(
'Specify an engine to migrate to with `--engine`. '.
'Use `files engines` to get a list of engines.');
}
$engine = PhabricatorFile::buildEngine($engine_id);
$iterator = $this->buildIterator($args);
if (!$iterator) {
throw new PhutilArgumentUsageException(
'Either specify a list of files to migrate, or use `--all` '.
'to migrate all files.');
}
$is_dry_run = $args->getArg('dry-run');
$failed = array();
foreach ($iterator as $file) {
$fid = 'F'.$file->getID();
if ($file->getStorageEngine() == $engine_id) {
$console->writeOut(
"%s: Already stored on '%s'\n",
$fid,
$engine_id);
continue;
}
if ($is_dry_run) {
$console->writeOut(
"%s: Would migrate from '%s' to '%s' (dry run)\n",
$fid,
$file->getStorageEngine(),
$engine_id);
continue;
}
$console->writeOut(
"%s: Migrating from '%s' to '%s'...",
$fid,
$file->getStorageEngine(),
$engine_id);
try {
$file->migrateToEngine($engine);
$console->writeOut("done.\n");
} catch (Exception $ex) {
$console->writeOut("failed!\n");
$console->writeErr("%s\n", (string)$ex);
$failed[] = $file;
}
}
if ($failed) {
$console->writeOut("**Failures!**\n");
$ids = array();
foreach ($failed as $file) {
$ids[] = 'F'.$file->getID();
}
$console->writeOut("%s\n", implode(', ', $ids));
return 1;
} else {
$console->writeOut("**Success!**\n");
return 0;
}
}
}
diff --git a/src/applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php b/src/applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php
index 89fab29b69..3f08c76623 100644
--- a/src/applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php
+++ b/src/applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php
@@ -1,69 +1,69 @@
<?php
final class PhabricatorFilesManagementPurgeWorkflow
extends PhabricatorFilesManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('purge')
->setSynopsis('Delete files with missing data.')
->setArguments(
array(
array(
'name' => 'all',
'help' => 'Update all files.',
),
array(
'name' => 'dry-run',
'help' => 'Show what would be updated.',
),
array(
'name' => 'names',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$iterator = $this->buildIterator($args);
if (!$iterator) {
throw new PhutilArgumentUsageException(
'Either specify a list of files to purge, or use `--all` '.
'to purge all files.');
}
$is_dry_run = $args->getArg('dry-run');
foreach ($iterator as $file) {
$fid = 'F'.$file->getID();
try {
$file->loadFileData();
$okay = true;
} catch (Exception $ex) {
$okay = false;
}
if ($okay) {
$console->writeOut(
"%s: File data is OK, not purging.\n",
$fid);
} else {
if ($is_dry_run) {
$console->writeOut(
"%s: Would purge (dry run).\n",
$fid);
} else {
$console->writeOut(
"%s: Purging.\n",
$fid);
$file->delete();
}
}
}
return 0;
}
}
diff --git a/src/applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php b/src/applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php
index 912c210628..95a3ea0567 100644
--- a/src/applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php
+++ b/src/applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php
@@ -1,149 +1,149 @@
<?php
final class PhabricatorFilesManagementRebuildWorkflow
extends PhabricatorFilesManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('rebuild')
->setSynopsis('Rebuild metadata of old files.')
->setArguments(
array(
array(
'name' => 'all',
'help' => 'Update all files.',
),
array(
'name' => 'dry-run',
'help' => 'Show what would be updated.',
),
array(
'name' => 'rebuild-mime',
'help' => 'Rebuild MIME information.',
),
array(
'name' => 'rebuild-dimensions',
'help' => 'Rebuild image dimension information.',
),
array(
'name' => 'names',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$iterator = $this->buildIterator($args);
if (!$iterator) {
throw new PhutilArgumentUsageException(
'Either specify a list of files to update, or use `--all` '.
'to update all files.');
}
$update = array(
'mime' => $args->getArg('rebuild-mime'),
'dimensions' => $args->getArg('rebuild-dimensions'),
);
// If the user didn't select anything, rebuild everything.
if (!array_filter($update)) {
foreach ($update as $key => $ignored) {
$update[$key] = true;
}
}
$is_dry_run = $args->getArg('dry-run');
$failed = array();
foreach ($iterator as $file) {
$fid = 'F'.$file->getID();
if ($update['mime']) {
$tmp = new TempFile();
Filesystem::writeFile($tmp, $file->loadFileData());
$new_type = Filesystem::getMimeType($tmp);
if ($new_type == $file->getMimeType()) {
$console->writeOut(
"%s: Mime type not changed (%s).\n",
$fid,
$new_type);
} else {
if ($is_dry_run) {
$console->writeOut(
"%s: Would update Mime type: '%s' -> '%s'.\n",
$fid,
$file->getMimeType(),
$new_type);
} else {
$console->writeOut(
"%s: Updating Mime type: '%s' -> '%s'.\n",
$fid,
$file->getMimeType(),
$new_type);
$file->setMimeType($new_type);
$file->save();
}
}
}
if ($update['dimensions']) {
if (!$file->isViewableImage()) {
$console->writeOut(
"%s: Not an image file.\n",
$fid);
continue;
}
$metadata = $file->getMetadata();
$image_width = idx($metadata, PhabricatorFile::METADATA_IMAGE_WIDTH);
$image_height = idx($metadata, PhabricatorFile::METADATA_IMAGE_HEIGHT);
if ($image_width && $image_height) {
$console->writeOut(
"%s: Image dimensions already exist.\n",
$fid);
continue;
}
if ($is_dry_run) {
$console->writeOut(
"%s: Would update file dimensions (dry run)\n",
$fid);
continue;
}
$console->writeOut(
'%s: Updating metadata... ',
$fid);
try {
$file->updateDimensions();
$console->writeOut("done.\n");
} catch (Exception $ex) {
$console->writeOut("failed!\n");
$console->writeErr("%s\n", (string)$ex);
$failed[] = $file;
}
}
}
if ($failed) {
$console->writeOut("**Failures!**\n");
$ids = array();
foreach ($failed as $file) {
$ids[] = 'F'.$file->getID();
}
$console->writeOut("%s\n", implode(', ', $ids));
return 1;
} else {
$console->writeOut("**Success!**\n");
return 0;
}
return 0;
}
}
diff --git a/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php b/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php
index e3d923ba67..e7d6adb54f 100644
--- a/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php
+++ b/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php
@@ -1,92 +1,92 @@
<?php
final class HarbormasterManagementBuildWorkflow
extends HarbormasterManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('build')
->setExamples('**build** [__options__] __buildable__ --plan __id__')
->setSynopsis(pht('Run plan __id__ on __buildable__.'))
->setArguments(
array(
array(
'name' => 'plan',
'param' => 'id',
'help' => pht('ID of build plan to run.'),
),
array(
'name' => 'buildable',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$names = $args->getArg('buildable');
if (count($names) != 1) {
throw new PhutilArgumentUsageException(
pht('Specify exactly one buildable object, by object name.'));
}
$name = head($names);
$buildable = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames($names)
->executeOne();
if (!$buildable) {
throw new PhutilArgumentUsageException(
pht('No such buildable "%s"!', $name));
}
if (!($buildable instanceof HarbormasterBuildableInterface)) {
throw new PhutilArgumentUsageException(
pht('Object "%s" is not a buildable!', $name));
}
$plan_id = $args->getArg('plan');
if (!$plan_id) {
throw new PhutilArgumentUsageException(
pht('Use --plan to specify a build plan to run.'));
}
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($plan_id))
->executeOne();
if (!$plan) {
throw new PhutilArgumentUsageException(
pht('Build plan "%s" does not exist.', $plan_id));
}
$console = PhutilConsole::getConsole();
$buildable = HarbormasterBuildable::initializeNewBuildable($viewer)
->setIsManualBuildable(true)
->setBuildablePHID($buildable->getHarbormasterBuildablePHID())
->setContainerPHID($buildable->getHarbormasterContainerPHID())
->save();
$console->writeOut(
"%s\n",
pht(
'Applying plan %s to new buildable %s...',
$plan->getID(),
'B'.$buildable->getID()));
$console->writeOut(
"\n %s\n\n",
PhabricatorEnv::getProductionURI('/B'.$buildable->getID()));
PhabricatorWorker::setRunAllTasksInProcess(true);
$buildable->applyPlan($plan);
$console->writeOut("%s\n", pht('Done.'));
return 0;
}
}
diff --git a/src/applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php b/src/applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php
index 2ef273ca3c..05d1e5a6a2 100644
--- a/src/applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php
+++ b/src/applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php
@@ -1,114 +1,114 @@
<?php
final class HarbormasterManagementUpdateWorkflow
extends HarbormasterManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('update')
->setExamples('**update** [__options__] __buildable__')
->setSynopsis(pht('Explicitly update the builds for __buildable__.'))
->setArguments(
array(
array(
'name' => 'build',
'param' => 'id',
'help' => pht('Update only this build.'),
),
array(
'name' => 'force',
'help' => pht(
'Force the buildable to update even if no build status '.
'changes occur during normal update.'),
),
array(
'name' => 'background',
'help' => pht(
'If updating generates tasks, queue them for the daemons '.
'instead of executing them in this process.'),
),
array(
'name' => 'buildable',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$force_update = $args->getArg('force');
$names = $args->getArg('buildable');
if (count($names) != 1) {
throw new PhutilArgumentUsageException(
pht('Specify exactly one buildable, by object name.'));
}
$buildable = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames($names)
->executeOne();
if (!$buildable) {
throw new PhutilArgumentUsageException(
pht('No such buildable "%s"!', head($names)));
}
if (!($buildable instanceof HarbormasterBuildable)) {
throw new PhutilArgumentUsageException(
pht('Object "%s" is not a Harbormaster Buildable!', head($names)));
}
// Reload the buildable directly to get builds.
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withIDs(array($buildable->getID()))
->needBuilds(true)
->executeOne();
$builds = $buildable->getBuilds();
$builds = mpull($builds, null, 'getID');
$build_id = $args->getArg('build');
if ($build_id) {
$builds = array_select_keys($builds, array($build_id));
if (!$builds) {
throw new PhutilArgumentUsageException(
pht(
'The specified buildable does not have a build with ID "%s".',
$build_id));
}
}
$console = PhutilConsole::getConsole();
if (!$args->getArg('background')) {
PhabricatorWorker::setRunAllTasksInProcess(true);
}
foreach ($builds as $build) {
$console->writeOut(
"%s\n",
pht(
'Updating build %d of buildable %s...',
$build->getID(),
$buildable->getMonogram()));
$engine = id(new HarbormasterBuildEngine())
->setViewer($viewer)
->setBuild($build);
if ($force_update) {
$engine->setForceBuildableUpdate(true);
}
$engine->continueBuild();
}
$console->writeOut("%s\n", pht('Done.'));
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php
index 7888ca824d..b34e430ff4 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php
@@ -1,92 +1,92 @@
<?php
final class PhabricatorRepositoryManagementCacheWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('cache')
->setExamples(
'**cache** [__options__] --commit __commit__ --path __path__')
->setSynopsis(pht('Manage the repository graph cache.'))
->setArguments(
array(
array(
'name' => 'commit',
'param' => 'commit',
'help' => pht('Specify a commit to look up.'),
),
array(
'name' => 'path',
'param' => 'path',
'help' => pht('Specify a path to look up.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$commit_name = $args->getArg('commit');
if ($commit_name === null) {
throw new PhutilArgumentUsageException(
pht('Specify a commit to look up with `--commit`.'));
}
$commit = $this->loadNamedCommit($commit_name);
$path_name = $args->getArg('path');
if ($path_name === null) {
throw new PhutilArgumentUsageException(
pht('Specify a path to look up with `--path`.'));
}
$path_map = id(new DiffusionPathIDQuery(array($path_name)))
->loadPathIDs();
if (empty($path_map[$path_name])) {
throw new PhutilArgumentUsageException(
pht('Path "%s" is not known to Phabricator.', $path_name));
}
$path_id = $path_map[$path_name];
$graph_cache = new PhabricatorRepositoryGraphCache();
$t_start = microtime(true);
$cache_result = $graph_cache->loadLastModifiedCommitID(
$commit->getID(),
$path_id);
$t_end = microtime(true);
$console = PhutilConsole::getConsole();
$console->writeOut(
"%s\n",
pht('Query took %s ms.', new PhutilNumber(1000 * ($t_end - $t_start))));
if ($cache_result === false) {
$console->writeOut(
"%s\n",
pht('Not found in graph cache.'));
} else if ($cache_result === null) {
$console->writeOut(
"%s\n",
pht('Path not modified in any ancestor commit.'));
} else {
$last = id(new DiffusionCommitQuery())
->setViewer($this->getViewer())
->withIDs(array($cache_result))
->executeOne();
if (!$last) {
throw new Exception(pht('Cache returned bogus result!'));
}
$console->writeOut(
"%s\n",
pht(
'Path was last changed at %s.',
$commit->getRepository()->formatCommitName(
$last->getcommitIdentifier())));
}
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php
index fbf6873db1..66d9a22fec 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php
@@ -1,53 +1,53 @@
<?php
final class PhabricatorRepositoryManagementDiscoverWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('discover')
->setExamples('**discover** [__options__] __repository__ ...')
->setSynopsis('Discover __repository__, named by callsign.')
->setArguments(
array(
array(
'name' => 'verbose',
'help' => 'Show additional debugging information.',
),
array(
'name' => 'repair',
'help' => 'Repair a repository with gaps in commit '.
'history.',
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
'Specify one or more repositories to discover, by callsign.');
}
$console = PhutilConsole::getConsole();
foreach ($repos as $repo) {
$console->writeOut("Discovering '%s'...\n", $repo->getCallsign());
id(new PhabricatorRepositoryDiscoveryEngine())
->setRepository($repo)
->setVerbose($args->getArg('verbose'))
->setRepairMode($args->getArg('repair'))
->discoverCommits();
}
$console->writeOut("Done.\n");
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php
index b3983d2c0c..ac7a7c6b07 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php
@@ -1,96 +1,96 @@
<?php
final class PhabricatorRepositoryManagementEditWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('edit')
->setExamples('**edit** --as __username__ __repository__ ...')
->setSynopsis('Edit __repository__, named by callsign.')
->setArguments(
array(
array(
'name' => 'repos',
'wildcard' => true,
),
array(
'name' => 'as',
'param' => 'user',
'help' => 'Edit as user.',
),
array(
'name' => 'local-path',
'param' => 'path',
'help' => 'Edit the local path.',
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
'Specify one or more repositories to edit, by callsign.');
}
$console = PhutilConsole::getConsole();
// TODO: It would be nice to just take this action as "Administrator" or
// similar, since that would make it easier to use this script, harder to
// impersonate users, and more clear to viewers what happened. However,
// the omnipotent user doesn't have a PHID right now, can't be loaded,
// doesn't have a handle, etc. Adding all of that is fairly involved, and
// I want to wait for stronger use cases first.
$username = $args->getArg('as');
if (!$username) {
throw new PhutilArgumentUsageException(
pht('Specify a user to edit as with --as <username>.'));
}
$actor = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withUsernames(array($username))
->executeOne();
if (!$actor) {
throw new PhutilArgumentUsageException(
pht("No such user '%s'!", $username));
}
foreach ($repos as $repo) {
$console->writeOut("Editing '%s'...\n", $repo->getCallsign());
$xactions = array();
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
if ($args->getArg('local-path')) {
$xactions[] = id(new PhabricatorRepositoryTransaction())
->setTransactionType($type_local_path)
->setNewValue($args->getArg('local-path'));
}
if (!$xactions) {
throw new PhutilArgumentUsageException(
pht('Specify one or more fields to edit!'));
}
$content_source = PhabricatorContentSource::newConsoleSource();
$editor = id(new PhabricatorRepositoryEditor())
->setActor($actor)
->setContentSource($content_source)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($repo, $xactions);
}
$console->writeOut("Done.\n");
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php
index 47dc47728f..d895b07ae6 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php
@@ -1,87 +1,87 @@
<?php
final class PhabricatorRepositoryManagementImportingWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('importing')
->setExamples('**importing** __repository__ ...')
->setSynopsis(
'Show commits in __repository__, named by callsign, which are still '.
'importing.')
->setArguments(
array(
array(
'name' => 'simple',
'help' => 'Show simpler output.',
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
'Specify one or more repositories to find importing commits for, '.
'by callsign.');
}
$repos = mpull($repos, null, 'getID');
$table = new PhabricatorRepositoryCommit();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT repositoryID, commitIdentifier, importStatus FROM %T
WHERE repositoryID IN (%Ld) AND (importStatus & %d) != %d',
$table->getTableName(),
array_keys($repos),
PhabricatorRepositoryCommit::IMPORTED_ALL,
PhabricatorRepositoryCommit::IMPORTED_ALL);
$console = PhutilConsole::getConsole();
if ($rows) {
foreach ($rows as $row) {
$repo = $repos[$row['repositoryID']];
$identifier = $row['commitIdentifier'];
$console->writeOut('%s', 'r'.$repo->getCallsign().$identifier);
if (!$args->getArg('simple')) {
$status = $row['importStatus'];
$need = array();
if (!($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE)) {
$need[] = 'Message';
}
if (!($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE)) {
$need[] = 'Change';
}
if (!($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS)) {
$need[] = 'Owners';
}
if (!($status & PhabricatorRepositoryCommit::IMPORTED_HERALD)) {
$need[] = 'Herald';
}
$console->writeOut(' %s', implode(', ', $need));
}
$console->writeOut("\n");
}
} else {
$console->writeErr(
"%s\n",
pht('No importing commits found.'));
}
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php
index 0ee1adbce9..9d584af738 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php
@@ -1,30 +1,30 @@
<?php
final class PhabricatorRepositoryManagementListWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('list')
->setSynopsis('Show a list of repositories.')
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->execute();
if ($repos) {
foreach ($repos as $repo) {
$console->writeOut("%s\n", $repo->getCallsign());
}
} else {
$console->writeErr("%s\n", 'There are no repositories.');
}
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php
index c06cdcd1f4..1f93618042 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php
@@ -1,112 +1,112 @@
<?php
final class PhabricatorRepositoryManagementLookupUsersWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('lookup-users')
->setExamples('**lookup-users** __commit__ ...')
->setSynopsis('Resolve user accounts for users attached to __commit__.')
->setArguments(
array(
array(
'name' => 'commits',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$commits = $this->loadCommits($args, 'commits');
if (!$commits) {
throw new PhutilArgumentUsageException(
'Specify one or more commits to resolve users for.');
}
$console = PhutilConsole::getConsole();
foreach ($commits as $commit) {
$repo = $commit->getRepository();
$name = $repo->formatCommitName($commit->getCommitIdentifier());
$console->writeOut(
"%s\n",
pht('Examining commit %s...', $name));
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
$this->getViewer(),
DiffusionRequest::newFromDictionary(
array(
'repository' => $repo,
'user' => $this->getViewer(),
)),
'diffusion.querycommits',
array(
'phids' => array($commit->getPHID()),
'bypassCache' => true,
));
if (empty($refs_raw['data'])) {
throw new Exception(
pht('Unable to retrieve details for commit "%s"!'));
}
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
$author = $ref->getAuthor();
$console->writeOut(
"%s\n",
pht('Raw author string: %s', coalesce($author, 'null')));
if ($author !== null) {
$handle = $this->resolveUser($commit, $author);
if ($handle) {
$console->writeOut(
"%s\n",
pht('Phabricator user: %s', $handle->getFullName()));
} else {
$console->writeOut(
"%s\n",
pht('Unable to resolve a corresponding Phabricator user.'));
}
}
$committer = $ref->getCommitter();
$console->writeOut(
"%s\n",
pht('Raw committer string: %s', coalesce($committer, 'null')));
if ($committer !== null) {
$handle = $this->resolveUser($commit, $committer);
if ($handle) {
$console->writeOut(
"%s\n",
pht('Phabricator user: %s', $handle->getFullName()));
} else {
$console->writeOut(
"%s\n",
pht('Unable to resolve a corresponding Phabricator user.'));
}
}
}
return 0;
}
private function resolveUser(PhabricatorRepositoryCommit $commit, $name) {
$phid = id(new DiffusionResolveUserQuery())
->withCommit($commit)
->withName($name)
->execute();
if (!$phid) {
return null;
}
return id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs(array($phid))
->executeOne();
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php
index d90e0e501d..411a3e10c1 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php
@@ -1,67 +1,67 @@
<?php
final class PhabricatorRepositoryManagementMarkImportedWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('mark-imported')
->setExamples('**mark-imported** __repository__ ...')
->setSynopsis('Mark __repository__, named by callsign, as imported.')
->setArguments(
array(
array(
'name' => 'mark-not-imported',
'help' => 'Instead, mark repositories as NOT imported.',
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
'Specify one or more repositories to mark imported, by callsign.');
}
$new_importing_value = (bool)$args->getArg('mark-not-imported');
$console = PhutilConsole::getConsole();
foreach ($repos as $repo) {
$callsign = $repo->getCallsign();
if ($repo->isImporting() && $new_importing_value) {
$console->writeOut(
"%s\n",
pht("Repository '%s' is already importing.", $callsign));
} else if (!$repo->isImporting() && !$new_importing_value) {
$console->writeOut(
"%s\n",
pht("Repository '%s' is already imported.", $callsign));
} else {
if ($new_importing_value) {
$console->writeOut(
"%s\n",
pht("Marking repository '%s' as importing.", $callsign));
} else {
$console->writeOut(
"%s\n",
pht("Marking repository '%s' as imported.", $callsign));
}
$repo->setDetail('importing', $new_importing_value);
$repo->save();
}
}
$console->writeOut("Done.\n");
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php
index 5aab451c59..243ae640a8 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php
@@ -1,52 +1,52 @@
<?php
final class PhabricatorRepositoryManagementMirrorWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('mirror')
->setExamples('**mirror** [__options__] __repository__ ...')
->setSynopsis(
pht('Push __repository__, named by callsign, to mirrors.'))
->setArguments(
array(
array(
'name' => 'verbose',
'help' => pht('Show additional debugging information.'),
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
pht(
'Specify one or more repositories to push to mirrors, by '.
'callsign.'));
}
$console = PhutilConsole::getConsole();
foreach ($repos as $repo) {
$console->writeOut(
"%s\n",
pht('Pushing "%s" to mirrors...', $repo->getCallsign()));
$engine = id(new PhabricatorRepositoryMirrorEngine())
->setRepository($repo)
->setVerbose($args->getArg('verbose'))
->pushToMirrors();
}
$console->writeOut("Done.\n");
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php
index 0cd355c46d..3cbbf5b5bf 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php
@@ -1,180 +1,180 @@
<?php
final class PhabricatorRepositoryManagementParentsWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('parents')
->setExamples('**parents** [options] [__repository__] ...')
->setSynopsis(
pht(
'Build parent caches in repositories that are missing the data, '.
'or rebuild them in a specific __repository__.'))
->setArguments(
array(
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->execute();
}
$console = PhutilConsole::getConsole();
foreach ($repos as $repo) {
$monogram = $repo->getMonogram();
if ($repo->isSVN()) {
$console->writeOut(
"%s\n",
pht(
'Skipping "%s": Subversion repositories do not require this '.
'cache to be built.',
$monogram));
continue;
}
$this->rebuildRepository($repo);
}
return 0;
}
private function rebuildRepository(PhabricatorRepository $repo) {
$console = PhutilConsole::getConsole();
$console->writeOut("%s\n", pht('Rebuilding "%s"...', $repo->getMonogram()));
$refs = id(new PhabricatorRepositoryRefCursorQuery())
->setViewer($this->getViewer())
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
->withRepositoryPHIDs(array($repo->getPHID()))
->execute();
$graph = array();
foreach ($refs as $ref) {
if (!$repo->shouldTrackBranch($ref->getRefName())) {
continue;
}
$console->writeOut(
"%s\n",
pht('Rebuilding branch "%s"...', $ref->getRefName()));
$commit = $ref->getCommitIdentifier();
if ($repo->isGit()) {
$stream = new PhabricatorGitGraphStream($repo, $commit);
} else {
$stream = new PhabricatorMercurialGraphStream($repo, $commit);
}
$discover = array($commit);
while ($discover) {
$target = array_pop($discover);
if (isset($graph[$target])) {
continue;
}
$graph[$target] = $stream->getParents($target);
foreach ($graph[$target] as $parent) {
$discover[] = $parent;
}
}
}
$console->writeOut(
"%s\n",
pht(
'Found %s total commit(s); updating...',
new PhutilNumber(count($graph))));
$commit_table = id(new PhabricatorRepositoryCommit());
$commit_table_name = $commit_table->getTableName();
$conn_w = $commit_table->establishConnection('w');
$bar = id(new PhutilConsoleProgressBar())
->setTotal(count($graph));
$need = array();
foreach ($graph as $child => $parents) {
foreach ($parents as $parent) {
$need[$parent] = $parent;
}
$need[$child] = $child;
}
$map = array();
foreach (array_chunk($need, 2048) as $chunk) {
$rows = queryfx_all(
$conn_w,
'SELECT id, commitIdentifier FROM %T
WHERE commitIdentifier IN (%Ls) AND repositoryID = %d',
$commit_table_name,
$chunk,
$repo->getID());
foreach ($rows as $row) {
$map[$row['commitIdentifier']] = $row['id'];
}
}
$insert_sql = array();
$delete_sql = array();
foreach ($graph as $child => $parents) {
$names = $parents;
$names[] = $child;
foreach ($names as $name) {
if (empty($map[$name])) {
throw new Exception(pht('Unknown commit "%s"!', $name));
}
}
if (!$parents) {
// Write an explicit 0 to indicate "no parents" instead of "no data".
$insert_sql[] = qsprintf(
$conn_w,
'(%d, 0)',
$map[$child]);
} else {
foreach ($parents as $parent) {
$insert_sql[] = qsprintf(
$conn_w,
'(%d, %d)',
$map[$child],
$map[$parent]);
}
}
$delete_sql[] = $map[$child];
$bar->update(1);
}
$commit_table->openTransaction();
foreach (PhabricatorLiskDAO::chunkSQL($delete_sql) as $chunk) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE childCommitID IN (%Q)',
PhabricatorRepository::TABLE_PARENTS,
$chunk);
}
foreach (PhabricatorLiskDAO::chunkSQL($insert_sql) as $chunk) {
queryfx(
$conn_w,
'INSERT INTO %T (childCommitID, parentCommitID) VALUES %Q',
PhabricatorRepository::TABLE_PARENTS,
$chunk);
}
$commit_table->saveTransaction();
$bar->done();
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
index ada90352a9..4cc7605610 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
@@ -1,47 +1,47 @@
<?php
final class PhabricatorRepositoryManagementPullWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('pull')
->setExamples('**pull** __repository__ ...')
->setSynopsis('Pull __repository__, named by callsign.')
->setArguments(
array(
array(
'name' => 'verbose',
'help' => 'Show additional debugging information.',
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
'Specify one or more repositories to pull, by callsign.');
}
$console = PhutilConsole::getConsole();
foreach ($repos as $repo) {
$console->writeOut("Pulling '%s'...\n", $repo->getCallsign());
id(new PhabricatorRepositoryPullEngine())
->setRepository($repo)
->setVerbose($args->getArg('verbose'))
->pullRepository();
}
$console->writeOut("Done.\n");
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
index 3d3b2040c8..77eb9608f2 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
@@ -1,49 +1,49 @@
<?php
final class PhabricatorRepositoryManagementRefsWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('refs')
->setExamples('**refs** [__options__] __repository__ ...')
->setSynopsis('Update refs in __repository__, named by callsign.')
->setArguments(
array(
array(
'name' => 'verbose',
'help' => 'Show additional debugging information.',
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
pht(
'Specify one or more repositories to update refs for, '.
'by callsign.'));
}
$console = PhutilConsole::getConsole();
foreach ($repos as $repo) {
$console->writeOut("Updating refs in '%s'...\n", $repo->getCallsign());
$engine = id(new PhabricatorRepositoryRefEngine())
->setRepository($repo)
->setVerbose($args->getArg('verbose'))
->updateRefs();
}
$console->writeOut("Done.\n");
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php
index c87945703b..9f3aa2715c 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php
@@ -1,309 +1,309 @@
<?php
final class PhabricatorRepositoryManagementReparseWorkflow
extends PhabricatorRepositoryManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('reparse')
->setExamples('**reparse** [options] __repository__')
->setSynopsis(pht(
'**reparse** __what__ __which_parts__ [--trace] [--force]'."\n\n".
'Rerun the Diffusion parser on specific commits and repositories. '.
'Mostly useful for debugging changes to Diffusion.'."\n\n".
'e.g. enqueue reparse owners in the TEST repo for all commits:'."\n".
'repository reparse --all TEST --owners'."\n\n".
'e.g. do same but exclude before yesterday (local time):'."\n".
'repository reparse --all TEST --owners --min-date yesterday'."\n".
'repository reparse --all TEST --owners --min-date "today -1 day".'.
"\n\n".
'e.g. do same but exclude before 03/31/2013 (local time):'."\n".
'repository reparse --all TEST --owners --min-date "03/31/2013"'))
->setArguments(
array(
array(
'name' => 'revision',
'wildcard' => true,
),
array(
'name' => 'all',
'param' => 'callsign or phid',
'help' => pht(
'Reparse all commits in the specified repository. This mode '.
'queues parsers into the task queue; you must run taskmasters '.
'to actually do the parses. Use with __--force-local__ to run '.
'the tasks locally instead of with taskmasters.'),
),
array(
'name' => 'min-date',
'param' => 'date',
'help' => pht(
'Must be used with __--all__, this will exclude commits which '.
'are earlier than __date__.'."\n".
"Valid examples:\n".
" 'today', 'today 2pm', '-1 hour', '-2 hours', '-24 hours',\n".
" 'yesterday', 'today -1 day', 'yesterday 2pm', '2pm -1 day',\n".
" 'last Monday', 'last Monday 14:00', 'last Monday 2pm',\n".
" '31 March 2013', '31 Mar', '03/31', '03/31/2013',\n".
'See __http://www.php.net/manual/en/datetime.formats.php__ for '.
'more.'),
),
array(
'name' => 'message',
'help' => pht('Reparse commit messages.'),
),
array(
'name' => 'change',
'help' => pht('Reparse changes.'),
),
array(
'name' => 'herald',
'help' => pht(
'Reevaluate Herald rules (may send huge amounts of email!)'),
),
array(
'name' => 'owners',
'help' => pht(
'Reevaluate related commits for owners packages (may delete '.
'existing relationship entries between your package and some '.
'old commits!)'),
),
array(
'name' => 'force',
'short' => 'f',
'help' => pht('Act noninteractively, without prompting.'),
),
array(
'name' => 'force-local',
'help' => pht(
'Only used with __--all__, use this to run the tasks locally '.
'instead of deferring them to taskmaster daemons.'),
),
array(
'name' => 'force-autoclose',
'help' => pht(
'Only used with __--message__, use this to make sure any '.
'pertinent diffs are closed regardless of configuration.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$all_from_repo = $args->getArg('all');
$reparse_message = $args->getArg('message');
$reparse_change = $args->getArg('change');
$reparse_herald = $args->getArg('herald');
$reparse_owners = $args->getArg('owners');
$reparse_what = $args->getArg('revision');
$force = $args->getArg('force');
$force_local = $args->getArg('force-local');
$min_date = $args->getArg('min-date');
if (!$all_from_repo && !$reparse_what) {
throw new PhutilArgumentUsageException(
pht('Specify a commit or repository to reparse.'));
}
if ($all_from_repo && $reparse_what) {
$commits = implode(', ', $reparse_what);
throw new PhutilArgumentUsageException(
pht(
"Specify a commit or repository to reparse, not both:\n".
"All from repo: %s\n".
"Commit(s) to reparse: %s",
$all_from_repo,
$commits));
}
if (!$reparse_message && !$reparse_change && !$reparse_herald &&
!$reparse_owners) {
throw new PhutilArgumentUsageException(
pht('Specify what information to reparse with --message, --change, '.
'--herald, and/or --owners'));
}
$min_timestamp = false;
if ($min_date) {
$min_timestamp = strtotime($min_date);
if (!$all_from_repo) {
throw new PhutilArgumentUsageException(
pht(
"You must use --all if you specify --min-date\n".
"e.g.\n".
" repository reparse --all TEST --owners --min-date yesterday"));
}
// previous to PHP 5.1.0 you would compare with -1, instead of false
if (false === $min_timestamp) {
throw new PhutilArgumentUsageException(
pht(
"Supplied --min-date is not valid. See help for valid examples.\n".
"Supplied value: '%s'\n",
$min_date));
}
}
if ($reparse_owners && !$force) {
$console->writeOut("%s\n", pht(
'You are about to recreate the relationship entries between the '.
'commits and the packages they touch. This might delete some existing '.
'relationship entries for some old commits.'));
if (!phutil_console_confirm('Are you ready to continue?')) {
throw new PhutilArgumentUsageException(pht('Cancelled.'));
}
}
$commits = array();
if ($all_from_repo) {
$repository = id(new PhabricatorRepository())->loadOneWhere(
'callsign = %s OR phid = %s',
$all_from_repo,
$all_from_repo);
if (!$repository) {
throw new PhutilArgumentUsageException(
pht('Unknown repository %s!', $all_from_repo));
}
$constraint = '';
if ($min_timestamp) {
$console->writeOut("%s\n", pht(
'Excluding entries before UNIX timestamp: %s',
$min_timestamp));
$table = new PhabricatorRepositoryCommit();
$conn_r = $table->establishConnection('r');
$constraint = qsprintf(
$conn_r,
'AND epoch >= %d',
$min_timestamp);
}
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'repositoryID = %d %Q',
$repository->getID(),
$constraint);
$callsign = $repository->getCallsign();
if (!$commits) {
throw new PhutilArgumentUsageException(pht(
"No commits have been discovered in %s repository!\n",
$callsign));
}
} else {
$commits = array();
foreach ($reparse_what as $identifier) {
$matches = null;
if (!preg_match('/r([A-Z]+)([a-z0-9]+)/', $identifier, $matches)) {
throw new PhutilArgumentUsageException(pht(
"Can't parse commit identifier: %s",
$identifier));
}
$callsign = $matches[1];
$commit_identifier = $matches[2];
$repository = id(new PhabricatorRepository())->loadOneWhere(
'callsign = %s',
$callsign);
if (!$repository) {
throw new PhutilArgumentUsageException(pht(
"No repository with callsign '%s'!",
$callsign));
}
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
'repositoryID = %d AND commitIdentifier = %s',
$repository->getID(),
$commit_identifier);
if (!$commit) {
throw new PhutilArgumentUsageException(pht(
"No matching commit '%s' in repository '%s'. ".
"(For git and mercurial repositories, you must specify the entire ".
"commit hash.)",
$commit_identifier,
$callsign));
}
$commits[] = $commit;
}
}
if ($all_from_repo && !$force_local) {
$console->writeOut("%s\n", pht(
'**NOTE**: This script will queue tasks to reparse the data. Once the '.
'tasks have been queued, you need to run Taskmaster daemons to '.
'execute them.'."\n\n".
"QUEUEING TASKS (%d Commits):",
number_format(count($commits))));
}
$progress = new PhutilConsoleProgressBar();
$progress->setTotal(count($commits));
$tasks = array();
foreach ($commits as $commit) {
$classes = array();
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
if ($reparse_message) {
$classes[] = 'PhabricatorRepositoryGitCommitMessageParserWorker';
}
if ($reparse_change) {
$classes[] = 'PhabricatorRepositoryGitCommitChangeParserWorker';
}
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
if ($reparse_message) {
$classes[] =
'PhabricatorRepositoryMercurialCommitMessageParserWorker';
}
if ($reparse_change) {
$classes[] = 'PhabricatorRepositoryMercurialCommitChangeParserWorker';
}
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
if ($reparse_message) {
$classes[] = 'PhabricatorRepositorySvnCommitMessageParserWorker';
}
if ($reparse_change) {
$classes[] = 'PhabricatorRepositorySvnCommitChangeParserWorker';
}
break;
}
if ($reparse_herald) {
$classes[] = 'PhabricatorRepositoryCommitHeraldWorker';
}
if ($reparse_owners) {
$classes[] = 'PhabricatorRepositoryCommitOwnersWorker';
}
$spec = array(
'commitID' => $commit->getID(),
'only' => true,
'forceAutoclose' => $args->getArg('force-autoclose'),
);
if ($all_from_repo && !$force_local) {
foreach ($classes as $class) {
PhabricatorWorker::scheduleTask(
$class,
$spec,
array(
'priority' => PhabricatorWorker::PRIORITY_IMPORT,
));
}
} else {
foreach ($classes as $class) {
$worker = newv($class, array($spec));
$worker->executeTask();
}
}
$progress->update(1);
}
$progress->done();
return 0;
}
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
index 163a826a31..587263a1f4 100644
--- a/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php
@@ -1,184 +1,184 @@
<?php
final class PhabricatorRepositoryManagementUpdateWorkflow
extends PhabricatorRepositoryManagementWorkflow {
private $verbose;
public function setVerbose($verbose) {
$this->verbose = $verbose;
return $this;
}
public function getVerbose() {
return $this->verbose;
}
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('update')
->setExamples('**update** [options] __repository__')
->setSynopsis(
pht(
'Update __repository__, named by callsign. '.
'This performs the __pull__, __discover__, __ref__ and __mirror__ '.
'operations and is primarily an internal workflow.'))
->setArguments(
array(
array(
'name' => 'verbose',
'help' => 'Show additional debugging information.',
),
array(
'name' => 'no-discovery',
'help' => 'Do not perform discovery.',
),
array(
'name' => 'repos',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$this->setVerbose($args->getArg('verbose'));
$console = PhutilConsole::getConsole();
$repos = $this->loadRepositories($args, 'repos');
if (count($repos) !== 1) {
throw new PhutilArgumentUsageException(
pht('Specify exactly one repository to update, by callsign.'));
}
$repository = head($repos);
$callsign = $repository->getCallsign();
$no_discovery = $args->getArg('no-discovery');
id(new PhabricatorRepositoryPullEngine())
->setRepository($repository)
->setVerbose($this->getVerbose())
->pullRepository();
if ($no_discovery) {
return;
}
// TODO: It would be nice to discover only if we pulled something, but this
// isn't totally trivial. It's slightly more complicated with hosted
// repositories, too.
$lock_name = get_class($this).':'.$callsign;
$lock = PhabricatorGlobalLock::newLock($lock_name);
$lock->lock();
try {
$repository->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
null);
$this->discoverRepository($repository);
$this->checkIfRepositoryIsFullyImported($repository);
$this->updateRepositoryRefs($repository);
$this->mirrorRepository($repository);
$repository->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_FETCH,
PhabricatorRepositoryStatusMessage::CODE_OKAY);
} catch (Exception $ex) {
$repository->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_FETCH,
PhabricatorRepositoryStatusMessage::CODE_ERROR,
array(
'message' => pht(
'Error updating working copy: %s', $ex->getMessage()),
));
$lock->unlock();
throw $ex;
}
$lock->unlock();
$console->writeOut(
pht(
'Updated repository **%s**.',
$repository->getMonogram())."\n");
return 0;
}
private function discoverRepository(PhabricatorRepository $repository) {
$refs = id(new PhabricatorRepositoryDiscoveryEngine())
->setRepository($repository)
->setVerbose($this->getVerbose())
->discoverCommits();
return (bool)count($refs);
}
private function mirrorRepository(PhabricatorRepository $repository) {
try {
id(new PhabricatorRepositoryMirrorEngine())
->setRepository($repository)
->pushToMirrors();
} catch (Exception $ex) {
// TODO: We should report these into the UI properly, but for now just
// complain. These errors are much less severe than pull errors.
$proxy = new PhutilProxyException(
pht(
'Error while pushing "%s" repository to mirrors.',
$repository->getCallsign()),
$ex);
phlog($proxy);
}
}
private function updateRepositoryRefs(PhabricatorRepository $repository) {
id(new PhabricatorRepositoryRefEngine())
->setRepository($repository)
->updateRefs();
}
private function checkIfRepositoryIsFullyImported(
PhabricatorRepository $repository) {
// Check if the repository has the "Importing" flag set. We want to clear
// the flag if we can.
$importing = $repository->getDetail('importing');
if (!$importing) {
// This repository isn't marked as "Importing", so we're done.
return;
}
// Look for any commit which hasn't imported.
$unparsed_commit = queryfx_one(
$repository->establishConnection('r'),
'SELECT * FROM %T WHERE repositoryID = %d AND (importStatus & %d) != %d
LIMIT 1',
id(new PhabricatorRepositoryCommit())->getTableName(),
$repository->getID(),
PhabricatorRepositoryCommit::IMPORTED_ALL,
PhabricatorRepositoryCommit::IMPORTED_ALL);
if ($unparsed_commit) {
// We found a commit which still needs to import, so we can't clear the
// flag.
return;
}
// Clear the "importing" flag.
$repository->openTransaction();
$repository->beginReadLocking();
$repository = $repository->reload();
$repository->setDetail('importing', false);
$repository->save();
$repository->endReadLocking();
$repository->saveTransaction();
}
}
diff --git a/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php b/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php
index 45e2bd14b4..2a4244e97f 100644
--- a/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php
+++ b/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php
@@ -1,110 +1,110 @@
<?php
final class PhabricatorSystemRemoveDestroyWorkflow
extends PhabricatorSystemRemoveWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('destroy')
->setSynopsis(pht('Permanently destroy objects.'))
->setExamples('**destroy** [__options__] __object__ ...')
->setArguments(
array(
array(
'name' => 'force',
'help' => pht('Destroy objects without prompting.'),
),
array(
'name' => 'objects',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$object_names = $args->getArg('objects');
if (!$object_names) {
throw new PhutilArgumentUsageException(
pht('Specify one or more objects to destroy.'));
}
$object_query = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withNames($object_names);
$object_query->execute();
$named_objects = $object_query->getNamedResults();
foreach ($object_names as $object_name) {
if (empty($named_objects[$object_name])) {
throw new PhutilArgumentUsageException(
pht('No such object "%s" exists!', $object_name));
}
}
foreach ($named_objects as $object_name => $object) {
if (!($object instanceof PhabricatorDestructibleInterface)) {
throw new PhutilArgumentUsageException(
pht(
'Object "%s" can not be destroyed (it does not implement %s).',
$object_name,
'PhabricatorDestructibleInterface'));
}
}
$console->writeOut(
"<bg:red>**%s**</bg>\n\n",
pht(' IMPORTANT: OBJECTS WILL BE PERMANENTLY DESTROYED! '));
$console->writeOut(
pht(
"There is no way to undo this operation or ever retrieve this data.".
"\n\n".
"These %s object(s) will be **completely destroyed forever**:".
"\n\n",
new PhutilNumber(count($named_objects))));
foreach ($named_objects as $object_name => $object) {
$console->writeOut(
" - %s (%s)\n",
$object_name,
get_class($object));
}
$force = $args->getArg('force');
if (!$force) {
$ok = $console->confirm(
pht(
'Are you absolutely certain you want to destroy these %s object(s)?',
new PhutilNumber(count($named_objects))));
if (!$ok) {
throw new PhutilArgumentUsageException(
pht('Aborted, your objects are safe.'));
}
}
$console->writeOut("%s\n", pht('Destroying objects...'));
foreach ($named_objects as $object_name => $object) {
$console->writeOut(
pht(
"Destroying %s **%s**...\n",
get_class($object),
$object_name));
id(new PhabricatorDestructionEngine())
->destroyObject($object);
}
$console->writeOut(
"%s\n",
pht(
'Permanently destroyed %s object(s).',
new PhutilNumber(count($named_objects))));
return 0;
}
}
diff --git a/src/applications/system/management/PhabricatorSystemRemoveLogWorkflow.php b/src/applications/system/management/PhabricatorSystemRemoveLogWorkflow.php
index bf3a6cb49d..73abff611e 100644
--- a/src/applications/system/management/PhabricatorSystemRemoveLogWorkflow.php
+++ b/src/applications/system/management/PhabricatorSystemRemoveLogWorkflow.php
@@ -1,31 +1,31 @@
<?php
final class PhabricatorSystemRemoveLogWorkflow
extends PhabricatorSystemRemoveWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('log')
->setSynopsis(pht('Show a log of permanently destroyed objects.'))
->setExamples('**log**')
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$table = new PhabricatorSystemDestructionLog();
foreach (new LiskMigrationIterator($table) as $row) {
$console->writeOut(
"[%s]\t%s %s\t%s\t%s\n",
phabricator_datetime($row->getEpoch(), $this->getViewer()),
($row->getRootLogID() ? ' ' : '*'),
$row->getObjectClass(),
$row->getObjectPHID(),
$row->getObjectMonogram());
}
return 0;
}
}
diff --git a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php
index 80a95be11f..7b80787204 100644
--- a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php
+++ b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php
@@ -1,52 +1,52 @@
<?php
final class PhabricatorWorkerManagementCancelWorkflow
extends PhabricatorWorkerManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('cancel')
->setExamples('**cancel** --id __id__')
->setSynopsis(
pht(
'Cancel selected tasks. The work these tasks represent will never '.
'be performed.'))
->setArguments($this->getTaskSelectionArguments());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$tasks = $this->loadTasks($args);
foreach ($tasks as $task) {
$can_cancel = !$task->isArchived();
if (!$can_cancel) {
$console->writeOut(
"**<bg:yellow> %s </bg>** %s\n",
pht('ARCHIVED'),
pht(
'%s is already archived, and can not be cancelled.',
$this->describeTask($task)));
continue;
}
// Forcibly break the lease if one exists, so we can archive the
// task.
$task->setLeaseOwner(null);
$task->setLeaseExpires(PhabricatorTime::getNow());
$task->archiveTask(
PhabricatorWorkerArchiveTask::RESULT_CANCELLED,
0);
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('CANCELLED'),
pht(
'%s was cancelled.',
$this->describeTask($task)));
}
return 0;
}
}
diff --git a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php
index 379ae2f5c4..9bf52cfb71 100644
--- a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php
+++ b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php
@@ -1,36 +1,36 @@
<?php
final class PhabricatorWorkerManagementFloodWorkflow
extends PhabricatorWorkerManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('flood')
->setExamples('**flood**')
->setSynopsis(
pht(
'Flood the queue with test tasks. This command is intended for '.
'use when developing and debugging Phabricator.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$console->writeOut(
"%s\n",
pht('Adding many test tasks to worker queue. Use ^C to exit.'));
$n = 0;
while (true) {
PhabricatorWorker::scheduleTask(
'PhabricatorTestWorker',
array());
if (($n++ % 100) === 0) {
$console->writeOut('.');
}
}
}
}
diff --git a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php
index f40d1ce3fe..1ee958be6e 100644
--- a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php
+++ b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php
@@ -1,58 +1,58 @@
<?php
final class PhabricatorWorkerManagementFreeWorkflow
extends PhabricatorWorkerManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('free')
->setExamples('**free** --id __id__')
->setSynopsis(
pht(
'Free leases on selected tasks. If the daemon holding the lease is '.
'still working on the task, this may cause the task to execute '.
'twice.'))
->setArguments($this->getTaskSelectionArguments());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$tasks = $this->loadTasks($args);
foreach ($tasks as $task) {
if ($task->isArchived()) {
$console->writeOut(
"**<bg:yellow> %s </bg>** %s\n",
pht('ARCHIVED'),
pht(
'%s is archived; archived tasks do not have leases.',
$this->describeTask($task)));
continue;
}
if ($task->getLeaseOwner() === null) {
$console->writeOut(
"**<bg:yellow> %s </bg>** %s\n",
pht('FREE'),
pht(
'%s has no active lease.',
$this->describeTask($task)));
continue;
}
$task->setLeaseOwner(null);
$task->setLeaseExpires(PhabricatorTime::getNow());
$task->save();
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('LEASE FREED'),
pht(
'%s was freed from its lease.',
$this->describeTask($task)));
}
return 0;
}
}
diff --git a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php
index 657b185250..6dbebd168d 100644
--- a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php
+++ b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php
@@ -1,57 +1,57 @@
<?php
final class PhabricatorWorkerManagementRetryWorkflow
extends PhabricatorWorkerManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('retry')
->setExamples('**retry** --id __id__')
->setSynopsis(
pht(
'Retry selected tasks which previously failed permanently or '.
'were cancelled. Only archived, unsuccessful tasks can be '.
'retried.'))
->setArguments($this->getTaskSelectionArguments());
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$tasks = $this->loadTasks($args);
foreach ($tasks as $task) {
if (!$task->isArchived()) {
$console->writeOut(
"**<bg:yellow> %s </bg>** %s\n",
pht('ACTIVE'),
pht(
'%s is already in the active task queue.',
$this->describeTask($task)));
continue;
}
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
if ($task->getResult() == $result_success) {
$console->writeOut(
"**<bg:yellow> %s </bg>** %s\n",
pht('SUCCEEDED'),
pht(
'%s has already succeeded, and can not be retried.',
$this->describeTask($task)));
continue;
}
$task->unarchiveTask();
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('QUEUED'),
pht(
'%s was queued for retry.',
$this->describeTask($task)));
}
return 0;
}
}
diff --git a/src/infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php b/src/infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php
index e2819d1f28..15b6a38831 100644
--- a/src/infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php
+++ b/src/infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php
@@ -1,102 +1,102 @@
<?php
final class PhabricatorInternationalizationManagementExtractWorkflow
extends PhabricatorInternationalizationManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('extract')
->setSynopsis(pht('Extract translatable strings.'))
->setArguments(
array(
array(
'name' => 'paths',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$paths = $args->getArg('paths');
$futures = array();
foreach ($paths as $path) {
$root = Filesystem::resolvePath($path);
$path_files = id(new FileFinder($root))
->withType('f')
->withSuffix('php')
->find();
foreach ($path_files as $file) {
$full_path = $root.DIRECTORY_SEPARATOR.$file;
$data = Filesystem::readFile($full_path);
$futures[$full_path] = xhpast_get_parser_future($data);
}
}
$console->writeOut(
"%s\n",
pht('Found %s file(s)...', new PhutilNumber(count($futures))));
$results = array();
$bar = id(new PhutilConsoleProgressBar())
->setTotal(count($futures));
$futures = id(new FutureIterator($futures))
->limit(8);
foreach ($futures as $full_path => $future) {
$bar->update(1);
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
Filesystem::readFile($full_path),
$future->resolve());
$root = $tree->getRootNode();
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
foreach ($calls as $call) {
$name = $call->getChildByIndex(0)->getConcreteString();
if ($name == 'pht') {
$params = $call->getChildByIndex(1, 'n_CALL_PARAMETER_LIST');
$string_node = $params->getChildByIndex(0);
$string_line = $string_node->getLineNumber();
try {
$string_value = $string_node->evalStatic();
$results[$string_value][] = array(
'file' => Filesystem::readablePath($full_path),
'line' => $string_line,
);
} catch (Exception $ex) {
// TODO: Deal with this junks.
}
}
}
$tree->dispose();
}
$bar->done();
ksort($results);
$out = array();
$out[] = '<?php';
$out[] = '// @nolint';
$out[] = 'return array(';
foreach ($results as $string => $locations) {
foreach ($locations as $location) {
$out[] = ' // '.$location['file'].':'.$location['line'];
}
$out[] = " '".addcslashes($string, "\0..\37\\'\177..\377")."' => null,";
$out[] = null;
}
$out[] = ');';
$out[] = null;
echo implode("\n", $out);
return 0;
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
index f423da8b2e..8800bfb0e1 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
@@ -1,64 +1,64 @@
<?php
final class PhabricatorStorageManagementAdjustWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('adjust')
->setExamples('**adjust** [__options__]')
->setSynopsis(
pht(
'Make schemata adjustments to correct issues with characters sets, '.
'collations, and keys.'))
->setArguments(
array(
array(
'name' => 'unsafe',
'help' => pht(
'Permit adjustments which truncate data. This option may '.
'destroy some data, but the lost data is usually not '.
'important (most commonly, the ends of very long object '.
'titles).'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$force = $args->getArg('force');
$unsafe = $args->getArg('unsafe');
$dry_run = $args->getArg('dryrun');
$this->requireAllPatchesApplied();
return $this->adjustSchemata($force, $unsafe, $dry_run);
}
private function requireAllPatchesApplied() {
$api = $this->getAPI();
$applied = $api->getAppliedPatches();
if ($applied === null) {
throw new PhutilArgumentUsageException(
pht(
'You have not initialized the database yet. You must initialize '.
'the database before you can adjust schemata. Run `storage upgrade` '.
'to initialize the database.'));
}
$applied = array_fuse($applied);
$patches = $this->getPatches();
$patches = mpull($patches, null, 'getFullKey');
$missing = array_diff_key($patches, $applied);
if ($missing) {
throw new PhutilArgumentUsageException(
pht(
'You have not applied all available storage patches yet. You must '.
'apply all available patches before you can adjust schemata. '.
'Run `storage status` to show patch status, and `storage upgrade` '.
'to apply missing patches.'));
}
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
index dcbc68f884..085ba1c877 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
@@ -1,23 +1,23 @@
<?php
final class PhabricatorStorageManagementDatabasesWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('databases')
->setExamples('**databases** [__options__]')
->setSynopsis('List Phabricator databases.');
}
public function execute(PhutilArgumentParser $args) {
$api = $this->getAPI();
$patches = $this->getPatches();
$databases = $api->getDatabaseList($patches, $only_living = true);
echo implode("\n", $databases)."\n";
return 0;
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php
index 9aa38d2c3d..4666494e55 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php
@@ -1,81 +1,81 @@
<?php
final class PhabricatorStorageManagementDestroyWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('destroy')
->setExamples('**destroy** [__options__]')
->setSynopsis('Permanently destroy all storage and data.')
->setArguments(
array(
array(
'name' => 'unittest-fixtures',
'help' => 'Restrict **destroy** operations to databases created '.
'by PhabricatorTestCase test fixtures.',
),
));
}
public function execute(PhutilArgumentParser $args) {
$is_dry = $args->getArg('dryrun');
$is_force = $args->getArg('force');
if (!$is_dry && !$is_force) {
echo phutil_console_wrap(
'Are you completely sure you really want to permanently destroy all '.
'storage for Phabricator data? This operation can not be undone and '.
'your data will not be recoverable if you proceed.');
if (!phutil_console_confirm('Permanently destroy all data?')) {
echo "Cancelled.\n";
exit(1);
}
if (!phutil_console_confirm('Really destroy all data forever?')) {
echo "Cancelled.\n";
exit(1);
}
}
$api = $this->getAPI();
$patches = $this->getPatches();
if ($args->getArg('unittest-fixtures')) {
$conn = $api->getConn(null);
$databases = queryfx_all(
$conn,
'SELECT DISTINCT(TABLE_SCHEMA) AS db '.
'FROM INFORMATION_SCHEMA.TABLES '.
'WHERE TABLE_SCHEMA LIKE %>',
PhabricatorTestCase::NAMESPACE_PREFIX);
$databases = ipull($databases, 'db');
} else {
$databases = $api->getDatabaseList($patches);
$databases[] = $api->getDatabaseName('meta_data');
// These are legacy databases that were dropped long ago. See T2237.
$databases[] = $api->getDatabaseName('phid');
$databases[] = $api->getDatabaseName('directory');
}
foreach ($databases as $database) {
if ($is_dry) {
echo "DRYRUN: Would drop database '{$database}'.\n";
} else {
echo "Dropping database '{$database}'...\n";
queryfx(
$api->getConn(null),
'DROP DATABASE IF EXISTS %T',
$database);
}
}
if (!$is_dry) {
echo "Storage was destroyed.\n";
}
return 0;
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php
index 4e01446929..2df475c027 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php
@@ -1,70 +1,70 @@
<?php
final class PhabricatorStorageManagementDumpWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('dump')
->setExamples('**dump** [__options__]')
->setSynopsis('Dump all data in storage to stdout.');
}
public function execute(PhutilArgumentParser $args) {
$api = $this->getAPI();
$patches = $this->getPatches();
$applied = $api->getAppliedPatches();
if ($applied === null) {
$namespace = $api->getNamespace();
echo phutil_console_wrap(
phutil_console_format(
"**No Storage**: There is no database storage initialized in this ".
"storage namespace ('{$namespace}'). Use '**storage upgrade**' to ".
"initialize storage.\n"));
return 1;
}
$databases = $api->getDatabaseList($patches, $only_living = true);
list($host, $port) = $this->getBareHostAndPort($api->getHost());
$flag_password = '';
$password = $api->getPassword();
if ($password) {
if (strlen($password->openEnvelope())) {
$flag_password = csprintf('-p%P', $password);
}
}
$flag_port = $port
? csprintf('--port %d', $port)
: '';
return phutil_passthru(
'mysqldump --single-transaction --default-character-set=utf8 '.
'-u %s %C -h %s %C --databases %Ls',
$api->getUser(),
$flag_password,
$host,
$flag_port,
$databases);
}
private function getBareHostAndPort($host) {
// Split out port information, since the command-line client requires a
// separate flag for the port.
$uri = new PhutilURI('mysql://'.$host);
if ($uri->getPort()) {
$port = $uri->getPort();
$bare_hostname = $uri->getDomain();
} else {
$port = null;
$bare_hostname = $host;
}
return array($bare_hostname, $port);
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php
index 416b3893db..00492f68ac 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php
@@ -1,78 +1,78 @@
<?php
final class PhabricatorStorageManagementProbeWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('probe')
->setExamples('**probe**')
->setSynopsis('Show approximate table sizes.');
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$console->writeErr(
"%s\n",
pht('Analyzing table sizes (this may take a moment)...'));
$api = $this->getAPI();
$patches = $this->getPatches();
$databases = $api->getDatabaseList($patches, $only_living = true);
$conn_r = $api->getConn(null);
$data = array();
foreach ($databases as $database) {
queryfx($conn_r, 'USE %C', $database);
$tables = queryfx_all(
$conn_r,
'SHOW TABLE STATUS');
$tables = ipull($tables, null, 'Name');
$data[$database] = $tables;
}
$totals = array_fill_keys(array_keys($data), 0);
$overall = 0;
foreach ($data as $db => $tables) {
foreach ($tables as $table => $info) {
$table_size = $info['Data_length'] + $info['Index_length'];
$data[$db][$table]['_totalSize'] = $table_size;
$totals[$db] += $table_size;
$overall += $table_size;
}
}
$console->writeOut("%s\n", pht('APPROXIMATE TABLE SIZES'));
asort($totals);
foreach ($totals as $db => $size) {
$database_size = $this->formatSize($totals[$db], $overall);
$console->writeOut(
"**%s**\n",
sprintf('%-32.32s %18s', $db, $database_size));
$data[$db] = isort($data[$db], '_totalSize');
foreach ($data[$db] as $table => $info) {
$table_size = $this->formatSize($info['_totalSize'], $overall);
$console->writeOut(
"%s\n",
sprintf(' %-28.28s %18s', $table, $table_size));
}
}
$overall_size = $this->formatSize($overall, $overall);
$console->writeOut(
"**%s**\n",
sprintf('%-32.32s %18s', pht('TOTAL'), $overall_size));
return 0;
}
private function formatSize($n, $o) {
return sprintf(
'%8.8s MB %5.5s%%',
number_format($n / (1024 * 1024), 1),
sprintf('%3.1f', 100 * ($n / $o)));
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php
index 44fb77ba83..d4cc60c174 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php
@@ -1,151 +1,151 @@
<?php
final class PhabricatorStorageManagementQuickstartWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('quickstart')
->setExamples('**quickstart** [__options__]')
->setSynopsis(
pht(
'Generate a new quickstart database dump. This command is mostly '.
'useful when developing Phabricator.'))
->setArguments(
array(
array(
'name' => 'output',
'param' => 'file',
'help' => pht('Specify output file to write.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$output = $args->getArg('output');
if (!$output) {
throw new PhutilArgumentUsageException(
pht(
'Specify a file to write with `--output`.'));
}
$namespace = 'phabricator_quickstart_'.Filesystem::readRandomCharacters(8);
$bin = dirname(phutil_get_library_root('phabricator')).'/bin/storage';
if (!$this->getAPI()->isCharacterSetAvailable('utf8mb4')) {
throw new PhutilArgumentUsageException(
pht(
'You can only generate a new quickstart file if MySQL supports '.
'the utf8mb4 character set (available in MySQL 5.5 and newer). The '.
'configured server does not support utf8mb4.'));
}
$err = phutil_passthru(
'%s upgrade --force --no-quickstart --namespace %s',
$bin,
$namespace);
if ($err) {
return $err;
}
$err = phutil_passthru(
'%s adjust --force --namespace %s',
$bin,
$namespace);
if ($err) {
return $err;
}
$tmp = new TempFile();
$err = phutil_passthru(
'%s dump --namespace %s > %s',
$bin,
$namespace,
$tmp);
if ($err) {
return $err;
}
$err = phutil_passthru(
'%s destroy --force --namespace %s',
$bin,
$namespace);
if ($err) {
return $err;
}
$dump = Filesystem::readFile($tmp);
$dump = str_replace(
$namespace,
'{$NAMESPACE}',
$dump);
// NOTE: This is a hack. We can not use `binary` for these columns, because
// they are a part of a fulltext index.
$old = $dump;
$dump = preg_replace(
'/`corpus` longtext CHARACTER SET .* COLLATE .*,/mi',
'`corpus` longtext CHARACTER SET {$CHARSET_FULLTEXT} '.
'COLLATE {$COLLATE_FULLTEXT},',
$dump);
if ($dump == $old) {
// If we didn't make any changes, yell about it. We'll produce an invalid
// dump otherwise.
throw new PhutilArgumentUsageException(
pht('Failed to apply hack to adjust FULLTEXT search column!'));
}
$dump = str_replace(
'utf8mb4_bin',
'{$COLLATE_TEXT}',
$dump);
$dump = str_replace(
'utf8mb4_unicode_ci',
'{$COLLATE_SORT}',
$dump);
$dump = str_replace(
'utf8mb4',
'{$CHARSET}',
$dump);
// Strip out a bunch of unnecessary commands which make the dump harder
// to handle and slower to import.
// Remove character set adjustments and key disables.
$dump = preg_replace(
'(^/\*.*\*/;$)m',
'',
$dump);
// Remove comments.
$dump = preg_replace('/^--.*$/m', '', $dump);
// Remove table drops, locks, and unlocks. These are never relevant when
// performing q quickstart.
$dump = preg_replace(
'/^(DROP TABLE|LOCK TABLES|UNLOCK TABLES).*$/m',
'',
$dump);
// Collapse adjacent newlines.
$dump = preg_replace('/\n\s*\n/', "\n", $dump);
$dump = str_replace(';', ";\n", $dump);
$dump = trim($dump)."\n";
Filesystem::writeFile($output, $dump);
$console = PhutilConsole::getConsole();
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('SUCCESS'),
pht('Wrote fresh quickstart SQL.'));
return 0;
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementStatusWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementStatusWorkflow.php
index c12836382b..8ab341996a 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementStatusWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementStatusWorkflow.php
@@ -1,49 +1,49 @@
<?php
final class PhabricatorStorageManagementStatusWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('status')
->setExamples('**status** [__options__]')
->setSynopsis('Show patch application status.');
}
public function execute(PhutilArgumentParser $args) {
$api = $this->getAPI();
$patches = $this->getPatches();
$applied = $api->getAppliedPatches();
if ($applied === null) {
echo phutil_console_format(
"**Database Not Initialized**: Run **storage upgrade** to ".
"initialize.\n");
return 1;
}
$table = id(new PhutilConsoleTable())
->setShowHeader(false)
->addColumn('id', array('title' => 'ID'))
->addColumn('status', array('title' => 'Status'))
->addColumn('type', array('title' => 'Type'))
->addColumn('name', array('title' => 'Name'));
foreach ($patches as $patch) {
$table->addRow(array(
'id' => $patch->getFullKey(),
'status' => in_array($patch->getFullKey(), $applied)
? 'Applied'
: 'Not Applied',
'type' => $patch->getType(),
'name' => $patch->getName(),
));
}
$table->draw();
return 0;
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php
index f9c2ed902a..3ce0891315 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php
@@ -1,210 +1,210 @@
<?php
final class PhabricatorStorageManagementUpgradeWorkflow
extends PhabricatorStorageManagementWorkflow {
- public function didConstruct() {
+ protected function didConstruct() {
$this
->setName('upgrade')
->setExamples('**upgrade** [__options__]')
->setSynopsis('Upgrade database schemata.')
->setArguments(
array(
array(
'name' => 'apply',
'param' => 'patch',
'help' => pht(
'Apply __patch__ explicitly. This is an advanced feature for '.
'development and debugging; you should not normally use this '.
'flag. This skips adjustment.'),
),
array(
'name' => 'no-quickstart',
'help' => pht(
'Build storage patch-by-patch from scatch, even if it could '.
'be loaded from the quickstart template.'),
),
array(
'name' => 'init-only',
'help' => pht(
'Initialize storage only; do not apply patches or adjustments.'),
),
array(
'name' => 'no-adjust',
'help' => pht(
'Do not apply storage adjustments after storage upgrades.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$is_dry = $args->getArg('dryrun');
$is_force = $args->getArg('force');
$api = $this->getAPI();
$patches = $this->getPatches();
if (!$is_dry && !$is_force) {
echo phutil_console_wrap(
'Before running storage upgrades, you should take down the '.
'Phabricator web interface and stop any running Phabricator '.
'daemons (you can disable this warning with --force).');
if (!phutil_console_confirm('Are you ready to continue?')) {
echo "Cancelled.\n";
return 1;
}
}
$apply_only = $args->getArg('apply');
if ($apply_only) {
if (empty($patches[$apply_only])) {
throw new PhutilArgumentUsageException(
"--apply argument '{$apply_only}' is not a valid patch. Use ".
"'storage status' to show patch status.");
}
}
$no_quickstart = $args->getArg('no-quickstart');
$init_only = $args->getArg('init-only');
$no_adjust = $args->getArg('no-adjust');
$applied = $api->getAppliedPatches();
if ($applied === null) {
if ($is_dry) {
echo "DRYRUN: Patch metadata storage doesn't exist yet, it would ".
"be created.\n";
return 0;
}
if ($apply_only) {
throw new PhutilArgumentUsageException(
'Storage has not been initialized yet, you must initialize storage '.
'before selectively applying patches.');
return 1;
}
$legacy = $api->getLegacyPatches($patches);
if ($legacy || $no_quickstart || $init_only) {
// If we have legacy patches, we can't quickstart.
$api->createDatabase('meta_data');
$api->createTable(
'meta_data',
'patch_status',
array(
'patch VARCHAR(255) NOT NULL PRIMARY KEY COLLATE utf8_general_ci',
'applied INT UNSIGNED NOT NULL',
));
foreach ($legacy as $patch) {
$api->markPatchApplied($patch);
}
} else {
echo "Loading quickstart template...\n";
$root = dirname(phutil_get_library_root('phabricator'));
$sql = $root.'/resources/sql/quickstart.sql';
$api->applyPatchSQL($sql);
}
}
if ($init_only) {
echo "Storage initialized.\n";
return 0;
}
$applied = $api->getAppliedPatches();
$applied = array_fuse($applied);
$skip_mark = false;
if ($apply_only) {
if (isset($applied[$apply_only])) {
unset($applied[$apply_only]);
$skip_mark = true;
if (!$is_force && !$is_dry) {
echo phutil_console_wrap(
"Patch '{$apply_only}' has already been applied. Are you sure ".
"you want to apply it again? This may put your storage in a state ".
"that the upgrade scripts can not automatically manage.");
if (!phutil_console_confirm('Apply patch again?')) {
echo "Cancelled.\n";
return 1;
}
}
}
}
while (true) {
$applied_something = false;
foreach ($patches as $key => $patch) {
if (isset($applied[$key])) {
unset($patches[$key]);
continue;
}
if ($apply_only && $apply_only != $key) {
unset($patches[$key]);
continue;
}
$can_apply = true;
foreach ($patch->getAfter() as $after) {
if (empty($applied[$after])) {
if ($apply_only) {
echo "Unable to apply patch '{$apply_only}' because it depends ".
"on patch '{$after}', which has not been applied.\n";
return 1;
}
$can_apply = false;
break;
}
}
if (!$can_apply) {
continue;
}
$applied_something = true;
if ($is_dry) {
echo "DRYRUN: Would apply patch '{$key}'.\n";
} else {
echo "Applying patch '{$key}'...\n";
$api->applyPatch($patch);
if (!$skip_mark) {
$api->markPatchApplied($key);
}
}
unset($patches[$key]);
$applied[$key] = true;
}
if (!$applied_something) {
if (count($patches)) {
throw new Exception(
'Some patches could not be applied: '.
implode(', ', array_keys($patches)));
} else if (!$is_dry && !$apply_only) {
echo "Storage is up to date. Use 'storage status' for details.\n";
}
break;
}
}
$console = PhutilConsole::getConsole();
if ($no_adjust || $init_only || $apply_only) {
$console->writeOut(
"%s\n",
pht('Declining to apply storage adjustments.'));
return 0;
} else {
return $this->adjustSchemata($is_force, $unsafe = false, $is_dry);
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 2, 7:23 AM (23 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
431714
Default Alt Text
(217 KB)

Event Timeline