1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00
phorge-phorge/scripts/repository/commit_hook.php
Valerio Bozzolan a5c93dea56 Fix InvalidArgumentException on commit hook
Summary:
Fix a regression introduced here:

96ae4ba13a

I reproduced this exception executing "svn commit" on a hosted repository.

That crash happened because the PHP getenv() function can return false.
But, that is a very terrible value that blasts the non-string-empty check.

So, now the default getenv() value is skipped, without causing problems.

Closes T15253
Ref T15190

Test Plan: - I've run `svn commit` and I have not encountered any issue now

Reviewers: O1 Blessed Committers, avivey

Reviewed By: O1 Blessed Committers, avivey

Subscribers: speck, tobiaswiese, Matthew, Cigaryno

Maniphest Tasks: T15253, T15190

Differential Revision: https://we.phorge.it/D25122
2023-04-14 22:09:23 +02:00

245 lines
7.6 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.
$hook_start = microtime(true);
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 = id(new DiffusionCommitHookEngine())
->setStartTime($hook_start);
$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()) {
// In Mercurial, the "pretxnchangegroup" hook fires for both pulls and
// pushes. Normally we only install the hook for hosted repositories, but
// if a hosted repository is later converted into an observed repository we
// can end up with an observed repository that has the hook installed.
// If we're running hooks from an observed repository, just exit without
// taking action. For more discussion, see PHI24.
return 0;
}
$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 ($username !== false) {
if (!phutil_nonempty_string($username)) {
throw new Exception(
pht(
'No Direct Pushes: You are pushing directly to a hosted repository. '.
'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 ($remote_address !== false) {
if (phutil_nonempty_string($remote_address)) {
$engine->setRemoteAddress($remote_address);
}
}
$remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL);
if ($remote_protocol !== false) {
if (phutil_nonempty_string($remote_protocol)) {
$engine->setRemoteProtocol($remote_protocol);
}
}
$request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST);
if ($request_identifier !== false) {
if (phutil_nonempty_string($request_identifier)) {
$engine->setRequestIdentifier($request_identifier);
}
}
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);