@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 245 lines 7.8 kB view raw
1#!/usr/bin/env php 2<?php 3 4// NOTE: This script will sometimes emit a warning like this on startup: 5// 6// No entry for terminal type "unknown"; 7// using dumb terminal settings. 8// 9// This can be fixed by adding "TERM=dumb" to the shebang line, but doing so 10// causes some systems to hang mysteriously. See T7119. 11 12// Commit hooks execute in an unusual context where the environment may be 13// unavailable, particularly in SVN. The first parameter to this script is 14// either a bare repository identifier ("X"), or a repository identifier 15// followed by an instance identifier ("X:instance"). If we have an instance 16// identifier, unpack it into the environment before we start up. This allows 17// subclasses of PhabricatorConfigSiteSource to read it and build an instance 18// environment. 19 20$hook_start = microtime(true); 21 22if ($argc > 1) { 23 $context = $argv[1]; 24 $context = explode(':', $context, 2); 25 $argv[1] = $context[0]; 26 27 if (count($context) > 1) { 28 $_ENV['PHABRICATOR_INSTANCE'] = $context[1]; 29 putenv('PHABRICATOR_INSTANCE='.$context[1]); 30 } 31} 32 33$root = dirname(dirname(dirname(__FILE__))); 34require_once $root.'/scripts/__init_script__.php'; 35 36if ($argc < 2) { 37 throw new Exception(pht('usage: commit-hook <repository>')); 38} 39 40$engine = id(new DiffusionCommitHookEngine()) 41 ->setStartTime($hook_start); 42 43$repository = id(new PhabricatorRepositoryQuery()) 44 ->setViewer(PhabricatorUser::getOmnipotentUser()) 45 ->withIdentifiers(array($argv[1])) 46 ->needProjectPHIDs(true) 47 ->executeOne(); 48 49if (!$repository) { 50 throw new Exception(pht('No such repository "%s"!', $argv[1])); 51} 52 53if (!$repository->isHosted()) { 54 // In Mercurial, the "pretxnchangegroup" hook fires for both pulls and 55 // pushes. Normally we only install the hook for hosted repositories, but 56 // if a hosted repository is later converted into an observed repository we 57 // can end up with an observed repository that has the hook installed. 58 // If we're running hooks from an observed repository, just exit without 59 // taking action. For more discussion, see PHI24. 60 return 0; 61} 62 63$engine->setRepository($repository); 64 65$args = new PhutilArgumentParser($argv); 66$args->parsePartial( 67 array( 68 array( 69 'name' => 'hook-mode', 70 'param' => 'mode', 71 'help' => pht('Hook execution mode.'), 72 ), 73 )); 74 75$argv = array_merge( 76 array($argv[0]), 77 $args->getUnconsumedArgumentVector()); 78 79// Figure out which user is writing the commit. 80$hook_mode = $args->getArg('hook-mode'); 81if ($hook_mode !== null) { 82 $known_modes = array( 83 'svn-revprop' => true, 84 ); 85 86 if (empty($known_modes[$hook_mode])) { 87 throw new Exception( 88 pht( 89 'Invalid Hook Mode: This hook was invoked in "%s" mode, but this '. 90 'is not a recognized hook mode. Valid modes are: %s.', 91 $hook_mode, 92 implode(', ', array_keys($known_modes)))); 93 } 94} 95 96$is_svnrevprop = ($hook_mode == 'svn-revprop'); 97 98if ($is_svnrevprop) { 99 // For now, we let these through if the repository allows dangerous changes 100 // and prevent them if it doesn't. See T11208 for discussion. 101 102 $revprop_key = $argv[5]; 103 104 if ($repository->shouldAllowDangerousChanges()) { 105 $err = 0; 106 } else { 107 $err = 1; 108 109 $console = PhutilConsole::getConsole(); 110 $console->writeErr( 111 pht( 112 "DANGEROUS CHANGE: Dangerous change protection is enabled for this ". 113 "repository, so you can not change revision properties (you are ". 114 "attempting to edit \"%s\").\n". 115 "Edit the repository configuration before making dangerous changes.", 116 $revprop_key)); 117 } 118 119 exit($err); 120} else if ($repository->isGit() || $repository->isHg()) { 121 $username = getenv(DiffusionCommitHookEngine::ENV_USER); 122 if ($username !== false) { 123 if (!phutil_nonempty_string($username)) { 124 throw new Exception( 125 pht( 126 'No Direct Pushes: You are pushing directly to a hosted repository. '. 127 'This will not work. See "No Direct Pushes" in the documentation '. 128 'for more information.')); 129 } 130 } 131 132 if ($repository->isHg()) { 133 // We respond to several different hooks in Mercurial. 134 $engine->setMercurialHook($argv[2]); 135 } 136 137} else if ($repository->isSVN()) { 138 // NOTE: In Subversion, the entire environment gets wiped so we can't read 139 // DiffusionCommitHookEngine::ENV_USER. Instead, we've set "--tunnel-user" to 140 // specify the correct user; read this user out of the commit log. 141 142 if ($argc < 4) { 143 throw new Exception(pht('usage: commit-hook <repository> <repo> <txn>')); 144 } 145 146 $svn_repo = $argv[2]; 147 $svn_txn = $argv[3]; 148 list($username) = execx('svnlook author -t %s %s', $svn_txn, $svn_repo); 149 $username = rtrim($username, "\n"); 150 151 $engine->setSubversionTransactionInfo($svn_txn, $svn_repo); 152} else { 153 throw new Exception(pht('Unknown repository type.')); 154} 155 156$user = id(new PhabricatorPeopleQuery()) 157 ->setViewer(PhabricatorUser::getOmnipotentUser()) 158 ->withUsernames(array($username)) 159 ->executeOne(); 160 161if (!$user) { 162 throw new Exception(pht('No such user "%s"!', $username)); 163} 164 165$engine->setViewer($user); 166 167 168// Read stdin for the hook engine. 169 170if ($repository->isHg()) { 171 // Mercurial leaves stdin open, so we can't just read it until EOF. 172 $stdin = ''; 173} else { 174 // Git and Subversion write data into stdin and then close it. Read the 175 // data. 176 $stdin = @file_get_contents('php://stdin'); 177 if ($stdin === false) { 178 throw new Exception(pht('Failed to read stdin!')); 179 } 180} 181 182$engine->setStdin($stdin); 183$engine->setOriginalArgv(array_slice($argv, 2)); 184 185$remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS); 186if ($remote_address !== false) { 187 if (phutil_nonempty_string($remote_address)) { 188 $engine->setRemoteAddress($remote_address); 189 } 190} 191 192$remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL); 193if ($remote_protocol !== false) { 194 if (phutil_nonempty_string($remote_protocol)) { 195 $engine->setRemoteProtocol($remote_protocol); 196 } 197} 198 199$request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST); 200if ($request_identifier !== false) { 201 if (phutil_nonempty_string($request_identifier)) { 202 $engine->setRequestIdentifier($request_identifier); 203 } 204} 205 206try { 207 $err = $engine->execute(); 208} catch (DiffusionCommitHookRejectException $ex) { 209 $console = PhutilConsole::getConsole(); 210 211 if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) { 212 $preamble = pht('*** PUSH REJECTED BY COMMIT HOOK ***'); 213 } else { 214 $preamble = pht(<<<EOTXT 215+---------------------------------------------------------------+ 216| * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * | 217+---------------------------------------------------------------+ 218 \ 219 \ ^ /^ 220 \ / \ // \ 221 \ |\___/| / \// .\ 222 \ /V V \__ / // | \ \ *----* 223 / / \/_/ // | \ \ \ | 224 @___@` \/_ // | \ \ \/\ \ 225 0/0/| \/_ // | \ \ \ \ 226 0/0/0/0/| \/// | \ \ | | 227 0/0/0/0/0/_|_ / ( // | \ _\ | / 228 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / / 229 ,-} _ *-.|.-~-. .~ ~ 230 * \__/ `/\ / ~-. _ .-~ / 231 \____(Oo) *. } { / 232 ( (..) .----~-.\ \-` .~ 233 //___\\\\ \ DENIED! ///.----..< \ _ -~ 234 // \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~ 235 236EOTXT 237); 238 } 239 240 $console->writeErr("%s\n\n", $preamble); 241 $console->writeErr("%s\n\n", $ex->getMessage()); 242 $err = 1; 243} 244 245exit($err);