1d87be7d5e
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
- Update REPO: from MokoStandards-API to moko-platform in 125 files - Fix wrong org path (mokoconsulting-tech → MokoConsulting) in 10 files - Fix SPDX-LICENSE-IDENTIFIER case in 2 template files - Add missing REPO: field to 3 files Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
252 lines
11 KiB
PHP
252 lines
11 KiB
PHP
#!/usr/bin/env php
|
|
<?php
|
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* REQUIRED FILE: This file must be present in all MokoStandards-compliant repositories
|
|
*
|
|
* This file is part of a Moko Consulting project.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* FILE INFORMATION
|
|
* DEFGROUP: MokoStandards.Scripts.Maintenance
|
|
* INGROUP: MokoStandards
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
|
* PATH: /maintenance/setup_labels.php
|
|
* BRIEF: REQUIRED label deployment script for all MokoStandards-governed repositories
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
use MokoEnterprise\CliFramework;
|
|
use MokoEnterprise\Config;
|
|
use MokoEnterprise\GitPlatformAdapter;
|
|
use MokoEnterprise\PlatformAdapterFactory;
|
|
|
|
/**
|
|
* Deploys the standard set of repository labels required by MokoStandards.
|
|
*
|
|
* Uses the platform adapter (GitHub or Gitea) to create or update each label.
|
|
* Supports --dry-run mode to preview without making changes.
|
|
*/
|
|
class SetupLabels extends CliFramework
|
|
{
|
|
private ?GitPlatformAdapter $adapter = null;
|
|
/**
|
|
* Label definitions — [name, hexColor (no #), description].
|
|
*
|
|
* @var list<array{0: string, 1: string, 2: string}>
|
|
*/
|
|
private const LABELS = [
|
|
// Project Type
|
|
['joomla', '7F52FF', 'Joomla extension or component'],
|
|
['dolibarr', 'FF6B6B', 'Dolibarr module or extension'],
|
|
['generic', '808080', 'Generic project or library'],
|
|
|
|
// Language
|
|
['php', '4F5D95', 'PHP code changes'],
|
|
['javascript', 'F7DF1E', 'JavaScript code changes'],
|
|
['typescript', '3178C6', 'TypeScript code changes'],
|
|
['python', '3776AB', 'Python code changes'],
|
|
['css', '1572B6', 'CSS/styling changes'],
|
|
['html', 'E34F26', 'HTML template changes'],
|
|
|
|
// Component
|
|
['documentation', '0075CA', 'Documentation changes'],
|
|
['ci-cd', '000000', 'CI/CD pipeline changes'],
|
|
['docker', '2496ED', 'Docker configuration changes'],
|
|
['tests', '00FF00', 'Test suite changes'],
|
|
['security', 'FF0000', 'Security-related changes'],
|
|
['dependencies', '0366D6', 'Dependency updates'],
|
|
['config', 'F9D0C4', 'Configuration file changes'],
|
|
['build', 'FFA500', 'Build system changes'],
|
|
|
|
// Workflow / Process
|
|
['automation', '8B4513', 'Automated processes or scripts'],
|
|
['mokostandards', 'B60205', 'MokoStandards compliance'],
|
|
['needs-review', 'FBCA04', 'Awaiting code review'],
|
|
['work-in-progress', 'D93F0B', 'Work in progress, not ready for merge'],
|
|
['breaking-change', 'D73A4A', 'Breaking API or functionality change'],
|
|
|
|
// Priority
|
|
['priority: critical', 'B60205', 'Critical priority, must be addressed immediately'],
|
|
['priority: high', 'D93F0B', 'High priority'],
|
|
['priority: medium', 'FBCA04', 'Medium priority'],
|
|
['priority: low', '0E8A16', 'Low priority'],
|
|
|
|
// Type
|
|
['type: bug', 'D73A4A', "Something isn't working"],
|
|
['type: feature', 'A2EEEF', 'New feature or request'],
|
|
['type: enhancement', '84B6EB', 'Enhancement to existing feature'],
|
|
['type: refactor', 'F9D0C4', 'Code refactoring'],
|
|
['type: chore', 'FEF2C0', 'Maintenance tasks'],
|
|
|
|
// Status
|
|
['status: pending', 'FBCA04', 'Pending action or decision'],
|
|
['status: in-progress', '0E8A16', 'Currently being worked on'],
|
|
['status: blocked', 'B60205', 'Blocked by another issue or dependency'],
|
|
['status: on-hold', 'D4C5F9', 'Temporarily on hold'],
|
|
['status: wontfix', 'FFFFFF', 'This will not be worked on'],
|
|
|
|
// Size
|
|
['size/xs', 'C5DEF5', 'Extra small change (1-10 lines)'],
|
|
['size/s', '6FD1E2', 'Small change (11-30 lines)'],
|
|
['size/m', 'F9DD72', 'Medium change (31-100 lines)'],
|
|
['size/l', 'FFA07A', 'Large change (101-300 lines)'],
|
|
['size/xl', 'FF6B6B', 'Extra large change (301-1000 lines)'],
|
|
['size/xxl', 'B60205', 'Extremely large change (1000+ lines)'],
|
|
|
|
// Health
|
|
['health: excellent', '0E8A16', 'Health score 90-100'],
|
|
['health: good', 'FBCA04', 'Health score 70-89'],
|
|
['health: fair', 'FFA500', 'Health score 50-69'],
|
|
['health: poor', 'FF6B6B', 'Health score below 50'],
|
|
|
|
// Sync / Automation
|
|
['standards-update', 'B60205', 'MokoStandards sync update'],
|
|
['standards-drift', 'FBCA04', 'Repository drifted from MokoStandards'],
|
|
['sync-report', '0075CA', 'Bulk sync run report'],
|
|
['sync-failure', 'D73A4A', 'Bulk sync failure requiring attention'],
|
|
['push-failure', 'D73A4A', 'File push failure requiring attention'],
|
|
['health-check', '0E8A16', 'Repository health check results'],
|
|
['version-drift', 'FFA500', 'Version mismatch detected'],
|
|
['deploy-failure', 'CC0000', 'Automated deploy failure tracking'],
|
|
['template-validation-failure', 'D73A4A', 'Template workflow validation failure'],
|
|
['version', '0E8A16', 'Version bump or release'],
|
|
['type: version', '0E8A16', 'Version-related change'],
|
|
|
|
// Testing
|
|
['type: test', '00FF00', 'Test suite additions or changes'],
|
|
['needs-testing', 'FBCA04', 'Requires manual or automated testing'],
|
|
['test-failure', 'D73A4A', 'Automated test failure'],
|
|
['regression', 'B60205', 'Regression from a previous working state'],
|
|
|
|
// Version & Release
|
|
['type: release', '0E8A16', 'Release preparation or tracking'],
|
|
['release-candidate', 'BFD4F2', 'Release candidate build'],
|
|
['minor-release', '0E8A16', 'Minor version release (XX.YY.00)'],
|
|
['patch-release', 'C5DEF5', 'Patch version release (XX.YY.ZZ)'],
|
|
['major-release', 'B60205', 'Major version release (breaking changes)'],
|
|
['version-branch', '1D76DB', 'Version branch related'],
|
|
];
|
|
|
|
/**
|
|
* Configure available arguments.
|
|
*/
|
|
protected function configure(): void
|
|
{
|
|
$this->setDescription('REQUIRED: Deploy standard labels to repository');
|
|
$this->addArgument('--dry-run', 'Show what would be created without actually creating labels', false);
|
|
$this->addArgument('--org', 'Organization name', 'mokoconsulting-tech');
|
|
$this->addArgument('--repo', 'Repository name (defaults to current repo)', '');
|
|
}
|
|
|
|
/**
|
|
* Run the label deployment.
|
|
*
|
|
* @return int Exit code: 0 on success, 1 on error.
|
|
*/
|
|
protected function run(): int
|
|
{
|
|
$dryRun = (bool) $this->getArgument('--dry-run');
|
|
|
|
$config = Config::load();
|
|
try {
|
|
$this->adapter = PlatformAdapterFactory::create($config);
|
|
} catch (\RuntimeException $e) {
|
|
$this->log('ERROR', $e->getMessage());
|
|
return 1;
|
|
}
|
|
|
|
$orgArg = (string) $this->getArgument('--org');
|
|
$repoArg = (string) $this->getArgument('--repo');
|
|
$org = $orgArg ?: $config->getString($this->adapter->getPlatformName() . '.organization', 'mokoconsulting-tech');
|
|
$repo = $repoArg ?: basename(getcwd() ?: '.');
|
|
|
|
$this->log('INFO', "Setting up labels for repository: {$org}/{$repo} ({$this->adapter->getPlatformName()})");
|
|
|
|
echo "\n";
|
|
|
|
$this->deployGroup('Creating REQUIRED project type labels...', 0, 2, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED language labels...', 3, 8, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED component labels...', 9, 16, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED workflow labels...', 17, 21, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED priority labels...', 22, 25, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED type labels...', 26, 30, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED status labels...', 31, 35, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED size labels...', 36, 41, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED health labels...', 42, 45, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED sync/automation labels...', 46, 56, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED testing labels...', 57, 60, $org, $repo, $dryRun);
|
|
$this->deployGroup('Creating REQUIRED version/release labels...', 61, 66, $org, $repo, $dryRun);
|
|
|
|
echo "\n============================================================\n";
|
|
if ($dryRun) {
|
|
$this->log('INFO', '[DRY-RUN] Label deployment simulation completed');
|
|
} else {
|
|
$this->log('INFO', 'Label deployment completed successfully!');
|
|
echo "\n - TOTAL: " . count(self::LABELS) . " labels\n";
|
|
}
|
|
echo "============================================================\n\n";
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ── Private helpers ───────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Deploy a named group of labels by index range in self::LABELS.
|
|
*
|
|
* @param string $heading Informational banner printed before the group.
|
|
* @param int $fromIndex First label index (inclusive).
|
|
* @param int $toIndex Last label index (inclusive).
|
|
* @param string $org Organization name.
|
|
* @param string $repo Repository name.
|
|
* @param bool $dryRun When true, preview only.
|
|
*/
|
|
private function deployGroup(string $heading, int $fromIndex, int $toIndex, string $org, string $repo, bool $dryRun): void
|
|
{
|
|
$this->log('INFO', $heading);
|
|
for ($i = $fromIndex; $i <= $toIndex; $i++) {
|
|
[$name, $color, $desc] = self::LABELS[$i];
|
|
$this->createLabelViaApi($name, $color, $desc, $org, $repo, $dryRun);
|
|
}
|
|
echo "\n";
|
|
}
|
|
|
|
/**
|
|
* Create or update a single label via the platform adapter.
|
|
*
|
|
* @param string $name Label name.
|
|
* @param string $color Hex colour without the leading '#'.
|
|
* @param string $desc Short description text.
|
|
* @param string $org Organization name.
|
|
* @param string $repo Repository name.
|
|
* @param bool $dryRun When true, preview only.
|
|
*/
|
|
private function createLabelViaApi(string $name, string $color, string $desc, string $org, string $repo, bool $dryRun): void
|
|
{
|
|
if ($dryRun) {
|
|
echo "[DRY-RUN] Would create label: {$name} (color: #{$color}, description: {$desc})\n";
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$this->adapter->createLabel($org, $repo, $name, $color, $desc);
|
|
$this->log('INFO', "Created/updated label: {$name}");
|
|
} catch (\Exception $e) {
|
|
// Label may already exist — that's fine
|
|
if (str_contains($e->getMessage(), '422') || str_contains($e->getMessage(), 'already exists')) {
|
|
$this->log('INFO', "Label already exists: {$name}");
|
|
} else {
|
|
$this->log('WARNING', "Failed to create label: {$name} — " . $e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$script = new SetupLabels('setup_labels', 'REQUIRED: Deploy standard labels to repository');
|
|
exit($script->execute());
|