diff --git a/hesabixCore/composer.json b/hesabixCore/composer.json
index b1f4b83..f389f77 100644
--- a/hesabixCore/composer.json
+++ b/hesabixCore/composer.json
@@ -23,6 +23,7 @@
"phpdocumentor/reflection-docblock": "^5.3",
"phpoffice/phpspreadsheet": "^1.29",
"phpstan/phpdoc-parser": "^1.16",
+ "ramsey/uuid": "^4.7",
"symfony/apache-pack": "^1.0",
"symfony/asset": "7.1.*",
"symfony/console": "7.1.*",
diff --git a/hesabixCore/composer.lock b/hesabixCore/composer.lock
index 5e6fb69..d2865eb 100644
--- a/hesabixCore/composer.lock
+++ b/hesabixCore/composer.lock
@@ -4,8 +4,68 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "b5f9aa88af7c562b82e1bcf67a995845",
+ "content-hash": "8fade6c168119e9380774d2ae76c4b26",
"packages": [
+ {
+ "name": "brick/math",
+ "version": "0.12.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/brick/math.git",
+ "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
+ "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^10.1",
+ "vimeo/psalm": "6.8.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Brick\\Math\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Arbitrary-precision arithmetic library",
+ "keywords": [
+ "Arbitrary-precision",
+ "BigInteger",
+ "BigRational",
+ "arithmetic",
+ "bigdecimal",
+ "bignum",
+ "bignumber",
+ "brick",
+ "decimal",
+ "integer",
+ "math",
+ "mathematics",
+ "rational"
+ ],
+ "support": {
+ "issues": "https://github.com/brick/math/issues",
+ "source": "https://github.com/brick/math/tree/0.12.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/BenMorel",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-28T13:11:00+00:00"
+ },
{
"name": "composer/pcre",
"version": "3.3.2",
@@ -3744,6 +3804,174 @@
},
"time": "2021-10-29T13:26:27+00:00"
},
+ {
+ "name": "ramsey/collection",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/collection.git",
+ "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
+ "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "captainhook/plugin-composer": "^5.3",
+ "ergebnis/composer-normalize": "^2.45",
+ "fakerphp/faker": "^1.24",
+ "hamcrest/hamcrest-php": "^2.0",
+ "jangregor/phpstan-prophecy": "^2.1",
+ "mockery/mockery": "^1.6",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4",
+ "phpspec/prophecy-phpunit": "^2.3",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^10.5",
+ "ramsey/coding-standard": "^2.3",
+ "ramsey/conventional-commits": "^1.6",
+ "roave/security-advisories": "dev-latest"
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ },
+ "ramsey/conventional-commits": {
+ "configFile": "conventional-commits.json"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ramsey\\Collection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com"
+ }
+ ],
+ "description": "A PHP library for representing and manipulating collections.",
+ "keywords": [
+ "array",
+ "collection",
+ "hash",
+ "map",
+ "queue",
+ "set"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/collection/issues",
+ "source": "https://github.com/ramsey/collection/tree/2.1.0"
+ },
+ "time": "2025-03-02T04:48:29+00:00"
+ },
+ {
+ "name": "ramsey/uuid",
+ "version": "4.7.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
+ "ext-json": "*",
+ "php": "^8.0",
+ "ramsey/collection": "^1.2 || ^2.0"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "captainhook/captainhook": "^5.10",
+ "captainhook/plugin-composer": "^5.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "doctrine/annotations": "^1.8",
+ "ergebnis/composer-normalize": "^2.15",
+ "mockery/mockery": "^1.3",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock": "^2.2",
+ "php-mock/php-mock-mockery": "^1.3",
+ "php-parallel-lint/php-parallel-lint": "^1.1",
+ "phpbench/phpbench": "^1.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9",
+ "ramsey/composer-repl": "^1.4",
+ "slevomat/coding-standard": "^8.4",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.9"
+ },
+ "suggest": {
+ "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+ "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+ "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+ "keywords": [
+ "guid",
+ "identifier",
+ "uuid"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "source": "https://github.com/ramsey/uuid/tree/4.7.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-04-27T21:32:50+00:00"
+ },
{
"name": "sabberworm/php-css-parser",
"version": "v8.7.0",
diff --git a/hesabixCore/src/Command/ReleaseUpdateLockCommand.php b/hesabixCore/src/Command/ReleaseUpdateLockCommand.php
index 1c95b66..a626730 100644
--- a/hesabixCore/src/Command/ReleaseUpdateLockCommand.php
+++ b/hesabixCore/src/Command/ReleaseUpdateLockCommand.php
@@ -9,7 +9,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Lock\LockFactory;
#[AsCommand(
- name: 'app:release-update-lock',
+ name: 'hesabix:release-update-lock',
description: 'Releases the software update lock manually.'
)]
class ReleaseUpdateLockCommand extends Command
diff --git a/hesabixCore/src/Command/UpdateSoftwareCommand.php b/hesabixCore/src/Command/UpdateSoftwareCommand.php
index 66bf01a..53ee5f6 100644
--- a/hesabixCore/src/Command/UpdateSoftwareCommand.php
+++ b/hesabixCore/src/Command/UpdateSoftwareCommand.php
@@ -13,8 +13,8 @@ use Ramsey\Uuid\Uuid;
use Symfony\Component\Lock\LockFactory;
#[AsCommand(
- name: 'app:update-software',
- description: 'Updates the software by pulling from GitHub, clearing cache, and updating the database.'
+ name: 'hesabix:update-software',
+ description: 'Updates the Hesabix Core by pulling from GitHub, clearing cache, and updating the database.'
)]
class UpdateSoftwareCommand extends Command
{
@@ -51,7 +51,6 @@ class UpdateSoftwareCommand extends Command
$this->logger->info("Starting software update with UUID: $uuid");
$this->writeOutput($output, "Starting software update (UUID: $uuid)...");
- // چک کردن اینکه آیا آپدیت لازم است
if ($this->isUpToDate()) {
$this->writeOutput($output, 'The software is already up to date with the remote repository.');
$this->logger->info('No update needed, software is up to date.');
@@ -59,7 +58,6 @@ class UpdateSoftwareCommand extends Command
return Command::SUCCESS;
}
- // ادامه فرآیند فقط در صورتی که آپدیت لازم باشه
if (!is_dir($this->backupDir)) {
mkdir($this->backupDir, 0755, true);
}
@@ -74,9 +72,101 @@ class UpdateSoftwareCommand extends Command
$archiveBackup = null;
try {
- // بقیه کد بدون تغییر ...
+ if (!in_array('pre_checks', $state['completedSteps'])) {
+ $this->preUpdateChecks($output);
+ $state['completedSteps'][] = 'pre_checks';
+ $this->saveState($uuid, $state);
+ }
+
+ if (!in_array('archive_backup', $state['completedSteps'])) {
+ $this->writeOutput($output, 'Backing up hesabixArchive...');
+ $archiveBackup = $this->backupArchive();
+ $state['archiveBackup'] = $archiveBackup;
+ $archiveHashBefore = $this->getDirectoryHash($this->archiveDir);
+ $state['archiveHashBefore'] = $archiveHashBefore;
+ $state['completedSteps'][] = 'archive_backup';
+ $this->saveState($uuid, $state);
+ } else {
+ $archiveBackup = $state['archiveBackup'];
+ $archiveHashBefore = $state['archiveHashBefore'];
+ }
+
+ if (!in_array('git_pull', $state['completedSteps'])) {
+ $this->writeOutput($output, 'Pulling latest changes from GitHub...');
+ $gitHeadBefore = $this->getCurrentGitHead();
+ $this->runProcess(['git', 'pull'], $this->rootDir, $output, 3);
+ $state['gitHeadBefore'] = $gitHeadBefore;
+ $state['completedSteps'][] = 'git_pull';
+ $this->saveState($uuid, $state);
+ } else {
+ $gitHeadBefore = $state['gitHeadBefore'];
+ }
+
+ if (!in_array('composer_install', $state['completedSteps'])) {
+ $this->writeOutput($output, 'Installing dependencies...');
+ $this->runProcess(['composer', 'install', '--no-dev', '--optimize-autoloader'], $this->appDir, $output, 3);
+ $state['completedSteps'][] = 'composer_install';
+ $this->saveState($uuid, $state);
+ }
+
+ if (!in_array('cache_clear', $state['completedSteps'])) {
+ $this->writeOutput($output, 'Clearing cache...');
+ $cacheDir = $this->appDir . '/var/cache';
+ $cacheBackup = $this->backupCache($cacheDir);
+ $state['cacheBackup'] = $cacheBackup;
+ $this->runProcess(['php', 'bin/console', 'cache:clear', '--env=prod'], $this->appDir, $output, 3);
+ $state['completedSteps'][] = 'cache_clear';
+ $this->saveState($uuid, $state);
+ } else {
+ $cacheBackup = $state['cacheBackup'];
+ }
+
+ if (!in_array('db_update', $state['completedSteps'])) {
+ $this->writeOutput($output, 'Updating database schema...');
+ $dbBackup = $this->backupDatabase();
+ $state['dbBackup'] = $dbBackup;
+ $this->runProcess(['php', 'bin/console', 'doctrine:schema:update', '--force', '--no-interaction'], $this->appDir, $output, 3);
+ $state['completedSteps'][] = 'db_update';
+ $this->saveState($uuid, $state);
+ } else {
+ $dbBackup = $state['dbBackup'];
+ }
+
+ if (!in_array('archive_check', $state['completedSteps'])) {
+ $archiveHashAfter = $this->getDirectoryHash($this->archiveDir);
+ if ($archiveHashBefore !== $archiveHashAfter) {
+ $this->writeOutput($output, 'hesabixArchive has changed, restoring from backup...');
+ $this->restoreArchive($archiveBackup);
+ $this->writeOutput($output, 'hesabixArchive restored successfully.');
+ } else {
+ $this->writeOutput($output, 'hesabixArchive unchanged, no restore needed.');
+ }
+ $state['completedSteps'][] = 'archive_check';
+ $this->saveState($uuid, $state);
+ }
+
+ if (!in_array('post_update_test', $state['completedSteps'])) {
+ $this->postUpdateChecks($output);
+ $state['completedSteps'][] = 'post_update_test';
+ $this->saveState($uuid, $state);
+ }
+
+ $version = $this->getPackageVersion();
+ $this->writeOutput($output, "Software updated to version: $version");
+ $state['version'] = $version;
+
+ $this->logger->info('Software update completed successfully!');
+ $this->writeOutput($output, 'Software update completed successfully!');
+ $this->saveState($uuid, $state);
+ return Command::SUCCESS;
} catch (\Exception $e) {
- // بقیه کد بدون تغییر ...
+ $this->logger->error('Update failed: ' . $e->getMessage());
+ $this->writeOutput($output, 'An error occurred: ' . $e->getMessage() . '');
+ $this->rollback($gitHeadBefore, $cacheBackup, $dbBackup, $archiveBackup, $output);
+ $this->writeOutput($output, 'Update process aborted and rolled back.');
+ $state['error'] = $e->getMessage();
+ $this->saveState($uuid, $state);
+ return Command::FAILURE;
} finally {
$this->cleanupBackups($cacheBackup, $dbBackup, $archiveBackup);
$lock->release();
@@ -86,30 +176,6 @@ class UpdateSoftwareCommand extends Command
}
}
- // متد جدید برای چک کردن بهروز بودن
- private function isUpToDate(): bool
- {
- // گرفتن HEAD فعلی مخزن محلی
- $localHeadProcess = new Process(['git', 'rev-parse', 'HEAD'], $this->rootDir);
- $localHeadProcess->run();
- if (!$localHeadProcess->isSuccessful()) {
- throw new \RuntimeException('Failed to get local Git HEAD: ' . $localHeadProcess->getErrorOutput());
- }
- $localHead = trim($localHeadProcess->getOutput());
-
- // گرفتن HEAD مخزن ریموت
- $remoteHeadProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $this->rootDir);
- $remoteHeadProcess->run();
- if (!$remoteHeadProcess->isSuccessful()) {
- throw new \RuntimeException('Failed to get remote Git HEAD: ' . $remoteHeadProcess->getErrorOutput());
- }
- $remoteOutput = explode("\t", trim($remoteHeadProcess->getOutput()));
- $remoteHead = $remoteOutput[0] ?? '';
-
- // مقایسه HEAD محلی و ریموت
- return $localHead === $remoteHead;
- }
-
private function writeOutput(OutputInterface $output, string $message): void
{
$output->writeln($message);
@@ -144,6 +210,26 @@ class UpdateSoftwareCommand extends Command
}
}
+ private function isUpToDate(): bool
+ {
+ $localHeadProcess = new Process(['git', 'rev-parse', 'HEAD'], $this->rootDir);
+ $localHeadProcess->run();
+ if (!$localHeadProcess->isSuccessful()) {
+ throw new \RuntimeException('Failed to get local Git HEAD: ' . $localHeadProcess->getErrorOutput());
+ }
+ $localHead = trim($localHeadProcess->getOutput());
+
+ $remoteHeadProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $this->rootDir);
+ $remoteHeadProcess->run();
+ if (!$remoteHeadProcess->isSuccessful()) {
+ throw new \RuntimeException('Failed to get remote Git HEAD: ' . $remoteHeadProcess->getErrorOutput());
+ }
+ $remoteOutput = explode("\t", trim($remoteHeadProcess->getOutput()));
+ $remoteHead = $remoteOutput[0] ?? '';
+
+ return $localHead === $remoteHead;
+ }
+
private function preUpdateChecks(OutputInterface $output): void
{
$this->writeOutput($output, 'Running pre-update checks...');
@@ -153,7 +239,7 @@ class UpdateSoftwareCommand extends Command
$this->writeOutput($output, 'Database connection OK.');
}
- private function postUpdateTest(OutputInterface $output): void
+ private function postUpdateChecks(OutputInterface $output): void
{
$this->writeOutput($output, 'Running post-update tests...');
$this->runProcess(['php', 'bin/console', 'cache:warmup', '--env=prod'], $this->appDir, $output);