Skip to content

Commit

Permalink
Add new forked versioning checks for project:version command
Browse files Browse the repository at this point in the history
  • Loading branch information
bennothommo committed Apr 7, 2021
1 parent 34de033 commit d5f7340
Showing 1 changed file with 118 additions and 27 deletions.
145 changes: 118 additions & 27 deletions src/Commands/ProjectVersion/SourceManifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,36 @@ class SourceManifest
*/
protected $builds = [];

/**
* @var array The version map where forks occurred.
*/
protected $forks;

/**
* @var string The URL to the forked version manifest
*/
protected $forksUrl = 'https://raw.githubusercontent.com/wintercms/meta/master/manifest/forks.json';

/**
* Constructor
*
* @param string $manifest Manifest file to load
* @param string $forks Forks manifest file to load
* @param bool $autoload Loads the manifest on construct
*/
public function __construct($source = null, $autoload = true)
public function __construct($source = null, $forks = null, $autoload = true)
{
if (isset($source)) {
$this->setSource($source);
}

if (isset($forks)) {
$this->setForksSource($forks);
}

if ($autoload) {
$this->load();
$this->loadSource();
$this->loadForks();
}
}

Expand All @@ -57,12 +73,25 @@ public function setSource($source)
}
}

/**
* Sets the forked version manifest URL.
*
* @param string $forks
* @return void
*/
public function setForksSource($forks)
{
if (is_string($forks)) {
$this->forksUrl = $forks;
}
}

/**
* Loads the manifest file.
*
* @throws Exception If the manifest is invalid, or cannot be parsed.
*/
public function load()
public function loadSource()
{
$source = file_get_contents($this->source);
if (empty($source)) {
Expand All @@ -85,7 +114,9 @@ public function load()
}

foreach ($data['manifest'] as $build) {
$this->builds[$build['build']] = [
$this->builds[$this->getVersionInt($build['build'])] = [
'version' => $build['build'],
'parent' => $build['parent'],
'modules' => $build['modules'],
'files' => $build['files'],
];
Expand All @@ -95,47 +126,81 @@ public function load()
}

/**
* Gets all builds.
* Loads the forked version manifest file.
*
* @return array
* @throws Exception If the manifest is invalid, or cannot be parsed.
*/
public function getBuilds()
public function loadForks()
{
return $this->builds;
$forks = file_get_contents($this->forksUrl);
if (empty($forks)) {
throw new Exception(
'Forked version manifest not found'
);
}

$data = json_decode($forks, true);

if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception(
'Unable to decode forked version manifest JSON data. JSON Error: ' . json_last_error_msg()
);
}
if (!isset($data['forks']) || !is_array($data['forks'])) {
throw new Exception(
'The forked version manifest at "' . $this->forksUrl . '" does not appear to be a valid forked version
manifest file.'
);
}

// Map forks to int values
foreach ($data['forks'] as $child => $parent) {
$this->forks[$this->getVersionInt($child)] = $this->getVersionInt($parent);
}

return $this;
}

/**
* Gets the maximum build number in the manifest.
* Gets all builds.
*
* @return int
* @return array
*/
public function getMaxBuild()
public function getBuilds()
{
if (!count($this->builds)) {
return null;
}

return max(array_keys($this->builds));
return array_values(array_map(function ($build) {
return $build['version'];
}, $this->builds));
}

/**
* Gets the filelist state at a selected build.
*
* This method will list all expected files and hashsums at the specified build number.
* This method will list all expected files and hashsums at the specified build number. It does this by following
* the history, switching branches as necessary.
*
* @param integer $build Build number to get the filelist state for.
* @param string|integer $build Build version to get the filelist state for.
* @throws Exception If the specified build has not been added to the source manifest.
* @return array
*/
public function getState($build)
{
if (is_string($build)) {
$build = $this->getVersionInt($build);
}

if (!isset($this->builds[$build])) {
throw new \Exception('The specified build has not been added.');
}

$state = [];

foreach ($this->builds as $number => $details) {
// Follow fork if necessary
if (isset($this->forks) && array_key_exists($build, $this->forks)) {
$state = $this->getState($this->forks[$build]);
}

if (isset($details['files']['added'])) {
foreach ($details['files']['added'] as $filename => $sum) {
$state[$filename] = $sum;
Expand Down Expand Up @@ -181,12 +246,14 @@ public function compare(FileManifest $manifest, $detailed = false)
$modules = $manifest->getModuleChecksums();

// Look for an unmodified version
foreach ($this->getBuilds() as $build => $details) {
$matched = array_intersect_assoc($details['modules'], $modules);
foreach ($this->getBuilds() as $buildString) {
$build = $this->builds[$this->getVersionInt($buildString)];

$matched = array_intersect_assoc($build['modules'], $modules);

if (count($matched) === count($modules)) {
$details = [
'build' => $build,
'build' => $buildString,
'modified' => false,
'confident' => true,
];
Expand All @@ -203,8 +270,10 @@ public function compare(FileManifest $manifest, $detailed = false)
// install.
$buildMatch = [];

foreach ($this->getBuilds() as $build => $details) {
$state = $this->getState($build);
foreach ($this->getBuilds() as $buildString) {
$build = $this->builds[$this->getVersionInt($buildString)];

$state = $this->getState($buildString);

// Include only the files that match the modules being loaded in this file manifest
$availableModules = array_keys($modules);
Expand Down Expand Up @@ -244,7 +313,7 @@ public function compare(FileManifest $manifest, $detailed = false)
$changedPercent = count($filesChanged) / $filesExpected;

$score = ((1 * $foundPercent) - $changedPercent);
$buildMatch[$build] = round($score * 100, 2);
$buildMatch[$buildString] = round($score * 100, 2);
}

// Find likely version
Expand All @@ -269,8 +338,8 @@ public function compare(FileManifest $manifest, $detailed = false)
* Will return an array of added, modified and removed files.
*
* @param FileManifest $manifest The current build's file manifest.
* @param FileManifest|integer $previous Either a previous manifest, or the previous build number as an int,
* used to determine changes with this build.
* @param FileManifest|string|integer $previous Either a previous manifest, or the previous build number as an int
* or string, used to determine changes with this build.
* @return array
*/
protected function processChanges(FileManifest $manifest, $previous = null)
Expand All @@ -283,7 +352,7 @@ protected function processChanges(FileManifest $manifest, $previous = null)
}

// Only save files if they are changing the "state" of the manifest (ie. the file is modified, added or removed)
if (is_int($previous)) {
if (is_int($previous) || is_string($previous)) {
$state = $this->getState($previous);
} else {
$state = $previous->getFiles();
Expand Down Expand Up @@ -319,4 +388,26 @@ protected function processChanges(FileManifest $manifest, $previous = null)

return $changes;
}


/**
* Converts a version string into an integer for comparison.
*
* @param string $version
* @throws Exception if a version string does not match the format "major.minor.path"
* @return int
*/
protected function getVersionInt(string $version)
{
// Get major.minor.patch versions
if (!preg_match('/^([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $versionParts)) {
throw new Exception('Invalid version string - must be of the format "major.minor.path"');
}

$int = $versionParts[1] * 1000000;
$int += $versionParts[2] * 1000;
$int += $versionParts[3];

return $int;
}
}

0 comments on commit d5f7340

Please sign in to comment.