diff --git a/resources/sql/patches/20131030.repostatusmessage.sql b/resources/sql/patches/20131030.repostatusmessage.sql new file mode 100644 index 0000000000..159f5dadb1 --- /dev/null +++ b/resources/sql/patches/20131030.repostatusmessage.sql @@ -0,0 +1,10 @@ +CREATE TABLE {$NAMESPACE}_repository.repository_statusmessage ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + repositoryID INT UNSIGNED NOT NULL, + statusType VARCHAR(32) NOT NULL COLLATE utf8_bin, + statusCode VARCHAR(32) NOT NULL COLLATE utf8_bin, + parameters LONGTEXT NOT NULL, + epoch INT UNSIGNED NOT NULL, + UNIQUE KEY (repositoryID, statusType) +) ENGINE=InnoDB, CHARSET utf8; + diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 47a9ccfdb8..98adad7aa9 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1665,6 +1665,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php', 'PhabricatorRepositorySearchEngine' => 'applications/repository/query/PhabricatorRepositorySearchEngine.php', 'PhabricatorRepositoryShortcut' => 'applications/repository/storage/PhabricatorRepositoryShortcut.php', + 'PhabricatorRepositoryStatusMessage' => 'applications/repository/storage/PhabricatorRepositoryStatusMessage.php', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositorySvnCommitMessageParserWorker.php', 'PhabricatorRepositorySymbol' => 'applications/repository/storage/PhabricatorRepositorySymbol.php', @@ -4005,6 +4006,7 @@ phutil_register_library_map(array( 'PhabricatorRepositoryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorRepositorySearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO', + 'PhabricatorRepositoryStatusMessage' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO', diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 1552d471df..b0f7d8144a 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -565,6 +565,10 @@ final class DiffusionRepositoryEditMainController $view = new PHUIStatusListView(); + $messages = id(new PhabricatorRepositoryStatusMessage()) + ->loadAllWhere('repositoryID = %d', $repository->getID()); + $messages = mpull($messages, null, 'getStatusType'); + if ($repository->isTracked()) { $view->addItem( id(new PHUIStatusItemView()) @@ -655,12 +659,53 @@ final class DiffusionRepositoryEditMainController if ($repository->usesLocalWorkingCopy()) { $local_path = $repository->getLocalPath(); - if (Filesystem::pathExists($local_path)) { - $view->addItem( - id(new PHUIStatusItemView()) - ->setIcon('accept-green') - ->setTarget(pht('Working Copy OK')) - ->setNote(phutil_tag('tt', array(), $local_path))); + $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT); + if ($message) { + switch ($message->getStatusCode()) { + case PhabricatorRepositoryStatusMessage::CODE_ERROR: + $view->addItem( + id(new PHUIStatusItemView()) + ->setIcon('warning-red') + ->setTarget(pht('Initialization Error')) + ->setNote($message->getParameter('message'))); + return $view; + case PhabricatorRepositoryStatusMessage::CODE_OKAY: + if (Filesystem::pathExists($local_path)) { + $view->addItem( + id(new PHUIStatusItemView()) + ->setIcon('accept-green') + ->setTarget(pht('Working Copy OK')) + ->setNote(phutil_tag('tt', array(), $local_path))); + } else { + $view->addItem( + id(new PHUIStatusItemView()) + ->setIcon('warning-red') + ->setTarget(pht('Working Copy Error')) + ->setNote( + pht( + 'Working copy %s has been deleted, or is not '. + 'readable by the webserver. Make this directory '. + 'readable. If it has been deleted, the daemons should '. + 'restore it automatically.', + phutil_tag('tt', array(), $local_path)))); + return $view; + } + break; + case PhabricatorRepositoryStatusMessage::CODE_WORKING: + $view->addItem( + id(new PHUIStatusItemView()) + ->setIcon('time-green') + ->setTarget(pht('Initializing Working Copy')) + ->setNote(pht('Daemons are initilizing the working copy.'))); + return $view; + default: + $view->addItem( + id(new PHUIStatusItemView()) + ->setIcon('warning-red') + ->setTarget(pht('Unknown Init Status')) + ->setNote($message->getStatusCode())); + return $view; + } } else { $view->addItem( id(new PHUIStatusItemView()) diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 1a2bd9eab3..2fa02c6832 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -27,7 +27,7 @@ final class PhabricatorRepositoryPullEngine $callsign = $repository->getCallsign(); if ($repository->isHosted()) { - $this->log( + $this->skipPull( pht( 'Repository "%s" is hosted, so Phabricator does not pull updates '. 'for it.', @@ -38,10 +38,11 @@ final class PhabricatorRepositoryPullEngine switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: // We never pull a local copy of Subversion repositories. - $this->log( - "Repository '%s' is a Subversion repository, which does not require ". - "a local working copy to be pulled.", - $callsign); + $this->skipPull( + pht( + "Repository '%s' is a Subversion repository, which does not ". + "require a local working copy to be pulled.", + $callsign)); return; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; @@ -50,44 +51,91 @@ final class PhabricatorRepositoryPullEngine $is_hg = true; break; default: - throw new Exception("Unsupported VCS '{$vcs}'!"); + $this->abortPull(pht('Unknown VCS "%s"!', $vcs)); } $callsign = $repository->getCallsign(); $local_path = $repository->getLocalPath(); if ($local_path === null) { - throw new Exception( - "No local path is configured for repository '{$callsign}'."); + $this->abortPull( + pht( + "No local path is configured for repository '%s'.", + $callsign)); } - $dirname = dirname($local_path); - if (!Filesystem::pathExists($dirname)) { - Filesystem::createDirectory($dirname, 0755, $recursive = true); + try { + $dirname = dirname($local_path); + if (!Filesystem::pathExists($dirname)) { + Filesystem::createDirectory($dirname, 0755, $recursive = true); + } + + if (!Filesystem::pathExists($local_path)) { + $this->logPull( + pht( + "Creating a new working copy for repository '%s'.", + $callsign)); + if ($is_git) { + $this->executeGitCreate(); + } else { + $this->executeMercurialCreate(); + } + } else { + $this->logPull( + pht( + "Updating the working copy for repository '%s'.", + $callsign)); + if ($is_git) { + $this->executeGitUpdate(); + } else { + $this->executeMercurialUpdate(); + } + } + } catch (Exception $ex) { + $this->abortPull( + pht('Pull of "%s" failed: %s', $callsign, $ex->getMessage()), + $ex); } - if (!Filesystem::pathExists($local_path)) { - $this->log( - "Creating a new working copy for repository '%s'.", - $callsign); - if ($is_git) { - $this->executeGitCreate(); - } else { - $this->executeMercurialCreate(); - } - } else { - $this->log( - "Updating the working copy for repository '%s'.", - $callsign); - if ($is_git) { - $this->executeGitUpdate(); - } else { - $this->executeMercurialUpdate(); - } - } + $this->donePull(); return $this; } + private function skipPull($message) { + $this->updateRepositoryInitStatus(null); + $this->log('%s', $message); + } + + private function abortPull($message, Exception $ex = null) { + $code_error = PhabricatorRepositoryStatusMessage::CODE_ERROR; + $this->updateRepositoryInitStatus($code_error, $message); + if ($ex) { + throw $ex; + } else { + throw new Exception($message); + } + } + + private function logPull($message) { + $code_working = PhabricatorRepositoryStatusMessage::CODE_WORKING; + $this->updateRepositoryInitStatus($code_working, $message); + $this->log('%s', $message); + } + + private function donePull() { + $code_okay = PhabricatorRepositoryStatusMessage::CODE_OKAY; + $this->updateRepositoryInitStatus($code_okay); + } + + private function updateRepositoryInitStatus($code, $message = null) { + $this->getRepository()->writeStatusMessage( + PhabricatorRepositoryStatusMessage::TYPE_INIT, + $code, + array( + 'message' => $message + )); + } + /* -( Pulling Git Working Copies )----------------------------------------- */ diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 6bd26aa9d1..dcb5a80320 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -815,6 +815,43 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } } + public function writeStatusMessage( + $status_type, + $status_code, + array $parameters = array()) { + + $table = new PhabricatorRepositoryStatusMessage(); + $conn_w = $table->establishConnection('w'); + $table_name = $table->getTableName(); + + if ($status_code === null) { + queryfx( + $conn_w, + 'DELETE FROM %T WHERE repositoryID = %d AND statusType = %s', + $table_name, + $this->getID(), + $status_type); + } else { + queryfx( + $conn_w, + 'INSERT INTO %T + (repositoryID, statusType, statusCode, parameters, epoch) + VALUES (%d, %s, %s, %s, %d) + ON DUPLICATE KEY UPDATE + statusCode = VALUES(statusCode), + parameters = VALUES(parameters), + epoch = VALUES(epoch)', + $table_name, + $this->getID(), + $status_type, + $status_code, + json_encode($parameters), + time()); + } + + return $this; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php b/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php new file mode 100644 index 0000000000..f35eec1cf2 --- /dev/null +++ b/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php @@ -0,0 +1,33 @@ + false, + self::CONFIG_SERIALIZATION => array( + 'parameters' => self::SERIALIZATION_JSON, + ), + ) + parent::getConfiguration(); + } + + public function getParameter($key, $default = null) { + return idx($this->parameters, $key, $default); + } + +} diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 5b21dcfab8..d738b20172 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1712,6 +1712,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'type' => 'sql', 'name' => $this->getPatchPath('20131026.commitstatus.sql'), ), + '20131030.repostatusmessage.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20131030.repostatusmessage.sql'), + ), ); } }