feat(actions): add job summaries (GITHUB_STEP_SUMMARY) (#37500)

- Add GitHub-style Actions **job summaries** support
(`GITHUB_STEP_SUMMARY` / `workflow/SUMMARY.md`) and render them on the
run Summary view.
- Store uploaded summaries internally in the DB (not as downloadable
artifacts).
- Add runtime-token endpoint for runners to upload summaries:
- `PUT
/api/actions_pipeline/_apis/pipelines/workflows/{run_id}/jobs/{job_id}/summary`
- Advertise support to runners via `RunnerService.Declare` response
header:
  - `X-Gitea-Actions-Capabilities: job-summary`
- Devtest: extend `/devtest/repo-action-view/...` to include mock
`jobSummaries` for previewing UI rendering.

## Compatibility
- New Gitea + old runner: no summary upload → UI shows nothing (no
behavior change)
- New runner + old Gitea: capability not advertised → runner skips
upload (no behavior change)

## Screenshot:

<img width="2017" height="729"
src="https://github.com/user-attachments/assets/31f8b945-50c4-40e1-9f40-382901a53013"
/>


Fixes #23721
PR on gitea-runner https://gitea.com/gitea/runner/pulls/917

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
bircni
2026-06-08 19:11:00 +00:00
committed by GitHub
co-authored by silverwind Claude
parent b1c088e9cf
commit 3b1e75764e
18 changed files with 683 additions and 23 deletions
+16
View File
@@ -15,6 +15,7 @@ import (
actions_model "gitea.dev/models/actions"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"gitea.dev/modules/templates"
"gitea.dev/modules/timeutil"
"gitea.dev/modules/util"
"gitea.dev/modules/web"
@@ -89,6 +90,7 @@ func MockActionsRunsJobs(ctx *context.Context) {
resp.State.Run.CanDeleteArtifact = true
resp.State.Run.WorkflowID = "workflow-id.yml"
resp.State.Run.TriggerEvent = "push"
renderUtils := templates.NewRenderUtils(ctx)
user2, _ := user_model.GetUserByID(ctx, 2)
if user2 == nil {
user2 = &user_model.User{Name: "user2"}
@@ -196,6 +198,20 @@ func MockActionsRunsJobs(ctx *context.Context) {
resp.State.Run.CanRerun = runID == 30 && isLatestAttempt
resp.State.Run.CanRerunFailed = runID == 30 && isLatestAttempt
// Mock job summaries so the devtest page can preview the Summary panel rendering.
resp.State.Run.JobSummaries = []*actions.ViewJobSummary{
{
JobID: runID * 10,
JobName: "job 100 (testsubname)",
SummaryHTML: renderUtils.MarkdownToHtml("### Devtest job summary\n\n- Markdown rendering\n- Links: [example](https://example.com)\n\n```sh\necho hello\n```\n"),
},
{
JobID: runID*10 + 2,
JobName: "ULTRA LOOOOOOOOOOOONG job name 102 that exceeds the limit",
SummaryHTML: renderUtils.MarkdownToHtml("### Another summary\n\nThis demonstrates multiple job summaries in one run.\n\n- Item A\n- Item B\n"),
},
}
resp.Artifacts = append(resp.Artifacts, &actions.ArtifactsViewItem{
Name: "artifact-a",
Size: 100 * 1024,