mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 01:10:58 +01:00
Merge branch 'master' of github.com:facebook/phabricator
This commit is contained in:
commit
00d9aae894
66 changed files with 2049 additions and 1092 deletions
1
bin/ssh-auth-key
Symbolic link
1
bin/ssh-auth-key
Symbolic link
|
@ -0,0 +1 @@
|
|||
../scripts/ssh/ssh-auth-key.php
|
|
@ -559,13 +559,6 @@ return array(
|
|||
// to have.
|
||||
'auth.sessions.conduit' => 5,
|
||||
|
||||
// Set this true to enable the Settings -> SSH Public Keys panel, which will
|
||||
// allow users to associated SSH public keys with their accounts. This is only
|
||||
// really useful if you're setting up services over SSH and want to use
|
||||
// Phabricator for authentication; in most situations you can leave this
|
||||
// disabled.
|
||||
'auth.sshkeys.enabled' => false,
|
||||
|
||||
// If true, email addresses must be verified (by clicking a link in an
|
||||
// email) before a user can login. By default, verification is optional
|
||||
// unless 'auth.email-domains' is nonempty (see below).
|
||||
|
|
5
resources/sql/patches/20131025.repopush.sql
Normal file
5
resources/sql/patches/20131025.repopush.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE {$NAMESPACE}_repository.repository
|
||||
ADD COLUMN pushPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin;
|
||||
|
||||
UPDATE {$NAMESPACE}_repository.repository
|
||||
SET pushPolicy = 'users' WHERE pushPolicy = '';
|
8
resources/sql/patches/20131026.commitstatus.sql
Normal file
8
resources/sql/patches/20131026.commitstatus.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
ALTER TABLE {$NAMESPACE}_repository.repository_commit
|
||||
ADD COLUMN importStatus INT UNSIGNED NOT NULL COLLATE utf8_bin;
|
||||
|
||||
UPDATE {$NAMESPACE}_repository.repository_commit
|
||||
SET importStatus = 15;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_repository.repository_commit
|
||||
ADD KEY (repositoryID, importStatus);
|
8
resources/sshd/phabricator-ssh-hook.sh
Executable file
8
resources/sshd/phabricator-ssh-hook.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
###
|
||||
### WARNING: This feature is new and experimental. Use it at your own risk!
|
||||
###
|
||||
|
||||
ROOT=/INSECURE/devtools/phabricator
|
||||
exec "$ROOT/bin/ssh-auth" $@
|
24
resources/sshd/sshd_config.example
Normal file
24
resources/sshd/sshd_config.example
Normal file
|
@ -0,0 +1,24 @@
|
|||
###
|
||||
### WARNING: This feature is new and experimental. Use it at your own risk!
|
||||
###
|
||||
|
||||
# You must have OpenSSHD 6.2 or newer; support for AuthorizedKeysCommand was
|
||||
# added in this version.
|
||||
|
||||
Port 2222
|
||||
AuthorizedKeysCommand /etc/phabricator-ssh-hook.sh
|
||||
AuthorizedKeysCommandUser some-unprivileged-user
|
||||
|
||||
# You may need to tweak these options, but mostly they just turn off everything
|
||||
# dangerous.
|
||||
|
||||
Protocol 2
|
||||
PermitRootLogin no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
PrintMotd no
|
||||
PrintLastLog no
|
||||
PasswordAuthentication no
|
||||
AuthorizedKeysFile none
|
||||
|
||||
PidFile /var/run/sshd-phabricator.pid
|
61
scripts/ssh/ssh-auth-key.php
Executable file
61
scripts/ssh/ssh-auth-key.php
Executable file
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$cert = file_get_contents('php://stdin');
|
||||
|
||||
if (!$cert) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$parts = preg_split('/\s+/', $cert);
|
||||
if (count($parts) < 2) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
list($type, $body) = $parts;
|
||||
|
||||
$user_dao = new PhabricatorUser();
|
||||
$ssh_dao = new PhabricatorUserSSHKey();
|
||||
$conn_r = $user_dao->establishConnection('r');
|
||||
|
||||
$row = queryfx_one(
|
||||
$conn_r,
|
||||
'SELECT userName FROM %T u JOIN %T ssh ON u.phid = ssh.userPHID
|
||||
WHERE ssh.keyType = %s AND ssh.keyBody = %s',
|
||||
$user_dao->getTableName(),
|
||||
$ssh_dao->getTableName(),
|
||||
$type,
|
||||
$body);
|
||||
|
||||
if (!$row) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$user = idx($row, 'userName');
|
||||
|
||||
if (!$user) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!PhabricatorUser::validateUsername($user)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$bin = $root.'/bin/ssh-exec';
|
||||
$cmd = csprintf('%s --phabricator-ssh-user %s', $bin, $user);
|
||||
// This is additional escaping for the SSH 'command="..."' string.
|
||||
$cmd = addcslashes($cmd, '"\\');
|
||||
|
||||
$options = array(
|
||||
'command="'.$cmd.'"',
|
||||
'no-port-forwarding',
|
||||
'no-X11-forwarding',
|
||||
'no-agent-forwarding',
|
||||
'no-pty',
|
||||
);
|
||||
|
||||
echo implode(',', $options);
|
||||
exit(0);
|
|
@ -4,58 +4,45 @@
|
|||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$cert = file_get_contents('php://stdin');
|
||||
|
||||
if (!$cert) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$parts = preg_split('/\s+/', $cert);
|
||||
if (count($parts) < 2) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
list($type, $body) = $parts;
|
||||
|
||||
$user_dao = new PhabricatorUser();
|
||||
$ssh_dao = new PhabricatorUserSSHKey();
|
||||
$conn_r = $user_dao->establishConnection('r');
|
||||
|
||||
$row = queryfx_one(
|
||||
$rows = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT userName FROM %T u JOIN %T ssh ON u.phid = ssh.userPHID
|
||||
WHERE ssh.keyType = %s AND ssh.keyBody = %s',
|
||||
'SELECT userName, keyBody, keyType FROM %T u JOIN %T ssh
|
||||
ON u.phid = ssh.userPHID',
|
||||
$user_dao->getTableName(),
|
||||
$ssh_dao->getTableName(),
|
||||
$type,
|
||||
$body);
|
||||
|
||||
if (!$row) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$user = idx($row, 'userName');
|
||||
|
||||
if (!$user) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!PhabricatorUser::validateUsername($user)) {
|
||||
exit(1);
|
||||
}
|
||||
$ssh_dao->getTableName());
|
||||
|
||||
$bin = $root.'/bin/ssh-exec';
|
||||
$cmd = csprintf('%s --phabricator-ssh-user %s', $bin, $user);
|
||||
// This is additional escaping for the SSH 'command="..."' string.
|
||||
$cmd = str_replace('"', '\\"', $cmd);
|
||||
foreach ($rows as $row) {
|
||||
$user = $row['userName'];
|
||||
|
||||
$options = array(
|
||||
'command="'.$cmd.'"',
|
||||
'no-port-forwarding',
|
||||
'no-X11-forwarding',
|
||||
'no-agent-forwarding',
|
||||
'no-pty',
|
||||
);
|
||||
$cmd = csprintf('%s --phabricator-ssh-user %s', $bin, $user);
|
||||
// This is additional escaping for the SSH 'command="..."' string.
|
||||
$cmd = addcslashes($cmd, '"\\');
|
||||
|
||||
echo implode(',', $options);
|
||||
// Strip out newlines and other nonsense from the key type and key body.
|
||||
|
||||
$type = $row['keyType'];
|
||||
$type = preg_replace('@[\x00-\x20]+@', '', $type);
|
||||
|
||||
$key = $row['keyBody'];
|
||||
$key = preg_replace('@[\x00-\x20]+@', '', $key);
|
||||
|
||||
|
||||
$options = array(
|
||||
'command="'.$cmd.'"',
|
||||
'no-port-forwarding',
|
||||
'no-X11-forwarding',
|
||||
'no-agent-forwarding',
|
||||
'no-pty',
|
||||
);
|
||||
$options = implode(',', $options);
|
||||
|
||||
$lines[] = $options.' '.$type.' '.$key."\n";
|
||||
}
|
||||
|
||||
echo implode('', $lines);
|
||||
exit(0);
|
||||
|
|
|
@ -4,29 +4,25 @@
|
|||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$original_command = getenv('SSH_ORIGINAL_COMMAND');
|
||||
$original_argv = id(new PhutilShellLexer())->splitArguments($original_command);
|
||||
$argv = array_merge($argv, $original_argv);
|
||||
|
||||
// First, figure out the authenticated user.
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline('receive SSH requests');
|
||||
$args->setSynopsis(<<<EOSYNOPSIS
|
||||
**ssh-exec** --phabricator-ssh-user __user__ __commmand__ [__options__]
|
||||
**ssh-exec** --phabricator-ssh-user __user__ [--ssh-command __commmand__]
|
||||
Receive SSH requests.
|
||||
|
||||
EOSYNOPSIS
|
||||
);
|
||||
|
||||
// NOTE: Do NOT parse standard arguments. Arguments are coming from a remote
|
||||
// client over SSH, and they should not be able to execute "--xprofile",
|
||||
// "--recon", etc.
|
||||
|
||||
$args->parsePartial(
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'phabricator-ssh-user',
|
||||
'param' => 'username',
|
||||
),
|
||||
array(
|
||||
'name' => 'ssh-command',
|
||||
'param' => 'command',
|
||||
),
|
||||
));
|
||||
|
||||
try {
|
||||
|
@ -46,24 +42,36 @@ try {
|
|||
throw new Exception("You have been exiled.");
|
||||
}
|
||||
|
||||
$workflows = array(
|
||||
new ConduitSSHWorkflow(),
|
||||
);
|
||||
|
||||
// This duplicates logic in parseWorkflows(), but allows us to raise more
|
||||
// concise/relevant exceptions when the client is a remote SSH.
|
||||
$remain = $args->getUnconsumedArgumentVector();
|
||||
if (empty($remain)) {
|
||||
throw new Exception("No interactive logins.");
|
||||
if ($args->getArg('ssh-command')) {
|
||||
$original_command = $args->getArg('ssh-command');
|
||||
} else {
|
||||
$command = head($remain);
|
||||
$workflow_names = mpull($workflows, 'getName', 'getName');
|
||||
if (empty($workflow_names[$command])) {
|
||||
throw new Exception("Invalid command.");
|
||||
}
|
||||
$original_command = getenv('SSH_ORIGINAL_COMMAND');
|
||||
}
|
||||
|
||||
$workflow = $args->parseWorkflows($workflows);
|
||||
// Now, rebuild the original command.
|
||||
$original_argv = id(new PhutilShellLexer())
|
||||
->splitArguments($original_command);
|
||||
if (!$original_argv) {
|
||||
throw new Exception("No interactive logins.");
|
||||
}
|
||||
$command = head($original_argv);
|
||||
array_unshift($original_argv, 'phabricator-ssh-exec');
|
||||
|
||||
$original_args = new PhutilArgumentParser($original_argv);
|
||||
|
||||
$workflows = array(
|
||||
new ConduitSSHWorkflow(),
|
||||
|
||||
new DiffusionSSHGitUploadPackWorkflow(),
|
||||
new DiffusionSSHGitReceivePackWorkflow(),
|
||||
);
|
||||
|
||||
$workflow_names = mpull($workflows, 'getName', 'getName');
|
||||
if (empty($workflow_names[$command])) {
|
||||
throw new Exception("Invalid command.");
|
||||
}
|
||||
|
||||
$workflow = $original_args->parseWorkflows($workflows);
|
||||
$workflow->setUser($user);
|
||||
|
||||
$sock_stdin = fopen('php://stdin', 'r');
|
||||
|
@ -76,16 +84,24 @@ try {
|
|||
throw new Exception("Unable to open stdout.");
|
||||
}
|
||||
|
||||
$sock_stderr = fopen('php://stderr', 'w');
|
||||
if (!$sock_stderr) {
|
||||
throw new Exception("Unable to open stderr.");
|
||||
}
|
||||
|
||||
$socket_channel = new PhutilSocketChannel(
|
||||
$sock_stdin,
|
||||
$sock_stdout);
|
||||
$error_channel = new PhutilSocketChannel(null, $sock_stderr);
|
||||
$metrics_channel = new PhutilMetricsChannel($socket_channel);
|
||||
$workflow->setIOChannel($metrics_channel);
|
||||
$workflow->setErrorChannel($error_channel);
|
||||
|
||||
$err = $workflow->execute($args);
|
||||
$err = $workflow->execute($original_args);
|
||||
|
||||
$metrics_channel->flush();
|
||||
$error_channel->flush();
|
||||
} catch (Exception $ex) {
|
||||
echo "phabricator-ssh-exec: ".$ex->getMessage()."\n";
|
||||
fwrite(STDERR, "phabricator-ssh-exec: ".$ex->getMessage()."\n");
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
@ -449,6 +449,11 @@ phutil_register_library_map(array(
|
|||
'DiffusionBrowseResultSet' => 'applications/diffusion/data/DiffusionBrowseResultSet.php',
|
||||
'DiffusionBrowseSearchController' => 'applications/diffusion/controller/DiffusionBrowseSearchController.php',
|
||||
'DiffusionBrowseTableView' => 'applications/diffusion/view/DiffusionBrowseTableView.php',
|
||||
'DiffusionCapabilityCreateRepositories' => 'applications/diffusion/capability/DiffusionCapabilityCreateRepositories.php',
|
||||
'DiffusionCapabilityDefaultEdit' => 'applications/diffusion/capability/DiffusionCapabilityDefaultEdit.php',
|
||||
'DiffusionCapabilityDefaultPush' => 'applications/diffusion/capability/DiffusionCapabilityDefaultPush.php',
|
||||
'DiffusionCapabilityDefaultView' => 'applications/diffusion/capability/DiffusionCapabilityDefaultView.php',
|
||||
'DiffusionCapabilityPush' => 'applications/diffusion/capability/DiffusionCapabilityPush.php',
|
||||
'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
|
||||
'DiffusionCommentListView' => 'applications/diffusion/view/DiffusionCommentListView.php',
|
||||
'DiffusionCommentView' => 'applications/diffusion/view/DiffusionCommentView.php',
|
||||
|
@ -475,6 +480,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php',
|
||||
'DiffusionGitRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php',
|
||||
'DiffusionGitRequest' => 'applications/diffusion/request/DiffusionGitRequest.php',
|
||||
'DiffusionGitResponse' => 'applications/diffusion/response/DiffusionGitResponse.php',
|
||||
'DiffusionGitStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionGitStableCommitNameQuery.php',
|
||||
'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php',
|
||||
'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
|
||||
|
@ -504,6 +510,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionRenameHistoryQuery' => 'applications/diffusion/query/DiffusionRenameHistoryQuery.php',
|
||||
'DiffusionRepositoryController' => 'applications/diffusion/controller/DiffusionRepositoryController.php',
|
||||
'DiffusionRepositoryCreateController' => 'applications/diffusion/controller/DiffusionRepositoryCreateController.php',
|
||||
'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php',
|
||||
'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php',
|
||||
'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php',
|
||||
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
|
||||
|
@ -511,6 +518,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
|
||||
'DiffusionRepositoryEditDeleteController' => 'applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php',
|
||||
'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php',
|
||||
'DiffusionRepositoryEditHostingController' => 'applications/diffusion/controller/DiffusionRepositoryEditHostingController.php',
|
||||
'DiffusionRepositoryEditLocalController' => 'applications/diffusion/controller/DiffusionRepositoryEditLocalController.php',
|
||||
'DiffusionRepositoryEditMainController' => 'applications/diffusion/controller/DiffusionRepositoryEditMainController.php',
|
||||
'DiffusionRepositoryEditPolicyController' => 'applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php',
|
||||
|
@ -519,6 +527,10 @@ phutil_register_library_map(array(
|
|||
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
|
||||
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
|
||||
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
|
||||
'DiffusionSSHGitReceivePackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php',
|
||||
'DiffusionSSHGitUploadPackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php',
|
||||
'DiffusionSSHGitWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitWorkflow.php',
|
||||
'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php',
|
||||
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
|
||||
'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php',
|
||||
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php',
|
||||
|
@ -1628,10 +1640,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRepositoryCommitSearchIndexer' => 'applications/repository/search/PhabricatorRepositoryCommitSearchIndexer.php',
|
||||
'PhabricatorRepositoryConfigOptions' => 'applications/repository/PhabricatorRepositoryConfigOptions.php',
|
||||
'PhabricatorRepositoryController' => 'applications/repository/controller/PhabricatorRepositoryController.php',
|
||||
'PhabricatorRepositoryCreateController' => 'applications/repository/controller/PhabricatorRepositoryCreateController.php',
|
||||
'PhabricatorRepositoryDAO' => 'applications/repository/storage/PhabricatorRepositoryDAO.php',
|
||||
'PhabricatorRepositoryDiscoveryEngine' => 'applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php',
|
||||
'PhabricatorRepositoryEditController' => 'applications/repository/controller/PhabricatorRepositoryEditController.php',
|
||||
'PhabricatorRepositoryEditor' => 'applications/repository/editor/PhabricatorRepositoryEditor.php',
|
||||
'PhabricatorRepositoryEngine' => 'applications/repository/engine/PhabricatorRepositoryEngine.php',
|
||||
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryGitCommitChangeParserWorker.php',
|
||||
|
@ -1862,6 +1872,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorUserTestCase' => 'applications/people/storage/__tests__/PhabricatorUserTestCase.php',
|
||||
'PhabricatorUserTitleField' => 'applications/people/customfield/PhabricatorUserTitleField.php',
|
||||
'PhabricatorUserTransaction' => 'applications/people/storage/PhabricatorUserTransaction.php',
|
||||
'PhabricatorVCSResponse' => 'applications/repository/response/PhabricatorVCSResponse.php',
|
||||
'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php',
|
||||
'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php',
|
||||
'PhabricatorWorkerArchiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerArchiveTask.php',
|
||||
|
@ -2634,6 +2645,11 @@ phutil_register_library_map(array(
|
|||
'DiffusionBrowseMainController' => 'DiffusionBrowseController',
|
||||
'DiffusionBrowseSearchController' => 'DiffusionBrowseController',
|
||||
'DiffusionBrowseTableView' => 'DiffusionView',
|
||||
'DiffusionCapabilityCreateRepositories' => 'PhabricatorPolicyCapability',
|
||||
'DiffusionCapabilityDefaultEdit' => 'PhabricatorPolicyCapability',
|
||||
'DiffusionCapabilityDefaultPush' => 'PhabricatorPolicyCapability',
|
||||
'DiffusionCapabilityDefaultView' => 'PhabricatorPolicyCapability',
|
||||
'DiffusionCapabilityPush' => 'PhabricatorPolicyCapability',
|
||||
'DiffusionChangeController' => 'DiffusionController',
|
||||
'DiffusionCommentListView' => 'AphrontView',
|
||||
'DiffusionCommentView' => 'AphrontView',
|
||||
|
@ -2658,6 +2674,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
|
||||
'DiffusionGitRawDiffQuery' => 'DiffusionRawDiffQuery',
|
||||
'DiffusionGitRequest' => 'DiffusionRequest',
|
||||
'DiffusionGitResponse' => 'AphrontResponse',
|
||||
'DiffusionGitStableCommitNameQuery' => 'DiffusionStableCommitNameQuery',
|
||||
'DiffusionHistoryController' => 'DiffusionController',
|
||||
'DiffusionHistoryTableView' => 'DiffusionView',
|
||||
|
@ -2681,6 +2698,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionRemarkupRule' => 'PhabricatorRemarkupRuleObject',
|
||||
'DiffusionRepositoryController' => 'DiffusionController',
|
||||
'DiffusionRepositoryCreateController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryDefaultController' => 'DiffusionController',
|
||||
'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController',
|
||||
|
@ -2688,6 +2706,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionRepositoryEditController' => 'DiffusionController',
|
||||
'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditEncodingController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditHostingController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditLocalController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditMainController' => 'DiffusionRepositoryEditController',
|
||||
'DiffusionRepositoryEditPolicyController' => 'DiffusionRepositoryEditController',
|
||||
|
@ -2697,6 +2716,10 @@ phutil_register_library_map(array(
|
|||
0 => 'DiffusionController',
|
||||
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
|
||||
),
|
||||
'DiffusionSSHGitReceivePackWorkflow' => 'DiffusionSSHGitWorkflow',
|
||||
'DiffusionSSHGitUploadPackWorkflow' => 'DiffusionSSHGitWorkflow',
|
||||
'DiffusionSSHGitWorkflow' => 'DiffusionSSHWorkflow',
|
||||
'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow',
|
||||
'DiffusionSetupException' => 'AphrontUsageException',
|
||||
'DiffusionStableCommitNameQuery' => 'DiffusionQuery',
|
||||
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
||||
|
@ -3955,10 +3978,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorRepositoryCommitSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
|
||||
'PhabricatorRepositoryConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorRepositoryController' => 'PhabricatorController',
|
||||
'PhabricatorRepositoryCreateController' => 'PhabricatorRepositoryController',
|
||||
'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorRepositoryDiscoveryEngine' => 'PhabricatorRepositoryEngine',
|
||||
'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
|
||||
'PhabricatorRepositoryEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
||||
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||
|
@ -4192,6 +4213,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorUserTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorUserTitleField' => 'PhabricatorUserCustomField',
|
||||
'PhabricatorUserTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorVCSResponse' => 'AphrontResponse',
|
||||
'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask',
|
||||
'PhabricatorWorkerArchiveTask' => 'PhabricatorWorkerTask',
|
||||
'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO',
|
||||
|
|
|
@ -49,6 +49,10 @@ abstract class AphrontResponse {
|
|||
return $this->responseCode;
|
||||
}
|
||||
|
||||
public function getHTTPResponseMessage() {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function setFrameable($frameable) {
|
||||
$this->frameable = $frameable;
|
||||
return $this;
|
||||
|
|
|
@ -25,13 +25,13 @@ abstract class AphrontHTTPSink {
|
|||
* @param int Numeric HTTP status code.
|
||||
* @return void
|
||||
*/
|
||||
final public function writeHTTPStatus($code) {
|
||||
final public function writeHTTPStatus($code, $message = '') {
|
||||
if (!preg_match('/^\d{3}$/', $code)) {
|
||||
throw new Exception("Malformed HTTP status code '{$code}'!");
|
||||
}
|
||||
|
||||
$code = (int)$code;
|
||||
$this->emitHTTPStatus($code);
|
||||
$this->emitHTTPStatus($code, $message);
|
||||
}
|
||||
|
||||
|
||||
|
@ -103,7 +103,9 @@ abstract class AphrontHTTPSink {
|
|||
$response->getHeaders(),
|
||||
$response->getCacheHeaders());
|
||||
|
||||
$this->writeHTTPStatus($response->getHTTPResponseCode());
|
||||
$this->writeHTTPStatus(
|
||||
$response->getHTTPResponseCode(),
|
||||
$response->getHTTPResponseMessage());
|
||||
$this->writeHeaders($all_headers);
|
||||
$this->writeData($response_string);
|
||||
}
|
||||
|
@ -112,7 +114,7 @@ abstract class AphrontHTTPSink {
|
|||
/* -( Emitting the Response )---------------------------------------------- */
|
||||
|
||||
|
||||
abstract protected function emitHTTPStatus($code);
|
||||
abstract protected function emitHTTPStatus($code, $message = '');
|
||||
abstract protected function emitHeader($name, $value);
|
||||
abstract protected function emitData($data);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ final class AphrontIsolatedHTTPSink extends AphrontHTTPSink {
|
|||
private $headers;
|
||||
private $data;
|
||||
|
||||
protected function emitHTTPStatus($code) {
|
||||
protected function emitHTTPStatus($code, $message = '') {
|
||||
$this->status = $code;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
*/
|
||||
final class AphrontPHPHTTPSink extends AphrontHTTPSink {
|
||||
|
||||
protected function emitHTTPStatus($code) {
|
||||
protected function emitHTTPStatus($code, $message = '') {
|
||||
if ($code != 200) {
|
||||
header("HTTP/1.0 {$code}");
|
||||
$header = "HTTP/1.0 {$code}";
|
||||
if (strlen($message)) {
|
||||
$header .= " {$message}";
|
||||
}
|
||||
header($header);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ abstract class PhabricatorController extends AphrontController {
|
|||
return PhabricatorUserEmail::isEmailVerificationRequired();
|
||||
}
|
||||
|
||||
final public function willBeginExecution() {
|
||||
public function willBeginExecution() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
if ($request->getUser()) {
|
||||
|
|
|
@ -31,7 +31,7 @@ final class ConduitSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
throw new Exception("Invalid JSON input.");
|
||||
}
|
||||
|
||||
$params = idx($raw_params, 'params', array());
|
||||
$params = idx($raw_params, 'params', '[]');
|
||||
$params = json_decode($params, true);
|
||||
$metadata = idx($params, '__conduit__', array());
|
||||
unset($params['__conduit__']);
|
||||
|
|
|
@ -162,6 +162,8 @@ final class PhabricatorSetupCheckExtraConfig extends PhabricatorSetupCheck {
|
|||
'been migrated.'),
|
||||
'differential.custom-remarkup-rules' => $markup_reason,
|
||||
'differential.custom-remarkup-block-rules' => $markup_reason,
|
||||
'auth.sshkeys.enabled' => pht(
|
||||
'SSH keys are now actually useful, so they are always enabled.'),
|
||||
);
|
||||
|
||||
return $ancient_config;
|
||||
|
|
|
@ -29,21 +29,6 @@ final class PhabricatorAuthenticationConfigOptions
|
|||
pht(
|
||||
"Maximum number of simultaneous Conduit sessions each user is ".
|
||||
"permitted to have.")),
|
||||
$this->newOption('auth.sshkeys.enabled', 'bool', false)
|
||||
->setBoolOptions(
|
||||
array(
|
||||
pht("Enable SSH key storage"),
|
||||
pht("Disable SSH key storage")))
|
||||
->setSummary(
|
||||
pht("Allow users to associate SSH keys with their accounts."))
|
||||
->setDescription(
|
||||
pht(
|
||||
"Set this true to enable the Settings -> SSH Public Keys panel, ".
|
||||
"which will allow users to associated SSH public keys with their ".
|
||||
"accounts. This is only really useful if you're setting up ".
|
||||
"services over SSH and want to use Phabricator for ".
|
||||
"authentication; in most situations you can leave this ".
|
||||
"disabled.")),
|
||||
$this->newOption('auth.require-email-verification', 'bool', false)
|
||||
->setBoolOptions(
|
||||
array(
|
||||
|
|
|
@ -75,8 +75,17 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication {
|
|||
'(?P<edit>remote)/' => 'DiffusionRepositoryCreateController',
|
||||
'local/' => 'DiffusionRepositoryEditLocalController',
|
||||
'delete/' => 'DiffusionRepositoryEditDeleteController',
|
||||
'hosting/' => 'DiffusionRepositoryEditHostingController',
|
||||
'(?P<serve>serve)/' => 'DiffusionRepositoryEditHostingController',
|
||||
),
|
||||
),
|
||||
|
||||
// NOTE: This must come after the rule above; it just gives us a
|
||||
// catch-all for serving repositories over HTTP. We must accept
|
||||
// requests without the trailing "/" because SVN commands don't
|
||||
// necessarily include it.
|
||||
'(?P<callsign>[A-Z]+)(/|$).*' => 'DiffusionRepositoryDefaultController',
|
||||
|
||||
'inline/' => array(
|
||||
'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',
|
||||
'preview/(?P<phid>[^/]+)/' =>
|
||||
|
@ -103,4 +112,19 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication {
|
|||
return 0.120;
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
DiffusionCapabilityDefaultView::CAPABILITY => array(
|
||||
),
|
||||
DiffusionCapabilityDefaultEdit::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
DiffusionCapabilityDefaultPush::CAPABILITY => array(
|
||||
),
|
||||
DiffusionCapabilityCreateRepositories::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCapabilityCreateRepositories
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'diffusion.create';
|
||||
|
||||
public function getCapabilityKey() {
|
||||
return self::CAPABILITY;
|
||||
}
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Create Repositories');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to create new repositories.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCapabilityDefaultEdit
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'diffusion.default.edit';
|
||||
|
||||
public function getCapabilityKey() {
|
||||
return self::CAPABILITY;
|
||||
}
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Edit Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCapabilityDefaultPush
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'diffusion.default.push';
|
||||
|
||||
public function getCapabilityKey() {
|
||||
return self::CAPABILITY;
|
||||
}
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Push Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCapabilityDefaultView
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'diffusion.default.view';
|
||||
|
||||
public function getCapabilityKey() {
|
||||
return self::CAPABILITY;
|
||||
}
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default View Policy');
|
||||
}
|
||||
|
||||
public function shouldAllowPublicPolicySetting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCapabilityPush
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'diffusion.push';
|
||||
|
||||
public function getCapabilityKey() {
|
||||
return self::CAPABILITY;
|
||||
}
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Push');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to push to this repository.');
|
||||
}
|
||||
|
||||
}
|
|
@ -76,7 +76,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$this->auditAuthorityPHIDs =
|
||||
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
|
||||
|
||||
|
||||
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
|
||||
$changesets = null;
|
||||
if ($is_foreign) {
|
||||
|
@ -154,10 +153,14 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
$hard_limit = 1000;
|
||||
|
||||
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
|
||||
$drequest);
|
||||
$change_query->setLimit($hard_limit + 1);
|
||||
$changes = $change_query->loadChanges();
|
||||
if ($commit->isImported()) {
|
||||
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
|
||||
$drequest);
|
||||
$change_query->setLimit($hard_limit + 1);
|
||||
$changes = $change_query->loadChanges();
|
||||
} else {
|
||||
$changes = array();
|
||||
}
|
||||
|
||||
$was_limited = (count($changes) > $hard_limit);
|
||||
if ($was_limited) {
|
||||
|
@ -206,37 +209,29 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
}
|
||||
|
||||
if ($bad_commit) {
|
||||
$error_panel = new AphrontErrorView();
|
||||
$error_panel->setTitle(pht('Bad Commit'));
|
||||
$error_panel->appendChild($bad_commit['description']);
|
||||
|
||||
$content[] = $error_panel;
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Bad Commit'),
|
||||
$bad_commit['description']);
|
||||
} else if ($is_foreign) {
|
||||
// Don't render anything else.
|
||||
} else if (!$commit->isImported()) {
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Still Importing...'),
|
||||
pht(
|
||||
'This commit is still importing. Changes will be visible once '.
|
||||
'the import finishes.'));
|
||||
} else if (!count($changes)) {
|
||||
$no_changes = new AphrontErrorView();
|
||||
$no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
||||
$no_changes->setTitle(pht('Not Yet Parsed'));
|
||||
// TODO: This can also happen with weird SVN changes that don't do
|
||||
// anything (or only alter properties?), although the real no-changes case
|
||||
// is extremely rare and might be impossible to produce organically. We
|
||||
// should probably write some kind of "Nothing Happened!" change into the
|
||||
// DB once we parse these changes so we can distinguish between
|
||||
// "not parsed yet" and "no changes".
|
||||
$no_changes->appendChild(
|
||||
pht("This commit hasn't been fully parsed yet (or doesn't affect any ".
|
||||
"paths)."));
|
||||
$content[] = $no_changes;
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Empty Commit'),
|
||||
pht(
|
||||
'This commit is empty and does not affect any paths.'));
|
||||
} else if ($was_limited) {
|
||||
$huge_commit = new AphrontErrorView();
|
||||
$huge_commit->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
||||
$huge_commit->setTitle(pht('Enormous Commit'));
|
||||
$huge_commit->appendChild(
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Enormous Commit'),
|
||||
pht(
|
||||
'This commit is enormous, and affects more than %d files. '.
|
||||
'Changes are not shown.',
|
||||
$hard_limit));
|
||||
$content[] = $huge_commit;
|
||||
} else {
|
||||
// The user has clicked "Show All Changes", and we should show all the
|
||||
// changes inline even if there are more than the soft limit.
|
||||
|
@ -253,6 +248,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
'href' => '?show_all=true',
|
||||
),
|
||||
pht('Show All Changes'));
|
||||
|
||||
$warning_view = id(new AphrontErrorView())
|
||||
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
|
||||
->setTitle('Very Large Commit')
|
||||
|
@ -1084,4 +1080,5 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
return $parser->processCorpus($corpus);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,188 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
|
||||
protected $diffusionRequest;
|
||||
|
||||
public function willBeginExecution() {
|
||||
$request = $this->getRequest();
|
||||
$uri = $request->getRequestURI();
|
||||
|
||||
// Check if this is a VCS request, e.g. from "git clone", "hg clone", or
|
||||
// "svn checkout". If it is, we jump off into repository serving code to
|
||||
// process the request.
|
||||
|
||||
$regex = '@^/diffusion/(?P<callsign>[A-Z]+)(/|$)@';
|
||||
$matches = null;
|
||||
if (preg_match($regex, (string)$uri, $matches)) {
|
||||
$vcs = null;
|
||||
|
||||
$content_type = $request->getHTTPHeader('Content-Type');
|
||||
|
||||
if ($request->getExists('__vcs__')) {
|
||||
// This is magic to make it easier for us to debug stuff by telling
|
||||
// users to run:
|
||||
//
|
||||
// curl http://example.phabricator.com/diffusion/X/?__vcs__=1
|
||||
//
|
||||
// ...to get a human-readable error.
|
||||
$vcs = $request->getExists('__vcs__');
|
||||
} else if ($request->getExists('service')) {
|
||||
$service = $request->getStr('service');
|
||||
// We get this initially for `info/refs`.
|
||||
// Git also gives us a User-Agent like "git/1.8.2.3".
|
||||
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||
} else if ($content_type == 'application/x-git-upload-pack-request') {
|
||||
// We get this for `git-upload-pack`.
|
||||
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||
} else if ($request->getExists('cmd')) {
|
||||
// Mercurial also sends an Accept header like
|
||||
// "application/mercurial-0.1", and a User-Agent like
|
||||
// "mercurial/proto-1.0".
|
||||
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
|
||||
} else {
|
||||
// Subversion also sends an initial OPTIONS request (vs GET/POST), and
|
||||
// has a User-Agent like "SVN/1.8.3 (x86_64-apple-darwin11.4.2)
|
||||
// serf/1.3.2".
|
||||
$dav = $request->getHTTPHeader('DAV');
|
||||
$dav = new PhutilURI($dav);
|
||||
if ($dav->getDomain() === 'subversion.tigris.org') {
|
||||
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
|
||||
}
|
||||
}
|
||||
|
||||
if ($vcs) {
|
||||
return $this->processVCSRequest($matches['callsign']);
|
||||
}
|
||||
}
|
||||
|
||||
parent::willBeginExecution();
|
||||
}
|
||||
|
||||
private function processVCSRequest($callsign) {
|
||||
|
||||
// TODO: Authenticate user.
|
||||
|
||||
$viewer = new PhabricatorUser();
|
||||
|
||||
$allow_public = PhabricatorEnv::getEnvConfig('policy.allow-public');
|
||||
if (!$allow_public) {
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('You must log in to access repositories.'));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->withCallsigns(array($callsign))
|
||||
->executeOne();
|
||||
if (!$repository) {
|
||||
return new PhabricatorVCSResponse(
|
||||
404,
|
||||
pht('No such repository exists.'));
|
||||
}
|
||||
} catch (PhabricatorPolicyException $ex) {
|
||||
if ($viewer->isLoggedIn()) {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('You do not have permission to access this repository.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to access this repository.'));
|
||||
}
|
||||
}
|
||||
|
||||
$is_push = !$this->isReadOnlyRequest($repository);
|
||||
|
||||
switch ($repository->getServeOverHTTP()) {
|
||||
case PhabricatorRepository::SERVE_READONLY:
|
||||
if ($is_push) {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('This repository is read-only over HTTP.'));
|
||||
}
|
||||
break;
|
||||
case PhabricatorRepository::SERVE_READWRITE:
|
||||
if ($is_push) {
|
||||
$can_push = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$repository,
|
||||
DiffusionCapabilityPush::CAPABILITY);
|
||||
if (!$can_push) {
|
||||
if ($viewer->isLoggedIn()) {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('You do not have permission to push to this repository.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to push to this repository.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PhabricatorRepository::SERVE_OFF:
|
||||
default:
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('This repository is not available over HTTP.'));
|
||||
}
|
||||
|
||||
switch ($repository->getVersionControlSystem()) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
return $this->serveGitRequest($repository);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new PhabricatorVCSResponse(
|
||||
999,
|
||||
pht('TODO: Implement meaningful responses.'));
|
||||
}
|
||||
|
||||
private function isReadOnlyRequest(
|
||||
PhabricatorRepository $repository) {
|
||||
$request = $this->getRequest();
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// TODO: This implementation is safe by default, but very incomplete.
|
||||
|
||||
switch ($repository->getVersionControlSystem()) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
$service = $request->getStr('service');
|
||||
$path = $this->getRequestDirectoryPath();
|
||||
// NOTE: Service names are the reverse of what you might expect, as they
|
||||
// are from the point of view of the server. The main read service is
|
||||
// "git-upload-pack", and the main write service is "git-receive-pack".
|
||||
|
||||
if ($method == 'GET' &&
|
||||
$path == '/info/refs' &&
|
||||
$service == 'git-upload-pack') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($path == '/git-upload-pack') {
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
$cmd = $request->getStr('cmd');
|
||||
switch ($cmd) {
|
||||
case 'capabilities':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SUBVERSION:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
if (isset($data['callsign'])) {
|
||||
$drequest = DiffusionRequest::newFromAphrontRequestDictionary(
|
||||
|
@ -213,5 +395,58 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
private function serveGitRequest(PhabricatorRepository $repository) {
|
||||
$request = $this->getRequest();
|
||||
|
||||
$request_path = $this->getRequestDirectoryPath();
|
||||
$repository_root = $repository->getLocalPath();
|
||||
|
||||
// Rebuild the query string to strip `__magic__` parameters and prevent
|
||||
// issues where we might interpret inputs like "service=read&service=write"
|
||||
// differently than the server does and pass it an unsafe command.
|
||||
$query_data = $request->getPassthroughRequestParameters();
|
||||
$query_string = http_build_query($query_data, '', '&');
|
||||
|
||||
// We're about to wipe out PATH with the rest of the environment, so
|
||||
// resolve the binary first.
|
||||
$bin = Filesystem::resolveBinary('git-http-backend');
|
||||
if (!$bin) {
|
||||
throw new Exception("Unable to find `git-http-backend` in PATH!");
|
||||
}
|
||||
|
||||
$env = array(
|
||||
'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
|
||||
'QUERY_STRING' => $query_string,
|
||||
'CONTENT_TYPE' => $_SERVER['CONTENT_TYPE'],
|
||||
'REMOTE_USER' => '',
|
||||
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
|
||||
'GIT_PROJECT_ROOT' => $repository_root,
|
||||
'GIT_HTTP_EXPORT_ALL' => '1',
|
||||
'PATH_INFO' => $request_path,
|
||||
);
|
||||
|
||||
list($stdout) = id(new ExecFuture('%s', $bin))
|
||||
->setEnv($env, true)
|
||||
->write(PhabricatorStartup::getRawInput())
|
||||
->resolvex();
|
||||
|
||||
return id(new DiffusionGitResponse())->setGitData($stdout);
|
||||
}
|
||||
|
||||
private function getRequestDirectoryPath() {
|
||||
$request = $this->getRequest();
|
||||
$request_path = $request->getRequestURI()->getPath();
|
||||
return preg_replace('@^/diffusion/[A-Z]+@', '', $request_path);
|
||||
}
|
||||
|
||||
protected function renderStatusMessage($title, $body) {
|
||||
return id(new AphrontErrorView())
|
||||
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
|
||||
->setTitle($title)
|
||||
->appendChild($body);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ final class DiffusionRepositoryController extends DiffusionController {
|
|||
}
|
||||
|
||||
public function processRequest() {
|
||||
$drequest = $this->diffusionRequest;
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
$content = array();
|
||||
|
||||
|
@ -15,106 +16,110 @@ final class DiffusionRepositoryController extends DiffusionController {
|
|||
$content[] = $crumbs;
|
||||
|
||||
$content[] = $this->buildPropertiesTable($drequest->getRepository());
|
||||
|
||||
$history_results = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.historyquery',
|
||||
array(
|
||||
'commit' => $drequest->getCommit(),
|
||||
'path' => $drequest->getPath(),
|
||||
'offset' => 0,
|
||||
'limit' => 15));
|
||||
$history = DiffusionPathChange::newFromConduit(
|
||||
$history_results['pathChanges']);
|
||||
|
||||
$browse_results = DiffusionBrowseResultSet::newFromConduit(
|
||||
$this->callConduitWithDiffusionRequest(
|
||||
'diffusion.browsequery',
|
||||
array(
|
||||
'path' => $drequest->getPath(),
|
||||
'commit' => $drequest->getCommit(),
|
||||
)));
|
||||
$browse_paths = $browse_results->getPaths();
|
||||
|
||||
$phids = array();
|
||||
foreach ($history as $item) {
|
||||
$data = $item->getCommitData();
|
||||
if ($data) {
|
||||
if ($data->getCommitDetail('authorPHID')) {
|
||||
$phids[$data->getCommitDetail('authorPHID')] = true;
|
||||
}
|
||||
if ($data->getCommitDetail('committerPHID')) {
|
||||
$phids[$data->getCommitDetail('committerPHID')] = true;
|
||||
|
||||
try {
|
||||
$history_results = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.historyquery',
|
||||
array(
|
||||
'commit' => $drequest->getCommit(),
|
||||
'path' => $drequest->getPath(),
|
||||
'offset' => 0,
|
||||
'limit' => 15));
|
||||
$history = DiffusionPathChange::newFromConduit(
|
||||
$history_results['pathChanges']);
|
||||
|
||||
foreach ($history as $item) {
|
||||
$data = $item->getCommitData();
|
||||
if ($data) {
|
||||
if ($data->getCommitDetail('authorPHID')) {
|
||||
$phids[$data->getCommitDetail('authorPHID')] = true;
|
||||
}
|
||||
if ($data->getCommitDetail('committerPHID')) {
|
||||
$phids[$data->getCommitDetail('committerPHID')] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$history_exception = null;
|
||||
} catch (Exception $ex) {
|
||||
$history_results = null;
|
||||
$history = null;
|
||||
$history_exception = $ex;
|
||||
}
|
||||
|
||||
foreach ($browse_paths as $item) {
|
||||
$data = $item->getLastCommitData();
|
||||
if ($data) {
|
||||
if ($data->getCommitDetail('authorPHID')) {
|
||||
$phids[$data->getCommitDetail('authorPHID')] = true;
|
||||
}
|
||||
if ($data->getCommitDetail('committerPHID')) {
|
||||
$phids[$data->getCommitDetail('committerPHID')] = true;
|
||||
try {
|
||||
$browse_results = DiffusionBrowseResultSet::newFromConduit(
|
||||
$this->callConduitWithDiffusionRequest(
|
||||
'diffusion.browsequery',
|
||||
array(
|
||||
'path' => $drequest->getPath(),
|
||||
'commit' => $drequest->getCommit(),
|
||||
)));
|
||||
$browse_paths = $browse_results->getPaths();
|
||||
|
||||
foreach ($browse_paths as $item) {
|
||||
$data = $item->getLastCommitData();
|
||||
if ($data) {
|
||||
if ($data->getCommitDetail('authorPHID')) {
|
||||
$phids[$data->getCommitDetail('authorPHID')] = true;
|
||||
}
|
||||
if ($data->getCommitDetail('committerPHID')) {
|
||||
$phids[$data->getCommitDetail('committerPHID')] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$browse_exception = null;
|
||||
} catch (Exception $ex) {
|
||||
$browse_results = null;
|
||||
$browse_paths = null;
|
||||
$browse_exception = $ex;
|
||||
}
|
||||
|
||||
$phids = array_keys($phids);
|
||||
$handles = $this->loadViewerHandles($phids);
|
||||
|
||||
$readme = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.readmequery',
|
||||
array(
|
||||
'paths' => $browse_results->getPathDicts()
|
||||
if ($browse_results) {
|
||||
$readme = $this->callConduitWithDiffusionRequest(
|
||||
'diffusion.readmequery',
|
||||
array(
|
||||
'paths' => $browse_results->getPathDicts()
|
||||
));
|
||||
} else {
|
||||
$readme = null;
|
||||
}
|
||||
|
||||
$history_table = new DiffusionHistoryTableView();
|
||||
$history_table->setUser($this->getRequest()->getUser());
|
||||
$history_table->setDiffusionRequest($drequest);
|
||||
$history_table->setHandles($handles);
|
||||
$history_table->setHistory($history);
|
||||
$history_table->loadRevisions();
|
||||
$history_table->setParents($history_results['parents']);
|
||||
$history_table->setIsHead(true);
|
||||
$content[] = $this->buildHistoryTable(
|
||||
$history_results,
|
||||
$history,
|
||||
$history_exception,
|
||||
$handles);
|
||||
|
||||
$callsign = $drequest->getRepository()->getCallsign();
|
||||
$all = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $drequest->generateURI(
|
||||
array(
|
||||
'action' => 'history',
|
||||
)),
|
||||
),
|
||||
pht('View Full Commit History'));
|
||||
$content[] = $this->buildBrowseTable(
|
||||
$browse_results,
|
||||
$browse_paths,
|
||||
$browse_exception,
|
||||
$handles);
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader(pht("Recent Commits · %s", $all));
|
||||
$panel->appendChild($history_table);
|
||||
$panel->setNoBackground();
|
||||
try {
|
||||
$content[] = $this->buildTagListTable($drequest);
|
||||
} catch (Exception $ex) {
|
||||
if (!$repository->isImporting()) {
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Unable to Load Tags'),
|
||||
$ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$content[] = $panel;
|
||||
|
||||
|
||||
$browse_table = new DiffusionBrowseTableView();
|
||||
$browse_table->setDiffusionRequest($drequest);
|
||||
$browse_table->setHandles($handles);
|
||||
$browse_table->setPaths($browse_paths);
|
||||
$browse_table->setUser($this->getRequest()->getUser());
|
||||
|
||||
$browse_panel = new AphrontPanelView();
|
||||
$browse_panel->setHeader(phutil_tag(
|
||||
'a',
|
||||
array('href' => $drequest->generateURI(array('action' => 'browse'))),
|
||||
pht('Browse Repository')));
|
||||
$browse_panel->appendChild($browse_table);
|
||||
$browse_panel->setNoBackground();
|
||||
|
||||
$content[] = $browse_panel;
|
||||
|
||||
$content[] = $this->buildTagListTable($drequest);
|
||||
|
||||
$content[] = $this->buildBranchListTable($drequest);
|
||||
try {
|
||||
$content[] = $this->buildBranchListTable($drequest);
|
||||
} catch (Exception $ex) {
|
||||
if (!$repository->isImporting()) {
|
||||
$content[] = $this->renderStatusMessage(
|
||||
pht('Unable to Load Branches'),
|
||||
$ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ($readme) {
|
||||
$box = new PHUIBoxView();
|
||||
|
@ -145,6 +150,15 @@ final class DiffusionRepositoryController extends DiffusionController {
|
|||
->setUser($user)
|
||||
->setPolicyObject($repository);
|
||||
|
||||
if (!$repository->isTracked()) {
|
||||
$header->setStatus('policy-noone', '', pht('Inactive'));
|
||||
} else if ($repository->isImporting()) {
|
||||
$header->setStatus('time', 'red', pht('Importing...'));
|
||||
} else {
|
||||
$header->setStatus('oh-ok', '', pht('Active'));
|
||||
}
|
||||
|
||||
|
||||
$actions = $this->buildActionList($repository);
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
|
@ -333,4 +347,109 @@ final class DiffusionRepositoryController extends DiffusionController {
|
|||
return $view;
|
||||
}
|
||||
|
||||
private function buildHistoryTable(
|
||||
$history_results,
|
||||
$history,
|
||||
$history_exception,
|
||||
array $handles) {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
if ($history_exception) {
|
||||
if ($repository->isImporting()) {
|
||||
return $this->renderStatusMessage(
|
||||
pht('Still Importing...'),
|
||||
pht(
|
||||
'This repository is still importing. History is not yet '.
|
||||
'available.'));
|
||||
} else {
|
||||
return $this->renderStatusMessage(
|
||||
pht('Unable to Retrieve History'),
|
||||
$history_exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$history_table = id(new DiffusionHistoryTableView())
|
||||
->setUser($viewer)
|
||||
->setDiffusionRequest($drequest)
|
||||
->setHandles($handles)
|
||||
->setHistory($history);
|
||||
|
||||
// TODO: Super sketchy.
|
||||
$history_table->loadRevisions();
|
||||
|
||||
if ($history_results) {
|
||||
$history_table->setParents($history_results['parents']);
|
||||
}
|
||||
|
||||
$history_table->setIsHead(true);
|
||||
|
||||
$callsign = $drequest->getRepository()->getCallsign();
|
||||
$all = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $drequest->generateURI(
|
||||
array(
|
||||
'action' => 'history',
|
||||
)),
|
||||
),
|
||||
pht('View Full Commit History'));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader(pht("Recent Commits · %s", $all));
|
||||
$panel->appendChild($history_table);
|
||||
$panel->setNoBackground();
|
||||
|
||||
return $panel;
|
||||
}
|
||||
|
||||
private function buildBrowseTable(
|
||||
$browse_results,
|
||||
$browse_paths,
|
||||
$browse_exception,
|
||||
array $handles) {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
$drequest = $this->getDiffusionRequest();
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
if ($browse_exception) {
|
||||
if ($repository->isImporting()) {
|
||||
// The history table renders a useful message.
|
||||
return null;
|
||||
} else {
|
||||
return $this->renderStatusMessage(
|
||||
pht('Unable to Retrieve Paths'),
|
||||
$browse_exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$browse_table = id(new DiffusionBrowseTableView())
|
||||
->setUser($viewer)
|
||||
->setDiffusionRequest($drequest)
|
||||
->setHandles($handles);
|
||||
if ($browse_paths) {
|
||||
$browse_table->setPaths($browse_paths);
|
||||
} else {
|
||||
$browse_table->setPaths(array());
|
||||
}
|
||||
|
||||
$browse_uri = $drequest->generateURI(array('action' => 'browse'));
|
||||
|
||||
$browse_panel = new AphrontPanelView();
|
||||
$browse_panel->setHeader(
|
||||
phutil_tag(
|
||||
'a',
|
||||
array('href' => $browse_uri),
|
||||
pht('Browse Repository')));
|
||||
$browse_panel->appendChild($browse_table);
|
||||
$browse_panel->setNoBackground();
|
||||
|
||||
return $browse_panel;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ final class DiffusionRepositoryCreateController
|
|||
|
||||
$cancel_uri = $this->getRepositoryControllerURI($repository, 'edit/');
|
||||
} else {
|
||||
$this->requireApplicationCapability(
|
||||
DiffusionCapabilityCreateRepositories::CAPABILITY);
|
||||
|
||||
$cancel_uri = $this->getApplicationURI();
|
||||
}
|
||||
|
||||
|
@ -60,15 +63,19 @@ final class DiffusionRepositoryCreateController
|
|||
if ($request->isFormPost()) {
|
||||
$form->readFromRequest($request);
|
||||
if ($form->isComplete()) {
|
||||
$is_create = ($this->edit === null);
|
||||
|
||||
if ($this->edit != 'remote') {
|
||||
// TODO: This exception is heartwarming but should probably take more
|
||||
// substantive actions.
|
||||
throw new Exception("GOOD JOB AT FORM");
|
||||
if ($is_create) {
|
||||
$repository = PhabricatorRepository::initializeNewRepository(
|
||||
$viewer);
|
||||
}
|
||||
|
||||
$template = id(new PhabricatorRepositoryTransaction());
|
||||
|
||||
$type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
|
||||
$type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS;
|
||||
$type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
|
||||
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
|
||||
$type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
|
||||
$type_ssh_login = PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN;
|
||||
$type_ssh_key = PhabricatorRepositoryTransaction::TYPE_SSH_KEY;
|
||||
|
@ -78,6 +85,48 @@ final class DiffusionRepositoryCreateController
|
|||
|
||||
$xactions = array();
|
||||
|
||||
// If we're creating a new repository, set all this core stuff.
|
||||
if ($is_create) {
|
||||
$callsign = $form->getPage('name')
|
||||
->getControl('callsign')->getValue();
|
||||
|
||||
// We must set this to a unique value to save the repository
|
||||
// initially, and it's immutable, so we don't bother using
|
||||
// transactions to apply this change.
|
||||
$repository->setCallsign($callsign);
|
||||
|
||||
// Put the repository in "Importing" mode until we finish
|
||||
// parsing it.
|
||||
$repository->setDetail('importing', true);
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_name)
|
||||
->setNewValue(
|
||||
$form->getPage('name')->getControl('name')->getValue());
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_vcs)
|
||||
->setNewValue(
|
||||
$form->getPage('vcs')->getControl('vcs')->getValue());
|
||||
|
||||
$activate = $form->getPage('done')
|
||||
->getControl('activate')->getValue();
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_activate)
|
||||
->setNewValue(
|
||||
($activate == 'start'));
|
||||
|
||||
$default_local_path = PhabricatorEnv::getEnvConfig(
|
||||
'repository.default-local-path');
|
||||
|
||||
$default_local_path = rtrim($default_local_path, '/');
|
||||
$default_local_path = $default_local_path.'/'.$callsign.'/';
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_local_path)
|
||||
->setNewValue($default_local_path);
|
||||
}
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_remote_uri)
|
||||
->setNewValue(
|
||||
|
@ -408,6 +457,21 @@ final class DiffusionRepositoryCreateController
|
|||
}
|
||||
}
|
||||
|
||||
// Catch confusion between Git/SCP-style URIs and normal URIs. See T3619
|
||||
// for discussion. This is usually a user adding "ssh://" to an implicit
|
||||
// SSH Git URI.
|
||||
if ($proto == 'ssh') {
|
||||
if (preg_match('(^[^:@]+://[^/:]+:[^\d])', $v_remote)) {
|
||||
$c_remote->setError(pht('Invalid'));
|
||||
$page->addPageError(
|
||||
pht(
|
||||
"The Remote URI is not formatted correctly. Remote URIs ".
|
||||
"with an explicit protocol should be in the form ".
|
||||
"'proto://domain/path', not 'proto://domain:/path'. ".
|
||||
"The ':/path' syntax is only valid in SCP-style URIs."));
|
||||
}
|
||||
}
|
||||
|
||||
switch ($proto) {
|
||||
case 'ssh':
|
||||
case 'http':
|
||||
|
@ -619,7 +683,7 @@ final class DiffusionRepositoryCreateController
|
|||
pht('Configure More Options First'),
|
||||
pht(
|
||||
'Configure more options before beginning the repository '.
|
||||
'import. This will let you fine-tune settings.. You can '.
|
||||
'import. This will let you fine-tune settings. You can '.
|
||||
'start the import whenever you are ready.')));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionRepositoryDefaultController extends DiffusionController {
|
||||
|
||||
public function processRequest() {
|
||||
// NOTE: This controller is just here to make sure we call
|
||||
// willBeginExecution() on any /diffusion/X/ URI, so we can intercept
|
||||
// `git`, `hg` and `svn` HTTP protocol requests.
|
||||
return new Aphront404Response();
|
||||
}
|
||||
}
|
|
@ -37,8 +37,12 @@ final class DiffusionRepositoryEditBranchesController
|
|||
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
|
||||
|
||||
$v_default = $repository->getHumanReadableDetail('default-branch');
|
||||
$v_track = $repository->getHumanReadableDetail('branch-filter');
|
||||
$v_autoclose = $repository->getHumanReadableDetail('close-commits-filter');
|
||||
$v_track = $repository->getHumanReadableDetail(
|
||||
'branch-filter',
|
||||
array());
|
||||
$v_autoclose = $repository->getHumanReadableDetail(
|
||||
'close-commits-filter',
|
||||
array());
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_default = $request->getStr('default');
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionRepositoryEditHostingController
|
||||
extends DiffusionRepositoryEditController {
|
||||
|
||||
private $serve;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
parent::willProcessRequest($data);
|
||||
$this->serve = idx($data, 'serve');
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$drequest = $this->diffusionRequest;
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($user)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withIDs(array($repository->getID()))
|
||||
->executeOne();
|
||||
if (!$repository) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if (!$this->serve) {
|
||||
return $this->handleHosting($repository);
|
||||
} else {
|
||||
return $this->handleProtocols($repository);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleHosting(PhabricatorRepository $repository) {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$v_hosting = $repository->isHosted();
|
||||
|
||||
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
|
||||
$next_uri = $this->getRepositoryControllerURI($repository, 'edit/serve/');
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_hosting = $request->getBool('hosting');
|
||||
|
||||
$xactions = array();
|
||||
$template = id(new PhabricatorRepositoryTransaction());
|
||||
|
||||
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_hosting)
|
||||
->setNewValue($v_hosting);
|
||||
|
||||
id(new PhabricatorRepositoryEditor())
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setActor($user)
|
||||
->applyTransactions($repository, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName(pht('Edit Hosting')));
|
||||
|
||||
$title = pht('Edit Hosting (%s)', $repository->getName());
|
||||
|
||||
$hosted_control = id(new AphrontFormRadioButtonControl())
|
||||
->setName('hosting')
|
||||
->setLabel(pht('Hosting'))
|
||||
->addButton(
|
||||
true,
|
||||
pht('Host Repository on Phabricator'),
|
||||
pht(
|
||||
'Phabricator will host this repository. Users will be able to '.
|
||||
'push commits to Phabricator. Phabricator will not pull '.
|
||||
'changes from elsewhere.'))
|
||||
->addButton(
|
||||
false,
|
||||
pht('Host Repository Elsewhere'),
|
||||
pht(
|
||||
'Phabricator will pull updates to this repository from a master '.
|
||||
'repository elsewhere (for example, on GitHub or Bitbucket). '.
|
||||
'Users will not be able to push commits to this repository.'))
|
||||
->setValue($v_hosting);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->appendRemarkupInstructions(
|
||||
pht(
|
||||
'NOTE: Hosting is extremely new and barely works! Use it at '.
|
||||
'your own risk.'.
|
||||
"\n\n".
|
||||
'Phabricator can host repositories, or it can track repositories '.
|
||||
'hosted elsewhere (like on GitHub or Bitbucket).'))
|
||||
->appendChild($hosted_control)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Save and Continue'))
|
||||
->addCancelButton($edit_uri));
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($title)
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$object_box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
));
|
||||
}
|
||||
|
||||
public function handleProtocols(PhabricatorRepository $repository) {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$v_http_mode = $repository->getServeOverHTTP();
|
||||
$v_ssh_mode = $repository->getServeOverSSH();
|
||||
|
||||
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
|
||||
$prev_uri = $this->getRepositoryControllerURI($repository, 'edit/hosting/');
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_http_mode = $request->getStr('http');
|
||||
$v_ssh_mode = $request->getStr('ssh');
|
||||
|
||||
$xactions = array();
|
||||
$template = id(new PhabricatorRepositoryTransaction());
|
||||
|
||||
$type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
|
||||
$type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_http)
|
||||
->setNewValue($v_http_mode);
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_ssh)
|
||||
->setNewValue($v_ssh_mode);
|
||||
|
||||
id(new PhabricatorRepositoryEditor())
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setActor($user)
|
||||
->applyTransactions($repository, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($edit_uri);
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName(pht('Edit Protocols')));
|
||||
|
||||
$title = pht('Edit Protocols (%s)', $repository->getName());
|
||||
|
||||
|
||||
if ($repository->isHosted()) {
|
||||
$rw_message = pht(
|
||||
'Phabricator will serve a read-write copy of this repository');
|
||||
} else {
|
||||
$rw_message = pht(
|
||||
'This repository is hosted elsewhere, so Phabricator can not perform '.
|
||||
'writes.');
|
||||
}
|
||||
|
||||
$ssh_control =
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setName('ssh')
|
||||
->setLabel(pht('SSH'))
|
||||
->setValue($v_ssh_mode)
|
||||
->addButton(
|
||||
PhabricatorRepository::SERVE_OFF,
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
PhabricatorRepository::SERVE_OFF),
|
||||
pht('Phabricator will not serve this repository.'))
|
||||
->addButton(
|
||||
PhabricatorRepository::SERVE_READONLY,
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
PhabricatorRepository::SERVE_READONLY),
|
||||
pht('Phabricator will serve a read-only copy of this repository.'))
|
||||
->addButton(
|
||||
PhabricatorRepository::SERVE_READWRITE,
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
PhabricatorRepository::SERVE_READWRITE),
|
||||
$rw_message,
|
||||
$repository->isHosted() ? null : 'disabled',
|
||||
$repository->isHosted() ? null : true);
|
||||
|
||||
$http_control =
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setName('http')
|
||||
->setLabel(pht('HTTP'))
|
||||
->setValue($v_http_mode)
|
||||
->addButton(
|
||||
PhabricatorRepository::SERVE_OFF,
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
PhabricatorRepository::SERVE_OFF),
|
||||
pht('Phabricator will not serve this repository.'))
|
||||
->addButton(
|
||||
PhabricatorRepository::SERVE_READONLY,
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
PhabricatorRepository::SERVE_READONLY),
|
||||
pht('Phabricator will serve a read-only copy of this repository.'))
|
||||
->addButton(
|
||||
PhabricatorRepository::SERVE_READWRITE,
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
PhabricatorRepository::SERVE_READWRITE),
|
||||
$rw_message,
|
||||
$repository->isHosted() ? null : 'disabled',
|
||||
$repository->isHosted() ? null : true);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->appendRemarkupInstructions(
|
||||
pht(
|
||||
'Phabricator can serve repositories over various protocols. You can '.
|
||||
'configure server protocols here.'))
|
||||
->appendChild($ssh_control)
|
||||
->appendChild($http_control)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Save Changes'))
|
||||
->addCancelButton($prev_uri, pht('Back')));
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($title)
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$object_box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -23,8 +23,7 @@ final class DiffusionRepositoryEditMainController
|
|||
$is_git = true;
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
// TOOD: This will be true for hosted SVN repositories.
|
||||
$has_local = false;
|
||||
$has_local = $repository->isHosted();
|
||||
$is_svn = true;
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
|
@ -63,6 +62,10 @@ final class DiffusionRepositoryEditMainController
|
|||
$encoding_properties =
|
||||
$this->buildEncodingProperties($repository, $encoding_actions);
|
||||
|
||||
$hosting_properties = $this->buildHostingProperties(
|
||||
$repository,
|
||||
$this->buildHostingActions($repository));
|
||||
|
||||
$branches_properties = null;
|
||||
if ($has_branches) {
|
||||
$branches_properties = $this->buildBranchesProperties(
|
||||
|
@ -114,6 +117,7 @@ final class DiffusionRepositoryEditMainController
|
|||
->setHeader($header)
|
||||
->addPropertyList($basic_properties)
|
||||
->addPropertyList($policy_properties)
|
||||
->addPropertyList($hosting_properties)
|
||||
->addPropertyList($remote_properties);
|
||||
|
||||
if ($local_properties) {
|
||||
|
@ -298,6 +302,10 @@ final class DiffusionRepositoryEditMainController
|
|||
pht('Editable By'),
|
||||
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
|
||||
|
||||
$pushable = $repository->isHosted()
|
||||
? $descriptions[DiffusionCapabilityPush::CAPABILITY]
|
||||
: phutil_tag('em', array(), pht('Not a Hosted Repository'));
|
||||
$view->addProperty(pht('Pushable By'), $pushable);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
@ -336,12 +344,12 @@ final class DiffusionRepositoryEditMainController
|
|||
$view->addProperty(pht('Default Branch'), $default_branch);
|
||||
|
||||
$track_only = nonempty(
|
||||
$repository->getHumanReadableDetail('branch-filter'),
|
||||
$repository->getHumanReadableDetail('branch-filter', array()),
|
||||
phutil_tag('em', array(), pht('Track All Branches')));
|
||||
$view->addProperty(pht('Track Only'), $track_only);
|
||||
|
||||
$autoclose_only = nonempty(
|
||||
$repository->getHumanReadableDetail('close-commits-filter'),
|
||||
$repository->getHumanReadableDetail('close-commits-filter', array()),
|
||||
phutil_tag('em', array(), pht('Autoclose On All Branches')));
|
||||
$view->addProperty(pht('Autoclose Only'), $autoclose_only);
|
||||
|
||||
|
@ -501,4 +509,57 @@ final class DiffusionRepositoryEditMainController
|
|||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function buildHostingActions(PhabricatorRepository $repository) {
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$view = id(new PhabricatorActionListView())
|
||||
->setObjectURI($this->getRequest()->getRequestURI())
|
||||
->setUser($user);
|
||||
|
||||
$edit = id(new PhabricatorActionView())
|
||||
->setIcon('edit')
|
||||
->setName(pht('Edit Hosting'))
|
||||
->setHref(
|
||||
$this->getRepositoryControllerURI($repository, 'edit/hosting/'));
|
||||
$view->addAction($edit);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function buildHostingProperties(
|
||||
PhabricatorRepository $repository,
|
||||
PhabricatorActionListView $actions) {
|
||||
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($user)
|
||||
->setActionList($actions)
|
||||
->addSectionHeader(pht('Hosting'));
|
||||
|
||||
$hosting = $repository->isHosted()
|
||||
? pht('Hosted on Phabricator')
|
||||
: pht('Hosted Elsewhere');
|
||||
$view->addProperty(pht('Hosting'), phutil_tag('em', array(), $hosting));
|
||||
|
||||
$view->addProperty(
|
||||
pht('Serve over HTTP'),
|
||||
phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
$repository->getServeOverHTTP())));
|
||||
|
||||
$view->addProperty(
|
||||
pht('Serve over SSH'),
|
||||
phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
PhabricatorRepository::getProtocolAvailabilityName(
|
||||
$repository->getServeOverSSH())));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,16 +27,19 @@ final class DiffusionRepositoryEditPolicyController
|
|||
|
||||
$v_view = $repository->getViewPolicy();
|
||||
$v_edit = $repository->getEditPolicy();
|
||||
$v_push = $repository->getPushPolicy();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_view = $request->getStr('viewPolicy');
|
||||
$v_edit = $request->getStr('editPolicy');
|
||||
$v_push = $request->getStr('pushPolicy');
|
||||
|
||||
$xactions = array();
|
||||
$template = id(new PhabricatorRepositoryTransaction());
|
||||
|
||||
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
$type_push = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
|
||||
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_view)
|
||||
|
@ -46,6 +49,12 @@ final class DiffusionRepositoryEditPolicyController
|
|||
->setTransactionType($type_edit)
|
||||
->setNewValue($v_edit);
|
||||
|
||||
if ($repository->isHosted()) {
|
||||
$xactions[] = id(clone $template)
|
||||
->setTransactionType($type_push)
|
||||
->setNewValue($v_push);
|
||||
}
|
||||
|
||||
id(new PhabricatorRepositoryEditor())
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSourceFromRequest($request)
|
||||
|
@ -62,7 +71,7 @@ final class DiffusionRepositoryEditPolicyController
|
|||
id(new PhabricatorCrumbView())
|
||||
->setName(pht('Edit Policies')));
|
||||
|
||||
$title = pht('Edit %s', $repository->getName());
|
||||
$title = pht('Edit Policies (%s)', $repository->getName());
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
|
@ -84,7 +93,25 @@ final class DiffusionRepositoryEditPolicyController
|
|||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicyObject($repository)
|
||||
->setPolicies($policies)
|
||||
->setName('editPolicy'))
|
||||
->setName('editPolicy'));
|
||||
|
||||
if ($repository->isHosted()) {
|
||||
$form->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setUser($viewer)
|
||||
->setCapability(DiffusionCapabilityPush::CAPABILITY)
|
||||
->setPolicyObject($repository)
|
||||
->setPolicies($policies)
|
||||
->setName('pushPolicy'));
|
||||
} else {
|
||||
$form->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Can Push'))
|
||||
->setValue(
|
||||
phutil_tag('em', array(), pht('Not a Hosted Repository'))));
|
||||
}
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Save Policies'))
|
||||
|
|
|
@ -90,7 +90,6 @@ final class DiffusionRepositoryListController extends DiffusionController
|
|||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
|
||||
id(new PhabricatorRepositorySearchEngine())
|
||||
->setViewer($viewer)
|
||||
->addNavigationItems($nav->getMenu());
|
||||
|
@ -100,6 +99,22 @@ final class DiffusionRepositoryListController extends DiffusionController
|
|||
return $nav;
|
||||
}
|
||||
|
||||
public function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
DiffusionCapabilityCreateRepositories::CAPABILITY);
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Import Repository'))
|
||||
->setHref($this->getApplicationURI('/create/'))
|
||||
->setDisabled(!$can_create)
|
||||
->setIcon('create'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
private function buildShortcuts() {
|
||||
$shortcuts = id(new PhabricatorRepositoryShortcut())->loadAll();
|
||||
if ($shortcuts) {
|
||||
|
|
|
@ -7,8 +7,7 @@ extends DiffusionStableCommitNameQuery {
|
|||
$repository = $this->getRepository();
|
||||
$branch = $this->getBranch();
|
||||
list($stdout) = $repository->execxLocalCommand(
|
||||
'rev-parse --verify %s/%s',
|
||||
DiffusionBranchInformation::DEFAULT_GIT_REMOTE,
|
||||
'rev-parse --verify %s',
|
||||
$branch);
|
||||
|
||||
$commit = trim($stdout);
|
||||
|
|
|
@ -31,8 +31,7 @@ final class DiffusionGitRequest extends DiffusionRequest {
|
|||
if ($this->commit) {
|
||||
return $this->commit;
|
||||
}
|
||||
$remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE;
|
||||
return $remote.'/'.$this->getBranch();
|
||||
return $this->getBranch();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
44
src/applications/diffusion/response/DiffusionGitResponse.php
Normal file
44
src/applications/diffusion/response/DiffusionGitResponse.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionGitResponse extends AphrontResponse {
|
||||
|
||||
private $httpCode;
|
||||
private $headers = array();
|
||||
private $response;
|
||||
|
||||
public function setGitData($data) {
|
||||
list($headers, $body) = explode("\r\n\r\n", $data, 2);
|
||||
$this->response = $body;
|
||||
$headers = explode("\r\n", $headers);
|
||||
|
||||
$matches = null;
|
||||
$this->httpCode = 200;
|
||||
$this->headers = array();
|
||||
foreach ($headers as $header) {
|
||||
if (preg_match('/^Status:\s*(\d+)/i', $header, $matches)) {
|
||||
$this->httpCode = (int)$matches[1];
|
||||
} else {
|
||||
$this->headers[] = explode(': ', $header, 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildResponseString() {
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function getHeaders() {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function getCacheHeaders() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getHTTPResponseCode() {
|
||||
return $this->httpCode;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionSSHGitReceivePackWorkflow
|
||||
extends DiffusionSSHGitWorkflow {
|
||||
|
||||
public function didConstruct() {
|
||||
$this->setName('git-receive-pack');
|
||||
$this->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'dir',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getRequestPath() {
|
||||
$args = $this->getArgs();
|
||||
return head($args->getArg('dir'));
|
||||
}
|
||||
|
||||
protected function executeRepositoryOperations(
|
||||
PhabricatorRepository $repository) {
|
||||
$future = new ExecFuture(
|
||||
'git-receive-pack %s',
|
||||
$repository->getLocalPath());
|
||||
return $this->passthruIO($future);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionSSHGitUploadPackWorkflow
|
||||
extends DiffusionSSHGitWorkflow {
|
||||
|
||||
public function didConstruct() {
|
||||
$this->setName('git-upload-pack');
|
||||
$this->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'dir',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRequestPath() {
|
||||
$args = $this->getArgs();
|
||||
return head($args->getArg('dir'));
|
||||
}
|
||||
|
||||
protected function executeRepositoryOperations(
|
||||
PhabricatorRepository $repository) {
|
||||
|
||||
$future = new ExecFuture('git-upload-pack %s', $repository->getLocalPath());
|
||||
|
||||
return $this->passthruIO($future);
|
||||
}
|
||||
|
||||
}
|
10
src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php
Normal file
10
src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
abstract class DiffusionSSHGitWorkflow extends DiffusionSSHWorkflow {
|
||||
|
||||
protected function writeError($message) {
|
||||
// Git assumes we'll add our own newlines.
|
||||
return parent::writeError($message."\n");
|
||||
}
|
||||
|
||||
}
|
89
src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
Normal file
89
src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||
|
||||
private $args;
|
||||
|
||||
public function getArgs() {
|
||||
return $this->args;
|
||||
}
|
||||
|
||||
abstract protected function isReadOnly();
|
||||
abstract protected function getRequestPath();
|
||||
abstract protected function executeRepositoryOperations(
|
||||
PhabricatorRepository $repository);
|
||||
|
||||
protected function writeError($message) {
|
||||
$this->getErrorChannel()->write($message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function execute(PhutilArgumentParser $args) {
|
||||
$this->args = $args;
|
||||
|
||||
try {
|
||||
$repository = $this->loadRepository();
|
||||
return $this->executeRepositoryOperations($repository);
|
||||
} catch (Exception $ex) {
|
||||
$this->writeError(get_class($ex).': '.$ex->getMessage());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private function loadRepository() {
|
||||
$viewer = $this->getUser();
|
||||
$path = $this->getRequestPath();
|
||||
|
||||
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|$)@';
|
||||
$matches = null;
|
||||
if (!preg_match($regex, $path, $matches)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized repository path "%s". Expected a path like '.
|
||||
'"%s".',
|
||||
$path,
|
||||
"/diffusion/X/"));
|
||||
}
|
||||
|
||||
$callsign = $matches[1];
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->withCallsigns(array($callsign))
|
||||
->executeOne();
|
||||
|
||||
if (!$repository) {
|
||||
throw new Exception(
|
||||
pht('No repository "%s" exists!', $callsign));
|
||||
}
|
||||
|
||||
$is_push = !$this->isReadOnly();
|
||||
|
||||
switch ($repository->getServeOverSSH()) {
|
||||
case PhabricatorRepository::SERVE_READONLY:
|
||||
if ($is_push) {
|
||||
throw new Exception(
|
||||
pht('This repository is read-only over SSH.'));
|
||||
}
|
||||
break;
|
||||
case PhabricatorRepository::SERVE_READWRITE:
|
||||
if ($is_push) {
|
||||
$can_push = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$repository,
|
||||
DiffusionCapabilityPush::CAPABILITY);
|
||||
if (!$can_push) {
|
||||
throw new Exception(
|
||||
pht('You do not have permission to push to this repository.'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PhabricatorRepository::SERVE_OFF:
|
||||
default:
|
||||
throw new Exception(
|
||||
pht('This repository is not available over SSH.'));
|
||||
}
|
||||
|
||||
return $repository;
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,8 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
|||
$commit_phids[] = $item->getCommit()->getPHID();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Get rid of this.
|
||||
$this->revisions = id(new DifferentialRevision())
|
||||
->loadIDsByCommitPHIDs($commit_phids);
|
||||
return $this;
|
||||
|
|
|
@ -16,14 +16,14 @@ final class PhabricatorRepositoryConfigOptions
|
|||
|
||||
public function getOptions() {
|
||||
return array(
|
||||
$this->newOption('repository.default-local-path', 'string', null)
|
||||
$this->newOption('repository.default-local-path', 'string', '/var/repo/')
|
||||
->setSummary(
|
||||
pht("Default location to store local copies of repositories."))
|
||||
->setDescription(
|
||||
pht(
|
||||
"The default location in which to store local copies of ".
|
||||
"repositories. Anything stored in this directory will be assumed ".
|
||||
"to be under the control of phabricator, which means that ".
|
||||
"to be under the control of Phabricator, which means that ".
|
||||
"Phabricator will try to do some maintenance on working copies ".
|
||||
"if there are problems (such as a change to the remote origin ".
|
||||
"url). This maintenance may include completely removing (and ".
|
||||
|
|
|
@ -29,10 +29,6 @@ final class PhabricatorApplicationRepositories extends PhabricatorApplication {
|
|||
return array(
|
||||
'/repository/' => array(
|
||||
'' => 'PhabricatorRepositoryListController',
|
||||
'create/' => 'PhabricatorRepositoryCreateController',
|
||||
'edit/(?P<id>[1-9]\d*)/(?:(?P<view>\w+)/)?' =>
|
||||
'PhabricatorRepositoryEditController',
|
||||
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorRepositoryDeleteController',
|
||||
'project/edit/(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorRepositoryArcanistProjectEditController',
|
||||
'project/delete/(?P<id>[1-9]\d*)/' =>
|
||||
|
|
|
@ -62,15 +62,24 @@ final class ConduitAPI_repository_create_Method
|
|||
}
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
if (!$request->getUser()->getIsAdmin()) {
|
||||
throw new ConduitException('ERR-PERMISSIONS');
|
||||
}
|
||||
$application = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($request->getUser())
|
||||
->withClasses(array('PhabricatorApplicationDiffusion'))
|
||||
->executeOne();
|
||||
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$request->getUser(),
|
||||
$application,
|
||||
DiffusionCapabilityCreateRepositories::CAPABILITY);
|
||||
|
||||
// TODO: This has some duplication with (and lacks some of the validation
|
||||
// of) the web workflow; refactor things so they can share more code as this
|
||||
// stabilizes.
|
||||
// stabilizes. Specifically, this should move to transactions since they
|
||||
// work properly now.
|
||||
|
||||
$repository = PhabricatorRepository::initializeNewRepository(
|
||||
$request->getUser());
|
||||
|
||||
$repository = new PhabricatorRepository();
|
||||
$repository->setName($request->getValue('name'));
|
||||
|
||||
$callsign = $request->getValue('callsign');
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorRepositoryCreateController
|
||||
extends PhabricatorRepositoryController {
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$e_name = true;
|
||||
$e_callsign = true;
|
||||
|
||||
$repository = new PhabricatorRepository();
|
||||
|
||||
$type_map = PhabricatorRepositoryType::getAllRepositoryTypes();
|
||||
$errors = array();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
|
||||
$repository->setName($request->getStr('name'));
|
||||
$repository->setCallsign($request->getStr('callsign'));
|
||||
$repository->setVersionControlSystem($request->getStr('type'));
|
||||
|
||||
if (!strlen($repository->getName())) {
|
||||
$e_name = 'Required';
|
||||
$errors[] = 'Repository name is required.';
|
||||
} else {
|
||||
$e_name = null;
|
||||
}
|
||||
|
||||
if (!strlen($repository->getCallsign())) {
|
||||
$e_callsign = 'Required';
|
||||
$errors[] = 'Callsign is required.';
|
||||
} else if (!preg_match('/^[A-Z]+$/', $repository->getCallsign())) {
|
||||
$e_callsign = 'Invalid';
|
||||
$errors[] = 'Callsign must be ALL UPPERCASE LETTERS.';
|
||||
} else {
|
||||
$e_callsign = null;
|
||||
}
|
||||
|
||||
if (empty($type_map[$repository->getVersionControlSystem()])) {
|
||||
$errors[] = 'Invalid version control system.';
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
try {
|
||||
$repository->save();
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/repository/edit/'.$repository->getID().'/');
|
||||
|
||||
} catch (AphrontQueryDuplicateKeyException $ex) {
|
||||
$e_callsign = 'Duplicate';
|
||||
$errors[] = 'Callsign must be unique. Another repository already '.
|
||||
'uses that callsign.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$error_view = null;
|
||||
if ($errors) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setErrors($errors);
|
||||
$error_view->setTitle('Form Errors');
|
||||
}
|
||||
|
||||
$form = new AphrontFormView();
|
||||
$form
|
||||
->setUser($user)
|
||||
->setAction('/repository/create/')
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Name')
|
||||
->setName('name')
|
||||
->setValue($repository->getName())
|
||||
->setError($e_name)
|
||||
->setCaption('Human-readable repository name.'))
|
||||
->appendChild(hsprintf(
|
||||
'<p class="aphront-form-instructions">Select a "Callsign" — a '.
|
||||
'short, uppercase string to identify revisions in this repository. If '.
|
||||
'you choose "EX", revisions in this repository will be identified '.
|
||||
'with the prefix "rEX".</p>'))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Callsign')
|
||||
->setName('callsign')
|
||||
->setValue($repository->getCallsign())
|
||||
->setError($e_callsign)
|
||||
->setCaption(
|
||||
'Short, UPPERCASE identifier. Once set, it can not be changed.'))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel('Type')
|
||||
->setName('type')
|
||||
->setOptions($type_map)
|
||||
->setValue($repository->getVersionControlSystem()))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue('Create Repository')
|
||||
->addCancelButton('/repository/'));
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Create Repository'))
|
||||
->setFormError($error_view)
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$form_box,
|
||||
),
|
||||
array(
|
||||
'title' => pht('Create Repository'),
|
||||
'device' => true,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,696 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorRepositoryEditController
|
||||
extends PhabricatorRepositoryController {
|
||||
|
||||
private $id;
|
||||
private $view;
|
||||
private $repository;
|
||||
private $sideNav;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
$this->view = idx($data, 'view');
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->executeOne();
|
||||
if (!$repository) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$views = array(
|
||||
'basic' => 'Basics',
|
||||
'tracking' => 'Tracking',
|
||||
);
|
||||
|
||||
$this->repository = $repository;
|
||||
|
||||
if (!isset($views[$this->view])) {
|
||||
$this->view = head_key($views);
|
||||
}
|
||||
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$base_uri = new PhutilURI('/repository/edit/'.$repository->getID().'/');
|
||||
$nav->setBaseURI($base_uri);
|
||||
foreach ($views as $view => $name) {
|
||||
$nav->addFilter($view, $name);
|
||||
}
|
||||
$nav->selectFilter($this->view, null);
|
||||
|
||||
$nav->appendChild($this->renderDaemonNotice());
|
||||
|
||||
$this->sideNav = $nav;
|
||||
|
||||
switch ($this->view) {
|
||||
case 'basic':
|
||||
return $this->processBasicRequest();
|
||||
case 'tracking':
|
||||
return $this->processTrackingRequest();
|
||||
default:
|
||||
throw new Exception("Unknown view.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function processBasicRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$repository = $this->repository;
|
||||
$repository_id = $repository->getID();
|
||||
|
||||
$errors = array();
|
||||
|
||||
$e_name = true;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$repository->setName($request->getStr('name'));
|
||||
|
||||
if (!strlen($repository->getName())) {
|
||||
$e_name = 'Required';
|
||||
$errors[] = 'Repository name is required.';
|
||||
} else {
|
||||
$e_name = null;
|
||||
}
|
||||
|
||||
$repository->setDetail('description', $request->getStr('description'));
|
||||
$repository->setDetail('encoding', $request->getStr('encoding'));
|
||||
|
||||
if (!$errors) {
|
||||
$repository->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/repository/edit/'.$repository_id.'/basic/?saved=true');
|
||||
}
|
||||
}
|
||||
|
||||
$error_view = null;
|
||||
if ($errors) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setErrors($errors);
|
||||
$error_view->setTitle('Form Errors');
|
||||
} else if ($request->getStr('saved')) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
|
||||
$error_view->setTitle('Changes Saved');
|
||||
$error_view->appendChild('Repository changes were saved.');
|
||||
}
|
||||
|
||||
$encoding_doc_link = PhabricatorEnv::getDoclink(
|
||||
'article/User_Guide_UTF-8_and_Character_Encoding.html');
|
||||
|
||||
$form = new AphrontFormView();
|
||||
$form
|
||||
->setUser($user)
|
||||
->setAction('/repository/edit/'.$repository->getID().'/')
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Name')
|
||||
->setName('name')
|
||||
->setValue($repository->getName())
|
||||
->setError($e_name)
|
||||
->setCaption('Human-readable repository name.'))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel('Description')
|
||||
->setName('description')
|
||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
|
||||
->setValue($repository->getDetail('description')))
|
||||
->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel('Callsign')
|
||||
->setName('callsign')
|
||||
->setValue($repository->getCallsign()))
|
||||
->appendChild(hsprintf('
|
||||
<p class="aphront-form-instructions">'.
|
||||
'If source code in this repository uses a character '.
|
||||
'encoding other than UTF-8 (for example, ISO-8859-1), '.
|
||||
'specify it here. You can usually leave this field blank. '.
|
||||
'See User Guide: '.
|
||||
'<a href="%s">UTF-8 and Character Encoding</a> for more information.'.
|
||||
'</p>',
|
||||
$encoding_doc_link))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Encoding')
|
||||
->setName('encoding')
|
||||
->setValue($repository->getDetail('encoding')))
|
||||
->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel('Type')
|
||||
->setName('type')
|
||||
->setValue($repository->getVersionControlSystem()))
|
||||
->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel('ID')
|
||||
->setValue($repository->getID()))
|
||||
->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel('PHID')
|
||||
->setValue($repository->getPHID()))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue('Save'));
|
||||
|
||||
$nav = $this->sideNav;
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Edit Repository'))
|
||||
->setFormError($error_view)
|
||||
->setForm($form);
|
||||
|
||||
$nav->appendChild($form_box);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$nav,
|
||||
array(
|
||||
'title' => pht('Edit Repository'),
|
||||
'device' => true,
|
||||
));
|
||||
}
|
||||
|
||||
private function processTrackingRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$repository = $this->repository;
|
||||
$repository_id = $repository->getID();
|
||||
|
||||
$errors = array();
|
||||
|
||||
$e_uri = null;
|
||||
$e_path = null;
|
||||
|
||||
$is_git = false;
|
||||
$is_svn = false;
|
||||
$is_mercurial = false;
|
||||
|
||||
$e_ssh_key = null;
|
||||
$e_ssh_keyfile = null;
|
||||
$e_branch = null;
|
||||
|
||||
switch ($repository->getVersionControlSystem()) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
$is_git = true;
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
$is_svn = true;
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
$is_mercurial = true;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported VCS!");
|
||||
}
|
||||
|
||||
$has_branches = ($is_git || $is_mercurial);
|
||||
$has_local = ($is_git || $is_mercurial);
|
||||
$has_branch_filter = ($is_git);
|
||||
$has_auth_support = $is_svn;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$tracking = ($request->getStr('tracking') == 'enabled' ? true : false);
|
||||
$repository->setDetail('tracking-enabled', $tracking);
|
||||
$repository->setDetail('remote-uri', $request->getStr('uri'));
|
||||
if ($has_local) {
|
||||
$repository->setDetail('local-path', $request->getStr('path'));
|
||||
}
|
||||
|
||||
if ($has_branch_filter) {
|
||||
$branch_filter = $request->getStrList('branch-filter');
|
||||
$branch_filter = array_fill_keys($branch_filter, true);
|
||||
|
||||
$repository->setDetail('branch-filter', $branch_filter);
|
||||
|
||||
$close_commits_filter = $request->getStrList('close-commits-filter');
|
||||
$close_commits_filter = array_fill_keys($close_commits_filter, true);
|
||||
|
||||
$repository->setDetail('close-commits-filter', $close_commits_filter);
|
||||
}
|
||||
|
||||
$repository->setDetail(
|
||||
'disable-autoclose',
|
||||
$request->getStr('autoclose') == 'disabled' ? true : false);
|
||||
|
||||
$repository->setDetail(
|
||||
'pull-frequency',
|
||||
max(1, $request->getInt('frequency')));
|
||||
|
||||
if ($has_branches) {
|
||||
$repository->setDetail(
|
||||
'default-branch',
|
||||
$request->getStr('default-branch'));
|
||||
if ($is_git) {
|
||||
$branch_name = $repository->getDetail('default-branch');
|
||||
if (strpos($branch_name, '/') !== false) {
|
||||
$e_branch = 'Invalid';
|
||||
$errors[] = "Your branch name should not specify an explicit ".
|
||||
"remote. For instance, use 'master', not ".
|
||||
"'origin/master'.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$repository->setDetail(
|
||||
'default-owners-path',
|
||||
$request->getStr(
|
||||
'default-owners-path',
|
||||
'/'));
|
||||
|
||||
$repository->setDetail('ssh-login', $request->getStr('ssh-login'));
|
||||
$repository->setDetail('ssh-key', $request->getStr('ssh-key'));
|
||||
$repository->setDetail('ssh-keyfile', $request->getStr('ssh-keyfile'));
|
||||
|
||||
$repository->setDetail('http-login', $request->getStr('http-login'));
|
||||
$repository->setDetail('http-pass', $request->getStr('http-pass'));
|
||||
|
||||
$repository->setDetail('show-user', $request->getInt('show-user'));
|
||||
|
||||
if ($repository->getDetail('ssh-key') &&
|
||||
$repository->getDetail('ssh-keyfile')) {
|
||||
$errors[] =
|
||||
"Specify only one of 'SSH Private Key' and 'SSH Private Key File', ".
|
||||
"not both.";
|
||||
$e_ssh_key = 'Choose Only One';
|
||||
$e_ssh_keyfile = 'Choose Only One';
|
||||
}
|
||||
|
||||
$repository->setDetail(
|
||||
'herald-disabled',
|
||||
$request->getInt('herald-disabled', 0));
|
||||
|
||||
if ($is_svn) {
|
||||
$repository->setUUID($request->getStr('uuid'));
|
||||
$subpath = ltrim($request->getStr('svn-subpath'), '/');
|
||||
if ($subpath) {
|
||||
$subpath = rtrim($subpath, '/').'/';
|
||||
}
|
||||
$repository->setDetail('svn-subpath', $subpath);
|
||||
}
|
||||
|
||||
if ($tracking) {
|
||||
if (!$repository->getDetail('remote-uri')) {
|
||||
$e_uri = 'Required';
|
||||
$errors[] = "Repository URI is required.";
|
||||
} else if ($is_svn &&
|
||||
!preg_match('@/$@', $repository->getDetail('remote-uri'))) {
|
||||
|
||||
$e_uri = 'Invalid';
|
||||
$errors[] = 'Subversion Repository Root must end in a slash ("/").';
|
||||
} else {
|
||||
$e_uri = null;
|
||||
}
|
||||
|
||||
if ($has_local) {
|
||||
if (!$repository->getDetail('local-path')) {
|
||||
$e_path = 'Required';
|
||||
$errors[] = "Local path is required.";
|
||||
} else {
|
||||
$e_path = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$repository->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/repository/edit/'.$repository_id.'/tracking/?saved=true');
|
||||
}
|
||||
}
|
||||
|
||||
$error_view = null;
|
||||
if ($errors) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setErrors($errors);
|
||||
$error_view->setTitle('Form Errors');
|
||||
} else if ($request->getStr('saved')) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
|
||||
$error_view->setTitle('Changes Saved');
|
||||
$error_view->appendChild('Tracking changes were saved.');
|
||||
} else if (!$repository->isTracked()) {
|
||||
$error_view = new AphrontErrorView();
|
||||
$error_view->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
||||
$error_view->setTitle('Repository Not Tracked');
|
||||
$error_view->appendChild(
|
||||
'Tracking is currently "Disabled" for this repository, so it will '.
|
||||
'not be imported into Phabricator. You can enable it below.');
|
||||
}
|
||||
|
||||
switch ($repository->getVersionControlSystem()) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
$is_git = true;
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
$is_svn = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$doc_href = PhabricatorEnv::getDoclink('article/Diffusion_User_Guide.html');
|
||||
$user_guide_link = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $doc_href,
|
||||
),
|
||||
'Diffusion User Guide');
|
||||
|
||||
$form = new AphrontFormView();
|
||||
$form
|
||||
->setUser($user)
|
||||
->setAction('/repository/edit/'.$repository->getID().'/tracking/')
|
||||
->appendChild(hsprintf(
|
||||
'<p class="aphront-form-instructions">Phabricator can track '.
|
||||
'repositories, importing commits as they happen and notifying '.
|
||||
'Differential, Diffusion, Herald, and other services. To enable '.
|
||||
'tracking for a repository, configure it here and then start (or '.
|
||||
'restart) the daemons. More information is available in the '.
|
||||
'<strong>%s</strong>.</p>',
|
||||
$user_guide_link));
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormInsetView())
|
||||
->setTitle('Basics')
|
||||
->appendChild(id(new AphrontFormStaticControl())
|
||||
->setLabel('Repository Name')
|
||||
->setValue($repository->getName()))
|
||||
->appendChild(id(new AphrontFormSelectControl())
|
||||
->setName('tracking')
|
||||
->setLabel('Tracking')
|
||||
->setOptions(array(
|
||||
'disabled' => 'Disabled',
|
||||
'enabled' => 'Enabled',
|
||||
))
|
||||
->setValue(
|
||||
$repository->isTracked()
|
||||
? 'enabled'
|
||||
: 'disabled')));
|
||||
|
||||
$inset = new AphrontFormInsetView();
|
||||
$inset->setTitle('Remote URI');
|
||||
|
||||
$clone_command = null;
|
||||
$fetch_command = null;
|
||||
if ($is_git) {
|
||||
$clone_command = 'git clone';
|
||||
$fetch_command = 'git fetch';
|
||||
} else if ($is_mercurial) {
|
||||
$clone_command = 'hg clone';
|
||||
$fetch_command = 'hg pull';
|
||||
}
|
||||
|
||||
$uri_label = 'Repository URI';
|
||||
if ($has_local) {
|
||||
if ($is_git) {
|
||||
$instructions = hsprintf(
|
||||
'Enter the URI to clone this repository from. It should look like '.
|
||||
'<tt>git@github.com:example/example.git</tt>, '.
|
||||
'<tt>ssh://user@host.com/git/example.git</tt>, or '.
|
||||
'<tt>file:///local/path/to/repo</tt>');
|
||||
} else if ($is_mercurial) {
|
||||
$instructions = hsprintf(
|
||||
'Enter the URI to clone this repository from. It should look '.
|
||||
'something like <tt>ssh://user@host.com/hg/example</tt>');
|
||||
}
|
||||
$inset->appendChild(hsprintf(
|
||||
'<p class="aphront-form-instructions">%s</p>',
|
||||
$instructions));
|
||||
} else if ($is_svn) {
|
||||
$instructions = hsprintf(
|
||||
'Enter the <strong>Repository Root</strong> for this SVN repository. '.
|
||||
'You can figure this out by running <tt>svn info</tt> and looking at '.
|
||||
'the value in the <tt>Repository Root</tt> field. It should be a URI '.
|
||||
'and look like <tt>http://svn.example.org/svn/</tt>, '.
|
||||
'<tt>svn+ssh://svn.example.com/svnroot/</tt>, or '.
|
||||
'<tt>svn://svn.example.net/svn/</tt>');
|
||||
$inset->appendChild(hsprintf(
|
||||
'<p class="aphront-form-instructions">%s</p>',
|
||||
$instructions));
|
||||
$uri_label = 'Repository Root';
|
||||
}
|
||||
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('uri')
|
||||
->setLabel($uri_label)
|
||||
->setID('remote-uri')
|
||||
->setValue($repository->getDetail('remote-uri'))
|
||||
->setError($e_uri));
|
||||
|
||||
$inset->appendChild(
|
||||
id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'show-user',
|
||||
1,
|
||||
pht('Permit users to view the username of this connection.'),
|
||||
$repository->getDetail('show-user') == 1));
|
||||
|
||||
$inset->appendChild(hsprintf(
|
||||
'<div class="aphront-form-instructions">'.
|
||||
'If you want to connect to this repository over SSH, enter the '.
|
||||
'username and private key to use. You can leave these fields blank if '.
|
||||
'the repository does not use SSH.'.
|
||||
'</div>'));
|
||||
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('ssh-login')
|
||||
->setLabel('SSH User')
|
||||
->setValue($repository->getDetail('ssh-login')))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setName('ssh-key')
|
||||
->setLabel('SSH Private Key')
|
||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
|
||||
->setValue($repository->getDetail('ssh-key'))
|
||||
->setError($e_ssh_key)
|
||||
->setCaption(
|
||||
hsprintf('Specify the entire private key, <em>or</em>...')))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('ssh-keyfile')
|
||||
->setLabel('SSH Private Key File')
|
||||
->setValue($repository->getDetail('ssh-keyfile'))
|
||||
->setError($e_ssh_keyfile)
|
||||
->setCaption(
|
||||
'...specify a path on disk where the daemon should '.
|
||||
'look for a private key.'));
|
||||
|
||||
if ($has_auth_support) {
|
||||
$inset
|
||||
->appendChild(hsprintf(
|
||||
'<div class="aphront-form-instructions">'.
|
||||
'If you want to connect to this repository with a username and '.
|
||||
'password, such as over HTTP Basic Auth or SVN with SASL, '.
|
||||
'enter the username and password to use. You can leave these '.
|
||||
'fields blank if the repository does not use a username and '.
|
||||
'password for authentication.'.
|
||||
'</div>'))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('http-login')
|
||||
->setLabel('Username')
|
||||
->setValue($repository->getDetail('http-login')))
|
||||
->appendChild(
|
||||
id(new AphrontFormPasswordControl())
|
||||
->setName('http-pass')
|
||||
->setLabel('Password')
|
||||
->setValue($repository->getDetail('http-pass')));
|
||||
}
|
||||
|
||||
$inset
|
||||
->appendChild(hsprintf(
|
||||
'<div class="aphront-form-important">'.
|
||||
'To test your authentication configuration, <strong>save this '.
|
||||
'form</strong> and then run this script:'.
|
||||
'<code>'.
|
||||
'phabricator/ $ ./scripts/repository/test_connection.php %s'.
|
||||
'</code>'.
|
||||
'This will verify that your configuration is correct and the '.
|
||||
'daemons can connect to the remote repository and pull changes '.
|
||||
'from it.'.
|
||||
'</div>',
|
||||
$repository->getCallsign()));
|
||||
|
||||
$form->appendChild($inset);
|
||||
|
||||
$inset = new AphrontFormInsetView();
|
||||
$inset->setTitle('Repository Information');
|
||||
|
||||
if ($has_local) {
|
||||
$default_local_path = '';
|
||||
$default =
|
||||
PhabricatorEnv::getEnvConfig('repository.default-local-path');
|
||||
if (!$repository->getDetail('remote-uri') && $default) {
|
||||
$default_local_path = $default.strtolower($repository->getCallsign());
|
||||
}
|
||||
$inset->appendChild(hsprintf(
|
||||
'<p class="aphront-form-instructions">Select a path on local disk '.
|
||||
'which the daemons should <tt>%s</tt> the repository into. This must '.
|
||||
'be readable and writable by the daemons, and readable by the '.
|
||||
'webserver. The daemons will <tt>%s</tt> and keep this repository up '.
|
||||
'to date.</p>',
|
||||
$clone_command,
|
||||
$fetch_command));
|
||||
$inset->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('path')
|
||||
->setLabel('Local Path')
|
||||
->setValue($repository->getDetail('local-path', $default_local_path))
|
||||
->setError($e_path));
|
||||
} else if ($is_svn) {
|
||||
$inset->appendChild(hsprintf(
|
||||
'<p class="aphront-form-instructions">If you only want to parse one '.
|
||||
'subpath of the repository, specify it here, relative to the '.
|
||||
'repository root (e.g., <tt>trunk/</tt> or <tt>projects/wheel/</tt>). '.
|
||||
'If you want to parse multiple subdirectories, create a separate '.
|
||||
'Phabricator repository for each one.</p>'));
|
||||
$inset->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('svn-subpath')
|
||||
->setLabel('Subpath')
|
||||
->setValue($repository->getDetail('svn-subpath'))
|
||||
->setError($e_path));
|
||||
}
|
||||
|
||||
if ($has_branch_filter) {
|
||||
$branch_filter_str = implode(
|
||||
', ',
|
||||
array_keys($repository->getDetail('branch-filter', array())));
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('branch-filter')
|
||||
->setLabel('Track Only')
|
||||
->setValue($branch_filter_str)
|
||||
->setCaption(hsprintf(
|
||||
'Optional list of branches to track. Other branches will be '.
|
||||
'completely ignored. If left empty, all branches are tracked. '.
|
||||
'Example: <tt>master, release</tt>')));
|
||||
}
|
||||
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('frequency')
|
||||
->setLabel('Pull Frequency')
|
||||
->setValue($repository->getDetail('pull-frequency', 15))
|
||||
->setCaption(
|
||||
'Number of seconds daemon should sleep between requests. Larger '.
|
||||
'numbers reduce load but also decrease responsiveness.'));
|
||||
|
||||
$form->appendChild($inset);
|
||||
|
||||
$inset = new AphrontFormInsetView();
|
||||
$inset->setTitle('Application Configuration');
|
||||
|
||||
if ($has_branches) {
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('default-branch')
|
||||
->setLabel('Default Branch')
|
||||
->setValue($repository->getDefaultBranch())
|
||||
->setError($e_branch)
|
||||
->setCaption(
|
||||
'Default branch to show in Diffusion.'));
|
||||
}
|
||||
|
||||
$inset
|
||||
->appendChild(id(new AphrontFormSelectControl())
|
||||
->setName('autoclose')
|
||||
->setLabel('Autoclose')
|
||||
->setOptions(array(
|
||||
'enabled' => 'Enabled: Automatically Close Pushed Revisions',
|
||||
'disabled' => 'Disabled: Ignore Pushed Revisions',
|
||||
))
|
||||
->setCaption(
|
||||
"Automatically close Differential revisions when associated commits ".
|
||||
"are pushed to this repository.")
|
||||
->setValue(
|
||||
$repository->getDetail('disable-autoclose', false)
|
||||
? 'disabled'
|
||||
: 'enabled'));
|
||||
|
||||
if ($has_branch_filter) {
|
||||
$close_commits_filter_str = implode(
|
||||
', ',
|
||||
array_keys($repository->getDetail('close-commits-filter', array())));
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('close-commits-filter')
|
||||
->setLabel('Autoclose Branches')
|
||||
->setValue($close_commits_filter_str)
|
||||
->setCaption(
|
||||
'Optional list of branches which can trigger autoclose. '.
|
||||
'If left empty, all branches trigger autoclose.'));
|
||||
}
|
||||
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('default-owners-path')
|
||||
->setLabel('Default Owners Path')
|
||||
->setValue(
|
||||
$repository->getDetail(
|
||||
'default-owners-path',
|
||||
'/'))
|
||||
->setCaption('Default path in Owners tool.'));
|
||||
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('herald-disabled')
|
||||
->setLabel('Herald/Feed Enabled')
|
||||
->setValue($repository->getDetail('herald-disabled', 0))
|
||||
->setOptions(
|
||||
array(
|
||||
0 => 'Enabled - Send Email and Publish Stories',
|
||||
1 => 'Disabled - Do Not Send Email or Publish Stories',
|
||||
))
|
||||
->setCaption(
|
||||
'You can disable Herald commit notifications and feed stories '.
|
||||
'for this repository. This can be useful when initially importing '.
|
||||
'a repository. Feed stories are never published about commits '.
|
||||
'that are more than 24 hours old.'));
|
||||
|
||||
if ($is_svn) {
|
||||
$inset
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('uuid')
|
||||
->setLabel('UUID')
|
||||
->setValue($repository->getUUID())
|
||||
->setCaption(hsprintf('Repository UUID from <tt>svn info</tt>.')));
|
||||
}
|
||||
|
||||
$form->appendChild($inset);
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue('Save Configuration'));
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Edit Repository Tracking'))
|
||||
->setFormError($error_view)
|
||||
->setForm($form);
|
||||
|
||||
$nav = $this->sideNav;
|
||||
$nav->appendChild($form_box);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$nav,
|
||||
array(
|
||||
'title' => pht('Edit Repository Tracking'),
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -38,7 +38,7 @@ final class PhabricatorRepositoryListController
|
|||
'a',
|
||||
array(
|
||||
'class' => 'button small grey',
|
||||
'href' => '/repository/edit/'.$repo->getID().'/',
|
||||
'href' => '/diffusion/'.$repo->getCallsign().'/edit/',
|
||||
),
|
||||
'Edit'),
|
||||
);
|
||||
|
@ -74,7 +74,7 @@ final class PhabricatorRepositoryListController
|
|||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader('Repositories');
|
||||
if ($is_admin) {
|
||||
$panel->setCreateButton('Create New Repository', '/repository/create/');
|
||||
$panel->setCreateButton('Create New Repository', '/diffusion/create/');
|
||||
}
|
||||
$panel->appendChild($table);
|
||||
$panel->setNoBackground();
|
||||
|
|
|
@ -128,7 +128,8 @@ final class PhabricatorRepositoryPullLocalDaemon
|
|||
|
||||
if (!$no_discovery) {
|
||||
// TODO: It would be nice to discover only if we pulled something,
|
||||
// but this isn't totally trivial.
|
||||
// but this isn't totally trivial. It's slightly more complicated
|
||||
// with hosted repositories, too.
|
||||
|
||||
$lock_name = get_class($this).':'.$callsign;
|
||||
$lock = PhabricatorGlobalLock::newLock($lock_name);
|
||||
|
@ -200,28 +201,41 @@ final class PhabricatorRepositoryPullLocalDaemon
|
|||
|
||||
public function discoverRepository(PhabricatorRepository $repository) {
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
|
||||
$result = null;
|
||||
$refs = null;
|
||||
switch ($vcs) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
return $this->executeGitDiscover($repository);
|
||||
$result = $this->executeGitDiscover($repository);
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
$refs = $this->getDiscoveryEngine($repository)
|
||||
->discoverCommits();
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
return $this->executeHgDiscover($repository);
|
||||
$result = $this->executeHgDiscover($repository);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown VCS '{$vcs}'!");
|
||||
}
|
||||
|
||||
foreach ($refs as $ref) {
|
||||
$this->recordCommit(
|
||||
$repository,
|
||||
$ref->getIdentifier(),
|
||||
$ref->getEpoch(),
|
||||
$ref->getBranch());
|
||||
if ($refs !== null) {
|
||||
foreach ($refs as $ref) {
|
||||
$this->recordCommit(
|
||||
$repository,
|
||||
$ref->getIdentifier(),
|
||||
$ref->getEpoch(),
|
||||
$ref->getBranch());
|
||||
}
|
||||
}
|
||||
|
||||
return (bool)count($refs);
|
||||
$this->checkIfRepositoryIsFullyImported($repository);
|
||||
|
||||
if ($refs !== null) {
|
||||
return (bool)count($refs);
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
private function getDiscoveryEngine(PhabricatorRepository $repository) {
|
||||
|
@ -455,7 +469,39 @@ final class PhabricatorRepositoryPullLocalDaemon
|
|||
return $repository->getID().':'.$commit_identifier;
|
||||
}
|
||||
|
||||
private function checkIfRepositoryIsFullyImported(
|
||||
PhabricatorRepository $repository) {
|
||||
|
||||
// Check if the repository has the "Importing" flag set. We want to clear
|
||||
// the flag if we can.
|
||||
$importing = $repository->getDetail('importing');
|
||||
if (!$importing) {
|
||||
// This repository isn't marked as "Importing", so we're done.
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for any commit which hasn't imported.
|
||||
$unparsed_commit = queryfx_one(
|
||||
$repository->establishConnection('r'),
|
||||
'SELECT * FROM %T WHERE repositoryID = %d AND importStatus != %d',
|
||||
id(new PhabricatorRepositoryCommit())->getTableName(),
|
||||
$repository->getID(),
|
||||
PhabricatorRepositoryCommit::IMPORTED_ALL);
|
||||
if ($unparsed_commit) {
|
||||
// We found a commit which still needs to import, so we can't clear the
|
||||
// flag.
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the "importing" flag.
|
||||
$repository->openTransaction();
|
||||
$repository->beginReadLocking();
|
||||
$repository = $repository->reload();
|
||||
$repository->setDetail('importing', false);
|
||||
$repository->save();
|
||||
$repository->endReadLocking();
|
||||
$repository->saveTransaction();
|
||||
}
|
||||
|
||||
/* -( Git Implementation )------------------------------------------------- */
|
||||
|
||||
|
@ -487,6 +533,12 @@ final class PhabricatorRepositoryPullLocalDaemon
|
|||
$stdout,
|
||||
$only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE);
|
||||
|
||||
if (!$branches) {
|
||||
// This repository has no branches at all, so we don't need to do
|
||||
// anything. Generally, this means the repository is empty.
|
||||
return;
|
||||
}
|
||||
|
||||
$callsign = $repository->getCallsign();
|
||||
|
||||
$tracked_something = false;
|
||||
|
|
|
@ -6,6 +6,7 @@ final class PhabricatorRepositoryEditor
|
|||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_VCS;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_NAME;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
|
||||
|
@ -24,6 +25,10 @@ final class PhabricatorRepositoryEditor
|
|||
$types[] = PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_HTTP_PASS;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_HOSTING;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
|
||||
$types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
@ -36,6 +41,8 @@ final class PhabricatorRepositoryEditor
|
|||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorRepositoryTransaction::TYPE_VCS:
|
||||
return $object->getVersionControlSystem();
|
||||
case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
|
||||
return $object->isTracked();
|
||||
case PhabricatorRepositoryTransaction::TYPE_NAME:
|
||||
|
@ -72,6 +79,14 @@ final class PhabricatorRepositoryEditor
|
|||
return $object->getDetail('http-pass');
|
||||
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
||||
return $object->getDetail('local-path');
|
||||
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
||||
return $object->isHosted();
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
|
||||
return $object->getServeOverHTTP();
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
|
||||
return $object->getServeOverSSH();
|
||||
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
|
||||
return $object->getPushPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +111,11 @@ final class PhabricatorRepositoryEditor
|
|||
case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
|
||||
case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
|
||||
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
||||
case PhabricatorRepositoryTransaction::TYPE_VCS:
|
||||
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
|
||||
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
|
||||
return $xaction->getNewValue();
|
||||
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
|
||||
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
|
||||
|
@ -108,6 +128,9 @@ final class PhabricatorRepositoryEditor
|
|||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorRepositoryTransaction::TYPE_VCS:
|
||||
$object->setVersionControlSystem($xaction->getNewValue());
|
||||
break;
|
||||
case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
|
||||
$object->setDetail('tracking-enabled', $xaction->getNewValue());
|
||||
break;
|
||||
|
@ -163,6 +186,14 @@ final class PhabricatorRepositoryEditor
|
|||
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
||||
$object->setDetail('local-path', $xaction->getNewValue());
|
||||
break;
|
||||
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
||||
return $object->setHosted($xaction->getNewValue());
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
|
||||
return $object->setServeOverHTTP($xaction->getNewValue());
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
|
||||
return $object->setServeOverSSH($xaction->getNewValue());
|
||||
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
|
||||
return $object->setPushPolicy($xaction->getNewValue());
|
||||
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
|
||||
// Make sure the encoding is valid by converting to UTF-8. This tests
|
||||
// that the user has mbstring installed, and also that they didn't type
|
||||
|
@ -219,4 +250,40 @@ final class PhabricatorRepositoryEditor
|
|||
return parent::transactionHasEffect($object, $xaction);
|
||||
}
|
||||
|
||||
protected function requireCapabilities(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
|
||||
case PhabricatorRepositoryTransaction::TYPE_NAME:
|
||||
case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
|
||||
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
|
||||
case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH:
|
||||
case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY:
|
||||
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
|
||||
case PhabricatorRepositoryTransaction::TYPE_UUID:
|
||||
case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
|
||||
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
|
||||
case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
|
||||
case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
|
||||
case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
|
||||
case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
|
||||
case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
|
||||
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
||||
case PhabricatorRepositoryTransaction::TYPE_VCS:
|
||||
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
|
||||
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
|
||||
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
|
||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
|
||||
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$this->requireActor(),
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ abstract class PhabricatorRepositoryEngine {
|
|||
if ($this->getVerbose()) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$argv = func_get_args();
|
||||
call_user_func_array(array($console, 'writeLog'), $argv);
|
||||
$argv[0] = $argv[0]."\n";
|
||||
call_user_func_array(array($console, 'writeOut'), $argv);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,16 @@ final class PhabricatorRepositoryPullEngine
|
|||
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
$callsign = $repository->getCallsign();
|
||||
|
||||
if ($repository->isHosted()) {
|
||||
$this->log(
|
||||
pht(
|
||||
'Repository "%s" is hosted, so Phabricator does not pull updates '.
|
||||
'for it.',
|
||||
$callsign));
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($vcs) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
// We never pull a local copy of Subversion repositories.
|
||||
|
@ -89,7 +99,7 @@ final class PhabricatorRepositoryPullEngine
|
|||
$repository = $this->getRepository();
|
||||
|
||||
$repository->execxRemoteCommand(
|
||||
'clone --origin origin %s %s',
|
||||
'clone --bare %s %s',
|
||||
$repository->getRemoteURI(),
|
||||
rtrim($repository->getLocalPath(), '/'));
|
||||
}
|
||||
|
@ -139,12 +149,11 @@ final class PhabricatorRepositoryPullEngine
|
|||
$repo_path = rtrim($stdout, "\n");
|
||||
|
||||
if (empty($repo_path)) {
|
||||
$err = true;
|
||||
$message =
|
||||
"Expected to find a git repository at '{$path}', but ".
|
||||
"there was no result from `git rev-parse --show-toplevel`. ".
|
||||
"Something is misconfigured or broken. The git repository ".
|
||||
"may be inside a '.git/' directory.";
|
||||
// This can mean one of two things: we're in a bare repository, or
|
||||
// we're inside a git repository inside another git repository. Since
|
||||
// the first is dramatically more likely now that we perform bare
|
||||
// clones and I don't have a great way to test for the latter, assume
|
||||
// we're OK.
|
||||
} else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) {
|
||||
$err = true;
|
||||
$message =
|
||||
|
|
|
@ -8,7 +8,7 @@ final class PhabricatorWorkingCopyPullTestCase
|
|||
|
||||
$this->assertEqual(
|
||||
true,
|
||||
Filesystem::pathExists($repo->getLocalPath().'/.git'));
|
||||
Filesystem::pathExists($repo->getLocalPath().'/HEAD'));
|
||||
}
|
||||
|
||||
public function testHgPullBasic() {
|
||||
|
|
|
@ -45,7 +45,8 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase {
|
|||
$dir = PhutilDirectoryFixture::newFromArchive($path);
|
||||
$local = new TempFile('.ignore');
|
||||
|
||||
$repo = id(new PhabricatorRepository())
|
||||
$user = $this->generateNewTestUser();
|
||||
$repo = PhabricatorRepository::initializeNewRepository($user)
|
||||
->setCallsign($callsign)
|
||||
->setName(pht('Test Repo "%s"', $callsign))
|
||||
->setVersionControlSystem($vcs_type)
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* In Git, there appears to be no way to send a message which will be output
|
||||
* by `git clone http://...`, although the response code is visible.
|
||||
*
|
||||
* In Mercurial, the HTTP status response message is printed to the console, so
|
||||
* we send human-readable text there.
|
||||
*
|
||||
* In Subversion, we can get it to print a custom message if we send an
|
||||
* invalid/unknown response code, although the output is ugly and difficult
|
||||
* to read. For known codes like 404, it prints a canned message.
|
||||
*
|
||||
* All VCS binaries ignore the response body; we include it only for
|
||||
* completeness.
|
||||
*/
|
||||
final class PhabricatorVCSResponse extends AphrontResponse {
|
||||
|
||||
private $code;
|
||||
private $message;
|
||||
|
||||
public function __construct($code, $message) {
|
||||
$this->code = $code;
|
||||
|
||||
$message = head(phutil_split_lines($message));
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function getMessage() {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function buildResponseString() {
|
||||
return $this->code.' '.$this->message;
|
||||
}
|
||||
|
||||
public function getHeaders() {
|
||||
$headers = array();
|
||||
|
||||
if ($this->getHTTPResponseCode() == 401) {
|
||||
$headers[] = array(
|
||||
'WWW-Authenticate',
|
||||
'Basic realm="Phabricator Repositories"',
|
||||
);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
public function getCacheHeaders() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getHTTPResponseCode() {
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getHTTPResponseMessage() {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,11 +26,16 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
const TABLE_BADCOMMIT = 'repository_badcommit';
|
||||
const TABLE_LINTMESSAGE = 'repository_lintmessage';
|
||||
|
||||
const SERVE_OFF = 'off';
|
||||
const SERVE_READONLY = 'readonly';
|
||||
const SERVE_READWRITE = 'readwrite';
|
||||
|
||||
protected $name;
|
||||
protected $callsign;
|
||||
protected $uuid;
|
||||
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
|
||||
protected $editPolicy = PhabricatorPolicies::POLICY_ADMIN;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $pushPolicy;
|
||||
|
||||
protected $versionControlSystem;
|
||||
protected $details = array();
|
||||
|
@ -40,6 +45,22 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
private $commitCount = self::ATTACHABLE;
|
||||
private $mostRecentCommit = self::ATTACHABLE;
|
||||
|
||||
public static function initializeNewRepository(PhabricatorUser $actor) {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($actor)
|
||||
->withClasses(array('PhabricatorApplicationDiffusion'))
|
||||
->executeOne();
|
||||
|
||||
$view_policy = $app->getPolicy(DiffusionCapabilityDefaultView::CAPABILITY);
|
||||
$edit_policy = $app->getPolicy(DiffusionCapabilityDefaultEdit::CAPABILITY);
|
||||
$push_policy = $app->getPolicy(DiffusionCapabilityDefaultPush::CAPABILITY);
|
||||
|
||||
return id(new PhabricatorRepository())
|
||||
->setViewPolicy($view_policy)
|
||||
->setEditPolicy($edit_policy)
|
||||
->setPushPolicy($push_policy);
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
|
@ -171,24 +192,32 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
}
|
||||
|
||||
public function execLocalCommand($pattern /* , $arg, ... */) {
|
||||
$this->assertLocalExists();
|
||||
|
||||
$args = func_get_args();
|
||||
$args = $this->formatLocalCommand($args);
|
||||
return call_user_func_array('exec_manual', $args);
|
||||
}
|
||||
|
||||
public function execxLocalCommand($pattern /* , $arg, ... */) {
|
||||
$this->assertLocalExists();
|
||||
|
||||
$args = func_get_args();
|
||||
$args = $this->formatLocalCommand($args);
|
||||
return call_user_func_array('execx', $args);
|
||||
}
|
||||
|
||||
public function getLocalCommandFuture($pattern /* , $arg, ... */) {
|
||||
$this->assertLocalExists();
|
||||
|
||||
$args = func_get_args();
|
||||
$args = $this->formatLocalCommand($args);
|
||||
return newv('ExecFuture', $args);
|
||||
}
|
||||
|
||||
public function passthruLocalCommand($pattern /* , $arg, ... */) {
|
||||
$this->assertLocalExists();
|
||||
|
||||
$args = func_get_args();
|
||||
$args = $this->formatLocalCommand($args);
|
||||
return call_user_func_array('phutil_passthru', $args);
|
||||
|
@ -412,6 +441,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
}
|
||||
|
||||
public function shouldAutocloseBranch($branch) {
|
||||
if ($this->isImporting()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->getDetail('disable-autoclose', false)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -465,6 +498,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return 'r'.$this->getCallsign().$short_identifier;
|
||||
}
|
||||
|
||||
public function isImporting() {
|
||||
return (bool)$this->getDetail('importing', false);
|
||||
}
|
||||
|
||||
/* -( Repository URI Management )------------------------------------------ */
|
||||
|
||||
|
||||
|
@ -691,6 +728,64 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL);
|
||||
}
|
||||
|
||||
public function isHosted() {
|
||||
return (bool)$this->getDetail('hosting-enabled', false);
|
||||
}
|
||||
|
||||
public function setHosted($enabled) {
|
||||
return $this->setDetail('hosting-enabled', $enabled);
|
||||
}
|
||||
|
||||
public function getServeOverHTTP() {
|
||||
return $this->getDetail('serve-over-http', self::SERVE_OFF);
|
||||
}
|
||||
|
||||
public function setServeOverHTTP($mode) {
|
||||
return $this->setDetail('serve-over-http', $mode);
|
||||
}
|
||||
|
||||
public function getServeOverSSH() {
|
||||
return $this->getDetail('serve-over-ssh', self::SERVE_OFF);
|
||||
}
|
||||
|
||||
public function setServeOverSSH($mode) {
|
||||
return $this->setDetail('serve-over-ssh', $mode);
|
||||
}
|
||||
|
||||
public static function getProtocolAvailabilityName($constant) {
|
||||
switch ($constant) {
|
||||
case self::SERVE_OFF:
|
||||
return pht('Off');
|
||||
case self::SERVE_READONLY:
|
||||
return pht('Read Only');
|
||||
case self::SERVE_READWRITE:
|
||||
return pht('Read/Write');
|
||||
default:
|
||||
return pht('Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Raise more useful errors when there are basic filesystem problems.
|
||||
*/
|
||||
private function assertLocalExists() {
|
||||
switch ($this->getVersionControlSystem()) {
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
if (!$this->isHosted()) {
|
||||
// For non-hosted SVN repositories, we don't expect a local directory
|
||||
// to exist.
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$local = $this->getLocalPath();
|
||||
Filesystem::assertExists($local);
|
||||
Filesystem::assertIsDirectory($local);
|
||||
Filesystem::assertReadable($local);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@ -699,6 +794,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
DiffusionCapabilityPush::CAPABILITY,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -708,6 +804,8 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return $this->getEditPolicy();
|
||||
case DiffusionCapabilityPush::CAPABILITY:
|
||||
return $this->getPushPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,13 @@ final class PhabricatorRepositoryCommit
|
|||
protected $authorPHID;
|
||||
protected $auditStatus = PhabricatorAuditCommitStatusConstants::NONE;
|
||||
protected $summary = '';
|
||||
protected $importStatus = 0;
|
||||
|
||||
const IMPORTED_MESSAGE = 1;
|
||||
const IMPORTED_CHANGE = 2;
|
||||
const IMPORTED_OWNERS = 4;
|
||||
const IMPORTED_HERALD = 8;
|
||||
const IMPORTED_ALL = 15;
|
||||
|
||||
private $commitData = self::ATTACHABLE;
|
||||
private $audits;
|
||||
|
@ -39,6 +46,20 @@ final class PhabricatorRepositoryCommit
|
|||
return $this->isUnparsed;
|
||||
}
|
||||
|
||||
public function isImported() {
|
||||
return ($this->getImportStatus() == self::IMPORTED_ALL);
|
||||
}
|
||||
|
||||
public function writeImportStatusFlag($flag) {
|
||||
queryfx(
|
||||
$this->establishConnection('w'),
|
||||
'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d',
|
||||
$this->getTableName(),
|
||||
$flag,
|
||||
$this->getID());
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
final class PhabricatorRepositoryTransaction
|
||||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
const TYPE_VCS = 'repo:vcs';
|
||||
const TYPE_ACTIVATE = 'repo:activate';
|
||||
const TYPE_NAME = 'repo:name';
|
||||
const TYPE_DESCRIPTION = 'repo:description';
|
||||
|
@ -21,6 +22,10 @@ final class PhabricatorRepositoryTransaction
|
|||
const TYPE_HTTP_LOGIN = 'repo:http-login';
|
||||
const TYPE_HTTP_PASS = 'repo:http-pass';
|
||||
const TYPE_LOCAL_PATH = 'repo:local-path';
|
||||
const TYPE_HOSTING = 'repo:hosting';
|
||||
const TYPE_PROTOCOL_HTTP = 'repo:serve-http';
|
||||
const TYPE_PROTOCOL_SSH = 'repo:serve-ssh';
|
||||
const TYPE_PUSH_POLICY = 'repo:push-policy';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'repository';
|
||||
|
@ -34,6 +39,64 @@ final class PhabricatorRepositoryTransaction
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getRequiredHandlePHIDs() {
|
||||
$phids = parent::getRequiredHandlePHIDs();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_PUSH_POLICY:
|
||||
$phids[] = $old;
|
||||
$phids[] = $new;
|
||||
break;
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_REMOTE_URI:
|
||||
case self::TYPE_SSH_LOGIN:
|
||||
case self::TYPE_SSH_KEY:
|
||||
case self::TYPE_SSH_KEYFILE:
|
||||
case self::TYPE_HTTP_LOGIN:
|
||||
case self::TYPE_HTTP_PASS:
|
||||
// Hide null vs empty string changes.
|
||||
return (!strlen($old) && !strlen($new));
|
||||
case self::TYPE_LOCAL_PATH:
|
||||
case self::TYPE_NAME:
|
||||
// Hide these on create, they aren't interesting and we have an
|
||||
// explicit "create" transaction.
|
||||
if (!strlen($old)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::shouldHide();
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_VCS:
|
||||
return 'create';
|
||||
}
|
||||
return parent::getIcon();
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_VCS:
|
||||
return 'green';
|
||||
}
|
||||
return parent::getIcon();
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
|
||||
|
@ -41,6 +104,10 @@ final class PhabricatorRepositoryTransaction
|
|||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_VCS:
|
||||
return pht(
|
||||
'%s created this repository.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case self::TYPE_ACTIVATE:
|
||||
if ($new) {
|
||||
return pht(
|
||||
|
@ -238,6 +305,36 @@ final class PhabricatorRepositoryTransaction
|
|||
$this->renderHandleLink($author_phid),
|
||||
$old,
|
||||
$new);
|
||||
case self::TYPE_HOSTING:
|
||||
if ($new) {
|
||||
return pht(
|
||||
'%s changed this repository to be hosted on Phabricator.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed this repository to track a remote elsewhere.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
case self::TYPE_PROTOCOL_HTTP:
|
||||
return pht(
|
||||
'%s changed the availability of this repository over HTTP from '.
|
||||
'"%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
PhabricatorRepository::getProtocolAvailabilityName($old),
|
||||
PhabricatorRepository::getProtocolAvailabilityName($new));
|
||||
case self::TYPE_PROTOCOL_SSH:
|
||||
return pht(
|
||||
'%s changed the availability of this repository over SSH from '.
|
||||
'"%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
PhabricatorRepository::getProtocolAvailabilityName($old),
|
||||
PhabricatorRepository::getProtocolAvailabilityName($new));
|
||||
case self::TYPE_PUSH_POLICY:
|
||||
return pht(
|
||||
'%s changed the push policy of this repository from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderPolicyName($old),
|
||||
$this->renderPolicyName($new));
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
|
@ -263,6 +360,5 @@ final class PhabricatorRepositoryTransaction
|
|||
return $view->render();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,25 @@ final class PhabricatorRepositoryCommitHeraldWorker
|
|||
PhabricatorRepository $repository,
|
||||
PhabricatorRepositoryCommit $commit) {
|
||||
|
||||
$result = $this->applyHeraldRules($repository, $commit);
|
||||
|
||||
$commit->writeImportStatusFlag(
|
||||
PhabricatorRepositoryCommit::IMPORTED_HERALD);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function applyHeraldRules(
|
||||
PhabricatorRepository $repository,
|
||||
PhabricatorRepositoryCommit $commit) {
|
||||
|
||||
// Don't take any actions on an importing repository. Principally, this
|
||||
// avoids generating thousands of audits or emails when you import an
|
||||
// established repository on an existing install.
|
||||
if ($repository->isImporting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
|
||||
'commitID = %d',
|
||||
$commit->getID());
|
||||
|
|
|
@ -58,6 +58,9 @@ final class PhabricatorRepositoryCommitOwnersWorker
|
|||
$commit->save();
|
||||
}
|
||||
|
||||
$commit->writeImportStatusFlag(
|
||||
PhabricatorRepositoryCommit::IMPORTED_OWNERS);
|
||||
|
||||
if ($this->shouldQueueFollowupTasks()) {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'PhabricatorRepositoryCommitHeraldWorker',
|
||||
|
|
|
@ -58,6 +58,9 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
|||
protected function finishParse() {
|
||||
$commit = $this->commit;
|
||||
|
||||
$commit->writeImportStatusFlag(
|
||||
PhabricatorRepositoryCommit::IMPORTED_CHANGE);
|
||||
|
||||
id(new PhabricatorSearchIndexer())
|
||||
->indexDocumentByPHID($commit->getPHID());
|
||||
|
||||
|
|
|
@ -220,6 +220,9 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
|
|||
}
|
||||
|
||||
$data->save();
|
||||
|
||||
$commit->writeImportStatusFlag(
|
||||
PhabricatorRepositoryCommit::IMPORTED_MESSAGE);
|
||||
}
|
||||
|
||||
private function loadUserName($user_phid, $default, PhabricatorUser $actor) {
|
||||
|
@ -249,7 +252,17 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
|
|||
->loadRawDiff();
|
||||
|
||||
// TODO: Support adds, deletes and moves under SVN.
|
||||
$changes = id(new ArcanistDiffParser())->parseDiff($raw_diff);
|
||||
if (strlen($raw_diff)) {
|
||||
$changes = id(new ArcanistDiffParser())->parseDiff($raw_diff);
|
||||
} else {
|
||||
// This is an empty diff, maybe made with `git commit --allow-empty`.
|
||||
// NOTE: These diffs have the same tree hash as their ancestors, so
|
||||
// they may attach to revisions in an unexpected way. Just let this
|
||||
// happen for now, although it might make sense to special case it
|
||||
// eventually.
|
||||
$changes = array();
|
||||
}
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges($changes)
|
||||
->setRevisionID($revision->getID())
|
||||
->setAuthorPHID($actor_phid)
|
||||
|
|
|
@ -16,7 +16,7 @@ final class PhabricatorSettingsPanelSSHKeys
|
|||
}
|
||||
|
||||
public function isEnabled() {
|
||||
return PhabricatorEnv::getEnvConfig('auth.sshkeys.enabled');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function processRequest(AphrontRequest $request) {
|
||||
|
|
|
@ -4,6 +4,16 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow {
|
|||
|
||||
private $user;
|
||||
private $iochannel;
|
||||
private $errorChannel;
|
||||
|
||||
public function setErrorChannel(PhutilChannel $error_channel) {
|
||||
$this->errorChannel = $error_channel;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrorChannel() {
|
||||
return $this->errorChannel;
|
||||
}
|
||||
|
||||
public function setUser(PhabricatorUser $user) {
|
||||
$this->user = $user;
|
||||
|
@ -27,6 +37,50 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow {
|
|||
return $this->iochannel;
|
||||
}
|
||||
|
||||
public function passthruIO(ExecFuture $future) {
|
||||
$exec_channel = new PhutilExecChannel($future);
|
||||
$exec_channel->setStderrHandler(array($this, 'writeErrorIOCallback'));
|
||||
|
||||
$io_channel = $this->getIOChannel();
|
||||
$error_channel = $this->getErrorChannel();
|
||||
|
||||
$channels = array($exec_channel, $io_channel, $error_channel);
|
||||
|
||||
while (true) {
|
||||
PhutilChannel::waitForAny($channels);
|
||||
|
||||
$io_channel->update();
|
||||
$exec_channel->update();
|
||||
$error_channel->update();
|
||||
|
||||
$done = !$exec_channel->isOpen();
|
||||
|
||||
$data = $io_channel->read();
|
||||
if (strlen($data)) {
|
||||
$exec_channel->write($data);
|
||||
}
|
||||
|
||||
$data = $exec_channel->read();
|
||||
if (strlen($data)) {
|
||||
$io_channel->write($data);
|
||||
}
|
||||
|
||||
// If we have nothing left on stdin, close stdin on the subprocess.
|
||||
if (!$io_channel->isOpenForReading()) {
|
||||
// TODO: This should probably be part of PhutilExecChannel?
|
||||
$future->write('');
|
||||
}
|
||||
|
||||
if ($done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list($err) = $future->resolve();
|
||||
|
||||
return $err;
|
||||
}
|
||||
|
||||
public function readAllInput() {
|
||||
$channel = $this->getIOChannel();
|
||||
while ($channel->update()) {
|
||||
|
@ -38,4 +92,18 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow {
|
|||
return $channel->read();
|
||||
}
|
||||
|
||||
public function writeIO($data) {
|
||||
$this->getIOChannel()->write($data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeErrorIO($data) {
|
||||
$this->getErrorChannel()->write($data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeErrorIOCallback(PhutilChannel $channel, $data) {
|
||||
$this->writeErrorIO($data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1704,6 +1704,14 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20131020.harbormaster.sql'),
|
||||
),
|
||||
'20131025.repopush.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20131025.repopush.sql'),
|
||||
),
|
||||
'20131026.commitstatus.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20131026.commitstatus.sql'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,18 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
|
|||
PhabricatorPolicyCapability::CAN_JOIN => pht('Joinable By'),
|
||||
);
|
||||
|
||||
$this->setLabel(idx($labels, $this->capability, pht('Unknown Policy')));
|
||||
if (isset($labels[$capability])) {
|
||||
$label = $labels[$capability];
|
||||
} else {
|
||||
$capobj = PhabricatorPolicyCapability::getCapabilityByKey($capability);
|
||||
if ($capobj) {
|
||||
$label = $capobj->getCapabilityName();
|
||||
} else {
|
||||
$label = pht('Capability "%s"', $capability);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setLabel($label);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue