Skip to content

refactor(discovery): share the GitHub repository search steps of both GitHub providers - #568

Merged
rubenvdlinde merged 4 commits into
developmentfrom
refactor/github-discovery-shared
Nov 2, 2026
Merged

rubenvdlinde merged 4 commits into
developmentfrom
refactor/github-discovery-shared

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

The public GitHub search (GithubSearchDiscovery) and the PAT-scoped search (GithubPrivateDiscovery) each carried their own copy of the search request (URL, headers, timeout), the decoding of the items list and the mapping of a repository to a discovery hit. Those steps now live once in the MapsGithubRepositories trait. Each provider keeps its own error handling (the public search reports rate limits, HTTP errors and malformed JSON; the PAT search drops a failed request silently) and its own allowlist hint text.

REVIEW: this touches how a PAT is used. The bearer token is still passed only into the request headers inside PatManager::useToken; the new fetchItems marks it #[\SensitiveParameter] so it stays out of stack traces.

SIG numbers (gate 122, five-star profile, PHP, against development 3e6e4b8)

check before after 5-star 4-star
duplication 3.87% (389 lines) 3.35% (335 lines) 2.8% 5.6%
size >15 54.69% 53.04% 23.6% 47.1%
size >30 22.64% 20.76% 11.6% 23.1% (met, more margin)
McCabe >5 40.01% 38.26% 10.1% 20.2%
McCabe >10 15.75% 14.66% 3.7% 7.3%
params >=3 22.93% 23.10% 7.5% 15.0%
params >=5 6.84% 6.90% 1.65% 3.3%

The params shares rise only because about 90 lines of PHP left the measured code; no unit gained a parameter, and the LOC in units with 3+ parameters is unchanged. The next PR in this series (AuditLogger::record) takes that band down. Both search units left the >30 band (51 and 53 LOC before; now at most 15). Every touched or new unit is within the 5-star unit bounds (at most 15 LOC, McCabe 5, 2 params). versioniq stays 36/44 at 4-star. Gate 122 ratchet: 0 findings (1 inherited, GithubPrivateDiscovery::__construct).

Verification

  • Golden GithubDiscoveryGoldenTest: 42 cases over both search() methods (disabled, blank query, every response shape: items, no items, scalar, malformed, too deep, 403, 500, 301, unreachable; nine PAT patterns, two PATs with duplicate hits, a PAT whose token cannot be used), comparing the result and every HTTP, allowlist, PAT and logger call. Recorded on the old code; plain re-run green on old and new.
  • Capped mutants against the golden only: trait 20 of 20, public search 7 of 7, PAT search 4 of 5 killed; the survivor ($items === null ? [] : repoHits($items) to repoHits($items ?? [])) is equivalent. No-op control survived each round.
  • Full unit-only suite under pcov with --fail-on-risky: OK (811 tests). composer check:strict: lint and psalm clean; php-cs-fixer flags only the inherited RemoveRetiredCronJobs pair. Hydra gates --scope-to-diff --base origin/development: exit 0.

AI assisted (Claude Code); reviewed against the golden before opening.

Update after review (head e622e75)

fetchItems again catches only the request (now in send()) and the JSON decoding, as on development, so an error reading the status or the body propagates to search(), which logs the failed PAT search and returns its error. The golden gained 5 cases recorded on the old code: unreadable status and unreadable body for both providers, a client Error, and a second PAT whose body cannot be read; the previous head fails 3 of them. Mutants on the new boundaries: 5 of 5 killed. Full unit-only suite under pcov: OK (931 tests); psalm clean; gate 122 ratchet 0 findings; gate 123 36 held.

… GitHub providers

The public and the PAT-scoped GitHub search build the same request, decode
the same items list and map repositories to hits the same way; those steps
now live once in the MapsGithubRepositories trait. Each provider keeps its
own error handling and its own allowlist hint.

Assisted-by: ClaudeCode:claude-opus-5-5
…T-scoped search again

Only the request and the JSON decoding are caught, as on development, so a
failing status or body read reaches search() and is reported as a failed
PAT-scoped search. The golden now pins unreadable status, unreadable body and
a client Error for both providers, recorded on the old code.

Assisted-by: ClaudeCode:claude-opus-5-5
@rubenvdlinde
rubenvdlinde merged commit d8ad6b2 into development Nov 2, 2026
7 checks passed
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/versioniq @ fab2a73

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
composer ✅ ✅ 32/32
npm ❌ ✅ 302/302
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅ 947/947 · 7 skipped (8 legs)
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ✅

Quality workflow — 2026-11-03 00:28 UTC

Download the full PDF report from the workflow artifacts.

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