#!/usr/local/bin/php $val) { if (is_array($val)) { $res[] = "[$key]"; foreach ($val as $skey => $sval) { $res[] = "$skey = $sval"; } $res[] = ''; } else { $res[] = "$key = $val"; } } file_put_contents($file, implode("\n", $res)); } // Get user's heys and put them to the configuration function handleSingleUserPHID( $keydir, $viewer, $userPHID, $system_keys, &$used_keys) { $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withPHIDs(array($userPHID)) ->executeOne(); if (!$user) { return array(); } $keys = id(new PhabricatorAuthSSHKey())->loadAllWhere( 'objectPHID = %s', $user->getPHID()); $members = array(); foreach ($keys as $key) { $full_key_content = $key->getKeyType().' '. $key->getKeyBody().' '. $key->getKeyComment()."\n"; if (array_key_exists($full_key_content, $system_keys)) { $members[] = $system_keys[$full_key_content]; } else { $escaped_key_name = escape_name($key->getName()); $member = 'PHAB_'.$user->getUserName(). '_'.$escaped_key_name. '_'.$key->getID(); $members[] = $member; if (!array_key_exists($member, $used_keys)) { $used_keys[$member] = true; file_put_contents("$keydir/$member.pub", $full_key_content); } } } return $members; } function handleCustomPolicy( $keydir, $viewer, $policy, $system_keys, &$used_keys) { $members = array(); $rules = $policy->getRules(); foreach ($rules as $rule) { // Everyone is denied by default anyway if ($rule['action'] == 'allow') { if ($rule['rule'] == 'PhabricatorPolicyRuleUsers') { foreach ($rule['value'] as $userPHID) { $members = array_merge($members, handleSingleUserPHID($keydir, $viewer, $userPHID, $system_keys, $used_keys)); } } else { /* pass */ } } } return $members; } // Parse repository and put it's members to the config file function handleSingleRepository( $keydir, $viewer, $repository, $all_repositories, $system_keys, &$new_configuration, &$used_keys) { $policies = PhabricatorPolicyQuery::loadPolicies( $viewer, $repository); $pushable = $policies[DiffusionPushCapability::CAPABILITY]; $type = $pushable->getType(); $members = array(); if ($type == PhabricatorPolicyType::TYPE_PROJECT) { $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->needMembers(true) ->withPHIDs(array($pushable->getPHID())) ->executeOne(); $memberPHIDs = $project->getMemberPHIDs(); foreach ($memberPHIDs as $memberPHID) { $members = array_merge($members, handleSingleUserPHID($keydir, $viewer, $memberPHID, $system_keys, $used_keys)); } } else if ($type == PhabricatorPolicyType::TYPE_USER) { $members = handleSingleUserPHID( $keydir, $viewer, $pushable->getPHID(), $system_keys, $used_keys); } else if ($type == PhabricatorPolicyType::TYPE_CUSTOM) { $members = handleCustomPolicy( $keydir, $viewer, $pushable, $system_keys, $used_keys); } else { /* pass */ } if (count($members)) { $uri = $repository->getRemoteURI(); $repository_name = basename($uri, '.git'); $escaped_repository_name = escape_name($repository->getName()); $group_name = "PHAB_${escaped_repository_name}"; $values = array(); $values['members'] = join(' ', $members); $values['readonly'] = join(' ', $all_repositories); $values['writable'] = $repository_name; $new_configuration["group $group_name"] = $values; } } // Remove groups from previous automated configuration built function getCleanOldConfiguration($old_configuration) { $new_configuration = array(); foreach ($old_configuration as $group => $values) { if (!startswith($group, 'group PHAB')) { $new_configuration[$group] = $values; } } return $new_configuration; } // Get non-phab keys function getSystemPublicKeys($keydir) { $files = scandir($keydir); $system_keys = array(); foreach ($files as $file) { if (!startswith($file, "PHAB") && endswith($file, '.pub')) { $key = file_get_contents("$keydir/$file"); $system_keys[$key] = basename($file, '.pub'); } } return $system_keys; } // Remove unused public keys function removeUnusedPublicKeys($keydir, $used_keys) { $files = scandir($keydir); foreach ($files as $file) { if (startswith($file, "PHAB")) { $member = basename($file, '.pub'); if (!array_key_exists($member, $used_keys)) { unlink("$keydir/$file"); } } } } function rebuildConfiguration($gitosis_root) { $keydir = "$gitosis_root/keydir"; $configuration_file = "$gitosis_root/gitosis.conf"; if (!file_exists($configuration_file)) { print("Not found: $configuration_file\n"); return false; } $viewer = id(new PhabricatorUser()) ->loadOneWhere('username = %s', 'sergey'); $old_configuration = parse_ini_file( $configuration_file, true, INI_SCANNER_RAW); $new_configuration = getCleanOldConfiguration( $old_configuration); // Get "system" keys to re-use if phab account uses the // same public key $system_keys = getSystemPublicKeys($keydir); // Get list of all repos which is awailable for read $all_repositories = array(); foreach ($old_configuration as $group => $values) { if (startswith($group, 'repo')) { $repository_name = substr($group, 5, strlen($group) - 5); if ($repository_name == 'gitosis-admin') continue; $all_repositories[] = $repository_name; } } // Fill in new configuration and keys $used_keys = array(); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->execute(); foreach ($repositories as $repository_id => $repository) { $type = $repository->getVersionControlSystem(); if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { handleSingleRepository( $keydir, $viewer, $repository, $all_repositories, $system_keys, $new_configuration, $used_keys); } } write_ini_file($new_configuration, $configuration_file); removeUnusedPublicKeys($keydir, $used_keys); return true; } function getGitCommand($repository) { $git_dir = realpath("$repository/.git"); $git = "git --git-dir='$git_dir'"; $git .= ' --work-tree='.realpath($repository); return $git; } function runGitCommand($repository, $arguments, &$output=null, &$return_var=null) { $git = getGitCommand($repository); $git .= " $arguments"; exec($git, $output, $return_var); return $return_var == 0; } function runGitSshCommand($repository, $key, $arguments, &$output=null, &$return_var=null) { $gitx_ssh = realpath(dirname(__FILE__) . "/gitx-ssh"); $abs_key = realpath($key); $git = "SSH_KEYFILE=$abs_key GIT_SSH=$gitx_ssh "; $git .= getGitCommand($repository); $git .= " $arguments"; exec($git, $output, $return_var); return $return_var == 0; } function repositoryPull($repository, $key) { return runGitSshCommand($repository, $key, 'pull'); } function repositoryCommitAll($repository, $author, $message) { if (!runGitCommand( $repository, 'ls-files --other --exclude-standard', $untracked_files)) { return false; } if (count($untracked_files)) { $flat_files = join(' ', $untracked_files); if (!runGitCommand($repository, "add $flat_files")) { return false; } } runGitCommand($repository, "update-index -q --refresh", $output); runGitCommand($repository, "diff-index --name-only HEAD --", $output); if (count($output)) { return runGitCommand( $repository, "commit --author='$author' -a -m '$message'", $output); } return true; } if (count($argv) != 3) { print("Usage: {$argv[0]} /path/to/gitosis-admin /path/to/id_rsa.pub\n"); exit(1); } $gitosis_root = $argv[1]; $key = $argv[2]; if (!repositoryPull($gitosis_root, $key)) { print("Failed to pull changes from server.\n"); exit(1); } if (!rebuildConfiguration($gitosis_root)) { exit(1); } if (!repositoryCommitAll( $gitosis_root, 'Rebuild Gitadmin ', 'Update to correspond changes in Phabricator')) { print("Failed to commit changes.\n"); exit(1); } runGitSshCommand($gitosis_root, $key, 'push origin master'); ?>