Skip to content

Commit df254b2

Browse files
feat: add --domain flag to deploy command for custom domains
- Add --domain flag (alias: --domains) to wrangler deploy command - Convert domain strings to custom domain route format automatically - Add comprehensive tests for domain flag functionality - Validate domains using existing custom domain validation rules Fixes #10215 Co-Authored-By: [email protected] <[email protected]>
1 parent 3b6ab8a commit df254b2

File tree

5 files changed

+136
-3
lines changed

5 files changed

+136
-3
lines changed

.changeset/nine-onions-wonder.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Added `--domain` flag to `wrangler deploy` command for deploying to custom domains. Use `--domain example.com` to deploy directly to a custom domain without manually configuring routes.

packages/wrangler/src/__tests__/deploy.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,117 @@ Update them to point to this script instead?`,
16961696
'Publishing to Custom Domain "api.example.com" was skipped, fix conflict and try again'
16971697
);
16981698
});
1699+
it("should deploy domains passed via --domain flag as custom domains", async () => {
1700+
writeWranglerConfig({});
1701+
writeWorkerSource();
1702+
mockSubDomainRequest();
1703+
mockUpdateWorkerSubdomain({ enabled: false });
1704+
mockUploadWorkerRequest({ expectedType: "esm" });
1705+
mockCustomDomainsChangesetRequest({});
1706+
mockPublishCustomDomainsRequest({
1707+
publishFlags: {
1708+
override_scope: true,
1709+
override_existing_origin: false,
1710+
override_existing_dns_record: false,
1711+
},
1712+
domains: [{ hostname: "api.example.com" }],
1713+
});
1714+
1715+
await runWrangler("deploy ./index --domain api.example.com");
1716+
expect(std.out).toContain("api.example.com (custom domain)");
1717+
});
1718+
1719+
it("should deploy multiple domains passed via --domain flags", async () => {
1720+
writeWranglerConfig({});
1721+
writeWorkerSource();
1722+
mockSubDomainRequest();
1723+
mockUpdateWorkerSubdomain({ enabled: false });
1724+
mockUploadWorkerRequest({ expectedType: "esm" });
1725+
mockCustomDomainsChangesetRequest({});
1726+
mockPublishCustomDomainsRequest({
1727+
publishFlags: {
1728+
override_scope: true,
1729+
override_existing_origin: false,
1730+
override_existing_dns_record: false,
1731+
},
1732+
domains: [
1733+
{ hostname: "api.example.com" },
1734+
{ hostname: "app.example.com" },
1735+
],
1736+
});
1737+
1738+
await runWrangler(
1739+
"deploy ./index --domain api.example.com --domain app.example.com"
1740+
);
1741+
expect(std.out).toContain("api.example.com (custom domain)");
1742+
expect(std.out).toContain("app.example.com (custom domain)");
1743+
});
1744+
1745+
it("should combine --domain flags with existing routes configuration", async () => {
1746+
writeWranglerConfig({
1747+
routes: ["example.com/api/*"],
1748+
});
1749+
writeWorkerSource();
1750+
mockSubDomainRequest();
1751+
mockUpdateWorkerSubdomain({ enabled: false });
1752+
mockUploadWorkerRequest({ expectedType: "esm" });
1753+
mockCustomDomainsChangesetRequest({});
1754+
mockPublishCustomDomainsRequest({
1755+
publishFlags: {
1756+
override_scope: true,
1757+
override_existing_origin: false,
1758+
override_existing_dns_record: false,
1759+
},
1760+
domains: [{ hostname: "api.example.com" }],
1761+
});
1762+
// Mock the regular route deployment for the configured route
1763+
msw.use(
1764+
http.put(
1765+
"*/accounts/:accountId/workers/scripts/:scriptName/routes",
1766+
() => {
1767+
return HttpResponse.json(
1768+
{
1769+
success: true,
1770+
errors: [],
1771+
messages: [],
1772+
result: ["example.com/api/*"],
1773+
},
1774+
{ status: 200 }
1775+
);
1776+
},
1777+
{ once: true }
1778+
)
1779+
);
1780+
1781+
await runWrangler("deploy ./index --domain api.example.com");
1782+
expect(std.out).toContain("example.com/api/*");
1783+
expect(std.out).toContain("api.example.com (custom domain)");
1784+
});
1785+
1786+
it("should validate domain flags and reject invalid domains with wildcards", async () => {
1787+
writeWranglerConfig({});
1788+
writeWorkerSource();
1789+
1790+
await expect(runWrangler("deploy ./index --domain *.example.com"))
1791+
.rejects.toThrowErrorMatchingInlineSnapshot(`
1792+
[Error: Invalid Routes:
1793+
*.example.com:
1794+
Wildcard operators (*) are not allowed in Custom Domains]
1795+
`);
1796+
});
1797+
1798+
it("should validate domain flags and reject invalid domains with paths", async () => {
1799+
writeWranglerConfig({});
1800+
writeWorkerSource();
1801+
1802+
await expect(
1803+
runWrangler("deploy ./index --domain api.example.com/path")
1804+
).rejects.toThrowErrorMatchingInlineSnapshot(`
1805+
[Error: Invalid Routes:
1806+
api.example.com/path:
1807+
Paths are not allowed in Custom Domains]
1808+
`);
1809+
});
16991810
});
17001811

17011812
describe("deploy asset routes", () => {

packages/wrangler/src/deploy/deploy.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ type Props = {
9494
alias: Record<string, string> | undefined;
9595
triggers: string[] | undefined;
9696
routes: string[] | undefined;
97+
domains: string[] | undefined;
9798
legacyEnv: boolean | undefined;
9899
jsxFactory: string | undefined;
99100
jsxFragment: string | undefined;
@@ -416,8 +417,13 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
416417
);
417418
}
418419

419-
const routes =
420+
const configRoutes =
420421
props.routes ?? config.routes ?? (config.route ? [config.route] : []) ?? [];
422+
const domainRoutes = (props.domains || []).map((domain) => ({
423+
pattern: domain,
424+
custom_domain: true,
425+
}));
426+
const routes = [...configRoutes, ...domainRoutes];
421427
validateRoutes(routes, props.assetsOptions);
422428

423429
const jsxFactory = props.jsxFactory || config.jsx_factory;
@@ -1050,7 +1056,10 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
10501056
}
10511057

10521058
// deploy triggers
1053-
const targets = await triggersDeploy(props);
1059+
const targets = await triggersDeploy({
1060+
...props,
1061+
routes: routes,
1062+
});
10541063

10551064
logger.log("Current Version ID:", versionId);
10561065

packages/wrangler/src/deploy/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ export const deployCommand = createCommand({
144144
requiresArg: true,
145145
array: true,
146146
},
147+
domains: {
148+
describe: "Custom domains to deploy to",
149+
alias: "domain",
150+
type: "string",
151+
requiresArg: true,
152+
array: true,
153+
},
147154
"jsx-factory": {
148155
describe: "The function that is called for each JSX element",
149156
type: "string",
@@ -344,6 +351,7 @@ export const deployCommand = createCommand({
344351
jsxFragment: args.jsxFragment,
345352
tsconfig: args.tsconfig,
346353
routes: args.routes,
354+
domains: args.domains,
347355
assetsOptions,
348356
legacyAssetPaths: siteAssetPaths,
349357
legacyEnv: isLegacyEnv(config),

packages/wrangler/src/triggers/deploy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type Props = {
2727
name: string | undefined;
2828
env: string | undefined;
2929
triggers: string[] | undefined;
30-
routes: string[] | undefined;
30+
routes: Route[] | undefined;
3131
legacyEnv: boolean | undefined;
3232
dryRun: boolean | undefined;
3333
assetsOptions: AssetsOptions | undefined;

0 commit comments

Comments
 (0)