getRequest(); $e_name = true; $e_trunk_branch = true; $e_branch_template = false; $errors = array(); $project_name = $request->getStr('name', $this->getReleephProject()->getName()); $phabricator_project_id = $request->getInt('projectID', $this->getReleephProject()->getProjectID()); $trunk_branch = $request->getStr('trunkBranch', $this->getReleephProject()->getTrunkBranch()); $branch_template = $request->getStr('branchTemplate'); if ($branch_template === null) { $branch_template = $this->getReleephProject()->getDetail('branchTemplate'); } $pick_failure_instructions = $request->getStr('pickFailureInstructions', $this->getReleephProject()->getDetail('pick_failure_instructions')); $commit_author = $request->getStr('commitWithAuthor', $this->getReleephProject()->getDetail('commitWithAuthor')); $test_paths = $request->getStr('testPaths'); if ($test_paths !== null) { $test_paths = array_filter(explode("\n", $test_paths)); } else { $test_paths = $this->getReleephProject()->getDetail('testPaths', array()); } $field_selector = $request->getStr('fieldSelector', get_class($this->getReleephProject()->getReleephFieldSelector())); $release_counter = $request->getInt( 'releaseCounter', $this->getReleephProject()->getCurrentReleaseNumber()); $arc_project_id = $this->getReleephProject()->getArcanistProjectID(); if ($request->isFormPost()) { $pusher_phids = $request->getArr('pushers'); if (!$project_name) { $e_name = 'Required'; $errors[] = 'Your releeph project should have a simple descriptive name'; } if (!$trunk_branch) { $e_trunk_branch = 'Required'; $errors[] = 'You must specify which branch you will be picking from.'; } if ($release_counter && !is_int($release_counter)) { $errors[] = "Release counter must be a positive integer!"; } $other_releeph_projects = id(new ReleephProject()) ->loadAllWhere('id <> %d', $this->getReleephProject()->getID()); $other_releeph_project_names = mpull($other_releeph_projects, 'getName', 'getID'); if (in_array($project_name, $other_releeph_project_names)) { $errors[] = "Releeph project name {$project_name} is already taken"; } foreach ($test_paths as $test_path) { $result = @preg_match($test_path, ''); $is_a_valid_regexp = $result !== false; if (!$is_a_valid_regexp) { $errors[] = 'Please provide a valid regular expression: '. "{$test_path} is not valid"; } } $project = $this->getReleephProject() ->setProjectID($phabricator_project_id) ->setTrunkBranch($trunk_branch) ->setDetail('pushers', $pusher_phids) ->setDetail('pick_failure_instructions', $pick_failure_instructions) ->setDetail('field_selector', $field_selector) ->setDetail('branchTemplate', $branch_template) ->setDetail('commitWithAuthor', $commit_author) ->setDetail('testPaths', $test_paths); if ($release_counter) { $project->setDetail('releaseCounter', $release_counter); } $fake_commit_handle = ReleephBranchTemplate::getFakeCommitHandleFor($arc_project_id); if ($branch_template) { list($branch_name, $template_errors) = id(new ReleephBranchTemplate()) ->setCommitHandle($fake_commit_handle) ->setReleephProjectName($project_name) ->interpolate($branch_template); if ($template_errors) { $e_branch_template = 'Invalid!'; foreach ($template_errors as $template_error) { $errors[] = "Template error: {$template_error}"; } } } if (!$errors) { $project->save(); return id(new AphrontRedirectResponse()) ->setURI('/releeph/project/'); } } $error_view = null; if ($errors) { $error_view = new AphrontErrorView(); $error_view->setErrors($errors); $error_view->setTitle('Form Errors'); } $projects = mpull( id(new PhabricatorProject())->loadAll(), 'getName', 'getID'); $projects[0] = '-'; // no project associated, that's ok $pusher_phids = $request->getArr( 'pushers', $this->getReleephProject()->getDetail('pushers', array())); $handles = id(new PhabricatorObjectHandleData($pusher_phids)) ->setViewer($request->getUser()) ->loadHandles(); $pusher_tokens = array(); foreach ($pusher_phids as $phid) { $pusher_tokens[$phid] = $handles[$phid]->getFullName(); } $basic_inset = id(new AphrontFormInsetView()) ->setTitle('Basics') ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Name') ->setName('name') ->setValue($project_name) ->setError($e_name) ->setCaption('A name like "Thrift" but not "Thrift releases".')) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Repository') ->setValue( $this ->getReleephProject() ->loadPhabricatorRepository() ->getName())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Arc Project') ->setValue( $this->getReleephProject()->loadArcanistProject()->getName())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Releeph Project PHID') ->setValue( $this->getReleephProject()->getPHID())) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Phabricator Project') ->setValue($phabricator_project_id) ->setName('projectID') ->setOptions($projects)) ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Trunk') ->setValue($trunk_branch) ->setName('trunkBranch') ->setError($e_trunk_branch)) ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Release counter') ->setValue($release_counter) ->setName('releaseCounter') ->setCaption( "Used by the command line branch cutter's %N field")) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Pick Instructions') ->setValue($pick_failure_instructions) ->setName('pickFailureInstructions') ->setCaption( "Instructions for pick failures, which will be used " . "in emails generated by failed picks")) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Tests paths') ->setValue(implode("\n", $test_paths)) ->setName('testPaths') ->setCaption( 'List of strings that all test files contain in their path '. 'in this project. One string per line. '. 'Examples: \'__tests__\', \'/javatests/\'...')); $pushers_inset = id(new AphrontFormInsetView()) ->setTitle('Pushers') ->appendChild( 'Pushers are allowed to approve Releeph requests to be committed. '. 'to this project\'s branches. If you leave this blank then anyone '. 'is allowed to approve requests.') ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel('Pushers') ->setName('pushers') ->setDatasource('/typeahead/common/users/') ->setValue($pusher_tokens)); $field_selector_options = array(); $field_selector_symbols = id(new PhutilSymbolLoader()) ->setType('class') ->setConcreteOnly(true) ->setAncestorClass('ReleephFieldSelector') ->selectAndLoadSymbols(); foreach ($field_selector_symbols as $symbol) { $selector_name = $symbol['name']; $field_selector_options[$selector_name] = $selector_name; } $field_selector_blurb = hsprintf( "If you you have additional information to render about Releeph ". "requests, or want to re-arrange the UI, implement a ". "ReleephFieldSelector and select it here."); $fields_inset = id(new AphrontFormInsetView()) ->setTitle('Fields') ->appendChild($field_selector_blurb) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Selector') ->setName('fieldSelector') ->setValue($field_selector) ->setOptions($field_selector_options)); $commit_author_inset = $this->buildCommitAuthorInset($commit_author); // Build the Template inset $markup_engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); // From DifferentialUnitFieldSpecification... $markup_engine->setConfig('viewer', $request->getUser()); $help_markup = phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), phutil_safe_html( $markup_engine->markupText(ReleephBranchTemplate::getHelpRemarkup()))); $branch_template_input = id(new AphrontFormTextControl()) ->setName('branchTemplate') ->setValue($branch_template) ->setLabel('Template') ->setError($e_branch_template) ->setCaption( "Leave this blank to use your installation's default."); $branch_template_preview = id(new ReleephBranchPreviewView()) ->setLabel('Preview') ->addControl('template', $branch_template_input) ->addStatic('arcProjectID', $arc_project_id) ->addStatic('isSymbolic', false) ->addStatic('projectName', $this->getReleephProject()->getName()); $template_inset = id(new AphrontFormInsetView()) ->setTitle('Branch Cutting') ->appendChild( 'Provide a pattern for creating new branches.') ->appendChild($branch_template_input) ->appendChild($branch_template_preview) ->appendChild($help_markup); // Build the form $form = id(new AphrontFormView()) ->setUser($request->getUser()) ->appendChild($basic_inset) ->appendChild($pushers_inset) ->appendChild($fields_inset) ->appendChild($commit_author_inset) ->appendChild($template_inset); $form ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton('/releeph/project/') ->setValue('Save')); $panel = id(new AphrontPanelView()) ->setHeader('Edit Releeph Project') ->appendChild($form) ->setWidth(AphrontPanelView::WIDTH_FORM); return $this->buildStandardPageResponse( array($error_view, $panel), array('title' => 'Edit Releeph Project')); } private function buildCommitAuthorInset($current) { $vcs_type = $this->getReleephProject() ->loadPhabricatorRepository() ->getVersionControlSystem(); switch ($vcs_type) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: return; break; } $vcs_name = PhabricatorRepositoryType::getNameForRepositoryType($vcs_type); $help_markup = hsprintf(<<arc releeph, they will be listed as the committer of the code committed to release branches. %s allows you to specify a separate author when committing code. Some tools use the author of a commit (rather than the committer) when they need to notify someone about a build or test failure. Releeph can use one of the following to set the author of the commits it makes: EOTEXT , $vcs_name); $trunk = $this->getReleephProject()->getTrunkBranch(); $options = array( array( 'value' => ReleephProject::COMMIT_AUTHOR_FROM_DIFF, 'label' => 'Original Author', 'caption' => "The author of the original commit in {$trunk}.", ), array( 'value' => ReleephProject::COMMIT_AUTHOR_REQUESTOR, 'label' => 'Requestor', 'caption' => "The person who requested that this code go into the release.", ), array( 'value' => ReleephProject::COMMIT_AUTHOR_NONE, 'label' => "None", 'caption' => "Only record the default committer information.", ), ); if (!$current) { $current = ReleephProject::COMMIT_AUTHOR_FROM_DIFF; } $control = id(new AphrontFormRadioButtonControl()) ->setLabel('Author') ->setName('commitWithAuthor') ->setValue($current); foreach ($options as $dict) { $control->addButton($dict['value'], $dict['label'], $dict['caption']); } return id(new AphrontFormInsetView()) ->setTitle('Authors') ->appendChild($help_markup) ->appendChild($control); } }