From 2871ea08096cba15546f357d0ec473734ee9d8be Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Wed, 1 Feb 2023 13:32:46 +0800
Subject: [PATCH] Add more events details supports for actions (#22680)

#21937 implemented only basic events based on name because of `act`'s
limitation. So I sent a PR to parse all possible events details in
https://gitea.com/gitea/act/pulls/11 and it merged. The ref
documentation is
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows

This PR depends on that and make more detail responses for `push` events
and `pull_request` events. And it lefts more events there for future
PRs.

---------

Co-authored-by: Jason Song <i@wolfogre.com>
---
 go.mod                              |   2 +-
 go.sum                              |   4 +-
 modules/actions/workflows.go        | 154 +++++++++++++++++++++++++++-
 services/actions/notifier_helper.go |   2 +-
 4 files changed, 154 insertions(+), 8 deletions(-)

diff --git a/go.mod b/go.mod
index 5ab0f4b27d..b3d4040323 100644
--- a/go.mod
+++ b/go.mod
@@ -284,7 +284,7 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142
 
 replace github.com/blevesearch/zapx/v15 v15.3.6 => github.com/zeripath/zapx/v15 v15.3.6-alignment-fix
 
-replace github.com/nektos/act => gitea.com/gitea/act v0.234.0
+replace github.com/nektos/act => gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744
 
 exclude github.com/gofrs/uuid v3.2.0+incompatible
 
diff --git a/go.sum b/go.sum
index 12042dd6fd..864ddff1cd 100644
--- a/go.sum
+++ b/go.sum
@@ -70,8 +70,8 @@ codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsi
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
 git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
-gitea.com/gitea/act v0.234.0 h1:gWgMPMKdNcMrp/o2CF/SyVKiiJLBFl+xmzfvoHCpykU=
-gitea.com/gitea/act v0.234.0/go.mod h1:2C/WbTalu1VPNgbVaZJaZDzlOtAKqkXJhdOClxkMy14=
+gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744 h1:cqzKmGlX0wynSXO04NILpL25eBGwogDrKpkkbwmIpj4=
+gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744/go.mod h1:2C/WbTalu1VPNgbVaZJaZDzlOtAKqkXJhdOClxkMy14=
 gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4=
 gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
 gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go
index d340a1aa65..c1c8e71f53 100644
--- a/modules/actions/workflows.go
+++ b/modules/actions/workflows.go
@@ -10,8 +10,11 @@ import (
 
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	api "code.gitea.io/gitea/modules/structs"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
 
+	"github.com/gobwas/glob"
+	"github.com/nektos/act/pkg/jobparser"
 	"github.com/nektos/act/pkg/model"
 )
 
@@ -41,7 +44,7 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
 	return ret, nil
 }
 
-func DetectWorkflows(commit *git.Commit, event webhook_module.HookEventType) (map[string][]byte, error) {
+func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) (map[string][]byte, error) {
 	entries, err := ListWorkflows(commit)
 	if err != nil {
 		return nil, err
@@ -63,13 +66,156 @@ func DetectWorkflows(commit *git.Commit, event webhook_module.HookEventType) (ma
 			log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
 			continue
 		}
-		for _, e := range workflow.On() {
-			if e == event.Event() {
+		events, err := jobparser.ParseRawOn(&workflow.RawOn)
+		if err != nil {
+			log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
+			continue
+		}
+		for _, evt := range events {
+			if evt.Name != triggedEvent.Event() {
+				continue
+			}
+
+			if detectMatched(commit, triggedEvent, payload, evt) {
 				workflows[entry.Name()] = content
-				break
 			}
 		}
 	}
 
 	return workflows, nil
 }
+
+func detectMatched(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool {
+	if len(evt.Acts) == 0 {
+		return true
+	}
+
+	switch triggedEvent {
+	case webhook_module.HookEventCreate:
+		fallthrough
+	case webhook_module.HookEventDelete:
+		fallthrough
+	case webhook_module.HookEventFork:
+		log.Warn("unsupported event %q", triggedEvent.Event())
+		return false
+	case webhook_module.HookEventPush:
+		pushPayload := payload.(*api.PushPayload)
+		matchTimes := 0
+		// all acts conditions should be satisfied
+		for cond, vals := range evt.Acts {
+			switch cond {
+			case "branches", "tags":
+				for _, val := range vals {
+					if glob.MustCompile(val, '/').Match(pushPayload.Ref) {
+						matchTimes++
+						break
+					}
+				}
+			case "paths":
+				filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
+				if err != nil {
+					log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
+				} else {
+					for _, val := range vals {
+						matched := false
+						for _, file := range filesChanged {
+							if glob.MustCompile(val, '/').Match(file) {
+								matched = true
+								break
+							}
+						}
+						if matched {
+							matchTimes++
+							break
+						}
+					}
+				}
+			default:
+				log.Warn("unsupported condition %q", cond)
+			}
+		}
+		return matchTimes == len(evt.Acts)
+
+	case webhook_module.HookEventIssues:
+		fallthrough
+	case webhook_module.HookEventIssueAssign:
+		fallthrough
+	case webhook_module.HookEventIssueLabel:
+		fallthrough
+	case webhook_module.HookEventIssueMilestone:
+		fallthrough
+	case webhook_module.HookEventIssueComment:
+		fallthrough
+	case webhook_module.HookEventPullRequest:
+		prPayload := payload.(*api.PullRequestPayload)
+		matchTimes := 0
+		// all acts conditions should be satisfied
+		for cond, vals := range evt.Acts {
+			switch cond {
+			case "types":
+				for _, val := range vals {
+					if glob.MustCompile(val, '/').Match(string(prPayload.Action)) {
+						matchTimes++
+						break
+					}
+				}
+			case "branches":
+				for _, val := range vals {
+					if glob.MustCompile(val, '/').Match(prPayload.PullRequest.Base.Ref) {
+						matchTimes++
+						break
+					}
+				}
+			case "paths":
+				filesChanged, err := commit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref)
+				if err != nil {
+					log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
+				} else {
+					for _, val := range vals {
+						matched := false
+						for _, file := range filesChanged {
+							if glob.MustCompile(val, '/').Match(file) {
+								matched = true
+								break
+							}
+						}
+						if matched {
+							matchTimes++
+							break
+						}
+					}
+				}
+			default:
+				log.Warn("unsupported condition %q", cond)
+			}
+		}
+		return matchTimes == len(evt.Acts)
+	case webhook_module.HookEventPullRequestAssign:
+		fallthrough
+	case webhook_module.HookEventPullRequestLabel:
+		fallthrough
+	case webhook_module.HookEventPullRequestMilestone:
+		fallthrough
+	case webhook_module.HookEventPullRequestComment:
+		fallthrough
+	case webhook_module.HookEventPullRequestReviewApproved:
+		fallthrough
+	case webhook_module.HookEventPullRequestReviewRejected:
+		fallthrough
+	case webhook_module.HookEventPullRequestReviewComment:
+		fallthrough
+	case webhook_module.HookEventPullRequestSync:
+		fallthrough
+	case webhook_module.HookEventWiki:
+		fallthrough
+	case webhook_module.HookEventRepository:
+		fallthrough
+	case webhook_module.HookEventRelease:
+		fallthrough
+	case webhook_module.HookEventPackage:
+		fallthrough
+	default:
+		log.Warn("unsupported event %q", triggedEvent.Event())
+	}
+	return false
+}
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 9c9fc644cb..5b8f6bfdf6 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -137,7 +137,7 @@ func notify(ctx context.Context, input *notifyInput) error {
 		return fmt.Errorf("gitRepo.GetCommit: %w", err)
 	}
 
-	workflows, err := actions_module.DetectWorkflows(commit, input.Event)
+	workflows, err := actions_module.DetectWorkflows(commit, input.Event, input.Payload)
 	if err != nil {
 		return fmt.Errorf("DetectWorkflows: %w", err)
 	}