summaryrefslogtreecommitdiffstats
path: root/services/actions/workflows.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--services/actions/workflows.go173
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
+}