Skip to content

fix(get): return a non-zero exit code and report why a download failed - #110

Merged
roxblnfk merged 2 commits into
1.xfrom
fix/download-failure-reporting
Aug 11, 2026
Merged

roxblnfk merged 2 commits into
1.xfrom
fix/download-failure-reporting

Conversation

@roxblnfk

Copy link
Copy Markdown
Member

🔍 What was changed

  • dload get now exits with a non-zero code when at least one requested package was not installed. A failure of one package no longer cancels the remaining ones.
  • Unsuccessful repository API responses are converted into exceptions with actionable messages: rate limit (429, 403 with an exhausted x-ratelimit-remaining, primary and secondary limits, with reset time), invalid token (401), denied access (403), missing repository (404), server errors and malformed bodies. Every message states whether a token was configured and what to do with GITHUB_TOKEN / GITLAB_TOKEN.
  • API failures are no longer swallowed by the release page loaders. Repository fallback is preserved, but the reason of each attempt is collected and printed as a single report: matched releases, releases available in the repository, assets of every checked release and the filters that rejected them.
  • A downloaded asset whose content matches no extraction rule is now a failure (NothingExtracted) instead of a silent success — the message lists the rules and the files inside the asset.

Why?

A broken run was indistinguishable from a successful one. In this CI run the Download binaries step reported success while nothing was downloaded, and the build failed later with Roadrunner is not installed. The only trace was 0 releases found, because catch (\Throwable) { return; } in GitHubRepository::getReleases() and catch (ClientExceptionInterface) { return; } in RepositoryApi::getReleases() turned any HTTP error into an empty release list, and the rate limit check only recognised one legacy body format.

Checklist

  • How was this tested:
    • Tested manually — invalid token, GitHub 404, GitLab 404, unsatisfiable version constraint, asset pattern matching nothing, asset with no extractable file (all exit 1 with the report), plus successful download and skip-existing-binary (exit 0)
    • Unit tests added — 42 tests for the response validators, the failure report and NothingExtracted; ClientTest rewritten around a 12-case status/exception provider
    • Full suite green locally: 455 tests, including the acceptance suite that downloads real assets

Notes for review

  • secrets.GITHUB_TOKEN in GitHub Actions is scoped to its own repository and shares a 1,000 requests/hour limit across the whole job matrix, which is the likely trigger of the linked run. The exact status code cannot be recovered from that log — with this change it would have been printed.
  • Two unavoidable psalm suppressions were added next to the existing ones in psalm-baseline.xml (InternalMethod on collection iteration, PropertyNotSetInConstructor for the two new context fields).
  • GitHubRateLimitException and GitLabRateLimitException now extend the shared Module\Repository\Exception\RateLimitException; their constructor signatures changed and GitHubRateLimitException::fromApiResponse() was removed. Both classes are @internal.
  • Exit code of get changes for anyone who relied on it always being 0.

feat(repository): turn unsuccessful API responses into actionable errors

feat(downloader): report why every download attempt failed

fix(downloader): fail when nothing matches the extraction rules

docs(readme): document failure reporting and GITHUB_TOKEN limits in CI

A failed run used to be indistinguishable from a successful one: the command
always exited with 0, and any API failure (invalid token, exhausted rate limit,
missing repository) was swallowed by `catch (\Throwable) { return; }` in the
repository page loaders, surfacing only as "0 releases found".

Responses are now validated centrally per provider, so the message names the
HTTP status, whether a token was configured and what to do about it. Repository
fallback is preserved: the reasons are collected per attempt and printed as a
single report with matched releases, release assets and the filters that
rejected them.

Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 53.60825% with 270 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Module/Downloader/Downloader.php 0.00% 77 Missing ⚠️
...e/Repository/Internal/GitHub/Api/RepositoryApi.php 0.00% 47 Missing ⚠️
...e/Repository/Internal/GitLab/Api/RepositoryApi.php 0.00% 46 Missing ⚠️
...c/Module/Repository/Internal/ResponseValidator.php 81.28% 38 Missing ⚠️
src/Command/Get.php 0.00% 27 Missing ⚠️
...le/Repository/Internal/GitHub/GitHubRepository.php 0.00% 6 Missing ⚠️
...le/Repository/Internal/GitLab/GitLabRepository.php 0.00% 6 Missing ⚠️
src/Module/Downloader/Exception/DownloadFailed.php 0.00% 5 Missing ⚠️
...c/Module/Repository/Internal/GitLab/Api/Client.php 0.00% 5 Missing ⚠️
...pository/Internal/GitHub/Api/ResponseValidator.php 83.33% 3 Missing ⚠️
... and 8 more
Files with missing lines Coverage Δ Complexity Δ
...c/Module/Downloader/Exception/NothingExtracted.php 100.00% <100.00%> (ø) 4.00 <4.00> (?)
...oader/Internal/Diagnostics/DownloadDiagnostics.php 100.00% <100.00%> (ø) 8.00 <8.00> (?)
...Downloader/Internal/Diagnostics/ReleaseAttempt.php 100.00% <100.00%> (ø) 9.00 <9.00> (?)
...nloader/Internal/Diagnostics/RepositoryAttempt.php 100.00% <100.00%> (ø) 15.00 <15.00> (?)
src/Module/Downloader/Internal/DownloadContext.php 0.00% <ø> (ø) 1.00 <0.00> (ø)
...c/Module/Repository/Internal/GitHub/Api/Client.php 100.00% <100.00%> (ø) 5.00 <0.00> (-7.00)
.../Module/Repository/Internal/GitHub/GitHubAsset.php 0.00% <ø> (ø) 6.00 <0.00> (ø)
.../Module/Repository/Internal/GitLab/GitLabAsset.php 0.00% <ø> (ø) 6.00 <0.00> (ø)
src/DLoad.php 82.03% <92.30%> (+1.16%) 40.00 <3.00> (+4.00)
...rnal/GitHub/Exception/GitHubRateLimitException.php 0.00% <0.00%> (ø) 1.00 <0.00> (-1.00)
... and 16 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

fix(downloader): distinguish an unfetched asset list from an empty one in reports
fix(repository): keep truncated API messages valid UTF-8
fix(get): escape the software name in the failure header

A rate limit hit on a follow-up page used to stop pagination silently,
so the failure report claimed the repository had nothing more to offer.
GitLab pagination failures are now logged the same way as GitHub ones.

Assisted-By: Claude Fable 5 <noreply@anthropic.com>
@roxblnfk
roxblnfk merged commit 0f52a0f into 1.x Aug 11, 2026
23 of 26 checks passed
@roxblnfk
roxblnfk deleted the fix/download-failure-reporting branch August 11, 2026 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant