2016-05-01 00:15:05 +00:00
|
|
|
<?php
|
2018-08-04 22:05:14 +00:00
|
|
|
namespace App;
|
2016-05-01 00:15:05 +00:00
|
|
|
|
2019-09-04 18:00:51 +00:00
|
|
|
use DateTime;
|
|
|
|
use DateTimeZone;
|
2019-08-07 17:50:48 +00:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
2018-11-02 05:25:47 +00:00
|
|
|
use Symfony\Component\Process\Process;
|
2016-05-01 00:15:05 +00:00
|
|
|
|
2018-08-04 22:05:14 +00:00
|
|
|
/**
|
|
|
|
* App Core Framework Version
|
|
|
|
*/
|
2016-05-01 00:15:05 +00:00
|
|
|
class Version
|
|
|
|
{
|
2018-11-20 04:53:15 +00:00
|
|
|
/** @var string Version that is displayed if no Git repository information is present. */
|
2019-11-23 21:54:41 +00:00
|
|
|
public const FALLBACK_VERSION = '0.9.8';
|
2018-11-20 04:53:15 +00:00
|
|
|
|
2019-08-07 17:50:48 +00:00
|
|
|
/** @var CacheInterface */
|
2018-11-02 05:25:47 +00:00
|
|
|
protected $cache;
|
|
|
|
|
2018-11-20 04:53:15 +00:00
|
|
|
/** @var string */
|
|
|
|
protected $repo_dir;
|
|
|
|
|
|
|
|
/** @var Settings */
|
|
|
|
protected $app_settings;
|
|
|
|
|
2019-08-07 17:50:48 +00:00
|
|
|
public function __construct(CacheInterface $cache, Settings $app_settings)
|
2018-11-02 05:25:47 +00:00
|
|
|
{
|
|
|
|
$this->cache = $cache;
|
2018-11-20 04:53:15 +00:00
|
|
|
$this->app_settings = $app_settings;
|
|
|
|
|
|
|
|
$this->repo_dir = $app_settings[Settings::BASE_DIR];
|
2018-11-02 05:25:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string The current tagged version.
|
|
|
|
*/
|
2019-01-31 17:54:17 +00:00
|
|
|
public function getVersion(): string
|
2018-11-02 05:25:47 +00:00
|
|
|
{
|
|
|
|
$details = $this->getDetails();
|
2018-11-20 04:53:15 +00:00
|
|
|
return $details['tag'] ?? self::FALLBACK_VERSION;
|
2018-11-02 05:25:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load cache or generate new repository details from the underlying Git repository.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getDetails(): array
|
|
|
|
{
|
|
|
|
static $details;
|
|
|
|
|
|
|
|
if (!$details) {
|
2019-08-07 17:50:48 +00:00
|
|
|
$details = $this->cache->get('app_version_details');
|
|
|
|
|
|
|
|
if (empty($details)) {
|
|
|
|
$details = $this->_getRawDetails();
|
|
|
|
$ttl = $this->app_settings->isProduction() ? 86400 : 600;
|
|
|
|
|
|
|
|
$this->cache->set('app_version_details', $details, $ttl);
|
|
|
|
}
|
2018-11-02 05:25:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $details;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate new repository details from the underlying Git repository.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function _getRawDetails(): array
|
2016-05-01 00:15:05 +00:00
|
|
|
{
|
2019-09-04 18:00:51 +00:00
|
|
|
if (!is_dir($this->repo_dir . '/.git')) {
|
2018-11-20 04:53:15 +00:00
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2018-11-02 05:25:47 +00:00
|
|
|
$details = [];
|
|
|
|
|
|
|
|
// Get the long form of the latest commit's hash.
|
|
|
|
$latest_commit_hash = $this->_runProcess(['git', 'log', '--pretty=%H', '-n1', 'HEAD']);
|
|
|
|
|
|
|
|
$details['commit'] = $latest_commit_hash;
|
|
|
|
$details['commit_short'] = substr($latest_commit_hash, 0, 7);
|
|
|
|
|
|
|
|
// Get the last commit's timestamp.
|
|
|
|
$latest_commit_date = $this->_runProcess(['git', 'log', '-n1', '--pretty=%ci', 'HEAD']);
|
|
|
|
|
|
|
|
if (!empty($latest_commit_date)) {
|
2019-09-04 18:00:51 +00:00
|
|
|
$commit_date = new DateTime($latest_commit_date);
|
|
|
|
$commit_date->setTimezone(new DateTimeZone('UTC'));
|
2018-11-02 05:25:47 +00:00
|
|
|
|
|
|
|
$details['commit_timestamp'] = $commit_date->getTimestamp();
|
|
|
|
$details['commit_date'] = $commit_date->format('Y-m-d G:i');
|
|
|
|
} else {
|
|
|
|
$details['commit_timestamp'] = 0;
|
|
|
|
$details['commit_date'] = 'N/A';
|
|
|
|
}
|
|
|
|
|
|
|
|
$last_tagged_commit = $this->_runProcess(['git', 'rev-list', '--tags', '--max-count=1']);
|
|
|
|
if (!empty($last_tagged_commit)) {
|
|
|
|
$details['tag'] = $this->_runProcess(['git', 'describe', '--tags', $last_tagged_commit], 'N/A');
|
|
|
|
} else {
|
|
|
|
$details['tag'] = 'N/A';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $details;
|
2016-05-01 00:15:05 +00:00
|
|
|
}
|
2016-09-03 07:34:18 +00:00
|
|
|
|
2018-11-02 05:25:47 +00:00
|
|
|
/**
|
|
|
|
* Run the specified process and return its output.
|
|
|
|
*
|
2019-01-31 17:54:17 +00:00
|
|
|
* @param array $proc
|
2018-11-02 05:25:47 +00:00
|
|
|
* @param string $default
|
2019-09-20 16:44:38 +00:00
|
|
|
*
|
2018-11-02 05:25:47 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function _runProcess($proc, $default = ''): string
|
2016-09-03 07:34:18 +00:00
|
|
|
{
|
2018-11-02 05:25:47 +00:00
|
|
|
$process = new Process($proc);
|
2018-11-20 04:53:15 +00:00
|
|
|
$process->setWorkingDirectory($this->repo_dir);
|
2018-11-02 05:25:47 +00:00
|
|
|
$process->run();
|
|
|
|
|
|
|
|
if (!$process->isSuccessful()) {
|
|
|
|
return $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
return trim($process->getOutput());
|
2016-09-03 07:34:18 +00:00
|
|
|
}
|
2019-09-04 18:00:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string A textual representation of the current installed version.
|
|
|
|
*/
|
|
|
|
public function getVersionText(): string
|
|
|
|
{
|
|
|
|
$details = $this->getDetails();
|
|
|
|
|
|
|
|
return (isset($details['tag']))
|
|
|
|
? 'v' . $details['tag'] . ', #' . $details['commit_short'] . ' (' . $details['commit_date'] . ')'
|
|
|
|
: 'v' . self::FALLBACK_VERSION . ' Release Build';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string|null The long-form Git hash that represents the current commit of this installation.
|
|
|
|
*/
|
|
|
|
public function getCommitHash(): ?string
|
|
|
|
{
|
|
|
|
$details = $this->getDetails();
|
|
|
|
return $details['commit'] ?? null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string|null The shortened Git hash corresponding to the current commit.
|
|
|
|
*/
|
|
|
|
public function getCommitShort(): ?string
|
|
|
|
{
|
|
|
|
$details = $this->getDetails();
|
|
|
|
return $details['commit_short'] ?? null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the installation has been modified by the user from the release build.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function isInstallationModified(): bool
|
|
|
|
{
|
|
|
|
// We can't detect if release builds are changed, so always return true.
|
|
|
|
if (!is_dir($this->repo_dir . '/.git')) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$changed_files = $this->_runProcess(['git', 'status', '-s']);
|
|
|
|
return !empty($changed_files);
|
|
|
|
}
|
2018-08-04 22:05:14 +00:00
|
|
|
}
|