112 lines
3.4 KiB
PHP
112 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
class NextjsRepoSyncService
|
|
{
|
|
/**
|
|
* Stage+commit+push assets under Next.js public/ that were copied by SiteAssetSyncService / ImportHugoContent.
|
|
*
|
|
* Best-effort and never throws: failure should not block the CMS workflow.
|
|
*/
|
|
public static function pushPublicAssets(): void
|
|
{
|
|
$repoPath = static::resolveRepoRoot();
|
|
if (! $repoPath) {
|
|
return;
|
|
}
|
|
|
|
$lock = Cache::lock('nextjs-repo-assets-push', 120);
|
|
if (! $lock->get()) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (! is_dir($repoPath.'/.git')) {
|
|
Log::warning("Next.js repo push skipped (no .git): {$repoPath}");
|
|
|
|
return;
|
|
}
|
|
|
|
$remote = (string) config('services.nextjs.repo_remote', 'origin');
|
|
$branch = (string) config('services.nextjs.repo_branch', 'main');
|
|
|
|
// Keep the repo up to date to reduce push failures.
|
|
static::run($repoPath, ['git', 'fetch', $remote]);
|
|
static::run($repoPath, ['git', 'checkout', $branch]);
|
|
static::run($repoPath, ['git', 'pull', '--rebase', $remote, $branch]);
|
|
|
|
// Only stage asset directories we manage.
|
|
static::run($repoPath, ['git', 'add', '-A', '--', 'public/uploads', 'public/images']);
|
|
|
|
$status = static::run($repoPath, ['git', 'status', '--porcelain']);
|
|
if (trim($status) === '') {
|
|
return;
|
|
}
|
|
|
|
$msg = (string) config('services.nextjs.repo_assets_commit_message', 'chore(assets): sync public assets');
|
|
$msg .= ' ('.now()->format('Y-m-d H:i:s').')';
|
|
|
|
static::run($repoPath, ['git', 'commit', '-m', $msg]);
|
|
static::run($repoPath, ['git', 'push', $remote, $branch]);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Next.js repo push failed: '.$e->getMessage());
|
|
} finally {
|
|
$lock->release();
|
|
}
|
|
}
|
|
|
|
public static function scheduleAssetsPush(): void
|
|
{
|
|
if (! config('services.nextjs.autopush_assets')) {
|
|
return;
|
|
}
|
|
|
|
// If queue is sync, this runs inline; otherwise requires a worker.
|
|
\App\Jobs\PushNextjsPublicAssetsJob::dispatch()->delay(now()->addSeconds(5));
|
|
}
|
|
|
|
private static function resolveRepoRoot(): ?string
|
|
{
|
|
$configured = config('services.nextjs.repo_path');
|
|
if (is_string($configured) && $configured !== '' && is_dir($configured)) {
|
|
return $configured;
|
|
}
|
|
|
|
$public = config('services.nextjs.public_path');
|
|
if (is_string($public) && $public !== '') {
|
|
$root = dirname($public);
|
|
if (is_dir($root)) {
|
|
return $root;
|
|
}
|
|
}
|
|
|
|
$guess = base_path('../usher-site');
|
|
if (is_dir($guess)) {
|
|
return $guess;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static function run(string $cwd, array $cmd): string
|
|
{
|
|
$p = new Process($cmd, $cwd);
|
|
$p->setTimeout(60);
|
|
$p->run();
|
|
|
|
$out = (string) $p->getOutput();
|
|
$err = (string) $p->getErrorOutput();
|
|
if (! $p->isSuccessful()) {
|
|
throw new \RuntimeException('Command failed: '.implode(' ', $cmd)."\n".$err.$out);
|
|
}
|
|
|
|
return $out.$err;
|
|
}
|
|
}
|
|
|