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
+306
View File
@@ -0,0 +1,306 @@
"""
GitHub / 构建相关站点配置的统一读取层。
取值优先级:
1. 后台「GitHub 构建配置」页保存的值(SiteSetting 表)
2. 环境变量(docker-compose / 系统环境,历史部署方式,继续支持)
3. 内置默认值
进程内带 15 秒短缓存,避免每次调用都查库;后台保存后立即失效当前进程缓存,
其他 gunicorn worker 最迟 15 秒后生效。
"""
import os
import time
# 每项:key=配置键 / env=对应环境变量名 / default=内置默认值 / label/help/组 供后台页渲染
SETTING_DEFS = [
{
'key': 'GHUSER',
'env': 'GHUSER',
'default': '',
'label': 'GitHub 用户名',
'group': 'basic',
'sensitive': False,
'help': 'Fork 本项目仓库所在的 GitHub 账号用户名(或组织名)。',
},
{
'key': 'REPONAME',
'env': 'REPONAME',
'default': 'rdgen',
'label': '仓库名称',
'group': 'basic',
'sensitive': False,
'help': 'Fork 后的仓库名,默认为 rdgen;若改名请如实填写。',
},
{
'key': 'GHBRANCH',
'env': 'GHBRANCH',
'default': 'master',
'label': '工作流分支',
'group': 'basic',
'sensitive': False,
'help': '触发 GitHub Actions 时使用的分支名,通常保持 master。',
},
{
'key': 'GHBEARER',
'env': 'GHBEARER',
'default': '',
'label': 'GitHub 访问令牌(Fine-grained PAT)',
'group': 'basic',
'sensitive': True,
'help': '需对该仓库授予 Actions 与 Workflows 的读写权限。页面仅显示保存状态,不会回显明文。',
},
{
'key': 'GENURL',
'env': 'GENURL',
'default': '',
'label': '本平台外部访问地址',
'group': 'callback',
'sensitive': False,
'help': '运行 Actions 的构建机可从网络访问到的本平台地址,例如 https://rdgen.example.com,用于回传构建产物。',
},
{
'key': 'PROTOCOL',
'env': 'PROTOCOL',
'default': 'https',
'label': '平台访问协议',
'group': 'callback',
'sensitive': False,
'choices': [('https', 'https'), ('http', 'http')],
'help': '经 Nginx/Caddy 等 HTTPS 反向代理时保持 https;本地纯 HTTP 部署选 http。',
},
{
'key': 'ZIP_PASSWORD',
'env': 'ZIP_PASSWORD',
'default': 'insecure',
'label': '配置压缩包密码',
'group': 'callback',
'sensitive': True,
'help': '本平台用它加密传给 Actions 的配置包,必须与构建仓库 Secret 中的 ZIP_PASSWORD 完全一致(GitHub 与 Gitea 仓库均需配置)。默认值不安全,建议更换。',
},
{
'key': 'SH_SECRET',
'env': 'SH_SECRET',
'default': 'secret',
'label': '自托管 Runner 密钥',
'group': 'advanced',
'sensitive': True,
'help': '仅在使用自托管 GitHub Runner(rdgen-cli 的 sh_secret_field)时需要,默认值不安全。',
},
{
'key': 'WEBHOOK_SECRET',
'env': 'WEBHOOK_SECRET',
'default': '',
'label': '回调校验密钥',
'group': 'advanced',
'sensitive': True,
'help': '可选。配置后构建平台(GitHub / Gitea)Actions 回调必须携带 X-Webhook-Secret 请求头;留空则不校验(兼容旧工作流)。',
},
{
'key': 'GH_PROXY',
'env': 'GH_PROXY',
'default': '',
'label': 'GitHub 访问代理',
'group': 'network',
'sensitive': True,
'help': (
'本服务器直连 GitHub 失败时填写,例如 http://127.0.0.1:7890;'
'支持 socks5://(需 PySocks)。可包含账密,如 http://user:pass@host:port。'
'留空则直连(仍遵循容器的 HTTPS_PROXY/HTTP_PROXY 环境变量)。'
),
},
# ===== 构建平台选择(GitHub / Gitea 二选一) =====
{
'key': 'BUILD_PLATFORM',
'env': 'BUILD_PLATFORM',
'default': 'github',
'label': '构建平台',
'group': 'platform',
'sensitive': False,
'choices': [('github', 'GitHub Actions'), ('gitea', 'Gitea Actions(自建)')],
'help': (
'选择实际执行构建任务的平台。切换后仅影响新提交的任务,'
'历史任务仍在原平台查询状态与日志。Gitea 需自建实例并自备构建机(Linux/Windows/macOS)。'
),
},
# ===== Gitea 连接(BUILD_PLATFORM=gitea 时生效) =====
{
'key': 'GITEA_SERVER_URL',
'env': 'GITEA_SERVER_URL',
'default': '',
'label': 'Gitea 服务器地址',
'group': 'gitea',
'sensitive': False,
'help': '自建 Gitea 的访问地址,例如 https://gitea.example.com(不带末尾斜杠);建议版本不低于 1.27。',
},
{
'key': 'GITEA_OWNER',
'env': 'GITEA_OWNER',
'default': '',
'label': 'Gitea 仓库属主',
'group': 'gitea',
'sensitive': False,
'help': '存放构建仓库的 Gitea 用户名或组织名。',
},
{
'key': 'GITEA_REPO',
'env': 'GITEA_REPO',
'default': 'rdgen',
'label': 'Gitea 仓库名称',
'group': 'gitea',
'sensitive': False,
'help': 'Gitea 上的构建仓库名,默认 rdgen;仓库需在设置中启用 Repository Actions。',
},
{
'key': 'GITEA_BRANCH',
'env': 'GITEA_BRANCH',
'default': 'master',
'label': 'Gitea 工作流分支',
'group': 'gitea',
'sensitive': False,
'help': '触发 Gitea Actions 时使用的分支名,通常保持 master。',
},
{
'key': 'GITEA_TOKEN',
'env': 'GITEA_TOKEN',
'default': '',
'label': 'Gitea 访问令牌',
'group': 'gitea',
'sensitive': True,
'help': '在 Gitea 个人设置中生成的 API 令牌,需对构建仓库授予 Actions 读写权限。页面仅显示保存状态,不会回显明文。',
},
{
'key': 'GITEA_PROXY',
'env': 'GITEA_PROXY',
'default': '',
'label': 'Gitea 访问代理',
'group': 'gitea',
'sensitive': True,
'help': (
'服务器访问 Gitea 需要代理时填写;Gitea 通常部署在同内网,多数情况留空直连即可。'
),
},
# ===== 任务维护(后台「维护设置」页使用,不渲染在 GitHub 配置页) =====
{
'key': 'BUILD_RETENTION_DAYS',
'env': 'BUILD_RETENTION_DAYS',
'default': '30',
'label': '构建任务保留天数',
'group': 'maintenance',
'sensitive': False,
'help': '超过该天数的已结束任务(成功/失败/取消)及其安装包产物会被自动清理;0 表示永不自动清理。',
},
{
'key': 'DB_BACKUP_ENABLED',
'env': 'DB_BACKUP_ENABLED',
'default': '1',
'label': '启用数据库定时备份',
'group': 'maintenance',
'sensitive': False,
'help': '开启后维护进程每天自动备份一次数据库(含账号、配置方案、构建记录)。',
},
{
'key': 'DB_BACKUP_KEEP',
'env': 'DB_BACKUP_KEEP',
'default': '7',
'label': '数据库备份保留份数',
'group': 'maintenance',
'sensitive': False,
'help': '超出份数的最旧备份会被自动删除,建议 7~30。',
},
]
DEFS_BY_KEY = {d['key']: d for d in SETTING_DEFS}
SOURCE_DB = 'db' # 后台配置
SOURCE_ENV = 'env' # 环境变量
SOURCE_DEFAULT = 'default' # 内置默认值
SOURCE_LABELS = {
SOURCE_DB: '后台配置',
SOURCE_ENV: '环境变量',
SOURCE_DEFAULT: '默认值',
}
_CACHE_TTL = 15.0
_cache = {'ts': 0.0, 'rows': {}}
def _load_rows():
"""一次性读取全部后台配置,带短缓存。"""
now = time.time()
if now - _cache['ts'] > _CACHE_TTL:
try:
from .models import SiteSetting
_cache['rows'] = dict(SiteSetting.objects.values_list('key', 'value'))
except Exception:
# 迁移尚未执行等异常情况下退回环境变量,避免阻断请求
_cache['rows'] = {}
_cache['ts'] = now
return _cache['rows']
def invalidate_cache():
_cache['ts'] = 0.0
def get_source(key):
"""返回某项当前生效值的来源:db / env / default。"""
d = DEFS_BY_KEY[key]
rows = _load_rows()
if key in rows:
return SOURCE_DB
if os.environ.get(d['env']) is not None:
return SOURCE_ENV
return SOURCE_DEFAULT
def get_value(key):
"""按 后台配置 → 环境变量 → 默认值 的优先级返回生效值(始终为字符串)。"""
d = DEFS_BY_KEY[key]
rows = _load_rows()
if key in rows:
return rows[key]
return os.environ.get(d['env'], d['default'])
def is_overridden(key):
return get_source(key) == SOURCE_DB
def set_value(key, value):
"""写入后台配置覆盖。"""
from .models import SiteSetting
SiteSetting.objects.update_or_create(key=key, defaults={'value': value})
invalidate_cache()
def delete_value(key):
"""删除后台覆盖,回退到环境变量/默认值。"""
from .models import SiteSetting
SiteSetting.objects.filter(key=key).delete()
invalidate_cache()
def all_settings():
"""供后台页面渲染:每项定义 + 当前生效值 + 来源。"""
result = []
for d in SETTING_DEFS:
result.append({
**d,
'value': get_value(d['key']),
'source': get_source(d['key']),
})
return result
def is_github_configured():
"""当前所选构建平台是否具备发起构建的最低配置(保留函数名兼容既有调用)。"""
from .build_backends import get_backend
return get_backend().configured()
def config_warnings():
"""返回当前所选构建平台的配置隐患列表(中文),供页面提示。"""
from .build_backends import get_backend
return get_backend().warnings()