Skip to content

Commit e6de261

Browse files
committed
feat(sync): sync install.sh, docker-compose, and env files to GitHub
Adds syncFilesToGitHubRepo method to handle syncing install.sh, docker-compose, and env files to the coolify-cdn repository via a feature branch and PR. Supports both nightly and production environments.
1 parent bf306ff commit e6de261

File tree

1 file changed

+305
-3
lines changed

1 file changed

+305
-3
lines changed

app/Console/Commands/SyncBunny.php

Lines changed: 305 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,162 @@ private function syncReleasesAndVersionsToGitHubRepo(string $versionsLocation, b
363363
}
364364
}
365365

366+
/**
367+
* Sync install.sh, docker-compose, and env files to GitHub repository via PR
368+
*/
369+
private function syncFilesToGitHubRepo(array $files, bool $nightly = false): bool
370+
{
371+
$envLabel = $nightly ? 'NIGHTLY' : 'PRODUCTION';
372+
$this->info("Syncing $envLabel files to GitHub repository...");
373+
try {
374+
$timestamp = time();
375+
$tmpDir = sys_get_temp_dir().'/coolify-cdn-files-'.$timestamp;
376+
$branchName = 'update-files-'.$timestamp;
377+
378+
// Clone the repository
379+
$this->info('Cloning coolify-cdn repository...');
380+
$output = [];
381+
exec('gh repo clone coollabsio/coolify-cdn '.escapeshellarg($tmpDir).' 2>&1', $output, $returnCode);
382+
if ($returnCode !== 0) {
383+
$this->error('Failed to clone repository: '.implode("\n", $output));
384+
385+
return false;
386+
}
387+
388+
// Create feature branch
389+
$this->info('Creating feature branch...');
390+
$output = [];
391+
exec('cd '.escapeshellarg($tmpDir).' && git checkout -b '.escapeshellarg($branchName).' 2>&1', $output, $returnCode);
392+
if ($returnCode !== 0) {
393+
$this->error('Failed to create branch: '.implode("\n", $output));
394+
exec('rm -rf '.escapeshellarg($tmpDir));
395+
396+
return false;
397+
}
398+
399+
// Copy each file to its target path in the CDN repo
400+
$copiedFiles = [];
401+
foreach ($files as $sourceFile => $targetPath) {
402+
if (! file_exists($sourceFile)) {
403+
$this->warn("Source file not found, skipping: $sourceFile");
404+
405+
continue;
406+
}
407+
408+
$destPath = "$tmpDir/$targetPath";
409+
$destDir = dirname($destPath);
410+
411+
if (! is_dir($destDir)) {
412+
if (! mkdir($destDir, 0755, true)) {
413+
$this->error("Failed to create directory: $destDir");
414+
exec('rm -rf '.escapeshellarg($tmpDir));
415+
416+
return false;
417+
}
418+
}
419+
420+
if (copy($sourceFile, $destPath) === false) {
421+
$this->error("Failed to copy $sourceFile to $destPath");
422+
exec('rm -rf '.escapeshellarg($tmpDir));
423+
424+
return false;
425+
}
426+
427+
$copiedFiles[] = $targetPath;
428+
$this->info("Copied: $targetPath");
429+
}
430+
431+
if (empty($copiedFiles)) {
432+
$this->warn('No files were copied. Nothing to commit.');
433+
exec('rm -rf '.escapeshellarg($tmpDir));
434+
435+
return true;
436+
}
437+
438+
// Stage all copied files
439+
$this->info('Staging changes...');
440+
$output = [];
441+
$stageCmd = 'cd '.escapeshellarg($tmpDir).' && git add '.implode(' ', array_map('escapeshellarg', $copiedFiles)).' 2>&1';
442+
exec($stageCmd, $output, $returnCode);
443+
if ($returnCode !== 0) {
444+
$this->error('Failed to stage changes: '.implode("\n", $output));
445+
exec('rm -rf '.escapeshellarg($tmpDir));
446+
447+
return false;
448+
}
449+
450+
// Check for changes
451+
$this->info('Checking for changes...');
452+
$statusOutput = [];
453+
exec('cd '.escapeshellarg($tmpDir).' && git status --porcelain 2>&1', $statusOutput, $returnCode);
454+
if ($returnCode !== 0) {
455+
$this->error('Failed to check repository status: '.implode("\n", $statusOutput));
456+
exec('rm -rf '.escapeshellarg($tmpDir));
457+
458+
return false;
459+
}
460+
461+
if (empty(array_filter($statusOutput))) {
462+
$this->info('All files are already up to date. No changes to commit.');
463+
exec('rm -rf '.escapeshellarg($tmpDir));
464+
465+
return true;
466+
}
467+
468+
// Commit changes
469+
$commitMessage = "Update $envLabel files (install.sh, docker-compose, env) - ".date('Y-m-d H:i:s');
470+
$output = [];
471+
exec('cd '.escapeshellarg($tmpDir).' && git commit -m '.escapeshellarg($commitMessage).' 2>&1', $output, $returnCode);
472+
if ($returnCode !== 0) {
473+
$this->error('Failed to commit changes: '.implode("\n", $output));
474+
exec('rm -rf '.escapeshellarg($tmpDir));
475+
476+
return false;
477+
}
478+
479+
// Push to remote
480+
$this->info('Pushing branch to remote...');
481+
$output = [];
482+
exec('cd '.escapeshellarg($tmpDir).' && git push origin '.escapeshellarg($branchName).' 2>&1', $output, $returnCode);
483+
if ($returnCode !== 0) {
484+
$this->error('Failed to push branch: '.implode("\n", $output));
485+
exec('rm -rf '.escapeshellarg($tmpDir));
486+
487+
return false;
488+
}
489+
490+
// Create pull request
491+
$this->info('Creating pull request...');
492+
$prTitle = "Update $envLabel files - ".date('Y-m-d H:i:s');
493+
$fileList = implode("\n- ", $copiedFiles);
494+
$prBody = "Automated update of $envLabel files:\n- $fileList";
495+
$prCommand = 'gh pr create --repo coollabsio/coolify-cdn --title '.escapeshellarg($prTitle).' --body '.escapeshellarg($prBody).' --base main --head '.escapeshellarg($branchName).' 2>&1';
496+
$output = [];
497+
exec($prCommand, $output, $returnCode);
498+
499+
// Clean up
500+
exec('rm -rf '.escapeshellarg($tmpDir));
501+
502+
if ($returnCode !== 0) {
503+
$this->error('Failed to create PR: '.implode("\n", $output));
504+
505+
return false;
506+
}
507+
508+
$this->info('Pull request created successfully!');
509+
if (! empty($output)) {
510+
$this->info('PR URL: '.implode("\n", $output));
511+
}
512+
$this->info('Files synced: '.count($copiedFiles));
513+
514+
return true;
515+
} catch (\Throwable $e) {
516+
$this->error('Error syncing files to GitHub: '.$e->getMessage());
517+
518+
return false;
519+
}
520+
}
521+
366522
/**
367523
* Sync versions.json to GitHub repository via PR
368524
*/
@@ -581,11 +737,130 @@ public function handle()
581737
$versions_location = "$parent_dir/other/nightly/$versions";
582738
}
583739
if (! $only_template && ! $only_version && ! $only_github_releases && ! $only_github_versions) {
740+
$envLabel = $nightly ? 'NIGHTLY' : 'PRODUCTION';
741+
$this->info("About to sync $envLabel files to BunnyCDN and create a GitHub PR for coolify-cdn.");
742+
$this->newLine();
743+
744+
// Build file mapping for diff
584745
if ($nightly) {
585-
$this->info('About to sync files NIGHTLY (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
746+
$fileMapping = [
747+
$compose_file_location => 'docker/nightly/docker-compose.yml',
748+
$compose_file_prod_location => 'docker/nightly/docker-compose.prod.yml',
749+
$production_env_location => 'environment/nightly/.env.production',
750+
$upgrade_script_location => 'scripts/nightly/upgrade.sh',
751+
$install_script_location => 'scripts/nightly/install.sh',
752+
];
586753
} else {
587-
$this->info('About to sync files PRODUCTION (docker-compose.yml, docker-compose.prod.yml, upgrade.sh, install.sh, etc) to BunnyCDN.');
754+
$fileMapping = [
755+
$compose_file_location => 'docker/docker-compose.yml',
756+
$compose_file_prod_location => 'docker/docker-compose.prod.yml',
757+
$production_env_location => 'environment/.env.production',
758+
$upgrade_script_location => 'scripts/upgrade.sh',
759+
$install_script_location => 'scripts/install.sh',
760+
];
761+
}
762+
763+
// BunnyCDN file mapping (local file => CDN URL path)
764+
$bunnyFileMapping = [
765+
$compose_file_location => "$bunny_cdn/$bunny_cdn_path/$compose_file",
766+
$compose_file_prod_location => "$bunny_cdn/$bunny_cdn_path/$compose_file_prod",
767+
$production_env_location => "$bunny_cdn/$bunny_cdn_path/$production_env",
768+
$upgrade_script_location => "$bunny_cdn/$bunny_cdn_path/$upgrade_script",
769+
$install_script_location => "$bunny_cdn/$bunny_cdn_path/$install_script",
770+
];
771+
772+
$diffTmpDir = sys_get_temp_dir().'/coolify-cdn-diff-'.time();
773+
@mkdir($diffTmpDir, 0755, true);
774+
$hasChanges = false;
775+
776+
// Diff against BunnyCDN
777+
$this->info('Fetching files from BunnyCDN to compare...');
778+
foreach ($bunnyFileMapping as $localFile => $cdnUrl) {
779+
if (! file_exists($localFile)) {
780+
$this->warn('Local file not found: '.$localFile);
781+
782+
continue;
783+
}
784+
785+
$fileName = basename($cdnUrl);
786+
$remoteTmp = "$diffTmpDir/bunny-$fileName";
787+
788+
try {
789+
$response = Http::timeout(10)->get($cdnUrl);
790+
if ($response->successful()) {
791+
file_put_contents($remoteTmp, $response->body());
792+
$diffOutput = [];
793+
exec('diff -u '.escapeshellarg($remoteTmp).' '.escapeshellarg($localFile).' 2>&1', $diffOutput, $diffCode);
794+
if ($diffCode !== 0) {
795+
$hasChanges = true;
796+
$this->newLine();
797+
$this->info("--- BunnyCDN: $bunny_cdn_path/$fileName");
798+
$this->info("+++ Local: $fileName");
799+
foreach ($diffOutput as $line) {
800+
if (str_starts_with($line, '---') || str_starts_with($line, '+++')) {
801+
continue;
802+
}
803+
$this->line($line);
804+
}
805+
}
806+
} else {
807+
$this->info("NEW on BunnyCDN: $bunny_cdn_path/$fileName (HTTP {$response->status()})");
808+
$hasChanges = true;
809+
}
810+
} catch (\Throwable $e) {
811+
$this->warn("Could not fetch $cdnUrl: {$e->getMessage()}");
812+
}
588813
}
814+
815+
// Diff against GitHub coolify-cdn repo
816+
$this->newLine();
817+
$this->info('Fetching coolify-cdn repo to compare...');
818+
$output = [];
819+
exec('gh repo clone coollabsio/coolify-cdn '.escapeshellarg("$diffTmpDir/repo").' -- --depth 1 2>&1', $output, $returnCode);
820+
821+
if ($returnCode === 0) {
822+
foreach ($fileMapping as $localFile => $cdnPath) {
823+
$remotePath = "$diffTmpDir/repo/$cdnPath";
824+
if (! file_exists($localFile)) {
825+
continue;
826+
}
827+
if (! file_exists($remotePath)) {
828+
$this->info("NEW on GitHub: $cdnPath (does not exist in coolify-cdn yet)");
829+
$hasChanges = true;
830+
831+
continue;
832+
}
833+
834+
$diffOutput = [];
835+
exec('diff -u '.escapeshellarg($remotePath).' '.escapeshellarg($localFile).' 2>&1', $diffOutput, $diffCode);
836+
if ($diffCode !== 0) {
837+
$hasChanges = true;
838+
$this->newLine();
839+
$this->info("--- GitHub: $cdnPath");
840+
$this->info("+++ Local: $cdnPath");
841+
foreach ($diffOutput as $line) {
842+
if (str_starts_with($line, '---') || str_starts_with($line, '+++')) {
843+
continue;
844+
}
845+
$this->line($line);
846+
}
847+
}
848+
}
849+
} else {
850+
$this->warn('Could not fetch coolify-cdn repo for diff.');
851+
}
852+
853+
exec('rm -rf '.escapeshellarg($diffTmpDir));
854+
855+
if (! $hasChanges) {
856+
$this->newLine();
857+
$this->info('No differences found. All files are already up to date.');
858+
859+
return;
860+
}
861+
862+
$this->newLine();
863+
589864
$confirmed = confirm('Are you sure you want to sync?');
590865
if (! $confirmed) {
591866
return;
@@ -692,7 +967,34 @@ public function handle()
692967
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
693968
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
694969
]);
695-
$this->info('All files uploaded & purged...');
970+
$this->info('All files uploaded & purged to BunnyCDN.');
971+
$this->newLine();
972+
973+
// Sync files to GitHub CDN repository via PR
974+
$this->info('Creating GitHub PR for coolify-cdn repository...');
975+
if ($nightly) {
976+
$files = [
977+
$compose_file_location => 'docker/nightly/docker-compose.yml',
978+
$compose_file_prod_location => 'docker/nightly/docker-compose.prod.yml',
979+
$production_env_location => 'environment/nightly/.env.production',
980+
$upgrade_script_location => 'scripts/nightly/upgrade.sh',
981+
$install_script_location => 'scripts/nightly/install.sh',
982+
];
983+
} else {
984+
$files = [
985+
$compose_file_location => 'docker/docker-compose.yml',
986+
$compose_file_prod_location => 'docker/docker-compose.prod.yml',
987+
$production_env_location => 'environment/.env.production',
988+
$upgrade_script_location => 'scripts/upgrade.sh',
989+
$install_script_location => 'scripts/install.sh',
990+
];
991+
}
992+
993+
$githubSuccess = $this->syncFilesToGitHubRepo($files, $nightly);
994+
$this->newLine();
995+
$this->info('=== Summary ===');
996+
$this->info('BunnyCDN sync: Complete');
997+
$this->info('GitHub PR: '.($githubSuccess ? 'Created' : 'Failed'));
696998
} catch (\Throwable $e) {
697999
$this->error('Error: '.$e->getMessage());
6981000
}

0 commit comments

Comments
 (0)