This command class could be used in build.xml to compare the local "inanzzz/*" dependencies to the remote "inanzzz/*" dependencies to see if the local "master" branch copies are behind remote "master" branch. An output is generated as a report.


Command


namespace Inanzzz\ApiBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class BuildCommand extends ContainerAwareCommand
{
const INANZZZ_PREFIX = 'inanzzz/';

private $composerJson;
private $composerLock;

protected function configure()
{
$this
->setName('compare:inanzzz-dependencies')
->setDescription('Checks if local "inanzzz/*" dependencies are behind the latest remote master or not.');
}

protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->composerJson = $this->getContainer()->getParameter('kernel.root_dir').'/../composer.json';
$this->composerLock = $this->getContainer()->getParameter('kernel.root_dir').'/../composer.lock';
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$composerJsonDependencies = $this->getComposerJsonDependencies();
if (empty($composerJsonDependencies)) {
$output->writeln('<bg=yellow>There is no local inanzzz dependency in composer.json</>');
exit;
}

$composerLockDependencies = $this->getComposerLockDependencies($composerJsonDependencies);
if (empty($composerLockDependencies)) {
$output->writeln('<bg=yellow>There is no local inanzzz dependency in composer.lock</>');
exit;
}

$latestRemoteMasterBranchCommitHash = $this->getLatestRemoteMasterBranchCommitHash($output, $composerJsonDependencies);
if (empty($latestRemoteMasterBranchCommitHash)) {
$output->writeln('<bg=yellow>Failed to fetch remote dependency information</>');
exit;
}

$comparison = $this->compare($composerLockDependencies, $latestRemoteMasterBranchCommitHash);

$this->printReport($output, $comparison);
}

/**
* @return array
*/
private function getComposerJsonDependencies()
{
$dependencies = [];
$content = json_decode(file_get_contents($this->composerJson), true);

if (!empty($content) && isset($content['require'])) {
foreach ($content['require'] as $key => $value) {
if (strpos($key, self::INANZZZ_PREFIX) !== false) {
$dependencies[$key] = 'git@github.com:'.ucfirst($key).'.git';
}
}
}

return $dependencies;
}

/**
* @param array $composerJsonDependencies
*
* @return array
*/
private function getComposerLockDependencies(array $composerJsonDependencies)
{
$dependencies = [];
$content = json_decode(file_get_contents($this->composerLock), true);

if (!empty($content) && isset($content['packages'])) {
foreach ($content['packages'] as $key => $value) {
if (
strpos($content['packages'][$key]['name'], self::INANZZZ_PREFIX) !== false
&&
array_key_exists($content['packages'][$key]['name'], $composerJsonDependencies)
) {
$dependencies[$content['packages'][$key]['name']] = $content['packages'][$key]['source']['reference'];
}
}
}

return $dependencies;
}

/**
* @param OutputInterface $output
* @param array $composerJsonDependencies
*
* @return array
*/
private function getLatestRemoteMasterBranchCommitHash(OutputInterface $output, array $composerJsonDependencies)
{
$commits = [];

$progress = new ProgressBar($output, count($composerJsonDependencies));
$progress->setFormat(' <comment>%current%/%max% [%bar%] %percent:3s%%</comment>');
$progress->start();

foreach ($composerJsonDependencies as $dependency => $gitUrl) {
$commits[$dependency] = substr(shell_exec('git ls-remote '.$gitUrl.' master'), 0, 40);

$progress->advance();
}

$progress->finish();
$output->writeln(PHP_EOL);

return $commits;
}

/**
* @param array $composerLockDependencies
* @param array $latestRemoteMasterBranchCommitHash
*
* @return array
*/
private function compare(array $composerLockDependencies, array $latestRemoteMasterBranchCommitHash)
{
$result = [];

foreach ($composerLockDependencies as $dependency => $localMasterHash) {
if (isset($latestRemoteMasterBranchCommitHash[$dependency])) {
$result[$dependency]['local_master_hash'] = $localMasterHash;
$result[$dependency]['remote_master_hash'] = $latestRemoteMasterBranchCommitHash[$dependency];
$result[$dependency]['master_hash_match'] =
($localMasterHash == $latestRemoteMasterBranchCommitHash[$dependency])
? 'YES'
: 'NO';
} else {
$result[$dependency]['local_master_hash'] = $localMasterHash;
$result[$dependency]['remote_master_hash'] = 'Failed to fetch';
$result[$dependency]['master_hash_match'] = 'N/A';
}
}

return $result;
}

/**
* @param OutputInterface $output
*
* @param array $comparison
*/
private function printReport(OutputInterface $output, array $comparison)
{
$header = [
'<fg=cyan;options=bold>#</fg=cyan;options=bold>',
'<fg=cyan;options=bold>DEPENDENCY</fg=cyan;options=bold>',
'<fg=cyan;options=bold>LOCAL MASTER</fg=cyan;options=bold>',
'<fg=cyan;options=bold>REMOTE MASTER</fg=cyan;options=bold>',
'<fg=cyan;options=bold>MATCH</fg=cyan;options=bold>',
];

$i = 1;
$rows = [];
foreach ($comparison as $dependency => $data) {
if ($data['master_hash_match'] == 'N/A') {
$colour = 'yellow';
} else {
$colour = ($data['master_hash_match'] == 'YES') ? 'green' : 'red';
}

$rows[] = [
'<fg='.$colour.'>'.$i++.'</fg='.$colour.'>',
'<fg='.$colour.'>'.$dependency.'</fg='.$colour.'>',
'<fg='.$colour.'>'.$data['local_master_hash'].'</fg='.$colour.'>',
'<fg='.$colour.'>'.$data['remote_master_hash'].'</fg='.$colour.'>',
'<fg='.$colour.'>'.$data['master_hash_match'].'</fg='.$colour.'>',
];
}

$table = new Table($output);
$table
->setHeaders($header)
->setRows($rows)
->setStyle('default');
$table->render();
}
}

Result