499 lines
12 KiB
PHP
499 lines
12 KiB
PHP
#!/usr/local/bin/php
|
|
<?php
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Bootstrap.
|
|
|
|
$PHABRICATOR_ROOT = dirname(dirname(dirname(__FILE__)));
|
|
require_once $PHABRICATOR_ROOT.'/scripts/__init_script__.php';
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Command line.
|
|
|
|
if (count($argv) != 3) {
|
|
print("Usage: $argv[0] <conduit_token_file> <output_directory>\n");
|
|
die;
|
|
}
|
|
|
|
$CONDUIT_TOKEN_FILE = $argv[1];
|
|
$OUTPUT_DIR = $argv[2];
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Global configuration.
|
|
|
|
$HOST = 'phabricator.local';
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Utilities.
|
|
|
|
// Create an instance of the AphrontApplicationConfiguration configured with all
|
|
// invariant settings (settings which do not change between handlers of different
|
|
// revisions).
|
|
function CreateApplicationConfiguration() {
|
|
global $HOST;
|
|
|
|
$application_configuration = new AphrontApplicationConfiguration();
|
|
$application_configuration->setHost($HOST);
|
|
return $application_configuration;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Global state.
|
|
|
|
$site = id(new PhabricatorPlatformSite());
|
|
|
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
|
|
|
$application_configuration = CreateApplicationConfiguration();
|
|
|
|
$differential_application = id(new PhabricatorDifferentialApplication());
|
|
$revision_controller = id(new DifferentialRevisionViewController())
|
|
->setCurrentApplication($differential_application);
|
|
|
|
$maniphest_application = id(new PhabricatorManiphestApplication());
|
|
$maniphest_controller = id(new ManiphestTaskDetailController())
|
|
->setCurrentApplication($maniphest_application);
|
|
|
|
$differential_routing_map = id(new AphrontRoutingMap())
|
|
->setSite($site)
|
|
->setApplication($differential_application)
|
|
->setRoutes($differential_application->getRoutes());
|
|
|
|
$maniphest_routing_map = id(new AphrontRoutingMap())
|
|
->setSite($site)
|
|
->setApplication($maniphest_application)
|
|
->setRoutes($maniphest_application->getRoutes());
|
|
|
|
$conduit = id(new ConduitClient('https://developer.blender.org/api/'))
|
|
->setConduitToken(trim(file_get_contents($CONDUIT_TOKEN_FILE)));
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Baking utilities.
|
|
|
|
function EnsureDirectoryOrDie($dir) {
|
|
if (!is_dir($dir)) {
|
|
if (!mkdir($dir, 0777, true)) {
|
|
print("Error creating output durectory $dir\n");
|
|
die;
|
|
}
|
|
}
|
|
}
|
|
|
|
function RenderElement($element) {
|
|
if (is_array($element)) {
|
|
$html = '';
|
|
foreach ($element as $child) {
|
|
$html .= RenderElement($child);
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
return $element->render();
|
|
}
|
|
|
|
function RenderResponseToHTML($response) {
|
|
$html = '';
|
|
|
|
$crumbs = $response->getCrumbs();
|
|
if ($crumbs) {
|
|
$html .= $crumbs->render();
|
|
}
|
|
|
|
foreach ($response->renderChildren() as $child) {
|
|
$html .= RenderElement($child);
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Revision baking.
|
|
|
|
class RevisionInfo {
|
|
public $title = '';
|
|
public $page_title = '';
|
|
|
|
public $last_diff_id = 0;
|
|
|
|
public $hash = '';
|
|
|
|
public static function ReadFromFile($file_name) {
|
|
$info = new RevisionInfo();
|
|
|
|
if (!file_exists($file_name)) {
|
|
return $info;
|
|
}
|
|
|
|
$json = json_decode(file_get_contents($file_name));
|
|
foreach ($json as $key => $value) {
|
|
$info->{$key} = $value;
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
public function SaveToFile($file_name) {
|
|
$json_str = json_encode(get_object_vars($this));
|
|
file_put_contents($file_name, $json_str);
|
|
}
|
|
};
|
|
|
|
function GetRevisionOutputDirectory($revision) {
|
|
global $OUTPUT_DIR;
|
|
|
|
$id = $revision->getID();
|
|
|
|
return PathJoin(array($OUTPUT_DIR, 'differential', SubpathFromId($id)));
|
|
}
|
|
|
|
function EnsureRevisionOutputDirectory($revision) {
|
|
$dir = GetRevisionOutputDirectory($revision);
|
|
|
|
EnsureDirectoryOrDie($dir);
|
|
EnsureDirectoryOrDie(PathJoin(array($dir, 'raw_diff')));
|
|
|
|
return $dir;
|
|
}
|
|
|
|
function GetRevisionInfoFilename($revision) {
|
|
$revision_dir = GetRevisionOutputDirectory($revision);
|
|
return PathJoin(array($revision_dir, 'info.json'));
|
|
}
|
|
|
|
function GetRevisionHTMLFilename($revision, $diff_id) {
|
|
$revision_id = $revision->getID();
|
|
$revision_dir = GetRevisionOutputDirectory($revision);
|
|
|
|
$file_name = "D{$revision_id}.id{$diff_id}.html";
|
|
|
|
return PathJoin(array($revision_dir, $file_name));
|
|
}
|
|
|
|
function GetRevisionRawDiffFilename($revision, $diff_id) {
|
|
$revision_id = $revision->getID();
|
|
$revision_dir = GetRevisionOutputDirectory($revision);
|
|
|
|
$file_name = "D{$revision_id}.id{$diff_id}.diff";
|
|
|
|
return PathJoin(array($revision_dir, 'raw_diff', $file_name));
|
|
}
|
|
|
|
function RenderDifferentialToResponse($revision, $diff_id) {
|
|
global $application_configuration;
|
|
global $revision_controller;
|
|
global $differential_routing_map;
|
|
global $viewer;
|
|
|
|
$revision_id = $revision->getID();
|
|
|
|
$path = '/D' . $revision_id;
|
|
|
|
$route_result = $differential_routing_map->routePath($path);
|
|
$uri_data = $route_result->getURIData();
|
|
|
|
$application_configuration->setPath($path);
|
|
|
|
$request_data = array(
|
|
'id' => $diff_id,
|
|
);
|
|
|
|
$request = $application_configuration->buildRequest()
|
|
->setUser($viewer)
|
|
->setRequestData($request_data)
|
|
->setURIMap($uri_data);
|
|
|
|
$response = id($revision_controller)
|
|
->setRequest($request)
|
|
->handleRequest($request);
|
|
|
|
return $response;
|
|
}
|
|
|
|
function StoreRawDiff($revision, $diff_id) {
|
|
global $viewer;
|
|
global $conduit;
|
|
|
|
$diff = id(new DifferentialDiffQuery())
|
|
->setViewer($viewer)
|
|
->withIDs(array($diff_id))
|
|
->needChangesets(true)
|
|
->executeOne();
|
|
|
|
$raw_changes = $diff->buildChangesList();
|
|
|
|
$changes = array();
|
|
foreach ($raw_changes as $changedict) {
|
|
$changes[] = ArcanistDiffChange::newFromDictionary($changedict);
|
|
}
|
|
|
|
$bundle = ArcanistBundle::newFromChanges($changes);
|
|
$bundle->setConduit($conduit);
|
|
$raw_diff = $bundle->toGitPatch();
|
|
|
|
$output_file = GetRevisionRawDiffFilename($revision, $diff_id);
|
|
file_put_contents($output_file, $raw_diff);
|
|
}
|
|
|
|
function GetRevisionHash($revision) {
|
|
global $viewer;
|
|
|
|
$xaction = id(new DifferentialTransactionQuery())
|
|
->setViewer($viewer)
|
|
->withObjectPHIDs(array($revision->getPHID()))
|
|
->setOrder('newest')
|
|
->setLimit(1)
|
|
->executeOne();
|
|
|
|
$digest = '';
|
|
|
|
$digest .= $revision->getDateModified();
|
|
|
|
if ($xaction) {
|
|
$digest .= '_' . $xaction->getPHID();
|
|
$digest .= '_' . $xaction->getDateModified();
|
|
}
|
|
|
|
return $digest;
|
|
}
|
|
|
|
function NeedBakeRevision($info, $revision, $diff_id) {
|
|
if (!file_exists(GetRevisionHTMLFilename($revision, $diff_id))) {
|
|
return true;
|
|
}
|
|
if (!file_exists(GetRevisionRawDiffFilename($revision, $diff_id))) {
|
|
return true;
|
|
}
|
|
|
|
if ($info->last_diff_id < $diff_id) {
|
|
return true;
|
|
}
|
|
|
|
if ($revision->getTitle() != $info->title) {
|
|
return true;
|
|
}
|
|
|
|
if (GetRevisionHash($revision) != $info->hash) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function BakeRevision($revision, $diff_id) {
|
|
$revision_id = $revision->getID();
|
|
|
|
printf('Baking D' . $revision_id . '?id=' . $diff_id . ' ...' . "\n");
|
|
|
|
$info_file_name = GetRevisionInfoFilename($revision);
|
|
|
|
$info = RevisionInfo::ReadFromFile($info_file_name);
|
|
|
|
if (!NeedBakeRevision($info, $revision, $diff_id)) {
|
|
print(' ... ignoring: up to date' . "\n");
|
|
return;
|
|
}
|
|
|
|
$response = RenderDifferentialToResponse($revision, $diff_id);
|
|
|
|
$html = RenderResponseToHTML($response);
|
|
$html_file_name = GetRevisionHTMLFIlename($revision, $diff_id);
|
|
file_put_contents($html_file_name, $html);
|
|
|
|
StoreRawDiff($revision, $diff_id);
|
|
|
|
$info->title = $revision->getTitle();
|
|
$info->page_title = $response->getTitle();
|
|
$info->hash = GetRevisionHash($revision);
|
|
$info->last_diff_id = max($info->last_diff_id, $diff_id);
|
|
|
|
$info->SaveToFile($info_file_name);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Task baking.
|
|
|
|
class TaskInfo {
|
|
public $title = '';
|
|
public $page_title = '';
|
|
|
|
public $hash = '';
|
|
|
|
public static function ReadFromFile($file_name) {
|
|
$info = new TaskInfo();
|
|
|
|
if (!file_exists($file_name)) {
|
|
return $info;
|
|
}
|
|
|
|
$json = json_decode(file_get_contents($file_name));
|
|
foreach ($json as $key => $value) {
|
|
$info->{$key} = $value;
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
public function SaveToFile($file_name) {
|
|
$json_str = json_encode(get_object_vars($this));
|
|
file_put_contents($file_name, $json_str);
|
|
}
|
|
};
|
|
|
|
function GetTaskOutputDirectory($task) {
|
|
global $OUTPUT_DIR;
|
|
|
|
$id = $task->getID();
|
|
|
|
return PathJoin(array($OUTPUT_DIR, 'maniphest', SubpathFromId($id)));
|
|
}
|
|
|
|
function EnsureTaskOutputDirectory($task) {
|
|
$dir = GetTaskOutputDirectory($task);
|
|
|
|
EnsureDirectoryOrDie($dir);
|
|
|
|
return $dir;
|
|
}
|
|
|
|
function GetTaskInfoFilename($task) {
|
|
$task_dir = GetTaskOutputDirectory($task);
|
|
return PathJoin(array($task_dir, 'info.json'));
|
|
}
|
|
|
|
function GetTaskHTMLFilename($task) {
|
|
$task_id = $task->getID();
|
|
$task_dir = GetTaskOutputDirectory($task);
|
|
|
|
$file_name = "index.html";
|
|
|
|
return PathJoin(array($task_dir, $file_name));
|
|
}
|
|
|
|
function RenderTaskToResponse($task) {
|
|
global $application_configuration;
|
|
global $maniphest_routing_map;
|
|
global $viewer;
|
|
global $maniphest_controller;
|
|
|
|
$task_id = $task->getID();
|
|
|
|
$path = '/T' . $task_id;
|
|
|
|
$route_result = $maniphest_routing_map->routePath($path);
|
|
$uri_data = $route_result->getURIData();
|
|
|
|
$application_configuration->setPath($path);
|
|
|
|
$request_data = array(
|
|
);
|
|
|
|
$request = $application_configuration->buildRequest()
|
|
->setUser($viewer)
|
|
->setRequestData($request_data)
|
|
->setURIMap($uri_data);
|
|
|
|
$response = id($maniphest_controller)
|
|
->setRequest($request)
|
|
->handleRequest($request);
|
|
|
|
return $response;
|
|
}
|
|
|
|
function GetTaskHash($task) {
|
|
global $viewer;
|
|
|
|
$xaction = id(new DifferentialTransactionQuery())
|
|
->setViewer($viewer)
|
|
->withObjectPHIDs(array($task->getPHID()))
|
|
->setOrder('newest')
|
|
->setLimit(1)
|
|
->executeOne();
|
|
|
|
$digest = '';
|
|
|
|
$digest .= $task->getDateModified();
|
|
|
|
if ($xaction) {
|
|
$digest .= '_' . $xaction->getPHID();
|
|
$digest .= '_' . $xaction->getDateModified();
|
|
}
|
|
|
|
return $digest;
|
|
}
|
|
|
|
function NeedBakeTask($info, $task) {
|
|
if (!file_exists(GetTaskHTMLFilename($task))) {
|
|
return true;
|
|
}
|
|
|
|
if ($task->getTitle() != $info->title) {
|
|
return true;
|
|
}
|
|
|
|
if (GetTaskHash($task) != $info->hash) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function BakeTask($task) {
|
|
$task_id = $task->getID();
|
|
|
|
printf('Baking T' . $task_id . ' ...' . "\n");
|
|
|
|
$info_file_name = GetTaskInfoFilename($task);
|
|
|
|
$info = TaskInfo::ReadFromFile($info_file_name);
|
|
|
|
if (!NeedBakeTask($info, $task)) {
|
|
print(' ... ignoring: up to date' . "\n");
|
|
return;
|
|
}
|
|
|
|
$response = RenderTaskToResponse($task);
|
|
|
|
$html = RenderResponseToHTML($response);
|
|
|
|
$html_file_name = GetTaskHTMLFIlename($task);
|
|
file_put_contents($html_file_name, $html);
|
|
|
|
$info->title = $task->getTitle();
|
|
$info->page_title = $response->getTitle();
|
|
$info->hash = GetTaskHash($task);
|
|
|
|
$info->SaveToFile($info_file_name);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Baking main loop.
|
|
|
|
print('Querying differential revisions from the database ... ' . "\n");
|
|
$revisions = id(new DifferentialRevisionQuery())
|
|
->setViewer($viewer)
|
|
->needDiffIDs(true)
|
|
->setOrder('oldest')
|
|
->execute();
|
|
|
|
foreach ($revisions as $revision_id => $revision) {
|
|
EnsureRevisionOutputDirectory($revision);
|
|
|
|
foreach ($revision->getDiffIDs() as $diff_id) {
|
|
BakeRevision($revision, $diff_id);
|
|
}
|
|
}
|
|
|
|
print("\n");
|
|
print('Querying maniphest tasks from the database ... ' . "\n");
|
|
$tasks = id(new ManiphestTaskQuery())
|
|
->setViewer($viewer)
|
|
->setOrder('oldest')
|
|
->execute();
|
|
|
|
foreach ($tasks as $task_id => $task) {
|
|
EnsureTaskOutputDirectory($task);
|
|
BakeTask($task);
|
|
}
|
|
|
|
?>
|