Initial commit of differential baking changes

NOTE: This branch is not to be deployed as a website, but instead
to be used as a client of a cluster (to have access to the database)
and run the baking script in a unattended mode.

A lot of tweaks all over the place to make the generated HTML suitable
for use in a static HTML with remapped paths to be relative and so on.

NOTE: Because of those tweaks the file rendering and access will not
work as expected when viewed from the website: it will be 404 because
it has different means accessing files than the static HTML. It is not
possible to keep website rendering correct, but it makes it harder to
investigate what parts needs attention without running the baker.

The baking script is found in `scripts/bake/bake_differential.php`
and it receives a single argument which is a directory with the baked
files.

Covers the following aspects:
- File attachments
- Image previews
- Pastes

The user avatars are replaced with the default user avatar.
This commit is contained in:
2023-01-17 10:19:43 +01:00
parent 436e69f1d0
commit 8a882018b9
27 changed files with 625 additions and 99 deletions

View File

@@ -0,0 +1,293 @@
#!/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) != 2) {
print("Usage: $argv[0] <output_directory>\n");
die;
}
$OUTPUT_DIR = $argv[1];
/////////////////////////////////////////////////////////////////////////////////
// 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();
$application = id(new PhabricatorDifferentialApplication());
$revision_controller = id(new DifferentialRevisionViewController())
->setCurrentApplication($application);
$routing_map = id(new AphrontRoutingMap())
->setSite($site)
->setApplication($application)
->setRoutes($application->getRoutes());
/////////////////////////////////////////////////////////////////////////////////
// Baking logic and loop.
function EnsureDirectoryOrDie($dir) {
if (!is_dir($dir)) {
if (!mkdir($dir, 0777, true)) {
print("Error creating output durectory $dir\n");
die;
}
}
}
function EnsureOutputDirectoryOrDie() {
global $OUTPUT_DIR;
EnsureDirectoryOrDie($OUTPUT_DIR);
}
function MakeGenericOutputDirectory($subject) {
global $OUTPUT_DIR;
$dir = $OUTPUT_DIR . DIRECTORY_SEPARATOR . $subject;
EnsureDirectoryOrDie($dir);
return $dir;
}
function MakeRevisionBaseOutputFileName($revision, $diff_id, $subject) {
$dir = MakeGenericOutputDirectory($subject);
$file_name = $dir . DIRECTORY_SEPARATOR . 'D' . $revision->getID();
if (!is_null($diff_id)) {
$file_name .= '.' . $diff_id;
}
return $file_name;
}
function MakeRevisionHTMLOutputFileName($revision, $diff_id) {
return MakeRevisionBaseOutputFileName(
$revision, $diff_id, 'revision') . '.html';
}
function MakeRawDiffOutputFileName($revision, $diff_id) {
return MakeRevisionBaseOutputFileName(
$revision, $diff_id, 'raw_diff') . '.diff';
}
function MakePasteOutputFileNameFromHandle($handle) {
return MakeGenericOutputDirectory('paste') .
DIRECTORY_SEPARATOR .
$handle->getName() . '.txt';
}
function MakeFileOutputFileNameFromFile($file) {
$dir = MakeGenericOutputDirectory('file');
$id = $file->getID();
EnsureDirectoryOrDie($dir . DIRECTORY_SEPARATOR . 'F' . $id);
return $dir.
DIRECTORY_SEPARATOR .
$file->getSafeFileNameForBake();
}
function BakeDifferentialToResponse($revision, $diff_id = NULL) {
global $application_configuration;
global $revision_controller;
global $routing_map;
global $viewer;
$revision_id = $revision->getID();
if (is_null($diff_id)) {
printf('Baking D' . $revision_id . ' ...' . "\n");
} else {
printf('Baking D' . $revision_id . ', diff ' . $diff_id . ' ...' . "\n");
}
$path = '/D' . $revision_id;
$route_result = $routing_map->routePath($path);
$uri_data = $route_result->getURIData();
$application_configuration->setPath($path);
$request_data = array(
);
if (!is_null($diff_id)) {
$request_data['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 BakeResponseToHTML($response) {
$html = '';
foreach ($response->renderChildren() as $child) {
$html .= $child->render();
}
return $html;
}
function StorePasteHandle($handle) {
global $viewer;
$file_name = MakePasteOutputFileNameFromHandle($handle);
if (file_exists($file_name)) {
return;
}
$paste = id(new PhabricatorPasteQuery())
->setViewer($viewer)
->withPHIDs(array($handle->getPHID()))
->needRawContent(true)
->executeOne();
file_put_contents($file_name, $paste->getRawContent());
}
function StoreFile($file) {
$file_name = MakeFileOutputFileNameFromFile($file);
file_put_contents($file_name, $file->loadFileData());
}
function StoreFileHandle($handle) {
global $viewer;
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($handle->getPHID()))
->needTransforms(
array(
PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW,
))
->executeOne();
$file_name = MakeFileOutputFileNameFromFile($file);
if (file_exists($file_name)) {
return;
}
StoreFile($file);
$preview_xform = $file->getTransform(PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW);
if ($preview_xform) {
StoreFile($preview_xform);
}
}
function StoreResponseAttachments($response) {
$user = $response->getRequest()->getUser();
$handlePool = $user->getHandlePool();
$handles = $handlePool->getHandles();
foreach ($handles as $phid => $handle) {
$type = $handle->getType();
if ($type == PhabricatorPastePastePHIDType::TYPECONST) {
StorePasteHandle($handle);
} else if ($type == PhabricatorFileFilePHIDType::TYPECONST) {
StoreFileHandle($handle);
} else {
// print("$type\n");
}
}
}
function StoreRawDiff($revision, $diff_id) {
if (is_null($diff_id)) {
return;
}
global $viewer;
$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);
$raw_diff = $bundle->toGitPatch();
$output_file = MakeRawDiffOutputFileName($revision, $diff_id);
file_put_contents($output_file, $raw_diff);
}
function BakeDifferentialToFile($revision, $diff_id = NULL) {
$response = BakeDifferentialToResponse($revision, $diff_id);
$html = BakeResponseToHTML($response);
$output_file = MakeRevisionHTMLOutputFileName($revision, $diff_id);
file_put_contents($output_file, $html);
file_put_contents($output_file . '.title', $response->getTitle());
StoreResponseAttachments($response);
StoreRawDiff($revision, $diff_id);
}
EnsureOutputDirectoryOrDie();
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->needDiffIDs(true)
->execute();
foreach ($revisions as $revision_id => $revision) {
BakeDifferentialToFile($revision);
foreach ($revision->getDiffIDs() as $diff_id) {
BakeDifferentialToFile($revision, $diff_id);
}
}
?>