From ac19c558225c5d94050f86677f8276b4960c0908 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 26 Dec 2013 10:40:43 -0800 Subject: [PATCH] Formalize "manual" buildables in Harbormaster Summary: Ref T1049. Generally, it's useful to separate test/trial/manual runs from production/automatic runs. For example, you don't want to email a bunch of people that the build is broken just because you messed something up when writing a new build plan. You'd rather try it first, then promote it into production once you have some good runs. Similarly, test runs generally should not affect the outside world, etc. Finally, some build steps (like "wait for other buildables") may want to behave differently when run in production/automation than when run in a testing environment (where they should probably continue immediately). So, formalize the distinction between automatic buildables (those created passively by the system in response to events) and manual buildables (those created explicitly by users). Add filtering, and stop the automated parts of the system from interacting with the manual parts (for example, we won't show manual results on revisions). This also moves the "Apply Build Plan" to a third, new home: instead of the sidebar or Buildables, it's now on plans. I think this generally makes more sense given how things have developed. Broadly, this improves isolation of test environments. Test Plan: Created some builds, browsed around, used filters, etc. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D7824 --- .../sql/patches/20131224.harbormanual.sql | 6 + src/__phutil_library_map__.php | 6 +- .../PhabricatorApplicationHarbormaster.php | 5 +- .../HarbormasterBuildableApplyController.php | 74 --------- .../HarbormasterBuildableEditController.php | 140 ------------------ .../HarbormasterBuildableListController.php | 9 +- .../HarbormasterBuildableViewController.php | 15 +- .../HarbormasterPlanRunController.php | 103 +++++++++++++ .../HarbormasterPlanViewController.php | 10 +- .../query/HarbormasterBuildableQuery.php | 13 ++ .../HarbormasterBuildableSearchEngine.php | 21 ++- ...WaitForPreviousBuildStepImplementation.php | 1 + .../storage/HarbormasterBuildable.php | 3 + .../patch/PhabricatorBuiltinPatchList.php | 4 + 14 files changed, 175 insertions(+), 235 deletions(-) create mode 100644 resources/sql/patches/20131224.harbormanual.sql delete mode 100644 src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php delete mode 100644 src/applications/harbormaster/controller/HarbormasterBuildableEditController.php create mode 100644 src/applications/harbormaster/controller/HarbormasterPlanRunController.php diff --git a/resources/sql/patches/20131224.harbormanual.sql b/resources/sql/patches/20131224.harbormanual.sql new file mode 100644 index 0000000000..fb5a5ee970 --- /dev/null +++ b/resources/sql/patches/20131224.harbormanual.sql @@ -0,0 +1,6 @@ +ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildable + ADD isManualBuildable BOOL NOT NULL; + +ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildable + ADD KEY `key_manual` (isManualBuildable); + diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 179fc3af91..6304b8737a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -707,8 +707,6 @@ phutil_register_library_map(array( 'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php', 'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php', 'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php', - 'HarbormasterBuildableApplyController' => 'applications/harbormaster/controller/HarbormasterBuildableApplyController.php', - 'HarbormasterBuildableEditController' => 'applications/harbormaster/controller/HarbormasterBuildableEditController.php', 'HarbormasterBuildableListController' => 'applications/harbormaster/controller/HarbormasterBuildableListController.php', 'HarbormasterBuildableQuery' => 'applications/harbormaster/query/HarbormasterBuildableQuery.php', 'HarbormasterBuildableSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildableSearchEngine.php', @@ -730,6 +728,7 @@ phutil_register_library_map(array( 'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php', 'HarbormasterPlanListController' => 'applications/harbormaster/controller/HarbormasterPlanListController.php', 'HarbormasterPlanOrderController' => 'applications/harbormaster/controller/HarbormasterPlanOrderController.php', + 'HarbormasterPlanRunController' => 'applications/harbormaster/controller/HarbormasterPlanRunController.php', 'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php', 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', 'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php', @@ -3133,8 +3132,6 @@ phutil_register_library_map(array( 0 => 'HarbormasterDAO', 1 => 'PhabricatorPolicyInterface', ), - 'HarbormasterBuildableApplyController' => 'HarbormasterController', - 'HarbormasterBuildableEditController' => 'HarbormasterController', 'HarbormasterBuildableListController' => array( 0 => 'HarbormasterController', @@ -3164,6 +3161,7 @@ phutil_register_library_map(array( 1 => 'PhabricatorApplicationSearchResultsControllerInterface', ), 'HarbormasterPlanOrderController' => 'HarbormasterController', + 'HarbormasterPlanRunController' => 'HarbormasterController', 'HarbormasterPlanViewController' => 'HarbormasterPlanController', 'HarbormasterRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'HarbormasterScratchTable' => 'HarbormasterDAO', diff --git a/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php b/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php index 209be18164..2560f201f5 100644 --- a/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php +++ b/src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php @@ -48,10 +48,6 @@ final class PhabricatorApplicationHarbormaster extends PhabricatorApplication { '/harbormaster/' => array( '(?:query/(?P[^/]+)/)?' => 'HarbormasterBuildableListController', - 'buildable/' => array( - 'edit/(?:(?P\d+)/)?' => 'HarbormasterBuildableEditController', - 'apply/(?:(?P\d+)/)?' => 'HarbormasterBuildableApplyController', - ), 'step/' => array( 'add/(?:(?P\d+)/)?' => 'HarbormasterStepAddController', 'edit/(?:(?P\d+)/)?' => 'HarbormasterStepEditController', @@ -67,6 +63,7 @@ final class PhabricatorApplicationHarbormaster extends PhabricatorApplication { 'edit/(?:(?P\d+)/)?' => 'HarbormasterPlanEditController', 'order/(?:(?P\d+)/)?' => 'HarbormasterPlanOrderController', 'disable/(?P\d+)/' => 'HarbormasterPlanDisableController', + 'run/(?P\d+)/' => 'HarbormasterPlanRunController', '(?P\d+)/' => 'HarbormasterPlanViewController', ), ), diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php b/src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php deleted file mode 100644 index b1eff91f15..0000000000 --- a/src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php +++ /dev/null @@ -1,74 +0,0 @@ -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(); - - HarbormasterBuildable::applyBuildPlans( - $buildable->getBuildablePHID(), - $buildable->getContainerPHID(), - array($plan->getPHID())); - - 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/HarbormasterBuildableEditController.php b/src/applications/harbormaster/controller/HarbormasterBuildableEditController.php deleted file mode 100644 index 3c27fb8ace..0000000000 --- a/src/applications/harbormaster/controller/HarbormasterBuildableEditController.php +++ /dev/null @@ -1,140 +0,0 @@ -id = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $this->requireApplicationCapability( - HarbormasterCapabilityManagePlans::CAPABILITY); - - if ($this->id) { - $buildable = id(new HarbormasterBuildableQuery()) - ->setViewer($viewer) - ->withIDs(array($this->id)) - ->executeOne(); - if (!$buildable) { - return new Aphront404Response(); - } - } else { - $buildable = HarbormasterBuildable::initializeNewBuildable($viewer); - } - - $e_name = true; - $v_name = null; - - $errors = array(); - if ($request->isFormPost()) { - $v_name = $request->getStr('buildablePHID'); - - if ($v_name) { - $object = id(new PhabricatorObjectQuery()) - ->setViewer($viewer) - ->withNames(array($v_name)) - ->executeOne(); - - if ($object instanceof DifferentialRevision) { - $revision = $object; - $object = $object->loadActiveDiff(); - $buildable - ->setBuildablePHID($object->getPHID()) - ->setContainerPHID($revision->getPHID()); - } else if ($object instanceof PhabricatorRepositoryCommit) { - $buildable - ->setBuildablePHID($object->getPHID()) - ->setContainerPHID($object->getRepository()->getPHID()); - } else { - $e_name = pht('Invalid'); - $errors[] = pht('Enter the name of a revision or commit.'); - } - } else { - $e_name = pht('Required'); - $errors[] = pht('You must choose a revision or commit to build.'); - } - - if (!$errors) { - $buildable->save(); - - $buildable_uri = '/B'.$buildable->getID(); - return id(new AphrontRedirectResponse())->setURI($buildable_uri); - } - } - - if ($errors) { - $errors = id(new AphrontErrorView())->setErrors($errors); - } - - $is_new = (!$buildable->getID()); - if ($is_new) { - $title = pht('New Buildable'); - $cancel_uri = $this->getApplicationURI(); - $save_button = pht('Create Buildable'); - } else { - $id = $buildable->getID(); - - $title = pht('Edit Buildable'); - $cancel_uri = "/B{$id}"; - $save_button = pht('Save Buildable'); - } - - $form = id(new AphrontFormView()) - ->setUser($viewer); - - if ($is_new) { - $form - ->appendRemarkupInstructions( - pht( - 'Enter the name of a commit or revision, like `rX123456789` '. - 'or `D123`.')) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel('Buildable Name') - ->setName('buildablePHID') - ->setError($e_name) - ->setValue($v_name)); - } else { - $form->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Buildable')) - ->setValue($buildable->getBuildableHandle()->renderLink())); - } - - $form->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue($save_button) - ->addCancelButton($cancel_uri)); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) - ->setFormError($errors) - ->setForm($form); - - $crumbs = $this->buildApplicationCrumbs(); - if ($is_new) { - $crumbs->addTextCrumb(pht('New Buildable')); - } else { - $id = $buildable->getID(); - $crumbs->addTextCrumb("B{$id}", "/B{$id}"); - $crumbs->addTextCrumb(pht('Edit')); - } - - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - 'device' => true, - )); - } - -} diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php index 1f51a8de46..26c756ecb4 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php @@ -44,8 +44,14 @@ final class HarbormasterBuildableListController $item->setHref("/B{$id}"); } + if ($buildable->getIsManualBuildable()) { + $item->addIcon('wrench-grey', pht('Manual')); + } + $list->addItem($item); + + // TODO: This is proof-of-concept for getting meaningful status // information into this list, and should get an improvement pass // once we're a little farther along. @@ -86,8 +92,7 @@ final class HarbormasterBuildableListController $nav->addFilter('new/', pht('New Build Plan')); } - $nav->addLabel('Utilities'); - $nav->addFilter('buildable/edit/', pht('New Manual Build')); + $nav->addLabel(pht('Build Plans')); $nav->addFilter('plan/', pht('Manage Build Plans')); $nav->selectFilter(null); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index 41e5ad747c..422d2d8321 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -118,15 +118,6 @@ final class HarbormasterBuildableViewController ->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; } @@ -153,6 +144,12 @@ final class HarbormasterBuildableViewController $buildable->getContainerHandle()->renderLink()); } + $properties->addProperty( + pht('Origin'), + $buildable->getIsManualBuildable() + ? pht('Manual Buildable') + : pht('Automatic Buildable')); + } } diff --git a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php new file mode 100644 index 0000000000..c7c10b09d1 --- /dev/null +++ b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php @@ -0,0 +1,103 @@ +id = $data['id']; + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $this->requireApplicationCapability( + HarbormasterCapabilityManagePlans::CAPABILITY); + + $plan_id = $this->id; + $plan = id(new HarbormasterBuildPlanQuery()) + ->setViewer($viewer) + ->withIDs(array($plan_id)) + ->executeOne(); + if (!$plan) { + return new Aphront404Response(); + } + + $e_name = true; + $v_name = null; + + $errors = array(); + if ($request->isFormPost()) { + $buildable = HarbormasterBuildable::initializeNewBuildable($viewer) + ->setIsManualBuildable(true); + + $v_name = $request->getStr('buildablePHID'); + + if ($v_name) { + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withNames(array($v_name)) + ->executeOne(); + + if ($object instanceof DifferentialRevision) { + $revision = $object; + $object = $object->loadActiveDiff(); + $buildable + ->setBuildablePHID($object->getPHID()) + ->setContainerPHID($revision->getPHID()); + } else if ($object instanceof PhabricatorRepositoryCommit) { + $buildable + ->setBuildablePHID($object->getPHID()) + ->setContainerPHID($object->getRepository()->getPHID()); + } else { + $e_name = pht('Invalid'); + $errors[] = pht('Enter the name of a revision or commit.'); + } + } else { + $e_name = pht('Required'); + $errors[] = pht('You must choose a revision or commit to build.'); + } + + if (!$errors) { + $buildable->save(); + + $buildable_uri = '/B'.$buildable->getID(); + return id(new AphrontRedirectResponse())->setURI($buildable_uri); + } + } + + if ($errors) { + $errors = id(new AphrontErrorView())->setErrors($errors); + } + + $title = pht('Run Build Plan Manually'); + $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); + $save_button = pht('Run Plan Manually'); + + $form = id(new PHUIFormLayoutView()) + ->setUser($viewer) + ->appendRemarkupInstructions( + pht( + "Enter the name of a commit or revision to run this plan on.\n\n". + "For example: `rX123456` or `D123`")) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Buildable Name') + ->setName('buildablePHID') + ->setError($e_name) + ->setValue($v_name)); + + $dialog = id(new AphrontDialogView()) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setUser($viewer) + ->setTitle($title) + ->appendChild($form) + ->addCancelButton($cancel_uri) + ->addSubmitButton($save_button); + + return id(new AphrontDialogResponse())->setDialog($dialog); + } + +} diff --git a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php index bf30b946ed..b11a6bf288 100644 --- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php @@ -200,10 +200,18 @@ final class HarbormasterPlanViewController id(new PhabricatorActionView()) ->setName(pht('Add Build Step')) ->setHref($this->getApplicationURI("step/add/{$id}/")) - ->setWorkflow($can_edit) + ->setWorkflow(true) ->setDisabled(!$can_edit) ->setIcon('new')); + $list->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Run Plan Manually')) + ->setHref($this->getApplicationURI("plan/run/{$id}/")) + ->setWorkflow(true) + ->setDisabled(!$can_edit) + ->setIcon('start-sandcastle')); + return $list; } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php index 1aae1b4240..1fb1d4224d 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php @@ -7,6 +7,7 @@ final class HarbormasterBuildableQuery private $phids; private $buildablePHIDs; private $containerPHIDs; + private $manualBuildables; private $needContainerObjects; private $needContainerHandles; @@ -33,6 +34,11 @@ final class HarbormasterBuildableQuery return $this; } + public function withManualBuildables($manual) { + $this->manualBuildables = $manual; + return $this; + } + public function needContainerObjects($need) { $this->needContainerObjects = $need; return $this; @@ -197,6 +203,13 @@ final class HarbormasterBuildableQuery $this->containerPHIDs); } + if ($this->manualBuildables !== null) { + $where[] = qsprintf( + $conn_r, + 'isManualBuildable = %d', + (int)$this->manualBuildables); + } + $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); diff --git a/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php index dbf263f8e2..3f33276af5 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php @@ -42,6 +42,9 @@ final class HarbormasterBuildableSearchEngine $buildable_phids = array_merge($commits, $diffs); $saved->setParameter('buildablePHIDs', $buildable_phids); + $saved->setParameter( + 'manual', + $this->readBoolFromRequest($request, 'manual')); return $saved; } @@ -63,6 +66,11 @@ final class HarbormasterBuildableSearchEngine $query->withBuildablePHIDs($buildable_phids); } + $manual = $saved->getParameter('manual'); + if ($manual !== null) { + $query->withManualBuildables($manual); + } + return $query; } @@ -126,7 +134,18 @@ final class HarbormasterBuildableSearchEngine id(new AphrontFormTextControl()) ->setLabel(pht('Commits')) ->setName('commits') - ->setValue(implode(', ', $commit_names))); + ->setValue(implode(', ', $commit_names))) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Origin')) + ->setName('manual') + ->setValue($this->getBoolFromQuery($saved_query, 'manual')) + ->setOptions( + array( + '' => pht('(All Origins)'), + 'true' => pht('Manual Buildables'), + 'false' => pht('Automatic Buildables'), + ))); } protected function getURI($path) { diff --git a/src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php b/src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php index e222e33e0b..7190100041 100644 --- a/src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php +++ b/src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php @@ -102,6 +102,7 @@ final class WaitForPreviousBuildStepImplementation $buildables = id(new HarbormasterBuildableQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withBuildablePHIDs($build_objects) + ->withManualBuildables(false) ->execute(); $buildable_phids = mpull($buildables, 'getPHID'); diff --git a/src/applications/harbormaster/storage/HarbormasterBuildable.php b/src/applications/harbormaster/storage/HarbormasterBuildable.php index a11137ecdc..c5663ab87e 100644 --- a/src/applications/harbormaster/storage/HarbormasterBuildable.php +++ b/src/applications/harbormaster/storage/HarbormasterBuildable.php @@ -7,6 +7,7 @@ final class HarbormasterBuildable extends HarbormasterDAO protected $containerPHID; protected $buildStatus; protected $buildableStatus; + protected $isManualBuildable; private $buildableObject = self::ATTACHABLE; private $containerObject = self::ATTACHABLE; @@ -18,6 +19,7 @@ final class HarbormasterBuildable extends HarbormasterDAO public static function initializeNewBuildable(PhabricatorUser $actor) { return id(new HarbormasterBuildable()) + ->setIsManualBuildable(0) ->setBuildStatus(self::STATUS_WHATEVER) ->setBuildableStatus(self::STATUS_WHATEVER); } @@ -34,6 +36,7 @@ final class HarbormasterBuildable extends HarbormasterDAO $buildable = id(new HarbormasterBuildableQuery()) ->setViewer($actor) ->withBuildablePHIDs(array($buildable_object_phid)) + ->withManualBuildables(false) ->setLimit(1) ->executeOne(); if ($buildable) { diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 38d46b7d6b..bec34235d7 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1848,6 +1848,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'type' => 'sql', 'name' => $this->getPatchPath('20131219.pxdrop.sql'), ), + '20131224.harbormanual.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20131224.harbormanual.sql'), + ), ); } }