Skip to content

Commit 4237736

Browse files
workflow_dispatch use workflow from trigger branch (#33098)
* htmx updates the input form on branch switch * add workflow warning to dispatch modal * use name if description of input is empty * show error if workflow_dispatch not available on branch Closes #33073 Closes #33099 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent 3078826 commit 4237736

File tree

6 files changed

+184
-117
lines changed

6 files changed

+184
-117
lines changed

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3765,6 +3765,7 @@ workflow.not_found = Workflow '%s' not found.
37653765
workflow.run_success = Workflow '%s' run successfully.
37663766
workflow.from_ref = Use workflow from
37673767
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger.
3768+
workflow.has_no_workflow_dispatch = Workflow '%s' has no workflow_dispatch event trigger.
37683769

37693770
need_approval_desc = Need approval to run workflows for fork pull request.
37703771

routers/web/repo/actions/actions.go

Lines changed: 131 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ import (
3232
)
3333

3434
const (
35-
tplListActions templates.TplName = "repo/actions/list"
36-
tplViewActions templates.TplName = "repo/actions/view"
35+
tplListActions templates.TplName = "repo/actions/list"
36+
tplDispatchInputsActions templates.TplName = "repo/actions/workflow_dispatch_inputs"
37+
tplViewActions templates.TplName = "repo/actions/view"
3738
)
3839

3940
type Workflow struct {
@@ -64,107 +65,143 @@ func MustEnableActions(ctx *context.Context) {
6465
func List(ctx *context.Context) {
6566
ctx.Data["Title"] = ctx.Tr("actions.actions")
6667
ctx.Data["PageIsActions"] = true
68+
69+
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
70+
if err != nil {
71+
ctx.ServerError("GetBranchCommit", err)
72+
return
73+
}
74+
75+
workflows := prepareWorkflowDispatchTemplate(ctx, commit)
76+
if ctx.Written() {
77+
return
78+
}
79+
80+
prepareWorkflowList(ctx, workflows)
81+
if ctx.Written() {
82+
return
83+
}
84+
85+
ctx.HTML(http.StatusOK, tplListActions)
86+
}
87+
88+
func WorkflowDispatchInputs(ctx *context.Context) {
89+
ref := ctx.FormString("ref")
90+
if ref == "" {
91+
ctx.NotFound("WorkflowDispatchInputs: no ref", nil)
92+
return
93+
}
94+
// get target commit of run from specified ref
95+
refName := git.RefName(ref)
96+
var commit *git.Commit
97+
var err error
98+
if refName.IsTag() {
99+
commit, err = ctx.Repo.GitRepo.GetTagCommit(refName.TagName())
100+
} else if refName.IsBranch() {
101+
commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName.BranchName())
102+
} else {
103+
ctx.ServerError("UnsupportedRefType", nil)
104+
return
105+
}
106+
if err != nil {
107+
ctx.ServerError("GetTagCommit/GetBranchCommit", err)
108+
return
109+
}
110+
prepareWorkflowDispatchTemplate(ctx, commit)
111+
if ctx.Written() {
112+
return
113+
}
114+
ctx.HTML(http.StatusOK, tplDispatchInputsActions)
115+
}
116+
117+
func prepareWorkflowDispatchTemplate(ctx *context.Context, commit *git.Commit) (workflows []Workflow) {
67118
workflowID := ctx.FormString("workflow")
68-
actorID := ctx.FormInt64("actor")
69-
status := ctx.FormInt("status")
70119
ctx.Data["CurWorkflow"] = workflowID
120+
ctx.Data["CurWorkflowExists"] = false
71121

72-
var workflows []Workflow
73122
var curWorkflow *model.Workflow
74-
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
75-
ctx.ServerError("IsEmpty", err)
76-
return
77-
} else if !empty {
78-
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
79-
if err != nil {
80-
ctx.ServerError("GetBranchCommit", err)
81-
return
82-
}
83-
entries, err := actions.ListWorkflows(commit)
84-
if err != nil {
85-
ctx.ServerError("ListWorkflows", err)
86-
return
87-
}
88123

89-
// Get all runner labels
90-
runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
91-
RepoID: ctx.Repo.Repository.ID,
92-
IsOnline: optional.Some(true),
93-
WithAvailable: true,
94-
})
124+
entries, err := actions.ListWorkflows(commit)
125+
if err != nil {
126+
ctx.ServerError("ListWorkflows", err)
127+
return nil
128+
}
129+
130+
// Get all runner labels
131+
runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
132+
RepoID: ctx.Repo.Repository.ID,
133+
IsOnline: optional.Some(true),
134+
WithAvailable: true,
135+
})
136+
if err != nil {
137+
ctx.ServerError("FindRunners", err)
138+
return nil
139+
}
140+
allRunnerLabels := make(container.Set[string])
141+
for _, r := range runners {
142+
allRunnerLabels.AddMultiple(r.AgentLabels...)
143+
}
144+
145+
workflows = make([]Workflow, 0, len(entries))
146+
for _, entry := range entries {
147+
workflow := Workflow{Entry: *entry}
148+
content, err := actions.GetContentFromEntry(entry)
95149
if err != nil {
96-
ctx.ServerError("FindRunners", err)
97-
return
150+
ctx.ServerError("GetContentFromEntry", err)
151+
return nil
98152
}
99-
allRunnerLabels := make(container.Set[string])
100-
for _, r := range runners {
101-
allRunnerLabels.AddMultiple(r.AgentLabels...)
153+
wf, err := model.ReadWorkflow(bytes.NewReader(content))
154+
if err != nil {
155+
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error())
156+
workflows = append(workflows, workflow)
157+
continue
102158
}
103-
104-
workflows = make([]Workflow, 0, len(entries))
105-
for _, entry := range entries {
106-
workflow := Workflow{Entry: *entry}
107-
content, err := actions.GetContentFromEntry(entry)
108-
if err != nil {
109-
ctx.ServerError("GetContentFromEntry", err)
110-
return
111-
}
112-
wf, err := model.ReadWorkflow(bytes.NewReader(content))
113-
if err != nil {
114-
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error())
115-
workflows = append(workflows, workflow)
159+
// The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
160+
hasJobWithoutNeeds := false
161+
// Check whether you have matching runner and a job without "needs"
162+
emptyJobsNumber := 0
163+
for _, j := range wf.Jobs {
164+
if j == nil {
165+
emptyJobsNumber++
116166
continue
117167
}
118-
// The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
119-
hasJobWithoutNeeds := false
120-
// Check whether have matching runner and a job without "needs"
121-
emptyJobsNumber := 0
122-
for _, j := range wf.Jobs {
123-
if j == nil {
124-
emptyJobsNumber++
168+
if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
169+
hasJobWithoutNeeds = true
170+
}
171+
runsOnList := j.RunsOn()
172+
for _, ro := range runsOnList {
173+
if strings.Contains(ro, "${{") {
174+
// Skip if it contains expressions.
175+
// The expressions could be very complex and could not be evaluated here,
176+
// so just skip it, it's OK since it's just a tooltip message.
125177
continue
126178
}
127-
if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
128-
hasJobWithoutNeeds = true
129-
}
130-
runsOnList := j.RunsOn()
131-
for _, ro := range runsOnList {
132-
if strings.Contains(ro, "${{") {
133-
// Skip if it contains expressions.
134-
// The expressions could be very complex and could not be evaluated here,
135-
// so just skip it, it's OK since it's just a tooltip message.
136-
continue
137-
}
138-
if !allRunnerLabels.Contains(ro) {
139-
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_matching_online_runner_helper", ro)
140-
break
141-
}
142-
}
143-
if workflow.ErrMsg != "" {
179+
if !allRunnerLabels.Contains(ro) {
180+
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_matching_online_runner_helper", ro)
144181
break
145182
}
146183
}
147-
if !hasJobWithoutNeeds {
148-
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
149-
}
150-
if emptyJobsNumber == len(wf.Jobs) {
151-
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job")
184+
if workflow.ErrMsg != "" {
185+
break
152186
}
153-
workflows = append(workflows, workflow)
187+
}
188+
if !hasJobWithoutNeeds {
189+
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
190+
}
191+
if emptyJobsNumber == len(wf.Jobs) {
192+
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job")
193+
}
194+
workflows = append(workflows, workflow)
154195

155-
if workflow.Entry.Name() == workflowID {
156-
curWorkflow = wf
157-
}
196+
if workflow.Entry.Name() == workflowID {
197+
curWorkflow = wf
198+
ctx.Data["CurWorkflowExists"] = true
158199
}
159200
}
201+
160202
ctx.Data["workflows"] = workflows
161203
ctx.Data["RepoLink"] = ctx.Repo.Repository.Link()
162204

163-
page := ctx.FormInt("page")
164-
if page <= 0 {
165-
page = 1
166-
}
167-
168205
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
169206
ctx.Data["ActionsConfig"] = actionsConfig
170207

@@ -188,7 +225,7 @@ func List(ctx *context.Context) {
188225
branches, err := git_model.FindBranchNames(ctx, branchOpts)
189226
if err != nil {
190227
ctx.ServerError("FindBranchNames", err)
191-
return
228+
return nil
192229
}
193230
// always put default branch on the top if it exists
194231
if slices.Contains(branches, ctx.Repo.Repository.DefaultBranch) {
@@ -200,12 +237,23 @@ func List(ctx *context.Context) {
200237
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
201238
if err != nil {
202239
ctx.ServerError("GetTagNamesByRepoID", err)
203-
return
240+
return nil
204241
}
205242
ctx.Data["Tags"] = tags
206243
}
207244
}
208245
}
246+
return workflows
247+
}
248+
249+
func prepareWorkflowList(ctx *context.Context, workflows []Workflow) {
250+
actorID := ctx.FormInt64("actor")
251+
status := ctx.FormInt("status")
252+
workflowID := ctx.FormString("workflow")
253+
page := ctx.FormInt("page")
254+
if page <= 0 {
255+
page = 1
256+
}
209257

210258
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
211259
// they will be 0 by default, which indicates get all status or actors
@@ -264,8 +312,6 @@ func List(ctx *context.Context) {
264312
pager.AddParamFromRequest(ctx.Req)
265313
ctx.Data["Page"] = pager
266314
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
267-
268-
ctx.HTML(http.StatusOK, tplListActions)
269315
}
270316

271317
// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.

routers/web/repo/actions/view.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -812,13 +812,8 @@ func Run(ctx *context_module.Context) {
812812
return
813813
}
814814

815-
// get workflow entry from default branch commit
816-
defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
817-
if err != nil {
818-
ctx.Error(http.StatusInternalServerError, err.Error())
819-
return
820-
}
821-
entries, err := actions.ListWorkflows(defaultBranchCommit)
815+
// get workflow entry from runTargetCommit
816+
entries, err := actions.ListWorkflows(runTargetCommit)
822817
if err != nil {
823818
ctx.Error(http.StatusInternalServerError, err.Error())
824819
return

routers/web/web.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,7 @@ func registerRoutes(m *web.Router) {
14121412
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
14131413
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
14141414
m.Post("/run", reqRepoActionsWriter, actions.Run)
1415+
m.Get("/workflow-dispatch-inputs", reqRepoActionsWriter, actions.WorkflowDispatchInputs)
14151416

14161417
m.Group("/runs/{run}", func() {
14171418
m.Combo("").
@@ -1433,7 +1434,7 @@ func registerRoutes(m *web.Router) {
14331434
m.Group("/workflows/{workflow_name}", func() {
14341435
m.Get("/badge.svg", actions.GetWorkflowBadge)
14351436
})
1436-
}, optSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions)
1437+
}, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqRepoActionsReader, actions.MustEnableActions)
14371438
// end "/{username}/{reponame}/actions"
14381439

14391440
m.Group("/{username}/{reponame}/wiki", func() {

templates/repo/actions/workflow_dispatch.tmpl

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<label>{{ctx.Locale.Tr "actions.workflow.from_ref"}}:</label>
1212
</span>
1313
<div class="ui inline field dropdown button select-branch branch-selector-dropdown ellipsis-items-nowrap">
14-
<input type="hidden" name="ref" value="refs/heads/{{index .Branches 0}}">
14+
<input type="hidden" name="ref" hx-sync="this:replace" hx-target="#runWorkflowDispatchModalInputs" hx-swap="innerHTML" hx-get="{{$.Link}}/workflow-dispatch-inputs?workflow={{$.CurWorkflow}}" hx-trigger="change" value="refs/heads/{{index .Branches 0}}">
1515
{{svg "octicon-git-branch" 14}}
1616
<div class="default text">{{index .Branches 0}}</div>
1717
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
@@ -49,30 +49,9 @@
4949

5050
<div class="divider"></div>
5151

52-
{{range $item := .WorkflowDispatchConfig.Inputs}}
53-
<div class="ui field {{if .Required}}required{{end}}">
54-
{{if eq .Type "choice"}}
55-
<label>{{.Description}}:</label>
56-
<select class="ui selection type dropdown" name="{{.Name}}">
57-
{{range .Options}}
58-
<option value="{{.}}" {{if eq $item.Default .}}selected{{end}} >{{.}}</option>
59-
{{end}}
60-
</select>
61-
{{else if eq .Type "boolean"}}
62-
<div class="ui inline checkbox">
63-
<label>{{.Description}}</label>
64-
<input type="checkbox" name="{{.Name}}" {{if eq .Default "true"}}checked{{end}}>
65-
</div>
66-
{{else if eq .Type "number"}}
67-
<label>{{.Description}}:</label>
68-
<input name="{{.Name}}" value="{{.Default}}" {{if .Required}}required{{end}}>
69-
{{else}}
70-
<label>{{.Description}}:</label>
71-
<input name="{{.Name}}" value="{{.Default}}" {{if .Required}}required{{end}}>
72-
{{end}}
52+
<div id="runWorkflowDispatchModalInputs">
53+
{{template "repo/actions/workflow_dispatch_inputs" .}}
7354
</div>
74-
{{end}}
75-
<button class="ui tiny primary button" type="submit">Submit</button>
7655
</form>
7756
</div>
7857
</div>

0 commit comments

Comments
 (0)