Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F85259
D7499.id16923.diff
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
52 KB
Referenced Files
None
Subscribers
None
D7499.id16923.diff
View Options
diff --git a/resources/sql/patches/20131105.buildstep.sql b/resources/sql/patches/20131105.buildstep.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/patches/20131105.buildstep.sql
@@ -0,0 +1,11 @@
+CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildstep (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ buildPlanPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ className VARCHAR(255) NOT NULL COLLATE utf8_bin,
+ details LONGTEXT CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ KEY `key_plan` (buildPlanPHID),
+ UNIQUE KEY `key_phid` (phid)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -3985,7 +3985,7 @@
),
'phui-workboard-view-css' =>
array(
- 'uri' => '/res/445c7c7e/rsrc/css/phui/phui-workboard-view.css',
+ 'uri' => '/res/44fcb197/rsrc/css/phui/phui-workboard-view.css',
'type' => 'css',
'requires' =>
array(
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -91,6 +91,7 @@
'AphrontView' => 'view/AphrontView.php',
'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php',
'AuditActionMenuEventListener' => 'applications/audit/events/AuditActionMenuEventListener.php',
+ 'BuildStepImplementation' => 'applications/harbormaster/step/BuildStepImplementation.php',
'CelerityAPI' => 'infrastructure/celerity/CelerityAPI.php',
'CelerityPhabricatorResourceController' => 'infrastructure/celerity/CelerityPhabricatorResourceController.php',
'CelerityResourceController' => 'infrastructure/celerity/CelerityResourceController.php',
@@ -369,6 +370,7 @@
'DifferentialFieldValidationException' => 'applications/differential/field/exception/DifferentialFieldValidationException.php',
'DifferentialFreeformFieldSpecification' => 'applications/differential/field/specification/DifferentialFreeformFieldSpecification.php',
'DifferentialFreeformFieldTestCase' => 'applications/differential/field/specification/__tests__/DifferentialFreeformFieldTestCase.php',
+ 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php',
'DifferentialGitSVNIDFieldSpecification' => 'applications/differential/field/specification/DifferentialGitSVNIDFieldSpecification.php',
'DifferentialHostFieldSpecification' => 'applications/differential/field/specification/DifferentialHostFieldSpecification.php',
'DifferentialHovercardEventListener' => 'applications/differential/event/DifferentialHovercardEventListener.php',
@@ -383,6 +385,9 @@
'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php',
'DifferentialInlineCommentView' => 'applications/differential/view/DifferentialInlineCommentView.php',
'DifferentialJIRAIssuesFieldSpecification' => 'applications/differential/field/specification/DifferentialJIRAIssuesFieldSpecification.php',
+ 'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php',
+ 'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php',
+ 'DifferentialLandingToHostedGit' => 'applications/differential/landing/DifferentialLandingToHostedGit.php',
'DifferentialLinesFieldSpecification' => 'applications/differential/field/specification/DifferentialLinesFieldSpecification.php',
'DifferentialLintFieldSpecification' => 'applications/differential/field/specification/DifferentialLintFieldSpecification.php',
'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php',
@@ -420,6 +425,7 @@
'DifferentialRevisionEditor' => 'applications/differential/editor/DifferentialRevisionEditor.php',
'DifferentialRevisionIDFieldParserTestCase' => 'applications/differential/field/specification/__tests__/DifferentialRevisionIDFieldParserTestCase.php',
'DifferentialRevisionIDFieldSpecification' => 'applications/differential/field/specification/DifferentialRevisionIDFieldSpecification.php',
+ 'DifferentialRevisionLandController' => 'applications/differential/controller/DifferentialRevisionLandController.php',
'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php',
'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php',
'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php',
@@ -656,7 +662,9 @@
'HarbormasterBuildStepQuery' => 'applications/harbormaster/query/HarbormasterBuildStepQuery.php',
'HarbormasterBuildTarget' => 'applications/harbormaster/storage/build/HarbormasterBuildTarget.php',
'HarbormasterBuildTargetQuery' => 'applications/harbormaster/query/HarbormasterBuildTargetQuery.php',
+ 'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php',
'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php',
+ 'HarbormasterBuildableApplyController' => 'applications/harbormaster/controller/HarbormasterBuildableApplyController.php',
'HarbormasterBuildableArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildableArtifactQuery.php',
'HarbormasterBuildableEditController' => 'applications/harbormaster/controller/HarbormasterBuildableEditController.php',
'HarbormasterBuildableListController' => 'applications/harbormaster/controller/HarbormasterBuildableListController.php',
@@ -675,11 +683,9 @@
'HarbormasterPHIDTypeBuildable' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuildable.php',
'HarbormasterPlanController' => 'applications/harbormaster/controller/HarbormasterPlanController.php',
'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php',
- 'HarbormasterPlanExecuteController' => 'applications/harbormaster/controller/HarbormasterPlanExecuteController.php',
'HarbormasterPlanListController' => 'applications/harbormaster/controller/HarbormasterPlanListController.php',
'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php',
'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php',
- 'HarbormasterRunnerWorker' => 'applications/harbormaster/worker/HarbormasterRunnerWorker.php',
'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php',
'HeraldAction' => 'applications/herald/storage/HeraldAction.php',
'HeraldAdapter' => 'applications/herald/adapter/HeraldAdapter.php',
@@ -2190,6 +2196,7 @@
'ReleephStatusFieldSpecification' => 'applications/releeph/field/specification/ReleephStatusFieldSpecification.php',
'ReleephSummaryFieldSpecification' => 'applications/releeph/field/specification/ReleephSummaryFieldSpecification.php',
'ReleephUserView' => 'applications/releeph/view/user/ReleephUserView.php',
+ 'SleepBuildStepImplementation' => 'applications/harbormaster/step/SleepBuildStepImplementation.php',
'SlowvoteEmbedView' => 'applications/slowvote/view/SlowvoteEmbedView.php',
'SlowvoteRemarkupRule' => 'applications/slowvote/remarkup/SlowvoteRemarkupRule.php',
),
@@ -2586,6 +2593,8 @@
'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
'DifferentialInlineCommentView' => 'AphrontView',
'DifferentialJIRAIssuesFieldSpecification' => 'DifferentialFieldSpecification',
+ 'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener',
+ 'DifferentialLandingToHostedGit' => 'DifferentialLandingStrategy',
'DifferentialLinesFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialLintFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialLocalCommitsView' => 'AphrontView',
@@ -2623,6 +2632,7 @@
'DifferentialRevisionEditor' => 'PhabricatorEditor',
'DifferentialRevisionIDFieldParserTestCase' => 'PhabricatorTestCase',
'DifferentialRevisionIDFieldSpecification' => 'DifferentialFieldSpecification',
+ 'DifferentialRevisionLandController' => 'DifferentialController',
'DifferentialRevisionListController' =>
array(
0 => 'DifferentialController',
@@ -2864,15 +2874,21 @@
'HarbormasterBuildPlanTransactionComment' => 'PhabricatorApplicationTransactionComment',
'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
- 'HarbormasterBuildStep' => 'HarbormasterDAO',
+ 'HarbormasterBuildStep' =>
+ array(
+ 0 => 'HarbormasterDAO',
+ 1 => 'PhabricatorPolicyInterface',
+ ),
'HarbormasterBuildStepQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildTarget' => 'HarbormasterDAO',
'HarbormasterBuildTargetQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'HarbormasterBuildWorker' => 'PhabricatorWorker',
'HarbormasterBuildable' =>
array(
0 => 'HarbormasterDAO',
1 => 'PhabricatorPolicyInterface',
),
+ 'HarbormasterBuildableApplyController' => 'HarbormasterController',
'HarbormasterBuildableArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildableEditController' => 'HarbormasterController',
'HarbormasterBuildableListController' =>
@@ -2895,15 +2911,13 @@
'HarbormasterPHIDTypeBuildable' => 'PhabricatorPHIDType',
'HarbormasterPlanController' => 'PhabricatorController',
'HarbormasterPlanEditController' => 'HarbormasterPlanController',
- 'HarbormasterPlanExecuteController' => 'HarbormasterPlanController',
'HarbormasterPlanListController' =>
array(
0 => 'HarbormasterPlanController',
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
),
'HarbormasterPlanViewController' => 'HarbormasterPlanController',
'HarbormasterRemarkupRule' => 'PhabricatorRemarkupRuleObject',
- 'HarbormasterRunnerWorker' => 'PhabricatorWorker',
'HarbormasterScratchTable' => 'HarbormasterDAO',
'HeraldAction' => 'HeraldDAO',
'HeraldApplyTranscript' => 'HeraldDAO',
@@ -4643,6 +4657,7 @@
'ReleephStatusFieldSpecification' => 'ReleephFieldSpecification',
'ReleephSummaryFieldSpecification' => 'ReleephFieldSpecification',
'ReleephUserView' => 'AphrontView',
+ 'SleepBuildStepImplementation' => 'BuildStepImplementation',
'SlowvoteEmbedView' => 'AphrontView',
'SlowvoteRemarkupRule' => 'PhabricatorRemarkupRuleObject',
),
diff --git a/src/applications/differential/DifferentialGetWorkingCopy.php b/src/applications/differential/DifferentialGetWorkingCopy.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/DifferentialGetWorkingCopy.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * Can't find a good place for this, so I'm putting it in the most notably
+ * wrong place.
+ */
+final class DifferentialGetWorkingCopy {
+
+ /**
+ * Creates and/or cleans a workspace for the requested repo.
+ *
+ * return ArcanistGitAPI
+ */
+ public static function getCleanGitWorkspace(
+ PhabricatorRepository $repo) {
+
+ $origin_path = $repo->getLocalPath();
+
+ $path = rtrim($origin_path, '/');
+ $path = $path . '__workspace';
+
+ if (!Filesystem::pathExists($path)) {
+ $repo->execxLocalCommand(
+ 'clone -- file://%s %s',
+ $origin_path,
+ $path);
+ }
+
+ $workspace = new ArcanistGitAPI($path);
+ $workspace->execxLocal('clean -f -d');
+ $workspace->execxLocal('checkout master');
+ $workspace->execxLocal('fetch');
+ $workspace->execxLocal('reset --hard origin/master');
+ $workspace->reloadWorkingCopy();
+
+ return $workspace;
+ }
+
+}
diff --git a/src/applications/differential/application/PhabricatorApplicationDifferential.php b/src/applications/differential/application/PhabricatorApplicationDifferential.php
--- a/src/applications/differential/application/PhabricatorApplicationDifferential.php
+++ b/src/applications/differential/application/PhabricatorApplicationDifferential.php
@@ -32,6 +32,7 @@
return array(
new DifferentialActionMenuEventListener(),
new DifferentialHovercardEventListener(),
+ new DifferentialLandingActionMenuEventListener(),
);
}
@@ -48,6 +49,8 @@
'changeset/' => 'DifferentialChangesetViewController',
'revision/edit/(?:(?P<id>[1-9]\d*)/)?'
=> 'DifferentialRevisionEditController',
+ 'revision/land/(?:(?P<id>[1-9]\d*))/(?P<strategy>[^/]+)/'
+ => 'DifferentialRevisionLandController',
'comment/' => array(
'preview/(?P<id>[1-9]\d*)/' => 'DifferentialCommentPreviewController',
'save/' => 'DifferentialCommentSaveController',
diff --git a/src/applications/differential/controller/DifferentialRevisionLandController.php b/src/applications/differential/controller/DifferentialRevisionLandController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/controller/DifferentialRevisionLandController.php
@@ -0,0 +1,130 @@
+<?php
+
+final class DifferentialRevisionLandController extends DifferentialController {
+
+ private $revisionID;
+ private $strategyClass;
+ private $pushStrategy;
+
+ public function willProcessRequest(array $data) {
+ $this->revisionID = $data['id'];
+ $this->strategyClass = $data['strategy'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $revision_id = $this->revisionID;
+
+ $revision = id(new DifferentialRevisionQuery())
+ ->withIDs(array($revision_id))
+ ->setViewer($viewer)
+ ->executeOne();
+ if (!$revision) {
+ return new Aphront404Response();
+ }
+
+ if (is_subclass_of($this->strategyClass, 'DifferentialLandingStrategy')) {
+ $this->pushStrategy = newv($this->strategyClass, array());
+ } else {
+ throw new Exception(
+ "Strategy type must be a valid class name and must subclass ".
+ "DifferentialLandingStrategy. ".
+ "'{$this->strategyClass}' is not a subclass of ".
+ "DifferentialLandingStrategy.");
+ }
+
+ if ($request->isDialogFormPost()) {
+ try {
+ $this->attemptLand($revision, $request);
+ $title = pht("Success!");
+ $text = pht("Revision was successfully landed.");
+ } catch (Exception $ex) {
+ $title = pht("Failed to land revision");
+ $text = 'moo';
+ if ($ex instanceof PhutilProxyException) {
+ $text = hsprintf(
+ '%s:<br><pre>%s</pre>',
+ $ex->getMessage(),
+ $ex->getPreviousException()->getMessage());
+ } else {
+ $text = hsprintf('<pre>%s</pre>', $ex->getMessage());
+ }
+ $text = id(new AphrontErrorView())
+ ->appendChild($text);
+ }
+
+ $dialog = id(new AphrontDialogView())
+ ->setUser($viewer)
+ ->setTitle($title)
+ ->appendChild(phutil_tag('p', array(), $text))
+ ->setSubmitURI('/D'.$revision_id)
+ ->addSubmitButton(pht('Done'));
+
+ return id(new AphrontDialogResponse())->setDialog($dialog);
+ }
+
+ $prompt = hsprintf('%s<br><br>%s',
+ pht(
+ 'This will squash and rebase revision %s, and push it to '.
+ 'origin/master.',
+ $revision_id),
+ pht('It is an experimental feature and may not work.'));
+
+ $dialog = id(new AphrontDialogView())
+ ->setUser($viewer)
+ ->setTitle(pht("Land Revision %s?", $revision_id))
+ ->appendChild($prompt)
+ ->setSubmitURI($request->getRequestURI())
+ ->addSubmitButton(pht('Land it!'))
+ ->addCancelButton('/D'.$revision_id);
+
+ return id(new AphrontDialogResponse())->setDialog($dialog);
+ }
+
+ private function attemptLand($revision, $request) {
+ $status = $revision->getStatus();
+ if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
+ throw new Exception("Only Accepted revisions can be landed.");
+ }
+
+ $repository = $revision->getRepository();
+
+ if ($repository === null) {
+ throw new Exception("revision is not attached to a repository.");
+ }
+
+ $can_push = PhabricatorPolicyFilter::hasCapability(
+ $request->getUser(),
+ $repository,
+ DiffusionCapabilityPush::CAPABILITY);
+
+ if (!$can_push) {
+ throw new Exception(
+ pht('You do not have permission to push to this repository.'));
+ }
+
+ $lock = $this->lockRepository($repository);
+
+ try {
+ $this->pushStrategy->processLandRequest(
+ $request,
+ $revision,
+ $repository);
+ } catch (Exception $e) {
+ $lock->unlock();
+ throw $e;
+ }
+
+ $lock->unlock();
+ }
+
+ private function lockRepository($repository) {
+ $lock_name = __CLASS__.':'.($repository->getCallsign());
+ $lock = PhabricatorGlobalLock::newLock($lock_name);
+ $lock->lock();
+ return $lock;
+ }
+}
+
diff --git a/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php b/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php
@@ -0,0 +1,54 @@
+<?php
+
+final class DifferentialLandingActionMenuEventListener
+ extends PhabricatorEventListener {
+
+ public function register() {
+ $this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
+ }
+
+ public function handleEvent(PhutilEvent $event) {
+ switch ($event->getType()) {
+ case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
+ $this->handleActionsEvent($event);
+ break;
+ }
+ }
+
+ private function handleActionsEvent(PhutilEvent $event) {
+ $object = $event->getValue('object');
+
+ $actions = null;
+ if ($object instanceof DifferentialRevision) {
+ $actions = $this->renderRevisionAction($event);
+ }
+
+ $this->addActionMenuItems($event, $actions);
+ }
+
+ private function renderRevisionAction(PhutilEvent $event) {
+ if (!$this->canUseApplication($event->getUser())) {
+ return null;
+ }
+
+ $revision = $event->getValue('object');
+
+ $repository = $revision->getRepository();
+ if ($repository === null) {
+ return null;
+ }
+
+ $strategies = id(new PhutilSymbolLoader())
+ ->setAncestorClass('DifferentialLandingStrategy')
+ ->loadObjects();
+ foreach ($strategies as $strategy) {
+ $actions = $strategy->createMenuItems(
+ $event->getUser(),
+ $revision,
+ $repository);
+ $this->addActionMenuItems($event, $actions);
+ }
+ }
+
+}
+
diff --git a/src/applications/differential/landing/DifferentialLandingStrategy.php b/src/applications/differential/landing/DifferentialLandingStrategy.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/landing/DifferentialLandingStrategy.php
@@ -0,0 +1,43 @@
+<?php
+
+abstract class DifferentialLandingStrategy {
+
+ public abstract function processLandRequest(
+ AphrontRequest $request,
+ DifferentialRevision $revision,
+ PhabricatorRepository $repository);
+
+ /**
+ * returns PhabricatorActionView or an array of PhabricatorActionView or null.
+ */
+ abstract function createMenuItems(
+ PhabricatorUser $viewer,
+ DifferentialRevision $revision,
+ PhabricatorRepository $repository);
+
+ /**
+ * returns PhabricatorActionView which can be attached to the revision view.
+ */
+ protected function createActionView($revision, $name, $disabled = false) {
+ $strategy = get_class($this);
+ $revision_id = $revision->getId();
+ return id(new PhabricatorActionView())
+ ->setRenderAsForm(true)
+ ->setName($name)
+ ->setHref("/differential/revision/land/{$revision_id}/{$strategy}/")
+ ->setDisabled($disabled);
+ }
+
+ /**
+ * might break if repository is not Git.
+ */
+ protected function getGitWorkspace(PhabricatorRepository $repository) {
+ try {
+ return DifferentialGetWorkingCopy::getCleanGitWorkspace($repository);
+ } catch (Exception $e) {
+ throw new PhutilProxyException (
+ 'Failed to allocate a workspace',
+ $e);
+ }
+ }
+}
diff --git a/src/applications/differential/landing/DifferentialLandingToHostedGit.php b/src/applications/differential/landing/DifferentialLandingToHostedGit.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/landing/DifferentialLandingToHostedGit.php
@@ -0,0 +1,136 @@
+<?php
+
+final class DifferentialLandingToHostedGit
+ extends DifferentialLandingStrategy {
+
+ public function processLandRequest(
+ AphrontRequest $request,
+ DifferentialRevision $revision,
+ PhabricatorRepository $repository) {
+
+ $viewer = $request->getUser();
+
+ $workspace = $this->getGitWorkspace($repository);
+
+ try {
+ $this->commitRevisionToWorkspace(
+ $revision,
+ $workspace,
+ $viewer);
+ } catch (Exception $e) {
+ throw new PhutilProxyException(
+ 'Failed to commit patch',
+ $e);
+ }
+
+ try {
+ $this->pushWorkspaceRepository(
+ $repository,
+ $workspace,
+ $viewer);
+ } catch (Exception $e) {
+ throw new PhutilProxyException(
+ 'Failed to push changes upstream',
+ $e);
+ }
+ }
+
+ public function commitRevisionToWorkspace(
+ DifferentialRevision $revision,
+ ArcanistRepositoryAPI $workspace,
+ PhabricatorUser $user) {
+
+ $diff_id = $revision->loadActiveDiff()->getID();
+
+ $call = new ConduitCall(
+ 'differential.getrawdiff',
+ array(
+ 'diffID' => $diff_id,
+ ));
+
+ $call->setUser($user);
+ $raw_diff = $call->execute();
+
+ $missing_binary =
+ "\nindex "
+ . "0000000000000000000000000000000000000000.."
+ . "0000000000000000000000000000000000000000\n";
+ if (strpos($raw_diff, $missing_binary) !== false) {
+ throw new Exception("Patch is missing content for a binary file");
+ }
+
+ $future = $workspace->execFutureLocal('apply --index -');
+ $future->write($raw_diff);
+ $future->resolvex();
+
+ $workspace->reloadWorkingCopy();
+
+ $call = new ConduitCall(
+ 'differential.getcommitmessage',
+ array(
+ 'revision_id' => $revision->getID(),
+ ));
+
+ $call->setUser($user);
+ $message = $call->execute();
+
+ $author = id(new PhabricatorUser())->loadOneWhere(
+ 'phid = %s',
+ $revision->getAuthorPHID());
+
+ $author_string = sprintf(
+ '%s <%s>',
+ $author->getRealName(),
+ $author->loadPrimaryEmailAddress());
+ $author_date = $revision->getDateCreated();
+
+ $workspace->execxLocal(
+ '-c user.name=%s -c user.email=%s ' .
+ 'commit --date=%s --author=%s '.
+ '--message=%s',
+ // -c will set the 'committer'
+ $user->getRealName(),
+ $user->loadPrimaryEmailAddress(),
+ $author_date,
+ $author_string,
+ $message);
+ }
+
+
+ public function pushWorkspaceRepository(
+ PhabricatorRepository $repository,
+ ArcanistRepositoryAPI $workspace,
+ PhabricatorUser $user) {
+
+ $workspace->execxLocal("push origin HEAD:master");
+ }
+
+ public function createMenuItems(
+ PhabricatorUser $viewer,
+ DifferentialRevision $revision,
+ PhabricatorRepository $repository) {
+
+ $vcs = $repository->getVersionControlSystem();
+ if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
+ return;
+ }
+
+ if (!$repository->isHosted()) {
+ return;
+ }
+
+ if (!$repository->isWorkingCopyBare()) {
+ return;
+ }
+
+ $can_push = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $repository,
+ DiffusionCapabilityPush::CAPABILITY);
+
+ return $this->createActionView(
+ $revision,
+ pht('Land to Hosted Repository'),
+ !$can_push);
+ }
+}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
@@ -726,27 +726,27 @@
->setNote($daemon_instructions));
}
- $local_parent = dirname($repository->getLocalPath());
- if (Filesystem::pathExists($local_parent)) {
- $view->addItem(
- id(new PHUIStatusItemView())
- ->setIcon('accept-green')
- ->setTarget(pht('Storage Directory OK'))
- ->setNote(phutil_tag('tt', array(), $local_parent)));
- } else {
- $view->addItem(
- id(new PHUIStatusItemView())
- ->setIcon('warning-red')
- ->setTarget(pht('No Storage Directory'))
- ->setNote(
- pht(
- 'Storage directory %s does not exist, or is not readable by '.
- 'the webserver. Create this directory or make it readable.',
- phutil_tag('tt', array(), $local_parent))));
- return $view;
- }
-
if ($repository->usesLocalWorkingCopy()) {
+ $local_parent = dirname($repository->getLocalPath());
+ if (Filesystem::pathExists($local_parent)) {
+ $view->addItem(
+ id(new PHUIStatusItemView())
+ ->setIcon('accept-green')
+ ->setTarget(pht('Storage Directory OK'))
+ ->setNote(phutil_tag('tt', array(), $local_parent)));
+ } else {
+ $view->addItem(
+ id(new PHUIStatusItemView())
+ ->setIcon('warning-red')
+ ->setTarget(pht('No Storage Directory'))
+ ->setNote(
+ pht(
+ 'Storage directory %s does not exist, or is not readable by '.
+ 'the webserver. Create this directory or make it readable.',
+ phutil_tag('tt', array(), $local_parent))));
+ return $view;
+ }
+
$local_path = $repository->getLocalPath();
$message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT);
if ($message) {
diff --git a/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php b/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php
--- a/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php
+++ b/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php
@@ -44,13 +44,13 @@
=> 'HarbormasterBuildableListController',
'buildable/' => array(
'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterBuildableEditController',
+ 'apply/(?:(?P<id>\d+)/)?' => 'HarbormasterBuildableApplyController',
),
'plan/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'HarbormasterPlanListController',
'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanEditController',
'(?P<id>\d+)/' => 'HarbormasterPlanViewController',
- 'execute/(?P<id>\d+)/' => 'HarbormasterPlanExecuteController',
),
),
);
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php b/src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php
@@ -0,0 +1,81 @@
+<?php
+
+final class HarbormasterBuildableApplyController
+ extends HarbormasterController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = $data['id'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $id = $this->id;
+
+ $buildable = id(new HarbormasterBuildableQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if ($buildable === null) {
+ throw new Exception("Buildable not found!");
+ }
+
+ $buildable_uri = '/B'.$buildable->getID();
+
+ if ($request->isDialogFormPost()) {
+ $plan = id(new HarbormasterBuildPlanQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($request->getInt('build-plan')))
+ ->executeOne();
+
+ $build = HarbormasterBuild::initializeNewBuild($viewer);
+ $build->setBuildablePHID($buildable->getPHID());
+ $build->setBuildPlanPHID($plan->getPHID());
+ $build->setBuildStatus(HarbormasterBuild::STATUS_PENDING);
+ $build->save();
+
+ PhabricatorWorker::scheduleTask(
+ 'HarbormasterBuildWorker',
+ array(
+ 'buildID' => $build->getID()
+ ));
+
+ return id(new AphrontRedirectResponse())->setURI($buildable_uri);
+ }
+
+ $plans = id(new HarbormasterBuildPlanQuery())
+ ->setViewer($viewer)
+ ->execute();
+
+ $options = array();
+ foreach ($plans as $plan) {
+ $options[$plan->getID()] = $plan->getName();
+ }
+
+ // FIXME: I'd really like to use the dialog that "Edit Differential
+ // Revisions" uses, but that code is quite hard-coded for the particular
+ // uses, so for now we just give a single dropdown.
+
+ $dialog = new AphrontDialogView();
+ $dialog->setTitle(pht('Apply which plan?'))
+ ->setUser($viewer)
+ ->addSubmitButton(pht('Apply'))
+ ->addCancelButton($buildable_uri);
+ $dialog->appendChild(
+ phutil_tag(
+ 'p',
+ array(),
+ pht(
+ 'Select what build plan you want to apply to this buildable:')));
+ $dialog->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setUser($viewer)
+ ->setName('build-plan')
+ ->setOptions($options));
+ return id(new AphrontDialogResponse())->setDialog($dialog);
+ }
+
+}
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php
--- a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php
@@ -36,7 +36,7 @@
$id = $buildable->getID();
$item = id(new PHUIObjectItemView())
- ->setHeader(pht('Build %d', $buildable->getID()));
+ ->setHeader(pht('Buildable %d', $buildable->getID()));
if ($id) {
$item->setHref("/B{$id}");
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
--- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
@@ -37,6 +37,32 @@
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Build %d', $build->getID()))
->setHeader($build->getName());
+ switch ($build->getBuildStatus()) {
+ case HarbormasterBuild::STATUS_INACTIVE:
+ $item->setBarColor('grey');
+ $item->addAttribute(pht('Inactive'));
+ break;
+ case HarbormasterBuild::STATUS_PENDING:
+ $item->setBarColor('blue');
+ $item->addAttribute(pht('Pending'));
+ break;
+ case HarbormasterBuild::STATUS_WAITING:
+ $item->setBarColor('blue');
+ $item->addAttribute(pht('Waiting on Resource'));
+ break;
+ case HarbormasterBuild::STATUS_BUILDING:
+ $item->setBarColor('yellow');
+ $item->addAttribute(pht('Building'));
+ break;
+ case HarbormasterBuild::STATUS_PASSED:
+ $item->setBarColor('green');
+ $item->addAttribute(pht('Passed'));
+ break;
+ case HarbormasterBuild::STATUS_FAILED:
+ $item->setBarColor('red');
+ $item->addAttribute(pht('Failed'));
+ break;
+ }
$build_list->addItem($item);
}
@@ -80,6 +106,15 @@
->setObject($buildable)
->setObjectURI("/B{$id}");
+ $apply_uri = $this->getApplicationURI('/buildable/apply/'.$id.'/');
+
+ $list->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Apply Build Plan'))
+ ->setIcon('edit')
+ ->setHref($apply_uri)
+ ->setWorkflow(true));
+
return $list;
}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanExecuteController.php b/src/applications/harbormaster/controller/HarbormasterPlanExecuteController.php
deleted file mode 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanExecuteController.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-final class HarbormasterPlanExecuteController
- extends HarbormasterPlanController {
-
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
-
- $this->requireApplicationCapability(
- HarbormasterCapabilityManagePlans::CAPABILITY);
-
- $id = $this->id;
-
- $plan = id(new HarbormasterBuildPlanQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->executeOne();
- if (!$plan) {
- return new Aphront404Response();
- }
-
- $cancel_uri = $this->getApplicationURI("plan/{$id}/");
-
- $v_buildable = null;
- $e_buildable = null;
-
- $errors = array();
- if ($request->isFormPost()) {
- $v_buildable = $request->getStr('buildable');
-
- if ($v_buildable) {
- $buildable = id(new HarbormasterBuildableQuery())
- ->setViewer($viewer)
- ->withIDs(array(trim($v_buildable, 'B')))
- ->executeOne();
- if (!$buildable) {
- $e_buildable = pht('Invalid');
- }
- } else {
- $e_buildable = pht('Required');
- $errors[] = pht('You must provide a buildable.');
- }
-
- if (!$errors) {
- $build_plan = HarbormasterBuild::initializeNewBuild($viewer)
- ->setBuildablePHID($buildable->getPHID())
- ->setBuildPlanPHID($plan->getPHID())
- ->save();
-
- $buildable_id = $buildable->getID();
-
- return id(new AphrontRedirectResponse())
- ->setURI("/B{$buildable_id}");
- }
- }
-
- if ($errors) {
- $errors = id(new AphrontErrorView())->setErrors($errors);
- }
-
- $form = id(new PHUIFormLayoutView())
- ->appendChild(
- id(new AphrontFormTextControl())
- ->setLabel(pht('Buildable'))
- ->setName('buildable')
- ->setValue($v_buildable)
- ->setError($e_buildable));
-
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
- ->setTitle(pht('Execute Build Plan'))
- ->setWidth(AphrontDialogView::WIDTH_FORM)
- ->appendChild($errors)
- ->appendChild($form)
- ->addSubmitButton(pht('Execute Build Plan'))
- ->addCancelButton($cancel_uri);
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
- }
-
-}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
--- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
@@ -88,14 +88,6 @@
->setDisabled(!$can_edit)
->setIcon('edit'));
- $list->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Manually Execute Plan'))
- ->setHref($this->getApplicationURI("plan/execute/{$id}/"))
- ->setWorkflow(true)
- ->setDisabled(!$can_edit)
- ->setIcon('arrow_right'));
-
return $list;
}
diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php
--- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php
+++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php
@@ -5,6 +5,7 @@
private $ids;
private $phids;
+ private $buildStatuses;
private $buildablePHIDs;
private $buildPlanPHIDs;
@@ -20,6 +21,11 @@
return $this;
}
+ public function withBuildStatuses(array $build_statuses) {
+ $this->buildStatuses = $build_statuses;
+ return $this;
+ }
+
public function withBuildablePHIDs(array $buildable_phids) {
$this->buildablePHIDs = $buildable_phids;
return $this;
@@ -115,6 +121,13 @@
$this->phids);
}
+ if ($this->buildStatuses) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'buildStatus in (%Ls)',
+ $this->buildStatuses);
+ }
+
if ($this->buildablePHIDs) {
$where[] = qsprintf(
$conn_r,
diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php
--- a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php
+++ b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php
@@ -5,6 +5,7 @@
private $ids;
private $phids;
+ private $buildPlanPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -16,6 +17,11 @@
return $this;
}
+ public function withBuildPlanPHIDs(array $phids) {
+ $this->buildPlanPHIDs = $phids;
+ return $this;
+ }
+
protected function loadPage() {
$table = new HarbormasterBuildStep();
$conn_r = $table->establishConnection('r');
@@ -48,11 +54,43 @@
$this->phids);
}
+ if ($this->buildPlanPHIDs) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'buildPlanPHID in (%Ls)',
+ $this->buildPlanPHIDs);
+ }
+
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ protected function willFilterPage(array $page) {
+ $plans = array();
+
+ $buildplan_phids = array_filter(mpull($page, 'getBuildPlanPHID'));
+ if ($buildplan_phids) {
+ $plans = id(new PhabricatorObjectQuery())
+ ->setViewer($this->getViewer())
+ ->withPHIDs($buildplan_phids)
+ ->setParentQuery($this)
+ ->execute();
+ $plans = mpull($plans, null, 'getPHID');
+ }
+
+ foreach ($page as $key => $build) {
+ $buildable_phid = $build->getBuildPlanPHID();
+ if (empty($plans[$buildable_phid])) {
+ unset($page[$key]);
+ continue;
+ }
+ $build->attachBuildPlan($plans[$buildable_phid]);
+ }
+
+ return $page;
+ }
+
public function getQueryApplicationClass() {
return 'PhabricatorApplicationHarbormaster';
}
diff --git a/src/applications/harbormaster/step/BuildStepImplementation.php b/src/applications/harbormaster/step/BuildStepImplementation.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/step/BuildStepImplementation.php
@@ -0,0 +1,48 @@
+<?php
+
+abstract class BuildStepImplementation {
+
+ private $settings;
+
+ /**
+ * The name of the implementation.
+ */
+ abstract public function getName();
+
+ /**
+ * The description of the implementation.
+ */
+ public function getDescription() {
+ return '';
+ }
+
+ /**
+ * Run the build step against the specified build.
+ */
+ abstract public function execute(HarbormasterBuild $build);
+
+ /**
+ * Gets the settings for this build step.
+ */
+ protected function getSettings() {
+ return $this->settings;
+ }
+
+ /**
+ * Loads the settings for this build step implementation from the build step.
+ */
+ public final function loadSettings(HarbormasterBuildStep $build_step) {
+ $this->settings = array();
+ foreach ($this->getSettingDefinitions() as $name => $opt) {
+ $this->settings[$name] = $build_step->getDetail($name);
+ }
+ return $this->settings;
+ }
+
+ /**
+ * Return an array of settings for this step implementation.
+ */
+ public function getSettingDefinitions() {
+ return array();
+ }
+}
diff --git a/src/applications/harbormaster/step/SleepBuildStepImplementation.php b/src/applications/harbormaster/step/SleepBuildStepImplementation.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/step/SleepBuildStepImplementation.php
@@ -0,0 +1,24 @@
+<?php
+
+final class SleepBuildStepImplementation extends BuildStepImplementation {
+
+ public function getName() {
+ return pht('Sleep');
+ }
+
+ public function getDescription() {
+ return pht('Sleep for a specified number of seconds.');
+ }
+
+ public function execute(HarbormasterBuild $build) {
+ $settings = $this->getSettings();
+
+ sleep($settings['seconds']);
+ }
+
+ public function getSettingDefinitions() {
+ return array(
+ 'seconds' => array());
+ }
+
+}
diff --git a/src/applications/harbormaster/storage/HarbormasterBuildable.php b/src/applications/harbormaster/storage/HarbormasterBuildable.php
--- a/src/applications/harbormaster/storage/HarbormasterBuildable.php
+++ b/src/applications/harbormaster/storage/HarbormasterBuildable.php
@@ -12,10 +12,12 @@
private $containerObject = self::ATTACHABLE;
private $buildableHandle = self::ATTACHABLE;
+ const STATUS_WHATEVER = 'whatever';
+
public static function initializeNewBuildable(PhabricatorUser $actor) {
return id(new HarbormasterBuildable())
- ->setBuildStatus('new') // TODO: Define these.
- ->setBuildableStatus('active'); // TODO: Define these, too.
+ ->setBuildStatus(self::STATUS_WHATEVER)
+ ->setBuildableStatus(self::STATUS_WHATEVER);
}
public function getConfiguration() {
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -10,9 +10,39 @@
private $buildable = self::ATTACHABLE;
private $buildPlan = self::ATTACHABLE;
+ /**
+ * Not currently being built.
+ */
+ const STATUS_INACTIVE = 'inactive';
+
+ /**
+ * Pending pick up by the Harbormaster daemon.
+ */
+ const STATUS_PENDING = 'pending';
+
+ /**
+ * Waiting for a resource to be allocated (not yet relevant).
+ */
+ const STATUS_WAITING = 'waiting';
+
+ /**
+ * Current building the buildable.
+ */
+ const STATUS_BUILDING = 'building';
+
+ /**
+ * The build has passed.
+ */
+ const STATUS_PASSED = 'passed';
+
+ /**
+ * The build has failed.
+ */
+ const STATUS_FAILED = 'failed';
+
public static function initializeNewBuild(PhabricatorUser $actor) {
return id(new HarbormasterBuild())
- ->setBuildStatus('building'); // TODO: Sort this.
+ ->setBuildStatus(self::STATUS_INACTIVE);
}
public function getConfiguration() {
diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
@@ -1,14 +1,20 @@
<?php
-final class HarbormasterBuildStep extends HarbormasterDAO {
+final class HarbormasterBuildStep extends HarbormasterDAO
+ implements PhabricatorPolicyInterface {
protected $buildPlanPHID;
+ protected $className;
+ protected $details = array();
private $buildPlan = self::ATTACHABLE;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
+ self::CONFIG_SERIALIZATION => array(
+ 'details' => self::SERIALIZATION_JSON,
+ )
) + parent::getConfiguration();
}
@@ -26,4 +32,47 @@
return $this->assertAttached($this->buildPlan);
}
+ public function getDetail($key, $default = null) {
+ return idx($this->details, $key, $default);
+ }
+
+ public function setDetail($key, $value) {
+ $this->details[$key] = $value;
+ return $this;
+ }
+
+ public function getStepImplementation() {
+ if ($this->className === null) {
+ throw new Exception("No implementation set for the given step.");
+ }
+
+ // TODO: We should look up the class in phutil's system to ensure
+ // that it actually extends BuildStepImplementation.
+ $class = $this->className;
+ $implementation = newv($class, array());
+ $implementation->loadSettings($this);
+ return $implementation;
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getBuildPlan()->getPolicy($capability);
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return $this->getBuildPlan()->hasAutomaticCapability($capability, $viewer);
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return pht('A build step has the same policies as it\'s build plan.');
+ }
}
diff --git a/src/applications/harbormaster/worker/HarbormasterBuildWorker.php b/src/applications/harbormaster/worker/HarbormasterBuildWorker.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/worker/HarbormasterBuildWorker.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Run builds
+ */
+final class HarbormasterBuildWorker extends PhabricatorWorker {
+
+ public function getRequiredLeaseTime() {
+ return 60 * 60 * 24;
+ }
+
+ public function doWork() {
+ $data = $this->getTaskData();
+ $id = idx($data, 'buildID');
+
+ // Get a reference to the build.
+ $build = id(new HarbormasterBuildQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withBuildStatuses(array(HarbormasterBuild::STATUS_PENDING))
+ ->withIDs(array($id))
+ ->needBuildPlans(true)
+ ->executeOne();
+ if (!$build) {
+ throw new PhabricatorWorkerPermanentFailureException(
+ pht('Invalid build ID "%s".', $id));
+ }
+
+ try {
+ $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING);
+ $build->save();
+
+ $buildable = $build->getBuildable();
+ $plan = $build->getBuildPlan();
+
+ $steps = id(new HarbormasterBuildStepQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withBuildPlanPHIDs(array($plan->getPHID()))
+ ->execute();
+
+ // Perform the build.
+ foreach ($steps as $step) {
+ $implementation = $step->getStepImplementation();
+ $implementation->execute($build);
+ if ($build->getBuildStatus() !== HarbormasterBuild::STATUS_BUILDING) {
+ break;
+ }
+ }
+
+ // If we get to here, then the build has finished. Set it to passed
+ // if no build step explicitly set the status.
+ if ($build->getBuildStatus() === HarbormasterBuild::STATUS_BUILDING) {
+ $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED);
+ }
+ $build->save();
+ } catch (Exception $e) {
+ // If any exception is raised, the build is marked as a failure and
+ // the exception is re-thrown (this ensures we don't leave builds
+ // in an inconsistent state).
+ $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED);
+ $build->save();
+ throw $e;
+ }
+ }
+
+}
diff --git a/src/applications/harbormaster/worker/HarbormasterRunnerWorker.php b/src/applications/harbormaster/worker/HarbormasterRunnerWorker.php
deleted file mode 100644
--- a/src/applications/harbormaster/worker/HarbormasterRunnerWorker.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-final class HarbormasterRunnerWorker extends PhabricatorWorker {
-
- public function getRequiredLeaseTime() {
- return 60 * 60 * 24;
- }
-
- protected function doWork() {
- $data = $this->getTaskData();
- $id = idx($data, 'commitID');
-
- $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
- 'id = %d',
- $id);
-
- if (!$commit) {
- throw new PhabricatorWorkerPermanentFailureException(
- "Commit '{$id}' does not exist!");
- }
-
- // TODO: (T603) Policy interaction?
- $repository = id(new PhabricatorRepository())->loadOneWhere(
- 'id = %d',
- $commit->getRepositoryID());
-
- if (!$repository) {
- throw new PhabricatorWorkerPermanentFailureException(
- "Unable to load repository for commit '{$id}'!");
- }
-
- $lease = id(new DrydockLease())
- ->setResourceType('working-copy')
- ->setAttributes(
- array(
- 'repositoryID' => $repository->getID(),
- 'commit' => $commit->getCommitIdentifier(),
- ))
- ->releaseOnDestruction()
- ->waitUntilActive();
-
- $cmd = $lease->getInterface('command');
- list($json) = $cmd
- ->setWorkingDirectory($lease->getResource()->getAttribute('path'))
- ->execx('arc unit --everything --json');
- $lease->release();
-
- // TODO: Do something actually useful with this. Requires Harbormaster
- // buildout.
- echo $json;
- }
-
-}
diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
--- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
+++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
@@ -750,8 +750,7 @@
$branches = mpull($branches, 'getHeadCommitIdentifier', 'getName');
$got_something = false;
- foreach ($branches as $name => $branch) {
- $commit = $branch['rev'];
+ foreach ($branches as $name => $commit) {
if ($this->isKnownCommit($repository, $commit)) {
continue;
} else {
diff --git a/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php b/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
--- a/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
+++ b/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
@@ -11,16 +11,19 @@
}
public function getRequiredObjectPHIDs() {
- return array(
- $this->getPrimaryTransactionPHID(),
- );
+ return $this->getValue('transactionPHIDs');
}
public function getRequiredHandlePHIDs() {
$phids = array();
- $phids[] = array($this->getValue('objectPHID'));
- $phids[] = $this->getPrimaryTransaction()->getRequiredHandlePHIDs();
- return array_mergev($phids);
+ $phids[] = $this->getValue('objectPHID');
+ foreach ($this->getValue('transactionPHIDs') as $xaction_phid) {
+ $xaction = $this->getObject($xaction_phid);
+ foreach ($xaction->getRequiredHandlePHIDs() as $handle_phid) {
+ $phids[] = $handle_phid;
+ }
+ }
+ return $phids;
}
protected function getPrimaryTransactionPHID() {
@@ -40,18 +43,23 @@
$view->setAppIconFromPHID($handle->getPHID());
$xaction_phids = $this->getValue('transactionPHIDs');
- $xaction = $this->getObject(head($xaction_phids));
+ $xaction = $this->getPrimaryTransaction();
$xaction->setHandles($this->getHandles());
$view->setTitle($xaction->getTitleForFeed($this));
- $body = $xaction->getBodyForFeed($this);
- if (nonempty($body)) {
- $view->appendChild($body);
+
+ foreach ($xaction_phids as $xaction_phid) {
+ $secondary_xaction = $this->getObject($xaction_phid);
+ $secondary_xaction->setHandles($this->getHandles());
+
+ $body = $secondary_xaction->getBodyForFeed($this);
+ if (nonempty($body)) {
+ $view->appendChild($body);
+ }
}
$view->setImage(
- $this->getHandle(
- $this->getPrimaryTransaction()->getAuthorPHID())->getImageURI());
+ $this->getHandle($xaction->getAuthorPHID())->getImageURI());
return $view;
}
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1720,6 +1720,10 @@
'type' => 'sql',
'name' => $this->getPatchPath('20131031.vcspassword.sql'),
),
+ '20131105.buildstep.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131105.buildstep.sql'),
+ ),
);
}
}
diff --git a/webroot/rsrc/css/phui/phui-workboard-view.css b/webroot/rsrc/css/phui/phui-workboard-view.css
--- a/webroot/rsrc/css/phui/phui-workboard-view.css
+++ b/webroot/rsrc/css/phui/phui-workboard-view.css
@@ -15,6 +15,15 @@
box-shadow: inset 0 0 5px rgba(0,0,0,.5);
}
+.phui-workboard-view-shadow::-webkit-scrollbar {
+ height: 12px;
+}
+
+.phui-workboard-view-shadow::-webkit-scrollbar-thumb {
+ background: {$lightbluetext};
+ border-radius: 10px;
+}
+
.phui-workboard-action-list {
width: 60px;
float: left;
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/hb/ev/2kpjxejzkddchkkf
Default Alt Text
D7499.id16923.diff (52 KB)
Attached To
Mode
D7499: Add build step implementation infrastructure and sleep build step.
Attached
Detach File
Event Timeline
Log In to Comment