Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / repository / management / PhabricatorRepositoryManagementRebuildIdentitiesWorkflow.php
blobad98fe30d07400264c81f36830524c71f9cf3741
1 <?php
3 final class PhabricatorRepositoryManagementRebuildIdentitiesWorkflow
4 extends PhabricatorRepositoryManagementWorkflow {
6 private $identityCache = array();
7 private $phidCache = array();
8 private $dryRun;
10 protected function didConstruct() {
11 $this
12 ->setName('rebuild-identities')
13 ->setExamples(
14 '**rebuild-identities** [__options__] __repository__')
15 ->setSynopsis(pht('Rebuild repository identities from commits.'))
16 ->setArguments(
17 array(
18 array(
19 'name' => 'all-repositories',
20 'help' => pht('Rebuild identities across all repositories.'),
22 array(
23 'name' => 'all-identities',
24 'help' => pht('Rebuild all currently-known identities.'),
26 array(
27 'name' => 'repository',
28 'param' => 'repository',
29 'repeat' => true,
30 'help' => pht('Rebuild identities in a repository.'),
32 array(
33 'name' => 'commit',
34 'param' => 'commit',
35 'repeat' => true,
36 'help' => pht('Rebuild identities for a commit.'),
38 array(
39 'name' => 'user',
40 'param' => 'user',
41 'repeat' => true,
42 'help' => pht('Rebuild identities for a user.'),
44 array(
45 'name' => 'email',
46 'param' => 'email',
47 'repeat' => true,
48 'help' => pht('Rebuild identities for an email address.'),
50 array(
51 'name' => 'raw',
52 'param' => 'raw',
53 'repeat' => true,
54 'help' => pht('Rebuild identities for a raw commit string.'),
56 array(
57 'name' => 'dry-run',
58 'help' => pht('Show changes, but do not make any changes.'),
60 ));
63 public function execute(PhutilArgumentParser $args) {
64 $viewer = $this->getViewer();
66 $rebuilt_anything = false;
68 $all_repositories = $args->getArg('all-repositories');
69 $repositories = $args->getArg('repository');
71 if ($all_repositories && $repositories) {
72 throw new PhutilArgumentUsageException(
73 pht(
74 'Flags "--all-repositories" and "--repository" are not '.
75 'compatible.'));
79 $all_identities = $args->getArg('all-identities');
80 $raw = $args->getArg('raw');
82 if ($all_identities && $raw) {
83 throw new PhutilArgumentUsageException(
84 pht(
85 'Flags "--all-identities" and "--raw" are not '.
86 'compatible.'));
89 $dry_run = $args->getArg('dry-run');
90 $this->dryRun = $dry_run;
92 if ($this->dryRun) {
93 $this->logWarn(
94 pht('DRY RUN'),
95 pht('This is a dry run, so no changes will be written.'));
98 if ($all_repositories || $repositories) {
99 $rebuilt_anything = true;
101 if ($repositories) {
102 $repository_list = $this->loadRepositories($args, 'repository');
103 } else {
104 $repository_query = id(new PhabricatorRepositoryQuery())
105 ->setViewer($viewer);
106 $repository_list = new PhabricatorQueryIterator($repository_query);
109 foreach ($repository_list as $repository) {
110 $commit_query = id(new DiffusionCommitQuery())
111 ->setViewer($viewer)
112 ->needCommitData(true)
113 ->withRepositoryIDs(array($repository->getID()));
115 // See T13457. Adjust ordering to hit keys better and tweak page size
116 // to improve performance slightly, since these records are small.
117 $commit_query->setOrderVector(array('-epoch', '-id'));
119 $commit_iterator = id(new PhabricatorQueryIterator($commit_query))
120 ->setPageSize(1000);
122 $this->rebuildCommits($commit_iterator);
126 $commits = $args->getArg('commit');
127 if ($commits) {
128 $rebuilt_anything = true;
129 $commit_list = $this->loadCommits($args, 'commit');
131 // Reload commits to get commit data.
132 $commit_list = id(new DiffusionCommitQuery())
133 ->setViewer($viewer)
134 ->needCommitData(true)
135 ->withIDs(mpull($commit_list, 'getID'))
136 ->execute();
138 $this->rebuildCommits($commit_list);
141 $users = $args->getArg('user');
142 if ($users) {
143 $rebuilt_anything = true;
145 $user_list = $this->loadUsersFromArguments($users);
146 $this->rebuildUsers($user_list);
149 $emails = $args->getArg('email');
150 if ($emails) {
151 $rebuilt_anything = true;
152 $this->rebuildEmails($emails);
155 if ($all_identities || $raw) {
156 $rebuilt_anything = true;
158 if ($raw) {
159 $identities = id(new PhabricatorRepositoryIdentityQuery())
160 ->setViewer($viewer)
161 ->withIdentityNames($raw)
162 ->execute();
164 $identities = mpull($identities, null, 'getIdentityNameRaw');
165 foreach ($raw as $raw_identity) {
166 if (!isset($identities[$raw_identity])) {
167 throw new PhutilArgumentUsageException(
168 pht(
169 'No identity "%s" exists. When selecting identities with '.
170 '"--raw", the entire identity must match exactly.',
171 $raw_identity));
175 $identity_list = $identities;
176 } else {
177 $identity_query = id(new PhabricatorRepositoryIdentityQuery())
178 ->setViewer($viewer);
180 $identity_list = new PhabricatorQueryIterator($identity_query);
182 $this->logInfo(
183 pht('REBUILD'),
184 pht('Rebuilding all existing identities.'));
187 $this->rebuildIdentities($identity_list);
190 if (!$rebuilt_anything) {
191 throw new PhutilArgumentUsageException(
192 pht(
193 'Nothing specified to rebuild. Use flags to choose which '.
194 'identities to rebuild, or "--help" for help.'));
197 return 0;
200 private function rebuildCommits($commits) {
201 foreach ($commits as $commit) {
202 $needs_update = false;
204 $data = $commit->getCommitData();
205 $author = $data->getAuthorString();
207 $author_identity = $this->getIdentityForCommit(
208 $commit,
209 $author);
211 $author_phid = $commit->getAuthorIdentityPHID();
212 $identity_phid = $author_identity->getPHID();
214 $aidentity_phid = $identity_phid;
215 if ($author_phid !== $identity_phid) {
216 $commit->setAuthorIdentityPHID($identity_phid);
217 $data->setCommitDetail('authorIdentityPHID', $identity_phid);
218 $needs_update = true;
221 $committer_name = $data->getCommitterString();
222 $committer_phid = $commit->getCommitterIdentityPHID();
223 if (phutil_nonempty_string($committer_name)) {
224 $committer_identity = $this->getIdentityForCommit(
225 $commit,
226 $committer_name);
227 $identity_phid = $committer_identity->getPHID();
228 } else {
229 $identity_phid = null;
232 if ($committer_phid !== $identity_phid) {
233 $commit->setCommitterIdentityPHID($identity_phid);
234 $data->setCommitDetail('committerIdentityPHID', $identity_phid);
235 $needs_update = true;
238 if ($needs_update) {
239 $commit->save();
240 $data->save();
242 $this->logInfo(
243 pht('COMMIT'),
244 pht(
245 'Rebuilt identities for "%s".',
246 $commit->getDisplayName()));
247 } else {
248 $this->logInfo(
249 pht('SKIP'),
250 pht(
251 'No changes for commit "%s".',
252 $commit->getDisplayName()));
257 private function getIdentityForCommit(
258 PhabricatorRepositoryCommit $commit,
259 $raw_identity) {
261 if (!isset($this->identityCache[$raw_identity])) {
262 $identity = $this->newIdentityEngine()
263 ->setSourcePHID($commit->getPHID())
264 ->newResolvedIdentity($raw_identity);
266 $this->identityCache[$raw_identity] = $identity;
269 return $this->identityCache[$raw_identity];
273 private function rebuildUsers($users) {
274 $viewer = $this->getViewer();
276 foreach ($users as $user) {
277 $this->logInfo(
278 pht('USER'),
279 pht(
280 'Rebuilding identities for user "%s".',
281 $user->getMonogram()));
283 $emails = id(new PhabricatorUserEmail())->loadAllWhere(
284 'userPHID = %s',
285 $user->getPHID());
286 if ($emails) {
287 $this->rebuildEmails(mpull($emails, 'getAddress'));
290 $identities = id(new PhabricatorRepositoryIdentityQuery())
291 ->setViewer($viewer)
292 ->withRelatedPHIDs(array($user->getPHID()))
293 ->execute();
295 if (!$identities) {
296 $this->logWarn(
297 pht('NO IDENTITIES'),
298 pht('Found no identities directly related to user.'));
299 continue;
302 $this->rebuildIdentities($identities);
306 private function rebuildEmails($emails) {
307 $viewer = $this->getViewer();
309 foreach ($emails as $email) {
310 $this->logInfo(
311 pht('EMAIL'),
312 pht('Rebuilding identities for email address "%s".', $email));
314 $identities = id(new PhabricatorRepositoryIdentityQuery())
315 ->setViewer($viewer)
316 ->withEmailAddresses(array($email))
317 ->execute();
319 if (!$identities) {
320 $this->logWarn(
321 pht('NO IDENTITIES'),
322 pht('Found no identities for email address "%s".', $email));
323 continue;
326 $this->rebuildIdentities($identities);
330 private function rebuildIdentities($identities) {
331 $dry_run = $this->dryRun;
333 foreach ($identities as $identity) {
334 $raw_identity = $identity->getIdentityName();
336 if (isset($this->identityCache[$raw_identity])) {
337 $this->logInfo(
338 pht('SKIP'),
339 pht(
340 'Identity "%s" has already been rebuilt.',
341 $raw_identity));
342 continue;
345 $this->logInfo(
346 pht('IDENTITY'),
347 pht(
348 'Rebuilding identity "%s".',
349 $raw_identity));
351 $old_auto = $identity->getAutomaticGuessedUserPHID();
352 $old_assign = $identity->getManuallySetUserPHID();
354 $identity = $this->newIdentityEngine()
355 ->newUpdatedIdentity($identity);
357 $this->identityCache[$raw_identity] = $identity;
359 $new_auto = $identity->getAutomaticGuessedUserPHID();
360 $new_assign = $identity->getManuallySetUserPHID();
362 $same_auto = ($old_auto === $new_auto);
363 $same_assign = ($old_assign === $new_assign);
365 if ($same_auto && $same_assign) {
366 $this->logInfo(
367 pht('UNCHANGED'),
368 pht('No changes to identity.'));
369 } else {
370 if (!$same_auto) {
371 if ($dry_run) {
372 $this->logWarn(
373 pht('DETECTED PHID'),
374 pht(
375 '(Dry Run) Would update detected user from "%s" to "%s".',
376 $this->renderPHID($old_auto),
377 $this->renderPHID($new_auto)));
378 } else {
379 $this->logWarn(
380 pht('DETECTED PHID'),
381 pht(
382 'Detected user updated from "%s" to "%s".',
383 $this->renderPHID($old_auto),
384 $this->renderPHID($new_auto)));
387 if (!$same_assign) {
388 if ($dry_run) {
389 $this->logWarn(
390 pht('ASSIGNED PHID'),
391 pht(
392 '(Dry Run) Would update assigned user from "%s" to "%s".',
393 $this->renderPHID($old_assign),
394 $this->renderPHID($new_assign)));
395 } else {
396 $this->logWarn(
397 pht('ASSIGNED PHID'),
398 pht(
399 'Assigned user updated from "%s" to "%s".',
400 $this->renderPHID($old_assign),
401 $this->renderPHID($new_assign)));
408 private function renderPHID($phid) {
409 if ($phid == null) {
410 return pht('NULL');
413 if (!isset($this->phidCache[$phid])) {
414 $viewer = $this->getViewer();
415 $handles = $viewer->loadHandles(array($phid));
416 $this->phidCache[$phid] = pht(
417 '%s <%s>',
418 $handles[$phid]->getFullName(),
419 $phid);
422 return $this->phidCache[$phid];
425 private function newIdentityEngine() {
426 $viewer = $this->getViewer();
428 return id(new DiffusionRepositoryIdentityEngine())
429 ->setViewer($viewer)
430 ->setDryRun($this->dryRun);