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("✅ 任務完成");