From 31efbafbe3047cdaff8abe6ae6719e257556307c Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Tue, 14 Mar 2023 22:43:10 -0400
Subject: [PATCH] Convert GitHub event on actions and fix some pull_request
 events. (#23037) (#23471)

Backport #23037 by @lunny

Follow #22680

Partially Fix #22958, on pull_request, `opened`, `reopened`,
`synchronize` supported, `edited` hasn't been supported yet because
Gitea doesn't trigger that events.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: yp05327 <576951401@qq.com>
---
 modules/actions/github.go    |  41 +++++
 modules/actions/workflows.go | 301 +++++++++++++++++++++--------------
 2 files changed, 220 insertions(+), 122 deletions(-)
 create mode 100644 modules/actions/github.go

diff --git a/modules/actions/github.go b/modules/actions/github.go
new file mode 100644
index 0000000000..bcde9a0f55
--- /dev/null
+++ b/modules/actions/github.go
@@ -0,0 +1,41 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+	webhook_module "code.gitea.io/gitea/modules/webhook"
+
+	"github.com/nektos/act/pkg/jobparser"
+)
+
+const (
+	githubEventPullRequest              = "pull_request"
+	githubEventPullRequestTarget        = "pull_request_target"
+	githubEventPullRequestReviewComment = "pull_request_review_comment"
+	githubEventPullRequestReview        = "pull_request_review"
+	githubEventRegistryPackage          = "registry_package"
+	githubEventCreate                   = "create"
+	githubEventDelete                   = "delete"
+	githubEventFork                     = "fork"
+	githubEventPush                     = "push"
+	githubEventIssues                   = "issues"
+	githubEventIssueComment             = "issue_comment"
+	githubEventRelease                  = "release"
+	githubEventPullRequestComment       = "pull_request_comment"
+)
+
+func convertFromGithubEvent(evt *jobparser.Event) string {
+	switch evt.Name {
+	case githubEventPullRequest, githubEventPullRequestTarget, githubEventPullRequestReview,
+		githubEventPullRequestReviewComment:
+		return string(webhook_module.HookEventPullRequest)
+	case githubEventRegistryPackage:
+		return string(webhook_module.HookEventPackage)
+	case githubEventCreate, githubEventDelete, githubEventFork, githubEventPush,
+		githubEventIssues, githubEventIssueComment, githubEventRelease, githubEventPullRequestComment:
+		fallthrough
+	default:
+		return evt.Name
+	}
+}
diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go
index 7f0e6e4564..6f4d98d52d 100644
--- a/modules/actions/workflows.go
+++ b/modules/actions/workflows.go
@@ -72,9 +72,7 @@ func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventTy
 			continue
 		}
 		for _, evt := range events {
-			if evt.Name != triggedEvent.Event() {
-				continue
-			}
+			log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent)
 			if detectMatched(commit, triggedEvent, payload, evt) {
 				workflows[entry.Name()] = content
 			}
@@ -85,138 +83,197 @@ func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventTy
 }
 
 func detectMatched(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool {
+	if convertFromGithubEvent(evt) != string(triggedEvent) {
+		return false
+	}
+
+	switch triggedEvent {
+	case webhook_module.HookEventCreate,
+		webhook_module.HookEventDelete,
+		webhook_module.HookEventFork,
+		webhook_module.HookEventIssueAssign,
+		webhook_module.HookEventIssueLabel,
+		webhook_module.HookEventIssueMilestone,
+		webhook_module.HookEventPullRequestAssign,
+		webhook_module.HookEventPullRequestLabel,
+		webhook_module.HookEventPullRequestMilestone,
+		webhook_module.HookEventPullRequestComment,
+		webhook_module.HookEventPullRequestReviewApproved,
+		webhook_module.HookEventPullRequestReviewRejected,
+		webhook_module.HookEventPullRequestReviewComment,
+		webhook_module.HookEventWiki,
+		webhook_module.HookEventRepository,
+		webhook_module.HookEventRelease,
+		webhook_module.HookEventPackage:
+		if len(evt.Acts) != 0 {
+			log.Warn("Ignore unsupported %s event arguments %q", triggedEvent, evt.Acts)
+		}
+		// no special filter parameters for these events, just return true if name matched
+		return true
+
+	case webhook_module.HookEventPush:
+		return matchPushEvent(commit, payload.(*api.PushPayload), evt)
+
+	case webhook_module.HookEventIssues:
+		return matchIssuesEvent(commit, payload.(*api.IssuePayload), evt)
+
+	case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync:
+		return matchPullRequestEvent(commit, payload.(*api.PullRequestPayload), evt)
+
+	case webhook_module.HookEventIssueComment:
+		return matchIssueCommentEvent(commit, payload.(*api.IssueCommentPayload), evt)
+
+	default:
+		log.Warn("unsupported event %q", triggedEvent)
+		return false
+	}
+}
+
+func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobparser.Event) bool {
+	// with no special filter parameters
 	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":
-				refShortName := git.RefName(pushPayload.Ref).ShortName()
-				for _, val := range vals {
-					if glob.MustCompile(val, '/').Match(refShortName) {
-						matchTimes++
-						break
-					}
+	matchTimes := 0
+	// all acts conditions should be satisfied
+	for cond, vals := range evt.Acts {
+		switch cond {
+		case "branches", "tags":
+			refShortName := git.RefName(pushPayload.Ref).ShortName()
+			for _, val := range vals {
+				if glob.MustCompile(val, '/').Match(refShortName) {
+					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++
+			}
+		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
 						}
 					}
-				}
-			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)) {
+					if matched {
 						matchTimes++
 						break
 					}
 				}
-			case "branches":
-				refShortName := git.RefName(prPayload.PullRequest.Base.Ref).ShortName()
-				for _, val := range vals {
-					if glob.MustCompile(val, '/').Match(refShortName) {
-						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)
 			}
+		default:
+			log.Warn("push event 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
+	return matchTimes == len(evt.Acts)
+}
+
+func matchIssuesEvent(commit *git.Commit, issuePayload *api.IssuePayload, evt *jobparser.Event) bool {
+	// with no special filter parameters
+	if len(evt.Acts) == 0 {
+		return true
+	}
+
+	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(issuePayload.Action)) {
+					matchTimes++
+					break
+				}
+			}
+		default:
+			log.Warn("issue event unsupported condition %q", cond)
+		}
+	}
+	return matchTimes == len(evt.Acts)
+}
+
+func matchPullRequestEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
+	// with no special filter parameters
+	if len(evt.Acts) == 0 {
+		// defaultly, only pull request opened and synchronized will trigger workflow
+		return prPayload.Action == api.HookIssueSynchronized || prPayload.Action == api.HookIssueOpened
+	}
+
+	matchTimes := 0
+	// all acts conditions should be satisfied
+	for cond, vals := range evt.Acts {
+		switch cond {
+		case "types":
+			action := prPayload.Action
+			if prPayload.Action == api.HookIssueSynchronized {
+				action = "synchronize"
+			}
+			log.Trace("matching pull_request %s with %v", action, vals)
+			for _, val := range vals {
+				if glob.MustCompile(val, '/').Match(string(action)) {
+					matchTimes++
+					break
+				}
+			}
+		case "branches":
+			refShortName := git.RefName(prPayload.PullRequest.Base.Ref).ShortName()
+			for _, val := range vals {
+				if glob.MustCompile(val, '/').Match(refShortName) {
+					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("pull request event unsupported condition %q", cond)
+		}
+	}
+	return matchTimes == len(evt.Acts)
+}
+
+func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool {
+	// with no special filter parameters
+	if len(evt.Acts) == 0 {
+		return true
+	}
+
+	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(issueCommentPayload.Action)) {
+					matchTimes++
+					break
+				}
+			}
+		default:
+			log.Warn("issue comment unsupported condition %q", cond)
+		}
+	}
+	return matchTimes == len(evt.Acts)
 }