Skip to content

Instantly share code, notes, and snippets.

@kdambekalns
Created February 12, 2014 11:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kdambekalns/8954133 to your computer and use it in GitHub Desktop.
Save kdambekalns/8954133 to your computer and use it in GitHub Desktop.
Fetch changes from Gerrit automatically (from review.typo3.org)

How to use

This script reads changes from the file gerrit.json. The description is just for the humans working with the file, the important bits are the project name and the numeric change id. If the change id is amended with a command and number that means "fetch that specific patch set" instread of the latest one.

If you have gerrit.json ready just run php gerrit_update.php and watch the magic. :)

{
"TYPO3.Flow": {
"[FEATURE] Provide request / response in exception dump": "25373",
"[TASK] Improve security checks and related logging": "25462",
"[FEATURE] Allow to cache Doctrine ORM query results": "25308"
},
"TYPO3.TYPO3CR": {
"[FEATURE] Provide API for search engine": "24925,15",
"[WIP][FEATURE] first-level cache in NodeDataRepository": "25402"
},
"TYPO3.Eel": {
"[BUGFIX] Prevent race conditions in Eel Expression Cache": "26454",
"[FEATURE] shuffle() operation": "25227,3"
},
"TYPO3.TypoScript": {
"[WIP][TASK] Cache results of tsValue() calls": "25225"
},
"TYPO3.Neos": {
"[WIP][FEATURE] adjustments to implementing a cache in NodeDataRepository": "25403"
}
}
#!/usr/bin/php
<?php
error_reporting(E_ALL ^ E_STRICT);
ini_set('display_errors', 1);
define('FLOW_PATH_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('FLOW_PATH_PACKAGES', FLOW_PATH_ROOT . 'Packages' . DIRECTORY_SEPARATOR);
class Gerrit {
/**
* @var array
*/
static protected $colors = array(
'green' => '0;32',
'red' => '0;31',
'yellow' => '0;33'
);
/**
* This command checks for a gerrit.json in the current dir and fetches patches from gerrit
*
* This command will cherry-pick all reviews specified in gerrit.json
*
* @return void
*/
static public function updateCommand() {
$gerritFile = FLOW_PATH_ROOT . 'gerrit.json';
$typeDirs = scandir(FLOW_PATH_PACKAGES);
$packagePaths = array('BuildEssentials' => 'Build/BuildEssentials');
foreach ($typeDirs as $typeDir) {
if (is_dir(FLOW_PATH_PACKAGES . $typeDir) && substr($typeDir, 0, 1) !== '.') {
$typeDir = FLOW_PATH_PACKAGES . $typeDir . '/';
$packageDirs = scandir($typeDir);
foreach ($packageDirs as $packageDir) {
if (is_dir($typeDir . $packageDir) && substr($packageDir, 0, 1) !== '.') {
$packagePaths[$packageDir] = $typeDir . $packageDir;
}
}
}
}
if (file_exists($gerritFile)) {
$packages = json_decode(@file_get_contents($gerritFile));
if (!is_object($packages)) {
echo self::colorize('Could not load gerrit.json! Check for Syntax errors', 'red');
return;
}
foreach (get_object_vars($packages) as $package => $changes) {
if (!isset($packagePaths[$package])) {
echo self::colorize('The Package ' . $package . ' is not installed', 'red') . PHP_EOL;
continue;
}
chdir($packagePaths[$package]);
$changes = get_object_vars($changes);
$gitLogOutput = self::executeShellCommand('git log -n30');
foreach ($changes as $description => $changeId) {
$patchSet = NULL;
if (strpos($changeId, ',') !== FALSE) {
list($changeId, $patchSet) = explode(',', $changeId);
}
$change = self::fetchChangeInformation($changeId);
if ($change === FALSE) {
echo self::colorize(sprintf('Could not fetch change information (%u)!', $changeId), 'red') . PHP_EOL;
continue;
}
$header = $package . ': ' . $change->subject;
echo self::colorize($header, 'green') . PHP_EOL;
if ($change->status == 'MERGED') {
echo self::colorize('This change has been merged upstream.', 'yellow') . PHP_EOL;
} elseif ($change->status == 'ABANDONED') {
echo self::colorize('This change has been abandoned!', 'red') . PHP_EOL;
}
$ref = $change->revisions->{$change->current_revision}->fetch->git->ref;
if ($patchSet !== NULL) {
$explodedRef = explode('/', $ref);
array_pop($explodedRef);
$explodedRef[] = $patchSet;
$ref = implode('/', $explodedRef);
}
$command = 'git fetch --quiet git://review.typo3.org/' . $change->project . ' ' . $ref . '';
$output = self::executeShellCommand($command);
$changeIdLine = self::executeShellCommand('git show FETCH_HEAD | grep \'Change-Id\'');
if (self::isAlreadyPicked($changeIdLine, $gitLogOutput)) {
echo self::colorize('Already picked', 'yellow') . PHP_EOL;
} else {
echo $output;
system('git cherry-pick -x --strategy=recursive -X theirs FETCH_HEAD');
}
echo PHP_EOL;
}
chdir(FLOW_PATH_ROOT);
}
}
}
/**
* Checks if the change is already picked by looking for the Change-Id
* line in the log.
*
* @param string $changeIdLine
* @param string $gitLogOutput
* @return boolean
*/
static protected function isAlreadyPicked($changeIdLine, $gitLogOutput) {
return stristr($gitLogOutput, trim($changeIdLine)) !== FALSE;
}
/**
* @param string $command
* @return string
*/
static protected function executeShellCommand($command) {
$output = '';
$fp = popen($command, 'r');
while (($line = fgets($fp)) !== FALSE) {
$output .= $line;
}
pclose($fp);
return trim($output);
}
/**
* @param string $text
* @param string $color Allowed values: green, red, yellow
* @return string
*/
static protected function colorize($text, $color) {
return sprintf("\033[%sm%s\033[0m", self::$colors[$color], $text);
}
/**
* @param integer $changeId The numeric change id, not the hash
* @return mixed
*/
static protected function fetchChangeInformation($changeId) {
$output = @file_get_contents('https://review.typo3.org/changes/?q=' . intval($changeId) . '&o=CURRENT_REVISION');
if ($output === FALSE) {
return FALSE;
}
// Remove first line
$output = substr($output, strpos($output, "\n") + 1);
// trim []
$output = ltrim($output, '[');
$output = rtrim(rtrim($output), ']');
$data = json_decode($output);
return $data;
}
}
Gerrit::updateCommand();
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment