mirror of
https://github.com/BillyOutlast/UNIT3D.git
synced 2026-02-04 03:01:20 +01:00
371 lines
10 KiB
PHP
371 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* NOTICE OF LICENSE.
|
|
*
|
|
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
|
* The details is bundled with this project in the file LICENSE.txt.
|
|
*
|
|
* @project UNIT3D Community Edition
|
|
*
|
|
* @author HDVinnie <hdinnovations@protonmail.com>
|
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
|
*/
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Console\ConsoleTools;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Str;
|
|
use Symfony\Component\Console\Input\ArgvInput;
|
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* @see \Tests\Todo\Unit\Console\Commands\GitUpdaterTest
|
|
*/
|
|
class GitUpdater extends Command
|
|
{
|
|
use ConsoleTools;
|
|
|
|
/**
|
|
* The copy command.
|
|
*/
|
|
private string $copyCommand = 'cp -Rfp';
|
|
|
|
/**
|
|
* The console command signature.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'git:update';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Executes The Commands Necessary To Update Your Website Using Git';
|
|
|
|
/**
|
|
* @var string[]
|
|
*/
|
|
private const ADDITIONAL = [
|
|
'.env',
|
|
'laravel-echo-server.json',
|
|
];
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle(): void
|
|
{
|
|
$this->input = new ArgvInput();
|
|
$this->output = new ConsoleOutput();
|
|
|
|
$this->io = new SymfonyStyle($this->input, $this->output);
|
|
|
|
$this->info('
|
|
***************************
|
|
* Git Updater v3.0 *
|
|
***************************
|
|
');
|
|
|
|
$this->line('<fg=cyan>
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) EVEN IF ADVISED OF THE POSSIBILITY
|
|
OF SUCH DAMAGE.
|
|
|
|
WITH THAT SAID YOU CAN BE GUARANTEED THAT YOUR DATABASE WILL NOT BE ALTERED.
|
|
|
|
<fg=red>BY PROCEEDING YOU AGREE TO THE ABOVE DISCLAIMER! USE AT YOUR OWN RISK!</>
|
|
</>');
|
|
|
|
if (!$this->io->confirm('Would you like to proceed', true)) {
|
|
$this->line('<fg=red>Aborted ...</>');
|
|
|
|
exit();
|
|
}
|
|
|
|
$this->io->writeln('
|
|
Press CTRL + C ANYTIME to abort! Aborting can lead to unexpected results!
|
|
');
|
|
|
|
sleep(1);
|
|
|
|
$this->update();
|
|
|
|
$this->white('Please report any errors or issues.');
|
|
|
|
$this->done();
|
|
}
|
|
|
|
private function update(): void
|
|
{
|
|
$updating = $this->checkForUpdates();
|
|
|
|
if ((is_countable($updating) ? \count($updating) : 0) > 0) {
|
|
$this->alertDanger('Found Updates');
|
|
|
|
$this->cyan('Files that need updated:');
|
|
$this->io->listing($updating);
|
|
|
|
if ($this->io->confirm('Start the update process', true)) {
|
|
$this->call('down');
|
|
|
|
$this->process('git add .');
|
|
|
|
$paths = $this->paths();
|
|
|
|
$this->backup($paths);
|
|
|
|
$this->header('Reseting Repository');
|
|
|
|
$this->commands([
|
|
'git fetch origin',
|
|
'git reset --hard origin/master',
|
|
]);
|
|
|
|
$this->restore($paths);
|
|
|
|
$conflicts = array_intersect($updating, $paths);
|
|
|
|
if ($conflicts !== []) {
|
|
$this->red('There are some files that was not updated because because of conflicts.');
|
|
$this->red('We will walk you through updating these files now.');
|
|
|
|
$this->manualUpdate($conflicts);
|
|
}
|
|
|
|
if ($this->io->confirm('Run new migrations (php artisan migrate)', true)) {
|
|
$this->migrations();
|
|
}
|
|
|
|
$this->clearCache();
|
|
|
|
if ($this->io->confirm('Install new packages (composer install)', true)) {
|
|
$this->composer();
|
|
}
|
|
|
|
$this->clearComposerCache();
|
|
|
|
$this->updateUNIT3DConfig();
|
|
|
|
$this->setCache();
|
|
|
|
if ($this->io->confirm('Compile assets (npx mix -p)', true)) {
|
|
$this->compile();
|
|
}
|
|
|
|
$this->permissions();
|
|
|
|
$this->supervisor();
|
|
|
|
$this->php();
|
|
|
|
$this->header('Bringing Site Live');
|
|
$this->call('up');
|
|
} else {
|
|
$this->alertDanger('Aborted Update');
|
|
|
|
exit();
|
|
}
|
|
} else {
|
|
$this->alertSuccess('No Available Updates Found');
|
|
}
|
|
}
|
|
|
|
private function checkForUpdates(): array
|
|
{
|
|
$this->header('Checking For Updates');
|
|
|
|
$this->process('git fetch origin');
|
|
$process = $this->process('git diff ..origin/master --name-only');
|
|
$updating = array_filter(explode("\n", $process->getOutput()), 'strlen');
|
|
|
|
$this->done();
|
|
|
|
return $updating;
|
|
}
|
|
|
|
private function manualUpdate($updating): void
|
|
{
|
|
$this->alertInfo('Manual Update');
|
|
$this->red('Updating will cause you to LOSE any changes you might have made to the file!');
|
|
|
|
foreach ($updating as $file) {
|
|
if ($this->io->confirm(sprintf('Update %s', $file), true)) {
|
|
$this->updateFile($file);
|
|
}
|
|
}
|
|
|
|
$this->done();
|
|
}
|
|
|
|
private function updateFile($file): void
|
|
{
|
|
$this->process(sprintf('git checkout origin/master -- %s', $file));
|
|
}
|
|
|
|
private function backup(array $paths): void
|
|
{
|
|
$this->header('Backing Up Files');
|
|
|
|
$this->commands([
|
|
'rm -rf '.storage_path('gitupdate'),
|
|
'mkdir '.storage_path('gitupdate'),
|
|
], true);
|
|
|
|
foreach ($paths as $path) {
|
|
$this->validatePath($path);
|
|
$this->createBackupPath($path);
|
|
$this->process($this->copyCommand.' '.base_path($path).' '.storage_path('gitupdate').'/'.$path);
|
|
}
|
|
|
|
$this->done();
|
|
}
|
|
|
|
private function restore(array $paths): void
|
|
{
|
|
$this->header('Restoring Backups');
|
|
|
|
foreach ($paths as $path) {
|
|
$to = Str::replaceLast('/.', '', base_path(\dirname((string) $path)));
|
|
$from = storage_path('gitupdate').'/'.$path;
|
|
|
|
if (is_dir($from)) {
|
|
$to .= '/'.basename($from).'/';
|
|
$from .= '/*';
|
|
}
|
|
|
|
$this->process(sprintf('%s %s %s', $this->copyCommand, $from, $to));
|
|
}
|
|
|
|
$this->commands([
|
|
'git add .',
|
|
'git checkout origin/master -- bun.lockb',
|
|
'git checkout origin/master -- composer.lock',
|
|
]);
|
|
}
|
|
|
|
private function composer(): void
|
|
{
|
|
$this->header('Installing Composer Packages');
|
|
|
|
$this->commands([
|
|
'composer self-update',
|
|
'composer install --prefer-dist --no-dev -o',
|
|
]);
|
|
|
|
$this->done();
|
|
}
|
|
|
|
private function compile(): void
|
|
{
|
|
$this->header('Compiling Assets ...');
|
|
|
|
$this->commands([
|
|
'npm install npm -g',
|
|
'rm -rf node_modules',
|
|
'npm cache clean --force',
|
|
'npm install --no-audit',
|
|
'npx mix -p',
|
|
]);
|
|
|
|
$this->done();
|
|
}
|
|
|
|
private function updateUNIT3DConfig(): void
|
|
{
|
|
$this->header('Updating UNIT3D Configuration File');
|
|
$this->process('git fetch origin && git checkout origin/master -- config/unit3d.php');
|
|
$this->done();
|
|
}
|
|
|
|
private function clearComposerCache(): void
|
|
{
|
|
$this->header('Clearing Composer Cache');
|
|
$this->process('composer clear-cache --no-interaction --ansi --verbose');
|
|
$this->done();
|
|
}
|
|
|
|
private function clearCache(): void
|
|
{
|
|
$this->header('Clearing Application Cache');
|
|
$this->call('optimize:clear');
|
|
$this->done();
|
|
}
|
|
|
|
private function setCache(): void
|
|
{
|
|
$this->header('Setting Cache');
|
|
$this->call('optimize');
|
|
$this->done();
|
|
}
|
|
|
|
private function migrations(): void
|
|
{
|
|
$this->header('Running New Migrations');
|
|
$this->call('migrate');
|
|
$this->done();
|
|
}
|
|
|
|
private function permissions(): void
|
|
{
|
|
$this->header('Refreshing Permissions');
|
|
$this->process('chown -R www-data: storage bootstrap public config');
|
|
$this->done();
|
|
}
|
|
|
|
private function supervisor(): void
|
|
{
|
|
$this->header('Restarting Supervisor');
|
|
$this->call('queue:restart');
|
|
$this->process('supervisorctl reread && supervisorctl update && supervisorctl reload');
|
|
$this->done();
|
|
}
|
|
|
|
private function php(): void
|
|
{
|
|
$this->header('Restarting PHP');
|
|
$this->process('systemctl restart php8.2-fpm');
|
|
$this->done();
|
|
}
|
|
|
|
private function validatePath($path): void
|
|
{
|
|
if (!is_file(base_path($path)) && !is_dir(base_path($path))) {
|
|
$this->red(sprintf("The path '%s' is invalid", $path));
|
|
}
|
|
}
|
|
|
|
private function createBackupPath($path): void
|
|
{
|
|
if (!is_dir(storage_path(sprintf('gitupdate/%s', $path))) && !is_file(base_path($path))) {
|
|
if (!mkdir($concurrentDirectory = storage_path(sprintf('gitupdate/%s', $path)), 0775, true) && !is_dir($concurrentDirectory)) {
|
|
throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
|
|
}
|
|
} elseif (is_file(base_path($path)) && \dirname((string) $path) !== '.') {
|
|
$path = \dirname((string) $path);
|
|
|
|
if (!is_dir(storage_path(sprintf('gitupdate/%s', $path))) && !mkdir($concurrentDirectory = storage_path(sprintf(
|
|
'gitupdate/%s',
|
|
$path
|
|
)), 0775, true) && !is_dir($concurrentDirectory)) {
|
|
throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
|
|
}
|
|
}
|
|
}
|
|
|
|
private function paths(): array
|
|
{
|
|
$p = $this->process('git diff master --name-only');
|
|
$paths = array_filter(explode("\n", $p->getOutput()), 'strlen');
|
|
|
|
return [...$paths, ...self::ADDITIONAL];
|
|
}
|
|
}
|