1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 17:28:51 +02:00
phorge-phorge/scripts/repository/commit_hook.php
epriestley 89f9f97159 Provide basic support for Subversion revprops
Summary:
Ref T11208. See that task for a more detailed description of revprops.

This allows revprop changes in a hosted Subversion repository if the repository has the "allow dangerous changes" flag set.

In the future, we could expand this into real Herald support, but the only use case we have for now is letting `svnsync` work.

Test Plan:
Edited revprops with `svn propset --revprop -r 2 propkey propvalue repositoryuri`:

  - Tried before patch, got a "configure a commit hook" error.
  - Tried after patch, got a "dangerous change" error.
  - Allowed dangerous changes.
  - Did a revprop edit.
  - Prevented dangerous changes.
  - Got an error again.
  - Made a normal commit to an SVN repository.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11208

Differential Revision: https://secure.phabricator.com/D16174
2016-06-24 13:43:32 -07:00

224 lines
6.8 KiB
PHP
Executable file

#!/usr/bin/env php
<?php
// NOTE: This script will sometimes emit a warning like this on startup:
//
// No entry for terminal type "unknown";
// using dumb terminal settings.
//
// This can be fixed by adding "TERM=dumb" to the shebang line, but doing so
// causes some systems to hang mysteriously. See T7119.
// Commit hooks execute in an unusual context where the environment may be
// unavailable, particularly in SVN. The first parameter to this script is
// either a bare repository identifier ("X"), or a repository identifier
// followed by an instance identifier ("X:instance"). If we have an instance
// identifier, unpack it into the environment before we start up. This allows
// subclasses of PhabricatorConfigSiteSource to read it and build an instance
// environment.
if ($argc > 1) {
$context = $argv[1];
$context = explode(':', $context, 2);
$argv[1] = $context[0];
if (count($context) > 1) {
$_ENV['PHABRICATOR_INSTANCE'] = $context[1];
putenv('PHABRICATOR_INSTANCE='.$context[1]);
}
}
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
if ($argc < 2) {
throw new Exception(pht('usage: commit-hook <repository>'));
}
$engine = new DiffusionCommitHookEngine();
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIdentifiers(array($argv[1]))
->needProjectPHIDs(true)
->executeOne();
if (!$repository) {
throw new Exception(pht('No such repository "%s"!', $argv[1]));
}
if (!$repository->isHosted()) {
// This should be redundant, but double check just in case.
throw new Exception(pht('Repository "%s" is not hosted!', $argv[1]));
}
$engine->setRepository($repository);
$args = new PhutilArgumentParser($argv);
$args->parsePartial(
array(
array(
'name' => 'hook-mode',
'param' => 'mode',
'help' => pht('Hook execution mode.'),
),
));
$argv = array_merge(
array($argv[0]),
$args->getUnconsumedArgumentVector());
// Figure out which user is writing the commit.
$hook_mode = $args->getArg('hook-mode');
if ($hook_mode !== null) {
$known_modes = array(
'svn-revprop' => true,
);
if (empty($known_modes[$hook_mode])) {
throw new Exception(
pht(
'Invalid Hook Mode: This hook was invoked in "%s" mode, but this '.
'is not a recognized hook mode. Valid modes are: %s.',
$hook_mode,
implode(', ', array_keys($known_modes))));
}
}
$is_svnrevprop = ($hook_mode == 'svn-revprop');
if ($is_svnrevprop) {
// For now, we let these through if the repository allows dangerous changes
// and prevent them if it doesn't. See T11208 for discussion.
$revprop_key = $argv[5];
if ($repository->shouldAllowDangerousChanges()) {
$err = 0;
} else {
$err = 1;
$console = PhutilConsole::getConsole();
$console->writeErr(
pht(
"DANGEROUS CHANGE: Dangerous change protection is enabled for this ".
"repository, so you can not change revision properties (you are ".
"attempting to edit \"%s\").\n".
"Edit the repository configuration before making dangerous changes.",
$revprop_key));
}
exit($err);
} else if ($repository->isGit() || $repository->isHg()) {
$username = getenv(DiffusionCommitHookEngine::ENV_USER);
if (!strlen($username)) {
throw new Exception(
pht(
'No Direct Pushes: You are pushing directly to a repository hosted '.
'by Phabricator. This will not work. See "No Direct Pushes" in the '.
'documentation for more information.'));
}
if ($repository->isHg()) {
// We respond to several different hooks in Mercurial.
$engine->setMercurialHook($argv[2]);
}
} else if ($repository->isSVN()) {
// NOTE: In Subversion, the entire environment gets wiped so we can't read
// DiffusionCommitHookEngine::ENV_USER. Instead, we've set "--tunnel-user" to
// specify the correct user; read this user out of the commit log.
if ($argc < 4) {
throw new Exception(pht('usage: commit-hook <repository> <repo> <txn>'));
}
$svn_repo = $argv[2];
$svn_txn = $argv[3];
list($username) = execx('svnlook author -t %s %s', $svn_txn, $svn_repo);
$username = rtrim($username, "\n");
$engine->setSubversionTransactionInfo($svn_txn, $svn_repo);
} else {
throw new Exception(pht('Unknown repository type.'));
}
$user = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUsernames(array($username))
->executeOne();
if (!$user) {
throw new Exception(pht('No such user "%s"!', $username));
}
$engine->setViewer($user);
// Read stdin for the hook engine.
if ($repository->isHg()) {
// Mercurial leaves stdin open, so we can't just read it until EOF.
$stdin = '';
} else {
// Git and Subversion write data into stdin and then close it. Read the
// data.
$stdin = @file_get_contents('php://stdin');
if ($stdin === false) {
throw new Exception(pht('Failed to read stdin!'));
}
}
$engine->setStdin($stdin);
$engine->setOriginalArgv(array_slice($argv, 2));
$remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS);
if (strlen($remote_address)) {
$engine->setRemoteAddress($remote_address);
}
$remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL);
if (strlen($remote_protocol)) {
$engine->setRemoteProtocol($remote_protocol);
}
try {
$err = $engine->execute();
} catch (DiffusionCommitHookRejectException $ex) {
$console = PhutilConsole::getConsole();
if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) {
$preamble = pht('*** PUSH REJECTED BY COMMIT HOOK ***');
} else {
$preamble = pht(<<<EOTXT
+---------------------------------------------------------------+
| * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
+---------------------------------------------------------------+
\
\ ^ /^
\ / \ // \
\ |\___/| / \// .\
\ /V V \__ / // | \ \ *----*
/ / \/_/ // | \ \ \ |
@___@` \/_ // | \ \ \/\ \
0/0/| \/_ // | \ \ \ \
0/0/0/0/| \/// | \ \ | |
0/0/0/0/0/_|_ / ( // | \ _\ | /
0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
,-} _ *-.|.-~-. .~ ~
\ \__/ `/\ / ~-. _ .-~ /
\____(Oo) *. } { /
( (--) .----~-.\ \-` .~
//__\\\\ \ DENIED! ///.----..< \ _ -~
// \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~
EOTXT
);
}
$console->writeErr("%s\n\n", $preamble);
$console->writeErr("%s\n\n", $ex->getMessage());
$err = 1;
}
exit($err);