mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Allow users to manage package dominion rules
Summary: Ref T10939. This adds UI, transactions, etc, to adjust dominion rules. Test Plan: - Read documentation. - Changed dominion rules. - Created packages on `/` ("A") and `/x` ("B") with "Auto Review: Review". - Touched `/x`. - Verified that A and B were added with strong dominion. - Verified that only B was added when A was set to weak dominion. - Viewed file in Diffusion, saw correct ownership with strong/weak dominion rules. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10939 Differential Revision: https://secure.phabricator.com/D15936
This commit is contained in:
parent
6cb2bde48d
commit
809c7bf996
10 changed files with 135 additions and 5 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_owners.owners_package
|
||||||
|
ADD dominion VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
||||||
|
UPDATE {$NAMESPACE}_owners.owners_package
|
||||||
|
SET dominion = 'strong' WHERE dominion = '';
|
|
@ -1723,6 +1723,10 @@ final class DifferentialTransactionEditor
|
||||||
$paths[] = $path_prefix.'/'.$changeset->getFilename();
|
$paths[] = $path_prefix.'/'.$changeset->getFilename();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save the affected paths; we'll use them later to query Owners. This
|
||||||
|
// uses the un-expanded paths.
|
||||||
|
$this->affectedPaths = $paths;
|
||||||
|
|
||||||
// Mark this as also touching all parent paths, so you can see all pending
|
// Mark this as also touching all parent paths, so you can see all pending
|
||||||
// changes to any file within a directory.
|
// changes to any file within a directory.
|
||||||
$all_paths = array();
|
$all_paths = array();
|
||||||
|
@ -1733,9 +1737,6 @@ final class DifferentialTransactionEditor
|
||||||
}
|
}
|
||||||
$all_paths = array_keys($all_paths);
|
$all_paths = array_keys($all_paths);
|
||||||
|
|
||||||
// Save the affected paths; we'll use them later to query Owners.
|
|
||||||
$this->affectedPaths = $all_paths;
|
|
||||||
|
|
||||||
$path_ids =
|
$path_ids =
|
||||||
PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
|
PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
|
||||||
$all_paths);
|
$all_paths);
|
||||||
|
|
|
@ -184,6 +184,13 @@ final class PhabricatorOwnersDetailController
|
||||||
}
|
}
|
||||||
$view->addProperty(pht('Owners'), $owner_list);
|
$view->addProperty(pht('Owners'), $owner_list);
|
||||||
|
|
||||||
|
|
||||||
|
$dominion = $package->getDominion();
|
||||||
|
$dominion_map = PhabricatorOwnersPackage::getDominionOptionsMap();
|
||||||
|
$spec = idx($dominion_map, $dominion, array());
|
||||||
|
$name = idx($spec, 'short', $dominion);
|
||||||
|
$view->addProperty(pht('Dominion'), $name);
|
||||||
|
|
||||||
$auto = $package->getAutoReview();
|
$auto = $package->getAutoReview();
|
||||||
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
|
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
|
||||||
$spec = idx($autoreview_map, $auto, array());
|
$spec = idx($autoreview_map, $auto, array());
|
||||||
|
|
|
@ -87,6 +87,9 @@ EOTEXT
|
||||||
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
|
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
|
||||||
$autoreview_map = ipull($autoreview_map, 'name');
|
$autoreview_map = ipull($autoreview_map, 'name');
|
||||||
|
|
||||||
|
$dominion_map = PhabricatorOwnersPackage::getDominionOptionsMap();
|
||||||
|
$dominion_map = ipull($dominion_map, 'name');
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
id(new PhabricatorTextEditField())
|
id(new PhabricatorTextEditField())
|
||||||
->setKey('name')
|
->setKey('name')
|
||||||
|
@ -103,6 +106,16 @@ EOTEXT
|
||||||
->setDatasource(new PhabricatorProjectOrUserDatasource())
|
->setDatasource(new PhabricatorProjectOrUserDatasource())
|
||||||
->setIsCopyable(true)
|
->setIsCopyable(true)
|
||||||
->setValue($object->getOwnerPHIDs()),
|
->setValue($object->getOwnerPHIDs()),
|
||||||
|
id(new PhabricatorSelectEditField())
|
||||||
|
->setKey('dominion')
|
||||||
|
->setLabel(pht('Dominion'))
|
||||||
|
->setDescription(
|
||||||
|
pht('Change package dominion rules.'))
|
||||||
|
->setTransactionType(
|
||||||
|
PhabricatorOwnersPackageTransaction::TYPE_DOMINION)
|
||||||
|
->setIsCopyable(true)
|
||||||
|
->setValue($object->getDominion())
|
||||||
|
->setOptions($dominion_map),
|
||||||
id(new PhabricatorSelectEditField())
|
id(new PhabricatorSelectEditField())
|
||||||
->setKey('autoReview')
|
->setKey('autoReview')
|
||||||
->setLabel(pht('Auto Review'))
|
->setLabel(pht('Auto Review'))
|
||||||
|
|
|
@ -21,6 +21,7 @@ final class PhabricatorOwnersPackageTransactionEditor
|
||||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
|
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
|
||||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
|
$types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
|
||||||
$types[] = PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW;
|
$types[] = PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW;
|
||||||
|
$types[] = PhabricatorOwnersPackageTransaction::TYPE_DOMINION;
|
||||||
|
|
||||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
|
@ -50,6 +51,8 @@ final class PhabricatorOwnersPackageTransactionEditor
|
||||||
return $object->getStatus();
|
return $object->getStatus();
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
||||||
return $object->getAutoReview();
|
return $object->getAutoReview();
|
||||||
|
case PhabricatorOwnersPackageTransaction::TYPE_DOMINION:
|
||||||
|
return $object->getDominion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +65,7 @@ final class PhabricatorOwnersPackageTransactionEditor
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
|
case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION:
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
||||||
|
case PhabricatorOwnersPackageTransaction::TYPE_DOMINION:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
||||||
$new = $xaction->getNewValue();
|
$new = $xaction->getNewValue();
|
||||||
|
@ -120,6 +124,9 @@ final class PhabricatorOwnersPackageTransactionEditor
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
||||||
$object->setAutoReview($xaction->getNewValue());
|
$object->setAutoReview($xaction->getNewValue());
|
||||||
return;
|
return;
|
||||||
|
case PhabricatorOwnersPackageTransaction::TYPE_DOMINION:
|
||||||
|
$object->setDominion($xaction->getNewValue());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||||
|
@ -135,6 +142,7 @@ final class PhabricatorOwnersPackageTransactionEditor
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
|
case PhabricatorOwnersPackageTransaction::TYPE_AUDITING:
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
case PhabricatorOwnersPackageTransaction::TYPE_STATUS:
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
case PhabricatorOwnersPackageTransaction::TYPE_AUTOREVIEW:
|
||||||
|
case PhabricatorOwnersPackageTransaction::TYPE_DOMINION:
|
||||||
return;
|
return;
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
|
case PhabricatorOwnersPackageTransaction::TYPE_OWNERS:
|
||||||
$old = $xaction->getOldValue();
|
$old = $xaction->getOldValue();
|
||||||
|
@ -249,6 +257,26 @@ final class PhabricatorOwnersPackageTransactionEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorOwnersPackageTransaction::TYPE_DOMINION:
|
||||||
|
$map = PhabricatorOwnersPackage::getDominionOptionsMap();
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
|
||||||
|
if (empty($map[$new])) {
|
||||||
|
$valid = array_keys($map);
|
||||||
|
|
||||||
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Invalid'),
|
||||||
|
pht(
|
||||||
|
'Dominion setting "%s" is not valid. '.
|
||||||
|
'Valid settings are: %s.',
|
||||||
|
$new,
|
||||||
|
implode(', ', $valid)),
|
||||||
|
$xaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
case PhabricatorOwnersPackageTransaction::TYPE_PATHS:
|
||||||
if (!$xactions) {
|
if (!$xactions) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -351,6 +351,7 @@ final class PhabricatorOwnersPackageQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
$packages = $this->controlResults;
|
$packages = $this->controlResults;
|
||||||
|
$weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK;
|
||||||
|
|
||||||
$matches = array();
|
$matches = array();
|
||||||
foreach ($packages as $package_id => $package) {
|
foreach ($packages as $package_id => $package) {
|
||||||
|
@ -373,6 +374,7 @@ final class PhabricatorOwnersPackageQuery
|
||||||
if ($best_match && $include) {
|
if ($best_match && $include) {
|
||||||
$matches[$package_id] = array(
|
$matches[$package_id] = array(
|
||||||
'strength' => $best_match,
|
'strength' => $best_match,
|
||||||
|
'weak' => ($package->getDominion() == $weak_dominion),
|
||||||
'package' => $package,
|
'package' => $package,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -381,6 +383,18 @@ final class PhabricatorOwnersPackageQuery
|
||||||
$matches = isort($matches, 'strength');
|
$matches = isort($matches, 'strength');
|
||||||
$matches = array_reverse($matches);
|
$matches = array_reverse($matches);
|
||||||
|
|
||||||
|
$first_id = null;
|
||||||
|
foreach ($matches as $package_id => $match) {
|
||||||
|
if ($first_id === null) {
|
||||||
|
$first_id = $package_id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($match['weak']) {
|
||||||
|
unset($matches[$package_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return array_values(ipull($matches, 'package'));
|
return array_values(ipull($matches, 'package'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ final class PhabricatorOwnersPackage
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
protected $dominion;
|
||||||
|
|
||||||
private $paths = self::ATTACHABLE;
|
private $paths = self::ATTACHABLE;
|
||||||
private $owners = self::ATTACHABLE;
|
private $owners = self::ATTACHABLE;
|
||||||
|
@ -51,6 +52,7 @@ final class PhabricatorOwnersPackage
|
||||||
return id(new PhabricatorOwnersPackage())
|
return id(new PhabricatorOwnersPackage())
|
||||||
->setAuditingEnabled(0)
|
->setAuditingEnabled(0)
|
||||||
->setAutoReview(self::AUTOREVIEW_NONE)
|
->setAutoReview(self::AUTOREVIEW_NONE)
|
||||||
|
->setDominion(self::DOMINION_STRONG)
|
||||||
->setViewPolicy($view_policy)
|
->setViewPolicy($view_policy)
|
||||||
->setEditPolicy($edit_policy)
|
->setEditPolicy($edit_policy)
|
||||||
->attachPaths(array())
|
->attachPaths(array())
|
||||||
|
@ -83,6 +85,19 @@ final class PhabricatorOwnersPackage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getDominionOptionsMap() {
|
||||||
|
return array(
|
||||||
|
self::DOMINION_STRONG => array(
|
||||||
|
'name' => pht('Strong (Control All Paths)'),
|
||||||
|
'short' => pht('Strong'),
|
||||||
|
),
|
||||||
|
self::DOMINION_WEAK => array(
|
||||||
|
'name' => pht('Weak (Control Unowned Paths)'),
|
||||||
|
'short' => pht('Weak'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
// This information is better available from the history table.
|
// This information is better available from the history table.
|
||||||
|
@ -97,6 +112,7 @@ final class PhabricatorOwnersPackage
|
||||||
'mailKey' => 'bytes20',
|
'mailKey' => 'bytes20',
|
||||||
'status' => 'text32',
|
'status' => 'text32',
|
||||||
'autoReview' => 'text32',
|
'autoReview' => 'text32',
|
||||||
|
'dominion' => 'text32',
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
@ -193,7 +209,7 @@ final class PhabricatorOwnersPackage
|
||||||
foreach (array_chunk(array_keys($fragments), 128) as $chunk) {
|
foreach (array_chunk(array_keys($fragments), 128) as $chunk) {
|
||||||
$rows[] = queryfx_all(
|
$rows[] = queryfx_all(
|
||||||
$conn,
|
$conn,
|
||||||
'SELECT pkg.id, "strong" dominion, p.excluded, p.path
|
'SELECT pkg.id, pkg.dominion, p.excluded, p.path
|
||||||
FROM %T pkg JOIN %T p ON p.packageID = pkg.id
|
FROM %T pkg JOIN %T p ON p.packageID = pkg.id
|
||||||
WHERE p.path IN (%Ls) %Q',
|
WHERE p.path IN (%Ls) %Q',
|
||||||
$package->getTableName(),
|
$package->getTableName(),
|
||||||
|
|
|
@ -11,6 +11,7 @@ final class PhabricatorOwnersPackageTransaction
|
||||||
const TYPE_PATHS = 'owners.paths';
|
const TYPE_PATHS = 'owners.paths';
|
||||||
const TYPE_STATUS = 'owners.status';
|
const TYPE_STATUS = 'owners.status';
|
||||||
const TYPE_AUTOREVIEW = 'owners.autoreview';
|
const TYPE_AUTOREVIEW = 'owners.autoreview';
|
||||||
|
const TYPE_DOMINION = 'owners.dominion';
|
||||||
|
|
||||||
public function getApplicationName() {
|
public function getApplicationName() {
|
||||||
return 'owners';
|
return 'owners';
|
||||||
|
@ -156,6 +157,18 @@ final class PhabricatorOwnersPackageTransaction
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$old,
|
$old,
|
||||||
$new);
|
$new);
|
||||||
|
case self::TYPE_DOMINION:
|
||||||
|
$map = PhabricatorOwnersPackage::getDominionOptionsMap();
|
||||||
|
$map = ipull($map, 'short');
|
||||||
|
|
||||||
|
$old = idx($map, $old, $old);
|
||||||
|
$new = idx($map, $new, $new);
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s adjusted package dominion rules from "%s" to "%s".',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$old,
|
||||||
|
$new);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::getTitle();
|
return parent::getTitle();
|
||||||
|
|
|
@ -45,6 +45,38 @@ belonging to the package when you look at them in Diffusion, or look at changes
|
||||||
which affect them in Diffusion or Differential.
|
which affect them in Diffusion or Differential.
|
||||||
|
|
||||||
|
|
||||||
|
Dominion
|
||||||
|
========
|
||||||
|
|
||||||
|
The **Dominion** option allows you to control how ownership cascades when
|
||||||
|
multiple packages own a path. The dominion rules are:
|
||||||
|
|
||||||
|
**Strong Dominion.** This is the default. In this mode, the package will always
|
||||||
|
own all files matching its configured paths, even if another package also owns
|
||||||
|
them.
|
||||||
|
|
||||||
|
For example, if the package owns `a/`, it will always own `a/b/c.z` even if
|
||||||
|
another package owns `a/b/`. In this case, both packages will own `a/b/c.z`.
|
||||||
|
|
||||||
|
This mode prevents users from stealing files away from the package by defining
|
||||||
|
more narrow ownership rules in new packages, but enforces hierarchical
|
||||||
|
ownership rules.
|
||||||
|
|
||||||
|
**Weak Dominion.** In this mode, the package will only own files which do not
|
||||||
|
match a more specific path in another package.
|
||||||
|
|
||||||
|
For example, if the package owns `a/` but another package owns `a/b/`, the
|
||||||
|
package will no longer consider `a/b/c.z` to be a file it owns because another
|
||||||
|
package matches the path with a more specific rule.
|
||||||
|
|
||||||
|
This mode lets you to define rules without implicit hierarchical ownership,
|
||||||
|
but allows users to steal files away from a package by defining a more
|
||||||
|
specific package.
|
||||||
|
|
||||||
|
For more details on files which match multiple packages, see
|
||||||
|
"Files in Multiple Packages", below.
|
||||||
|
|
||||||
|
|
||||||
Auto Review
|
Auto Review
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
@ -93,4 +125,6 @@ configuration, these files are part of three packages: "iOS Application",
|
||||||
"Android Application", and "Design Assets".
|
"Android Application", and "Design Assets".
|
||||||
|
|
||||||
(You can use an "exclude" rule if you want to make a different package with a
|
(You can use an "exclude" rule if you want to make a different package with a
|
||||||
more specific claim the owner of a file or subdirectory.)
|
more specific claim the owner of a file or subdirectory. You can also change
|
||||||
|
the **Dominion** setting for a package to let it give up ownership of paths
|
||||||
|
owned by another package.)
|
||||||
|
|
Loading…
Reference in a new issue