Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

207 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* services/zhuyuan-bridge/lib/repo-operator.js
*
* 仓库操作封装 — 通过 GitHub API 读写代码、创建分支和 PR
*
* 纯 Node.js无外部依赖。
*/
'use strict';
const https = require('https');
class RepoOperator {
/**
* @param {string} token — GitHub access token
* @param {string} owner — Repository owner
* @param {string} repo — Repository name
*/
constructor(token, owner, repo) {
this.token = token;
this.owner = owner;
this.repo = repo;
this.baseUrl = `/repos/${owner}/${repo}`;
}
/**
* GitHub API 请求
*/
_request(method, apiPath, body) {
return new Promise((resolve, reject) => {
const options = {
hostname: 'api.github.com',
path: apiPath,
method: method,
headers: {
'User-Agent': 'zhuyuan-bridge/1.0',
'Accept': 'application/vnd.github+json',
'Authorization': `token ${this.token}`,
}
};
if (body) {
options.headers['Content-Type'] = 'application/json';
}
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve({ status: res.statusCode, data: JSON.parse(data) });
} catch (_) {
resolve({ status: res.statusCode, data: data });
}
});
});
req.on('error', reject);
if (body) req.write(JSON.stringify(body));
req.end();
});
}
/**
* 读取文件内容
* @param {string} filePath
* @param {string} ref — branch or commit SHA
* @returns {Promise<{content: string, sha: string}|null>}
*/
async readFile(filePath, ref = 'main') {
const res = await this._request(
'GET',
`${this.baseUrl}/contents/${encodeURIComponent(filePath)}?ref=${encodeURIComponent(ref)}`
);
if (res.status !== 200) return null;
return {
content: Buffer.from(res.data.content, 'base64').toString('utf8'),
sha: res.data.sha
};
}
/**
* 创建或更新文件
* @param {string} filePath
* @param {string} content
* @param {string} message — commit message
* @param {string} branch
* @returns {Promise<object>}
*/
async writeFile(filePath, content, message, branch = 'main') {
let sha;
try {
const existing = await this.readFile(filePath, branch);
if (existing) sha = existing.sha;
} catch (_) { /* new file */ }
const body = {
message,
content: Buffer.from(content).toString('base64'),
branch,
committer: {
name: '铸渊 (ZhùYuān)',
email: 'zhuyuan@guanghulab.com'
}
};
if (sha) body.sha = sha;
const res = await this._request(
'PUT',
`${this.baseUrl}/contents/${encodeURIComponent(filePath)}`,
body
);
return res.data;
}
/**
* 创建分支
* @param {string} branchName
* @param {string} fromBranch
* @returns {Promise<object>}
*/
async createBranch(branchName, fromBranch = 'main') {
const refRes = await this._request(
'GET',
`${this.baseUrl}/git/ref/heads/${encodeURIComponent(fromBranch)}`
);
if (refRes.status !== 200) {
throw new Error(`获取分支 ${fromBranch} 失败: ${refRes.status}`);
}
const res = await this._request(
'POST',
`${this.baseUrl}/git/refs`,
{
ref: `refs/heads/${branchName}`,
sha: refRes.data.object.sha
}
);
return res.data;
}
/**
* 创建 Pull Request
* @param {string} title
* @param {string} body
* @param {string} head — source branch
* @param {string} base — target branch
* @returns {Promise<object>}
*/
async createPR(title, body, head, base = 'main') {
const res = await this._request(
'POST',
`${this.baseUrl}/pulls`,
{ title, body, head, base }
);
return res.data;
}
/**
* 评论 Issue
* @param {number} issueNumber
* @param {string} body
* @returns {Promise<object>}
*/
async commentOnIssue(issueNumber, body) {
const res = await this._request(
'POST',
`${this.baseUrl}/issues/${issueNumber}/comments`,
{ body }
);
return res.data;
}
/**
* 创建 Issue
* @param {string} title
* @param {string} body
* @param {string[]} labels
* @returns {Promise<object>}
*/
async createIssue(title, body, labels = []) {
const res = await this._request(
'POST',
`${this.baseUrl}/issues`,
{ title, body, labels }
);
return res.data;
}
/**
* 列出目录文件
* @param {string} dirPath
* @param {string} ref
* @returns {Promise<Array>}
*/
async listDir(dirPath, ref = 'main') {
const encodedPath = dirPath ? encodeURIComponent(dirPath) : '';
const res = await this._request(
'GET',
`${this.baseUrl}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`
);
if (res.status !== 200) return [];
return Array.isArray(res.data) ? res.data : [];
}
}
module.exports = { RepoOperator };