diff --git a/internal/app/run/runner.go b/internal/app/run/runner.go index a751c2de..247068b5 100644 --- a/internal/app/run/runner.go +++ b/internal/app/run/runner.go @@ -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() } diff --git a/internal/app/run/runner_test.go b/internal/app/run/runner_test.go index 43635549..7305908d 100644 --- a/internal/app/run/runner_test.go +++ b/internal/app/run/runner_test.go @@ -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{