@@ -11,13 +11,16 @@ import (
1111 actions_model "code.gitea.io/gitea/models/actions"
1212 "code.gitea.io/gitea/models/db"
1313 actions_module "code.gitea.io/gitea/modules/actions"
14+ "code.gitea.io/gitea/modules/actions/jobparser"
1415 "code.gitea.io/gitea/modules/container"
1516 "code.gitea.io/gitea/modules/git"
1617 "code.gitea.io/gitea/modules/json"
1718 "code.gitea.io/gitea/modules/setting"
19+ api "code.gitea.io/gitea/modules/structs"
1820 "code.gitea.io/gitea/modules/util"
1921
20- "github.com/nektos/act/pkg/model"
22+ act_pkg_model "github.com/nektos/act/pkg/model"
23+ "go.yaml.in/yaml/v4"
2124)
2225
2326type GiteaContext map [string ]any
@@ -139,12 +142,31 @@ func FindTaskNeeds(ctx context.Context, job *actions_model.ActionRunJob) (map[st
139142 }
140143
141144 ret := make (map [string ]* TaskNeed , len (needs ))
145+ reusableCallerOutputsCache := make (map [int64 ]map [string ]string )
142146 for jobID , jobsWithSameID := range jobIDJobs {
143147 if ! needs .Contains (jobID ) {
144148 continue
145149 }
146150 var jobOutputs map [string ]string
147151 for _ , job := range jobsWithSameID {
152+ if job .IsReusableCall && job .TaskID == 0 && job .Status .IsDone () {
153+ outputs , ok := reusableCallerOutputsCache [job .ID ]
154+ if ! ok {
155+ var err error
156+ outputs , err = computeReusableCallerOutputs (ctx , job )
157+ if err != nil {
158+ return nil , err
159+ }
160+ reusableCallerOutputsCache [job .ID ] = outputs
161+ }
162+ if len (jobOutputs ) == 0 {
163+ jobOutputs = outputs
164+ } else {
165+ jobOutputs = mergeTwoOutputs (outputs , jobOutputs )
166+ }
167+ continue
168+ }
169+
148170 if job .TaskID == 0 || ! job .Status .IsDone () {
149171 // it shouldn't happen, or the job has been rerun
150172 continue
@@ -171,6 +193,105 @@ func FindTaskNeeds(ctx context.Context, job *actions_model.ActionRunJob) (map[st
171193 return ret , nil
172194}
173195
196+ func computeReusableCallerOutputs (ctx context.Context , callerJob * actions_model.ActionRunJob ) (map [string ]string , error ) {
197+ cache := make (map [int64 ]map [string ]string )
198+ return computeReusableCallerOutputsInternal (ctx , callerJob , cache )
199+ }
200+
201+ func computeReusableCallerOutputsInternal (ctx context.Context , callerJob * actions_model.ActionRunJob , cache map [int64 ]map [string ]string ) (map [string ]string , error ) {
202+ if callerJob == nil || ! callerJob .IsReusableCall || callerJob .ReusableWorkflowUses == "" {
203+ return map [string ]string {}, nil
204+ }
205+
206+ if cached , ok := cache [callerJob .ID ]; ok {
207+ return cached , nil
208+ }
209+
210+ if err := callerJob .LoadAttributes (ctx ); err != nil {
211+ return nil , err
212+ }
213+
214+ childJobs , err := actions_model .GetReusableCallerChildJobs (ctx , callerJob )
215+ if err != nil {
216+ return nil , fmt .Errorf ("GetReusableCallerChildJobs: %w" , err )
217+ }
218+ if len (childJobs ) == 0 {
219+ cache [callerJob .ID ] = map [string ]string {}
220+ return cache [callerJob .ID ], nil
221+ }
222+ for _ , child := range childJobs {
223+ if ! child .Status .IsDone () {
224+ cache [callerJob .ID ] = map [string ]string {}
225+ return cache [callerJob .ID ], nil
226+ }
227+ }
228+
229+ singleWorkflow := & jobparser.SingleWorkflow {}
230+ if err := yaml .Unmarshal (childJobs [0 ].WorkflowPayload , singleWorkflow ); err != nil {
231+ return nil , fmt .Errorf ("unmarshal reusable workflow: %w" , err )
232+ }
233+ workflow := & act_pkg_model.Workflow {RawOn : singleWorkflow .RawOn }
234+
235+ inputs := map [string ]any {}
236+ if callerJob .CallEventPayload != "" {
237+ var payload api.WorkflowCallPayload
238+ if err := json .Unmarshal ([]byte (callerJob .CallEventPayload ), & payload ); err != nil {
239+ return nil , fmt .Errorf ("unmarshal workflow_call payload for caller job %d: %w" , callerJob .ID , err )
240+ }
241+ if payload .Inputs != nil {
242+ inputs = payload .Inputs
243+ }
244+ }
245+
246+ gitCtx , err := GenerateGiteaContext (ctx , callerJob .Run , callerJob )
247+ if err != nil {
248+ return nil , err
249+ }
250+
251+ vars , err := actions_model .GetVariablesOfRun (ctx , callerJob .Run )
252+ if err != nil {
253+ return nil , fmt .Errorf ("GetVariablesOfRun: %w" , err )
254+ }
255+
256+ jobOutputs := make (map [string ]map [string ]string )
257+ for _ , child := range childJobs {
258+ var outputs map [string ]string
259+ switch {
260+ case child .IsReusableCall && child .ReusableWorkflowUses != "" :
261+ childOutputs , err := computeReusableCallerOutputsInternal (ctx , child , cache )
262+ if err != nil {
263+ return nil , err
264+ }
265+ outputs = childOutputs
266+ case child .TaskID > 0 :
267+ got , err := actions_model .FindTaskOutputByTaskID (ctx , child .TaskID )
268+ if err != nil {
269+ return nil , fmt .Errorf ("FindTaskOutputByTaskID: %w" , err )
270+ }
271+ outputs = make (map [string ]string , len (got ))
272+ for _ , v := range got {
273+ outputs [v .OutputKey ] = v .OutputValue
274+ }
275+ default :
276+ outputs = map [string ]string {}
277+ }
278+
279+ if len (jobOutputs [child .JobID ]) == 0 {
280+ jobOutputs [child .JobID ] = outputs
281+ } else {
282+ jobOutputs [child .JobID ] = mergeTwoOutputs (outputs , jobOutputs [child .JobID ])
283+ }
284+ }
285+
286+ outputs , err := jobparser .EvaluateWorkflowCallOutputs (workflow , gitCtx , vars , inputs , jobOutputs )
287+ if err != nil {
288+ return nil , err
289+ }
290+
291+ cache [callerJob .ID ] = outputs
292+ return outputs , nil
293+ }
294+
174295// mergeTwoOutputs merges two outputs from two different ActionRunJobs
175296// Values with the same output name may be overridden. The user should ensure the output names are unique.
176297// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
@@ -186,8 +307,8 @@ func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
186307 return ret
187308}
188309
189- func (g * GiteaContext ) ToGitHubContext () * model .GithubContext {
190- return & model .GithubContext {
310+ func (g * GiteaContext ) ToGitHubContext () * act_pkg_model .GithubContext {
311+ return & act_pkg_model .GithubContext {
191312 Event : util .GetMapValueOrDefault (* g , "event" , map [string ]any (nil )),
192313 EventPath : util .GetMapValueOrDefault (* g , "event_path" , "" ),
193314 Workflow : util .GetMapValueOrDefault (* g , "workflow" , "" ),
0 commit comments