- Fixed some remaining issues caused by the update process. - Support custom rules for the repositories. Currnetly only supports allowing users.
		
			
				
	
	
		
			313 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/local/bin/php
 | |
| <?php
 | |
| 
 | |
| $root = dirname(dirname(dirname(__FILE__)));
 | |
| require_once $root.'/scripts/__init_script__.php';
 | |
| 
 | |
| function escape_name($name) {
 | |
|   return preg_replace('/[^A-Za-z0-9\-]/', '_', $name);
 | |
| }
 | |
| 
 | |
| function startswith($string, $prefix) {
 | |
|   return substr($string, 0, strlen($prefix)) == $prefix;
 | |
| }
 | |
| 
 | |
| function endswith($string, $suffix) {
 | |
|   $suffix_length = strlen($suffix);
 | |
|   return substr($string, strlen($string) - $suffix_length,
 | |
|                 $suffix_length) == $suffix;
 | |
| }
 | |
| 
 | |
| function write_ini_file($array, $file) {
 | |
|   $res = array();
 | |
|   foreach ($array as $key => $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 <null@git.blender.org>',
 | |
|   'Update to correspond changes in Phabricator')) {
 | |
|   print("Failed to commit changes.\n");
 | |
|   exit(1);
 | |
| }
 | |
| 
 | |
| runGitSshCommand($gitosis_root, $key, 'push origin master');
 | |
| ?>
 |