302 lines
11 KiB
PHP
302 lines
11 KiB
PHP
<?php
|
||
// run.php
|
||
require 'vendor/autoload.php';
|
||
use Google\Client;
|
||
use Google\Service\Sheets;
|
||
|
||
$GITEA_URL = 'https://git.catan.com.tw/api/v1';
|
||
$GITEA_TOKEN = '96ed6b6d33931b122c7f12f94153594be0d75b32';
|
||
$SERVICE_KEY = __DIR__ . '/google-service-key.json';
|
||
$SHEET_ID = '1e-8Cj3Szkb-P0lTKQTeeaS_wI4S7KLS4wyzf5PjNyHQ';
|
||
|
||
header('Content-Type: text/event-stream');
|
||
header('Cache-Control: no-cache');
|
||
header('Connection: keep-alive');
|
||
|
||
function logMsg($msg) {
|
||
echo "data: $msg\n\n";
|
||
@ob_flush();
|
||
flush();
|
||
}
|
||
|
||
function getExcelValue($excelRef, $orgInput) {
|
||
global $SERVICE_KEY, $SHEET_ID;
|
||
if (!preg_match('/^excel:(\d+):\$(\w)$/', $excelRef, $m)) return null;
|
||
|
||
$gid = $m[1];
|
||
$targetCol = strtoupper($m[2]);
|
||
$colIndex = ord($targetCol) - 65;
|
||
|
||
try {
|
||
$client = new Client();
|
||
$client->setAuthConfig($SERVICE_KEY);
|
||
$client->addScope(\Google\Service\Sheets::SPREADSHEETS_READONLY);
|
||
$service = new \Google\Service\Sheets($client);
|
||
|
||
$spreadsheet = $service->spreadsheets->get($SHEET_ID);
|
||
$sheetName = null;
|
||
foreach ($spreadsheet->getSheets() as $sheet) {
|
||
if ($sheet->getProperties()->getSheetId() === intval($gid)) {
|
||
$sheetName = $sheet->getProperties()->getTitle();
|
||
break;
|
||
}
|
||
}
|
||
if (!$sheetName) { logMsg("⚠️ 找不到 GID: $gid"); return null; }
|
||
|
||
$response = $service->spreadsheets_values->get($SHEET_ID, $sheetName);
|
||
$values = $response->getValues() ?? [];
|
||
|
||
$lastNonEmptyCol = []; // 保存每個欄位最後非空值
|
||
|
||
foreach ($values as $r => &$row) {
|
||
// 保證列長度至少到目標欄位
|
||
for ($c = 0; $c <= $colIndex; $c++) {
|
||
$cell = $row[$c] ?? '';
|
||
$cell = trim($cell);
|
||
if ($cell !== '') {
|
||
$lastNonEmptyCol[$c] = $cell;
|
||
} else if (isset($lastNonEmptyCol[$c])) {
|
||
$row[$c] = $lastNonEmptyCol[$c]; // 更新列
|
||
} else {
|
||
$row[$c] = ''; // 避免未設定
|
||
}
|
||
// logMsg("DEBUG row={$r} col={$c} value='{$row[$c]}'");
|
||
}
|
||
|
||
$cellOrg = $row[0] ?? '';
|
||
if ($cellOrg === $orgInput) {
|
||
$value = $row[$colIndex] ?? '';
|
||
// logMsg("DEBUG 找到 org='{$orgInput}',回傳 col={$colIndex} 值='{$value}'");
|
||
return $value;
|
||
}
|
||
}
|
||
unset($row);
|
||
|
||
logMsg("⚠️ Excel 找不到 org={$orgInput} 的資料 (sheet='{$sheetName}')");
|
||
return '';
|
||
} catch (\Exception $e) {
|
||
logMsg("⚠️ Excel 讀取錯誤: " . $e->getMessage());
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function fetchJSON($url, $method='GET', $data=null) {
|
||
global $GITEA_TOKEN;
|
||
$ch = curl_init($url);
|
||
$headers = ["Content-Type: application/json"];
|
||
if ($GITEA_TOKEN) $headers[] = "Authorization: token $GITEA_TOKEN";
|
||
|
||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
if ($method === 'POST') curl_setopt($ch, CURLOPT_POST, true);
|
||
if ($method === 'PUT') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||
if ($method === 'PATCH') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||
if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||
|
||
$res = curl_exec($ch);
|
||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
return [$code, json_decode($res, true)];
|
||
}
|
||
|
||
function createTeams($org) {
|
||
global $GITEA_URL;
|
||
$teams = json_decode(file_get_contents('teams.json'), true) ?? [];
|
||
|
||
logMsg("🟢 開始執行 createTeams...");
|
||
|
||
// 先抓全部團隊
|
||
[$codeAll, $allTeams] = fetchJSON("$GITEA_URL/orgs/$org/teams", 'GET');
|
||
$allTeams = $allTeams ?? [];
|
||
|
||
foreach ($teams as $team) {
|
||
$foundTeam = null;
|
||
foreach ($allTeams as $t) {
|
||
if (strcasecmp($t['name'], $team['name']) === 0) {
|
||
$foundTeam = $t;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ($foundTeam) {
|
||
logMsg("🔄 團隊已存在,更新權限: {$team['name']}");
|
||
// 更新權限
|
||
fetchJSON("$GITEA_URL/teams/{$foundTeam['id']}", 'PATCH', [
|
||
'units_map' => $team['units_map'] ?? [],
|
||
'permission' => $team['permission'] ?? 'write'
|
||
]);
|
||
|
||
// 補齊成員
|
||
[$codeMembers, $resMembers] = fetchJSON("$GITEA_URL/teams/{$foundTeam['id']}/members", 'GET');
|
||
$existingMembers = array_column($resMembers ?? [], 'username');
|
||
foreach ($team['members'] ?? [] as $member) {
|
||
if (!in_array($member, $existingMembers)) {
|
||
[$codeAdd, $resAdd] = fetchJSON("$GITEA_URL/teams/{$foundTeam['id']}/members/$member", 'PUT');
|
||
if ($codeAdd === 204) {
|
||
logMsg(" ✅ 新增成員: $member");
|
||
} else {
|
||
logMsg(" ⚠️ 新增成員失敗: $member → " . json_encode($resAdd, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
}
|
||
}
|
||
|
||
} else {
|
||
logMsg("➕ 建立團隊: {$team['name']}");
|
||
[$codeCreate, $resCreate] = fetchJSON("$GITEA_URL/orgs/$org/teams", 'POST', [
|
||
'name' => $team['name'],
|
||
'units_map' => $team['units_map'] ?? [],
|
||
'permission' => $team['permission'] ?? 'write'
|
||
]);
|
||
|
||
if ($codeCreate === 201) {
|
||
$teamId = $resCreate['id'];
|
||
foreach ($team['members'] ?? [] as $member) {
|
||
[$codeAdd, $resAdd] = fetchJSON("$GITEA_URL/teams/$teamId/members/$member", 'PUT');
|
||
if ($codeAdd === 204) {
|
||
logMsg(" ✅ 新增成員: $member");
|
||
} else {
|
||
logMsg(" ⚠️ 新增成員失敗: $member → " . json_encode($resAdd, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
}
|
||
} else {
|
||
logMsg("⚠️ 建立團隊失敗: {$team['name']} → " . json_encode($resCreate, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
}
|
||
}
|
||
|
||
logMsg("✅ 所有團隊設定完成!");
|
||
}
|
||
|
||
function createRepos($org) {
|
||
global $GITEA_URL;
|
||
|
||
$repos = json_decode(file_get_contents('repos.json'), true) ?? [];
|
||
|
||
logMsg("🟢 開始執行 createRepos...");
|
||
|
||
// 先抓全部 repo,避免重複建立
|
||
[$codeAll, $allRepos] = fetchJSON("$GITEA_URL/orgs/$org/repos");
|
||
$allRepos = $allRepos ?? [];
|
||
|
||
foreach ($repos as $repo) {
|
||
$repoExists = false;
|
||
foreach ($allRepos as $r) {
|
||
if ($r['name'] === $repo['name']) {
|
||
$repoExists = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ($repoExists) {
|
||
logMsg("⚠️ 儲存庫已存在,略過建立:{$repo['name']}");
|
||
continue;
|
||
}
|
||
|
||
$payload = [
|
||
'name' => $repo['name'],
|
||
'description' => $repo['description'] ?? '',
|
||
'default_branch' => $repo['default_branch'] ?? 'master',
|
||
'private' => false,
|
||
'auto_init' => true
|
||
];
|
||
|
||
[$codeCreate, $resCreate] = fetchJSON("$GITEA_URL/orgs/$org/repos", 'POST', $payload);
|
||
|
||
if ($codeCreate === 201) {
|
||
$repoName = $resCreate['name'];
|
||
logMsg("✅ 建立儲存庫:{$repoName}");
|
||
|
||
// 加入團隊
|
||
foreach ($repo['teams'] ?? [] as $teamName) {
|
||
[$codeTeams, $teamsList] = fetchJSON("$GITEA_URL/orgs/$org/teams");
|
||
$targetTeam = null;
|
||
foreach ($teamsList ?? [] as $t) {
|
||
if ($t['name'] === $teamName) {
|
||
$targetTeam = $t;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ($targetTeam) {
|
||
[$codeAdd, $resAdd] = fetchJSON("$GITEA_URL/teams/{$targetTeam['id']}/repos/$org/$repoName", 'PUT');
|
||
if (in_array($codeAdd, [200,204])) {
|
||
logMsg(" 👥 已加入團隊:{$teamName}");
|
||
} else {
|
||
logMsg(" ⚠️ 加入團隊失敗:{$teamName} → " . json_encode($resAdd, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
} else {
|
||
logMsg(" ⚠️ 找不到團隊:{$teamName}");
|
||
}
|
||
}
|
||
|
||
// 建立 Issue
|
||
if (!empty($repo['issue'])) {
|
||
$issuePayload = [
|
||
'title' => $repo['issue']['title'] ?? '',
|
||
'body' => $repo['issue']['content'] ?? ''
|
||
];
|
||
[$codeIssue, $resIssue] = fetchJSON("$GITEA_URL/repos/$org/$repoName/issues", 'POST', $issuePayload);
|
||
if ($codeIssue === 201) logMsg(" 📌 已建立 Issue:{$issuePayload['title']}");
|
||
else logMsg(" ⚠️ 建立 Issue 失敗:{$issuePayload['title']} → " . json_encode($resIssue, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
|
||
} else {
|
||
logMsg("⚠️ 建立儲存庫失敗:{$repo['name']} → " . json_encode($resCreate, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
}
|
||
|
||
logMsg("✅ 所有儲存庫處理完成!");
|
||
}
|
||
|
||
function setActions($org) {
|
||
global $GITEA_URL;
|
||
$actions = json_decode(file_get_contents('actions_settings.json'), true) ?? [];
|
||
|
||
logMsg("🟢 開始執行 setActions...");
|
||
|
||
$projectSecrets = $actions['project_settings']['secrets'] ?? [];
|
||
logMsg("🔧 設定 project");
|
||
foreach($projectSecrets as $key=>$value) {
|
||
$finalValue = (is_string($value) && str_starts_with($value,'excel:'))
|
||
? getExcelValue($value,$org)
|
||
: $value;
|
||
if(trim($finalValue)==='') { logMsg(" ⚠️ 略過空值 Project Secret: $key"); continue; }
|
||
|
||
[$code,$res] = fetchJSON("$GITEA_URL/orgs/$org/actions/secrets/$key",'PUT',['data'=>$finalValue]);
|
||
if (in_array($code, [200,204,201])) logMsg(" ✅ Project Secret 設定成功: $key");
|
||
else logMsg(" ⚠️ Project Secret 設定失敗: $key → ".json_encode($res,JSON_UNESCAPED_UNICODE));
|
||
}
|
||
|
||
$reposArr = $actions['repos'] ?? [];
|
||
foreach($reposArr as $repo) {
|
||
$repoName = $repo['name'];
|
||
logMsg("🔧 設定 repo: $repoName");
|
||
$secrets = $repo['secrets'] ?? [];
|
||
foreach($secrets as $key=>$value) {
|
||
$finalValue = (is_string($value) && str_starts_with($value,'excel:'))
|
||
? getExcelValue($value,$org)
|
||
: $value;
|
||
if(trim($finalValue)==='') { logMsg(" ⚠️ 略過空值 Repo Secret: $key"); continue; }
|
||
|
||
[$code,$res] = fetchJSON("$GITEA_URL/repos/$org/$repoName/actions/secrets/$key",'PUT',['data'=>$finalValue]);
|
||
if (in_array($code, [200,204,201])) logMsg(" ✅ Repo Secret 設定成功: $key");
|
||
else logMsg(" ⚠️ Repo Secret 設定失敗: $key → ".json_encode($res,JSON_UNESCAPED_UNICODE));
|
||
}
|
||
}
|
||
logMsg("✅ 所有 Actions 設定完成!");
|
||
}
|
||
|
||
$org = $_GET['org'] ?? '';
|
||
$action = $_GET['action'] ?? '';
|
||
|
||
if (!$org) { logMsg("❌ 請輸入組織名稱"); exit; }
|
||
|
||
switch($action) {
|
||
case 'createTeams': createTeams($org); break;
|
||
case 'createRepos': createRepos($org); break;
|
||
case 'setActions': setActions($org); break;
|
||
default: logMsg("⚠️ 未知操作: $action"); break;
|
||
}
|
||
|
||
logMsg("✅ 任務完成");
|