fix(EdgedriverBinary): generate per-platform URLs, drop dead listing API#1026
fix(EdgedriverBinary): generate per-platform URLs, drop dead listing API#1026
Conversation
…isting.json
The old Azure Blob container listing API stopped working:
https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver?prefix=<version>/&delimiter=/&comp=list
started returning HTTP 409 `PublicAccessNotPermitted` around 2026-04-07,
after Microsoft disabled public access on the storage account. The old
`msedgedriver.azureedge.net` CDN had already stopped resolving in
July 2025 (see MicrosoftEdge/EdgeWebDriver#203,
MicrosoftEdge/EdgeWebDriver#201, seleniumbase/SeleniumBase#3888).
This broke `EdgedriverBinary.fetch()` for any version subpath and made
`test/common/adapter/binary/EdgedriverBinary.test.ts` fail on the
`items.length >= 3` assertion.
Microsoft now hosts Edge WebDriver downloads on
`msedgedriver.microsoft.com`, and the same host serves a single
JSON dump that replaces the Azure Blob XML listing:
GET https://msedgedriver.microsoft.com/listing.json
-> {
items: [{ isDirectory, name, contentLength, lastModified }, ...],
generatedAt: <iso8601>
}
(This is the endpoint used by Microsoft's own catalog at
https://msedgedriver.microsoft.com/catalog/.) The adapter now fetches
this listing, filters by the requested version prefix, and exposes each
matching file as a `BinaryItem` pointing at
`https://msedgedriver.microsoft.com/<name>` for download.
Also mock the new endpoint in the EdgedriverBinary test with a fixture
(`test/fixtures/msedgedriver-listing.json`) so the test is no longer
dependent on live Microsoft endpoints.
Known coverage regression: the new `listing.json` only covers Edge
WebDriver versions from 112.0.1722.39 onwards (~2003 versions / 9000+
files as of 2026-04-08). Versions older than 112 are no longer
downloadable from any public Microsoft endpoint — they return HTTP
404 on `msedgedriver.microsoft.com/<old-version>/edgedriver_*.zip`
and HTTP 409 `PublicAccessNotPermitted` on the retired Azure Blob
container. This is a Microsoft-side change; cnpmcore users will have
to rely on previously mirrored copies for Edge WebDriver versions
< 112.
No formal Microsoft announcement for this specific storage lockdown,
but the migration has been discussed across multiple community
projects:
- MicrosoftEdge/EdgeWebDriver#183 (azureedge.net CDN sunset,
2025-01)
- MicrosoftEdge/EdgeWebDriver#146 (blob listing stopped updating
since 125.0, 2024-05)
- MicrosoftEdge/EdgeWebDriver#201, #203 (azureedge.net
unreachable, 2025-07/08)
- seleniumbase/SeleniumBase#3888 (CDN moved to
msedgedriver.microsoft.com, fixed in SeleniumBase 4.40.6)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughReplaces dynamic Azure-XML listing and parser in EdgeDriver binary retrieval with a deterministic generator of six platform ZIP entries per version; download URLs now use a fixed Microsoft CDN base, size/date set to '-' and missing artifacts are ignored via Changes
Sequence Diagram(s)(Skipped — changes are internal adapter logic and tests; do not introduce a multi-component runtime flow that benefits from a sequence diagram.) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1026 +/- ##
==========================================
+ Coverage 94.12% 94.32% +0.19%
==========================================
Files 211 211
Lines 8619 8664 +45
Branches 1672 1690 +18
==========================================
+ Hits 8113 8172 +59
+ Misses 506 492 -14 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request updates the EdgedriverBinary adapter to use the new JSON-based listing API from Microsoft, replacing the deprecated Azure Blob XML API. The changes include new interfaces for the JSON structure and updated fetching logic. Feedback focuses on optimizing performance by caching the large listing file to avoid redundant network requests, improving error handling when the API fails, and leveraging base class helpers to reduce code duplication.
| if (!listing) { | ||
| return { items: [], nextParams: null }; | ||
| } |
There was a problem hiding this comment.
Returning an empty items array when the listing fetch fails can be misleading, as it suggests the version exists but contains no files. It is safer to return undefined to signal a fetch failure. Additionally, verifying that listing.items exists before iterating prevents potential runtime errors if the API response structure is unexpected. Ensure that parsing of query parameters or API responses gracefully handles invalid inputs by providing sensible fallbacks or defaults.
| if (!listing) { | |
| return { items: [], nextParams: null }; | |
| } | |
| if (!listing?.items) { | |
| return; | |
| } |
References
- Ensure that parsing of query parameters gracefully handles invalid inputs (e.g., non-numeric values) by providing sensible fallbacks or defaults, rather than causing unexpected behavior.
|
@codex review |
The previous patch added a defensive branch in `#fetchListing()` that logs a warning and returns `undefined` on non-200 responses, and a matching `if (!listing)` branch in `fetch()` that returns an empty `items` array. Neither was exercised by the existing happy-path test, dropping patch coverage to ~82%. Add a second test case that mocks `https://msedgedriver.microsoft.com/listing.json` with `status: 500` and asserts that `fetch('/<version>/')` returns an empty item list rather than throwing. This covers both uncovered branches in a single case. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes EdgedriverBinary.fetch('/<version>/') by switching Edge WebDriver subdirectory discovery from the now-blocked Azure Blob XML listing API to Microsoft’s new https://msedgedriver.microsoft.com/listing.json, and updates tests to avoid relying on live upstream endpoints.
Changes:
- Update
EdgedriverBinaryto list per-version files by filteringmsedgedriver.microsoft.com/listing.jsonand generating download URLs frommsedgedriver.microsoft.com/<name>. - Update the EdgedriverBinary test to mock the new listing endpoint and adjust URL/date assertions.
- Add a fixture JSON response for
listing.jsonused by the test.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
app/common/adapter/binary/EdgedriverBinary.ts |
Switches version subdir listing to filter Microsoft’s listing.json and builds download URLs from the new host. |
test/common/adapter/binary/EdgedriverBinary.test.ts |
Mocks listing.json and updates assertions for the new URL host and date format. |
test/fixtures/msedgedriver-listing.json |
Adds a stable fixture response for listing.json used in tests. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34a47e14ec
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…reuse requestJSON, return undefined on failure
Apply three pieces of feedback from the PR review:
1. Cache the `listing.json` response per sync task.
`listing.json` is a ~9000-entry dump (~500KB). A single sync task
calls `fetch('/<version>/')` for many versions; previously each
call would re-download the whole listing. Cache it in a
promise-level field on the adapter and reset it in `initFetch` so
each new sync task gets fresh data.
2. Return `undefined` on listing failure instead of `{ items: [] }`.
The previous empty-items return value was ambiguous — callers
couldn't tell "listing API is down" from "this version genuinely
has no files". Returning `undefined` propagates the failure
signal cleanly. Also guard against malformed responses by
requiring `listing.items` to be an array before trusting it.
3. Reuse `AbstractBinary.requestJSON` instead of re-implementing
`this.httpclient.request` with the same options. The helper
already handles timeout / followRedirect / gzip / non-200
warn logging.
Test updates:
- The existing non-200 case now asserts `result === undefined`.
- A new caching test mocks `listing.json` with a call counter and
verifies two successive `fetch('/126.0.2578.0/')` calls produce
identical items while only hitting the network once.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
test/common/adapter/binary/EdgedriverBinary.test.ts (2)
20-23: Add a regression test for a non-200listing.jsonresponse.These new lines only cover the success case. The outage path this PR is fixing can regress silently unless we also mock a 409/500 response and assert the empty result plus the warning log from
#fetchListing().As per coding guidelines "Use app.mockHttpclient('https://example.com/path', 'GET', { data, persist }) for HTTP mocking in tests" and "Assert logs using
app.mockLog()followed byapp.expectLog(/pattern/)for log assertions in tests."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/common/adapter/binary/EdgedriverBinary.test.ts` around lines 20 - 23, Add a regression test in EdgedriverBinary.test that exercises the non-200 path of `#fetchListing`(): use app.mockHttpclient('https://msedgedriver.microsoft.com/listing.json', 'GET', { data: <any>, persist: false }) to return a 409 (or 500) response and then call the same code that invokes fetchListing(); assert it returns an empty result and capture the warning by calling app.mockLog() before execution and app.expectLog(/listing.*status|warning/i) (or similar pattern) afterwards; ensure the test follows existing success-case structure and uses the same helpers (app.mockHttpclient, app.mockLog, app.expectLog) to validate both the empty return and the warning log from fetchListing().
64-76: Assert the exact child entries instead of broad regexes.Lines 65-76 are loose enough that this still passes if a platform artifact disappears, if the URL loses the version prefix, or if the new direct-child filter starts leaking extra entries. Please lock this down with the fixture’s exact names/count/URLs, and add one nested or
isDirectory: truefixture entry so the exclusion branch is exercised.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/common/adapter/binary/EdgedriverBinary.test.ts` around lines 64 - 76, The test currently uses broad regex asserts on result.items which can hide regressions; update the assertion in EdgedriverBinary.test.ts to assert the exact expected child entries (names, urls, sizes, dates and isDir flags) and exact length instead of regexes: build an expectedItems array and deep-compare it to result.items (or compare lengths then iterate exact equality for each field), and add one fixture entry with isDir: true (a nested directory) to exercise the exclusion branch in the adapter logic; reference result.items and the test assertions around item.name/item.url/item.isDir when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/common/adapter/binary/EdgedriverBinary.ts`:
- Around line 192-195: The code repeatedly calls this.#fetchListing() (in
EdgedriverBinary) for every version-directory lookup, causing redundant network
requests; add an instance-level memoization field (e.g., a private property like
this.#listingCache) and have the method that currently calls
this.#fetchListing() first check and reuse the cached listing.json if present,
only invoking this.#fetchListing() to populate the cache on the first call;
ensure the cache is used across subdirectory/version fetches and
invalidated/refreshed only when needed.
---
Nitpick comments:
In `@test/common/adapter/binary/EdgedriverBinary.test.ts`:
- Around line 20-23: Add a regression test in EdgedriverBinary.test that
exercises the non-200 path of `#fetchListing`(): use
app.mockHttpclient('https://msedgedriver.microsoft.com/listing.json', 'GET', {
data: <any>, persist: false }) to return a 409 (or 500) response and then call
the same code that invokes fetchListing(); assert it returns an empty result and
capture the warning by calling app.mockLog() before execution and
app.expectLog(/listing.*status|warning/i) (or similar pattern) afterwards;
ensure the test follows existing success-case structure and uses the same
helpers (app.mockHttpclient, app.mockLog, app.expectLog) to validate both the
empty return and the warning log from fetchListing().
- Around line 64-76: The test currently uses broad regex asserts on result.items
which can hide regressions; update the assertion in EdgedriverBinary.test.ts to
assert the exact expected child entries (names, urls, sizes, dates and isDir
flags) and exact length instead of regexes: build an expectedItems array and
deep-compare it to result.items (or compare lengths then iterate exact equality
for each field), and add one fixture entry with isDir: true (a nested directory)
to exercise the exclusion branch in the adapter logic; reference result.items
and the test assertions around item.name/item.url/item.isDir when making this
change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5983b8ef-f5b0-4b12-b0bd-9d7d0d6f4d67
📒 Files selected for processing (3)
app/common/adapter/binary/EdgedriverBinary.tstest/common/adapter/binary/EdgedriverBinary.test.tstest/fixtures/msedgedriver-listing.json
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/common/adapter/binary/EdgedriverBinary.ts (1)
15-24: Export the new listing contracts.
EdgedriverListingEntryandEdgedriverListingare file-local right now. Exporting them keeps the payload shape reusable for tests or sibling modules instead of forcing a second declaration later.♻️ Proposed change
-interface EdgedriverListingEntry { +export interface EdgedriverListingEntry { isDirectory: boolean; name: string; contentLength: number; lastModified: string; } -interface EdgedriverListing { +export interface EdgedriverListing { items: EdgedriverListingEntry[]; generatedAt: string; }As per coding guidelines, "Export types and interfaces for reusability across modules".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/common/adapter/binary/EdgedriverBinary.ts` around lines 15 - 24, The interfaces EdgedriverListingEntry and EdgedriverListing are currently file-local; export them so other modules/tests can import the payload shapes. Update the declarations for EdgedriverListingEntry and EdgedriverListing to be exported (e.g., export interface EdgedriverListingEntry { ... } and export interface EdgedriverListing { ... }) and ensure any local usages still compile after making them exported.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/common/adapter/binary/EdgedriverBinary.ts`:
- Around line 223-228: The `#fetchListing` method caches the result in
`#listingPromise` but currently returns undefined for unusable/non-throwing
responses without clearing the cache, causing subsequent calls to reuse the bad
undefined; update `#fetchListing` (and related flow that returns undefined from
`#loadListing`) to clear/reset this.#listingPromise before returning undefined so
a future call will attempt a fresh fetch (mirror the existing cache-clearing
behavior used in the catch branch), referencing the `#fetchListing`,
`#listingPromise` and `#loadListing` symbols to locate and modify the logic.
---
Nitpick comments:
In `@app/common/adapter/binary/EdgedriverBinary.ts`:
- Around line 15-24: The interfaces EdgedriverListingEntry and EdgedriverListing
are currently file-local; export them so other modules/tests can import the
payload shapes. Update the declarations for EdgedriverListingEntry and
EdgedriverListing to be exported (e.g., export interface EdgedriverListingEntry
{ ... } and export interface EdgedriverListing { ... }) and ensure any local
usages still compile after making them exported.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2fd02f04-3f1d-4534-a889-8c6c656e46c8
📒 Files selected for processing (2)
app/common/adapter/binary/EdgedriverBinary.tstest/common/adapter/binary/EdgedriverBinary.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- test/common/adapter/binary/EdgedriverBinary.test.ts
… entirely Previous attempt fetched `https://msedgedriver.microsoft.com/listing.json` and filtered by version prefix. This had two problems: 1. `listing.json` is a ~1.2MB static dump containing every version since 112.0.1722.39 (~9000 entries). It does not support any query/filter parameter — all of `?top`, `?limit`, `?max`, `?pageSize`, `?prefix`, etc. return the same 1212112-byte payload. A per-sync cache helps but still downloads 1.2MB per sync. 2. The promise-level cache in the previous commit leaked across tests because `EdgedriverBinary` is a `@SingletonProto` and the test suite shares one instance. Instead, mirror the approach already used by `FirefoxBinary` and `ChromeForTestingBinary`: don't call any listing API for sub-dirs at all. Microsoft hosts per-version driver files at a stable URL pattern https://msedgedriver.microsoft.com/<version>/edgedriver_<platform>.zip where `<platform>` is one of the six known values observed in the current listing (arm64, linux64, mac64, mac64_m1, win32, win64). The adapter now generates one `BinaryItem` per platform with `ignoreDownloadStatuses: [404]`, so older versions that don't ship every platform (or versions that are gated by Microsoft) are skipped cleanly by cnpmcore's sync pipeline rather than failing the task. Side benefits: - 0 bytes downloaded per sub-dir fetch. - No per-sync cache needed. - No singleton state pollution between tests. - Smaller surface area (removes ~60 lines of listing parse / fallback code, plus the XML `requestXml` dependency in this adapter). Test changes: - The happy path test now asserts the exact 6 generated items with `ignoreDownloadStatuses: [404]` on each. - The "listing request fails" test is removed — there is no listing request anymore. - The `beforeEach` calls `initFetch()` to reset `dirItems` between tests, so the singleton-shared state doesn't leak. - The now-unused `test/fixtures/msedgedriver-listing.json` fixture is removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| // Microsoft moved Edge WebDriver binaries to https://msedgedriver.microsoft.com/ | ||
| // in July 2025 after `msedgedriver.azureedge.net` was retired, and around | ||
| // 2026-04-07 also disabled public access on the legacy Azure Blob container | ||
| // that used to host the XML file listing. There is still no paginated/filtered | ||
| // listing API — the only "listing" endpoint on the new host is a ~1.2MB static | ||
| // JSON dump (`/listing.json`, ~9000 entries covering every version since | ||
| // 112.0.1722.39). | ||
| // | ||
| // To avoid hammering that 1.2MB dump for every version subdirectory during a | ||
| // sync, we mirror the approach used by `FirefoxBinary` / `ChromeForTestingBinary` | ||
| // and generate the per-version download URLs from a static list of known | ||
| // platform filenames. cnpmcore's sync pipeline honors the per-item | ||
| // `ignoreDownloadStatuses` field, so any version that doesn't ship a given | ||
| // platform (e.g. older builds without `edgedriver_mac64_m1.zip`) gets a clean | ||
| // 404 and is skipped rather than failing the sync. |
There was a problem hiding this comment.
PR title/description indicate switching EdgeDriver subdir listing to https://msedgedriver.microsoft.com/listing.json, but the adapter now explicitly avoids any listing endpoint and generates a static set of platform URLs. Either update the PR metadata/rationale (and comments) to reflect the actual approach, or implement the listing.json-based discovery described in the PR to keep behavior and documentation aligned.
oxfmt prefers `(name) => ({...})` over `name => ({...})` for arrow
function arguments. Brought up by cnpmcore CI's `fmtcheck` step.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 4.32.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
…ary fix Picks up cnpm/cnpmcore#1026, which replaces the dead Azure Blob XML listing API with directly-generated per-platform download URLs on msedgedriver.microsoft.com. This unblocks the chore-ut-ci E2E run, which has been failing on `EdgedriverBinary.test.ts > should work` with `items.length >= 3` since 2026-04-08 04:19 UTC. Old hash: e82df3f4 (pre-fix) New hash: 98463c33 (cnpmcore release v4.32.1, contains the merged PR) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…API (#1026) `EdgedriverBinary.fetch('/<version>/')` has been broken since around **2026-04-07** — the legacy Azure Blob container listing API now returns **HTTP 409 `PublicAccessNotPermitted`** after Microsoft disabled public access on the storage account: ``` https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver?prefix=<version>/&delimiter=/&comp=list → HTTP 409 PublicAccessNotPermitted ``` This made `test/common/adapter/binary/EdgedriverBinary.test.ts > fetch() > should work` fail on the `items.length >= 3` assertion, blocking `@eggjs/egg` monorepo's E2E pipeline and any project depending on EdgedriverBinary. Microsoft has migrated Edge WebDriver hosting to `https://msedgedriver.microsoft.com/`. Per-version files are still reachable at the same path pattern: ``` https://msedgedriver.microsoft.com/<version>/edgedriver_<platform>.zip ``` The new host **also** serves a `listing.json` dump (used by Microsoft's own catalog page at https://msedgedriver.microsoft.com/catalog/), but it has two drawbacks: 1. **It's a single static file containing every version since `112.0.1722.39`** — ~9000 entries, ~1.2MB. I verified empirically that **none of `?top` / `?limit` / `?max` / `?pageSize` / `?prefix` / `?latest` query parameters do anything** — the response is byte-identical regardless of query string. There is no server-side filtering. 2. Even with caching, that's 1.2MB extra per sync. So instead this PR mirrors the approach already used by [`FirefoxBinary`](https://github.com/cnpm/cnpmcore/blob/master/app/common/adapter/binary/FirefoxBinary.ts) and [`ChromeForTestingBinary`](https://github.com/cnpm/cnpmcore/blob/master/app/common/adapter/binary/ChromeForTestingBinary.ts): **don't call any listing API for sub-dirs at all.** Instead, enumerate the per-version download URLs from a static list of known platform filenames: ```ts const EDGEDRIVER_PLATFORM_FILES = [ 'edgedriver_arm64.zip', 'edgedriver_linux64.zip', 'edgedriver_mac64.zip', 'edgedriver_mac64_m1.zip', 'edgedriver_win32.zip', 'edgedriver_win64.zip', ] as const; ``` Each emitted `BinaryItem` carries `ignoreDownloadStatuses: [404]`, so any version that doesn't ship every platform (e.g. older builds without `mac64_m1`) is skipped cleanly by cnpmcore's sync pipeline rather than failing the task. The root-dir listing path (`#syncDirItems` → `https://edgeupdates.microsoft.com/api/products`) is **completely unchanged** — that JSON API still works. | | Use `listing.json` | Generate URLs (this PR) | |---|---|---| | Network per sub-dir fetch | ~1.2MB JSON dump | **0 bytes** | | Per-sync caching needed | yes (~1.2MB cached + reset logic) | no | | Failure modes | listing endpoint down → all sub-dir fetches fail | per-platform 404 handled cleanly | | Code surface | listing parse + cache + error path | one `.map()` over a const array | | Singleton state safety | needs careful reset in `initFetch` | none, stateless | | Platform discovery | from server response | static const (loses any net-new platform until updated) | The trade-off is that if Microsoft adds a new Edge WebDriver platform (e.g. `linux_arm64`), this adapter won't pick it up until the const list is updated. That's a maintenance cost but a small one — the platform list has been stable at six entries throughout the entire history of the new CDN. - **Root-dir listing (`fetch('/')`)**: still uses `https://edgeupdates.microsoft.com/api/products`. Microsoft only exposes the very latest release of each channel via this API, so the result today is ~5 versions total (one per channel: Stable / Beta / Dev / Canary). Historical version coverage was — and still is — governed by what cnpmcore had previously synced, not by what edgeupdates returns now. - **Version ordering in the root listing**: items come out in the order Microsoft's API returns them, which is grouped by channel (Stable → Beta → Dev → Canary), newest within each channel. This is *not* a strict latest-first sort, but it matches the pre-PR behavior. If a strict ordering is desired, that's a separate change. Microsoft's per-version download URL works for **versions from `112.0.1722.39` onwards**. Versions older than 112 are no longer downloadable from any public Microsoft endpoint: | Version | Old Blob URL | New URL | |---|---|---| | 80.0.361.111 | 409 PublicAccessNotPermitted | 404 | | 100.0.1185.27 | 409 PublicAccessNotPermitted | 404 | | 110.0.1587.69 | 409 PublicAccessNotPermitted | 404 | | **112.0.1722.39** | 409 PublicAccessNotPermitted | **200 ✓** | | 120.0.2210.133 | 409 PublicAccessNotPermitted | 200 ✓ | | 148.0.3966.0 | 409 PublicAccessNotPermitted | 200 ✓ | This is a Microsoft-side cutoff, not something cnpmcore can work around. **Existing mirrored copies of pre-112 Edge WebDriver binaries in cnpmcore's own storage are unaffected** — this PR only changes *discovery* of new files from upstream. Also adds `await binary.initFetch()` in the test's `beforeEach`. `EdgedriverBinary` is a `@SingletonProto`, so `app.getEggObject(EdgedriverBinary)` returns the same instance across tests, which means `dirItems` populated by one test would leak into the next. This was a pre-existing latent issue masked by the old test only having one `it` block, but it surfaces immediately when adding more cases. There is no formal Microsoft announcement for this specific storage lockdown, but the migration off `azureedge.net` and the gradual lockdown of the legacy XML listing have been documented across multiple community projects. Timeline: - **2024-05** — [MicrosoftEdge/EdgeWebDriver#146](MicrosoftEdge/EdgeWebDriver#146): Azure Blob listing stopped updating for versions ≥ 125.0. - **2025-01** — [MicrosoftEdge/EdgeWebDriver#183](MicrosoftEdge/EdgeWebDriver#183): `azureedge.net` CDN sunset (Edgio bankruptcy); Microsoft initially said it would continue under a different CDN provider. - **2025-07** — [seleniumbase/SeleniumBase#3888](seleniumbase/SeleniumBase#3888), [MicrosoftEdge/EdgeWebDriver#201](MicrosoftEdge/EdgeWebDriver#201), [#203](MicrosoftEdge/EdgeWebDriver#203): `msedgedriver.azureedge.net` DNS stopped resolving; new CDN is `msedgedriver.microsoft.com`. SeleniumBase fixed this in 4.40.6. - **2026-04-07** — (this PR): The Azure Blob container backing the old XML listing also had public access disabled; only `msedgedriver.microsoft.com` works now. - [x] `EdgedriverBinary.test.ts > should list recent stable versions from edgeupdates.microsoft.com` — green - [x] `EdgedriverBinary.test.ts > should generate all known platform driver URLs for a version` — green (asserts the exact 6 generated items with `ignoreDownloadStatuses: [404]` on each) - [x] **All 6 cnpmcore CI matrices** (`mysql node@22/24 × jsonBuilder true/false`, `postgresql node@22/24`) green - [x] `test-deployment`, `typecheck` (lint + fmtcheck + tsc), `codecov/patch`, `codecov/project` green - [ ] Manual smoke (suggested for reviewer): a fresh sync of a recent Edge WebDriver version (e.g. 148.0.3966.0) enumerates all 6 platform binaries and downloads all of them successfully, without 404s - [ ] Manual smoke (suggested for reviewer): a sync of an older version that doesn't ship every platform (e.g. 112.0.1722.39 has no `mac64_m1`) — the missing platform should be skipped via `ignoreDownloadStatuses: [404]` and the rest should complete 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> * **Bug Fixes** * Improved handling of unavailable EdgeDriver versions for specific platforms—missing combinations are now skipped instead of causing failures. * **Improvements** * Enhanced EdgeDriver binary delivery infrastructure for greater reliability and consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
EdgedriverBinary.fetch('/<version>/')has been broken since around 2026-04-07 — the legacy Azure Blob container listing API now returns HTTP 409PublicAccessNotPermittedafter Microsoft disabled public access on the storage account:This made
test/common/adapter/binary/EdgedriverBinary.test.ts > fetch() > should workfail on theitems.length >= 3assertion, blocking@eggjs/eggmonorepo's E2E pipeline and any project depending on EdgedriverBinary.Fix
Microsoft has migrated Edge WebDriver hosting to
https://msedgedriver.microsoft.com/. Per-version files are still reachable at the same path pattern:The new host also serves a
listing.jsondump (used by Microsoft's own catalog page at https://msedgedriver.microsoft.com/catalog/), but it has two drawbacks:112.0.1722.39— ~9000 entries, ~1.2MB. I verified empirically that none of?top/?limit/?max/?pageSize/?prefix/?latestquery parameters do anything — the response is byte-identical regardless of query string. There is no server-side filtering.So instead this PR mirrors the approach already used by
FirefoxBinaryandChromeForTestingBinary: don't call any listing API for sub-dirs at all. Instead, enumerate the per-version download URLs from a static list of known platform filenames:Each emitted
BinaryItemcarriesignoreDownloadStatuses: [404], so any version that doesn't ship every platform (e.g. older builds withoutmac64_m1) is skipped cleanly by cnpmcore's sync pipeline rather than failing the task.The root-dir listing path (
#syncDirItems→https://edgeupdates.microsoft.com/api/products) is completely unchanged — that JSON API still works.Why not use
listing.jsonat all?listing.json.map()over a const arrayinitFetchThe trade-off is that if Microsoft adds a new Edge WebDriver platform (e.g.
linux_arm64), this adapter won't pick it up until the const list is updated. That's a maintenance cost but a small one — the platform list has been stable at six entries throughout the entire history of the new CDN.What this PR does NOT change
fetch('/')): still useshttps://edgeupdates.microsoft.com/api/products. Microsoft only exposes the very latest release of each channel via this API, so the result today is ~5 versions total (one per channel: Stable / Beta / Dev / Canary). Historical version coverage was — and still is — governed by what cnpmcore had previously synced, not by what edgeupdates returns now.Impact on historical versions
Microsoft's per-version download URL works for versions from
112.0.1722.39onwards. Versions older than 112 are no longer downloadable from any public Microsoft endpoint:This is a Microsoft-side cutoff, not something cnpmcore can work around. Existing mirrored copies of pre-112 Edge WebDriver binaries in cnpmcore's own storage are unaffected — this PR only changes discovery of new files from upstream.
Side fix
Also adds
await binary.initFetch()in the test'sbeforeEach.EdgedriverBinaryis a@SingletonProto, soapp.getEggObject(EdgedriverBinary)returns the same instance across tests, which meansdirItemspopulated by one test would leak into the next. This was a pre-existing latent issue masked by the old test only having oneitblock, but it surfaces immediately when adding more cases.Context / related issues
There is no formal Microsoft announcement for this specific storage lockdown, but the migration off
azureedge.netand the gradual lockdown of the legacy XML listing have been documented across multiple community projects. Timeline:azureedge.netCDN sunset (Edgio bankruptcy); Microsoft initially said it would continue under a different CDN provider.msedgedriver.azureedge.netDNS stopped resolving; new CDN ismsedgedriver.microsoft.com. SeleniumBase fixed this in 4.40.6.msedgedriver.microsoft.comworks now.Test plan
EdgedriverBinary.test.ts > should list recent stable versions from edgeupdates.microsoft.com— greenEdgedriverBinary.test.ts > should generate all known platform driver URLs for a version— green (asserts the exact 6 generated items withignoreDownloadStatuses: [404]on each)mysql node@22/24 × jsonBuilder true/false,postgresql node@22/24) greentest-deployment,typecheck(lint + fmtcheck + tsc),codecov/patch,codecov/projectgreenmac64_m1) — the missing platform should be skipped viaignoreDownloadStatuses: [404]and the rest should complete🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Improvements