fix: checkout pull_request_target head commit

For pull_request_target tasks, use the pull request head SHA from the event payload as the default checkout ref so actions/checkout resolves the submitted PR content instead of the base branch tip.

Fixes: https://gitea.com/gitea/runner/issues/563

Co-Authored-By: GPT-5 Codex <codex@openai.com>
This commit is contained in:
bircni
2026-07-03 21:58:37 +02:00
parent 0ee4643d4a
commit 4691a92ea8
2 changed files with 95 additions and 0 deletions
+33
View File
@@ -366,6 +366,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
} else if t := task.Secrets["GITHUB_TOKEN"]; t != "" {
preset.Token = t
}
applyPullRequestTargetCheckoutContext(preset)
if actionsIDTokenRequestURL := taskContext["actions_id_token_request_url"].GetStringValue(); actionsIDTokenRequestURL != "" {
envs["ACTIONS_ID_TOKEN_REQUEST_URL"] = actionsIDTokenRequestURL
@@ -574,6 +575,38 @@ func postInternalCache(url, secret string, body map[string]string) error {
return nil
}
func applyPullRequestTargetCheckoutContext(preset *model.GithubContext) {
if preset == nil || preset.EventName != "pull_request_target" {
return
}
headSHA, _ := nestedString(preset.Event, "pull_request", "head", "sha")
if headSHA == "" {
return
}
preset.Sha = headSHA
preset.Ref = headSHA
if headRef, _ := nestedString(preset.Event, "pull_request", "head", "ref"); headRef != "" {
preset.HeadRef = headRef
preset.RefName = headRef
}
}
func nestedString(m map[string]any, keys ...string) (string, bool) {
var current any = m
for _, key := range keys {
next, ok := current.(map[string]any)
if !ok {
return "", false
}
current, ok = next[key]
if !ok {
return "", false
}
}
value, ok := current.(string)
return value, ok
}
func (r *Runner) RunningCount() int64 {
return r.runningCount.Load()
}
+62
View File
@@ -7,6 +7,7 @@ import (
"context"
"testing"
"gitea.com/gitea/runner/act/model"
clientmocks "gitea.com/gitea/runner/internal/pkg/client/mocks"
"gitea.com/gitea/runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/ver"
@@ -92,6 +93,67 @@ func TestNewRunnerInitializesLabelsAndEnvironment(t *testing.T) {
require.Nil(t, r.cacheHandler)
}
func TestApplyPullRequestTargetCheckoutContextUsesHeadSHA(t *testing.T) {
preset := &model.GithubContext{
EventName: "pull_request_target",
Sha: "base-sha",
Ref: "refs/heads/main",
RefName: "main",
HeadRef: "feature",
Event: map[string]any{
"pull_request": map[string]any{
"head": map[string]any{
"sha": "head-sha",
"ref": "contributor-branch",
},
},
},
}
applyPullRequestTargetCheckoutContext(preset)
require.Equal(t, "head-sha", preset.Sha)
require.Equal(t, "head-sha", preset.Ref)
require.Equal(t, "contributor-branch", preset.RefName)
require.Equal(t, "contributor-branch", preset.HeadRef)
}
func TestApplyPullRequestTargetCheckoutContextNoOpsWithoutHeadSHA(t *testing.T) {
preset := &model.GithubContext{
EventName: "pull_request_target",
Sha: "base-sha",
Ref: "refs/heads/main",
RefName: "main",
Event: map[string]any{},
}
applyPullRequestTargetCheckoutContext(preset)
require.Equal(t, "base-sha", preset.Sha)
require.Equal(t, "refs/heads/main", preset.Ref)
require.Equal(t, "main", preset.RefName)
}
func TestApplyPullRequestTargetCheckoutContextNoOpsForOtherEvents(t *testing.T) {
preset := &model.GithubContext{
EventName: "pull_request",
Sha: "merge-sha",
Ref: "refs/pull/1/merge",
Event: map[string]any{
"pull_request": map[string]any{
"head": map[string]any{
"sha": "head-sha",
},
},
},
}
applyPullRequestTargetCheckoutContext(preset)
require.Equal(t, "merge-sha", preset.Sha)
require.Equal(t, "refs/pull/1/merge", preset.Ref)
}
func taskWithDefaultActionsURL(url string) *runnerv1.Task {
return &runnerv1.Task{
Context: &structpb.Struct{