Skip to content

Commit 67f8afc

Browse files
fix(#2045): use PR head SHA in status comment instead of GITHUB_SHA
In cross-repo workflow_dispatch mode, GITHUB_SHA points to the .fullsend config repo's default branch HEAD, not the PR's actual head commit. This made the status comment's "Commit:" line show an unrelated SHA. Two changes fix this: 1. dispatch.yml: Include pull_request.head.sha in the event payload that gets forwarded to agent workflows. Previously only head.ref and head.repo were extracted. 2. run.go: Add prHeadSHAFromEventPath() which reads the workflow_dispatch event file (GITHUB_EVENT_PATH), parses the nested event_payload JSON string, and extracts pull_request.head.sha. setupStatusNotifier() now prefers this value over GITHUB_SHA when available, falling back gracefully for non-PR events or direct triggers. Note: pre-commit could not run in sandbox (gitleaks install failed due to Go module cache permissions). The post-script runs pre-commit authoritatively on the runner. Closes #2045
1 parent c4f2c74 commit 67f8afc

3 files changed

Lines changed: 104 additions & 1 deletion

File tree

internal/cli/run.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,12 @@ func setupStatusNotifier(fullsendDir string, sOpts statusOpts, printer *ui.Print
19111911
client := gh.New(token)
19121912

19131913
sha := os.Getenv("GITHUB_SHA")
1914+
// In cross-repo workflow_dispatch mode, GITHUB_SHA is the dispatching
1915+
// repo's default branch HEAD — not the PR's head commit. Prefer the
1916+
// PR head SHA from the event payload when available. See #2045.
1917+
if prSHA := prHeadSHAFromEventPath(os.Getenv("GITHUB_EVENT_PATH")); prSHA != "" {
1918+
sha = prSHA
1919+
}
19141920
runID := os.Getenv("GITHUB_RUN_ID")
19151921
if runID == "" {
19161922
runID = fmt.Sprintf("%d", time.Now().UnixNano())
@@ -1922,3 +1928,37 @@ func setupStatusNotifier(fullsendDir string, sOpts statusOpts, printer *ui.Print
19221928
})
19231929
return n, nil
19241930
}
1931+
1932+
// prHeadSHAFromEventPath extracts pull_request.head.sha from the event
1933+
// payload embedded in a workflow_dispatch event file. For workflow_dispatch
1934+
// events, the file contains {"inputs": {"event_payload": "<json-string>"}}.
1935+
// Returns empty string if the file is unreadable or the field is absent.
1936+
func prHeadSHAFromEventPath(path string) string {
1937+
if path == "" {
1938+
return ""
1939+
}
1940+
data, err := os.ReadFile(path)
1941+
if err != nil {
1942+
return ""
1943+
}
1944+
// The workflow_dispatch event has inputs.event_payload as a JSON string.
1945+
var event struct {
1946+
Inputs struct {
1947+
EventPayload string `json:"event_payload"`
1948+
} `json:"inputs"`
1949+
}
1950+
if err := json.Unmarshal(data, &event); err != nil || event.Inputs.EventPayload == "" {
1951+
return ""
1952+
}
1953+
var payload struct {
1954+
PullRequest struct {
1955+
Head struct {
1956+
SHA string `json:"sha"`
1957+
} `json:"head"`
1958+
} `json:"pull_request"`
1959+
}
1960+
if err := json.Unmarshal([]byte(event.Inputs.EventPayload), &payload); err != nil {
1961+
return ""
1962+
}
1963+
return payload.PullRequest.Head.SHA
1964+
}

internal/cli/run_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,3 +1027,66 @@ func TestOpenTeeReader_FileCompleteOnParserError(t *testing.T) {
10271027
require.NoError(t, err)
10281028
assert.Equal(t, content, string(fileData), "file should contain all bytes including post-error drain")
10291029
}
1030+
1031+
func TestPRHeadSHAFromEventPath_WithSHA(t *testing.T) {
1032+
// Simulate a workflow_dispatch event file where the nested event_payload
1033+
// contains pull_request.head.sha.
1034+
eventJSON := `{
1035+
"inputs": {
1036+
"event_payload": "{\"pull_request\":{\"number\":42,\"head\":{\"ref\":\"feature\",\"sha\":\"abc123def\",\"repo\":{\"full_name\":\"org/repo\"}}}}"
1037+
}
1038+
}`
1039+
f := filepath.Join(t.TempDir(), "event.json")
1040+
require.NoError(t, os.WriteFile(f, []byte(eventJSON), 0o644))
1041+
1042+
got := prHeadSHAFromEventPath(f)
1043+
assert.Equal(t, "abc123def", got)
1044+
}
1045+
1046+
func TestPRHeadSHAFromEventPath_WithoutSHA(t *testing.T) {
1047+
// Event payload has pull_request but no head.sha — should return empty.
1048+
eventJSON := `{
1049+
"inputs": {
1050+
"event_payload": "{\"pull_request\":{\"number\":42,\"head\":{\"ref\":\"feature\",\"repo\":{\"full_name\":\"org/repo\"}}}}"
1051+
}
1052+
}`
1053+
f := filepath.Join(t.TempDir(), "event.json")
1054+
require.NoError(t, os.WriteFile(f, []byte(eventJSON), 0o644))
1055+
1056+
got := prHeadSHAFromEventPath(f)
1057+
assert.Empty(t, got)
1058+
}
1059+
1060+
func TestPRHeadSHAFromEventPath_NoPullRequest(t *testing.T) {
1061+
// Issue-only event — no pull_request in the payload.
1062+
eventJSON := `{
1063+
"inputs": {
1064+
"event_payload": "{\"issue\":{\"number\":99}}"
1065+
}
1066+
}`
1067+
f := filepath.Join(t.TempDir(), "event.json")
1068+
require.NoError(t, os.WriteFile(f, []byte(eventJSON), 0o644))
1069+
1070+
got := prHeadSHAFromEventPath(f)
1071+
assert.Empty(t, got)
1072+
}
1073+
1074+
func TestPRHeadSHAFromEventPath_EmptyPath(t *testing.T) {
1075+
got := prHeadSHAFromEventPath("")
1076+
assert.Empty(t, got)
1077+
}
1078+
1079+
func TestPRHeadSHAFromEventPath_MissingFile(t *testing.T) {
1080+
got := prHeadSHAFromEventPath("/nonexistent/path/event.json")
1081+
assert.Empty(t, got)
1082+
}
1083+
1084+
func TestPRHeadSHAFromEventPath_NoInputs(t *testing.T) {
1085+
// Direct event (not workflow_dispatch) — no inputs field.
1086+
eventJSON := `{"action": "opened", "pull_request": {"number": 1}}`
1087+
f := filepath.Join(t.TempDir(), "event.json")
1088+
require.NoError(t, os.WriteFile(f, []byte(eventJSON), 0o644))
1089+
1090+
got := prHeadSHAFromEventPath(f)
1091+
assert.Empty(t, got)
1092+
}

internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ jobs:
322322
EVENT_PAYLOAD=$(jq -c '{
323323
issue: (.issue // null | if . then {number, html_url} else null end),
324324
pull_request: (.pull_request // null | if . then {number, html_url,
325-
head: {ref: .head.ref, repo: {full_name: .head.repo.full_name}},
325+
head: {ref: .head.ref, sha: .head.sha, repo: {full_name: .head.repo.full_name}},
326326
base: {ref: .base.ref, repo: {full_name: .base.repo.full_name}}} else null end),
327327
comment: (.comment // null | if . then {body: .body[:4096]} else null end)
328328
}' "$GITHUB_EVENT_PATH")

0 commit comments

Comments
 (0)