diff options
Diffstat (limited to 'services/actions/workflows.go')
-rw-r--r-- | services/actions/workflows.go | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/services/actions/workflows.go b/services/actions/workflows.go new file mode 100644 index 0000000..e2fb316 --- /dev/null +++ b/services/actions/workflows.go @@ -0,0 +1,173 @@ +// Copyright The Forgejo Authors. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "bytes" + "context" + "errors" + "fmt" + "strconv" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/perm/access" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/webhook" + "code.gitea.io/gitea/services/convert" + + "github.com/nektos/act/pkg/jobparser" + act_model "github.com/nektos/act/pkg/model" +) + +type InputRequiredErr struct { + Name string +} + +func (err InputRequiredErr) Error() string { + return fmt.Sprintf("input required for '%s'", err.Name) +} + +func IsInputRequiredErr(err error) bool { + _, ok := err.(InputRequiredErr) + return ok +} + +type Workflow struct { + WorkflowID string + Ref string + Commit *git.Commit + GitEntry *git.TreeEntry +} + +type InputValueGetter func(key string) string + +func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) error { + content, err := actions.GetContentFromEntry(entry.GitEntry) + if err != nil { + return err + } + + wf, err := act_model.ReadWorkflow(bytes.NewReader(content)) + if err != nil { + return err + } + + fullWorkflowID := ".forgejo/workflows/" + entry.WorkflowID + + title := wf.Name + if len(title) < 1 { + title = fullWorkflowID + } + + inputs := make(map[string]string) + if workflowDispatch := wf.WorkflowDispatchConfig(); workflowDispatch != nil { + for key, input := range workflowDispatch.Inputs { + val := inputGetter(key) + if len(val) == 0 { + val = input.Default + if len(val) == 0 { + if input.Required { + name := input.Description + if len(name) == 0 { + name = key + } + return InputRequiredErr{Name: name} + } + continue + } + } else if input.Type == "boolean" { + // Since "boolean" inputs are rendered as a checkbox in html, the value inside the form is "on" + val = strconv.FormatBool(val == "on") + } + inputs[key] = val + } + } + + if int64(len(inputs)) > setting.Actions.LimitDispatchInputs { + return errors.New("to many inputs") + } + + payload := &structs.WorkflowDispatchPayload{ + Inputs: inputs, + Ref: entry.Ref, + Repository: convert.ToRepo(ctx, repo, access.Permission{AccessMode: perm.AccessModeNone}), + Sender: convert.ToUser(ctx, doer, nil), + Workflow: fullWorkflowID, + } + + p, err := json.Marshal(payload) + if err != nil { + return err + } + + run := &actions_model.ActionRun{ + Title: title, + RepoID: repo.ID, + Repo: repo, + OwnerID: repo.OwnerID, + WorkflowID: entry.WorkflowID, + TriggerUserID: doer.ID, + TriggerUser: doer, + Ref: entry.Ref, + CommitSHA: entry.Commit.ID.String(), + Event: webhook.HookEventWorkflowDispatch, + EventPayload: string(p), + TriggerEvent: string(webhook.HookEventWorkflowDispatch), + Status: actions_model.StatusWaiting, + } + + vars, err := actions_model.GetVariablesOfRun(ctx, run) + if err != nil { + return err + } + + jobs, err := jobparser.Parse(content, jobparser.WithVars(vars)) + if err != nil { + return err + } + + return actions_model.InsertRun(ctx, run, jobs) +} + +func GetWorkflowFromCommit(gitRepo *git.Repository, ref, workflowID string) (*Workflow, error) { + ref, err := gitRepo.ExpandRef(ref) + if err != nil { + return nil, err + } + + commit, err := gitRepo.GetCommit(ref) + if err != nil { + return nil, err + } + + entries, err := actions.ListWorkflows(commit) + if err != nil { + return nil, err + } + + var workflowEntry *git.TreeEntry + for _, entry := range entries { + if entry.Name() == workflowID { + workflowEntry = entry + break + } + } + if workflowEntry == nil { + return nil, errors.New("workflow not found") + } + + return &Workflow{ + WorkflowID: workflowID, + Ref: ref, + Commit: commit, + GitEntry: workflowEntry, + }, nil +} |