Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/scripts/repository/commit_hook.php
12241 views
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
22
if ($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__)));
34
require_once $root.'/scripts/__init_script__.php';
35
36
if ($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
49
if (!$repository) {
50
throw new Exception(pht('No such repository "%s"!', $argv[1]));
51
}
52
53
if (!$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');
81
if ($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
98
if ($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 === null || !strlen($username)) {
123
throw new Exception(
124
pht(
125
'No Direct Pushes: You are pushing directly to a hosted repository. '.
126
'This will not work. See "No Direct Pushes" in the documentation '.
127
'for more information.'));
128
}
129
130
if ($repository->isHg()) {
131
// We respond to several different hooks in Mercurial.
132
$engine->setMercurialHook($argv[2]);
133
}
134
135
} else if ($repository->isSVN()) {
136
// NOTE: In Subversion, the entire environment gets wiped so we can't read
137
// DiffusionCommitHookEngine::ENV_USER. Instead, we've set "--tunnel-user" to
138
// specify the correct user; read this user out of the commit log.
139
140
if ($argc < 4) {
141
throw new Exception(pht('usage: commit-hook <repository> <repo> <txn>'));
142
}
143
144
$svn_repo = $argv[2];
145
$svn_txn = $argv[3];
146
list($username) = execx('svnlook author -t %s %s', $svn_txn, $svn_repo);
147
$username = rtrim($username, "\n");
148
149
$engine->setSubversionTransactionInfo($svn_txn, $svn_repo);
150
} else {
151
throw new Exception(pht('Unknown repository type.'));
152
}
153
154
$user = id(new PhabricatorPeopleQuery())
155
->setViewer(PhabricatorUser::getOmnipotentUser())
156
->withUsernames(array($username))
157
->executeOne();
158
159
if (!$user) {
160
throw new Exception(pht('No such user "%s"!', $username));
161
}
162
163
$engine->setViewer($user);
164
165
166
// Read stdin for the hook engine.
167
168
if ($repository->isHg()) {
169
// Mercurial leaves stdin open, so we can't just read it until EOF.
170
$stdin = '';
171
} else {
172
// Git and Subversion write data into stdin and then close it. Read the
173
// data.
174
$stdin = @file_get_contents('php://stdin');
175
if ($stdin === false) {
176
throw new Exception(pht('Failed to read stdin!'));
177
}
178
}
179
180
$engine->setStdin($stdin);
181
$engine->setOriginalArgv(array_slice($argv, 2));
182
183
$remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS);
184
if ($remote_address !== false && strlen($remote_address)) {
185
$engine->setRemoteAddress($remote_address);
186
}
187
188
$remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL);
189
if ($remote_protocol !== false && strlen($remote_protocol)) {
190
$engine->setRemoteProtocol($remote_protocol);
191
}
192
193
$request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST);
194
if ($request_identifier !== false && strlen($request_identifier)) {
195
$engine->setRequestIdentifier($request_identifier);
196
}
197
198
try {
199
$err = $engine->execute();
200
} catch (DiffusionCommitHookRejectException $ex) {
201
$console = PhutilConsole::getConsole();
202
203
if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) {
204
$preamble = pht('*** PUSH REJECTED BY COMMIT HOOK ***');
205
} else {
206
$preamble = pht(<<<EOTXT
207
+---------------------------------------------------------------+
208
| * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
209
+---------------------------------------------------------------+
210
\
211
\ ^ /^
212
\ / \ // \
213
\ |\___/| / \// .\
214
\ /V V \__ / // | \ \ *----*
215
/ / \/_/ // | \ \ \ |
216
@___@` \/_ // | \ \ \/\ \
217
0/0/| \/_ // | \ \ \ \
218
0/0/0/0/| \/// | \ \ | |
219
0/0/0/0/0/_|_ / ( // | \ _\ | /
220
0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
221
,-} _ *-.|.-~-. .~ ~
222
* \__/ `/\ / ~-. _ .-~ /
223
\____(Oo) *. } { /
224
( (..) .----~-.\ \-` .~
225
//___\\\\ \ DENIED! ///.----..< \ _ -~
226
// \\\\ ///-._ _ _ _ _ _ _{^ - - - - ~
227
228
EOTXT
229
);
230
}
231
232
$console->writeErr("%s\n\n", $preamble);
233
$console->writeErr("%s\n\n", $ex->getMessage());
234
$err = 1;
235
}
236
237
exit($err);
238
239