From f6d8ad1775a4f1c87690dc412e6461871f09dd93 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:52:20 +0100 Subject: [PATCH] feat: add support for syncing versions.json to GitHub repository via PR --- app/Console/Commands/SyncBunny.php | 191 ++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/SyncBunny.php b/app/Console/Commands/SyncBunny.php index e634feadb..7a15dd01e 100644 --- a/app/Console/Commands/SyncBunny.php +++ b/app/Console/Commands/SyncBunny.php @@ -16,7 +16,7 @@ class SyncBunny extends Command * * @var string */ - protected $signature = 'sync:bunny {--templates} {--release} {--github-releases} {--nightly}'; + protected $signature = 'sync:bunny {--templates} {--release} {--github-releases} {--github-versions} {--nightly}'; /** * The console command description. @@ -70,12 +70,25 @@ private function syncReleasesToGitHubRepo(): bool // Write releases.json $this->info('Writing releases.json...'); $releasesPath = "$tmpDir/json/releases.json"; + $releasesDir = dirname($releasesPath); + + // Ensure directory exists + if (! is_dir($releasesDir)) { + $this->info("Creating directory: $releasesDir"); + if (! mkdir($releasesDir, 0755, true)) { + $this->error("Failed to create directory: $releasesDir"); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + } + $jsonContent = json_encode($releases, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); $bytesWritten = file_put_contents($releasesPath, $jsonContent); if ($bytesWritten === false) { $this->error("Failed to write releases.json to: $releasesPath"); - $this->error('Possible reasons: directory does not exist, permission denied, or disk full.'); + $this->error('Possible reasons: permission denied or disk full.'); exec('rm -rf '.escapeshellarg($tmpDir)); return false; @@ -158,6 +171,156 @@ private function syncReleasesToGitHubRepo(): bool } } + /** + * Sync versions.json to GitHub repository via PR + */ + private function syncVersionsToGitHubRepo(string $versionsLocation, bool $nightly = false): bool + { + $this->info('Syncing versions.json to GitHub repository...'); + try { + if (! file_exists($versionsLocation)) { + $this->error("versions.json not found at: $versionsLocation"); + + return false; + } + + $file = file_get_contents($versionsLocation); + $json = json_decode($file, true); + $actualVersion = data_get($json, 'coolify.v4.version'); + + $timestamp = time(); + $tmpDir = sys_get_temp_dir().'/coolify-cdn-versions-'.$timestamp; + $branchName = 'update-versions-'.$timestamp; + $targetPath = $nightly ? 'json/versions-nightly.json' : 'json/versions.json'; + + // Clone the repository + $this->info('Cloning coolify-cdn repository...'); + exec('gh repo clone coollabsio/coolify-cdn '.escapeshellarg($tmpDir).' 2>&1', $output, $returnCode); + if ($returnCode !== 0) { + $this->error('Failed to clone repository: '.implode("\n", $output)); + + return false; + } + + // Create feature branch + $this->info('Creating feature branch...'); + $output = []; + exec('cd '.escapeshellarg($tmpDir).' && git checkout -b '.escapeshellarg($branchName).' 2>&1', $output, $returnCode); + if ($returnCode !== 0) { + $this->error('Failed to create branch: '.implode("\n", $output)); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + + // Write versions.json + $this->info('Writing versions.json...'); + $versionsPath = "$tmpDir/$targetPath"; + $versionsDir = dirname($versionsPath); + + // Ensure directory exists + if (! is_dir($versionsDir)) { + $this->info("Creating directory: $versionsDir"); + if (! mkdir($versionsDir, 0755, true)) { + $this->error("Failed to create directory: $versionsDir"); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + } + + $jsonContent = json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + $bytesWritten = file_put_contents($versionsPath, $jsonContent); + + if ($bytesWritten === false) { + $this->error("Failed to write versions.json to: $versionsPath"); + $this->error('Possible reasons: permission denied or disk full.'); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + + // Stage and commit + $this->info('Committing changes...'); + $output = []; + exec('cd '.escapeshellarg($tmpDir).' && git add '.escapeshellarg($targetPath).' 2>&1', $output, $returnCode); + if ($returnCode !== 0) { + $this->error('Failed to stage changes: '.implode("\n", $output)); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + + $this->info('Checking for changes...'); + $statusOutput = []; + exec('cd '.escapeshellarg($tmpDir).' && git status --porcelain '.escapeshellarg($targetPath).' 2>&1', $statusOutput, $returnCode); + if ($returnCode !== 0) { + $this->error('Failed to check repository status: '.implode("\n", $statusOutput)); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + + if (empty(array_filter($statusOutput))) { + $this->info('versions.json is already up to date. No changes to commit.'); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return true; + } + + $envLabel = $nightly ? 'NIGHTLY' : 'PRODUCTION'; + $commitMessage = "Update $envLabel versions.json to $actualVersion - ".date('Y-m-d H:i:s'); + $output = []; + exec('cd '.escapeshellarg($tmpDir).' && git commit -m '.escapeshellarg($commitMessage).' 2>&1', $output, $returnCode); + if ($returnCode !== 0) { + $this->error('Failed to commit changes: '.implode("\n", $output)); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + + // Push to remote + $this->info('Pushing branch to remote...'); + $output = []; + exec('cd '.escapeshellarg($tmpDir).' && git push origin '.escapeshellarg($branchName).' 2>&1', $output, $returnCode); + if ($returnCode !== 0) { + $this->error('Failed to push branch: '.implode("\n", $output)); + exec('rm -rf '.escapeshellarg($tmpDir)); + + return false; + } + + // Create pull request + $this->info('Creating pull request...'); + $prTitle = "Update $envLabel versions.json to $actualVersion - ".date('Y-m-d H:i:s'); + $prBody = "Automated update of $envLabel versions.json to version $actualVersion"; + $output = []; + $prCommand = 'gh pr create --repo coollabsio/coolify-cdn --title '.escapeshellarg($prTitle).' --body '.escapeshellarg($prBody).' --base main --head '.escapeshellarg($branchName).' 2>&1'; + exec($prCommand, $output, $returnCode); + + // Clean up + exec('rm -rf '.escapeshellarg($tmpDir)); + + if ($returnCode !== 0) { + $this->error('Failed to create PR: '.implode("\n", $output)); + + return false; + } + + $this->info('Pull request created successfully!'); + if (! empty($output)) { + $this->info('PR URL: '.implode("\n", $output)); + } + $this->info("Version synced: $actualVersion"); + + return true; + } catch (\Throwable $e) { + $this->error('Error syncing versions.json: '.$e->getMessage()); + + return false; + } + } + /** * Execute the console command. */ @@ -167,6 +330,7 @@ public function handle() $only_template = $this->option('templates'); $only_version = $this->option('release'); $only_github_releases = $this->option('github-releases'); + $only_github_versions = $this->option('github-versions'); $nightly = $this->option('nightly'); $bunny_cdn = 'https://cdn.coollabs.io'; $bunny_cdn_path = 'coolify'; @@ -224,7 +388,7 @@ public function handle() $install_script_location = "$parent_dir/other/nightly/$install_script"; $versions_location = "$parent_dir/other/nightly/$versions"; } - if (! $only_template && ! $only_version && ! $only_github_releases) { + if (! $only_template && ! $only_version && ! $only_github_releases && ! $only_github_versions) { if ($nightly) { $this->info('About to sync files NIGHTLY (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.'); } else { @@ -249,6 +413,11 @@ public function handle() return; } elseif ($only_version) { + $this->warn('⚠️ DEPRECATION WARNING: The --release option is deprecated.'); + $this->warn(' Please use --github-versions instead to create a PR to the coolify-cdn repository.'); + $this->warn(' This option will continue to work but may be removed in a future version.'); + $this->newLine(); + if ($nightly) { $this->info('About to sync NIGHLTY versions.json to BunnyCDN.'); } else { @@ -281,6 +450,22 @@ public function handle() // Sync releases to GitHub repository $this->syncReleasesToGitHubRepo(); + return; + } elseif ($only_github_versions) { + $envLabel = $nightly ? 'NIGHTLY' : 'PRODUCTION'; + $file = file_get_contents($versions_location); + $json = json_decode($file, true); + $actual_version = data_get($json, 'coolify.v4.version'); + + $this->info("About to sync $envLabel versions.json ($actual_version) to GitHub repository."); + $confirmed = confirm('Are you sure you want to sync versions.json via GitHub PR?'); + if (! $confirmed) { + return; + } + + // Sync versions.json to GitHub repository + $this->syncVersionsToGitHubRepo($versions_location, $nightly); + return; }