Initial commit: rdgen with GitHub/Gitea dual build backend

This commit is contained in:
naeeo
2026-09-29 19:56:02 +08:00
commit a3686a49d4
103 changed files with 20007 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
"""GitHub Actions 构建后端(迁自 views.py / web_views.py 的原有逻辑,行为保持一致)。"""
from .. import app_settings, ghnet
from .base import BackendError, DispatchResult, RunInfo, WORKFLOW_FILES, BuildBackend
API = 'https://api.github.com'
# 未进入 completed 的状态一律视为进行中
_GITHUB_ACTIVE = {'queued', 'in_progress', 'waiting', 'requested', 'pending'}
class GitHubBackend(BuildBackend):
name = 'github'
label = 'GitHub'
# ---- 配置 ----
def configured(self):
return bool(app_settings.get_value('GHUSER').strip()) \
and bool(app_settings.get_value('GHBEARER').strip())
def warnings(self):
warnings = []
if not app_settings.get_value('GHUSER').strip():
warnings.append('GitHub 用户名未配置,无法触发构建。')
if not app_settings.get_value('GHBEARER').strip():
warnings.append('GitHub 访问令牌未配置,无法触发构建。')
warnings.extend(self._shared_warnings(include_sh_secret=True))
return warnings
# ---- 工作流选择(windows selfhosted 走 sh-generator-windows.yml) ----
def workflow_file_for(self, platform, selfhosted=False):
if selfhosted and platform in ('windows',):
return 'sh-generator-windows.yml'
return WORKFLOW_FILES.get(platform, 'generator-windows.yml')
# ---- 触发 ----
def dispatch(self, workflow_file, inputs, *, timeout=20):
user = app_settings.get_value('GHUSER')
repo = app_settings.get_value('REPONAME')
url = f'{API}/repos/{user}/{repo}/actions/workflows/{workflow_file}/dispatches'
payload = {
'ref': app_settings.get_value('GHBRANCH'),
'inputs': inputs,
'return_run_details': True,
}
token = app_settings.get_value('GHBEARER')
try:
resp = ghnet.post(url, json=payload, token=token, timeout=timeout)
except Exception as e:
raise BackendError(f'无法连接 GitHub 构建服务:{e}', kind='network')
if resp.status_code not in (200, 204):
raise BackendError('GitHub 构建服务拒绝了启动请求,请检查服务配置或稍后重试。', kind='rejected')
data = {}
try:
data = resp.json()
except ValueError:
pass
return DispatchResult(run_id=data.get('workflow_run_id'), html_url=data.get('html_url') or '')
# ---- 状态查询 ----
def get_run(self, run_id, *, timeout=12):
user = app_settings.get_value('GHUSER')
repo = app_settings.get_value('REPONAME')
url = f'{API}/repos/{user}/{repo}/actions/runs/{run_id}'
try:
resp = ghnet.get(url, token=app_settings.get_value('GHBEARER'), timeout=timeout)
except Exception as e:
print(f'查询 GitHub 状态出错:{e}')
return None
if resp.status_code != 200:
return None
data = resp.json()
if data.get('status') == 'completed':
return RunInfo(finished=True, status=data.get('conclusion') or 'failure',
html_url=data.get('html_url') or self.log_url(run_id))
return RunInfo(finished=False, status=data.get('status') or 'in_progress',
html_url=data.get('html_url') or self.log_url(run_id))
# ---- 日志链接 ----
def log_url(self, run_id):
if not run_id:
return ''
return (
f"https://github.com/{app_settings.get_value('GHUSER')}"
f"/{app_settings.get_value('REPONAME')}/actions/runs/{run_id}"
)
# ---- 连接测试 ----
def test_connection(self):
user = app_settings.get_value('GHUSER').strip()
repo = app_settings.get_value('REPONAME').strip()
token = app_settings.get_value('GHBEARER').strip()
if not user or not token:
return {'ok': False, 'message': 'GitHub 用户名或访问令牌为空,请先保存配置。'}
try:
resp = ghnet.get(f'{API}/repos/{user}/{repo}', token=token, timeout=10)
if resp.status_code == 401:
return {'ok': False, 'message': '令牌无效或已过期(GitHub 返回 401)。'}
if resp.status_code == 404:
return {'ok': False,
'message': f'找不到仓库 {user}/{repo},或令牌无权访问该仓库(请确认仓库名与令牌的 Repository access)。'}
if resp.status_code != 200:
return {'ok': False, 'message': f'GitHub 返回异常状态码:{resp.status_code}。'}
act = ghnet.get(f'{API}/repos/{user}/{repo}/actions/workflows', token=token, timeout=10)
if act.status_code == 200:
return {'ok': True, 'message': f'连接正常:令牌可访问仓库 {user}/{repo},且具备 Actions 权限。'}
return {'ok': False,
'message': '可以访问仓库,但令牌缺少 Actions 权限;请编辑令牌,授予 Actions 与 Workflows 的读写权限。'}
except Exception as e:
return {'ok': False,
'message': f'无法连接 GitHub API:{e}(如服务器在国内网络,请在下方配置代理后重试)'}