Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15505456
D15742.id37936.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
20 KB
Referenced Files
None
Subscribers
None
D15742.id37936.diff
View Options
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
@@ -779,6 +779,7 @@
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
'DiffusionRepositoryTestAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php',
'DiffusionRepositoryURIsIndexEngineExtension' => 'applications/diffusion/engineextension/DiffusionRepositoryURIsIndexEngineExtension.php',
+ 'DiffusionRepositoryURIsManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php',
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
'DiffusionResolveRefsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionResolveRefsConduitAPIMethod.php',
'DiffusionResolveUserQuery' => 'applications/diffusion/query/DiffusionResolveUserQuery.php',
@@ -3216,6 +3217,7 @@
'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php',
'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php',
'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php',
+ 'PhabricatorRepositoryURI' => 'applications/repository/storage/PhabricatorRepositoryURI.php',
'PhabricatorRepositoryURIIndex' => 'applications/repository/storage/PhabricatorRepositoryURIIndex.php',
'PhabricatorRepositoryURINormalizer' => 'applications/repository/data/PhabricatorRepositoryURINormalizer.php',
'PhabricatorRepositoryURINormalizerTestCase' => 'applications/repository/data/__tests__/PhabricatorRepositoryURINormalizerTestCase.php',
@@ -4978,6 +4980,7 @@
'DiffusionRepositoryTag' => 'Phobject',
'DiffusionRepositoryTestAutomationController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryURIsIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
+ 'DiffusionRepositoryURIsManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRequest' => 'Phobject',
'DiffusionResolveRefsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionResolveUserQuery' => 'Phobject',
@@ -7875,6 +7878,7 @@
'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorRepositoryType' => 'Phobject',
+ 'PhabricatorRepositoryURI' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryURIIndex' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryURINormalizer' => 'Phobject',
'PhabricatorRepositoryURINormalizerTestCase' => 'PhabricatorTestCase',
diff --git a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php
@@ -0,0 +1,126 @@
+<?php
+
+final class DiffusionRepositoryURIsManagementPanel
+ extends DiffusionRepositoryManagementPanel {
+
+ const PANELKEY = 'uris';
+
+ public function getManagementPanelLabel() {
+ return pht('Clone / Fetch / Mirror');
+ }
+
+ public function getManagementPanelOrder() {
+ return 300;
+ }
+
+ public function buildManagementPanelContent() {
+ $repository = $this->getRepository();
+ $viewer = $this->getViewer();
+
+ $repository->attachURIs(array());
+ $uris = $repository->getURIs();
+
+ Javelin::initBehavior('phabricator-tooltips');
+ $rows = array();
+ foreach ($uris as $uri) {
+
+ $uri_name = $uri->getDisplayURI();
+
+ if ($uri->getIsDisabled()) {
+ $status_icon = 'fa-times grey';
+ } else {
+ $status_icon = 'fa-check green';
+ }
+
+ $uri_status = id(new PHUIIconView())->setIcon($status_icon);
+
+ switch ($uri->getEffectiveIOType()) {
+ case PhabricatorRepositoryURI::IO_OBSERVE:
+ $io_icon = 'fa-download green';
+ $io_label = pht('Observe');
+ break;
+ case PhabricatorRepositoryURI::IO_MIRROR:
+ $io_icon = 'fa-upload green';
+ $io_label = pht('Mirror');
+ break;
+ case PhabricatorRepositoryURI::IO_NONE:
+ $io_icon = 'fa-times grey';
+ $io_label = pht('No I/O');
+ break;
+ case PhabricatorRepositoryURI::IO_READ:
+ $io_icon = 'fa-folder blue';
+ $io_label = pht('Read Only');
+ break;
+ case PhabricatorRepositoryURI::IO_READWRITE:
+ $io_icon = 'fa-folder-open blue';
+ $io_label = pht('Read/Write');
+ break;
+ }
+
+ $uri_io = array(
+ id(new PHUIIconView())->setIcon($io_icon),
+ ' ',
+ $io_label,
+ );
+
+ switch ($uri->getEffectiveDisplayType()) {
+ case PhabricatorRepositoryURI::DISPLAY_NEVER:
+ $display_icon = 'fa-eye-slash grey';
+ $display_label = pht('Hidden');
+ break;
+ case PhabricatorRepositoryURI::DISPLAY_ALWAYS:
+ $display_icon = 'fa-eye green';
+ $display_label = pht('Visible');
+ break;
+ }
+
+ $uri_display = array(
+ id(new PHUIIconView())->setIcon($display_icon),
+ ' ',
+ $display_label,
+ );
+
+ $rows[] = array(
+ $uri_status,
+ $uri_name,
+ $uri_io,
+ $uri_display,
+ );
+ }
+
+ $table = id(new AphrontTableView($rows))
+ ->setNoDataString(pht('This repository has no URIs.'))
+ ->setHeaders(
+ array(
+ null,
+ pht('URI'),
+ pht('I/O'),
+ pht('Display'),
+ ))
+ ->setColumnClasses(
+ array(
+ null,
+ 'pri wide',
+ null,
+ null,
+ ));
+
+ $doc_href = PhabricatorEnv::getDoclink(
+ 'Diffusion User Guide: Repository URIs');
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Repository URIs'))
+ ->addActionLink(
+ id(new PHUIButtonView())
+ ->setIcon('fa-book')
+ ->setHref($doc_href)
+ ->setTag('a')
+ ->setText(pht('Documentation')));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
+ ->setTable($table);
+ }
+
+}
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -63,10 +63,12 @@
private $commitCount = self::ATTACHABLE;
private $mostRecentCommit = self::ATTACHABLE;
private $projectPHIDs = self::ATTACHABLE;
+ private $uris = self::ATTACHABLE;
private $clusterWriteLock;
private $clusterWriteVersion;
+
public static function initializeNewRepository(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
@@ -2266,6 +2268,98 @@
return $client;
}
+/* -( Repository URIs )---------------------------------------------------- */
+
+
+ public function attachURIs(array $uris) {
+ $custom_map = array();
+ foreach ($uris as $key => $uri) {
+ $builtin_key = $uri->getRepositoryURIBuiltinKey();
+ if ($builtin_key !== null) {
+ $custom_map[$builtin_key] = $key;
+ }
+ }
+
+ $builtin_uris = $this->newBuiltinURIs();
+ $seen_builtins = array();
+ foreach ($builtin_uris as $builtin_uri) {
+ $builtin_key = $builtin_uri->getRepositoryURIBuiltinKey();
+ $seen_builtins[$builtin_key] = true;
+
+ // If this builtin URI is disabled, don't attach it and remove the
+ // persisted version if it exists.
+ if ($builtin_uri->getIsDisabled()) {
+ if (isset($custom_map[$builtin_key])) {
+ unset($uris[$custom_map[$builtin_key]]);
+ }
+ continue;
+ }
+
+ // If we don't have a persisted version of the URI, add the builtin
+ // version.
+ if (empty($custom_map[$builtin_key])) {
+ $uris[] = $builtin_uri;
+ }
+ }
+
+ // Remove any builtins which no longer exist.
+ foreach ($custom_map as $builtin_key => $key) {
+ if (empty($seen_builtins[$builtin_key])) {
+ unset($uris[$key]);
+ }
+ }
+
+ $this->uris = $uris;
+
+ return $this;
+ }
+
+ public function getURIs() {
+ return $this->assertAttached($this->uris);
+ }
+
+ protected function newBuiltinURIs() {
+ $has_callsign = ($this->getCallsign() !== null);
+ $has_shortname = ($this->getRepositorySlug() !== null);
+
+ $identifier_map = array(
+ PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_CALLSIGN => $has_callsign,
+ PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_SHORTNAME => $has_shortname,
+ PhabricatorRepositoryURI::BUILTIN_IDENTIFIER_ID => true,
+ );
+
+ $allow_http = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
+
+ $base_uri = PhabricatorEnv::getURI('/');
+ $base_uri = new PhutilURI($base_uri);
+ $has_https = ($base_uri->getProtocol() == 'https');
+ $has_https = ($has_https && $allow_http);
+
+ $has_http = !PhabricatorEnv::getEnvConfig('security.require-https');
+ $has_http = ($has_http && $allow_http);
+
+ // TODO: Maybe allow users to disable this by default somehow?
+ $has_ssh = true;
+
+ $protocol_map = array(
+ PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH => $has_ssh,
+ PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS => $has_https,
+ PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP => $has_http,
+ );
+
+ $uris = array();
+ foreach ($protocol_map as $protocol => $proto_supported) {
+ foreach ($identifier_map as $identifier => $id_supported) {
+ $uris[] = PhabricatorRepositoryURI::initializeNewURI($this)
+ ->setBuiltinProtocol($protocol)
+ ->setBuiltinIdentifier($identifier)
+ ->setIsDisabled(!$proto_supported || !$id_supported);
+ }
+ }
+
+ return $uris;
+ }
+
/* -( Cluster Synchronization )-------------------------------------------- */
diff --git a/src/applications/repository/storage/PhabricatorRepositoryURI.php b/src/applications/repository/storage/PhabricatorRepositoryURI.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/storage/PhabricatorRepositoryURI.php
@@ -0,0 +1,300 @@
+<?php
+
+final class PhabricatorRepositoryURI
+ extends PhabricatorRepositoryDAO {
+
+ protected $repositoryPHID;
+ protected $uri;
+ protected $builtinProtocol;
+ protected $builtinIdentifier;
+ protected $credentialPHID;
+ protected $ioType;
+ protected $displayType;
+ protected $isDisabled;
+
+ private $repository = self::ATTACHABLE;
+
+ const BUILTIN_PROTOCOL_SSH = 'ssh';
+ const BUILTIN_PROTOCOL_HTTP = 'http';
+ const BUILTIN_PROTOCOL_HTTPS = 'https';
+
+ const BUILTIN_IDENTIFIER_ID = 'id';
+ const BUILTIN_IDENTIFIER_SHORTNAME = 'shortname';
+ const BUILTIN_IDENTIFIER_CALLSIGN = 'callsign';
+
+ const DISPLAY_DEFAULT = 'default';
+ const DISPLAY_NEVER = 'never';
+ const DISPLAY_ALWAYS = 'always';
+
+ const IO_DEFAULT = 'default';
+ const IO_OBSERVE = 'observe';
+ const IO_MIRROR = 'mirror';
+ const IO_NONE = 'none';
+ const IO_READ = 'read';
+ const IO_READWRITE = 'readwrite';
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'uri' => 'text',
+ 'builtinProtocol' => 'text32?',
+ 'builtinIdentifier' => 'text32?',
+ 'ioType' => 'text32',
+ 'displayType' => 'text32',
+ 'isDisabled' => 'bool',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_builtin' => array(
+ 'columns' => array(
+ 'repositoryPHID',
+ 'builtinProtocol',
+ 'builtinIdentifier',
+ ),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public static function initializeNewURI(PhabricatorRepository $repository) {
+ return id(new self())
+ ->attachRepository($repository)
+ ->setRepositoryPHID($repository->getPHID())
+ ->setIoType(self::IO_DEFAULT)
+ ->setDisplayType(self::DISPLAY_DEFAULT)
+ ->setIsDisabled(0);
+ }
+
+ public function attachRepository(PhabricatorRepository $repository) {
+ $this->repository = $repository;
+ return $this;
+ }
+
+ public function getRepository() {
+ return $this->assertAttached($this->repository);
+ }
+
+ public function getRepositoryURIBuiltinKey() {
+ if (!$this->getBuiltinProtocol()) {
+ return null;
+ }
+
+ $parts = array(
+ $this->getBuiltinProtocol(),
+ $this->getBuiltinIdentifier(),
+ );
+ return implode('.', $parts);
+ }
+
+ public function isBuiltin() {
+ return (bool)$this->getBuiltinProtocol();
+ }
+
+ public function getEffectiveDisplayType() {
+ $display = $this->getDisplayType();
+
+ if ($display != self::IO_DEFAULT) {
+ return $display;
+ }
+
+ switch ($this->getEffectiveIOType()) {
+ case self::IO_MIRROR:
+ case self::IO_OBSERVE:
+ return self::DISPLAY_NEVER;
+ case self::IO_NONE:
+ if ($this->isBuiltin()) {
+ return self::DISPLAY_NEVER;
+ } else {
+ return self::DISPLAY_ALWAYS;
+ }
+ case self::IO_READ:
+ case self::IO_READWRITE:
+ // By default, only show the "best" version of the builtin URI, not the
+ // other redundant versions.
+ if ($this->isBuiltin()) {
+ $repository = $this->getRepository();
+ $other_uris = $repository->getURIs();
+
+ $identifier_value = array(
+ self::BUILTIN_IDENTIFIER_CALLSIGN => 3,
+ self::BUILTIN_IDENTIFIER_SHORTNAME => 2,
+ self::BUILTIN_IDENTIFIER_ID => 1,
+ );
+
+ $have_identifiers = array();
+ foreach ($other_uris as $other_uri) {
+ if ($other_uri->getIsDisabled()) {
+ continue;
+ }
+
+ $identifier = $other_uri->getBuiltinIdentifier();
+ if (!$identifier) {
+ continue;
+ }
+
+ $have_identifiers[$identifier] = $identifier_value[$identifier];
+ }
+
+ $best_identifier = max($have_identifiers);
+ $this_identifier = $identifier_value[$this->getBuiltinIdentifier()];
+
+ if ($this_identifier < $best_identifier) {
+ return self::DISPLAY_NEVER;
+ }
+ }
+
+ return self::DISPLAY_ALWAYS;
+ }
+ }
+
+
+ public function getEffectiveIOType() {
+ $io = $this->getIoType();
+
+ if ($io != self::IO_DEFAULT) {
+ return $io;
+ }
+
+ if ($this->isBuiltin()) {
+ $repository = $this->getRepository();
+ $other_uris = $repository->getURIs();
+
+ $any_observe = false;
+ foreach ($other_uris as $other_uri) {
+ if ($other_uri->getIoType() == self::IO_OBSERVE) {
+ $any_observe = true;
+ break;
+ }
+ }
+
+ if ($any_observe) {
+ return self::IO_READ;
+ } else {
+ return self::IO_READWRITE;
+ }
+ }
+
+ return self::IO_IGNORE;
+ }
+
+
+ public function getDisplayURI() {
+ $uri = new PhutilURI($this->getURI());
+
+ $protocol = $this->getForcedProtocol();
+ if ($protocol) {
+ $uri->setProtocol($protocol);
+ }
+
+ $user = $this->getForcedUser();
+ if ($user) {
+ $uri->setUser($user);
+ }
+
+ $host = $this->getForcedHost();
+ if ($host) {
+ $uri->setDomain($host);
+ }
+
+ $port = $this->getForcedPort();
+ if ($port) {
+ $uri->setPort($port);
+ }
+
+ $path = $this->getForcedPath();
+ if ($path) {
+ $uri->setPath($path);
+ }
+
+ return $uri;
+ }
+
+ private function getForcedProtocol() {
+ switch ($this->getBuiltinProtocol()) {
+ case self::BUILTIN_PROTOCOL_SSH:
+ return 'ssh';
+ case self::BUILTIN_PROTOCOL_HTTP:
+ return 'http';
+ case self::BUILTIN_PROTOCOL_HTTPS:
+ return 'https';
+ default:
+ return null;
+ }
+ }
+
+ private function getForcedUser() {
+ switch ($this->getBuiltinProtocol()) {
+ case self::BUILTIN_PROTOCOL_SSH:
+ return PhabricatorEnv::getEnvConfig('diffusion.ssh-user');
+ default:
+ return null;
+ }
+ }
+
+ private function getForcedHost() {
+ $phabricator_uri = PhabricatorEnv::getURI('/');
+ $phabricator_uri = new PhutilURI($phabricator_uri);
+
+ $phabricator_host = $phabricator_uri->getDomain();
+
+ switch ($this->getBuiltinProtocol()) {
+ case self::BUILTIN_PROTOCOL_SSH:
+ $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
+ if ($ssh_host !== null) {
+ return $ssh_host;
+ }
+ return $phabricator_host;
+ case self::BUILTIN_PROTOCOL_HTTP:
+ case self::BUILTIN_PROTOCOL_HTTPS:
+ return $phabricator_host;
+ default:
+ return null;
+ }
+ }
+
+ private function getForcedPort() {
+ switch ($this->getBuiltinProtocol()) {
+ case self::BUILTIN_PROTOCOL_SSH:
+ return PhabricatorEnv::getEnvConfig('diffusion.ssh-port');
+ case self::BUILTIN_PROTOCOL_HTTP:
+ case self::BUILTIN_PROTOCOL_HTTPS:
+ default:
+ return null;
+ }
+ }
+
+ private function getForcedPath() {
+ if (!$this->isBuiltin()) {
+ return null;
+ }
+
+ $repository = $this->getRepository();
+
+ $id = $repository->getID();
+ $callsign = $repository->getCallsign();
+ $short_name = $repository->getRepositorySlug();
+
+ $clone_name = $repository->getCloneName();
+
+ if ($repository->isGit()) {
+ $suffix = '.git';
+ } else if ($repository->isHg()) {
+ $suffix = '/';
+ } else {
+ $suffix = '';
+ }
+
+ switch ($this->getBuiltinIdentifier()) {
+ case self::BUILTIN_IDENTIFIER_ID:
+ return "/diffusion/{$id}/{$clone_name}{$suffix}";
+ case self::BUILTIN_IDENTIFIER_SHORTNAME:
+ return "/source/{$short_name}{$suffix}";
+ case self::BUILTIN_IDENTIFIER_CALLSIGN:
+ return "/diffusion/{$callsign}/{$clone_name}{$suffix}";
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/docs/user/userguide/diffusion_uris.diviner b/src/docs/user/userguide/diffusion_uris.diviner
new file mode 100644
--- /dev/null
+++ b/src/docs/user/userguide/diffusion_uris.diviner
@@ -0,0 +1,47 @@
+@title Diffusion User Guide: URIs
+@group userguide
+
+Guide to configuring repository URIs for fetching, cloning and mirroring.
+
+Overview
+========
+
+WARNING: This document describes a feature which is still under development,
+and is not necessarily accurate or complete.
+
+Phabricator can host, observe, mirror, and proxy repositories. For example,
+here are some supported use cases:
+
+**Host Repositories**: Phabricator can host repositories locally. Phabricator
+maintains the writable master version of the repository, and you can push and
+pull the repository. This is the most straightforward kind of repository
+configuration, and similar to repositories on other services like GitHub or
+Bitbucket.
+
+**Observe Repositories**: Phabricator can create a copy of an repository which
+is hosted elsewhere (like GitHub or Bitbucket) and track updates to the remote
+repository. This will create a read-only copy of the repository in Phabricator.
+
+**Mirror Repositories**: Phabricator can publish any repository to mirrors,
+updating the mirrors as changes are made to the repository. This works with
+both local hosted repositories and remote repositories that Phabricator is
+observing.
+
+**Proxy Repositories**: If you are observing a repository, you can allow users
+to read Phabricator's copy of the repository. Phabricator supports granular
+read permissions, so this can let you open a private repository up a little
+bit in a flexible way.
+
+**Import Repositories**: If you have a repository elsewhere that you want to
+host on Phabricator, you can observe the remote repository first, then turn
+the tracking off once the repository fully synchronizes. This allows you to
+copy an existing repository and begin hosting it in Phabricator.
+
+You can also import repositories by creating an empty hosted repository and
+then pushing everything to the repository directly.
+
+You configure the behavior of a Phabricator repository by adding and
+configuring URIs and marking them to be fetched from, mirrored to, clonable,
+and so on. By configuring all the URIs that a repository should interact with
+and expose to users, you configure the read, write, and mirroring behavior
+of the repository.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Apr 16, 3:08 AM (1 w, 15 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7702989
Default Alt Text
D15742.id37936.diff (20 KB)
Attached To
Mode
D15742: Rough in the new custom URI panel
Attached
Detach File
Event Timeline
Log In to Comment