2013-03-15 12:28:43 +01:00
|
|
|
<?php
|
|
|
|
|
2013-07-21 17:42:10 +02:00
|
|
|
final class ReleephProjectEditController extends ReleephProjectController {
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
|
|
|
|
$e_name = true;
|
|
|
|
$e_trunk_branch = true;
|
|
|
|
$e_branch_template = false;
|
|
|
|
$errors = array();
|
|
|
|
|
|
|
|
$project_name = $request->getStr('name',
|
|
|
|
$this->getReleephProject()->getName());
|
|
|
|
|
|
|
|
$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());
|
|
|
|
}
|
|
|
|
|
|
|
|
$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) {
|
2013-05-04 03:33:49 +02:00
|
|
|
$e_name = pht('Required');
|
2013-03-15 12:28:43 +01:00
|
|
|
$errors[] =
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('Your releeph project should have a simple descriptive name');
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$trunk_branch) {
|
2013-05-04 03:33:49 +02:00
|
|
|
$e_trunk_branch = pht('Required');
|
2013-03-15 12:28:43 +01:00
|
|
|
$errors[] =
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('You must specify which branch you will be picking from.');
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($release_counter && !is_int($release_counter)) {
|
2013-05-04 03:33:49 +02:00
|
|
|
$errors[] = pht("Release counter must be a positive integer!");
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$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)) {
|
2013-05-04 03:33:49 +02:00
|
|
|
$errors[] = pht("Releeph project name %s is already taken",
|
|
|
|
$project_name);
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($test_paths as $test_path) {
|
|
|
|
$result = @preg_match($test_path, '');
|
|
|
|
$is_a_valid_regexp = $result !== false;
|
|
|
|
if (!$is_a_valid_regexp) {
|
2013-05-04 03:33:49 +02:00
|
|
|
$errors[] = pht('Please provide a valid regular expression: '.
|
|
|
|
'%s is not valid', $test_path);
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$project = $this->getReleephProject()
|
|
|
|
->setTrunkBranch($trunk_branch)
|
|
|
|
->setDetail('pushers', $pusher_phids)
|
|
|
|
->setDetail('pick_failure_instructions', $pick_failure_instructions)
|
|
|
|
->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) {
|
2013-05-04 03:33:49 +02:00
|
|
|
$e_branch_template = pht('Whoopsies!');
|
2013-03-15 12:28:43 +01:00
|
|
|
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);
|
2013-05-04 03:33:49 +02:00
|
|
|
$error_view->setTitle(pht('Form Errors'));
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$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()));
|
|
|
|
|
2013-09-11 21:27:28 +02:00
|
|
|
$handles = id(new PhabricatorHandleQuery())
|
2013-03-15 12:28:43 +01:00
|
|
|
->setViewer($request->getUser())
|
2013-09-11 21:27:28 +02:00
|
|
|
->withPHIDs($pusher_phids)
|
|
|
|
->execute();
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
$pusher_tokens = array();
|
|
|
|
foreach ($pusher_phids as $phid) {
|
|
|
|
$pusher_tokens[$phid] = $handles[$phid]->getFullName();
|
|
|
|
}
|
|
|
|
|
|
|
|
$basic_inset = id(new AphrontFormInsetView())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setTitle(pht('Basics'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Name'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setName('name')
|
|
|
|
->setValue($project_name)
|
|
|
|
->setError($e_name)
|
2013-05-04 03:33:49 +02:00
|
|
|
->setCaption(pht('A name like "Thrift" but not "Thrift releases".')))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Repository'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue(
|
|
|
|
$this
|
|
|
|
->getReleephProject()
|
|
|
|
->loadPhabricatorRepository()
|
|
|
|
->getName()))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Arc Project'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue(
|
|
|
|
$this->getReleephProject()->loadArcanistProject()->getName()))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Releeph Project PHID'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue(
|
|
|
|
$this->getReleephProject()->getPHID()))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Trunk'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue($trunk_branch)
|
|
|
|
->setName('trunkBranch')
|
|
|
|
->setError($e_trunk_branch))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Release counter'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue($release_counter)
|
|
|
|
->setName('releaseCounter')
|
|
|
|
->setCaption(
|
2013-05-10 20:38:35 +02:00
|
|
|
pht("Used by the command line branch cutter's %%N field")))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextAreaControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Pick Instructions'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue($pick_failure_instructions)
|
|
|
|
->setName('pickFailureInstructions')
|
|
|
|
->setCaption(
|
2013-05-04 03:33:49 +02:00
|
|
|
pht("Instructions for pick failures, which will be used " .
|
|
|
|
"in emails generated by failed picks")))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextAreaControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Tests paths'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setValue(implode("\n", $test_paths))
|
|
|
|
->setName('testPaths')
|
|
|
|
->setCaption(
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('List of strings that all test files contain in their path '.
|
2013-03-15 12:28:43 +01:00
|
|
|
'in this project. One string per line. '.
|
2013-05-04 03:33:49 +02:00
|
|
|
'Examples: \'__tests__\', \'/javatests/\'...')));
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
$pushers_inset = id(new AphrontFormInsetView())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setTitle(pht('Pushers'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('Pushers are allowed to approve Releeph requests to be committed. '.
|
2013-03-15 12:28:43 +01:00
|
|
|
'to this project\'s branches. If you leave this blank then anyone '.
|
2013-05-04 03:33:49 +02:00
|
|
|
'is allowed to approve requests.'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Pushers'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setName('pushers')
|
|
|
|
->setDatasource('/typeahead/common/users/')
|
|
|
|
->setValue($pusher_tokens));
|
|
|
|
|
|
|
|
$commit_author_inset = $this->buildCommitAuthorInset($commit_author);
|
|
|
|
|
|
|
|
// Build the Template inset
|
2013-05-24 21:37:53 +02:00
|
|
|
$help_markup = PhabricatorMarkupEngine::renderOneObject(
|
|
|
|
id(new PhabricatorMarkupOneOff())->setContent($this->getBranchHelpText()),
|
|
|
|
'default',
|
|
|
|
$request->getUser());
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
$branch_template_input = id(new AphrontFormTextControl())
|
|
|
|
->setName('branchTemplate')
|
|
|
|
->setValue($branch_template)
|
|
|
|
->setLabel('Template')
|
|
|
|
->setError($e_branch_template)
|
|
|
|
->setCaption(
|
2013-05-04 03:33:49 +02:00
|
|
|
pht("Leave this blank to use your installation's default."));
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
$branch_template_preview = id(new ReleephBranchPreviewView())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Preview'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->addControl('template', $branch_template_input)
|
|
|
|
->addStatic('arcProjectID', $arc_project_id)
|
|
|
|
->addStatic('isSymbolic', false)
|
|
|
|
->addStatic('projectName', $this->getReleephProject()->getName());
|
|
|
|
|
|
|
|
$template_inset = id(new AphrontFormInsetView())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setTitle(pht('Branch Cutting'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild(
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('Provide a pattern for creating new branches.'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->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($commit_author_inset)
|
|
|
|
->appendChild($template_inset);
|
|
|
|
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->addCancelButton('/releeph/project/')
|
2013-05-04 03:33:49 +02:00
|
|
|
->setValue(pht('Save')));
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
$panel = id(new AphrontPanelView())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setHeader(pht('Edit Releeph Project'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild($form)
|
|
|
|
->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
|
|
|
array($error_view, $panel),
|
2013-05-04 03:33:49 +02:00
|
|
|
array('title' => pht('Edit Releeph Project')));
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2013-05-04 03:33:49 +02:00
|
|
|
// pht?
|
2013-03-15 12:28:43 +01:00
|
|
|
$help_markup = hsprintf(<<<EOTEXT
|
|
|
|
When your project's release engineers run <tt>arc releeph</tt>, they will be
|
|
|
|
listed as the <strong>committer</strong> 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 <strong>author</strong> of the
|
|
|
|
commits it makes:
|
|
|
|
EOTEXT
|
|
|
|
, $vcs_name);
|
|
|
|
|
|
|
|
$trunk = $this->getReleephProject()->getTrunkBranch();
|
|
|
|
|
|
|
|
$options = array(
|
|
|
|
array(
|
|
|
|
'value' => ReleephProject::COMMIT_AUTHOR_FROM_DIFF,
|
2013-05-04 03:33:49 +02:00
|
|
|
'label' => pht('Original Author'),
|
2013-03-15 12:28:43 +01:00
|
|
|
'caption' =>
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('The author of the original commit in: %s.', $trunk),
|
2013-03-15 12:28:43 +01:00
|
|
|
),
|
|
|
|
array(
|
|
|
|
'value' => ReleephProject::COMMIT_AUTHOR_REQUESTOR,
|
2013-05-04 03:33:49 +02:00
|
|
|
'label' => pht('Requestor'),
|
2013-03-15 12:28:43 +01:00
|
|
|
'caption' =>
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('The person who requested that this code go into the release.'),
|
2013-03-15 12:28:43 +01:00
|
|
|
),
|
|
|
|
array(
|
|
|
|
'value' => ReleephProject::COMMIT_AUTHOR_NONE,
|
2013-05-04 03:33:49 +02:00
|
|
|
'label' => pht('None'),
|
2013-03-15 12:28:43 +01:00
|
|
|
'caption' =>
|
2013-05-04 03:33:49 +02:00
|
|
|
pht('Only record the default committer information.'),
|
2013-03-15 12:28:43 +01:00
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!$current) {
|
|
|
|
$current = ReleephProject::COMMIT_AUTHOR_FROM_DIFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
$control = id(new AphrontFormRadioButtonControl())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setLabel(pht('Author'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->setName('commitWithAuthor')
|
|
|
|
->setValue($current);
|
|
|
|
|
|
|
|
foreach ($options as $dict) {
|
|
|
|
$control->addButton($dict['value'], $dict['label'], $dict['caption']);
|
|
|
|
}
|
|
|
|
|
|
|
|
return id(new AphrontFormInsetView())
|
2013-05-04 03:33:49 +02:00
|
|
|
->setTitle(pht('Authors'))
|
2013-03-15 12:28:43 +01:00
|
|
|
->appendChild($help_markup)
|
|
|
|
->appendChild($control);
|
|
|
|
}
|
|
|
|
|
2013-05-24 21:37:53 +02:00
|
|
|
private function getBranchHelpText() {
|
|
|
|
return <<<EOTEXT
|
|
|
|
|
|
|
|
==== Interpolations ====
|
|
|
|
|
|
|
|
| Code | Meaning
|
|
|
|
| ----- | -------
|
|
|
|
| `%P` | The name of your project, with spaces changed to "-".
|
|
|
|
| `%p` | Like %P, but all lowercase.
|
|
|
|
| `%Y` | The four digit year associated with the branch date.
|
|
|
|
| `%m` | The two digit month.
|
|
|
|
| `%d` | The two digit day.
|
|
|
|
| `%v` | The handle of the commit where the branch was cut ("rXYZa4b3c2d1").
|
|
|
|
| `%V` | The abbreviated commit id where the branch was cut ("a4b3c2d1").
|
|
|
|
| `%..` | Any other sequence interpreted by `strftime()`.
|
|
|
|
| `%%` | A literal percent sign.
|
|
|
|
|
|
|
|
|
|
|
|
==== Tips for Branch Templates ====
|
|
|
|
|
|
|
|
Use a directory to separate your release branches from other branches:
|
|
|
|
|
|
|
|
lang=none
|
|
|
|
releases/%Y-%M-%d-%v
|
|
|
|
=> releases/2012-30-16-rHERGE32cd512a52b7
|
|
|
|
|
|
|
|
Include a second hierarchy if you share your repository with other projects:
|
|
|
|
|
|
|
|
lang=none
|
|
|
|
releases/%P/%p-release-%Y%m%d-%V
|
|
|
|
=> releases/Tintin/tintin-release-20121116-32cd512a52b7
|
|
|
|
|
|
|
|
Keep your branch names simple, avoiding strange punctuation, most of which is
|
|
|
|
forbidden or escaped anyway:
|
|
|
|
|
|
|
|
lang=none, counterexample
|
|
|
|
releases//..clown-releases..//`date --iso=seconds`-$(sudo halt)
|
|
|
|
|
|
|
|
Include the date early in your template, in an order which sorts properly:
|
|
|
|
|
|
|
|
lang=none
|
|
|
|
releases/%Y%m%d-%v
|
|
|
|
=> releases/20121116-rHERGE32cd512a52b7 (good!)
|
|
|
|
|
|
|
|
releases/%V-%m.%d.%Y
|
|
|
|
=> releases/32cd512a52b7-11.16.2012 (awful!)
|
|
|
|
|
|
|
|
|
|
|
|
EOTEXT;
|
|
|
|
}
|
|
|
|
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|