Last active
October 27, 2025 21:38
-
-
Save egyjs/728de2deb6f750936c096587ec5ddf0b to your computer and use it in GitHub Desktop.
NinjaPortal Local Development Setup Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env php | |
| <?php | |
| /** | |
| * Rollback Local Development Setup | |
| * | |
| * This script reverts changes made by setup-local-dev.php: | |
| * - Restores composer.json from backup | |
| * - Optionally removes local package clones | |
| * - Runs composer update to use remote packages again | |
| * | |
| * Usage: | |
| * Local: php rollback-local-dev.php | |
| * Remote: curl -fsSL https://gist.githubusercontent.com/egyjs/728de2deb6f750936c096587ec5ddf0b/raw/1aede9f68585646a653465504d1695d5a5f3822e/rollback-local-dev.php | php | |
| * | |
| * @version 1.0.0 | |
| * @author NinjaPortal Team | |
| */ | |
| // Colors for terminal output | |
| const COLOR_GREEN = "\033[0;32m"; | |
| const COLOR_BLUE = "\033[0;34m"; | |
| const COLOR_YELLOW = "\033[1;33m"; | |
| const COLOR_RED = "\033[0;31m"; | |
| const COLOR_NC = "\033[0m"; | |
| const COMPOSER_FILE = './composer.json'; | |
| const COMPOSER_BACKUP = './composer.json.backup'; | |
| const PACKAGES_DIR = './packages/ninjaportal'; | |
| function println($message, $color = COLOR_NC) { | |
| echo $color . $message . COLOR_NC . PHP_EOL; | |
| } | |
| function printHeader($message) { | |
| println("\n=========================================", COLOR_BLUE); | |
| println($message, COLOR_BLUE); | |
| println("=========================================\n", COLOR_BLUE); | |
| } | |
| function deleteDirectory($dir) { | |
| if (!is_dir($dir)) { | |
| return true; | |
| } | |
| $items = array_diff(scandir($dir), ['.', '..']); | |
| foreach ($items as $item) { | |
| $path = $dir . DIRECTORY_SEPARATOR . $item; | |
| is_dir($path) ? deleteDirectory($path) : unlink($path); | |
| } | |
| return rmdir($dir); | |
| } | |
| try { | |
| printHeader("Rollback Local Development Setup"); | |
| // Step 1: Check for backup | |
| println("Step 1: Checking for composer.json backup...", COLOR_GREEN); | |
| if (!file_exists(COMPOSER_BACKUP)) { | |
| println("Warning: No backup file found at " . COMPOSER_BACKUP, COLOR_YELLOW); | |
| println("Cannot restore composer.json automatically.", COLOR_YELLOW); | |
| echo "\nDo you want to manually remove 'repositories' from composer.json? (y/n): "; | |
| $handle = fopen("php://stdin", "r"); | |
| $line = fgets($handle); | |
| fclose($handle); | |
| if (trim(strtolower($line)) === 'y') { | |
| $composerData = json_decode(file_get_contents(COMPOSER_FILE), true); | |
| if ($composerData && isset($composerData['repositories'])) { | |
| unset($composerData['repositories']); | |
| // Also remove local PSR-4 mappings | |
| if (isset($composerData['autoload']['psr-4'])) { | |
| foreach ($composerData['autoload']['psr-4'] as $namespace => $path) { | |
| if (strpos($path, './packages/ninjaportal/') === 0) { | |
| unset($composerData['autoload']['psr-4'][$namespace]); | |
| } | |
| } | |
| } | |
| $json = json_encode($composerData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); | |
| file_put_contents(COMPOSER_FILE, $json . "\n"); | |
| println(" β Removed local repositories from composer.json", COLOR_GREEN); | |
| } | |
| } else { | |
| println("Skipped composer.json modification.", COLOR_YELLOW); | |
| } | |
| } else { | |
| println("Found backup: " . COMPOSER_BACKUP, COLOR_YELLOW); | |
| // Step 2: Restore backup | |
| println("\nStep 2: Restoring composer.json from backup...", COLOR_GREEN); | |
| // Create a backup of current state before restoring | |
| $currentBackup = COMPOSER_FILE . '.before-rollback'; | |
| copy(COMPOSER_FILE, $currentBackup); | |
| println(" Created backup of current state: $currentBackup", COLOR_YELLOW); | |
| // Restore from backup | |
| copy(COMPOSER_BACKUP, COMPOSER_FILE); | |
| println(" β composer.json restored from backup", COLOR_GREEN); | |
| } | |
| // Step 3: Ask about removing local packages | |
| println("\nStep 3: Remove local package clones...", COLOR_GREEN); | |
| if (is_dir(PACKAGES_DIR)) { | |
| $packages = array_filter(scandir(PACKAGES_DIR), function($item) { | |
| return $item !== '.' && $item !== '..' && is_dir(PACKAGES_DIR . '/' . $item); | |
| }); | |
| if (!empty($packages)) { | |
| println("Found " . count($packages) . " local packages:", COLOR_YELLOW); | |
| foreach ($packages as $pkg) { | |
| println(" - $pkg"); | |
| } | |
| echo "\nDo you want to DELETE these local packages? (y/n): "; | |
| $handle = fopen("php://stdin", "r"); | |
| $line = fgets($handle); | |
| fclose($handle); | |
| if (trim(strtolower($line)) === 'y') { | |
| println(" Removing local packages...", COLOR_BLUE); | |
| foreach ($packages as $pkg) { | |
| $pkgPath = PACKAGES_DIR . '/' . $pkg; | |
| if (deleteDirectory($pkgPath)) { | |
| println(" β Removed $pkg", COLOR_GREEN); | |
| } else { | |
| println(" β Failed to remove $pkg", COLOR_RED); | |
| } | |
| } | |
| // Remove packages directory if empty | |
| $remaining = array_diff(scandir(PACKAGES_DIR), ['.', '..']); | |
| if (empty($remaining)) { | |
| rmdir(PACKAGES_DIR); | |
| println(" β Removed empty packages directory", COLOR_GREEN); | |
| } | |
| } else { | |
| println(" Kept local packages (not deleted)", COLOR_YELLOW); | |
| println(" Note: You can delete them manually later if needed", COLOR_YELLOW); | |
| } | |
| } else { | |
| println("No local packages found.", COLOR_YELLOW); | |
| } | |
| } else { | |
| println("Packages directory doesn't exist.", COLOR_YELLOW); | |
| } | |
| // Step 4: Run composer update | |
| println("\nStep 4: Running composer update...", COLOR_GREEN); | |
| println(" This will reinstall packages from remote repositories.", COLOR_YELLOW); | |
| echo " Do you want to run 'composer update' now? (y/n): "; | |
| $handle = fopen("php://stdin", "r"); | |
| $line = fgets($handle); | |
| fclose($handle); | |
| if (trim(strtolower($line)) === 'y') { | |
| println(" Running composer update...", COLOR_BLUE); | |
| passthru('composer update'); | |
| println(" β Composer update completed", COLOR_GREEN); | |
| } else { | |
| println(" Skipped. Run 'composer update' manually to complete rollback.", COLOR_YELLOW); | |
| } | |
| // Summary | |
| printHeader("Rollback Complete!"); | |
| println("You are now back to using remote packages.\n", COLOR_GREEN); | |
| println("What was done:", COLOR_YELLOW); | |
| println(" β composer.json restored (backup saved)"); | |
| println(" β Local packages removed (if selected)"); | |
| println(" β Composer updated (if selected)"); | |
| println("\nNext steps:", COLOR_YELLOW); | |
| println(" 1. Run 'composer update' if you haven't already"); | |
| println(" 2. Continue working with remote packages"); | |
| println(" 3. Run setup-local-dev.php again if you need local development"); | |
| println("\nDone! π", COLOR_BLUE); | |
| } catch (Exception $e) { | |
| println("\nError: " . $e->getMessage(), COLOR_RED); | |
| exit(1); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env php | |
| <?php | |
| /** | |
| * NinjaPortal Local Development Setup Script | |
| * | |
| * This script automates the process of setting up local development for ninjaportal packages: | |
| * 1. Lists all ninjaportal packages in vendor/ | |
| * 2. Clones them from GitHub to packages/ninjaportal/ | |
| * 3. Updates composer.json to use local repositories with symlinks | |
| * 4. Configures composer to use local path repositories with locked versions | |
| * 5. Updates PSR-4 autoload mappings | |
| * | |
| * IMPORTANT: Your original composer.json is backed up as composer.json.backup and | |
| * can be restored using rollback-local-dev.php | |
| * | |
| * Usage: | |
| * Local: php setup-local-dev.php | |
| * Remote: url -fsSL https://gist.githubusercontent.com/egyjs/728de2deb6f750936c096587ec5ddf0b/raw/96302b943ae6320ac1f383125a92397f0c7a0351/setup-local-dev.php | php | |
| * | |
| * @version 1.1.0 | |
| * @author NinjaPortal Team | |
| */ | |
| // Colors for terminal output | |
| const COLOR_GREEN = "\033[0;32m"; | |
| const COLOR_BLUE = "\033[0;34m"; | |
| const COLOR_YELLOW = "\033[1;33m"; | |
| const COLOR_RED = "\033[0;31m"; | |
| const COLOR_NC = "\033[0m"; | |
| // Configuration | |
| const VENDOR_DIR = './vendor/ninjaportal'; | |
| const PACKAGES_DIR = './packages/ninjaportal'; | |
| const GITHUB_ORG = 'ninjaportal'; | |
| const COMPOSER_FILE = './composer.json'; | |
| const COMPOSER_LOCK_FILE = './composer.lock'; | |
| function println($message, $color = COLOR_NC) { | |
| echo $color . $message . COLOR_NC . PHP_EOL; | |
| } | |
| function printHeader($message) { | |
| println("\n=========================================", COLOR_BLUE); | |
| println($message, COLOR_BLUE); | |
| println("=========================================\n", COLOR_BLUE); | |
| } | |
| function execCommand($command, &$output = null, &$returnCode = null) { | |
| exec($command . ' 2>&1', $output, $returnCode); | |
| return $returnCode === 0; | |
| } | |
| function getInstalledPackageVersions(): array { | |
| if (!file_exists(COMPOSER_LOCK_FILE)) { | |
| return []; | |
| } | |
| $lockData = json_decode(file_get_contents(COMPOSER_LOCK_FILE), true); | |
| if (!is_array($lockData)) { | |
| return []; | |
| } | |
| $versions = []; | |
| foreach (['packages', 'packages-dev'] as $section) { | |
| if (empty($lockData[$section]) || !is_array($lockData[$section])) { | |
| continue; | |
| } | |
| foreach ($lockData[$section] as $package) { | |
| if (isset($package['name'], $package['version'])) { | |
| $versions[$package['name']] = $package['version']; | |
| } | |
| } | |
| } | |
| return $versions; | |
| } | |
| function checkoutPackageVersion(string $packagePath, string $packageName, ?string $version): bool { | |
| if (!$version) { | |
| return false; | |
| } | |
| $originalDir = getcwd(); | |
| chdir($packagePath); | |
| $statusOutput = []; | |
| execCommand('git status --porcelain', $statusOutput, $statusCode); | |
| if ($statusCode === 0 && !empty(array_filter($statusOutput))) { | |
| println(" β Local changes detected in $packageName. Skipping checkout of $version.", COLOR_YELLOW); | |
| chdir($originalDir); | |
| return false; | |
| } | |
| execCommand('git fetch --tags --quiet', $fetchOutput, $fetchCode); | |
| if ($fetchCode !== 0) { | |
| println(" β Failed to fetch tags for $packageName.", COLOR_YELLOW); | |
| chdir($originalDir); | |
| return false; | |
| } | |
| $tagReference = escapeshellarg("refs/tags/$version"); | |
| $tagCheckOutput = []; | |
| execCommand("git rev-parse -q --verify $tagReference", $tagCheckOutput, $tagExists); | |
| if ($tagExists !== 0) { | |
| println(" β Tag $version not found for $packageName.", COLOR_YELLOW); | |
| chdir($originalDir); | |
| return false; | |
| } | |
| $escapedVersion = escapeshellarg($version); | |
| execCommand("git checkout --force --detach $escapedVersion", $checkoutOutput, $checkoutCode); | |
| chdir($originalDir); | |
| if ($checkoutCode !== 0) { | |
| println(" β Failed to check out $version for $packageName.", COLOR_YELLOW); | |
| return false; | |
| } | |
| println(" β Checked out $version", COLOR_GREEN); | |
| return true; | |
| } | |
| // Main execution | |
| try { | |
| printHeader("NinjaPortal Local Development Setup"); | |
| // Step 1: List packages | |
| println("Step 1: Listing ninjaportal packages in vendor...", COLOR_GREEN); | |
| if (!is_dir(VENDOR_DIR)) { | |
| println("Error: " . VENDOR_DIR . " directory not found!", COLOR_RED); | |
| println("Please run 'composer install' first.", COLOR_YELLOW); | |
| exit(1); | |
| } | |
| $packages = array_filter(scandir(VENDOR_DIR), function($item) { | |
| return $item !== '.' && $item !== '..' && is_dir(VENDOR_DIR . '/' . $item); | |
| }); | |
| println("Found " . count($packages) . " packages:", COLOR_YELLOW); | |
| foreach ($packages as $pkg) { | |
| println(" - $pkg"); | |
| } | |
| $installedVersions = getInstalledPackageVersions(); | |
| if (empty($installedVersions)) { | |
| println("Warning: composer.lock not found or unreadable. Falling back to default branches.", COLOR_YELLOW); | |
| } | |
| // Step 2: Create packages directory | |
| println("\nStep 2: Creating packages directory...", COLOR_GREEN); | |
| if (!is_dir(PACKAGES_DIR)) { | |
| mkdir(PACKAGES_DIR, 0755, true); | |
| println("Created: " . PACKAGES_DIR, COLOR_YELLOW); | |
| } else { | |
| println("Directory already exists: " . PACKAGES_DIR, COLOR_YELLOW); | |
| } | |
| // Step 3: Clone repositories | |
| println("\nStep 3: Cloning repositories...", COLOR_GREEN); | |
| $clonedPackages = []; | |
| $packageVersions = []; | |
| foreach ($packages as $pkg) { | |
| $packageName = GITHUB_ORG . '/' . $pkg; | |
| $targetVersion = $installedVersions[$packageName] ?? null; | |
| $pkgPath = PACKAGES_DIR . '/' . $pkg; | |
| $packageVersions[$pkg] = $targetVersion; | |
| if (is_dir($pkgPath)) { | |
| if ($targetVersion) { | |
| println(" β Package '$pkg' already exists, aligning to $targetVersion...", COLOR_YELLOW); | |
| if (!checkoutPackageVersion($pkgPath, $packageName, $targetVersion)) { | |
| println(" Using existing checkout for $pkg", COLOR_YELLOW); | |
| } | |
| } else { | |
| println(" β Package '$pkg' already exists, pulling latest changes...", COLOR_YELLOW); | |
| chdir($pkgPath); | |
| $output = []; | |
| execCommand("git pull origin main", $output, $returnCode); | |
| if ($returnCode !== 0) { | |
| execCommand("git pull origin master", $output, $returnCode); | |
| } | |
| if ($returnCode === 0) { | |
| println(" β Updated $pkg", COLOR_GREEN); | |
| } else { | |
| println(" Could not pull, using existing version", COLOR_YELLOW); | |
| } | |
| chdir(__DIR__); | |
| } | |
| $clonedPackages[] = $pkg; | |
| } else { | |
| println(" π¦ Cloning $pkg...", COLOR_BLUE); | |
| $repoUrl = "https://github.com/" . GITHUB_ORG . "/$pkg.git"; | |
| $output = []; | |
| if (execCommand("git clone $repoUrl $pkgPath", $output, $returnCode)) { | |
| println(" β Successfully cloned $pkg", COLOR_GREEN); | |
| if ($targetVersion) { | |
| println(" Aligning $pkg to $targetVersion", COLOR_BLUE); | |
| checkoutPackageVersion($pkgPath, $packageName, $targetVersion); | |
| } else { | |
| println(" β No locked version found; using repository default branch", COLOR_YELLOW); | |
| } | |
| $clonedPackages[] = $pkg; | |
| } else { | |
| println(" β Failed to clone $pkg from $repoUrl", COLOR_RED); | |
| println(" Please check if the repository exists and you have access", COLOR_YELLOW); | |
| } | |
| } | |
| } | |
| if (empty($clonedPackages)) { | |
| println("\nNo packages were cloned. Exiting...", COLOR_YELLOW); | |
| exit(0); | |
| } | |
| // Step 4: Update composer.json | |
| println("\nStep 4: Updating composer.json...", COLOR_GREEN); | |
| // Create backup | |
| $backupFile = COMPOSER_FILE . '.backup'; | |
| copy(COMPOSER_FILE, $backupFile); | |
| println(" Backup created: $backupFile", COLOR_YELLOW); | |
| // Load composer.json | |
| $composerData = json_decode(file_get_contents(COMPOSER_FILE), true); | |
| if (!$composerData) { | |
| throw new Exception("Failed to parse composer.json"); | |
| } | |
| // Add repositories | |
| $repositories = []; | |
| foreach ($clonedPackages as $pkg) { | |
| $packageName = GITHUB_ORG . '/' . $pkg; | |
| $repositoryOptions = [ | |
| 'symlink' => true | |
| ]; | |
| if (!empty($packageVersions[$pkg])) { | |
| $repositoryOptions['versions'] = [ | |
| $packageName => $packageVersions[$pkg] | |
| ]; | |
| } | |
| $repositories[] = [ | |
| 'type' => 'path', | |
| 'url' => './packages/ninjaportal/' . $pkg, | |
| 'options' => $repositoryOptions | |
| ]; | |
| } | |
| $composerData['repositories'] = $repositories; | |
| // Step 5: Preserve existing version constraints | |
| println("\nStep 5: Preserving version constraints (no changes needed)", COLOR_GREEN); | |
| // Step 6: Update PSR-4 autoload | |
| println("\nStep 6: Updating PSR-4 autoload mappings...", COLOR_GREEN); | |
| if (!isset($composerData['autoload'])) { | |
| $composerData['autoload'] = []; | |
| } | |
| if (!isset($composerData['autoload']['psr-4'])) { | |
| $composerData['autoload']['psr-4'] = []; | |
| } | |
| // Read PSR-4 mappings from each package | |
| foreach ($clonedPackages as $pkg) { | |
| $pkgComposerFile = PACKAGES_DIR . '/' . $pkg . '/composer.json'; | |
| if (file_exists($pkgComposerFile)) { | |
| $pkgComposer = json_decode(file_get_contents($pkgComposerFile), true); | |
| if ($pkgComposer && isset($pkgComposer['autoload']['psr-4'])) { | |
| foreach ($pkgComposer['autoload']['psr-4'] as $namespace => $path) { | |
| // Map to local package path | |
| $localPath = './packages/ninjaportal/' . $pkg . '/' . ltrim($path, './'); | |
| $composerData['autoload']['psr-4'][$namespace] = $localPath; | |
| println(" Added: $namespace => $localPath", COLOR_YELLOW); | |
| } | |
| } | |
| } | |
| } | |
| // Save updated composer.json | |
| $json = json_encode($composerData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); | |
| file_put_contents(COMPOSER_FILE, $json . "\n"); | |
| println(" β composer.json updated", COLOR_GREEN); | |
| // Step 7: Prompt for composer update | |
| println("\nStep 7: Running composer update...", COLOR_GREEN); | |
| println(" This will install packages from local paths instead of remote repositories.", COLOR_YELLOW); | |
| echo " Do you want to run 'composer update' now? (y/n): "; | |
| $handle = fopen("php://stdin", "r"); | |
| $line = fgets($handle); | |
| fclose($handle); | |
| if (trim(strtolower($line)) === 'y') { | |
| println(" Running composer update...", COLOR_BLUE); | |
| passthru('composer update'); | |
| println(" β Composer update completed", COLOR_GREEN); | |
| } else { | |
| println(" Skipped. Run 'composer update' manually when ready.", COLOR_YELLOW); | |
| } | |
| // Summary | |
| printHeader("Setup Complete!"); | |
| println("Local development environment is ready!\n", COLOR_GREEN); | |
| println("What was changed:", COLOR_YELLOW); | |
| println(" β Added path repositories for local packages with locked versions"); | |
| println(" β Preserved root version constraints"); | |
| println(" β Updated PSR-4 autoload mappings"); | |
| println(" β Created backup: composer.json.backup"); | |
| println("\nNext steps:", COLOR_YELLOW); | |
| println(" 1. Run 'composer update' if you haven't already"); | |
| println(" 2. Start developing in ./packages/ninjaportal/<package-name>"); | |
| println(" 3. Changes will be reflected immediately via symlinks"); | |
| println(" 4. Commit and push changes from the package directories"); | |
| println("\nAvailable packages:", COLOR_YELLOW); | |
| foreach ($clonedPackages as $pkg) { | |
| println(" β $pkg -> " . PACKAGES_DIR . "/$pkg"); | |
| } | |
| println("\nTo revert to remote packages:", COLOR_YELLOW); | |
| println(" php rollback-local-dev.php"); | |
| println("\nHappy coding! π", COLOR_BLUE); | |
| } catch (Exception $e) { | |
| println("\nError: " . $e->getMessage(), COLOR_RED); | |
| exit(1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment