c6da98289b
Update DEFGROUP and INGROUP fields across all CLI scripts to reflect the repo rename from MokoStandards to moko-platform. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
158 lines
5.5 KiB
PHP
158 lines
5.5 KiB
PHP
#!/usr/bin/env php
|
|
<?php
|
|
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* 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/archive_repo.php
|
|
* BRIEF: Gracefully retire a governed repository — archive, close issues/PRs, remove sync def
|
|
*
|
|
* USAGE
|
|
* php cli/archive_repo.php --repo MokoOldModule
|
|
* php cli/archive_repo.php --repo MokoOldModule --dry-run
|
|
* php cli/archive_repo.php --repo MokoOldModule --skip-close # Archive only, keep issues open
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
use MokoEnterprise\Config;
|
|
use MokoEnterprise\PlatformAdapterFactory;
|
|
|
|
$dryRun = in_array('--dry-run', $argv);
|
|
$skipClose = in_array('--skip-close', $argv);
|
|
|
|
$repoName = null;
|
|
|
|
foreach ($argv as $i => $arg) {
|
|
if ($arg === '--repo' && isset($argv[$i + 1])) { $repoName = $argv[$i + 1]; }
|
|
}
|
|
|
|
if (!$repoName) {
|
|
fwrite(STDERR, "Usage: php archive_repo.php --repo <RepoName> [--skip-close] [--dry-run]\n");
|
|
exit(2);
|
|
}
|
|
|
|
$config = Config::load();
|
|
$adapter = PlatformAdapterFactory::create($config);
|
|
$org = $config->getString(
|
|
$adapter->getPlatformName() . '.organization',
|
|
'mokoconsulting-tech'
|
|
);
|
|
|
|
$repoRoot = dirname(__DIR__, 2);
|
|
$platformName = $adapter->getPlatformName();
|
|
|
|
echo "Archiving repository: {$org}/{$repoName} (on {$platformName})\n\n";
|
|
|
|
// ── Step 1: Verify repo exists ──────────────────────────────────────────
|
|
echo "Step 1: Verifying repository...\n";
|
|
try {
|
|
$repoData = $adapter->getRepo($org, $repoName);
|
|
} catch (\Exception $e) {
|
|
fwrite(STDERR, " Repository {$org}/{$repoName} not found: " . $e->getMessage() . "\n");
|
|
exit(1);
|
|
}
|
|
if ($repoData['archived'] ?? false) {
|
|
echo " Already archived — nothing to do\n";
|
|
exit(0);
|
|
}
|
|
echo " Found: " . ($repoData['html_url'] ?? "{$org}/{$repoName}") . "\n";
|
|
|
|
// ── Step 2: Close all open PRs ──────────────────────────────────────────
|
|
if (!$skipClose) {
|
|
echo "Step 2: Closing open pull requests...\n";
|
|
$prs = $adapter->listPullRequests($org, $repoName, ['state' => 'open']);
|
|
$prCount = count($prs);
|
|
echo " Found {$prCount} open PRs\n";
|
|
|
|
foreach ($prs as $pr) {
|
|
$num = $pr['number'];
|
|
if (!$dryRun) {
|
|
$adapter->updatePullRequest($org, $repoName, $num, ['state' => 'closed']);
|
|
$adapter->addIssueComment($org, $repoName, $num,
|
|
"Closed as part of repository archival. This repository is being retired.\n\n*Auto-closed by `archive_repo.php`*"
|
|
);
|
|
}
|
|
echo " Closed PR #{$num}: {$pr['title']}\n";
|
|
}
|
|
|
|
// ── Step 3: Close all open issues ───────────────────────────────────
|
|
echo "Step 3: Closing open issues...\n";
|
|
$issues = $adapter->listIssues($org, $repoName, ['state' => 'open']);
|
|
$issues = array_filter($issues, fn($i) => !isset($i['pull_request']));
|
|
$issueCount = count($issues);
|
|
echo " Found {$issueCount} open issues\n";
|
|
|
|
foreach ($issues as $issue) {
|
|
$num = $issue['number'];
|
|
if (!$dryRun) {
|
|
$adapter->closeIssue($org, $repoName, $num);
|
|
$adapter->addIssueComment($org, $repoName, $num,
|
|
"Closed as part of repository archival.\n\n*Auto-closed by `archive_repo.php`*"
|
|
);
|
|
}
|
|
echo " Closed issue #{$num}: {$issue['title']}\n";
|
|
}
|
|
} else {
|
|
echo "Step 2-3: Skipping issue/PR closure (--skip-close)\n";
|
|
}
|
|
|
|
// ── Step 4: Archive the repository ──────────────────────────────────────
|
|
echo "Step 4: Archiving repository...\n";
|
|
if (!$dryRun) {
|
|
try {
|
|
$adapter->archiveRepo($org, $repoName);
|
|
echo " Repository archived\n";
|
|
} catch (\Exception $e) {
|
|
echo " Failed to archive: " . $e->getMessage() . "\n";
|
|
}
|
|
} else {
|
|
echo " (dry-run) would archive {$org}/{$repoName}\n";
|
|
}
|
|
|
|
// ── Step 5: Remove sync definition ──────────────────────────────────────
|
|
echo "Step 5: Removing sync definition...\n";
|
|
$defFile = "{$repoRoot}/definitions/sync/{$repoName}.def.tf";
|
|
if (file_exists($defFile)) {
|
|
if (!$dryRun) {
|
|
unlink($defFile);
|
|
echo " Removed: {$defFile}\n";
|
|
} else {
|
|
echo " (dry-run) would remove {$defFile}\n";
|
|
}
|
|
} else {
|
|
echo " No sync definition found\n";
|
|
}
|
|
|
|
// ── Step 6: Create archival record ──────────────────────────────────────
|
|
echo "Step 6: Creating archival record...\n";
|
|
if (!$dryRun) {
|
|
$now = gmdate('Y-m-d H:i:s') . ' UTC';
|
|
try {
|
|
$issue = $adapter->createIssue($org, 'MokoStandards',
|
|
"chore: archived repository {$repoName}",
|
|
"## Repository Archived\n\n**Repository:** `{$org}/{$repoName}`\n**Archived:** {$now}\n**Platform:** {$platformName}\n**Sync definition removed:** yes\n\n---\n*Auto-created by `archive_repo.php`*\n",
|
|
[
|
|
'labels' => ['type: chore', 'automation', 'archived'],
|
|
'assignees' => ['jmiller'],
|
|
]
|
|
);
|
|
if (isset($issue['number'])) { echo " Archival record: MokoStandards#{$issue['number']}\n"; }
|
|
} catch (\Exception $e) {
|
|
echo " Warning: could not create archival record: " . $e->getMessage() . "\n";
|
|
}
|
|
} else {
|
|
echo " (dry-run) would create archival record issue\n";
|
|
}
|
|
|
|
echo "\n" . str_repeat('-', 50) . "\n";
|
|
echo "Repository {$org}/{$repoName} archived successfully\n";
|