1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Allow WorkingCopy resources to have multiple working copies

Summary:
Ref T9252. For building Phabricator itself, we need to have `libphutil/`, `arcanist/` and `phabricator/` next to one another on disk.

Expand the Drydock WorkingCopy resource so that it can have multiple repositories if the caller needs them.

I'm not sure if I'm going to put the actual config for this in Harbormaster or Drydock yet, but the WorkingCopy resource itself should work the same way in either case.

Test Plan: Restarted a Harbormaster build which leases a working copy, saw it build as expected.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9252

Differential Revision: https://secure.phabricator.com/D14180
This commit is contained in:
epriestley 2015-09-28 09:35:58 -07:00
parent 9b29d46e60
commit 33be8f719f
2 changed files with 155 additions and 64 deletions

View file

@ -37,10 +37,37 @@ final class DrydockWorkingCopyBlueprintImplementation
DrydockResource $resource, DrydockResource $resource,
DrydockLease $lease) { DrydockLease $lease) {
$have_phid = $resource->getAttribute('repositoryPHID'); $need_map = $lease->getAttribute('repositories.map');
$need_phid = $lease->getAttribute('repositoryPHID'); if (!is_array($need_map)) {
return false;
}
if ($need_phid !== $have_phid) { $have_map = $resource->getAttribute('repositories.map');
if (!is_array($have_map)) {
return false;
}
$have_as = ipull($have_map, 'phid');
$need_as = ipull($need_map, 'phid');
foreach ($need_as as $need_directory => $need_phid) {
if (empty($have_as[$need_directory])) {
// This resource is missing a required working copy.
return false;
}
if ($have_as[$need_directory] != $need_phid) {
// This resource has a required working copy, but it contains
// the wrong repository.
return false;
}
unset($have_as[$need_directory]);
}
if ($have_as && $lease->getAttribute('repositories.strict')) {
// This resource has extra repositories, but the lease is strict about
// which repositories are allowed to exist.
return false; return false;
} }
@ -70,14 +97,9 @@ final class DrydockWorkingCopyBlueprintImplementation
DrydockBlueprint $blueprint, DrydockBlueprint $blueprint,
DrydockLease $lease) { DrydockLease $lease) {
$repository_phid = $lease->getAttribute('repositoryPHID');
$repository = $this->loadRepository($repository_phid);
$resource = $this->newResourceTemplate( $resource = $this->newResourceTemplate(
$blueprint, $blueprint,
pht( pht('Working Copy'));
'Working Copy (%s)',
$repository->getCallsign()));
$resource_phid = $resource->getPHID(); $resource_phid = $resource->getPHID();
@ -90,8 +112,17 @@ final class DrydockWorkingCopyBlueprintImplementation
// TODO: Add some limits to the number of working copies we can have at // TODO: Add some limits to the number of working copies we can have at
// once? // once?
$map = $lease->getAttribute('repositories.map');
foreach ($map as $key => $value) {
$map[$key] = array_select_keys(
$value,
array(
'phid',
));
}
return $resource return $resource
->setAttribute('repositoryPHID', $repository->getPHID()) ->setAttribute('repositories.map', $map)
->setAttribute('host.leasePHID', $host_lease->getPHID()) ->setAttribute('host.leasePHID', $host_lease->getPHID())
->allocateResource(); ->allocateResource();
} }
@ -103,26 +134,32 @@ final class DrydockWorkingCopyBlueprintImplementation
$lease = $this->loadHostLease($resource); $lease = $this->loadHostLease($resource);
$this->requireActiveLease($lease); $this->requireActiveLease($lease);
$repository_phid = $resource->getAttribute('repositoryPHID');
$repository = $this->loadRepository($repository_phid);
$repository_id = $repository->getID();
$command_type = DrydockCommandInterface::INTERFACE_TYPE; $command_type = DrydockCommandInterface::INTERFACE_TYPE;
$interface = $lease->getInterface($command_type); $interface = $lease->getInterface($command_type);
// TODO: Make this configurable. // TODO: Make this configurable.
$resource_id = $resource->getID(); $resource_id = $resource->getID();
$root = "/var/drydock/workingcopy-{$resource_id}"; $root = "/var/drydock/workingcopy-{$resource_id}";
$path = "{$root}/repo/{$repository_id}/";
$interface->execx( $map = $resource->getAttribute('repositories.map');
'git clone -- %s %s',
(string)$repository->getCloneURIObject(), $repositories = $this->loadRepositories(ipull($map, 'phid'));
$path); foreach ($map as $directory => $spec) {
// TODO: Validate directory isn't goofy like "/etc" or "../../lol"
// somewhere?
$repository = $repositories[$spec['phid']];
$path = "{$root}/repo/{$directory}/";
// TODO: Run these in parallel?
$interface->execx(
'git clone -- %s %s',
(string)$repository->getCloneURIObject(),
$path);
}
$resource $resource
->setAttribute('workingcopy.root', $root) ->setAttribute('workingcopy.root', $root)
->setAttribute('workingcopy.path', $path)
->activateResource(); ->activateResource();
} }
@ -151,33 +188,55 @@ final class DrydockWorkingCopyBlueprintImplementation
DrydockResource $resource, DrydockResource $resource,
DrydockLease $lease) { DrydockLease $lease) {
$host_lease = $this->loadHostLease($resource);
$command_type = DrydockCommandInterface::INTERFACE_TYPE; $command_type = DrydockCommandInterface::INTERFACE_TYPE;
$interface = $lease->getInterface($command_type); $interface = $host_lease->getInterface($command_type);
$cmd = array(); $map = $lease->getAttribute('repositories.map');
$arg = array(); $root = $resource->getAttribute('workingcopy.root');
$cmd[] = 'git clean -d --force'; $default = null;
$cmd[] = 'git reset --hard HEAD'; foreach ($map as $directory => $spec) {
$cmd[] = 'git fetch'; $cmd = array();
$arg = array();
$commit = $lease->getAttribute('commit'); $cmd[] = 'cd %s';
$branch = $lease->getAttribute('branch'); $arg[] = "{$root}/repo/{$directory}/";
if ($commit !== null) { $cmd[] = 'git clean -d --force';
$cmd[] = 'git reset --hard %s'; $cmd[] = 'git fetch';
$arg[] = $commit;
} else if ($branch !== null) { $commit = idx($spec, 'commit');
$cmd[] = 'git reset --hard %s'; $branch = idx($spec, 'branch');
$arg[] = $branch;
if ($commit !== null) {
$cmd[] = 'git reset --hard %s';
$arg[] = $commit;
} else if ($branch !== null) {
$cmd[] = 'git reset --hard %s';
$arg[] = $branch;
} else {
$cmd[] = 'git reset --hard HEAD';
}
$cmd = implode(' && ', $cmd);
$argv = array_merge(array($cmd), $arg);
$result = call_user_func_array(
array($interface, 'execx'),
$argv);
if (idx($spec, 'default')) {
$default = $directory;
}
} }
$cmd = implode(' && ', $cmd); if ($default === null) {
$argv = array_merge(array($cmd), $arg); $default = head_key($map);
}
$result = call_user_func_array( // TODO: Use working storage?
array($interface, 'execx'), $lease->setAttribute('workingcopy.default', "{$root}/repo/{$default}/");
$argv);
$lease->activateOnResource($resource); $lease->activateOnResource($resource);
} }
@ -217,35 +276,41 @@ final class DrydockWorkingCopyBlueprintImplementation
$host_lease = $this->loadHostLease($resource); $host_lease = $this->loadHostLease($resource);
$command_interface = $host_lease->getInterface($type); $command_interface = $host_lease->getInterface($type);
$path = $resource->getAttribute('workingcopy.path'); $path = $lease->getAttribute('workingcopy.default');
$command_interface->setWorkingDirectory($path); $command_interface->setWorkingDirectory($path);
return $command_interface; return $command_interface;
} }
} }
private function loadRepository($repository_phid) { private function loadRepositories(array $phids) {
$repository = id(new PhabricatorRepositoryQuery()) $repositories = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($repository_phid)) ->withPHIDs($phids)
->executeOne(); ->execute();
if (!$repository) { $repositories = mpull($repositories, null, 'getPHID');
// TODO: Permanent failure.
throw new Exception(
pht(
'Repository PHID "%s" does not exist.',
$repository_phid));
}
switch ($repository->getVersionControlSystem()) { foreach ($phids as $phid) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: if (empty($repositories[$phid])) {
break;
default:
// TODO: Permanent failure. // TODO: Permanent failure.
throw new Exception(pht('Unsupported VCS!')); throw new Exception(
pht(
'Repository PHID "%s" does not exist.',
$phid));
}
} }
return $repository; foreach ($repositories as $repository) {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
break;
default:
// TODO: Permanent failure.
throw new Exception(pht('Unsupported VCS!'));
}
}
return $repositories;
} }
private function loadHostLease(DrydockResource $resource) { private function loadHostLease(DrydockResource $resource) {

View file

@ -45,14 +45,9 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
->setResourceType($working_copy_type) ->setResourceType($working_copy_type)
->setOwnerPHID($build_target->getPHID()); ->setOwnerPHID($build_target->getPHID());
$variables = $build_target->getVariables(); $map = $this->buildRepositoryMap($build_target);
$repository_phid = idx($variables, 'repository.phid'); $lease->setAttribute('repositories.map', $map);
$commit = idx($variables, 'repository.commit');
$lease
->setAttribute('repositoryPHID', $repository_phid)
->setAttribute('commit', $commit);
$task_id = $this->getCurrentWorkerTaskID(); $task_id = $this->getCurrentWorkerTaskID();
if ($task_id) { if ($task_id) {
@ -108,4 +103,35 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
); );
} }
private function buildRepositoryMap(HarbormasterBuildTarget $build_target) {
$viewer = PhabricatorUser::getOmnipotentUser();
$variables = $build_target->getVariables();
$repository_phid = idx($variables, 'repository.phid');
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs(array($repository_phid))
->executeOne();
if (!$repository) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'Unable to load repository with PHID "%s".',
$repository_phid));
}
$commit = idx($variables, 'repository.commit');
$map = array();
$directory = $repository->getCloneName();
$map[$directory] = array(
'phid' => $repository->getPHID(),
'commit' => $commit,
'default' => true,
);
return $map;
}
} }