#!/usr/bin/env php * * This file is part of a Moko Consulting project. * * SPDX-License-Identifier: GPL-3.0-or-later * * FILE INFORMATION * DEFGROUP: moko-platform.CLI * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/create_repo.php * BRIEF: Scaffold a new governed repository with full MokoStandards baseline * * USAGE * php cli/create_repo.php --name MokoNewModule --type dolibarr --description "My new module" * php cli/create_repo.php --name MokoNewModule --type joomla --private * php cli/create_repo.php --name MokoNewModule --type generic --dry-run */ declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; use MokoEnterprise\Config; use MokoEnterprise\PlatformAdapterFactory; $dryRun = in_array('--dry-run', $argv); $private = in_array('--private', $argv); $name = null; $type = null; $description = ''; foreach ($argv as $i => $arg) { if ($arg === '--name' && isset($argv[$i + 1])) { $name = $argv[$i + 1]; } if ($arg === '--type' && isset($argv[$i + 1])) { $type = $argv[$i + 1]; } if ($arg === '--description' && isset($argv[$i + 1])) { $description = $argv[$i + 1]; } } if (!$name || !$type) { fwrite(STDERR, "Usage: php create_repo.php --name --type [--description \"...\"] [--private] [--dry-run]\n"); fwrite(STDERR, "\nTypes: generic, dolibarr, dolibarr-platform, joomla, nodejs, terraform, python, wordpress\n"); exit(2); } $config = Config::load(); $adapter = PlatformAdapterFactory::create($config); $org = $config->getString( $adapter->getPlatformName() . '.organization', 'mokoconsulting-tech' ); $repoRoot = dirname(__DIR__, 2); $TYPE_TO_PLATFORM = [ 'dolibarr' => 'crm-module', 'dolibarr-platform' => 'crm-platform', 'joomla' => 'waas-component', 'nodejs' => 'nodejs', 'terraform' => 'terraform', 'python' => 'python', 'wordpress' => 'wordpress', 'generic' => 'generic', ]; $TYPE_TO_TOPICS = [ 'dolibarr' => ['dolibarr', 'erp', 'crm', 'php', 'mokostandards'], 'joomla' => ['joomla', 'cms', 'php', 'mokostandards'], 'nodejs' => ['nodejs', 'javascript', 'typescript', 'mokostandards'], 'terraform' => ['terraform', 'infrastructure', 'iac', 'mokostandards'], 'python' => ['python', 'mokostandards'], 'wordpress' => ['wordpress', 'php', 'cms', 'mokostandards'], 'generic' => ['mokostandards'], ]; $platform = $TYPE_TO_PLATFORM[$type] ?? 'generic'; $topics = $TYPE_TO_TOPICS[$type] ?? ['mokostandards']; $platformName = $adapter->getPlatformName(); echo "Scaffolding new repository: {$org}/{$name} (on {$platformName})\n"; echo " Type: {$type} (platform: {$platform})\n"; echo " Visibility: " . ($private ? 'private' : 'public') . "\n"; if ($description) { echo " Description: {$description}\n"; } echo "\n"; // ── Step 1: Create the repository ─────────────────────────────────────── echo "Step 1: Creating repository...\n"; if (!$dryRun) { try { $data = $adapter->createOrgRepo($org, $name, [ 'description' => $description ?: "Managed by MokoStandards ({$type})", 'private' => $private, 'has_issues' => true, 'has_projects' => true, 'has_wiki' => false, 'auto_init' => true, 'delete_branch_on_merge' => true, 'allow_squash_merge' => true, 'allow_merge_commit' => false, 'allow_rebase_merge' => false, ]); echo " Created: " . ($data['html_url'] ?? "{$org}/{$name}") . "\n"; } catch (\Exception $e) { if (str_contains($e->getMessage(), '422') || str_contains($e->getMessage(), 'already exists')) { echo " Repository already exists — continuing with setup\n"; } else { fwrite(STDERR, " Failed to create repo: " . $e->getMessage() . "\n"); exit(1); } } } else { echo " (dry-run) would create {$org}/{$name}\n"; } // ── Step 2: Set topics ────────────────────────────────────────────────── echo "Step 2: Setting topics...\n"; if (!$dryRun) { $adapter->setRepoTopics($org, $name, $topics); echo " Topics: " . implode(', ', $topics) . "\n"; } else { echo " (dry-run) would set topics: " . implode(', ', $topics) . "\n"; } // ── Step 3: Create .mokostandards file ────────────────────────────────── echo "Step 3: Creating .github/.mokostandards...\n"; $mokoContent = "platform: {$platform}\nversion: 04.02.30\nmanaged: true\n"; if (!$dryRun) { try { $adapter->createOrUpdateFile( $org, $name, '.github/.mokostandards', $mokoContent, 'chore: add .mokostandards platform config [skip ci]' ); echo " .mokostandards created\n"; } catch (\Exception $e) { echo " Warning: " . $e->getMessage() . "\n"; } } else { echo " (dry-run) would create .github/.mokostandards\n"; } // ── Step 4: Create initial README.md ──────────────────────────────────── echo "Step 4: Creating README.md...\n"; // Determine the repo base URL based on platform $baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com'; $repoUrl = "{$baseUrl}/{$org}/{$name}"; $standardsUrl = "{$baseUrl}/{$org}/MokoStandards"; $readmeContent = << SPDX-License-Identifier: GPL-3.0-or-later # FILE INFORMATION DEFGROUP: {$name} INGROUP: moko-platform REPO: {$repoUrl} PATH: /README.md BRIEF: {$description} --> # {$name} [![MokoStandards](https://img.shields.io/badge/MokoStandards-04.06.00-blue)]({$standardsUrl}) [![Version](https://img.shields.io/badge/version-01.00.00-green)]({$repoUrl}) {$description} ## Getting Started This repository is governed by [MokoStandards]({$standardsUrl}). ## License This project is licensed under the GPL-3.0-or-later license. See [LICENSE](LICENSE) for details. --- *This file is part of the Moko Consulting ecosystem. All rights reserved.* *This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.* MD; if (!$dryRun) { // Get existing README sha (auto_init creates one) $sha = null; try { $existing = $adapter->getFileContents($org, $name, 'README.md'); $sha = $existing['sha'] ?? null; } catch (\Exception $e) { $adapter->getApiClient()->resetCircuitBreaker(); } $adapter->createOrUpdateFile( $org, $name, 'README.md', $readmeContent, 'docs: initialize README with MokoStandards header [skip ci]', $sha ); echo " README.md created\n"; } else { echo " (dry-run) would create README.md\n"; } // ── Step 5: Provision labels ──────────────────────────────────────────── echo "Step 5: Provisioning labels...\n"; if (!$dryRun) { $labelScript = "{$repoRoot}/api/maintenance/setup_labels.php"; if (file_exists($labelScript)) { $exitCode = 0; passthru("php " . escapeshellarg($labelScript) . " --org " . escapeshellarg($org) . " --repo " . escapeshellarg($name), $exitCode); echo $exitCode === 0 ? " Labels provisioned\n" : " Label provisioning had issues\n"; } else { echo " Labels will be provisioned on next sync\n"; } } else { echo " (dry-run) would provision standard labels\n"; } // ── Step 6: Run first sync ────────────────────────────────────────────── echo "Step 6: Running initial sync...\n"; if (!$dryRun) { $syncScript = "{$repoRoot}/api/automation/bulk_sync.php"; if (file_exists($syncScript)) { passthru("php " . escapeshellarg($syncScript) . " --repos " . escapeshellarg($name) . " --force --yes"); } else { echo " Run manually: php automation/bulk_sync.php --repos {$name} --force --yes\n"; } } else { echo " (dry-run) would run initial sync\n"; } // ── Step 7: Create Project ────────────────────────────────────────────── echo "Step 7: Creating Project...\n"; if (!$dryRun) { $projectScript = "{$repoRoot}/api/cli/create_project.php"; if (file_exists($projectScript)) { passthru("php " . escapeshellarg($projectScript) . " --repo " . escapeshellarg($name) . " --type " . escapeshellarg($type)); } else { echo " Run manually: php cli/create_project.php --repo {$name} --type {$type}\n"; } } else { echo " (dry-run) would create Project\n"; } echo "\n" . str_repeat('-', 50) . "\n"; echo "Repository {$org}/{$name} scaffolded successfully\n"; echo " URL: {$repoUrl}\n"; echo " Platform: {$platform} ({$platformName})\n"; echo " Next: verify the sync and merge any PRs\n";