feat(actions): add support for using workflow run-name as run title

This commit is contained in:
OFHansen 2026-06-17 15:31:36 +02:00 committed by klausfyhn
commit 53ef4ec0bb
4 changed files with 113 additions and 0 deletions

View file

@ -437,6 +437,12 @@ func handleWorkflows(
}
}
if errorCode == 0 {
if err := ConfigureActionRunTitle(jobs, run); err != nil {
log.Error("ConfigureActionRunTitle: %v", err)
}
}
if run.ConcurrencyType == actions_model.CancelInProgress {
if err := CancelPreviousWithConcurrencyGroup(
ctx,

View file

@ -189,6 +189,10 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
return err
}
if err := ConfigureActionRunTitle(workflows, run); err != nil {
return err
}
// Insert the action run and its associated jobs into the database
if err := InsertRun(ctx, run, workflows); err != nil {
return err

View file

@ -197,6 +197,10 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
return nil, nil, err
}
if err := ConfigureActionRunTitle(jobs, run); err != nil {
return nil, nil, err
}
if err := InsertRun(ctx, run, jobs); err != nil {
return run, jobNames, err
}
@ -240,6 +244,23 @@ func GetWorkflowFromCommit(gitRepo *git.Repository, ref, workflowID string) (*Wo
}, nil
}
// Sets Title of a workflow run to the value of the workflow's `run-name` field, if present.
// Keeps the existing default title when run-name is absent or when run-name evaluates to "".
func ConfigureActionRunTitle(workflows []*jobparser.SingleWorkflow, run *actions_model.ActionRun) error {
if len(workflows) == 0 {
return nil
}
// run-name is workflow-level, so each job's SingleWorkflow has the same run-name.
runName, err := workflows[0].EvaluateRunName()
if err != nil {
return fmt.Errorf("unable to evaluate workflow `run-name`: %w", err)
}
if runName != "" {
run.Title = runName
}
return nil
}
// Sets the ConcurrencyGroup & ConcurrencyType on the provided ActionRun based upon the Workflow's `concurrency` data,
// or appropriate defaults if not present.
func ConfigureActionRunConcurrency(workflow *act_model.Workflow, run *actions_model.ActionRun, vars map[string]string, inputs map[string]any) error {

View file

@ -5,17 +5,99 @@ package actions
import (
"errors"
"fmt"
"testing"
actions_model "forgejo.org/models/actions"
"forgejo.org/models/repo"
"forgejo.org/models/user"
"forgejo.org/modules/webhook"
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
act_model "code.forgejo.org/forgejo/runner/v12/act/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConfigureActionRunTitle(t *testing.T) {
const defaultTitle = "default title"
for _, tc := range []struct {
name string
runName string
vars map[string]string
inputs map[string]any
expectedTitle string
}{
{
name: "empty run-name keeps default title",
runName: "",
expectedTitle: defaultTitle,
},
{
name: "plain string",
runName: "deploy to production",
expectedTitle: "deploy to production",
},
{
name: "github context actor",
runName: "deploy by ${{ github.actor }}",
expectedTitle: "deploy by someone",
},
{
name: "inputs",
runName: "deploy to ${{ inputs.environment }}",
inputs: map[string]any{"environment": "staging"},
expectedTitle: "deploy to staging",
},
{
name: "vars",
runName: "build for ${{ vars.region }}",
vars: map[string]string{"region": "eu"},
expectedTitle: "build for eu",
},
{
name: "empty evaluation result keeps default title",
runName: "${{ inputs.does_not_exist }}",
expectedTitle: defaultTitle,
},
} {
t.Run(tc.name, func(t *testing.T) {
run := &actions_model.ActionRun{
Title: defaultTitle,
Ref: "refs/head/main",
WorkflowID: "testing.yml",
Event: webhook.HookEventPush,
TriggerEvent: string(webhook.HookEventPush),
TriggerUser: &user.User{Name: "someone"},
Repo: &repo.Repository{},
}
runNameLine := ""
if tc.runName != "" {
runNameLine = "run-name: " + tc.runName
}
content := fmt.Sprintf(`
name: testing
%s
on: push
jobs:
job:
runs-on: ubuntu
steps: []
`, runNameLine)
workflows, err := jobparser.Parse([]byte(content), false,
jobparser.WithGitContext(generateGiteaContextForRun(run)),
jobparser.WithVars(tc.vars),
jobparser.WithInputs(tc.inputs),
)
require.NoError(t, err)
require.NoError(t, ConfigureActionRunTitle(workflows, run))
assert.Equal(t, tc.expectedTitle, run.Title)
})
}
}
func TestConfigureActionRunConcurrency(t *testing.T) {
for _, tc := range []struct {
name string