diff --git a/cli/scaffold_client.php b/cli/scaffold_client.php new file mode 100644 index 0000000..adf16c7 --- /dev/null +++ b/cli/scaffold_client.php @@ -0,0 +1,250 @@ +#!/usr/bin/env php + + * + * This file is part of a Moko Consulting project. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * FILE INFORMATION + * DEFGROUP: MokoStandards.Scripts.CLI + * INGROUP: MokoStandards + * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform + * PATH: /cli/scaffold_client.php + * VERSION: 01.00.00 + * BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings + */ + +declare(strict_types=1); + +final class ScaffoldClient +{ + private string $name = ''; + private string $org = ''; + private string $giteaUrl = 'https://git.mokoconsulting.tech'; + private string $token = ''; + private bool $dryRun = false; + + public function run(): int + { + $this->parseArgs(); + + if ($this->name === '' || $this->org === '' || $this->token === '') + { + $this->log('ERROR: --name, --org, and --token are required.'); + $this->printUsage(); + return 1; + } + + $repoName = 'client-waas-' . $this->name; + + $this->log("Scaffolding client repo: {$this->org}/{$repoName}"); + $this->log("Gitea URL: {$this->giteaUrl}"); + + if ($this->dryRun) + { + $this->log('[DRY RUN] Would create repo from template MokoConsulting/Template-Client-WaaS'); + $this->log("[DRY RUN] Repo: {$this->org}/{$repoName}"); + $this->log("[DRY RUN] Description: \"{$this->name} WaaS site\""); + $this->log('[DRY RUN] Would create dev branch from main'); + $this->printPostSetupInstructions($repoName); + return 0; + } + + // Step 1: Create repo from template + $this->log('Step 1: Creating repo from template...'); + + $createPayload = json_encode([ + 'owner' => $this->org, + 'name' => $repoName, + 'description' => "{$this->name} WaaS site", + 'private' => true, + 'git_content' => true, + 'topics' => true, + 'labels' => true, + ]); + + $response = $this->apiRequest( + 'POST', + "/api/v1/repos/MokoConsulting/Template-Client-WaaS/generate", + $createPayload + ); + + if ($response['code'] < 200 || $response['code'] >= 300) + { + $this->log("ERROR: Failed to create repo (HTTP {$response['code']})."); + $this->log("Response: {$response['body']}"); + return 1; + } + + $this->log("Repo created: {$this->org}/{$repoName}"); + + // Step 2: Set repo description (already set via generate, but confirm) + $this->log('Step 2: Updating repo description...'); + + $updatePayload = json_encode([ + 'description' => "{$this->name} WaaS site", + ]); + + $response = $this->apiRequest( + 'PATCH', + "/api/v1/repos/{$this->org}/{$repoName}", + $updatePayload + ); + + if ($response['code'] >= 200 && $response['code'] < 300) + { + $this->log('Description updated.'); + } + else + { + $this->log("WARNING: Could not update description (HTTP {$response['code']})."); + } + + // Step 3: Create dev branch from main + $this->log('Step 3: Creating dev branch from main...'); + + $branchPayload = json_encode([ + 'new_branch_name' => 'dev', + 'old_branch_name' => 'main', + ]); + + $response = $this->apiRequest( + 'POST', + "/api/v1/repos/{$this->org}/{$repoName}/branches", + $branchPayload + ); + + if ($response['code'] >= 200 && $response['code'] < 300) + { + $this->log('Branch "dev" created from "main".'); + } + else + { + $this->log("WARNING: Could not create dev branch (HTTP {$response['code']})."); + $this->log("Response: {$response['body']}"); + } + + // Step 4: Print post-setup instructions + $this->printPostSetupInstructions($repoName); + + $this->log('Scaffold complete.'); + + return 0; + } + + private function parseArgs(): void + { + $args = $_SERVER['argv'] ?? []; + $count = count($args); + + for ($i = 1; $i < $count; $i++) + { + switch ($args[$i]) + { + case '--name': + $this->name = $args[++$i] ?? ''; + break; + case '--org': + $this->org = $args[++$i] ?? ''; + break; + case '--gitea-url': + $this->giteaUrl = rtrim($args[++$i] ?? '', '/'); + break; + case '--token': + $this->token = $args[++$i] ?? ''; + break; + case '--dry-run': + $this->dryRun = true; + break; + case '--help': + case '-h': + $this->printUsage(); + exit(0); + default: + $this->log("WARNING: Unknown argument: {$args[$i]}"); + break; + } + } + } + + private function printUsage(): void + { + $this->log('Usage: scaffold_client.php --name --org --token [options]'); + $this->log(''); + $this->log('Options:'); + $this->log(' --name Client name (e.g., "clarksvillefurs")'); + $this->log(' --org Gitea organization (e.g., "ClarksvilleFurs")'); + $this->log(' --gitea-url Gitea URL (default: https://git.mokoconsulting.tech)'); + $this->log(' --token Gitea API token'); + $this->log(' --dry-run Show what would be done without making changes'); + $this->log(' --help, -h Show this help'); + } + + private function printPostSetupInstructions(string $repoName): void + { + $this->log(''); + $this->log('=== POST-SETUP INSTRUCTIONS ==='); + $this->log(''); + $this->log("Navigate to: {$this->giteaUrl}/{$this->org}/{$repoName}/settings"); + $this->log(''); + $this->log('Set the following REPO VARIABLES (Settings > Actions > Variables):'); + $this->log(' DEV_SYNC_HOST - Dev server hostname or IP'); + $this->log(' DEV_SYNC_PORT - Dev server SSH port (default: 22)'); + $this->log(' DEV_SYNC_USER - Dev server SSH username'); + $this->log(' DEV_SYNC_PATH - Dev server deploy path'); + $this->log(' LIVE_SSH_HOST - Live server hostname or IP'); + $this->log(' LIVE_SSH_PORT - Live server SSH port (default: 22)'); + $this->log(' LIVE_SSH_USER - Live server SSH username'); + $this->log(' LIVE_SYNC_PATH - Live server deploy path'); + $this->log(''); + $this->log('Set the following REPO SECRETS (Settings > Actions > Secrets):'); + $this->log(' DEV_SYNC_KEY - Private SSH key for dev server'); + $this->log(' LIVE_SSH_KEY - Private SSH key for live server'); + $this->log(''); + $this->log('================================'); + } + + private function apiRequest(string $method, string $endpoint, ?string $body = null): array + { + $url = $this->giteaUrl . $endpoint; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Accept: application/json', + "Authorization: token {$this->token}", + ]); + + if ($body !== null) + { + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + + $responseBody = curl_exec($ch); + $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) + { + $error = curl_error($ch); + curl_close($ch); + + return ['code' => 0, 'body' => "cURL error: {$error}"]; + } + + curl_close($ch); + + return ['code' => $httpCode, 'body' => $responseBody]; + } + + private function log(string $message): void + { + fwrite(STDERR, $message . PHP_EOL); + } +} + +$app = new ScaffoldClient(); +exit($app->run());