Files
phabricator/scripts/bake/bake.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);
}
?>