Initial commit: rdgen with GitHub/Gitea dual build backend
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user