mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 06:42:42 +01:00
Add HarbormasterRunnerWorker, for running CI tests
Summary: This is very preliminary and doesn't actually do anything useful. In theory, it uses Drydock to check out a working copy and run tests. In practice, it's not actually capable of running any of our tests (because of complicated interdependency stuff), but does check out a working copy and //try// to run tests there. Adds various sorts of utility methods to various things as well. Test Plan: Ran `reparse.php --harbormaster --trace <commit>`, observed attempt to run tests via Drydock. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2015, T1049 Differential Revision: https://secure.phabricator.com/D4215
This commit is contained in:
parent
5cd39b3964
commit
adfe84ffce
9 changed files with 142 additions and 5 deletions
|
@ -55,6 +55,10 @@ $args->parse(
|
|||
'delete existing relationship entries between your '.
|
||||
'package and some old commits!)',
|
||||
),
|
||||
array(
|
||||
'name' => 'harbormaster',
|
||||
'help' => 'EXPERIMENTAL. Execute Harbormaster.',
|
||||
),
|
||||
// misc options
|
||||
array(
|
||||
'name' => 'force',
|
||||
|
@ -73,6 +77,7 @@ $reparse_message = $args->getArg('message');
|
|||
$reparse_change = $args->getArg('change');
|
||||
$reparse_herald = $args->getArg('herald');
|
||||
$reparse_owners = $args->getArg('owners');
|
||||
$reparse_harbormaster = $args->getArg('harbormaster');
|
||||
$reparse_what = $args->getArg('revision');
|
||||
$force = $args->getArg('force');
|
||||
$force_local = $args->getArg('force-local');
|
||||
|
@ -83,9 +88,9 @@ if (!$all_from_repo && !$reparse_what) {
|
|||
}
|
||||
|
||||
if (!$reparse_message && !$reparse_change && !$reparse_herald &&
|
||||
!$reparse_owners) {
|
||||
!$reparse_owners && !$reparse_harbormaster) {
|
||||
usage("Specify what information to reparse with --message, --change, ".
|
||||
"--herald, and/or --owners");
|
||||
"--herald, --harbormaster, and/or --owners");
|
||||
}
|
||||
if ($reparse_owners && !$force) {
|
||||
echo phutil_console_wrap(
|
||||
|
@ -202,6 +207,10 @@ foreach ($commits as $commit) {
|
|||
$classes[] = 'PhabricatorRepositoryCommitOwnersWorker';
|
||||
}
|
||||
|
||||
if ($reparse_harbormaster) {
|
||||
$classes[] = 'HarbormasterRunnerWorker';
|
||||
}
|
||||
|
||||
$spec = array(
|
||||
'commitID' => $commit->getID(),
|
||||
'only' => true,
|
||||
|
@ -218,7 +227,7 @@ foreach ($commits as $commit) {
|
|||
foreach ($classes as $class) {
|
||||
$worker = newv($class, array($spec));
|
||||
echo "Running '{$class}'...\n";
|
||||
$worker->doWork();
|
||||
$worker->executeTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -456,6 +456,7 @@ phutil_register_library_map(array(
|
|||
'FeedPublisherWorker' => 'applications/feed/worker/FeedPublisherWorker.php',
|
||||
'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
|
||||
'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php',
|
||||
'HarbormasterRunnerWorker' => 'applications/harbormaster/worker/HarbormasterRunnerWorker.php',
|
||||
'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php',
|
||||
'HeraldAction' => 'applications/herald/storage/HeraldAction.php',
|
||||
'HeraldActionConfig' => 'applications/herald/config/HeraldActionConfig.php',
|
||||
|
@ -1744,6 +1745,7 @@ phutil_register_library_map(array(
|
|||
'FeedPublisherWorker' => 'PhabricatorWorker',
|
||||
'HarbormasterDAO' => 'PhabricatorLiskDAO',
|
||||
'HarbormasterObject' => 'HarbormasterDAO',
|
||||
'HarbormasterRunnerWorker' => 'PhabricatorWorker',
|
||||
'HarbormasterScratchTable' => 'HarbormasterDAO',
|
||||
'HeraldAction' => 'HeraldDAO',
|
||||
'HeraldApplyTranscript' => 'HeraldDAO',
|
||||
|
|
|
@ -22,6 +22,17 @@ abstract class DrydockBlueprint {
|
|||
return get_class($this);
|
||||
}
|
||||
|
||||
protected function loadLease($lease_id) {
|
||||
$lease = id(new DrydockLease())->load($lease_id);
|
||||
if (!$lease) {
|
||||
throw new Exception("No such lease '{$lease_id}'!");
|
||||
}
|
||||
|
||||
$resource = $lease->loadResource();
|
||||
$lease->attachResource($resource);
|
||||
return $lease;
|
||||
}
|
||||
|
||||
|
||||
/* -( Lease Acquisition )-------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -86,6 +86,13 @@ final class DrydockWorkingCopyBlueprint extends DrydockBlueprint {
|
|||
DrydockLease $lease,
|
||||
$type) {
|
||||
|
||||
switch ($type) {
|
||||
case 'command':
|
||||
return $this
|
||||
->loadLease($resource->getAttribute('lease.host'))
|
||||
->getInterface($type);
|
||||
}
|
||||
|
||||
throw new Exception("No interface of type '{$type}'.");
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
|
||||
abstract class DrydockCommandInterface extends DrydockInterface {
|
||||
|
||||
private $workingDirectory;
|
||||
|
||||
public function setWorkingDirectory($working_directory) {
|
||||
$this->workingDirectory = $working_directory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWorkingDirectory() {
|
||||
return $this->workingDirectory;
|
||||
}
|
||||
|
||||
final public function getInterfaceType() {
|
||||
return 'command';
|
||||
}
|
||||
|
@ -24,4 +35,17 @@ abstract class DrydockCommandInterface extends DrydockInterface {
|
|||
|
||||
abstract public function getExecFuture($command);
|
||||
|
||||
protected function applyWorkingDirectoryToArgv(array $argv) {
|
||||
if ($this->getWorkingDirectory() !== null) {
|
||||
$cmd = $argv[0];
|
||||
$cmd = "(cd %s; {$cmd})";
|
||||
$argv = array_merge(
|
||||
array($cmd),
|
||||
array($this->getWorkingDirectory()),
|
||||
array_slice($argv, 1));
|
||||
}
|
||||
|
||||
return $argv;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ final class DrydockLocalCommandInterface extends DrydockCommandInterface {
|
|||
|
||||
public function getExecFuture($command) {
|
||||
$argv = func_get_args();
|
||||
$argv = $this->applyWorkingDirectoryToArgv($argv);
|
||||
|
||||
return newv('ExecFuture', $argv);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface {
|
|||
|
||||
public function getExecFuture($command) {
|
||||
$argv = func_get_args();
|
||||
$argv = $this->applyWorkingDirectoryToArgv($argv);
|
||||
|
||||
$full_command = call_user_func_array('csprintf', $argv);
|
||||
|
||||
// NOTE: The "-t -t" is for psuedo-tty allocation so we can "sudo" on some
|
||||
|
|
|
@ -11,6 +11,26 @@ final class DrydockLease extends DrydockDAO {
|
|||
protected $taskID;
|
||||
|
||||
private $resource;
|
||||
private $releaseOnDestruction;
|
||||
|
||||
/**
|
||||
* Flag this lease to be released when its destructor is called. This is
|
||||
* mostly useful if you have a script which acquires, uses, and then releases
|
||||
* a lease, as you don't need to explicitly handle exceptions to properly
|
||||
* release the lease.
|
||||
*/
|
||||
public function releaseOnDestruction() {
|
||||
$this->releaseOnDestruction = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if ($this->releaseOnDestruction) {
|
||||
if ($this->isActive()) {
|
||||
$this->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getLeaseName() {
|
||||
return pht('Lease %d', $this->getID());
|
||||
|
@ -103,9 +123,17 @@ final class DrydockLease extends DrydockDAO {
|
|||
return $this;
|
||||
}
|
||||
|
||||
private function isActive() {
|
||||
switch ($this->status) {
|
||||
case DrydockLeaseStatus::STATUS_ACTIVE:
|
||||
case DrydockLeaseStatus::STATUS_ACQUIRING:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function assertActive() {
|
||||
if (($this->status != DrydockLeaseStatus::STATUS_ACTIVE) &&
|
||||
($this->status != DrydockLeaseStatus::STATUS_ACQUIRING)) {
|
||||
if (!$this->isActive()) {
|
||||
throw new Exception(
|
||||
"Lease is not active! You can not interact with resources through ".
|
||||
"an inactive lease.");
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterRunnerWorker extends PhabricatorWorker {
|
||||
|
||||
public function getRequiredLeaseTime() {
|
||||
return 60 * 60 * 24;
|
||||
}
|
||||
|
||||
protected function doWork() {
|
||||
$data = $this->getTaskData();
|
||||
$id = idx($data, 'commitID');
|
||||
|
||||
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
|
||||
'id = %d',
|
||||
$id);
|
||||
|
||||
if (!$commit) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
"Commit '{$id}' does not exist!");
|
||||
}
|
||||
|
||||
$repository = id(new PhabricatorRepository())->loadOneWhere(
|
||||
'id = %d',
|
||||
$commit->getRepositoryID());
|
||||
|
||||
if (!$repository) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
"Unable to load repository for commit '{$id}'!");
|
||||
}
|
||||
|
||||
$lease = id(new DrydockLease())
|
||||
->setResourceType('working-copy')
|
||||
->setAttributes(
|
||||
array(
|
||||
'repositoryID' => $repository->getID(),
|
||||
'commit' => $commit->getCommitIdentifier(),
|
||||
))
|
||||
->releaseOnDestruction()
|
||||
->waitUntilActive();
|
||||
|
||||
$cmd = $lease->getInterface('command');
|
||||
list($json) = $cmd
|
||||
->setWorkingDirectory($lease->getResource()->getAttribute('path'))
|
||||
->execx('arc unit --everything --json');
|
||||
$lease->release();
|
||||
|
||||
// TODO: Do something actually useful with this. Requires Harbormaster
|
||||
// buildout.
|
||||
echo $json;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue