-
Notifications
You must be signed in to change notification settings - Fork 756
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for redirecting Wrangler to a generated config when…
… running deploy commands This new feature is designed for build tools and frameworks to provide a deploy-specific configuration, which Wrangler can use instead of user configuration when running deploy commands. It is not expected that developers of Workers will need to use this feature directly. The commands that use this feature are: - `wrangler deploy` - `wrangler dev` - `wrangler versions upload` - `wrangler versions deploy` When running these commands, Wrangler will look up the directory tree from the current working directory for a file at the path `.wrangler/deploy/config.json`. This file must contain only a single JSON object of the form: ```json { "configPath": "../../path/to/wrangler.json" } ``` When this file exists Wrangler will use the `configPath` (relative to the `deploy.json` file) to find an alternative Wrangler configuration file to load and use as part of this command. When this happens Wrangler will display a warning to the user to indicate that the configuration has been redirected to a different file than the user's configuration file. A common approach that a build tool might choose to implement. - The user writes code that uses Cloudflare Workers resources, configured via a user `wrangler.toml` file. ```toml name = "my-worker" main = "src/index.ts" [[kv_namespaces]] binding = "<BINDING_NAME1>" id = "<NAMESPACE_ID1>" ``` Note that this configuration points `main` at user code entry-point. - The user runs a custom build, which might read the `wrangler.toml` to find the entry-point: ```bash > my-tool build ``` - This tool generates a `dist` directory that contains both compiled code and a new deployment configuration file, but also a `.wrangler/deploy/config.json` file that redirects Wrangler to this new deployment configuration file: ```plain - dist - index.js - wrangler.json - .wrangler - deploy - config.json ``` The `dist/wrangler.json` will contain: ```json { "name": "my-worker", "main": "./index.js", "kv_namespaces": [{ "binding": "<BINDING_NAME1>", "id": "<NAMESPACE_ID1>" }] } ``` And the `.wrangler/deploy/config.json` will contain: ```json { "configPath": "../../dist/wrangler.json" } ```
- Loading branch information
1 parent
a29a41c
commit e251387
Showing
14 changed files
with
457 additions
and
49 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
--- | ||
"wrangler": minor | ||
--- | ||
|
||
feat: add support for redirecting Wrangler to a generated config when running deploy commands | ||
|
||
This new feature is designed for build tools and frameworks to provide a deploy-specific configuration, | ||
which Wrangler can use instead of user configuration when running deploy commands. | ||
It is not expected that developers of Workers will need to use this feature directly. | ||
|
||
### Affected commands | ||
|
||
The commands that use this feature are: | ||
|
||
- `wrangler deploy` | ||
- `wrangler dev` | ||
- `wrangler versions upload` | ||
- `wrangler versions deploy` | ||
|
||
### Config redirect file | ||
|
||
When running these commands, Wrangler will look up the directory tree from the current working directory for a file at the path `.wrangler/deploy/config.json`. This file must contain only a single JSON object of the form: | ||
|
||
```json | ||
{ "configPath": "../../path/to/wrangler.json" } | ||
``` | ||
|
||
When this file exists Wrangler will use the `configPath` (relative to the `deploy.json` file) to find an alternative Wrangler configuration file to load and use as part of this command. | ||
|
||
When this happens Wrangler will display a warning to the user to indicate that the configuration has been redirected to a different file than the user's configuration file. | ||
|
||
### Custom build tool example | ||
|
||
A common approach that a build tool might choose to implement. | ||
|
||
- The user writes code that uses Cloudflare Workers resources, configured via a user `wrangler.toml` file. | ||
|
||
```toml | ||
name = "my-worker" | ||
main = "src/index.ts" | ||
[[kv_namespaces]] | ||
binding = "<BINDING_NAME1>" | ||
id = "<NAMESPACE_ID1>" | ||
``` | ||
|
||
Note that this configuration points `main` at user code entry-point. | ||
|
||
- The user runs a custom build, which might read the `wrangler.toml` to find the entry-point: | ||
|
||
```bash | ||
> my-tool build | ||
``` | ||
|
||
- This tool generates a `dist` directory that contains both compiled code and a new deployment configuration file, but also a `.wrangler/deploy/config.json` file that redirects Wrangler to this new deployment configuration file: | ||
|
||
```plain | ||
- dist | ||
- index.js | ||
- wrangler.json | ||
- .wrangler | ||
- deploy | ||
- config.json | ||
``` | ||
|
||
The `dist/wrangler.json` will contain: | ||
|
||
```json | ||
{ | ||
"name": "my-worker", | ||
"main": "./index.js", | ||
"kv_namespaces": [{ "binding": "<BINDING_NAME1>", "id": "<NAMESPACE_ID1>" }] | ||
} | ||
``` | ||
|
||
And the `.wrangler/deploy/config.json` will contain: | ||
|
||
```json | ||
{ | ||
"configPath": "../../dist/wrangler.json" | ||
} | ||
``` |
6 changes: 3 additions & 3 deletions
6
...src/__tests__/configuration.pages.test.ts → ...ests__/config/configuration.pages.test.ts
This file contains 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
14 changes: 7 additions & 7 deletions
14
...ngler/src/__tests__/configuration.test.ts → ...rc/__tests__/config/configuration.test.ts
This file contains 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
218 changes: 218 additions & 0 deletions
218
packages/wrangler/src/__tests__/config/findWranglerConfig.test.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
import path from "node:path"; | ||
import { findWranglerConfig } from "../../config/config-helpers"; | ||
import { mockConsoleMethods } from "../helpers/mock-console"; | ||
import { runInTempDir } from "../helpers/run-in-tmp"; | ||
import { seed } from "../helpers/seed"; | ||
|
||
describe("config findWranglerConfig()", () => { | ||
runInTempDir(); | ||
const std = mockConsoleMethods(); | ||
const NO_LOGS = { debug: "", err: "", info: "", out: "", warn: "" }; | ||
|
||
describe("(useRedirect: false)", () => { | ||
it.each(["toml", "json", "jsonc"])( | ||
"should find the nearest wrangler.%s to the current working directory", | ||
async (ext) => { | ||
await seed({ | ||
[`wrangler.${ext}`]: "DUMMY", | ||
[`foo/wrangler.${ext}`]: "DUMMY", | ||
[`foo/bar/wrangler.${ext}`]: "DUMMY", | ||
[`foo/bar/qux/holder.txt`]: "DUMMY", | ||
}); | ||
expect(findWranglerConfig(".")).toEqual( | ||
path.resolve(`wrangler.${ext}`) | ||
); | ||
expect(findWranglerConfig("./foo")).toEqual( | ||
path.resolve(`foo/wrangler.${ext}`) | ||
); | ||
expect(findWranglerConfig("./foo/bar")).toEqual( | ||
path.resolve(`foo/bar/wrangler.${ext}`) | ||
); | ||
expect(findWranglerConfig("./foo/bar/qux")).toEqual( | ||
path.resolve(`foo/bar/wrangler.${ext}`) | ||
); | ||
expect(std).toEqual(NO_LOGS); | ||
} | ||
); | ||
|
||
describe.each([ | ||
["json", "jsonc"], | ||
["json", "toml"], | ||
["jsonc", "toml"], | ||
])("should prefer the wrangler.%s over wrangler.%s", (ext1, ext2) => { | ||
it("in the same directory", async () => { | ||
await seed({ | ||
[`wrangler.${ext1}`]: "DUMMY", | ||
[`wrangler.${ext2}`]: "DUMMY", | ||
}); | ||
expect(findWranglerConfig(".")).toEqual( | ||
path.resolve(`wrangler.${ext1}`) | ||
); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
|
||
it("in different directories", async () => { | ||
await seed({ | ||
[`wrangler.${ext1}`]: "DUMMY", | ||
[`foo/wrangler.${ext2}`]: "DUMMY", | ||
}); | ||
expect(findWranglerConfig("./foo")).toEqual( | ||
path.resolve(`wrangler.${ext1}`) | ||
); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
}); | ||
|
||
it("should return user config path even if a deploy config is found", async () => { | ||
await seed({ | ||
[`wrangler.toml`]: "DUMMY", | ||
[".wrangler/deploy/config.json"]: `{"configPath": "../../dist/wrangler.json" }`, | ||
[`dist/wrangler.json`]: "DUMMY", | ||
}); | ||
expect(findWranglerConfig(".", { useRedirect: false })).toEqual( | ||
path.resolve(`wrangler.toml`) | ||
); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
}); | ||
|
||
describe("(useRedirect: true)", () => { | ||
it("should return redirected config path if no user config and a deploy config is found", async () => { | ||
await seed({ | ||
[".wrangler/deploy/config.json"]: `{"configPath": "../../dist/wrangler.json" }`, | ||
[`dist/wrangler.json`]: "DUMMY", | ||
["foo/holder.txt"]: "DUMMY", | ||
}); | ||
expect(findWranglerConfig(".", { useRedirect: true })).toEqual( | ||
path.resolve(`dist/wrangler.json`) | ||
); | ||
expect(findWranglerConfig("./foo", { useRedirect: true })).toEqual( | ||
path.resolve(`dist/wrangler.json`) | ||
); | ||
expect(std).toMatchInlineSnapshot(` | ||
Object { | ||
"debug": "", | ||
"err": "", | ||
"info": "", | ||
"out": "", | ||
"warn": "[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mUsing redirected Wrangler configuration.[0m | ||
Redirected config path: \\"dist/wrangler.json\\" | ||
Deploy config path: \\".wrangler/deploy/config.json\\" | ||
Original config path: \\"<no user config found>\\" | ||
[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mUsing redirected Wrangler configuration.[0m | ||
Redirected config path: \\"dist/wrangler.json\\" | ||
Deploy config path: \\".wrangler/deploy/config.json\\" | ||
Original config path: \\"<no user config found>\\" | ||
", | ||
} | ||
`); | ||
}); | ||
|
||
it("should return redirected config path if matching user config and a deploy config is found", async () => { | ||
await seed({ | ||
[`wrangler.toml`]: "DUMMY", | ||
[".wrangler/deploy/config.json"]: `{"configPath": "../../dist/wrangler.json" }`, | ||
[`dist/wrangler.json`]: "DUMMY", | ||
["foo/holder.txt"]: "DUMMY", | ||
}); | ||
expect(findWranglerConfig(".", { useRedirect: true })).toEqual( | ||
path.resolve(`dist/wrangler.json`) | ||
); | ||
expect(findWranglerConfig("./foo", { useRedirect: true })).toEqual( | ||
path.resolve(`dist/wrangler.json`) | ||
); | ||
expect(std).toMatchInlineSnapshot(` | ||
Object { | ||
"debug": "", | ||
"err": "", | ||
"info": "", | ||
"out": "", | ||
"warn": "[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mUsing redirected Wrangler configuration.[0m | ||
Redirected config path: \\"dist/wrangler.json\\" | ||
Deploy config path: \\".wrangler/deploy/config.json\\" | ||
Original config path: \\"wrangler.toml\\" | ||
[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mUsing redirected Wrangler configuration.[0m | ||
Redirected config path: \\"dist/wrangler.json\\" | ||
Deploy config path: \\".wrangler/deploy/config.json\\" | ||
Original config path: \\"wrangler.toml\\" | ||
", | ||
} | ||
`); | ||
}); | ||
|
||
it("should error if deploy config is not valid JSON", async () => { | ||
await seed({ | ||
[".wrangler/deploy/config.json"]: `INVALID JSON`, | ||
}); | ||
expect(() => | ||
findWranglerConfig(".", { useRedirect: true }) | ||
).toThrowErrorMatchingInlineSnapshot( | ||
`[Error: Failed to load the deploy config at .wrangler/deploy/config.json]` | ||
); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
|
||
it("should error if deploy config does not contain a `configPath` property", async () => { | ||
await seed({ | ||
[".wrangler/deploy/config.json"]: `{}`, | ||
}); | ||
expect(() => findWranglerConfig(".", { useRedirect: true })) | ||
.toThrowErrorMatchingInlineSnapshot(` | ||
[Error: A redirect config was found at ".wrangler/deploy/config.json". | ||
But this is not valid - the required "configPath" property was not found. | ||
Instead this file contains: | ||
\`\`\` | ||
{} | ||
\`\`\`] | ||
`); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
|
||
it("should error if redirected config file does not exist", async () => { | ||
await seed({ | ||
[".wrangler/deploy/config.json"]: `{ "configPath": "missing/wrangler.json" }`, | ||
}); | ||
expect(() => findWranglerConfig(".", { useRedirect: true })) | ||
.toThrowErrorMatchingInlineSnapshot(` | ||
[Error: There is a redirect configuration at ".wrangler/deploy/config.json". | ||
But the config path it points to, ".wrangler/deploy/missing/wrangler.json", does not exist.] | ||
`); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
|
||
it("should error if deploy config file and user config file do not have the same base path", async () => { | ||
await seed({ | ||
[`foo/wrangler.toml`]: "DUMMY", | ||
["foo/bar/.wrangler/deploy/config.json"]: `{ "configPath": "../../dist/wrangler.json" }`, | ||
[`foo/bar/dist/wrangler.json`]: "DUMMY", | ||
|
||
[`bar/foo/wrangler.toml`]: "DUMMY", | ||
["bar/.wrangler/deploy/config.json"]: `{ "configPath": "../../dist/wrangler.json" }`, | ||
[`bar/dist/wrangler.json`]: "DUMMY", | ||
}); | ||
expect(() => findWranglerConfig("foo/bar", { useRedirect: true })) | ||
.toThrowErrorMatchingInlineSnapshot(` | ||
[Error: Found both a user config file at "foo/wrangler.toml" | ||
and a redirect config file at "foo/bar/.wrangler/deploy/config.json". | ||
But these do not share the same base path so it is not clear which should be used.] | ||
`); | ||
expect(() => findWranglerConfig("bar/foo", { useRedirect: true })) | ||
.toThrowErrorMatchingInlineSnapshot(` | ||
[Error: Found both a user config file at "bar/foo/wrangler.toml" | ||
and a redirect config file at "bar/.wrangler/deploy/config.json". | ||
But these do not share the same base path so it is not clear which should be used.] | ||
`); | ||
expect(std).toEqual(NO_LOGS); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.