feat: 同步构建真实状态并支持在列表停止在途任务

- Gitea 运行未结束时探测作业级结论,修复前置作业失败后可复用工作流父作业/等待标签作业长期挂起导致状态永远构建中

- 打开我的构建/管理仪表盘时主动向构建平台同步在途任务状态;创建超 6 小时仍在途的任务判超时

- 构建中任务列表增加停止按钮(Gitea 需 1.28+ 取消 API,旧版本提示去运行页手动 Cancel),终态任务保留删除

- GitHub/Gitea 后端统一 cancel_run 接口
This commit is contained in:
naeeo
2026-09-29 23:45:25 +08:00
parent 7168c71d34
commit 4fd930aa0b
10 changed files with 199 additions and 13 deletions
+56 -3
View File
@@ -31,7 +31,12 @@ def staff_required(view_func):
@login_required
def my_builds(request):
builds = GithubRun.objects.filter(created_by=request.user)
builds = list(GithubRun.objects.filter(created_by=request.user))
# 列表打开时向构建平台同步在途任务的真实状态(Gitea 可能不主动回调终态)
from .views import refresh_active_run
for run in builds:
if not run.is_finished():
refresh_active_run(run)
return render(request, 'my_builds.html', {'builds': builds})
@@ -48,7 +53,7 @@ def delete_my_build(request, run_id):
"""用户删除自己的已结束任务;进行中的任务不允许删除(避免与 GitHub 回调竞态)。"""
run = get_object_or_404(GithubRun, pk=run_id, created_by=request.user)
if not run.is_finished():
messages.error(request, "该任务仍在进行中,请等待结束(或先在 GitHub Actions 中取消)后再删除。")
messages.error(request, "该任务仍在进行中,请先点「停止」取消构建(或等待结束)后再删除。")
else:
_remove_run_artifacts(run)
run.delete()
@@ -56,6 +61,49 @@ def delete_my_build(request, run_id):
return redirect('my_builds')
@login_required
@require_POST
def stop_build(request, run_id):
"""停止自己的在途构建:调用构建平台取消运行,并把任务标记为已取消。"""
run = get_object_or_404(GithubRun, pk=run_id, created_by=request.user)
if run.is_finished():
messages.info(request, "该任务已经结束,无需停止。")
return redirect('my_builds')
if not run.github_run_id:
messages.error(request, "该任务尚未获得构建平台的运行编号,暂时无法停止,请稍后重试。")
return redirect('my_builds')
from . import build_backends
from .build_backends.base import BackendError
backend = build_backends.get_backend(run.backend or 'github')
try:
backend.cancel_run(run.github_run_id)
except BackendError as e:
if e.kind == 'finished':
# 平台侧已结束:立即拉一次真实状态,避免本地仍显示构建中
from .views import refresh_active_run
refresh_active_run(run)
if run.is_finished():
messages.info(request, f"构建平台上该任务实际已结束({run.status_label()}),列表状态已同步。")
else:
messages.error(request, str(e))
elif e.kind == 'unsupported':
# 平台版本不支持远程取消(如 Gitea ≤ 1.27):给出运行页,本地状态保持构建中
log_link = backend.log_url(run.github_run_id)
messages.error(
request,
f"无法远程停止:{e.message} 运行页面:{log_link}",
)
else:
messages.error(request, f"停止失败:{e.message}")
return redirect('my_builds')
run.status = 'cancelled'
run.save(update_fields=['status', 'updated_at'])
messages.success(request, f"已请求{run.backend_label}停止构建任务「{run.filename or run.short_uuid()}」。")
return redirect('my_builds')
@login_required
@require_POST
def retry_build(request, run_id):
@@ -118,7 +166,12 @@ def delete_token(request, key):
@staff_required
def dashboard(request):
builds = GithubRun.objects.select_related('created_by').all()[:100]
builds = list(GithubRun.objects.select_related('created_by').all()[:100])
# 打开仪表盘时同步在途任务的真实状态
from .views import refresh_active_run
for run in builds:
if not run.is_finished():
refresh_active_run(run)
stats = {
'total': GithubRun.objects.count(),
'in_progress': GithubRun.objects.exclude(