123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
<?php
if (php_sapi_name() !== 'cli') {
exit('This script can only be run from the command line.');
}
$scriptDir = dirname(dirname(__FILE__));
$indexerFilesDir = $scriptDir;
$configFile = $indexerFilesDir . '/config.json';
class VersionChecker {
private $indexerFilesDir;
private $configFile;
private $versionDb;
private $versionPdo = null;
private $primaryUrl = 'https://raw.githubusercontent.com/5q12-ccls/5q12-s-Indexer/refs/heads/main/repo';
private $fallbackUrl = 'https://ccls.icu/src/repositories/5q12-indexer/main/repo/?view=raw';
public function __construct($indexerFilesDir, $configFile) {
$this->indexerFilesDir = $indexerFilesDir;
$this->configFile = $configFile;
$this->versionDb = $indexerFilesDir . '/cache/version.sqlite';
$this->initializeVersionDb();
}
private function getStoredVersionData() {
if (!$this->versionPdo) {
return null;
}
try {
$stmt = $this->versionPdo->prepare("SELECT value, cached_at FROM version_info WHERE key = 'remote_version_info'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
$data = json_decode($result['value'], true);
$data['cached_at'] = $result['cached_at'];
return $data;
}
return null;
} catch (Exception $e) {
echo "Error reading stored version data: " . $e->getMessage() . "\n";
return null;
}
}
private function compareVersions($currentVersion, $latestVersion) {
if (version_compare($currentVersion, $latestVersion, '<')) {
echo "Update available: $currentVersion -> $latestVersion\n";
} elseif (version_compare($currentVersion, $latestVersion, '>')) {
echo "Current version is newer than remote: $currentVersion > $latestVersion\n";
} else {
echo "Versions match: $currentVersion = $latestVersion\n";
}
}
private function initializeVersionDb() {
$cacheDir = $this->indexerFilesDir . '/cache';
if (!is_dir($cacheDir)) {
@mkdir($cacheDir, 0777, true);
}
try {
$this->versionPdo = new PDO('sqlite:' . $this->versionDb);
$this->versionPdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->versionPdo->exec("CREATE TABLE IF NOT EXISTS version_info (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
cached_at INTEGER NOT NULL
)");
if (file_exists($this->versionDb)) {
@chmod($this->versionDb, 0666);
}
} catch (Exception $e) {
echo "Version database initialization error: " . $e->getMessage() . "\n";
$this->versionPdo = null;
}
}
public function checkAndUpdateVersion() {
echo "Starting version check...\n";
$currentVersion = $this->getCurrentVersion();
echo "Current version from config.json: " . ($currentVersion ?: 'Not found') . "\n";
$storedVersionData = $this->getStoredVersionData();
$storedCurrentVersion = $storedVersionData ? $storedVersionData['current_version'] : null;
echo "Stored current version from database: " . ($storedCurrentVersion ?: 'Not found') . "\n";
if ($currentVersion && $storedCurrentVersion && $currentVersion === $storedCurrentVersion) {
echo "Config version matches database version - no remote check needed\n";
echo "Using cached remote version: " . ($storedVersionData['remote_version'] ?: 'Unknown') . "\n";
if ($storedVersionData['remote_version']) {
$this->compareVersions($currentVersion, $storedVersionData['remote_version']);
}
echo "Last checked: " . ($storedVersionData['last_checked'] ? date('Y-m-d H:i:s', $storedVersionData['last_checked']) : 'Unknown') . "\n";
echo "Version check completed using cached data\n";
return true;
}
echo "Config version differs from database or no cached data - fetching remote version\n";
$latestVersion = $this->fetchLatestVersion();
if (!$latestVersion) {
echo "Failed to fetch latest version from all sources\n";
return false;
}
echo "Latest version fetched: $latestVersion\n";
$updateSuccess = $this->updateVersionDatabase($latestVersion, $currentVersion);
echo "Version database update: " . ($updateSuccess ? 'SUCCESS' : 'FAILED') . "\n";
if ($currentVersion && $latestVersion) {
$this->compareVersions($currentVersion, $latestVersion);
}
return $updateSuccess;
}
private function getCurrentVersion() {
if (!file_exists($this->configFile)) {
echo "Config file not found: {$this->configFile}\n";
return null;
}
$configData = json_decode(file_get_contents($this->configFile), true);
if (!$configData || !isset($configData['version'])) {
echo "Version not found in config.json\n";
return null;
}
return $configData['version'];
}
private function fetchLatestVersion() {
echo "Attempting to fetch from primary URL: {$this->primaryUrl}\n";
$content = $this->fetchUrl($this->primaryUrl);
if ($content === false) {
echo "Primary URL unreachable, trying fallback URL: {$this->fallbackUrl}\n";
$content = $this->fetchUrl($this->fallbackUrl);
if ($content === false) {
echo "Fallback URL also unreachable\n";
return null;
} else {
echo "Successfully fetched from fallback URL\n";
}
} else {
echo "Successfully fetched from primary URL\n";
}
return $this->parseVersionFromContent($content);
}
private function fetchUrl($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_USERAGENT => '5q12-Indexer',
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_MAXREDIRS => 3
]);
$content = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($content === false) {
echo "cURL error: $error\n";
return false;
}
if ($httpCode !== 200) {
echo "HTTP error: $httpCode\n";
return false;
}
return $content;
}
private function parseVersionFromContent($content) {
$lines = explode("\n", $content);
if (empty($lines)) {
echo "Empty content received\n";
return null;
}
$firstLine = trim($lines[0]);
echo "First line of repo file: $firstLine\n";
if (preg_match('/^VERSION=(.+)$/', $firstLine, $matches)) {
$version = trim($matches[1]);
echo "Parsed version: $version\n";
return $version;
}
echo "Could not parse version from first line\n";
return null;
}
private function updateVersionDatabase($remoteVersion, $currentVersion) {
if (!$this->versionPdo) {
echo "Version database not available\n";
return false;
}
try {
$installationType = $this->determineInstallationType();
$versionData = [
'remote_version' => $remoteVersion,
'current_version' => $currentVersion,
'installation_type' => $installationType,
'last_checked' => time()
];
$stmt = $this->versionPdo->prepare("INSERT OR REPLACE INTO version_info (key, value, cached_at) VALUES (?, ?, ?)");
$success = $stmt->execute([
'remote_version_info',
json_encode($versionData),
time()
]);
if ($success) {
echo "Version data saved to database:\n";
echo " Remote version: $remoteVersion\n";
echo " Current version: " . ($currentVersion ?: 'Unknown') . "\n";
echo " Installation type: $installationType\n";
echo " Last checked: " . date('Y-m-d H:i:s') . "\n";
}
return $success;
} catch (Exception $e) {
echo "Error updating version database: " . $e->getMessage() . "\n";
return false;
}
}
private function determineInstallationType() {
if (file_exists($this->indexerFilesDir . '/.docker')) {
return 'docker';
} elseif (file_exists($this->indexerFilesDir . '/.script')) {
return 'script';
} else {
return 'manual';
}
}
}
try {
$versionChecker = new VersionChecker($indexerFilesDir, $configFile);
$success = $versionChecker->checkAndUpdateVersion();
echo "\nVersion check completed " . ($success ? 'successfully' : 'with errors') . "\n";
exit($success ? 0 : 1);
} catch (Exception $e) {
echo "Fatal error: " . $e->getMessage() . "\n";
exit(1);
}
?>