Path: blob/master/scripts/repository/commit_hook.php
12241 views
#!/usr/bin/env php1<?php23// NOTE: This script will sometimes emit a warning like this on startup:4//5// No entry for terminal type "unknown";6// using dumb terminal settings.7//8// This can be fixed by adding "TERM=dumb" to the shebang line, but doing so9// causes some systems to hang mysteriously. See T7119.1011// Commit hooks execute in an unusual context where the environment may be12// unavailable, particularly in SVN. The first parameter to this script is13// either a bare repository identifier ("X"), or a repository identifier14// followed by an instance identifier ("X:instance"). If we have an instance15// identifier, unpack it into the environment before we start up. This allows16// subclasses of PhabricatorConfigSiteSource to read it and build an instance17// environment.1819$hook_start = microtime(true);2021if ($argc > 1) {22$context = $argv[1];23$context = explode(':', $context, 2);24$argv[1] = $context[0];2526if (count($context) > 1) {27$_ENV['PHABRICATOR_INSTANCE'] = $context[1];28putenv('PHABRICATOR_INSTANCE='.$context[1]);29}30}3132$root = dirname(dirname(dirname(__FILE__)));33require_once $root.'/scripts/__init_script__.php';3435if ($argc < 2) {36throw new Exception(pht('usage: commit-hook <repository>'));37}3839$engine = id(new DiffusionCommitHookEngine())40->setStartTime($hook_start);4142$repository = id(new PhabricatorRepositoryQuery())43->setViewer(PhabricatorUser::getOmnipotentUser())44->withIdentifiers(array($argv[1]))45->needProjectPHIDs(true)46->executeOne();4748if (!$repository) {49throw new Exception(pht('No such repository "%s"!', $argv[1]));50}5152if (!$repository->isHosted()) {53// In Mercurial, the "pretxnchangegroup" hook fires for both pulls and54// pushes. Normally we only install the hook for hosted repositories, but55// if a hosted repository is later converted into an observed repository we56// can end up with an observed repository that has the hook installed.57// If we're running hooks from an observed repository, just exit without58// taking action. For more discussion, see PHI24.59return 0;60}6162$engine->setRepository($repository);6364$args = new PhutilArgumentParser($argv);65$args->parsePartial(66array(67array(68'name' => 'hook-mode',69'param' => 'mode',70'help' => pht('Hook execution mode.'),71),72));7374$argv = array_merge(75array($argv[0]),76$args->getUnconsumedArgumentVector());7778// Figure out which user is writing the commit.79$hook_mode = $args->getArg('hook-mode');80if ($hook_mode !== null) {81$known_modes = array(82'svn-revprop' => true,83);8485if (empty($known_modes[$hook_mode])) {86throw new Exception(87pht(88'Invalid Hook Mode: This hook was invoked in "%s" mode, but this '.89'is not a recognized hook mode. Valid modes are: %s.',90$hook_mode,91implode(', ', array_keys($known_modes))));92}93}9495$is_svnrevprop = ($hook_mode == 'svn-revprop');9697if ($is_svnrevprop) {98// For now, we let these through if the repository allows dangerous changes99// and prevent them if it doesn't. See T11208 for discussion.100101$revprop_key = $argv[5];102103if ($repository->shouldAllowDangerousChanges()) {104$err = 0;105} else {106$err = 1;107108$console = PhutilConsole::getConsole();109$console->writeErr(110pht(111"DANGEROUS CHANGE: Dangerous change protection is enabled for this ".112"repository, so you can not change revision properties (you are ".113"attempting to edit \"%s\").\n".114"Edit the repository configuration before making dangerous changes.",115$revprop_key));116}117118exit($err);119} else if ($repository->isGit() || $repository->isHg()) {120$username = getenv(DiffusionCommitHookEngine::ENV_USER);121if ($username === null || !strlen($username)) {122throw new Exception(123pht(124'No Direct Pushes: You are pushing directly to a hosted repository. '.125'This will not work. See "No Direct Pushes" in the documentation '.126'for more information.'));127}128129if ($repository->isHg()) {130// We respond to several different hooks in Mercurial.131$engine->setMercurialHook($argv[2]);132}133134} else if ($repository->isSVN()) {135// NOTE: In Subversion, the entire environment gets wiped so we can't read136// DiffusionCommitHookEngine::ENV_USER. Instead, we've set "--tunnel-user" to137// specify the correct user; read this user out of the commit log.138139if ($argc < 4) {140throw new Exception(pht('usage: commit-hook <repository> <repo> <txn>'));141}142143$svn_repo = $argv[2];144$svn_txn = $argv[3];145list($username) = execx('svnlook author -t %s %s', $svn_txn, $svn_repo);146$username = rtrim($username, "\n");147148$engine->setSubversionTransactionInfo($svn_txn, $svn_repo);149} else {150throw new Exception(pht('Unknown repository type.'));151}152153$user = id(new PhabricatorPeopleQuery())154->setViewer(PhabricatorUser::getOmnipotentUser())155->withUsernames(array($username))156->executeOne();157158if (!$user) {159throw new Exception(pht('No such user "%s"!', $username));160}161162$engine->setViewer($user);163164165// Read stdin for the hook engine.166167if ($repository->isHg()) {168// Mercurial leaves stdin open, so we can't just read it until EOF.169$stdin = '';170} else {171// Git and Subversion write data into stdin and then close it. Read the172// data.173$stdin = @file_get_contents('php://stdin');174if ($stdin === false) {175throw new Exception(pht('Failed to read stdin!'));176}177}178179$engine->setStdin($stdin);180$engine->setOriginalArgv(array_slice($argv, 2));181182$remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS);183if ($remote_address !== false && strlen($remote_address)) {184$engine->setRemoteAddress($remote_address);185}186187$remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL);188if ($remote_protocol !== false && strlen($remote_protocol)) {189$engine->setRemoteProtocol($remote_protocol);190}191192$request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST);193if ($request_identifier !== false && strlen($request_identifier)) {194$engine->setRequestIdentifier($request_identifier);195}196197try {198$err = $engine->execute();199} catch (DiffusionCommitHookRejectException $ex) {200$console = PhutilConsole::getConsole();201202if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) {203$preamble = pht('*** PUSH REJECTED BY COMMIT HOOK ***');204} else {205$preamble = pht(<<<EOTXT206+---------------------------------------------------------------+207| * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |208+---------------------------------------------------------------+209\210\ ^ /^211\ / \ // \212\ |\___/| / \// .\213\ /V V \__ / // | \ \ *----*214/ / \/_/ // | \ \ \ |215@___@` \/_ // | \ \ \/\ \2160/0/| \/_ // | \ \ \ \2170/0/0/0/| \/// | \ \ | |2180/0/0/0/0/_|_ / ( // | \ _\ | /2190/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /220,-} _ *-.|.-~-. .~ ~221* \__/ `/\ / ~-. _ .-~ /222\____(Oo) *. } { /223( (..) .----~-.\ \-` .~224//___\\\\ \ DENIED! ///.----..< \ _ -~225// \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~226227EOTXT228);229}230231$console->writeErr("%s\n\n", $preamble);232$console->writeErr("%s\n\n", $ex->getMessage());233$err = 1;234}235236exit($err);237238239