Skip to content

fix(ci): prefer GitHub Actions PR-event head SHA over GITHUB_SHA#1469

Closed
jd wants to merge 4 commits into
devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55cfrom
devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55
Closed

fix(ci): prefer GitHub Actions PR-event head SHA over GITHUB_SHA#1469
jd wants to merge 4 commits into
devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55cfrom
devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55

Conversation

@jd
Copy link
Copy Markdown
Member

@jd jd commented May 27, 2026

On GitHub Actions pull_request builds, GITHUB_SHA points at
the synthetic merge commit GitHub creates by pre-merging the PR
head into the base — not the actual code the tests ran against.
The contributor's real head SHA lives in the event payload at
pull_request.head.sha, and that's the value Mergify dashboards
correlate with. Without this fix, every ci junit-process run
on a PR was uploading the wrong vcs.ref.head.revision resource
attribute.

get_github_actions_head_sha now reads
GITHUB_EVENT_PATH, looks for pull_request.head.sha, and falls
back to GITHUB_SHA if the payload is missing, malformed, or
the build isn't a pull_request event — matching Python's
get_github_actions_head_sha. The PR-event JSON read is shared
across the pull-request-number lookup (existing) and the head-SHA
lookup (new); the number lookup stays strict about parse errors
because it's load-bearing for scopes-send dispatch, while the
SHA lookup stays lenient because GITHUB_SHA is a sane fallback.

Three unit tests pin: payload-wins-over-env, no-PR-field-falls-
back-to-env, and missing-event-file-still-uses-env.

The CircleCI PR-build API fallback Python implements stays
Python-side for now — it requires a GitHub REST API client we
don't have on the Rust side yet.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

Depends-On: #1468

@jd
Copy link
Copy Markdown
Member Author

jd commented May 27, 2026

This pull request is part of a Mergify stack:

# Pull Request Link
1 refactor(ci): consolidate the CI-env scrubber into a shared testing module #1442
2 refactor: drop stale Phase X.Y doc markers and one inline color branch #1443
3 refactor(tui): share StyledGlyph across queue show/status renderers #1444
4 refactor(queue): drop indexmap, group_by_scope returns a Vec<(K, V)> #1445
5 refactor(ci): swap uuid for getrandom in the GHA heredoc delimiter #1446
6 refactor(config): standardize the workspace on serde_yaml_ng for YAML parsing #1447
7 test(ci): add live smoke test for ci scopes select-all path #1460
8 feat(rust): port ci scopes to native Rust #1461
9 feat(ci): parse JUnit XML reports in native Rust #1465
10 feat(ci): encode JUnit cases as OTLP and upload to Mergify CI Insights #1466
11 feat(ci): promote ci junit-process from shim to native Rust #1467
12 feat(ci): promote ci junit-upload from shim to native as a deprecated alias #1468
13 fix(ci): prefer GitHub Actions PR-event head SHA over GITHUB_SHA #1469 👈

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented May 27, 2026

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🔴 ⛓️ Depends-On Requirements

Waiting for

This rule is failing.

Requirement based on the presence of Depends-On in the body of the pull request

🔴 👀 Review Requirements

Waiting for

  • #approved-reviews-by>=2
This rule is failing.
  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🔴 🔎 Reviews

Waiting for

  • #review-requested = 0
This rule is failing.
  • #review-requested = 0
  • #changes-requested-reviews-by = 0
  • #review-threads-unresolved = 0

🟢 🤖 Continuous Integration

Wonderful, this rule succeeded.
  • all of:
    • check-success=ci-gate

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert|ui)(?:\(.+\))?:

🟢 📕 PR description

Wonderful, this rule succeeded.
  • body ~= (?ms:.{48,})

@jd jd force-pushed the devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55c branch from 57bb83b to bd85763 Compare May 27, 2026 08:27
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from 73d824f to 53e9e75 Compare May 27, 2026 08:27
@jd jd temporarily deployed to func-tests-live May 27, 2026 08:27 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 27, 2026 08:27 — with GitHub Actions Inactive
@jd
Copy link
Copy Markdown
Member Author

jd commented May 27, 2026

Revision history

# Type Changes Reason Date
1 initial 73d824f 2026-05-27 08:27 UTC
2 rebase 73d824f → 53e9e75 (rebase only) 2026-05-27 08:27 UTC
3 rebase 53e9e75 → eb452fa (rebase only) 2026-05-27 08:46 UTC
4 rebase eb452fa → ab01533 (rebase only) 2026-05-27 09:00 UTC
5 rebase ab01533 → dbb0765 (rebase only) 2026-05-27 09:18 UTC
6 rebase dbb0765 → edcef26 (rebase only) 2026-05-27 10:04 UTC
7 rebase edcef26 → 1573dd6 (rebase only) 2026-05-27 10:15 UTC

@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 27, 2026 08:29 Failure
@mergify mergify Bot requested a review from a team May 27, 2026 08:40
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from 53e9e75 to eb452fa Compare May 27, 2026 08:46
@jd jd force-pushed the devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55c branch from bd85763 to c994e93 Compare May 27, 2026 08:46
@jd jd temporarily deployed to func-tests-live May 27, 2026 08:46 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 27, 2026 08:46 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 27, 2026 08:46 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from eb452fa to ab01533 Compare May 27, 2026 09:00
@jd jd temporarily deployed to func-tests-live May 27, 2026 09:00 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 27, 2026 09:00 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55c branch from 8460659 to de0921a Compare May 27, 2026 09:18
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from ab01533 to dbb0765 Compare May 27, 2026 09:18
@jd jd temporarily deployed to func-tests-live May 27, 2026 09:18 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 27, 2026 09:18 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 27, 2026 09:19 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from dbb0765 to edcef26 Compare May 27, 2026 10:04
@jd jd temporarily deployed to func-tests-live May 27, 2026 10:04 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 27, 2026 10:06 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55c branch from a823367 to 7a63271 Compare May 27, 2026 10:15
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from edcef26 to 1573dd6 Compare May 27, 2026 10:15
@jd jd temporarily deployed to func-tests-live May 27, 2026 10:15 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 27, 2026 10:15 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 27, 2026 10:15 Failure
jd and others added 4 commits May 28, 2026 09:42
Second layer of the `mergify ci junit-process` port — turn the
parsed `TestCase` values into an OTLP `ExportTraceServiceRequest`
and ship it to `/v1/repos/<owner>/<repo>/ci/traces` as
gzipped protobuf.

Two new modules under `junit_process`:

- `spans` builds one session span per upload, one suite span per
  `<testsuite>`, and one case span per `<testcase>`, all sharing
  a resource that carries the CI-env attributes the backend uses
  for routing (provider, pipeline run id, branch, head SHA, …).
  The test cases pin parent/child wiring, status-code mapping,
  attribute propagation, and resource scraping by feeding a
  deterministic RNG into a hidden seam.

- `upload` POSTs the request with the same headers the Python
  `OTLPSpanExporter` sends (`Bearer` auth, `application/x-protobuf`,
  `Content-Encoding: gzip`) and matches the error wording so any
  log scrapers tracking Python output keep working. Wiremock
  covers the happy path, the empty-request short-circuit, and the
  401 error surface.

The detector grows the resource-attribute lookups Python emits
(pipeline name, job name, run id/attempt/url, head/base ref,
head SHA, repository URL, runner name) — sync env reads only,
matching the Python sync path. The PR-event-aware async SHA
fallback stays on the Python side for now and lands with the
CLI dispatch in Phase C.

`opentelemetry-proto` is the only otel dep we pull in, gated to
`gen-tonic-messages + trace` so it boils down to the prost
generated types plus the trace proto module — no tonic, no
otel SDK, no exporter runtime. Compression is `flate2` and the
HTTP layer reuses the existing workspace `reqwest` to keep the
TLS / rustls flavour consistent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: Icbc727166711d76678877aa172ca47c2dcc07ebc
Third and final layer of the port: the orchestration that wires
the JUnit parser, OTLP encoder, uploader, and a new quarantine
API client together, plus the native clap dispatch that retires
the Python shim for this subcommand.

Two new modules under `junit_process`:

- `quarantine` POSTs failing test names to
  `/v1/ci/<owner>/repositories/<repo>/quarantines/check` and
  splits the response into quarantined / non-quarantined buckets.
  The categorize step counts unknown names as non-quarantined to
  match Python (an API that silently drops a name still blocks
  the corresponding failure).

- `command::run` orchestrates the per-invocation flow: resolve
  the token / repo / target branch (falling back to the CI-env
  detector for the latter two), expand `**`/`*`/`?` patterns into
  paths, parse every file, hit the quarantine endpoint, feed the
  quarantined set into the OTLP builder so each case span gets
  the right `cicd.test.quarantined` value at build time, upload
  the gzipped protobuf, and render the report. Network failures
  on quarantine or upload are non-fatal — the report calls them
  out and the verdict still fires based on
  `failing_tests_not_quarantined_count`.

The dispatch layer in `main.rs` promotes
`Subcommands::Ci(CiSubcommand::JunitProcess)` from
`ShimmedArgs` to a full clap variant, adds the
`("ci", "junit-process")` entry to `NATIVE_COMMANDS`, and routes
through `mergify_ci::junit_process::run`. `ci junit-upload`
(deprecated) keeps its shim.

The detector gains `get_tests_target_branch` (base ref OR head
ref) so the orchestrator can derive the branch the quarantine API
should look up tests on without round-tripping through Python.

Output is byte-for-byte the same as the Python implementation's
`process_junit_files` — same separators, same emoji, same
`Run ID`/`Exit code` lines, same `┌ … │ … └─` box-drawn failure
blocks. End-to-end smoke against the live `junit_fail.xml`
fixture exits 1 on a non-quarantined failure and 0 on an
all-pass run, matching Python's contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I6bd59de407ddcd4196bdd7e7b8ddb4a88885af7a
…ed alias

`junit-upload` is the deprecated alias for `junit-process` —
Python's `mergify_cli/ci/cli.py` declares it with click's
`deprecated="Use \`junit-process\` instead"` decorator and
delegates straight to the same `process_junit_files` body. The
shim was still routing it through Python only because the
orchestrator wasn't native yet; now that Phase C landed, we can
collapse it down to a clap variant that reuses
`JunitProcessCliArgs` and dispatches to
`mergify_ci::junit_process::run` after printing a
`DeprecationWarning: …` line to stderr (matches click's behavior
on first invocation).

This retires the last `ci` shim — `prepend_two` lost its only
remaining caller and goes with it. `stack` is now the only
shimmed top-level group left.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: Ic316f55c5d8a834cefa5d30ffbf5ebabec0a4122
On GitHub Actions `pull_request` builds, `GITHUB_SHA` points at
the synthetic merge commit GitHub creates by pre-merging the PR
head into the base — not the actual code the tests ran against.
The contributor's real head SHA lives in the event payload at
`pull_request.head.sha`, and that's the value Mergify dashboards
correlate with. Without this fix, every `ci junit-process` run
on a PR was uploading the wrong `vcs.ref.head.revision` resource
attribute.

`get_github_actions_head_sha` now reads
`GITHUB_EVENT_PATH`, looks for `pull_request.head.sha`, and falls
back to `GITHUB_SHA` if the payload is missing, malformed, or
the build isn't a `pull_request` event — matching Python's
`get_github_actions_head_sha`. The PR-event JSON read is shared
across the pull-request-number lookup (existing) and the head-SHA
lookup (new); the number lookup stays strict about parse errors
because it's load-bearing for `scopes-send` dispatch, while the
SHA lookup stays lenient because `GITHUB_SHA` is a sane fallback.

Three unit tests pin: payload-wins-over-env, no-PR-field-falls-
back-to-env, and missing-event-file-still-uses-env.

The CircleCI PR-build API fallback Python implements stays
Python-side for now — it requires a GitHub REST API client we
don't have on the Rust side yet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Change-Id: I99912b552f5841c3364c1a84e29a73486d6c083e
@jd jd force-pushed the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch from 1573dd6 to 560ddd8 Compare May 28, 2026 07:44
@jd jd force-pushed the devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55c branch from 7a63271 to 3f9ccca Compare May 28, 2026 07:44
@jd jd temporarily deployed to func-tests-live May 28, 2026 07:44 — with GitHub Actions Inactive
@jd jd temporarily deployed to func-tests-live May 28, 2026 07:44 — with GitHub Actions Inactive
@mergify mergify Bot had a problem deploying to Mergify Merge Protections May 28, 2026 07:45 Failure
@jd jd force-pushed the devs/jd/worktree-rust-port/promote-ci-junit-upload-shim-native-deprecated--c316f55c branch from 3f9ccca to 07fd863 Compare May 28, 2026 08:20
@jd jd closed this May 28, 2026
@jd jd deleted the devs/jd/worktree-rust-port/prefer-github-actions-pr-event-head-sha-over--99912b55 branch May 28, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant