Skip to content

[wrangler] Pick the nearest configuration file, whatever its format - #15664

Open
oddharsh wants to merge 9 commits into
cloudflare:mainfrom
oddharsh:config-discovery-nearest-first
Open

oddharsh wants to merge 9 commits into
cloudflare:mainfrom
oddharsh:config-discovery-nearest-first

Conversation

@oddharsh

@oddharsh oddharsh commented Sep 16, 2026

Copy link
Copy Markdown

Fixes #15663

findWranglerConfig ran three find-up searches one after the other, so wrangler.json was searched all the way to the filesystem root before wrangler.jsonc or wrangler.toml in the reference directory was looked at. A wrangler.json in any ancestor directory therefore shadowed the project's own config, silently: wrangler deploy in a directory holding a perfectly good wrangler.jsonc printed the ancestor's bindings and would have built the ancestor's Worker. The four-line reproduction and the story of how I hit it are on the issue.

What changed

  • findNearestUserConfig walks up from the reference path one directory at a time (empathic/walk, already a dependency) and checks all three file names in each directory before moving to its parent. The same-directory preference is unchanged: wrangler.json, then wrangler.jsonc, then wrangler.toml.
  • It stays file-only, matching the find.file calls it replaces. empathic's find.any(names) does the per-directory walk in fewer lines but matches with existsSync, so a directory named wrangler.json would have won; there is a test for that shape now.
  • findRedirectedWranglerConfig is untouched.

The test that pinned the old behaviour

findWranglerConfig.test.ts had should prefer the wrangler.%s over wrangler.%s > in different directories, which asserted that a root wrangler.json beats foo/wrangler.jsonc when searching from ./foo. It came in with #7442 around code that was already there, and I could not find a discussion of the cross-directory case in that PR or elsewhere, so I have read it as a test that captured what the code did rather than a decision. That expectation now names the nearer file, and the describe block is renamed to say what it actually asserts (only when they are in the same directory). Two cases are added: a three-level json/toml/jsonc mix searched from each level, and a directory carrying a config file's name.

If the format-over-proximity order was intentional, I would rather this became a docs PR saying so than land; nothing currently documents it either way.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: this makes discovery match what the configuration docs already imply (both formats supported, nearest wins). Happy to add a sentence on the configuration page if you would like the precedence spelled out.

Locally: pnpm -F @cloudflare/workers-utils test 1066/1066; wrangler's entry-points, deploy/core, pages/deploy suites 166/166 against the rebuilt workers-utils; oxfmt and oxlint --type-aware clean on both files. Control: with the source change stashed, the changed expectation and the new three-level case fail against the old implementation (4 failures), so the tests are asserting the behaviour rather than the fixture.


Devin Review

findWranglerConfig ran three find-up searches in sequence, so a
wrangler.json anywhere up the tree was found before a wrangler.jsonc or
wrangler.toml in the reference directory was ever tried. Walk one
directory at a time instead, checking all three names per level. The
same-directory preference (json, jsonc, toml) is unchanged.

The cross-directory case was pinned the other way by a test cloudflare#7442 added
around the existing behaviour; that expectation now names the nearer
file, and two cases cover a three-level mix and a directory that has a
config file's name.
@changeset-bot

changeset-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3411150

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
wrangler Patch
@cloudflare/workers-utils Patch
@cloudflare/vite-plugin Patch
@cloudflare/vitest-plugin Patch
@cloudflare/autoconfig Patch
@cloudflare/cli-shared-helpers Patch
@cloudflare/containers-shared Patch
@cloudflare/deploy-helpers Patch
@cloudflare/remote-bindings Patch
@cloudflare/workers-auth Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Sep 16, 2026
@workers-devprod
workers-devprod requested review from a team and NuroDev and removed request for a team September 16, 2026 03:01
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/nearest-config-wins.md: [@cloudflare/wrangler]
  • packages/workers-utils/src/config/config-helpers.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/config/findWranglerConfig.test.ts: [@cloudflare/wrangler]

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@pkg-pr-new

pkg-pr-new Bot commented Sep 16, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@15664

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@15664

@cloudflare/codemods

npm i https://pkg.pr.new/@cloudflare/codemods@15664

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@15664

@cloudflare/containers-shared

npm i https://pkg.pr.new/@cloudflare/containers-shared@15664

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@15664

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@15664

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@15664

miniflare

npm i https://pkg.pr.new/miniflare@15664

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@15664

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@15664

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@15664

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@15664

@cloudflare/vitest-plugin

npm i https://pkg.pr.new/@cloudflare/vitest-plugin@15664

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@15664

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@15664

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@15664

wrangler

npm i https://pkg.pr.new/wrangler@15664

commit: 3411150

Comment thread packages/workers-utils/src/config/config-helpers.ts Outdated
Comment on lines +96 to +119
/**
* Walk up from `referencePath` and return the first user configuration file found,
* checking every recognised file name in each directory before moving to its parent.
*
* Proximity wins over format: a `wrangler.jsonc` in the current directory is chosen
* over a `wrangler.json` in a parent. Searching for each file name all the way to the
* filesystem root before trying the next name would let an unrelated ancestor's config
* shadow the project's own.
*/
function findNearestUserConfig(referencePath: string): string | undefined {
for (const dir of walk.up(referencePath)) {
for (const name of USER_CONFIG_FILE_NAMES) {
const candidate = path.join(dir, name);
try {
if (statSync(candidate).isFile()) {
return candidate;
}
} catch {
// not present in this directory, try the next name
}
}
}
return undefined;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this kind of a change / fix not be better served upstreamed to empathic/find? If it, afaict, seems to be performing a similar kind of file tree walking like empathic/find does but using statSync to check if it's a file?

Also with this new function is the find glob import even being used anymore?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe find is still used on Line 135 of the branch, when findRedirectedWranglerConfig calls find.file(PATH_TO_DEPLOY_CONFIG, { cwd }), but the PR only replaced the three chained find.file calls inside findWranglerConfig; walk was added for the new function, and find stays for the redirect lookup

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed on the upstream to find, and appreciate the flag - I gave that a go!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alrighty, the change should be in empathic 2.1!

@github-project-automation github-project-automation Bot moved this from Untriaged to In Review in workers-sdk Sep 18, 2026
Co-authored-by: Ben <4991309+NuroDev@users.noreply.github.com>
`statSync(p, { throwIfNoEntry: false })` is what cloudflare#12602 moved this package
to; the try/catch was copied from empathic, which supports Node 14 and
cannot use it. lukeed/empathic#14 adds `type: "file"` to `find.any`, which
is this function.
devin-ai-integration[bot]

This comment was marked as resolved.

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

oddharsh and others added 4 commits September 18, 2026 12:54
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Tests (macOS, packages-and-tools) failed in miniflare test/plugins/r2/s3.spec.ts
with read ECONNRESET, which this PR does not touch; the same reset hit
test/index.spec.ts on Windows on an unrelated branch the same hour.
Tests (Windows, fixtures): entrypoints-rpc timed out on a dev-server fetch
(UND_ERR_HEADERS_TIMEOUT). Wrangler E2E (macOS, shard 4/4): cancelled at the
30 minute job timeout while the other 11 shards passed, including shard 4 on
Linux and Windows. The job that failed on the previous run passed on this one.
@Cherry

Cherry commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Question: would this not be a breaking change as per an older PR where a very similar implementation was rejected for this reason? #7799 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

[wrangler] A wrangler.json in any ancestor directory shadows the project's own wrangler.jsonc/wrangler.toml

4 participants