Fix E2E on Blacksmith runners: DoH polling with cache purge + safer PR trigger - #45
Merged
Merged
Conversation
Since the E2E workflow moved to Blacksmith runners (ec9fad2), all three e2e tests time out in pollDnsTxtRecord: the Cloudflare TXT records are created successfully, but the runner's system resolver keeps returning empty results for the full 10-minute polling window. Blacksmith routes DNS through its own resolver infrastructure, so freshly created records can stay invisible to Deno.resolveDns far longer than on GitHub-hosted runners. Instead of depending on the runner's DNS path, the e2e tests now poll propagation via the package's own DoH resolver (Cloudflare endpoint), which rides plain HTTPS and behaves the same on any runner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
… challenges" This reverts commit adf34ac.
The E2E suite has been failing on Blacksmith runners because their DNS path never surfaces the freshly created challenge TXT records to the test's polling. Move back to ubuntu-latest where the suite passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
…l_request_target pull_request_target ran the workflow and checked out the code from the base branch, so labeled PR runs only ever re-tested main — a PR could never actually exercise its own changes. Switch to a plain pull_request trigger with a protected 'e2e' environment: runs now use the PR's own workflow and code, while secret access still requires a manual approval on every run, including new pushes and fork PRs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
…l challenges" This reverts commit fd600b5.
With PR e2e runs now testing PR code, this combination can be verified for real: the DoH resolver should sidestep the Blacksmith DNS issue that broke polling before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
…t hang the suite The Blacksmith trial hung mid-run: the DoH fetch had no timeout, so a silently stalled connection to the resolver endpoint blocked pollDnsTxtRecord forever — its polling budget is only checked between attempts. Add an optional timeout to createResolveDns (resolveDns.doh) via AbortSignal.timeout, and make the e2e resolver try Cloudflare then Google with a 10s per-request timeout, logging failures and returning 'not visible yet' so polling retries. A full DoH outage now surfaces as the regular polling TimeoutError with diagnostics in the log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
…ce period Run 78 showed the real failure mode: DoH works on Blacksmith (2 of 3 tests passed), but polling starts the instant the Cloudflare API returns, so the first query can hit the resolver edge before the record propagates — the resulting NXDOMAIN gets negative-cached longer than the 10-minute polling budget, leaving one unlucky domain stuck at []. Two mitigations: - resolveDns now queries Cloudflare and Google DoH in parallel and merges answers, so a single resolver's stale negative cache can't stall polling; failures are logged and treated as 'not visible yet'. - Tests wait 15s after creating DNS records before the first lookup, so the negative cache is far less likely to be seeded at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
Trial whether the merged multi-resolver DoH polling alone is enough to keep e2e green on Blacksmith, without the extra 15s wait after record creation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
… Google DoH Simplify the e2e resolver back to Cloudflare DoH only. When a lookup returns empty, flush the record from 1.1.1.1 via the purge endpoint behind one.one.one.one/purge-cache, so a negatively cached NXDOMAIN seeded by a lookup that raced record propagation can't outlive the polling budget — the next attempt re-queries the authoritative servers. The purge endpoint is unofficial; lookup and purge failures are logged and treated as 'record not visible yet' so polling keeps retrying. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
The environment no longer has required reviewers; fork runs are gated by the repo-level 'require approval for all outside collaborators' setting instead. Describe what the environment actually does now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
… 10s The timeout option never addressed an observed failure — remove it to keep the resolveDns.doh API surface unchanged. Keep a hardcoded abort on the (unofficial) 1.1.1.1 purge request only. Bump pollDnsTxtRecord's default interval from 5s to 10s: DNS propagation is slow enough that 5s polling mostly burns queries (and now purge requests) without finding records sooner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
Remove the 1.1.1.1 purge-on-empty logic and restore the 5s default poll interval, leaving only the Cloudflare DoH resolver — the minimal change over the system resolver — to find out how much machinery the Blacksmith fix actually needs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
Bare Cloudflare DoH polling failed twice (runs 78, 86) with a TXT record stuck at [] for the whole 10-minute budget — an early lookup racing record propagation gets its NXDOMAIN negatively cached for longer than we can afford to wait. Purge before each lookup so every poll attempt effectively re-queries the authoritative servers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
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
E2E has been failing since the workflow moved to Blacksmith runners (#41). This PR makes the suite runner-proof and keeps it on Blacksmith, and fixes a security/correctness issue in how the workflow runs on PRs.
Root cause
Two separate DNS problems:
[]for the full 10 minutes while sibling records resolved in seconds.Changes
e2e/utils/resolveDns.ts(new): e2e DNS polling now purges the record from 1.1.1.1's cache (via the endpoint behind https://one.one.one.one/purge-cache/) before every lookup, then resolves over Cloudflare DoH. This bypasses the runner's DNS stack entirely and defeats the negative-cache race. Note: the purge endpoint is unofficial — if polling ever starts timing out with⚠️ Failed to purgewarnings in the log, check that endpoint first.resolveDns.deno(system resolver).e2e.yaml:runs-on: blacksmith-2vcpu-ubuntu-2404(kept on Blacksmith).pull_request_target+e2e:approvelabel with a plainpull_requesttrigger and the protectede2eenvironment. The old setup ran the workflow and code from the base branch, so PR runs never tested PR changes; the new setup tests the PR's own code, while fork PRs only receive the Cloudflare secrets after an explicit "Approve and run" (repo is set to require approval for all outside collaborators).src/is untouched — no published package behavior changes.Verification
Bare Cloudflare DoH (no purge) failed e2e runs 78 and 86 with the stuck-record signature; purge variants passed runs 82–85 and 87, typically completing in ~3 minutes on Blacksmith.
https://claude.ai/code/session_01JJeFA4CinTHKcfKHwCE7a9
<img alt="View with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1786626697&installation_id=142134082&pr_number=45&repository=fishballapp%2Facme&return_to=https%3A%2F%2Fgithub.com%2Ffishballapp%2Facme%2Fpull%2F45&signature=f6b8bb178033d246c929346d4b6c5cad69138055d213d5426da1a80ed3a3d724"rel="nofollow noreferrer noopener" target="_blank"><img alt="Autofix with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg">Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.