From a4ef0716b4486a4dc52cd8ac3aa3f4f95516795f Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 16 Jan 2026 13:26:59 -0700 Subject: [PATCH] fix --- models/perm/access/repo_permission.go | 55 +++++------------- modules/structs/hook.go | 19 +++++++ services/actions/reusable_workflow.go | 82 +++++++++++++++++---------- 3 files changed, 87 insertions(+), 69 deletions(-) diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 605e3ab163..83d3983bde 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -267,15 +267,25 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito if err != nil { return perm, err } + if err := task.LoadAttributes(ctx); err != nil { + return perm, err + } + + return GetActionsUserRepoPermissionByActionRun(ctx, repo, actionsUser, task.Job.Run) +} + +func GetActionsUserRepoPermissionByActionRun(ctx context.Context, repo *repo_model.Repository, actionsUser *user_model.User, run *actions_model.ActionRun) (perm Permission, err error) { + if actionsUser.ID != user_model.ActionsUserID { + return perm, errors.New("api GetActionsUserRepoPermissionByActionRun can only be called by the actions user") + } var accessMode perm_model.AccessMode - if task.RepoID != repo.ID { - taskRepo, exist, err := db.GetByID[repo_model.Repository](ctx, task.RepoID) - if err != nil || !exist { + if run.RepoID != repo.ID { + if err := run.LoadRepo(ctx); err != nil { return perm, err } actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() - if !actionsCfg.IsCollaborativeOwner(taskRepo.OwnerID) || !taskRepo.IsPrivate { + if !actionsCfg.IsCollaborativeOwner(run.Repo.OwnerID) || !run.Repo.IsPrivate { // The task repo can access the current repo only if the task repo is private and // the owner of the task repo is a collaborative owner of the current repo. // FIXME should owner's visibility also be considered here? @@ -289,42 +299,7 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito return perm, nil } accessMode = perm_model.AccessModeRead - } else if task.IsForkPullRequest { - accessMode = perm_model.AccessModeRead - } else { - accessMode = perm_model.AccessModeWrite - } - - if err := repo.LoadUnits(ctx); err != nil { - return perm, err - } - perm.SetUnitsWithDefaultAccessMode(repo.Units, accessMode) - return perm, nil -} - -// GetActionsUserRepoPermissionByRepoID returns the actions user permissions to the repository -func GetActionsUserRepoPermissionByRepoID(ctx context.Context, repo *repo_model.Repository, actionsUser *user_model.User, sourceRepoID int64, isForkPullRequest bool) (perm Permission, err error) { - if actionsUser.ID != user_model.ActionsUserID { - return perm, errors.New("api GetActionsUserRepoPermissionByRepoID can only be called by the actions user") - } - - var accessMode perm_model.AccessMode - if sourceRepoID != repo.ID { - taskRepo, exist, err := db.GetByID[repo_model.Repository](ctx, sourceRepoID) - if err != nil || !exist { - return perm, err - } - actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig() - if !actionsCfg.IsCollaborativeOwner(taskRepo.OwnerID) || !taskRepo.IsPrivate { - perm, err = GetUserRepoPermission(ctx, repo, user_model.NewActionsUser()) - if err != nil { - return perm, err - } - perm.AccessMode = min(perm.AccessMode, perm_model.AccessModeRead) - return perm, nil - } - accessMode = perm_model.AccessModeRead - } else if isForkPullRequest { + } else if run.IsForkPullRequest { accessMode = perm_model.AccessModeRead } else { accessMode = perm_model.AccessModeWrite diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 57af38464a..6e87a14c89 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -642,3 +642,22 @@ type WorkflowJobPayload struct { func (p *WorkflowJobPayload) JSONPayload() ([]byte, error) { return json.MarshalIndent(p, "", " ") } + +// WorkflowCallPayload represents a workflow_call payload +type WorkflowCallPayload struct { + // The name or path of the workflow file + Workflow string `json:"workflow"` + // The git reference (branch, tag, or commit SHA) to run the workflow on + Ref string `json:"ref"` + // Input parameters for the workflow_call event + Inputs map[string]any `json:"inputs"` + // The repository containing the workflow + Repository *Repository `json:"repository"` + // The user who triggered the workflow + Sender *User `json:"sender"` +} + +// JSONPayload implements Payload +func (p *WorkflowCallPayload) JSONPayload() ([]byte, error) { + return json.MarshalIndent(p, "", " ") +} diff --git a/services/actions/reusable_workflow.go b/services/actions/reusable_workflow.go index b5a99aaf84..7339e73adf 100644 --- a/services/actions/reusable_workflow.go +++ b/services/actions/reusable_workflow.go @@ -11,13 +11,15 @@ import ( "strings" actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/convert" "github.com/nektos/act/pkg/jobparser" actmodel "github.com/nektos/act/pkg/model" @@ -40,7 +42,7 @@ func expandReusableWorkflows(ctx context.Context, run *actions_model.ActionRun, if err != nil { return err } - if err := createChildRunFromReusableWorkflow(ctx, run, job, workflowJob, ref, vars); err != nil { + if err := createChildRunFromReusableWorkflow(ctx, job, workflowJob, ref, vars); err != nil { return err } } @@ -67,47 +69,68 @@ func expandReusableWorkflow(ctx context.Context, parentJob *actions_model.Action if err != nil { return err } - if err := createChildRunFromReusableWorkflow(ctx, parentJob.Run, parentJob, workflowJob, ref, vars); err != nil { + if err := createChildRunFromReusableWorkflow(ctx, parentJob, workflowJob, ref, vars); err != nil { return err } return nil } -func createChildRunFromReusableWorkflow(ctx context.Context, run *actions_model.ActionRun, parentJob *actions_model.ActionRunJob, workflowJob *jobparser.Job, ref *act_runner_pkg.ReusableWorkflowRef, vars map[string]string) error { - content, err := loadReusableWorkflowContent(ctx, run, ref) +func createChildRunFromReusableWorkflow(ctx context.Context, parentJob *actions_model.ActionRunJob, workflowJob *jobparser.Job, ref *act_runner_pkg.ReusableWorkflowRef, vars map[string]string) error { + if err := parentJob.LoadAttributes(ctx); err != nil { + return err + } + parentRun := parentJob.Run + + content, err := loadReusableWorkflowContent(ctx, parentRun, ref) if err != nil { return err } - inputsWithDefaults, err := buildWorkflowCallInputs(ctx, run, parentJob, workflowJob, content, vars) + inputsWithDefaults, err := buildWorkflowCallInputs(ctx, parentJob, workflowJob, content, vars) if err != nil { return err } - eventPayload, err := json.Marshal(map[string]any{ - "inputs": inputsWithDefaults, - }) + workflowCallPayload := &api.WorkflowCallPayload{ + Workflow: parentRun.WorkflowID, + Ref: parentRun.Ref, + Repository: convert.ToRepo(ctx, parentRun.Repo, access_model.Permission{AccessMode: perm.AccessModeNone}), + Sender: convert.ToUserWithAccessMode(ctx, parentRun.TriggerUser, perm.AccessModeNone), + Inputs: inputsWithDefaults, + } + + giteaCtx := GenerateGiteaContext(parentJob.Run, parentJob) + + jobs, err := jobparser.Parse(content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext()), jobparser.WithInputs(inputsWithDefaults)) if err != nil { - return err + return fmt.Errorf("parse workflow: %w", err) + } + childRunName := ref.WorkflowPath + if len(jobs) > 0 && jobs[0].RunName != "" { + childRunName = jobs[0].RunName + } + + var eventPayload []byte + if eventPayload, err = workflowCallPayload.JSONPayload(); err != nil { + return fmt.Errorf("JSONPayload: %w", err) } childRun := &actions_model.ActionRun{ - Title: run.Title, - RepoID: run.RepoID, - Repo: run.Repo, - OwnerID: run.OwnerID, + Title: fmt.Sprintf("%s / %s", parentRun.Title, childRunName), + RepoID: parentRun.RepoID, + OwnerID: parentRun.OwnerID, ParentJobID: parentJob.ID, WorkflowID: ref.WorkflowPath, - TriggerUserID: run.TriggerUserID, - TriggerUser: run.TriggerUser, - Ref: run.Ref, - CommitSHA: run.CommitSHA, - IsForkPullRequest: run.IsForkPullRequest, + TriggerUserID: parentRun.TriggerUserID, + TriggerUser: parentRun.TriggerUser, + Ref: parentRun.Ref, + CommitSHA: parentRun.CommitSHA, + IsForkPullRequest: parentRun.IsForkPullRequest, Event: "workflow_call", TriggerEvent: "workflow_call", EventPayload: string(eventPayload), Status: actions_model.StatusWaiting, - NeedApproval: run.NeedApproval, + NeedApproval: parentRun.NeedApproval, } if err := PrepareRunAndInsert(ctx, content, childRun, inputsWithDefaults); err != nil { @@ -120,7 +143,7 @@ func createChildRunFromReusableWorkflow(ctx context.Context, run *actions_model. return nil } -func buildWorkflowCallInputs(ctx context.Context, run *actions_model.ActionRun, parentJob *actions_model.ActionRunJob, workflowJob *jobparser.Job, content []byte, vars map[string]string) (map[string]any, error) { +func buildWorkflowCallInputs(ctx context.Context, parentJob *actions_model.ActionRunJob, workflowJob *jobparser.Job, content []byte, vars map[string]string) (map[string]any, error) { singleWorkflow := &jobparser.SingleWorkflow{} if err := yaml.Unmarshal(content, singleWorkflow); err != nil { return nil, fmt.Errorf("unmarshal workflow: %w", err) @@ -130,8 +153,9 @@ func buildWorkflowCallInputs(ctx context.Context, run *actions_model.ActionRun, RawOn: singleWorkflow.RawOn, } - giteaCtx := GenerateGiteaContext(run, parentJob) - inputs, err := getInputsFromRun(run) + giteaCtx := GenerateGiteaContext(parentJob.Run, parentJob) + + inputs, err := getInputsFromRun(parentJob.Run) if err != nil { return nil, fmt.Errorf("get inputs: %w", err) } @@ -144,21 +168,21 @@ func buildWorkflowCallInputs(ctx context.Context, run *actions_model.ActionRun, return jobparser.EvaluateWorkflowCallInputs(workflow, parentJob.JobID, workflowJob, giteaCtx, results, vars, inputs) } -func loadReusableWorkflowContent(ctx context.Context, run *actions_model.ActionRun, ref *act_runner_pkg.ReusableWorkflowRef) ([]byte, error) { - if ref.Kind == act_runner_pkg.ReusableWorkflowKindLocal { - if err := run.LoadRepo(ctx); err != nil { +func loadReusableWorkflowContent(ctx context.Context, parentRun *actions_model.ActionRun, ref *act_runner_pkg.ReusableWorkflowRef) ([]byte, error) { + if ref.Kind == act_runner_pkg.ReusableWorkflowKindLocalSameRepo { + if err := parentRun.LoadRepo(ctx); err != nil { return nil, err } - return readWorkflowContentFromRepo(ctx, run.Repo, run.Ref, ref.WorkflowPath) + return readWorkflowContentFromRepo(ctx, parentRun.Repo, parentRun.Ref, ref.WorkflowPath) } - if ref.Kind == act_runner_pkg.ReusableWorkflowKindOtherRepo || isSameInstanceHost(ref.Host) { + if ref.Kind == act_runner_pkg.ReusableWorkflowKindLocalOtherRepo || isSameInstanceHost(ref.Host) { repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, ref.Owner, ref.Repo) if err != nil { return nil, err } if repo.IsPrivate { - perm, err := access_model.GetActionsUserRepoPermissionByRepoID(ctx, repo, user_model.NewActionsUser(), run.RepoID, run.IsForkPullRequest) + perm, err := access_model.GetActionsUserRepoPermissionByActionRun(ctx, repo, user_model.NewActionsUser(), parentRun) if err != nil { return nil, err }