Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/d1-execute-logger-level.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wrangler": patch
---

Restore the D1 `executeSql` logger level via try/finally

`wrangler d1 execute --json` and the internal `executeSql` helper temporarily lower the global logger to `"error"` to keep human-readable output out of the JSON payload. Previously the level was restored only on the happy path, so any early return or thrown error left the singleton logger muted, silencing later `logger.warn`/`logger.log` output (notably from migration helpers that wrap `executeSql` and are commonly mocked in tests).

The level swap is now wrapped in `try`/`finally` so it is always restored.
28 changes: 28 additions & 0 deletions .changeset/d1-migrations-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
"wrangler": minor
"@cloudflare/workers-utils": minor
---

Add `migrations_pattern` to D1 database bindings

The D1 binding now accepts an optional `migrations_pattern` field, allowing you to point `wrangler d1 migrations apply` and `wrangler d1 migrations list` at migration files in nested layouts (e.g. ORM-generated folders like `migrations/0000_init/migration.sql`).

`migrations_pattern` is a glob (relative to the wrangler config file) and defaults to `${migrations_dir}/*.sql`, which preserves today's behaviour. Files that do not match the pattern are not executed.

```jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "my-db",
"database_id": "...",
"migrations_dir": "migrations",
"migrations_pattern": "migrations/*/migration.sql",
},
],
}
```

When no migrations match the configured pattern but files matching the common `migrations/*/migration.sql` (drizzle-style) layout do exist, Wrangler logs a hint suggesting `migrations_pattern` as an opt-in.

`wrangler d1 migrations create` now returns an actionable error if the generated migration filename would not match the configured pattern.
12 changes: 12 additions & 0 deletions .changeset/deploy-path-positional.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"wrangler": minor
---

Generalize `wrangler deploy` and `wrangler versions upload` positional argument from `[script]` to `[path]`

Both `wrangler deploy` and `wrangler versions upload` now accept a generic `[path]` positional argument that can point to either a Worker entry-point file or a directory of static assets. The type is auto-detected. For example:

- **File**: `wrangler deploy ./src/index.ts` deploys a Worker (same as before)
- **Directory**: `wrangler deploy ./public` deploys a static assets site (no interactive confirmation prompt)

The `--script` named option is now hidden and deprecated for both commands. It continues to work for backwards compatibility but only accepts file paths. Passing a directory to `--script` now produces a clear error message suggesting the positional `path` argument or `--assets` flag instead.
11 changes: 11 additions & 0 deletions .changeset/good-ducks-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"wrangler": patch
---

In non-interactive mode remove the skills installation message

When Wrangler run in non interactive mode and it detected agents that it could install skills for, it would print a message such as:

`Cloudflare agent skills are available for: <DETECTED_AGENTS>. Run wrangler in an interactive terminal to install them, or use '--install-skills' to install without prompting.`

This message seems to be confusing and unhelpful so it has now been removed.
7 changes: 7 additions & 0 deletions .changeset/simplify-construct-wrangler-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cloudflare/workers-utils": minor
---

Simplify `constructWranglerConfig` to accept a single worker instead of an array

The `constructWranglerConfig` function now accepts a single `APIWorkerConfig` object instead of `APIWorkerConfig | APIWorkerConfig[]`. The multi-environment array support has been removed since the array use-case was removed and now the only call site already passes a single worker object. This is a breaking change to the function's public signature.
7 changes: 7 additions & 0 deletions .changeset/warn-custom-domain-route-inheritance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Warn when a named environment silently inherits custom_domain routes from the top-level config

When an `env.<name>` block does not override `routes`, it inherits the top-level `routes` array. If that array contains entries with `custom_domain: true`, every deploy to the named environment will silently reassign the custom domain away from the top-level Worker and towards the env Worker, causing routing drift. Wrangler now emits a warning in this situation and suggests adding `"routes": []` to the env block to prevent inheritance.
15 changes: 15 additions & 0 deletions packages/workers-utils/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,21 @@ export interface EnvironmentNonInheritable {
migrations_table?: string;
/** The path to the directory of migrations for this D1 database (defaults to './migrations'). */
migrations_dir?: string;
/**
* A glob pattern (relative to the Wrangler config file) used to discover
* migration files for this D1 database. Defaults to `${migrations_dir}/*.sql`
* if not specified.
*
* Use this to opt in to nested layouts such as `migrations/*\/migration.sql`
* (as produced by some ORMs).
*
* When `migrations_pattern` is set, `migrations_dir` must also be set, and
* `migrations_pattern` must start with `${migrations_dir}/`. This keeps the
* relationship between the two settings explicit and lets Wrangler record
* each migration's name in the migrations table as a path relative to
* `migrations_dir`.
*/
migrations_pattern?: string;
/** Internal use only. */
database_internal_env?: string;
/** Whether the D1 database should be remote or not in local development */
Expand Down
42 changes: 39 additions & 3 deletions packages/workers-utils/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import type {
Assets,
CacheOptions,
ContainerApp,
CustomDomainRoute,
DispatchNamespaceOutbound,
Environment,
Observability,
Expand Down Expand Up @@ -1134,16 +1135,36 @@ function normalizeAndValidateRoute(
function validateRoutes(
diagnostics: Diagnostics,
topLevelEnv: Environment | undefined,
rawEnv: RawEnvironment
rawEnv: RawEnvironment,
envName?: string
): Config["routes"] {
return inheritable(
const result = inheritable(
diagnostics,
topLevelEnv,
rawEnv,
"routes",
all(isRouteArray, isMutuallyExclusiveWith(rawEnv, "route")),
undefined
);

if (
topLevelEnv !== undefined &&
envName !== undefined &&
rawEnv.routes === undefined
) {
const customDomainRoutes = topLevelEnv.routes?.filter(
(r): r is CustomDomainRoute =>
typeof r === "object" && r !== null && r.custom_domain === true
);
if (customDomainRoutes && customDomainRoutes.length > 0) {
const customDomains = customDomainRoutes.map((r) => r.pattern).join(", ");
diagnostics.warnings.push(
`The "env.${envName}" environment inherits the top-level \`routes\` configuration, which includes the custom domain(s): ${customDomains}. Deploying this environment will reassign these custom domains away from the top-level Worker. Add \`"routes": []\` to "env.${envName}" to prevent inheritance, or copy the route configuration from the top level to hide this warning.`
);
}
}

return result;
}

function normalizeAndValidatePlacement(
Expand Down Expand Up @@ -1456,7 +1477,12 @@ function normalizeAndValidateEnvironment(
undefined
);

const routes = validateRoutes(diagnostics, topLevelEnv, rawEnv);
const routes = validateRoutes(
diagnostics,
topLevelEnv,
rawEnv,
topLevelEnv === undefined ? undefined : envName
);

const workers_dev = inheritable(
diagnostics,
Expand Down Expand Up @@ -4117,12 +4143,22 @@ const validateD1Binding: ValidatorFn = (diagnostics, field, value) => {
isValid = false;
}

if (!isOptionalProperty(value, "migrations_pattern", "string")) {
diagnostics.errors.push(
`"${field}" bindings should, optionally, have a string "migrations_pattern" field but got ${JSON.stringify(
value
)}.`
);
isValid = false;
}

validateAdditionalProperties(diagnostics, field, Object.keys(value), [
"binding",
"database_id",
"database_internal_env",
"database_name",
"migrations_dir",
"migrations_pattern",
"migrations_table",
"preview_database_id",
"remote",
Expand Down
58 changes: 8 additions & 50 deletions packages/workers-utils/src/construct-wrangler-config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getTodaysCompatDate } from "./compatibility-date";
import { ENVIRONMENT_TAG_PREFIX, SERVICE_TAG_PREFIX } from "./constants";
import { mapWorkerMetadataBindings } from "./map-worker-metadata-bindings";
import type { RawConfig } from "./config";
import type {
Expand Down Expand Up @@ -48,7 +47,14 @@ interface APIWorkerConfig {
};
}

function convertWorkerToWranglerConfig(config: APIWorkerConfig): RawConfig {
/**
* Given information about a Worker,
* construct a Wrangler config file for the application.
*
* @param config - The Worker configuration sourced from the Cloudflare API
* @returns A Wrangler-compatible raw config object
*/
export function constructWranglerConfig(config: APIWorkerConfig): RawConfig {
const mappedBindings = mapWorkerMetadataBindings(config.bindings);

const durableObjectClassNames = config.bindings
Expand Down Expand Up @@ -109,51 +115,3 @@ function convertWorkerToWranglerConfig(config: APIWorkerConfig): RawConfig {
...mappedBindings,
};
}

/**
* Given the information of multiple Workers (representing different environments),
* construct a Wrangler config file for the application.
*/
export function constructWranglerConfig(
workerOrWorkers: APIWorkerConfig | APIWorkerConfig[]
): RawConfig {
let workers: APIWorkerConfig[];
if (Array.isArray(workerOrWorkers)) {
workers = workerOrWorkers;
} else {
workers = [workerOrWorkers];
}

const topLevelEnv = workers.find(
(w) => !w.tags?.some((t) => t.startsWith(ENVIRONMENT_TAG_PREFIX))
);
const workerName = topLevelEnv?.name ?? workers[0].name;
const entrypoint = topLevelEnv?.entrypoint ?? workers[0].entrypoint;
let combinedConfig: RawConfig;
if (topLevelEnv) {
combinedConfig = convertWorkerToWranglerConfig(topLevelEnv);
} else {
// Make a synthetic top level environment
combinedConfig = {
name: workerName,
main: entrypoint,
};
}

for (const env of workers) {
const serviceTag = env.tags?.find(
(t) => t === `${SERVICE_TAG_PREFIX}${workerName}`
);
const envTag = env.tags?.find((t) => t.startsWith(ENVIRONMENT_TAG_PREFIX));
if (
serviceTag !== `${SERVICE_TAG_PREFIX}${workerName}` ||
envTag === undefined
) {
continue;
}
const [_, envName] = envTag.split("=");
combinedConfig.env ??= {};
combinedConfig.env[envName] = convertWorkerToWranglerConfig(env);
}
return combinedConfig;
}
1 change: 1 addition & 0 deletions packages/workers-utils/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export interface CfD1Database {
database_internal_env?: string;
migrations_table?: string;
migrations_dir?: string;
migrations_pattern?: string;
remote?: boolean;
raw?: boolean;
}
Expand Down
Loading
Loading