fix(github-push): surface the real upstream status/body when a broker call fails - #262
Merged
Merged
Conversation
… call fails GitHubPushService::brokerCall() logged only a bare HTTP status (no body) on a non-2xx broker response and discarded the exception message on a transport failure; postJson()/createRepo() then threw a fixed, content-free RuntimeException that became the ExportJob's errorMessage verbatim, leaving a failed publish undiagnosable without a blind retry. Capture the real detail (HTTP status + a truncated, scrubbed body excerpt, or the scrubbed transport-failure reason) and fold it into the thrown message. Reuses the existing PAT-token scrub() already applied to broker exception messages; verified against CredentialBrokerService's contract that no other redaction is needed.
…ch it doesn't take
testPushFailsClosedWhenTheBrokerCannotServeTheCall assumed Server::get() would
throw in a bare PHPUnit environment, landing brokerCall() in its
transport-exception branch ("transport error: ..."). CI showed the real
behavior instead: the call falls through to the non-2xx branch and produces
"HTTP 0" — which already satisfies the actual requirement (diagnostic detail
present, not the bare fixed string), just via a different internal branch than
assumed. Drop the over-specific "transport error:" assertion; keep the two
that actually guard the requirement.
Contributor
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 14:50 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-19 15:34 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-19 16:29 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GitHubPushService::brokerCall()— the single choke point every GitHub write in the export pipeline goes through (createRepo, andpostJson()backingpushTree's blob/tree/commit/ref calls plusopenPullRequest) — logged only a bare HTTP status on a non-2xx broker response (no body) and discarded the exception message on a transport failure, returningnulleither way.postJson()/createRepo()then threw a fixed, content-freeRuntimeException('GitHub API call failed: POST ' . $path/'GitHub create-repo failed.') thatRunExportJob::run()writes verbatim into the ExportJob'serrorMessage— the only thing the user ever sees. This was observed live this session: a publish failure surfaced only the generic message, forcing a blind retry that happened to succeed without ever learning why the first attempt failed.brokerCall()now captures the real failure detail on a$lastFailureDetailproperty:HTTP {status}: {truncated, scrubbed body}for a completed non-2xx response, ortransport error: {scrubbed message}for a caught broker exception. Reset at the top of every call so a stale value can never leak into an unrelated failure.postJson()andcreateRepo()append that detail to theRuntimeExceptionthey already throw.scrub()helper (GitHub PAT-shaped token redaction) already applied to broker exception messages, and caps the body excerpt at 300 chars. Verified againstCredentialBrokerService::request()'s contract ({status, headers, body}returned verbatim from GitHub — GitHub's own response body cannot contain our secret) and the two exceptions it can throw (CredentialAccessDeniedException/CredentialUpstreamException, neither of which carries secret material) that no additional redaction is needed.openbuild-exporter's "Export target — GitHub repository" requirement (new scenario generalising the existing auth-failure scenario to any upstream failure).Test plan
tests/Unit/Service/GitHubPushServiceTest.phpextended: the existing fail-closed regression now asserts the thrown message carries transport-failure detail, not the bare fixed string; 5 new focused tests pinfailureDetailFromStatus()(status+body, empty-body, truncation),scrub()redacting a PAT-shaped token, andfailureSuffix()'s exact formatting.composer lint/phpcs/phpmd/phpstan/psalm— all clean on the full tree (psalm's 4 pre-existingZipArchive-missing errors are environmental, unrelated, present ondevelopmenttoo).development), same 23 pre-existing errors (ZipArchive/FlowServicemissing from this environment) — parity confirmed againstdevelopment.hydra-gates: same 3 pre-existing failures asdevelopment(e2e-coverage,manifest-validation,effective-manifest-crossref— allajv/node_modulesenvironmental, unrelated) — parity confirmed, zero new gate failures introduced.🤖 Generated with Claude Code