mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 00:02:41 +01:00
32d4ae8cb2
Summary: If Mercurial 3.4+ is used to host repositories in Phabricator, any clients using 3.5+ will receive an exception after the bundle is pushed up. Clients will also fail to update phases for changesets pushed up. Before directly responding to mercurial clients with all capabilities, this change filters out the 'bundle2' capability so the client negotiates using a legacy bundle wire format instead. Test Plan: Server: Mercurial 3.5 Client: Mercurial 3.4 Test with both HTTP and SSH protocols: 1. Create a local commit on client 2. Push commit to server 3. Verify the client emits something like: ``` searching for changes remote: adding changesets remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files ``` Closes T9450 Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T9450 Differential Revision: https://secure.phabricator.com/D14241
120 lines
3.5 KiB
PHP
120 lines
3.5 KiB
PHP
<?php
|
|
|
|
final class DiffusionMercurialServeSSHWorkflow
|
|
extends DiffusionMercurialSSHWorkflow {
|
|
|
|
protected $didSeeWrite;
|
|
|
|
protected function didConstruct() {
|
|
$this->setName('hg');
|
|
$this->setArguments(
|
|
array(
|
|
array(
|
|
'name' => 'repository',
|
|
'short' => 'R',
|
|
'param' => 'repo',
|
|
),
|
|
array(
|
|
'name' => 'stdio',
|
|
),
|
|
array(
|
|
'name' => 'command',
|
|
'wildcard' => true,
|
|
),
|
|
));
|
|
}
|
|
|
|
protected function identifyRepository() {
|
|
$args = $this->getArgs();
|
|
$path = $args->getArg('repository');
|
|
return $this->loadRepositoryWithPath($path);
|
|
}
|
|
|
|
protected function executeRepositoryOperations() {
|
|
$repository = $this->getRepository();
|
|
$args = $this->getArgs();
|
|
|
|
if (!$args->getArg('stdio')) {
|
|
throw new Exception(pht('Expected `%s`!', 'hg ... --stdio'));
|
|
}
|
|
|
|
if ($args->getArg('command') !== array('serve')) {
|
|
throw new Exception(pht('Expected `%s`!', 'hg ... serve'));
|
|
}
|
|
|
|
if ($this->shouldProxy()) {
|
|
$command = $this->getProxyCommand();
|
|
} else {
|
|
$command = csprintf(
|
|
'hg -R %s serve --stdio',
|
|
$repository->getLocalPath());
|
|
}
|
|
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
|
|
|
|
$future = id(new ExecFuture('%C', $command))
|
|
->setEnv($this->getEnvironment());
|
|
|
|
$io_channel = $this->getIOChannel();
|
|
$protocol_channel = new DiffusionMercurialWireClientSSHProtocolChannel(
|
|
$io_channel);
|
|
|
|
$err = id($this->newPassthruCommand())
|
|
->setIOChannel($protocol_channel)
|
|
->setCommandChannelFromExecFuture($future)
|
|
->setWillWriteCallback(array($this, 'willWriteMessageCallback'))
|
|
->execute();
|
|
|
|
// TODO: It's apparently technically possible to communicate errors to
|
|
// Mercurial over SSH by writing a special "\n<error>\n-\n" string. However,
|
|
// my attempt to implement that resulted in Mercurial closing the socket and
|
|
// then hanging, without showing the error. This might be an issue on our
|
|
// side (we need to close our half of the socket?), or maybe the code
|
|
// for this in Mercurial doesn't actually work, or maybe something else
|
|
// is afoot. At some point, we should look into doing this more cleanly.
|
|
// For now, when we, e.g., reject writes for policy reasons, the user will
|
|
// see "abort: unexpected response: empty string" after the diagnostically
|
|
// useful, e.g., "remote: This repository is read-only over SSH." message.
|
|
|
|
if (!$err && $this->didSeeWrite) {
|
|
$repository->writeStatusMessage(
|
|
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
|
|
PhabricatorRepositoryStatusMessage::CODE_OKAY);
|
|
}
|
|
|
|
return $err;
|
|
}
|
|
|
|
public function willWriteMessageCallback(
|
|
PhabricatorSSHPassthruCommand $command,
|
|
$message) {
|
|
|
|
$command = $message['command'];
|
|
|
|
// Check if this is a readonly command.
|
|
|
|
$is_readonly = false;
|
|
if ($command == 'batch') {
|
|
$cmds = idx($message['arguments'], 'cmds');
|
|
if (DiffusionMercurialWireProtocol::isReadOnlyBatchCommand($cmds)) {
|
|
$is_readonly = true;
|
|
}
|
|
} else if (DiffusionMercurialWireProtocol::isReadOnlyCommand($command)) {
|
|
$is_readonly = true;
|
|
}
|
|
|
|
if (!$is_readonly) {
|
|
$this->requireWriteAccess();
|
|
$this->didSeeWrite = true;
|
|
}
|
|
|
|
$raw_message = $message['raw'];
|
|
if ($command == 'capabilities') {
|
|
$raw_message = DiffusionMercurialWireProtocol::filterBundle2Capability(
|
|
$raw_message);
|
|
}
|
|
|
|
// If we're good, return the raw message data.
|
|
return $raw_message;
|
|
}
|
|
|
|
}
|