Page MenuHomePhabricator

D15742.diff
No OneTemporary

D15742.diff

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

Mime Type
text/plain
Expires
May 9 2024, 7:54 PM (5 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6276098
Default Alt Text
D15742.diff (20 KB)

Event Timeline