"""构建后端统一接口与共享逻辑。""" from dataclasses import dataclass class BackendError(Exception): """触发构建失败时抛出,message 为可直接展示给用户的中文提示。""" def __init__(self, message, *, kind='error'): super().__init__(message) self.message = message # rejected=平台明确拒绝(4xx/5xx);network=连不上平台 self.kind = kind @dataclass class DispatchResult: run_id: object = None # 平台返回的运行编号(int 或 None) html_url: str = '' # 平台上的运行详情页地址 @dataclass class RunInfo: finished: bool # 是否已进入终态 status: str = '' # 终态结论:success/failure/cancelled/skipped;未结束为进行中状态 html_url: str = '' # rdgen 目标平台 -> 工作流文件名(两平台同名) WORKFLOW_FILES = { 'windows': 'generator-windows.yml', 'windows-x86': 'generator-windows-x86.yml', 'linux': 'generator-linux.yml', 'android': 'generator-android.yml', 'macos': 'generator-macos.yml', } class BuildBackend: """各构建平台实现此接口。所有配置均通过 app_settings 实时读取。""" name = '' label = '' # ---- 配置健康度(后台页 / 全局告警条使用) ---- def configured(self) -> bool: raise NotImplementedError def warnings(self) -> list: """配置隐患的中文提示列表;通用项(GENURL/ZIP_PASSWORD)各实现自行包含。""" raise NotImplementedError # ---- 构建任务 ---- def workflow_file_for(self, platform, selfhosted=False) -> str: raise NotImplementedError def dispatch(self, workflow_file, inputs, *, timeout=20) -> DispatchResult: """触发工作流。inputs 为完整输入字典(GitHub/Gitea 均为 {ref, inputs})。""" raise NotImplementedError def get_run(self, run_id, *, timeout=12): """查询单次运行;网络异常或非 200 返回 None(调用方按「仍在进行」处理)。""" raise NotImplementedError def get_runs_batch(self, run_ids, *, timeout=12): """批量查询多个运行,返回 {str(run_id): RunInfo}。 列表页一次刷新多个在途任务时使用;平台不支持或查询失败返回 None, 调用方退化为逐个 get_run。返回字典中缺失的 id 由调用方逐个兜底。 """ return None def cancel_run(self, run_id, *, timeout=15): """请求平台取消一次运行;成功无返回,已结束时抛 BackendError(kind='finished')。""" raise NotImplementedError def log_url(self, run_id) -> str: raise NotImplementedError # ---- 后台连接测试 ---- def test_connection(self) -> dict: """返回 {'ok': bool, 'message': 中文说明},不抛异常。""" raise NotImplementedError # ---- 共享的配置检查 ---- def _shared_warnings(self, *, include_sh_secret=False): """两平台共用的回调/加密检查。""" from .. import app_settings warnings = [] if not app_settings.get_value('GENURL').strip(): warnings.append('平台外部访问地址(GENURL)未配置,构建机将无法回传构建产物。') zip_password = app_settings.get_value('ZIP_PASSWORD') if zip_password in ('', 'insecure'): warnings.append( '配置压缩包密码为空或仍为默认值 insecure,建议设置随机强密码,' f'并同步更新{self.label}仓库 Secret。' ) if include_sh_secret and app_settings.get_value('SH_SECRET') in ('', 'secret'): warnings.append('自托管 Runner 密钥为空或仍为默认值 secret;如使用自托管 Runner,建议设置独立强密钥,避免被冒用触发。') return warnings