mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-03 04:02:43 +01:00
(stable) Promote 2015 Week 41
This commit is contained in:
commit
be4752f05a
31 changed files with 488 additions and 209 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -34,3 +34,7 @@
|
||||||
|
|
||||||
# NPM local packages
|
# NPM local packages
|
||||||
/support/aphlict/server/node_modules/
|
/support/aphlict/server/node_modules/
|
||||||
|
|
||||||
|
# Places for users to add custom resources.
|
||||||
|
/resources/cows/custom/*
|
||||||
|
/resources/figlet/custom/*
|
||||||
|
|
|
@ -13,7 +13,7 @@ return array(
|
||||||
'differential.pkg.css' => '2de124c9',
|
'differential.pkg.css' => '2de124c9',
|
||||||
'differential.pkg.js' => '6223dd9d',
|
'differential.pkg.js' => '6223dd9d',
|
||||||
'diffusion.pkg.css' => 'f45955ed',
|
'diffusion.pkg.css' => 'f45955ed',
|
||||||
'diffusion.pkg.js' => '0115b37c',
|
'diffusion.pkg.js' => 'ca1c8b5a',
|
||||||
'maniphest.pkg.css' => '4845691a',
|
'maniphest.pkg.css' => '4845691a',
|
||||||
'maniphest.pkg.js' => '3ec6a6d5',
|
'maniphest.pkg.js' => '3ec6a6d5',
|
||||||
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
|
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
|
||||||
|
@ -371,7 +371,7 @@ return array(
|
||||||
'rsrc/js/application/diffusion/behavior-jump-to.js' => '73d09eef',
|
'rsrc/js/application/diffusion/behavior-jump-to.js' => '73d09eef',
|
||||||
'rsrc/js/application/diffusion/behavior-load-blame.js' => '42126667',
|
'rsrc/js/application/diffusion/behavior-load-blame.js' => '42126667',
|
||||||
'rsrc/js/application/diffusion/behavior-locate-file.js' => '6d3e1947',
|
'rsrc/js/application/diffusion/behavior-locate-file.js' => '6d3e1947',
|
||||||
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => '2b228192',
|
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc',
|
||||||
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
|
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
|
||||||
'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58',
|
'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58',
|
||||||
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
|
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
|
||||||
|
@ -574,7 +574,7 @@ return array(
|
||||||
'javelin-behavior-diffusion-commit-graph' => '9007c197',
|
'javelin-behavior-diffusion-commit-graph' => '9007c197',
|
||||||
'javelin-behavior-diffusion-jump-to' => '73d09eef',
|
'javelin-behavior-diffusion-jump-to' => '73d09eef',
|
||||||
'javelin-behavior-diffusion-locate-file' => '6d3e1947',
|
'javelin-behavior-diffusion-locate-file' => '6d3e1947',
|
||||||
'javelin-behavior-diffusion-pull-lastmodified' => '2b228192',
|
'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
|
||||||
'javelin-behavior-doorkeeper-tag' => 'e5822781',
|
'javelin-behavior-doorkeeper-tag' => 'e5822781',
|
||||||
'javelin-behavior-durable-column' => 'c72aa091',
|
'javelin-behavior-durable-column' => 'c72aa091',
|
||||||
'javelin-behavior-error-log' => '6882e80a',
|
'javelin-behavior-error-log' => '6882e80a',
|
||||||
|
@ -998,13 +998,6 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
),
|
),
|
||||||
'2b228192' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-workflow',
|
|
||||||
'javelin-json',
|
|
||||||
),
|
|
||||||
'2b8de964' => array(
|
'2b8de964' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1939,6 +1932,13 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
),
|
),
|
||||||
|
'f01586dc' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-workflow',
|
||||||
|
'javelin-json',
|
||||||
|
),
|
||||||
'f24a53cb' => array(
|
'f24a53cb' => array(
|
||||||
'phui-fontkit-css',
|
'phui-fontkit-css',
|
||||||
),
|
),
|
||||||
|
|
|
@ -1011,6 +1011,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
|
'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
|
||||||
'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php',
|
'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php',
|
||||||
'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
|
'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
|
||||||
|
'HarbormasterDrydockBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterDrydockBuildStepGroup.php',
|
||||||
'HarbormasterDrydockCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterDrydockCommandBuildStepImplementation.php',
|
'HarbormasterDrydockCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterDrydockCommandBuildStepImplementation.php',
|
||||||
'HarbormasterDrydockLeaseArtifact' => 'applications/harbormaster/artifact/HarbormasterDrydockLeaseArtifact.php',
|
'HarbormasterDrydockLeaseArtifact' => 'applications/harbormaster/artifact/HarbormasterDrydockLeaseArtifact.php',
|
||||||
'HarbormasterExecFuture' => 'applications/harbormaster/future/HarbormasterExecFuture.php',
|
'HarbormasterExecFuture' => 'applications/harbormaster/future/HarbormasterExecFuture.php',
|
||||||
|
@ -4813,6 +4814,7 @@ phutil_register_library_map(array(
|
||||||
'HarbormasterController' => 'PhabricatorController',
|
'HarbormasterController' => 'PhabricatorController',
|
||||||
'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
|
'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
|
||||||
'HarbormasterDAO' => 'PhabricatorLiskDAO',
|
'HarbormasterDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'HarbormasterDrydockBuildStepGroup' => 'HarbormasterBuildStepGroup',
|
||||||
'HarbormasterDrydockCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
'HarbormasterDrydockCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||||
'HarbormasterDrydockLeaseArtifact' => 'HarbormasterArtifact',
|
'HarbormasterDrydockLeaseArtifact' => 'HarbormasterArtifact',
|
||||||
'HarbormasterExecFuture' => 'Future',
|
'HarbormasterExecFuture' => 'Future',
|
||||||
|
|
|
@ -98,12 +98,10 @@ final class DiffusionLastModifiedController extends DiffusionController {
|
||||||
$modified = DiffusionView::linkCommit(
|
$modified = DiffusionView::linkCommit(
|
||||||
$drequest->getRepository(),
|
$drequest->getRepository(),
|
||||||
$commit->getCommitIdentifier());
|
$commit->getCommitIdentifier());
|
||||||
$date = phabricator_date($epoch, $viewer);
|
$date = phabricator_datetime($epoch, $viewer);
|
||||||
$time = phabricator_time($epoch, $viewer);
|
|
||||||
} else {
|
} else {
|
||||||
$modified = '';
|
$modified = '';
|
||||||
$date = '';
|
$date = '';
|
||||||
$time = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $commit->getCommitData();
|
$data = $commit->getCommitData();
|
||||||
|
@ -137,7 +135,6 @@ final class DiffusionLastModifiedController extends DiffusionController {
|
||||||
$return = array(
|
$return = array(
|
||||||
'commit' => $modified,
|
'commit' => $modified,
|
||||||
'date' => $date,
|
'date' => $date,
|
||||||
'time' => $time,
|
|
||||||
'author' => $author,
|
'author' => $author,
|
||||||
'details' => $details,
|
'details' => $details,
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,11 @@ final class DiffusionBranchTableView extends DiffusionView {
|
||||||
$drequest = $this->getDiffusionRequest();
|
$drequest = $this->getDiffusionRequest();
|
||||||
$current_branch = $drequest->getBranch();
|
$current_branch = $drequest->getBranch();
|
||||||
$repository = $drequest->getRepository();
|
$repository = $drequest->getRepository();
|
||||||
|
$commits = $this->commits;
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
|
$buildables = $this->loadBuildables($commits);
|
||||||
|
$have_builds = false;
|
||||||
|
|
||||||
$can_close_branches = ($repository->isHg());
|
$can_close_branches = ($repository->isHg());
|
||||||
|
|
||||||
|
@ -31,13 +36,21 @@ final class DiffusionBranchTableView extends DiffusionView {
|
||||||
$rows = array();
|
$rows = array();
|
||||||
$rowc = array();
|
$rowc = array();
|
||||||
foreach ($this->branches as $branch) {
|
foreach ($this->branches as $branch) {
|
||||||
$commit = idx($this->commits, $branch->getCommitIdentifier());
|
$commit = idx($commits, $branch->getCommitIdentifier());
|
||||||
if ($commit) {
|
if ($commit) {
|
||||||
$details = $commit->getSummary();
|
$details = $commit->getSummary();
|
||||||
$datetime = phabricator_datetime($commit->getEpoch(), $this->user);
|
$datetime = phabricator_datetime($commit->getEpoch(), $viewer);
|
||||||
|
$buildable = idx($buildables, $commit->getPHID());
|
||||||
|
if ($buildable) {
|
||||||
|
$build_status = $this->renderBuildable($buildable);
|
||||||
|
$have_builds = true;
|
||||||
|
} else {
|
||||||
|
$build_status = null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$datetime = null;
|
$datetime = null;
|
||||||
$details = null;
|
$details = null;
|
||||||
|
$build_status = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($repository->shouldSkipAutocloseBranch($branch->getShortName())) {
|
switch ($repository->shouldSkipAutocloseBranch($branch->getShortName())) {
|
||||||
|
@ -86,16 +99,7 @@ final class DiffusionBranchTableView extends DiffusionView {
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
phutil_tag(
|
$this->linkBranchHistory($branch->getShortName()),
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => $drequest->generateURI(
|
|
||||||
array(
|
|
||||||
'action' => 'history',
|
|
||||||
'branch' => $branch->getShortName(),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
pht('History')),
|
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
|
@ -109,10 +113,11 @@ final class DiffusionBranchTableView extends DiffusionView {
|
||||||
self::linkCommit(
|
self::linkCommit(
|
||||||
$drequest->getRepository(),
|
$drequest->getRepository(),
|
||||||
$branch->getCommitIdentifier()),
|
$branch->getCommitIdentifier()),
|
||||||
|
$build_status,
|
||||||
$status,
|
$status,
|
||||||
|
AphrontTableView::renderSingleDisplayLine($details),
|
||||||
$status_icon,
|
$status_icon,
|
||||||
$datetime,
|
$datetime,
|
||||||
AphrontTableView::renderSingleDisplayLine($details),
|
|
||||||
);
|
);
|
||||||
if ($branch->getShortName() == $current_branch) {
|
if ($branch->getShortName() == $current_branch) {
|
||||||
$rowc[] = 'highlighted';
|
$rowc[] = 'highlighted';
|
||||||
|
@ -124,33 +129,37 @@ final class DiffusionBranchTableView extends DiffusionView {
|
||||||
$view = new AphrontTableView($rows);
|
$view = new AphrontTableView($rows);
|
||||||
$view->setHeaders(
|
$view->setHeaders(
|
||||||
array(
|
array(
|
||||||
pht('History'),
|
null,
|
||||||
pht('Branch'),
|
pht('Branch'),
|
||||||
pht('Head'),
|
pht('Head'),
|
||||||
|
null,
|
||||||
pht('State'),
|
pht('State'),
|
||||||
pht(''),
|
|
||||||
pht('Modified'),
|
|
||||||
pht('Details'),
|
pht('Details'),
|
||||||
|
null,
|
||||||
|
pht('Committed'),
|
||||||
));
|
));
|
||||||
$view->setColumnClasses(
|
$view->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
'',
|
'',
|
||||||
'pri',
|
'pri',
|
||||||
'',
|
'',
|
||||||
'',
|
'icon',
|
||||||
'',
|
|
||||||
'',
|
'',
|
||||||
'wide',
|
'wide',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
));
|
));
|
||||||
$view->setColumnVisibility(
|
$view->setColumnVisibility(
|
||||||
array(
|
array(
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
$have_builds,
|
||||||
$can_close_branches,
|
$can_close_branches,
|
||||||
));
|
));
|
||||||
$view->setRowClasses($rowc);
|
$view->setRowClasses($rowc);
|
||||||
return $view->render();
|
return $view->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
||||||
$show_edit = false;
|
$show_edit = false;
|
||||||
foreach ($this->paths as $path) {
|
foreach ($this->paths as $path) {
|
||||||
|
|
||||||
|
$history_link = $this->linkHistory($path->getPath());
|
||||||
|
|
||||||
$dir_slash = null;
|
$dir_slash = null;
|
||||||
$file_type = $path->getFileType();
|
$file_type = $path->getFileType();
|
||||||
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
|
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
|
||||||
|
@ -67,7 +69,6 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
||||||
'lint' => celerity_generate_unique_node_id(),
|
'lint' => celerity_generate_unique_node_id(),
|
||||||
'commit' => celerity_generate_unique_node_id(),
|
'commit' => celerity_generate_unique_node_id(),
|
||||||
'date' => celerity_generate_unique_node_id(),
|
'date' => celerity_generate_unique_node_id(),
|
||||||
'time' => celerity_generate_unique_node_id(),
|
|
||||||
'author' => celerity_generate_unique_node_id(),
|
'author' => celerity_generate_unique_node_id(),
|
||||||
'details' => celerity_generate_unique_node_id(),
|
'details' => celerity_generate_unique_node_id(),
|
||||||
);
|
);
|
||||||
|
@ -78,13 +79,13 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
|
$history_link,
|
||||||
$browse_link,
|
$browse_link,
|
||||||
idx($dict, 'lint'),
|
idx($dict, 'lint'),
|
||||||
$dict['commit'],
|
$dict['commit'],
|
||||||
$dict['author'],
|
$dict['author'],
|
||||||
$dict['details'],
|
$dict['details'],
|
||||||
$dict['date'],
|
$dict['date'],
|
||||||
$dict['time'],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,29 +109,29 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
||||||
$view = new AphrontTableView($rows);
|
$view = new AphrontTableView($rows);
|
||||||
$view->setHeaders(
|
$view->setHeaders(
|
||||||
array(
|
array(
|
||||||
|
null,
|
||||||
pht('Path'),
|
pht('Path'),
|
||||||
($lint ? $lint : pht('Lint')),
|
($lint ? $lint : pht('Lint')),
|
||||||
pht('Modified'),
|
pht('Modified'),
|
||||||
pht('Author/Committer'),
|
pht('Author/Committer'),
|
||||||
pht('Details'),
|
pht('Details'),
|
||||||
pht('Date'),
|
pht('Committed'),
|
||||||
pht('Time'),
|
|
||||||
));
|
));
|
||||||
$view->setColumnClasses(
|
$view->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
|
'nudgeright',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
'',
|
'',
|
||||||
'n',
|
|
||||||
'n',
|
|
||||||
'',
|
'',
|
||||||
'wide',
|
'wide',
|
||||||
'',
|
'',
|
||||||
'right',
|
|
||||||
));
|
));
|
||||||
$view->setColumnVisibility(
|
$view->setColumnVisibility(
|
||||||
array(
|
array(
|
||||||
true,
|
true,
|
||||||
$show_lint,
|
|
||||||
true,
|
true,
|
||||||
|
$show_lint,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
@ -140,11 +141,11 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
||||||
$view->setDeviceVisibility(
|
$view->setDeviceVisibility(
|
||||||
array(
|
array(
|
||||||
true,
|
true,
|
||||||
false,
|
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
true,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,10 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
private $handles = array();
|
private $handles = array();
|
||||||
private $isHead;
|
private $isHead;
|
||||||
private $parents;
|
private $parents;
|
||||||
private $buildCache;
|
|
||||||
|
|
||||||
public function setHistory(array $history) {
|
public function setHistory(array $history) {
|
||||||
assert_instances_of($history, 'DiffusionPathChange');
|
assert_instances_of($history, 'DiffusionPathChange');
|
||||||
$this->history = $history;
|
$this->history = $history;
|
||||||
$this->buildCache = null;
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,33 +60,14 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadBuildablesOnDemand() {
|
|
||||||
if ($this->buildCache !== null) {
|
|
||||||
return $this->buildCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
$commits_to_builds = array();
|
|
||||||
|
|
||||||
$commits = mpull($this->history, 'getCommit');
|
|
||||||
|
|
||||||
$commit_phids = mpull($commits, 'getPHID');
|
|
||||||
|
|
||||||
$buildables = id(new HarbormasterBuildableQuery())
|
|
||||||
->setViewer($this->getUser())
|
|
||||||
->withBuildablePHIDs($commit_phids)
|
|
||||||
->withManualBuildables(false)
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$this->buildCache = mpull($buildables, null, 'getBuildablePHID');
|
|
||||||
|
|
||||||
return $this->buildCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
$drequest = $this->getDiffusionRequest();
|
$drequest = $this->getDiffusionRequest();
|
||||||
|
|
||||||
$viewer = $this->getUser();
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
|
$buildables = $this->loadBuildables(mpull($this->history, 'getCommit'));
|
||||||
|
$has_any_build = false;
|
||||||
|
|
||||||
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
|
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
|
||||||
'PhabricatorDifferentialApplication',
|
'PhabricatorDifferentialApplication',
|
||||||
$viewer);
|
$viewer);
|
||||||
|
@ -110,11 +89,9 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
$epoch = $history->getEpoch();
|
$epoch = $history->getEpoch();
|
||||||
|
|
||||||
if ($epoch) {
|
if ($epoch) {
|
||||||
$date = phabricator_date($epoch, $this->user);
|
$committed = phabricator_datetime($epoch, $viewer);
|
||||||
$time = phabricator_time($epoch, $this->user);
|
|
||||||
} else {
|
} else {
|
||||||
$date = null;
|
$committed = null;
|
||||||
$time = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $history->getCommitData();
|
$data = $history->getCommitData();
|
||||||
|
@ -160,36 +137,9 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
|
|
||||||
$build = null;
|
$build = null;
|
||||||
if ($show_builds) {
|
if ($show_builds) {
|
||||||
$buildable_lookup = $this->loadBuildablesOnDemand();
|
$buildable = idx($buildables, $commit->getPHID());
|
||||||
$buildable = idx($buildable_lookup, $commit->getPHID());
|
|
||||||
if ($buildable !== null) {
|
if ($buildable !== null) {
|
||||||
$icon = HarbormasterBuildable::getBuildableStatusIcon(
|
$build = $this->renderBuildable($buildable);
|
||||||
$buildable->getBuildableStatus());
|
|
||||||
$color = HarbormasterBuildable::getBuildableStatusColor(
|
|
||||||
$buildable->getBuildableStatus());
|
|
||||||
$name = HarbormasterBuildable::getBuildableStatusName(
|
|
||||||
$buildable->getBuildableStatus());
|
|
||||||
|
|
||||||
$icon_view = id(new PHUIIconView())
|
|
||||||
->setIconFont($icon.' '.$color);
|
|
||||||
|
|
||||||
$tooltip_view = javelin_tag(
|
|
||||||
'span',
|
|
||||||
array(
|
|
||||||
'sigil' => 'has-tooltip',
|
|
||||||
'meta' => array('tip' => $name),
|
|
||||||
),
|
|
||||||
$icon_view);
|
|
||||||
|
|
||||||
Javelin::initBehavior('phabricator-tooltips');
|
|
||||||
|
|
||||||
$href_view = phutil_tag(
|
|
||||||
'a',
|
|
||||||
array('href' => '/'.$buildable->getMonogram()),
|
|
||||||
$tooltip_view);
|
|
||||||
|
|
||||||
$build = $href_view;
|
|
||||||
|
|
||||||
$has_any_build = true;
|
$has_any_build = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,8 +164,7 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
null),
|
null),
|
||||||
$author,
|
$author,
|
||||||
$summary,
|
$summary,
|
||||||
$date,
|
$committed,
|
||||||
$time,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,30 +175,28 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
null,
|
null,
|
||||||
pht('Commit'),
|
pht('Commit'),
|
||||||
null,
|
null,
|
||||||
pht('Revision'),
|
null,
|
||||||
pht('Author/Committer'),
|
pht('Author/Committer'),
|
||||||
pht('Details'),
|
pht('Details'),
|
||||||
pht('Date'),
|
pht('Committed'),
|
||||||
pht('Time'),
|
|
||||||
));
|
));
|
||||||
$view->setColumnClasses(
|
$view->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
'threads',
|
'threads',
|
||||||
'nudgeright',
|
'nudgeright',
|
||||||
'n',
|
'',
|
||||||
'icon',
|
'icon',
|
||||||
'n',
|
'',
|
||||||
'',
|
'',
|
||||||
'wide',
|
'wide',
|
||||||
'',
|
'',
|
||||||
'right',
|
|
||||||
));
|
));
|
||||||
$view->setColumnVisibility(
|
$view->setColumnVisibility(
|
||||||
array(
|
array(
|
||||||
$graph ? true : false,
|
$graph ? true : false,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true,
|
$has_any_build,
|
||||||
$show_revisions,
|
$show_revisions,
|
||||||
));
|
));
|
||||||
$view->setDeviceVisibility(
|
$view->setDeviceVisibility(
|
||||||
|
@ -262,7 +209,6 @@ final class DiffusionHistoryTableView extends DiffusionView {
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
));
|
));
|
||||||
return $view->render();
|
return $view->render();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ final class DiffusionTagListView extends DiffusionView {
|
||||||
$drequest = $this->getDiffusionRequest();
|
$drequest = $this->getDiffusionRequest();
|
||||||
$repository = $drequest->getRepository();
|
$repository = $drequest->getRepository();
|
||||||
|
|
||||||
|
$buildables = $this->loadBuildables($this->commits);
|
||||||
|
$has_builds = false;
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
foreach ($this->tags as $tag) {
|
foreach ($this->tags as $tag) {
|
||||||
|
@ -80,30 +82,56 @@ final class DiffusionTagListView extends DiffusionView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$build = null;
|
||||||
|
if ($commit) {
|
||||||
|
$buildable = idx($buildables, $commit->getPHID());
|
||||||
|
if ($buildable) {
|
||||||
|
$build = $this->renderBuildable($buildable);
|
||||||
|
$has_builds = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$history = $this->linkTagHistory($tag->getName());
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
|
$history,
|
||||||
$tag_link,
|
$tag_link,
|
||||||
$commit_link,
|
$commit_link,
|
||||||
$description,
|
$build,
|
||||||
$author,
|
$author,
|
||||||
|
$description,
|
||||||
phabricator_datetime($tag->getEpoch(), $this->user),
|
phabricator_datetime($tag->getEpoch(), $this->user),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$table = new AphrontTableView($rows);
|
$table = id(new AphrontTableView($rows))
|
||||||
$table->setHeaders(
|
->setHeaders(
|
||||||
array(
|
array(
|
||||||
pht('Tag'),
|
null,
|
||||||
pht('Commit'),
|
pht('Tag'),
|
||||||
pht('Description'),
|
pht('Commit'),
|
||||||
pht('Author'),
|
null,
|
||||||
pht('Created'),
|
pht('Author'),
|
||||||
));
|
pht('Description'),
|
||||||
$table->setColumnClasses(
|
pht('Created'),
|
||||||
array(
|
))
|
||||||
'pri',
|
->setColumnClasses(
|
||||||
'',
|
array(
|
||||||
'wide',
|
'nudgeright',
|
||||||
));
|
'pri',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'wide',
|
||||||
|
))
|
||||||
|
->setColumnVisibility(
|
||||||
|
array(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
$has_builds,
|
||||||
|
));
|
||||||
|
|
||||||
return $table->render();
|
return $table->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,30 @@ abstract class DiffusionView extends AphrontView {
|
||||||
'path' => $path,
|
'path' => $path,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
return $this->renderHistoryLink($href);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function linkBranchHistory($branch) {
|
||||||
|
$href = $this->getDiffusionRequest()->generateURI(
|
||||||
|
array(
|
||||||
|
'action' => 'history',
|
||||||
|
'branch' => $branch,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->renderHistoryLink($href);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function linkTagHistory($tag) {
|
||||||
|
$href = $this->getDiffusionRequest()->generateURI(
|
||||||
|
array(
|
||||||
|
'action' => 'history',
|
||||||
|
'commit' => $tag,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->renderHistoryLink($href);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderHistoryLink($href) {
|
||||||
return javelin_tag(
|
return javelin_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
|
@ -31,7 +55,7 @@ abstract class DiffusionView extends AphrontView {
|
||||||
'align' => 'E',
|
'align' => 'E',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
id(new PHUIIconView())->setIconFont('fa-list-ul blue'));
|
id(new PHUIIconView())->setIconFont('fa-history bluegrey'));
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function linkBrowse($path, array $details = array()) {
|
final public function linkBrowse($path, array $details = array()) {
|
||||||
|
@ -170,4 +194,58 @@ abstract class DiffusionView extends AphrontView {
|
||||||
return hsprintf('%s', $name);
|
return hsprintf('%s', $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function renderBuildable(HarbormasterBuildable $buildable) {
|
||||||
|
$status = $buildable->getBuildableStatus();
|
||||||
|
|
||||||
|
$icon = HarbormasterBuildable::getBuildableStatusIcon($status);
|
||||||
|
$color = HarbormasterBuildable::getBuildableStatusColor($status);
|
||||||
|
$name = HarbormasterBuildable::getBuildableStatusName($status);
|
||||||
|
|
||||||
|
$icon_view = id(new PHUIIconView())
|
||||||
|
->setIconFont($icon.' '.$color);
|
||||||
|
|
||||||
|
$tooltip_view = javelin_tag(
|
||||||
|
'span',
|
||||||
|
array(
|
||||||
|
'sigil' => 'has-tooltip',
|
||||||
|
'meta' => array('tip' => $name),
|
||||||
|
),
|
||||||
|
$icon_view);
|
||||||
|
|
||||||
|
Javelin::initBehavior('phabricator-tooltips');
|
||||||
|
|
||||||
|
return phutil_tag(
|
||||||
|
'a',
|
||||||
|
array('href' => '/'.$buildable->getMonogram()),
|
||||||
|
$tooltip_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function loadBuildables(array $commits) {
|
||||||
|
assert_instances_of($commits, 'PhabricatorRepositoryCommit');
|
||||||
|
|
||||||
|
if (!$commits) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
|
$harbormaster_app = 'PhabricatorHarbormasterApplication';
|
||||||
|
$have_harbormaster = PhabricatorApplication::isClassInstalledForViewer(
|
||||||
|
$harbormaster_app,
|
||||||
|
$viewer);
|
||||||
|
|
||||||
|
if ($have_harbormaster) {
|
||||||
|
$buildables = id(new HarbormasterBuildableQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withBuildablePHIDs(mpull($commits, 'getPHID'))
|
||||||
|
->withManualBuildables(false)
|
||||||
|
->execute();
|
||||||
|
$buildables = mpull($buildables, null, 'getBuildablePHID');
|
||||||
|
} else {
|
||||||
|
$buildables = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $buildables;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,13 +262,14 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
||||||
array(
|
array(
|
||||||
DrydockResourceStatus::STATUS_PENDING,
|
DrydockResourceStatus::STATUS_PENDING,
|
||||||
DrydockResourceStatus::STATUS_ACTIVE,
|
DrydockResourceStatus::STATUS_ACTIVE,
|
||||||
|
DrydockResourceStatus::STATUS_BROKEN,
|
||||||
DrydockResourceStatus::STATUS_RELEASED,
|
DrydockResourceStatus::STATUS_RELEASED,
|
||||||
))
|
))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$allocated_phids = array();
|
$allocated_phids = array();
|
||||||
foreach ($pool as $resource) {
|
foreach ($pool as $resource) {
|
||||||
$allocated_phids[] = $resource->getAttribute('almanacDevicePHID');
|
$allocated_phids[] = $resource->getAttribute('almanacBindingPHID');
|
||||||
}
|
}
|
||||||
$allocated_phids = array_fuse($allocated_phids);
|
$allocated_phids = array_fuse($allocated_phids);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getViewer() {
|
||||||
|
return PhabricatorUser::getOmnipotentUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Lease Acquisition )-------------------------------------------------- */
|
/* -( Lease Acquisition )-------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -288,7 +292,7 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function newLease(DrydockBlueprint $blueprint) {
|
protected function newLease(DrydockBlueprint $blueprint) {
|
||||||
return id(new DrydockLease());
|
return DrydockLease::initializeNewLease();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function requireActiveLease(DrydockLease $lease) {
|
protected function requireActiveLease(DrydockLease $lease) {
|
||||||
|
@ -310,4 +314,67 @@ abstract class DrydockBlueprintImplementation extends Phobject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply standard limits on resource allocation rate.
|
||||||
|
*
|
||||||
|
* @param DrydockBlueprint The blueprint requesting an allocation.
|
||||||
|
* @return bool True if further allocations should be limited.
|
||||||
|
*/
|
||||||
|
protected function shouldLimitAllocatingPoolSize(
|
||||||
|
DrydockBlueprint $blueprint) {
|
||||||
|
|
||||||
|
// TODO: If this mechanism sticks around, these values should be
|
||||||
|
// configurable by the blueprint implementation.
|
||||||
|
|
||||||
|
// Limit on total number of active resources.
|
||||||
|
$total_limit = 1;
|
||||||
|
|
||||||
|
// Always allow at least this many allocations to be in flight at once.
|
||||||
|
$min_allowed = 1;
|
||||||
|
|
||||||
|
// Allow this fraction of allocating resources as a fraction of active
|
||||||
|
// resources.
|
||||||
|
$growth_factor = 0.25;
|
||||||
|
|
||||||
|
$resource = new DrydockResource();
|
||||||
|
$conn_r = $resource->establishConnection('r');
|
||||||
|
|
||||||
|
$counts = queryfx_all(
|
||||||
|
$conn_r,
|
||||||
|
'SELECT status, COUNT(*) N FROM %T WHERE blueprintPHID = %s',
|
||||||
|
$resource->getTableName(),
|
||||||
|
$blueprint->getPHID());
|
||||||
|
$counts = ipull($counts, 'N', 'status');
|
||||||
|
|
||||||
|
$n_alloc = idx($counts, DrydockResourceStatus::STATUS_PENDING, 0);
|
||||||
|
$n_active = idx($counts, DrydockResourceStatus::STATUS_ACTIVE, 0);
|
||||||
|
$n_broken = idx($counts, DrydockResourceStatus::STATUS_BROKEN, 0);
|
||||||
|
$n_released = idx($counts, DrydockResourceStatus::STATUS_RELEASED, 0);
|
||||||
|
|
||||||
|
// If we're at the limit on total active resources, limit additional
|
||||||
|
// allocations.
|
||||||
|
$n_total = ($n_alloc + $n_active + $n_broken + $n_released);
|
||||||
|
if ($n_total >= $total_limit) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the number of in-flight allocations is fewer than the minimum number
|
||||||
|
// of allowed allocations, don't impose a limit.
|
||||||
|
if ($n_alloc < $min_allowed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed_alloc = (int)ceil($n_active * $growth_factor);
|
||||||
|
|
||||||
|
// If the number of in-flight allocation is fewer than the number of
|
||||||
|
// allowed allocations according to the pool growth factor, don't impose
|
||||||
|
// a limit.
|
||||||
|
if ($n_alloc < $allowed_alloc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,17 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
public function canAllocateResourceForLease(
|
public function canAllocateResourceForLease(
|
||||||
DrydockBlueprint $blueprint,
|
DrydockBlueprint $blueprint,
|
||||||
DrydockLease $lease) {
|
DrydockLease $lease) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
if ($this->shouldLimitAllocatingPoolSize($blueprint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: If we have a pending resource which is compatible with the
|
||||||
|
// configuration for this lease, prevent a new allocation? Otherwise the
|
||||||
|
// queue can fill up with copies of requests from the same lease. But
|
||||||
|
// maybe we can deal with this with "pre-leasing"?
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +48,12 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
DrydockResource $resource,
|
DrydockResource $resource,
|
||||||
DrydockLease $lease) {
|
DrydockLease $lease) {
|
||||||
|
|
||||||
|
// Don't hand out leases on working copies which have not activated, since
|
||||||
|
// it may take an arbitrarily long time for them to acquire a host.
|
||||||
|
if (!$resource->isActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$need_map = $lease->getAttribute('repositories.map');
|
$need_map = $lease->getAttribute('repositories.map');
|
||||||
if (!is_array($need_map)) {
|
if (!is_array($need_map)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -104,8 +121,13 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
$host_lease = $this->newLease($blueprint)
|
$host_lease = $this->newLease($blueprint)
|
||||||
->setResourceType('host')
|
->setResourceType('host')
|
||||||
->setOwnerPHID($resource_phid)
|
->setOwnerPHID($resource_phid)
|
||||||
->setAttribute('workingcopy.resourcePHID', $resource_phid)
|
->setAttribute('workingcopy.resourcePHID', $resource_phid);
|
||||||
->queueForActivation();
|
|
||||||
|
$resource
|
||||||
|
->setAttribute('host.leasePHID', $host_lease->getPHID())
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$host_lease->queueForActivation();
|
||||||
|
|
||||||
// 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?
|
||||||
|
@ -121,7 +143,6 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
|
|
||||||
return $resource
|
return $resource
|
||||||
->setAttribute('repositories.map', $map)
|
->setAttribute('repositories.map', $map)
|
||||||
->setAttribute('host.leasePHID', $host_lease->getPHID())
|
|
||||||
->allocateResource();
|
->allocateResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +186,13 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
DrydockBlueprint $blueprint,
|
DrydockBlueprint $blueprint,
|
||||||
DrydockResource $resource) {
|
DrydockResource $resource) {
|
||||||
|
|
||||||
$lease = $this->loadHostLease($resource);
|
try {
|
||||||
|
$lease = $this->loadHostLease($resource);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
// If we can't load the lease, assume we don't need to take any actions
|
||||||
|
// to destroy it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy the lease on the host.
|
// Destroy the lease on the host.
|
||||||
$lease->releaseOnDestruction();
|
$lease->releaseOnDestruction();
|
||||||
|
@ -310,8 +337,10 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadRepositories(array $phids) {
|
private function loadRepositories(array $phids) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$repositories = id(new PhabricatorRepositoryQuery())
|
$repositories = id(new PhabricatorRepositoryQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer($viewer)
|
||||||
->withPHIDs($phids)
|
->withPHIDs($phids)
|
||||||
->execute();
|
->execute();
|
||||||
$repositories = mpull($repositories, null, 'getPHID');
|
$repositories = mpull($repositories, null, 'getPHID');
|
||||||
|
@ -343,7 +372,7 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadHostLease(DrydockResource $resource) {
|
private function loadHostLease(DrydockResource $resource) {
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$lease_phid = $resource->getAttribute('host.leasePHID');
|
$lease_phid = $resource->getAttribute('host.leasePHID');
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class DrydockLeaseWaitingForResourcesLogType extends DrydockLogType {
|
||||||
|
|
||||||
return pht(
|
return pht(
|
||||||
'Waiting for available resources from: %s.',
|
'Waiting for available resources from: %s.',
|
||||||
$viewer->renderHandleList($blueprint_phids));
|
$viewer->renderHandleList($blueprint_phids)->render());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,16 @@ final class DrydockLease extends DrydockDAO
|
||||||
private $activateWhenAcquired = false;
|
private $activateWhenAcquired = false;
|
||||||
private $slotLocks = array();
|
private $slotLocks = array();
|
||||||
|
|
||||||
|
public static function initializeNewLease() {
|
||||||
|
$lease = new DrydockLease();
|
||||||
|
|
||||||
|
// Pregenerate a PHID so that the caller can set something up to release
|
||||||
|
// this lease before queueing it for activation.
|
||||||
|
$lease->setPHID($lease->generatePHID());
|
||||||
|
|
||||||
|
return $lease;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag this lease to be released when its destructor is called. This is
|
* 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
|
* mostly useful if you have a script which acquires, uses, and then releases
|
||||||
|
@ -232,6 +242,7 @@ final class DrydockLease extends DrydockDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->openTransaction();
|
$this->openTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
||||||
$this->slotLocks = array();
|
$this->slotLocks = array();
|
||||||
|
@ -247,16 +258,12 @@ final class DrydockLease extends DrydockDAO
|
||||||
throw $ex;
|
throw $ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$this
|
||||||
$this
|
->setResourcePHID($resource->getPHID())
|
||||||
->setResourcePHID($resource->getPHID())
|
->attachResource($resource)
|
||||||
->attachResource($resource)
|
->setStatus($new_status)
|
||||||
->setStatus($new_status)
|
->save();
|
||||||
->save();
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$this->killTransaction();
|
|
||||||
throw $ex;
|
|
||||||
}
|
|
||||||
$this->saveTransaction();
|
$this->saveTransaction();
|
||||||
|
|
||||||
$this->isAcquired = true;
|
$this->isAcquired = true;
|
||||||
|
@ -295,12 +302,24 @@ final class DrydockLease extends DrydockDAO
|
||||||
|
|
||||||
$this->openTransaction();
|
$this->openTransaction();
|
||||||
|
|
||||||
$this
|
try {
|
||||||
->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)
|
|
||||||
->save();
|
|
||||||
|
|
||||||
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
||||||
$this->slotLocks = array();
|
$this->slotLocks = array();
|
||||||
|
} catch (DrydockSlotLockException $ex) {
|
||||||
|
$this->killTransaction();
|
||||||
|
|
||||||
|
$this->logEvent(
|
||||||
|
DrydockSlotLockFailureLogType::LOGCONST,
|
||||||
|
array(
|
||||||
|
'locks' => $ex->getLockMap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this
|
||||||
|
->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->saveTransaction();
|
$this->saveTransaction();
|
||||||
|
|
||||||
|
|
|
@ -113,13 +113,6 @@ final class DrydockResource extends DrydockDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function allocateResource() {
|
public function allocateResource() {
|
||||||
if ($this->getID()) {
|
|
||||||
throw new Exception(
|
|
||||||
pht(
|
|
||||||
'Trying to allocate a resource which has already been persisted. '.
|
|
||||||
'Only new resources may be allocated.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We expect resources to have a pregenerated PHID, as they should have
|
// We expect resources to have a pregenerated PHID, as they should have
|
||||||
// been created by a call to DrydockBlueprint->newResourceTemplate().
|
// been created by a call to DrydockBlueprint->newResourceTemplate().
|
||||||
if (!$this->getPHID()) {
|
if (!$this->getPHID()) {
|
||||||
|
@ -155,9 +148,14 @@ final class DrydockResource extends DrydockDAO
|
||||||
} catch (DrydockSlotLockException $ex) {
|
} catch (DrydockSlotLockException $ex) {
|
||||||
$this->killTransaction();
|
$this->killTransaction();
|
||||||
|
|
||||||
// NOTE: We have to log this on the blueprint, as the resource is not
|
if ($this->getID()) {
|
||||||
// going to be saved so the PHID will vanish.
|
$log_target = $this;
|
||||||
$this->getBlueprint()->logEvent(
|
} else {
|
||||||
|
// If we don't have an ID, we have to log this on the blueprint, as the
|
||||||
|
// resource is not going to be saved so the PHID will vanish.
|
||||||
|
$log_target = $this->getBlueprint();
|
||||||
|
}
|
||||||
|
$log_target->logEvent(
|
||||||
DrydockSlotLockFailureLogType::LOGCONST,
|
DrydockSlotLockFailureLogType::LOGCONST,
|
||||||
array(
|
array(
|
||||||
'locks' => $ex->getLockMap(),
|
'locks' => $ex->getLockMap(),
|
||||||
|
@ -166,14 +164,9 @@ final class DrydockResource extends DrydockDAO
|
||||||
throw $ex;
|
throw $ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$this
|
||||||
$this
|
->setStatus($new_status)
|
||||||
->setStatus($new_status)
|
->save();
|
||||||
->save();
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$this->killTransaction();
|
|
||||||
throw $ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->saveTransaction();
|
$this->saveTransaction();
|
||||||
|
|
||||||
|
@ -210,12 +203,24 @@ final class DrydockResource extends DrydockDAO
|
||||||
|
|
||||||
$this->openTransaction();
|
$this->openTransaction();
|
||||||
|
|
||||||
$this
|
try {
|
||||||
->setStatus(DrydockResourceStatus::STATUS_ACTIVE)
|
|
||||||
->save();
|
|
||||||
|
|
||||||
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
|
||||||
$this->slotLocks = array();
|
$this->slotLocks = array();
|
||||||
|
} catch (DrydockSlotLockException $ex) {
|
||||||
|
$this->killTransaction();
|
||||||
|
|
||||||
|
$this->logEvent(
|
||||||
|
DrydockSlotLockFailureLogType::LOGCONST,
|
||||||
|
array(
|
||||||
|
'locks' => $ex->getLockMap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this
|
||||||
|
->setStatus(DrydockResourceStatus::STATUS_ACTIVE)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->saveTransaction();
|
$this->saveTransaction();
|
||||||
|
|
||||||
|
@ -288,6 +293,15 @@ final class DrydockResource extends DrydockDAO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isActive() {
|
||||||
|
switch ($this->getStatus()) {
|
||||||
|
case DrydockResourceStatus::STATUS_ACTIVE:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function logEvent($type, array $data = array()) {
|
public function logEvent($type, array $data = array()) {
|
||||||
$log = id(new DrydockLog())
|
$log = id(new DrydockLog())
|
||||||
->setEpoch(PhabricatorTime::getNow())
|
->setEpoch(PhabricatorTime::getNow())
|
||||||
|
|
|
@ -149,6 +149,7 @@ final class DrydockSlotLock extends DrydockDAO {
|
||||||
// time we should be able to figure out which locks are already held.
|
// time we should be able to figure out which locks are already held.
|
||||||
$held = self::loadHeldLocks($locks);
|
$held = self::loadHeldLocks($locks);
|
||||||
$held = mpull($held, 'getOwnerPHID', 'getLockKey');
|
$held = mpull($held, 'getOwnerPHID', 'getLockKey');
|
||||||
|
|
||||||
throw new DrydockSlotLockException($held);
|
throw new DrydockSlotLockException($held);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -535,7 +535,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker {
|
||||||
// If this lease has been acquired but not activated, queue a task to
|
// If this lease has been acquired but not activated, queue a task to
|
||||||
// activate it.
|
// activate it.
|
||||||
if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) {
|
if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) {
|
||||||
PhabricatorWorker::scheduleTask(
|
$this->queueTask(
|
||||||
__CLASS__,
|
__CLASS__,
|
||||||
array(
|
array(
|
||||||
'leasePHID' => $lease->getPHID(),
|
'leasePHID' => $lease->getPHID(),
|
||||||
|
@ -691,7 +691,14 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker {
|
||||||
->setStatus(DrydockLeaseStatus::STATUS_BROKEN)
|
->setStatus(DrydockLeaseStatus::STATUS_BROKEN)
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
$lease->scheduleUpdate();
|
$this->queueTask(
|
||||||
|
__CLASS__,
|
||||||
|
array(
|
||||||
|
'leasePHID' => $lease->getPHID(),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'objectPHID' => $lease->getPHID(),
|
||||||
|
));
|
||||||
|
|
||||||
$lease->logEvent(
|
$lease->logEvent(
|
||||||
DrydockLeaseActivationFailureLogType::LOGCONST,
|
DrydockLeaseActivationFailureLogType::LOGCONST,
|
||||||
|
|
|
@ -29,7 +29,9 @@ abstract class HarbormasterDrydockLeaseArtifact
|
||||||
}
|
}
|
||||||
|
|
||||||
public function willCreateArtifact(PhabricatorUser $actor) {
|
public function willCreateArtifact(PhabricatorUser $actor) {
|
||||||
$this->loadArtifactLease($actor);
|
// We don't load the lease here because it's expected that artifacts are
|
||||||
|
// created before leases actually exist. This guarantees that the leases
|
||||||
|
// will be cleaned up.
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadArtifactLease(PhabricatorUser $viewer) {
|
public function loadArtifactLease(PhabricatorUser $viewer) {
|
||||||
|
@ -51,7 +53,15 @@ abstract class HarbormasterDrydockLeaseArtifact
|
||||||
}
|
}
|
||||||
|
|
||||||
public function releaseArtifact(PhabricatorUser $actor) {
|
public function releaseArtifact(PhabricatorUser $actor) {
|
||||||
$lease = $this->loadArtifactLease($actor);
|
try {
|
||||||
|
$lease = $this->loadArtifactLease($actor);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
// If we can't load the lease, treat it as already released. Artifacts
|
||||||
|
// are generated before leases are queued, so it's possible to arrive
|
||||||
|
// here under normal conditions.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$lease->canRelease()) {
|
if (!$lease->canRelease()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ final class HarbormasterPlanEditController extends HarbormasterPlanController {
|
||||||
$is_new = (!$plan->getID());
|
$is_new = (!$plan->getID());
|
||||||
if ($is_new) {
|
if ($is_new) {
|
||||||
$title = pht('New Build Plan');
|
$title = pht('New Build Plan');
|
||||||
$cancel_uri = $this->getApplicationURI();
|
$cancel_uri = $this->getApplicationURI('plan/');
|
||||||
$save_button = pht('Create Build Plan');
|
$save_button = pht('Create Build Plan');
|
||||||
} else {
|
} else {
|
||||||
$id = $plan->getID();
|
$id = $plan->getID();
|
||||||
|
|
|
@ -12,7 +12,7 @@ final class HarbormasterDrydockCommandBuildStepImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBuildStepGroupKey() {
|
public function getBuildStepGroupKey() {
|
||||||
return HarbormasterPrototypeBuildStepGroup::GROUPKEY;
|
return HarbormasterDrydockBuildStepGroup::GROUPKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDescription() {
|
public function getDescription() {
|
||||||
|
|
|
@ -12,7 +12,7 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBuildStepGroupKey() {
|
public function getBuildStepGroupKey() {
|
||||||
return HarbormasterPrototypeBuildStepGroup::GROUPKEY;
|
return HarbormasterDrydockBuildStepGroup::GROUPKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(
|
public function execute(
|
||||||
|
@ -41,7 +41,7 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
|
||||||
$working_copy_type = id(new DrydockWorkingCopyBlueprintImplementation())
|
$working_copy_type = id(new DrydockWorkingCopyBlueprintImplementation())
|
||||||
->getType();
|
->getType();
|
||||||
|
|
||||||
$lease = id(new DrydockLease())
|
$lease = DrydockLease::initializeNewLease()
|
||||||
->setResourceType($working_copy_type)
|
->setResourceType($working_copy_type)
|
||||||
->setOwnerPHID($build_target->getPHID());
|
->setOwnerPHID($build_target->getPHID());
|
||||||
|
|
||||||
|
@ -54,6 +54,18 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
|
||||||
$lease->setAwakenTaskIDs(array($task_id));
|
$lease->setAwakenTaskIDs(array($task_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe add a method to mark artifacts like this as pending?
|
||||||
|
|
||||||
|
// Create the artifact now so that the lease is always disposed of, even
|
||||||
|
// if this target is aborted.
|
||||||
|
$build_target->createArtifact(
|
||||||
|
$viewer,
|
||||||
|
$settings['name'],
|
||||||
|
HarbormasterWorkingCopyArtifact::ARTIFACTCONST,
|
||||||
|
array(
|
||||||
|
'drydockLeasePHID' => $lease->getPHID(),
|
||||||
|
));
|
||||||
|
|
||||||
$lease->queueForActivation();
|
$lease->queueForActivation();
|
||||||
|
|
||||||
$build_target
|
$build_target
|
||||||
|
@ -73,14 +85,6 @@ final class HarbormasterLeaseWorkingCopyBuildStepImplementation
|
||||||
'Lease "%s" never activated.',
|
'Lease "%s" never activated.',
|
||||||
$lease->getPHID()));
|
$lease->getPHID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$artifact = $build_target->createArtifact(
|
|
||||||
$viewer,
|
|
||||||
$settings['name'],
|
|
||||||
HarbormasterWorkingCopyArtifact::ARTIFACTCONST,
|
|
||||||
array(
|
|
||||||
'drydockLeasePHID' => $lease->getPHID(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getArtifactOutputs() {
|
public function getArtifactOutputs() {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class HarbormasterDrydockBuildStepGroup
|
||||||
|
extends HarbormasterBuildStepGroup {
|
||||||
|
|
||||||
|
const GROUPKEY = 'harbormaster.drydock';
|
||||||
|
|
||||||
|
public function getGroupName() {
|
||||||
|
return pht('Drydock');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroupOrder() {
|
||||||
|
return 3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEnabled() {
|
||||||
|
$drydock_class = 'PhabricatorDrydockApplication';
|
||||||
|
return PhabricatorApplication::isClassInstalled($drydock_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldShowIfEmpty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -77,10 +77,17 @@ final class PonderAnswerView extends AphrontTagView {
|
||||||
->setIconFont('fa-bars')
|
->setIconFont('fa-bars')
|
||||||
->setDropdownMenu($actions);
|
->setDropdownMenu($actions);
|
||||||
|
|
||||||
|
$header_name = phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $handle->getURI(),
|
||||||
|
),
|
||||||
|
$handle->getName());
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setEpoch($answer->getDateModified())
|
->setEpoch($answer->getDateModified())
|
||||||
->setHeader($handle->getName())
|
->setHeader($header_name)
|
||||||
->addActionLink($action_button)
|
->addActionLink($action_button)
|
||||||
->setImage($handle->getImageURI())
|
->setImage($handle->getImageURI())
|
||||||
->setImageURL($handle->getURI());
|
->setImageURL($handle->getURI());
|
||||||
|
|
|
@ -160,8 +160,7 @@ abstract class PhabricatorWorker extends Phobject {
|
||||||
try {
|
try {
|
||||||
$worker->doWork();
|
$worker->doWork();
|
||||||
foreach ($worker->getQueuedTasks() as $queued_task) {
|
foreach ($worker->getQueuedTasks() as $queued_task) {
|
||||||
list($queued_class, $queued_data, $queued_priority) = $queued_task;
|
list($queued_class, $queued_data, $queued_options) = $queued_task;
|
||||||
$queued_options = array('priority' => $queued_priority);
|
|
||||||
self::scheduleTask($queued_class, $queued_data, $queued_options);
|
self::scheduleTask($queued_class, $queued_data, $queued_options);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -220,11 +219,14 @@ abstract class PhabricatorWorker extends Phobject {
|
||||||
*
|
*
|
||||||
* @param string Task class to queue.
|
* @param string Task class to queue.
|
||||||
* @param array Data for the followup task.
|
* @param array Data for the followup task.
|
||||||
* @param int|null Priority for the followup task.
|
* @param array Options for the followup task.
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
final protected function queueTask($class, array $data, $priority = null) {
|
final protected function queueTask(
|
||||||
$this->queuedTasks[] = array($class, $data, $priority);
|
$class,
|
||||||
|
array $data,
|
||||||
|
array $options = array()) {
|
||||||
|
$this->queuedTasks[] = array($class, $data, $options);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,8 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
$task->getDataID());
|
$task->getDataID());
|
||||||
$task->setData($task_data->getData());
|
$task->setData($task_data->getData());
|
||||||
|
|
||||||
$console->writeOut(
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
pht(
|
pht(
|
||||||
'Executing task %d (%s)...',
|
'Executing task %d (%s)...',
|
||||||
$task->getID(),
|
$task->getID(),
|
||||||
|
|
|
@ -217,13 +217,14 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask {
|
||||||
// so execute it out here and just let the exception escape.
|
// so execute it out here and just let the exception escape.
|
||||||
if ($did_succeed) {
|
if ($did_succeed) {
|
||||||
foreach ($worker->getQueuedTasks() as $task) {
|
foreach ($worker->getQueuedTasks() as $task) {
|
||||||
list($class, $data) = $task;
|
list($class, $data, $options) = $task;
|
||||||
PhabricatorWorker::scheduleTask(
|
|
||||||
$class,
|
// Default the new task priority to our own priority.
|
||||||
$data,
|
$options = $options + array(
|
||||||
array(
|
'priority' => (int)$this->getPriority(),
|
||||||
'priority' => (int)$this->getPriority(),
|
);
|
||||||
));
|
|
||||||
|
PhabricatorWorker::scheduleTask($class, $data, $options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1398,6 +1398,11 @@ final class PhabricatorUSEnglishTranslation
|
||||||
'Setting retention policy for "%s" to %s days.',
|
'Setting retention policy for "%s" to %s days.',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'Waiting %s second(s) for lease to activate.' => array(
|
||||||
|
'Waiting a second for lease to activate.',
|
||||||
|
'Waiting %s seconds for lease to activate.',
|
||||||
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,16 @@ final class PhabricatorRemarkupCowsayBlockInterpreter
|
||||||
->setText($content)
|
->setText($content)
|
||||||
->renderCow();
|
->renderCow();
|
||||||
|
|
||||||
if ($this->getEngine()->isTextMode()) {
|
$engine = $this->getEngine();
|
||||||
|
|
||||||
|
if ($engine->isTextMode()) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($engine->isHTMLMailMode()) {
|
||||||
|
return phutil_tag('pre', array(), $result);
|
||||||
|
}
|
||||||
|
|
||||||
return phutil_tag(
|
return phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -27,10 +27,16 @@ final class PhabricatorRemarkupFigletBlockInterpreter
|
||||||
|
|
||||||
$result = $figlet->lineEcho($content);
|
$result = $figlet->lineEcho($content);
|
||||||
|
|
||||||
if ($this->getEngine()->isTextMode()) {
|
$engine = $this->getEngine();
|
||||||
|
|
||||||
|
if ($engine->isTextMode()) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($engine->isHTMLMailMode()) {
|
||||||
|
return phutil_tag('pre', array(), $result);
|
||||||
|
}
|
||||||
|
|
||||||
return phutil_tag(
|
return phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -60,9 +60,9 @@ final class PhabricatorStorageManagementProbeWorkflow
|
||||||
$overall);
|
$overall);
|
||||||
|
|
||||||
$table->addRow(array(
|
$table->addRow(array(
|
||||||
'name' => phutil_console_format('**%s**', $db),
|
'name' => tsprintf('**%s**', $db),
|
||||||
'size' => phutil_console_format('**%s**', $database_size),
|
'size' => tsprintf('**%s**', $database_size),
|
||||||
'percentage' => phutil_console_format('**%s**', $database_percentage),
|
'percentage' => tsprintf('**%s**', $database_percentage),
|
||||||
));
|
));
|
||||||
$data[$db] = isort($data[$db], '_totalSize');
|
$data[$db] = isort($data[$db], '_totalSize');
|
||||||
foreach ($data[$db] as $table_name => $info) {
|
foreach ($data[$db] as $table_name => $info) {
|
||||||
|
@ -82,9 +82,9 @@ final class PhabricatorStorageManagementProbeWorkflow
|
||||||
$overall,
|
$overall,
|
||||||
$overall);
|
$overall);
|
||||||
$table->addRow(array(
|
$table->addRow(array(
|
||||||
'name' => phutil_console_format('**%s**', pht('TOTAL')),
|
'name' => tsprintf('**%s**', pht('TOTAL')),
|
||||||
'size' => phutil_console_format('**%s**', $overall_size),
|
'size' => tsprintf('**%s**', $overall_size),
|
||||||
'percentage' => phutil_console_format('**%s**', $overall_percentage),
|
'percentage' => tsprintf('**%s**', $overall_percentage),
|
||||||
));
|
));
|
||||||
|
|
||||||
$table->draw();
|
$table->draw();
|
||||||
|
|
|
@ -16,7 +16,16 @@ JX.behavior('diffusion-pull-lastmodified', function(config) {
|
||||||
if (!config.map[k][l]) {
|
if (!config.map[k][l]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
JX.DOM.setContent(JX.$(config.map[k][l]), JX.$H(r[k][l]));
|
try {
|
||||||
|
JX.DOM.setContent(JX.$(config.map[k][l]), JX.$H(r[k][l]));
|
||||||
|
} catch (ex) {
|
||||||
|
// The way this works is weird and sometimes the components get
|
||||||
|
// out of sync. Fail gently until we can eventually improve the
|
||||||
|
// underlying mechanism.
|
||||||
|
|
||||||
|
// In particular, we currently may generate lint information
|
||||||
|
// without generating a lint column. See T9524.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue