From 5eea61dbc8f8e82e0dd05addf76751ee517459a0 Mon Sep 17 00:00:00 2001
From: sillyguodong <33891828+sillyguodong@users.noreply.github.com>
Date: Tue, 14 Mar 2023 05:05:19 +0800
Subject: [PATCH] Fix missing commit status in PR which from forked repo
 (#23351)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

close: #23347

### Reference and Inference
According to Github REST API
[doc](https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#list-commit-statuses-for-a-reference):
1. The `Drone CI` that can create some commit status by
[API](https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status)
is enabled in `go-gitea/gitea`. So I tried to call the API to get a
commit status list of a PR which is commited to upstream
repo(`go-gitea/gitea`). As a result, the API returned a array of commit
status.

![image](https://user-images.githubusercontent.com/33891828/223913371-313d047a-5e2e-484c-b13e-dcd38748703e.png)
2. Then I tried to call the API to get commit status list of the
reference which of the `SHA` is the same as step 1 in the repo which is
forked from `go-gitea/gitea`. But I got a empty array.

![image](https://user-images.githubusercontent.com/33891828/223930827-17a64d3c-f466-4980-897c-77fe386c4d3b.png)

So, I believe it that:
1. The commit status is not shared between upstream repo and forked
repo.
2. The coomit status is bound to a repo that performs actions. (Gitea's
logic is the same)

### Cause
During debugging, I found it that commit status are not stored in the DB
as expected.
So, I located the following code:

https://github.com/go-gitea/gitea/blob/8cadd51bf295e6ff36ac36efed68cc5de34c9382/services/actions/commit_status.go#L18-L26
When I create a PR, the type of `event` is `pull request`, not `push`.
So the code return function directly.

### Screenshot

![image](https://user-images.githubusercontent.com/33891828/223939339-dadf539c-1fdd-40c4-96e9-2e4fa733f531.png)

![image](https://user-images.githubusercontent.com/33891828/223939519-edb02bf0-2478-4ea5-9366-be85468f02db.png)

![image](https://user-images.githubusercontent.com/33891828/223939557-ec6f1375-5536-400e-8987-fb7d2fd452fa.png)



### Other
In this PR, I also fix the problem of missing icon which represents
running in PRs list.

![image](https://user-images.githubusercontent.com/33891828/223939898-2a0339e4-713f-4c7b-9d99-2250a43f3457.png)

![image](https://user-images.githubusercontent.com/33891828/223939979-037a975f-5ced-480c-bac7-0ee00ebfff4b.png)
---
 models/actions/run.go             | 11 +++++
 services/actions/commit_status.go | 75 ++++++++++++++++++++-----------
 templates/repo/commit_status.tmpl |  3 ++
 3 files changed, 64 insertions(+), 25 deletions(-)

diff --git a/models/actions/run.go b/models/actions/run.go
index d5ab45a519..a711cfee2e 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -128,6 +128,17 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
 	return nil, fmt.Errorf("event %s is not a push event", run.Event)
 }
 
+func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
+	if run.Event == webhook_module.HookEventPullRequest {
+		var payload api.PullRequestPayload
+		if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
+			return nil, err
+		}
+		return &payload, nil
+	}
+	return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
+}
+
 func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
 	_, err := db.GetEngine(ctx).ID(repo.ID).
 		SetExpr("num_action_runs",
diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go
index 4f31349352..84de106eec 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -21,35 +21,60 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
 	}
 
 	run := job.Run
-	if run.Event != webhook_module.HookEventPush {
+	var (
+		sha       string
+		creatorID int64
+	)
+
+	switch run.Event {
+	case webhook_module.HookEventPush:
+		payload, err := run.GetPushEventPayload()
+		if err != nil {
+			return fmt.Errorf("GetPushEventPayload: %w", err)
+		}
+
+		// Since the payload comes from json data, we should check if it's broken, or it will cause panic
+		switch {
+		case payload.Repo == nil:
+			return fmt.Errorf("repo is missing in event payload")
+		case payload.Pusher == nil:
+			return fmt.Errorf("pusher is missing in event payload")
+		case payload.HeadCommit == nil:
+			return fmt.Errorf("head commit is missing in event payload")
+		}
+
+		sha = payload.HeadCommit.ID
+		creatorID = payload.Pusher.ID
+	case webhook_module.HookEventPullRequest:
+		payload, err := run.GetPullRequestEventPayload()
+		if err != nil {
+			return fmt.Errorf("GetPullRequestEventPayload: %w", err)
+		}
+
+		switch {
+		case payload.PullRequest == nil:
+			return fmt.Errorf("pull request is missing in event payload")
+		case payload.PullRequest.Head == nil:
+			return fmt.Errorf("head of pull request is missing in event payload")
+		case payload.PullRequest.Head.Repository == nil:
+			return fmt.Errorf("head repository of pull request is missing in event payload")
+		case payload.PullRequest.Head.Repository.Owner == nil:
+			return fmt.Errorf("owner of head repository of pull request is missing in evnt payload")
+		}
+
+		sha = payload.PullRequest.Head.Sha
+		creatorID = payload.PullRequest.Head.Repository.Owner.ID
+	default:
 		return nil
 	}
 
-	payload, err := run.GetPushEventPayload()
-	if err != nil {
-		return fmt.Errorf("GetPushEventPayload: %w", err)
-	}
-
-	// Since the payload comes from json data, we should check if it's broken, or it will cause panic
-	switch {
-	case payload.Repo == nil:
-		return fmt.Errorf("repo is missing in event payload")
-	case payload.Pusher == nil:
-		return fmt.Errorf("pusher is missing in event payload")
-	case payload.HeadCommit == nil:
-		return fmt.Errorf("head commit is missing in event payload")
-	}
-
-	creator, err := user_model.GetUserByID(ctx, payload.Pusher.ID)
+	repo := run.Repo
+	ctxname := job.Name
+	state := toCommitStatus(job.Status)
+	creator, err := user_model.GetUserByID(ctx, creatorID)
 	if err != nil {
 		return fmt.Errorf("GetUserByID: %w", err)
 	}
-
-	repo := run.Repo
-	sha := payload.HeadCommit.ID
-	ctxname := job.Name
-	state := toCommitStatus(job.Status)
-
 	if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil {
 		for _, v := range statuses {
 			if v.Context == ctxname {
@@ -65,14 +90,14 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
 
 	if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
 		Repo:    repo,
-		SHA:     payload.HeadCommit.ID,
+		SHA:     sha,
 		Creator: creator,
 		CommitStatus: &git_model.CommitStatus{
 			SHA:         sha,
 			TargetURL:   run.Link(),
 			Description: "",
 			Context:     ctxname,
-			CreatorID:   payload.Pusher.ID,
+			CreatorID:   creatorID,
 			State:       state,
 		},
 	}); err != nil {
diff --git a/templates/repo/commit_status.tmpl b/templates/repo/commit_status.tmpl
index fbf064527d..470869b381 100644
--- a/templates/repo/commit_status.tmpl
+++ b/templates/repo/commit_status.tmpl
@@ -1,6 +1,9 @@
 {{if eq .State "pending"}}
 	{{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}}
 {{end}}
+{{if eq .State "running"}}
+	{{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}}
+{{end}}
 {{if eq .State "success"}}
 	{{svg "octicon-check" 18 "commit-status icon text green"}}
 {{end}}