feat(actions): add branch filters to run list (#37826)

## Summary

- Add a Branch filter dropdown to the repo Actions run list web UI
- Wire `?branch=` query param through the web handler, matching the
existing REST API filter behavior
- Source the Branch dropdown from the indexed `branch` table (filtering
out deleted branches) instead of scanning `action_run.ref`, addressing
review feedback about unindexed columns

The Event filter was dropped after review: a static list of supported
events was noisy as UX, and querying distinct values from
`action_run.trigger_event` is slow because the column is not indexed.
`FindRunOptions.TriggerEvent` is kept for the REST API.

Closes #25042

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Nicolas
2026-05-26 11:08:05 +02:00
committed by GitHub
parent 4a6db5a7c2
commit a03e0364eb
7 changed files with 140 additions and 13 deletions
+25 -1
View File
@@ -7,7 +7,9 @@ import (
"bytes"
stdCtx "context"
"errors"
"fmt"
"net/http"
"net/url"
"slices"
"strings"
@@ -286,6 +288,7 @@ func prepareWorkflowList(ctx *context.Context, workflows []WorkflowInfo, otherWo
actorID := ctx.FormInt64("actor")
status := ctx.FormInt("status")
workflowID := ctx.FormString("workflow")
branch := ctx.FormString("branch")
page := ctx.FormInt("page")
if page <= 0 {
page = 1
@@ -295,7 +298,8 @@ func prepareWorkflowList(ctx *context.Context, workflows []WorkflowInfo, otherWo
// they will be 0 by default, which indicates get all status or actors
ctx.Data["CurActor"] = actorID
ctx.Data["CurStatus"] = status
if actorID > 0 || status > int(actions_model.StatusUnknown) {
ctx.Data["CurBranch"] = branch
if actorID > 0 || status > int(actions_model.StatusUnknown) || branch != "" {
ctx.Data["IsFiltered"] = true
}
@@ -313,6 +317,9 @@ func prepareWorkflowList(ctx *context.Context, workflows []WorkflowInfo, otherWo
if actions_model.Status(status) != actions_model.StatusUnknown {
opts.Status = []actions_model.Status{actions_model.Status(status)}
}
if branch != "" {
opts.Ref = string(git.RefNameFromBranch(branch))
}
runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)
if err != nil {
@@ -393,6 +400,13 @@ func prepareWorkflowList(ctx *context.Context, workflows []WorkflowInfo, otherWo
ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx, ctx.Locale)
runBranches, err := actions_model.GetRunBranches(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetRunBranches", err)
return
}
ctx.Data["RunBranches"] = runBranches
pager := context.NewPagination(total, opts.PageSize, opts.Page, 5)
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
@@ -509,3 +523,13 @@ func decodeNode(node yaml.Node, out any) bool {
}
return true
}
func actionsListRedirectURL(repoLink, workflow, actor, status, branch string) string {
return fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s&branch=%s",
repoLink,
url.QueryEscape(workflow),
url.QueryEscape(actor),
url.QueryEscape(status),
url.QueryEscape(branch),
)
}
+4 -4
View File
@@ -1103,14 +1103,14 @@ func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
ctx.Flash.Success(ctx.Tr("actions.workflow.disable_success", workflow))
}
redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(workflow),
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))
redirectURL := actionsListRedirectURL(ctx.Repo.RepoLink, workflow,
ctx.FormString("actor"), ctx.FormString("status"), ctx.FormString("branch"))
ctx.JSONRedirect(redirectURL)
}
func Run(ctx *context_module.Context) {
redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(ctx.FormString("workflow")),
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))
redirectURL := actionsListRedirectURL(ctx.Repo.RepoLink, ctx.FormString("workflow"),
ctx.FormString("actor"), ctx.FormString("status"), ctx.FormString("branch"))
workflowID := ctx.FormString("workflow")
if len(workflowID) == 0 {