diff --git a/hesabixCore/config/services.yaml b/hesabixCore/config/services.yaml index fe07298..94e6c86 100644 --- a/hesabixCore/config/services.yaml +++ b/hesabixCore/config/services.yaml @@ -1,28 +1,28 @@ -# This file is the entry point to configure your own services. -# Files in the packages/ subdirectory configure your dependencies. - -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: archiveMediaDir: '%kernel.project_dir%/../hesabixArchive' archiveTempMediaDir: '%kernel.project_dir%/../hesabixArchive/temp' avatarDir: '%kernel.project_dir%/../hesabixArchive/avatars' sealDir: '%kernel.project_dir%/../hesabixArchive/seal' SupportFilesDir: '%kernel.project_dir%/../hesabixArchive/support' + services: - # default configuration for services in *this* file _defaults: - autowire: true # Automatically injects dependencies in your services. - autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + autowire: true + autoconfigure: true public: false App\Command\UpdateSoftwareCommand: arguments: $logger: '@Psr\Log\LoggerInterface' - $projectDir: '%kernel.project_dir%' $lockFactory: '@Symfony\Component\Lock\LockFactory' tags: - { name: 'console.command' } + App\Command\ReleaseUpdateLockCommand: + arguments: + $lockFactory: '@Symfony\Component\Lock\LockFactory' + tags: + - { name: 'console.command' } + # تنظیمات Lock Symfony\Component\Lock\LockFactory: arguments: @@ -33,19 +33,7 @@ services: arguments: - '%kernel.project_dir%/var/lock' - doctrine.orm.default_attribute_driver: - class: Doctrine\ORM\Mapping\Driver\AttributeDriver - arguments: - - [ '%kernel.project_dir%/src/Entity' ] - - true # reportFieldsWhereDeclared - tags: - - { name: doctrine.orm.mapping_driver } - App\Security\AuthenticationFailureHandler: - arguments: - $captchaService: '@App\Service\CaptchaService' - $requestStack: '@request_stack' - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name + # سایر سرویس‌ها App\: resource: '../src/' exclude: @@ -53,28 +41,46 @@ services: - '../src/Entity/' - '../src/Kernel.php' - # add more service definitions when explicit configuration is needed - # please note that last definitions always *replace* previous ones + doctrine.orm.default_attribute_driver: + class: Doctrine\ORM\Mapping\Driver\AttributeDriver + arguments: + - [ '%kernel.project_dir%/src/Entity' ] + - true + tags: + - { name: doctrine.orm.mapping_driver } + + App\Security\AuthenticationFailureHandler: + arguments: + $captchaService: '@App\Service\CaptchaService' + $requestStack: '@request_stack' + Jdate: class: App\Service\Jdate + Exctractor: class: App\Service\Exctractor + Log: class: App\Service\Log - arguments: [ "@doctrine.orm.entity_manager" ] + arguments: [ '@doctrine.orm.entity_manager' ] + SMS: class: App\Service\SMS arguments: - $entityManager: "@doctrine.orm.entity_manager" + $entityManager: '@doctrine.orm.entity_manager' + Provider: class: App\Service\Provider - arguments: [ "@doctrine.orm.entity_manager" ] + arguments: [ '@doctrine.orm.entity_manager' ] + twigFunctions: class: App\Service\twigFunctions - arguments: [ "@doctrine.orm.entity_manager" ] + arguments: [ '@doctrine.orm.entity_manager' ] + registryMGR: class: App\Service\registryMGR - arguments: [ "@doctrine.orm.entity_manager" ] + arguments: [ '@doctrine.orm.entity_manager' ] + Printers: class: App\Service\Printers - arguments: [ "@doctrine.orm.entity_manager" ] + arguments: [ '@doctrine.orm.entity_manager' ] diff --git a/hesabixCore/src/Command/HesabixCommand.php b/hesabixCore/src/Command/HesabixCommand.php deleted file mode 100644 index 1f19d8f..0000000 --- a/hesabixCore/src/Command/HesabixCommand.php +++ /dev/null @@ -1,44 +0,0 @@ -addArgument('arg1', InputArgument::OPTIONAL, 'Argument description') - ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') - ; - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - $arg1 = $input->getArgument('arg1'); - - if ($arg1) { - $io->note(sprintf('You passed an argument: %s', $arg1)); - } - - if ($input->getOption('option1')) { - // ... - } - - $io->success('You have a new command! Now make it your own! Pass --help to see your options.'); - - return Command::SUCCESS; - } -} diff --git a/hesabixCore/src/Command/ReleaseUpdateLockCommand.php b/hesabixCore/src/Command/ReleaseUpdateLockCommand.php index acc0f94..1c95b66 100644 --- a/hesabixCore/src/Command/ReleaseUpdateLockCommand.php +++ b/hesabixCore/src/Command/ReleaseUpdateLockCommand.php @@ -1,15 +1,19 @@ logger = $logger; - $this->rootDir = dirname($projectDir); - $this->appDir = $projectDir; + $this->lockFactory = $lockFactory; + $this->appDir = dirname(__DIR__, 2); // src/Command -> hesabixCore + $this->rootDir = dirname($this->appDir); // hesabixCore -> parent dir $this->archiveDir = $this->rootDir . '/hesabixArchive'; $this->backupDir = $this->rootDir . '/../backup'; $this->stateFile = $this->backupDir . '/update_state.json'; - $this->lockFactory = $lockFactory; parent::__construct(); } @@ -49,6 +51,15 @@ 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.'); + $lock->release(); + return Command::SUCCESS; + } + + // ادامه فرآیند فقط در صورتی که آپدیت لازم باشه if (!is_dir($this->backupDir)) { mkdir($this->backupDir, 0755, true); } @@ -63,101 +74,9 @@ 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->postUpdateTest($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(); @@ -167,6 +86,30 @@ 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);