mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Install pre-commit hooks in Git repositories
Summary: Ref T4189. T4189 describes most of the intent here: - When updating hosted repositories, sync a pre-commit hook into them instead of doing a `git fetch`. - The hook calls into Phabricator. The acting Phabricator user is sent via PHABRICATOR_USER in the environment. The active repository is sent via CLI. - The hook doesn't do anything useful yet; it just veifies basic parameters, does a little parsing, and exits 0 to allow the commit. Test Plan: - Performed Git pushes and pulls over SSH and HTTP. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4189 Differential Revision: https://secure.phabricator.com/D7682
This commit is contained in:
parent
95c2b50974
commit
618b5cbbc4
9 changed files with 189 additions and 7 deletions
1
bin/commit-hook
Symbolic link
1
bin/commit-hook
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../scripts/repository/commit_hook.php
|
56
scripts/repository/commit_hook.php
Executable file
56
scripts/repository/commit_hook.php
Executable file
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$root = dirname(dirname(dirname(__FILE__)));
|
||||||
|
require_once $root.'/scripts/__init_script__.php';
|
||||||
|
|
||||||
|
$username = getenv('PHABRICATOR_USER');
|
||||||
|
if (!$username) {
|
||||||
|
throw new Exception(pht('usage: define PHABRICATOR_USER in environment'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = id(new PhabricatorPeopleQuery())
|
||||||
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
->withUsernames(array($username))
|
||||||
|
->executeOne();
|
||||||
|
if (!$user) {
|
||||||
|
throw new Exception(pht('No such user "%s"!', $username));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
throw new Exception(pht('usage: commit-hook <callsign>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($user)
|
||||||
|
->withCallsigns(array($argv[1]))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
// This capability check is redundant, but can't hurt.
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
DiffusionCapabilityPush::CAPABILITY,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
if (!$repository) {
|
||||||
|
throw new Exception(pht('No such repository "%s"!', $callsign));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$repository->isHosted()) {
|
||||||
|
// This should be redundant too, but double check just in case.
|
||||||
|
throw new Exception(pht('Repository "%s" is not hosted!', $callsign));
|
||||||
|
}
|
||||||
|
|
||||||
|
$stdin = @file_get_contents('php://stdin');
|
||||||
|
if ($stdin === false) {
|
||||||
|
throw new Exception(pht('Failed to read stdin!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$engine = id(new DiffusionCommitHookEngine())
|
||||||
|
->setViewer($user)
|
||||||
|
->setRepository($repository)
|
||||||
|
->setStdin($stdin);
|
||||||
|
|
||||||
|
$err = $engine->execute();
|
||||||
|
|
||||||
|
exit($err);
|
|
@ -474,6 +474,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/DiffusionCommitChangeTableView.php',
|
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/DiffusionCommitChangeTableView.php',
|
||||||
'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php',
|
'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php',
|
||||||
'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php',
|
'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php',
|
||||||
|
'DiffusionCommitHookEngine' => 'applications/diffusion/engine/DiffusionCommitHookEngine.php',
|
||||||
'DiffusionCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionCommitParentsQuery.php',
|
'DiffusionCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionCommitParentsQuery.php',
|
||||||
'DiffusionCommitQuery' => 'applications/diffusion/query/DiffusionCommitQuery.php',
|
'DiffusionCommitQuery' => 'applications/diffusion/query/DiffusionCommitQuery.php',
|
||||||
'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php',
|
'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php',
|
||||||
|
@ -2797,6 +2798,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionCommitChangeTableView' => 'DiffusionView',
|
'DiffusionCommitChangeTableView' => 'DiffusionView',
|
||||||
'DiffusionCommitController' => 'DiffusionController',
|
'DiffusionCommitController' => 'DiffusionController',
|
||||||
'DiffusionCommitEditController' => 'DiffusionController',
|
'DiffusionCommitEditController' => 'DiffusionController',
|
||||||
|
'DiffusionCommitHookEngine' => 'Phobject',
|
||||||
'DiffusionCommitParentsQuery' => 'DiffusionQuery',
|
'DiffusionCommitParentsQuery' => 'DiffusionQuery',
|
||||||
'DiffusionCommitQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'DiffusionCommitQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'DiffusionCommitTagsController' => 'DiffusionController',
|
'DiffusionCommitTagsController' => 'DiffusionController',
|
||||||
|
|
|
@ -318,6 +318,7 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
'PATH_INFO' => $request_path,
|
'PATH_INFO' => $request_path,
|
||||||
|
|
||||||
'REMOTE_USER' => $viewer->getUsername(),
|
'REMOTE_USER' => $viewer->getUsername(),
|
||||||
|
'PHABRICATOR_USER' => $viewer->getUsername(),
|
||||||
|
|
||||||
// TODO: Set these correctly.
|
// TODO: Set these correctly.
|
||||||
// GIT_COMMITTER_NAME
|
// GIT_COMMITTER_NAME
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionCommitHookEngine extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
private $repository;
|
||||||
|
private $stdin;
|
||||||
|
|
||||||
|
public function setStdin($stdin) {
|
||||||
|
$this->stdin = $stdin;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStdin() {
|
||||||
|
return $this->stdin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRepository(PhabricatorRepository $repository) {
|
||||||
|
$this->repository = $repository;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRepository() {
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() {
|
||||||
|
$type = $this->getRepository()->getVersionControlSystem();
|
||||||
|
switch ($type) {
|
||||||
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||||
|
$err = $this->executeGitHook();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(pht('Unsupported repository type "%s"!', $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeGitHook() {
|
||||||
|
$updates = $this->parseGitUpdates($this->getStdin());
|
||||||
|
|
||||||
|
// TODO: Do useful things.
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseGitUpdates($stdin) {
|
||||||
|
$updates = array();
|
||||||
|
|
||||||
|
$lines = phutil_split_lines($stdin, $retain_endings = false);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$parts = explode(' ', $line, 3);
|
||||||
|
if (count($parts) != 3) {
|
||||||
|
throw new Exception(pht('Expected "old new ref", got "%s".', $line));
|
||||||
|
}
|
||||||
|
$updates[] = array(
|
||||||
|
'old' => $parts[0],
|
||||||
|
'new' => $parts[1],
|
||||||
|
'ref' => $parts[2],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $updates;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,7 +25,8 @@ final class DiffusionSSHGitReceivePackWorkflow
|
||||||
$command = csprintf('git-receive-pack %s', $repository->getLocalPath());
|
$command = csprintf('git-receive-pack %s', $repository->getLocalPath());
|
||||||
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
|
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
|
||||||
|
|
||||||
$future = new ExecFuture('%C', $command);
|
$future = id(new ExecFuture('%C', $command))
|
||||||
|
->setEnv($this->getEnvironment());
|
||||||
|
|
||||||
$err = $this->newPassthruCommand()
|
$err = $this->newPassthruCommand()
|
||||||
->setIOChannel($this->getIOChannel())
|
->setIOChannel($this->getIOChannel())
|
||||||
|
|
|
@ -22,7 +22,8 @@ final class DiffusionSSHGitUploadPackWorkflow
|
||||||
$command = csprintf('git-upload-pack -- %s', $repository->getLocalPath());
|
$command = csprintf('git-upload-pack -- %s', $repository->getLocalPath());
|
||||||
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
|
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
|
||||||
|
|
||||||
$future = new ExecFuture('%C', $command);
|
$future = id(new ExecFuture('%C', $command))
|
||||||
|
->setEnv($this->getEnvironment());
|
||||||
|
|
||||||
$err = $this->newPassthruCommand()
|
$err = $this->newPassthruCommand()
|
||||||
->setIOChannel($this->getIOChannel())
|
->setIOChannel($this->getIOChannel())
|
||||||
|
|
|
@ -17,6 +17,12 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
return $this->args;
|
return $this->args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEnvironment() {
|
||||||
|
return array(
|
||||||
|
'PHABRICATOR_USER' => $this->getUser()->getUsername(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function executeRepositoryOperations();
|
abstract protected function executeRepositoryOperations();
|
||||||
|
|
||||||
protected function writeError($message) {
|
protected function writeError($message) {
|
||||||
|
|
|
@ -83,11 +83,15 @@ final class PhabricatorRepositoryPullEngine
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($repository->isHosted()) {
|
if ($repository->isHosted()) {
|
||||||
$this->logPull(
|
if ($is_git) {
|
||||||
pht(
|
$this->installGitHook();
|
||||||
"Repository '%s' is hosted, so Phabricator does not pull ".
|
} else {
|
||||||
"updates for it.",
|
$this->logPull(
|
||||||
$callsign));
|
pht(
|
||||||
|
"Repository '%s' is hosted, so Phabricator does not pull ".
|
||||||
|
"updates for it.",
|
||||||
|
$callsign));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->logPull(
|
$this->logPull(
|
||||||
pht(
|
pht(
|
||||||
|
@ -146,6 +150,22 @@ final class PhabricatorRepositoryPullEngine
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function installHook($path) {
|
||||||
|
$this->log('%s', pht('Installing commit hook to "%s"...', $path));
|
||||||
|
|
||||||
|
$repository = $this->getRepository();
|
||||||
|
$callsign = $repository->getCallsign();
|
||||||
|
|
||||||
|
$root = dirname(phutil_get_library_root('phabricator'));
|
||||||
|
$bin = $root.'/bin/commit-hook';
|
||||||
|
$cmd = csprintf('exec -- %s %s', $bin, $callsign);
|
||||||
|
|
||||||
|
$hook = "#!/bin/sh\n{$cmd}\n";
|
||||||
|
|
||||||
|
Filesystem::writeFile($path, $hook);
|
||||||
|
Filesystem::changePermissions($path, 0755);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Pulling Git Working Copies )----------------------------------------- */
|
/* -( Pulling Git Working Copies )----------------------------------------- */
|
||||||
|
|
||||||
|
@ -279,6 +299,23 @@ final class PhabricatorRepositoryPullEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task git
|
||||||
|
*/
|
||||||
|
private function installGitHook() {
|
||||||
|
$repository = $this->getRepository();
|
||||||
|
$path = $repository->getLocalPath();
|
||||||
|
|
||||||
|
if ($repository->isWorkingCopyBare()) {
|
||||||
|
$path .= 'hooks/pre-receive';
|
||||||
|
} else {
|
||||||
|
$path .= '.git/hooks/pre-receive';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->installHook($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Pulling Mercurial Working Copies )----------------------------------- */
|
/* -( Pulling Mercurial Working Copies )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
@ -357,4 +394,5 @@ final class PhabricatorRepositoryPullEngine
|
||||||
execx('svnadmin create -- %s', $path);
|
execx('svnadmin create -- %s', $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue