diff --git a/tools/gitea.ts b/tools/gitea.ts new file mode 100644 index 0000000..f97473e --- /dev/null +++ b/tools/gitea.ts @@ -0,0 +1,169 @@ +/** + * Gitea Automation CLI & API Client for DBK Platform / HomAroma + * Usage: bun tools/gitea.ts [args] + */ + +const GITEA_URL = process.env.GITEA_URL || 'https://git.kittisak.com'; +const GITEA_API = process.env.GITEA_API_URL || `${GITEA_URL}/api/v1`; +const GITEA_TOKEN = process.env.GITEA_TOKEN || ''; +const OWNER = process.env.GITEA_OWNER || 'admin'; +const REPO = process.env.GITEA_REPO || 'homaroma'; + +if (!GITEA_TOKEN) { + console.error('Error: GITEA_TOKEN is not set. Please check .env.local'); + process.exit(1); +} + +const headers = { + 'Authorization': `token ${GITEA_TOKEN}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', +}; + +async function api(path: string, options: RequestInit = {}) { + const url = `${GITEA_API}${path.startsWith('/') ? path : `/${path}`}`; + const res = await fetch(url, { + ...options, + headers: { ...headers, ...(options.headers || {}) }, + }); + + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Gitea API Error (${res.status} ${res.statusText}): ${errorText}`); + } + + const contentType = res.headers.get('content-type'); + if (contentType && contentType.includes('application/json')) { + return await res.json(); + } + return await res.text(); +} + +async function main() { + const [,, cmd, ...args] = process.argv; + + try { + switch (cmd) { + case 'whoami': { + const user = await api('/user'); + console.log(`Logged in as: ${user.username} (${user.email}) [Admin: ${user.is_admin}]`); + break; + } + + case 'repo': { + const repo = await api(`/repos/${OWNER}/${REPO}`); + console.log(`Repository: ${repo.full_name}`); + console.log(`URL: ${repo.html_url}`); + console.log(`Default Branch: ${repo.default_branch}`); + console.log(`Open Issues: ${repo.open_issues_count} | Open PRs: ${repo.open_pr_counter}`); + break; + } + + case 'issues': { + const sub = args[0] || 'list'; + if (sub === 'list') { + const state = args[1] || 'open'; + const issues = await api(`/repos/${OWNER}/${REPO}/issues?state=${state}`); + if (issues.length === 0) { + console.log(`No ${state} issues found.`); + } else { + console.log(`Found ${issues.length} ${state} issues:`); + for (const iss of issues) { + console.log(` #${iss.number} [${iss.state.toUpperCase()}] ${iss.title} (${iss.html_url})`); + } + } + } else if (sub === 'create') { + const title = args[1]; + const body = args[2] || ''; + if (!title) { + console.error('Usage: bun tools/gitea.ts issues create [body]'); + process.exit(1); + } + const issue = await api(`/repos/${OWNER}/${REPO}/issues`, { + method: 'POST', + body: JSON.stringify({ title, body }), + }); + console.log(`Created Issue #${issue.number}: ${issue.title}`); + console.log(`URL: ${issue.html_url}`); + } + break; + } + + case 'prs': { + const sub = args[0] || 'list'; + if (sub === 'list') { + const state = args[1] || 'open'; + const prs = await api(`/repos/${OWNER}/${REPO}/pulls?state=${state}`); + if (prs.length === 0) { + console.log(`No ${state} pull requests found.`); + } else { + console.log(`Found ${prs.length} ${state} pull requests:`); + for (const pr of prs) { + console.log(` #${pr.number} [${pr.state.toUpperCase()}] ${pr.title} (${pr.head.ref} -> ${pr.base.ref})`); + console.log(` URL: ${pr.html_url}`); + } + } + } else if (sub === 'create') { + const [,, head, base, title, ...bodyParts] = args; + const body = bodyParts.join(' ') || ''; + if (!head || !base || !title) { + console.error('Usage: bun tools/gitea.ts prs create <head_branch> <base_branch> <title> [body]'); + process.exit(1); + } + const pr = await api(`/repos/${OWNER}/${REPO}/pulls`, { + method: 'POST', + body: JSON.stringify({ head, base, title, body }), + }); + console.log(`Created Pull Request #${pr.number}: ${pr.title}`); + console.log(`URL: ${pr.html_url}`); + } + break; + } + + case 'releases': { + const sub = args[0] || 'list'; + if (sub === 'list') { + const releases = await api(`/repos/${OWNER}/${REPO}/releases`); + if (releases.length === 0) { + console.log('No releases found.'); + } else { + for (const rel of releases) { + console.log(` ${rel.tag_name} - ${rel.name} (${rel.html_url})`); + } + } + } else if (sub === 'create') { + const tag_name = args[1]; + const name = args[2] || tag_name; + const body = args[3] || ''; + if (!tag_name) { + console.error('Usage: bun tools/gitea.ts releases create <tag_name> [title] [notes]'); + process.exit(1); + } + const rel = await api(`/repos/${OWNER}/${REPO}/releases`, { + method: 'POST', + body: JSON.stringify({ tag_name, name, body }), + }); + console.log(`Created Release ${rel.tag_name}: ${rel.name}`); + console.log(`URL: ${rel.html_url}`); + } + break; + } + + default: { + console.log(`Gitea Helper CLI for ${OWNER}/${REPO}`); + console.log('Available commands:'); + console.log(' bun tools/gitea.ts whoami'); + console.log(' bun tools/gitea.ts repo'); + console.log(' bun tools/gitea.ts issues [list|create]'); + console.log(' bun tools/gitea.ts prs [list|create]'); + console.log(' bun tools/gitea.ts releases [list|create]'); + break; + } + } + } catch (err: any) { + console.error(err.message || err); + process.exit(1); + } +} + +main();