@@ -7,22 +7,28 @@ import (
7
7
"bytes"
8
8
"fmt"
9
9
"net/http"
10
+ "slices"
10
11
"strings"
11
12
12
13
actions_model "code.gitea.io/gitea/models/actions"
13
14
"code.gitea.io/gitea/models/db"
15
+ git_model "code.gitea.io/gitea/models/git"
16
+ repo_model "code.gitea.io/gitea/models/repo"
14
17
"code.gitea.io/gitea/models/unit"
15
18
"code.gitea.io/gitea/modules/actions"
16
19
"code.gitea.io/gitea/modules/base"
17
20
"code.gitea.io/gitea/modules/container"
18
21
"code.gitea.io/gitea/modules/git"
22
+ "code.gitea.io/gitea/modules/log"
19
23
"code.gitea.io/gitea/modules/optional"
20
24
"code.gitea.io/gitea/modules/setting"
25
+ "code.gitea.io/gitea/modules/util"
21
26
"code.gitea.io/gitea/routers/web/repo"
22
27
"code.gitea.io/gitea/services/context"
23
28
"code.gitea.io/gitea/services/convert"
24
29
25
30
"github.com/nektos/act/pkg/model"
31
+ "gopkg.in/yaml.v3"
26
32
)
27
33
28
34
const (
@@ -58,8 +64,13 @@ func MustEnableActions(ctx *context.Context) {
58
64
func List (ctx * context.Context ) {
59
65
ctx .Data ["Title" ] = ctx .Tr ("actions.actions" )
60
66
ctx .Data ["PageIsActions" ] = true
67
+ workflowID := ctx .FormString ("workflow" )
68
+ actorID := ctx .FormInt64 ("actor" )
69
+ status := ctx .FormInt ("status" )
70
+ ctx .Data ["CurWorkflow" ] = workflowID
61
71
62
72
var workflows []Workflow
73
+ var curWorkflow * model.Workflow
63
74
if empty , err := ctx .Repo .GitRepo .IsEmpty (); err != nil {
64
75
ctx .ServerError ("IsEmpty" , err )
65
76
return
@@ -140,6 +151,10 @@ func List(ctx *context.Context) {
140
151
workflow .ErrMsg = ctx .Locale .TrString ("actions.runs.no_job" )
141
152
}
142
153
workflows = append (workflows , workflow )
154
+
155
+ if workflow .Entry .Name () == workflowID {
156
+ curWorkflow = wf
157
+ }
143
158
}
144
159
}
145
160
ctx .Data ["workflows" ] = workflows
@@ -150,17 +165,46 @@ func List(ctx *context.Context) {
150
165
page = 1
151
166
}
152
167
153
- workflow := ctx .FormString ("workflow" )
154
- actorID := ctx .FormInt64 ("actor" )
155
- status := ctx .FormInt ("status" )
156
- ctx .Data ["CurWorkflow" ] = workflow
157
-
158
168
actionsConfig := ctx .Repo .Repository .MustGetUnit (ctx , unit .TypeActions ).ActionsConfig ()
159
169
ctx .Data ["ActionsConfig" ] = actionsConfig
160
170
161
- if len (workflow ) > 0 && ctx .Repo .IsAdmin () {
171
+ if len (workflowID ) > 0 && ctx .Repo .IsAdmin () {
162
172
ctx .Data ["AllowDisableOrEnableWorkflow" ] = true
163
- ctx .Data ["CurWorkflowDisabled" ] = actionsConfig .IsWorkflowDisabled (workflow )
173
+ isWorkflowDisabled := actionsConfig .IsWorkflowDisabled (workflowID )
174
+ ctx .Data ["CurWorkflowDisabled" ] = isWorkflowDisabled
175
+
176
+ if ! isWorkflowDisabled && curWorkflow != nil {
177
+ workflowDispatchConfig := workflowDispatchConfig (curWorkflow )
178
+ if workflowDispatchConfig != nil {
179
+ ctx .Data ["WorkflowDispatchConfig" ] = workflowDispatchConfig
180
+
181
+ branchOpts := git_model.FindBranchOptions {
182
+ RepoID : ctx .Repo .Repository .ID ,
183
+ IsDeletedBranch : optional .Some (false ),
184
+ ListOptions : db.ListOptions {
185
+ ListAll : true ,
186
+ },
187
+ }
188
+ branches , err := git_model .FindBranchNames (ctx , branchOpts )
189
+ if err != nil {
190
+ ctx .ServerError ("FindBranchNames" , err )
191
+ return
192
+ }
193
+ // always put default branch on the top if it exists
194
+ if slices .Contains (branches , ctx .Repo .Repository .DefaultBranch ) {
195
+ branches = util .SliceRemoveAll (branches , ctx .Repo .Repository .DefaultBranch )
196
+ branches = append ([]string {ctx .Repo .Repository .DefaultBranch }, branches ... )
197
+ }
198
+ ctx .Data ["Branches" ] = branches
199
+
200
+ tags , err := repo_model .GetTagNamesByRepoID (ctx , ctx .Repo .Repository .ID )
201
+ if err != nil {
202
+ ctx .ServerError ("GetTagNamesByRepoID" , err )
203
+ return
204
+ }
205
+ ctx .Data ["Tags" ] = tags
206
+ }
207
+ }
164
208
}
165
209
166
210
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
@@ -177,7 +221,7 @@ func List(ctx *context.Context) {
177
221
PageSize : convert .ToCorrectPageSize (ctx .FormInt ("limit" )),
178
222
},
179
223
RepoID : ctx .Repo .Repository .ID ,
180
- WorkflowID : workflow ,
224
+ WorkflowID : workflowID ,
181
225
TriggerUserID : actorID ,
182
226
}
183
227
@@ -214,11 +258,94 @@ func List(ctx *context.Context) {
214
258
215
259
pager := context .NewPagination (int (total ), opts .PageSize , opts .Page , 5 )
216
260
pager .SetDefaultParams (ctx )
217
- pager .AddParamString ("workflow" , workflow )
261
+ pager .AddParamString ("workflow" , workflowID )
218
262
pager .AddParamString ("actor" , fmt .Sprint (actorID ))
219
263
pager .AddParamString ("status" , fmt .Sprint (status ))
220
264
ctx .Data ["Page" ] = pager
221
265
ctx .Data ["HasWorkflowsOrRuns" ] = len (workflows ) > 0 || len (runs ) > 0
222
266
223
267
ctx .HTML (http .StatusOK , tplListActions )
224
268
}
269
+
270
+ type WorkflowDispatchInput struct {
271
+ Name string `yaml:"name"`
272
+ Description string `yaml:"description"`
273
+ Required bool `yaml:"required"`
274
+ Default string `yaml:"default"`
275
+ Type string `yaml:"type"`
276
+ Options []string `yaml:"options"`
277
+ }
278
+
279
+ type WorkflowDispatch struct {
280
+ Inputs []WorkflowDispatchInput
281
+ }
282
+
283
+ func workflowDispatchConfig (w * model.Workflow ) * WorkflowDispatch {
284
+ switch w .RawOn .Kind {
285
+ case yaml .ScalarNode :
286
+ var val string
287
+ if ! decodeNode (w .RawOn , & val ) {
288
+ return nil
289
+ }
290
+ if val == "workflow_dispatch" {
291
+ return & WorkflowDispatch {}
292
+ }
293
+ case yaml .SequenceNode :
294
+ var val []string
295
+ if ! decodeNode (w .RawOn , & val ) {
296
+ return nil
297
+ }
298
+ for _ , v := range val {
299
+ if v == "workflow_dispatch" {
300
+ return & WorkflowDispatch {}
301
+ }
302
+ }
303
+ case yaml .MappingNode :
304
+ var val map [string ]yaml.Node
305
+ if ! decodeNode (w .RawOn , & val ) {
306
+ return nil
307
+ }
308
+
309
+ workflowDispatchNode , found := val ["workflow_dispatch" ]
310
+ if ! found {
311
+ return nil
312
+ }
313
+
314
+ var workflowDispatch WorkflowDispatch
315
+ var workflowDispatchVal map [string ]yaml.Node
316
+ if ! decodeNode (workflowDispatchNode , & workflowDispatchVal ) {
317
+ return & workflowDispatch
318
+ }
319
+
320
+ inputsNode , found := workflowDispatchVal ["inputs" ]
321
+ if ! found || inputsNode .Kind != yaml .MappingNode {
322
+ return & workflowDispatch
323
+ }
324
+
325
+ i := 0
326
+ for {
327
+ if i + 1 >= len (inputsNode .Content ) {
328
+ break
329
+ }
330
+ var input WorkflowDispatchInput
331
+ if decodeNode (* inputsNode .Content [i + 1 ], & input ) {
332
+ input .Name = inputsNode .Content [i ].Value
333
+ workflowDispatch .Inputs = append (workflowDispatch .Inputs , input )
334
+ }
335
+ i += 2
336
+ }
337
+ return & workflowDispatch
338
+
339
+ default :
340
+ return nil
341
+ }
342
+ return nil
343
+ }
344
+
345
+ func decodeNode (node yaml.Node , out any ) bool {
346
+ if err := node .Decode (out ); err != nil {
347
+ log .Warn ("Failed to decode node %v into %T: %v" , node , out , err )
348
+ return false
349
+ }
350
+ return true
351
+ }
0 commit comments