@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
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);