diff --git a/AGENTS.md b/AGENTS.md
index dc14715..784b475 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -128,8 +128,8 @@ to run non-interactively.
- static: `--static-dir
`, `--single-page-app`
- `--framework-preset `, `--install-command`, `--build-command`,
`--pre-deploy-command`, `--app-directory `
-- `--region `, `--build-timeout `,
- `--build-memory-limit `
+- `--region `, `--build-timeout ` (minutes, or e.g.
+ `10m`/`600s`), `--build-memory-limit `
### `env`
diff --git a/deploy/create/flow.ts b/deploy/create/flow.ts
index e707e33..612dbd1 100644
--- a/deploy/create/flow.ts
+++ b/deploy/create/flow.ts
@@ -22,6 +22,28 @@ export const AVAILABLE_BUILD_TIMEOUTS = [5, 10, 15, 20, 25, 30];
export const AVAILABLE_BUILD_MEMORY_LIMITS = [1024, 2048, 3072, 4096];
export const REGIONS = ["us", "eu", "global"];
+const DURATION_UNIT_MINUTES: Record = {
+ s: 1 / 60,
+ m: 1,
+ h: 60,
+};
+
+/**
+ * Parses `--build-timeout`: a number of minutes (`10`, as the flag has always
+ * taken), or a whole number with a unit suffix as in deno.json (`"600s"`,
+ * `"10m"`). Returns the timeout in minutes if it is one of the available
+ * steps (5 to 30 minutes), and null otherwise.
+ */
+export function parseBuildTimeoutFlag(value: string): number | null {
+ const match = /^([1-9][0-9]*)([smh])$/.exec(value);
+ // Without a unit, convert as the flag's former numeric type did, so that
+ // spellings such as `10.0` keep working.
+ const minutes = match === null
+ ? Number(value)
+ : Number(match[1]) * DURATION_UNIT_MINUTES[match[2]];
+ return AVAILABLE_BUILD_TIMEOUTS.includes(minutes) ? minutes : null;
+}
+
/**
* The build timeout a detected build config asks for (deno.json
* `deploy.buildTimeout`), as the largest available step not above it or
diff --git a/deploy/create/mod.ts b/deploy/create/mod.ts
index a9fd3ed..54e8216 100644
--- a/deploy/create/mod.ts
+++ b/deploy/create/mod.ts
@@ -6,6 +6,7 @@ import {
AVAILABLE_BUILD_TIMEOUTS,
createFlow,
detectedBuildTimeoutForOrg,
+ parseBuildTimeoutFlag,
REGIONS,
renderBuildConfig,
} from "./flow.ts";
@@ -126,21 +127,24 @@ export const createCommand = new Command()
"For static configuration",
)
.option(
- "--build-timeout ",
- `The build timeout in minutes. One of ${
+ "--build-timeout ",
+ `The build timeout, as minutes or with a unit suffix (e.g. 10, 10m, 600s). One of ${
AVAILABLE_BUILD_TIMEOUTS.join(", ")
- }. Defaults to deploy.buildTimeout from a detected deno.json, or ${
+ } minutes. Defaults to deploy.buildTimeout from a detected deno.json, or ${
AVAILABLE_BUILD_TIMEOUTS[0]
- }`,
+ } minutes`,
{
- value(value: number) {
- if (AVAILABLE_BUILD_TIMEOUTS.includes(value)) {
- return value;
+ value(value: string) {
+ const minutes = parseBuildTimeoutFlag(value);
+ if (minutes !== null) {
+ return minutes;
} else {
throw new ValidationError(
`Invalid build timeout: ${value}. Valid values are ${
AVAILABLE_BUILD_TIMEOUTS.join(", ")
- }.`,
+ } minutes, e.g. ${AVAILABLE_BUILD_TIMEOUTS[1]} or ${
+ AVAILABLE_BUILD_TIMEOUTS[1]
+ }m.`,
);
}
},
diff --git a/tests/build_timeout.test.ts b/tests/build_timeout.test.ts
index c60ebee..384c93c 100644
--- a/tests/build_timeout.test.ts
+++ b/tests/build_timeout.test.ts
@@ -3,6 +3,7 @@ import type { TRPCClient } from "../auth.ts";
import {
detectedBuildTimeout,
detectedBuildTimeoutForOrg,
+ parseBuildTimeoutFlag,
} from "../deploy/create/flow.ts";
Deno.test("detectedBuildTimeout: undefined without a deno.json build timeout", () => {
@@ -87,3 +88,18 @@ Deno.test("detectedBuildTimeoutForOrg: leaves an unknown org uncapped for the co
20,
);
});
+
+Deno.test("parseBuildTimeoutFlag: minutes, or a duration with a unit suffix", () => {
+ assertEquals(parseBuildTimeoutFlag("10"), 10);
+ assertEquals(parseBuildTimeoutFlag("10m"), 10);
+ assertEquals(parseBuildTimeoutFlag("600s"), 10);
+ assertEquals(parseBuildTimeoutFlag("30m"), 30);
+ // A unitless value converts like the flag's former numeric type.
+ assertEquals(parseBuildTimeoutFlag("10.0"), 10);
+ assertEquals(parseBuildTimeoutFlag("05"), 5);
+ for (
+ const value of ["7", "90s", "1h", "0", "10min", "1.5m", "", "-5", "05m"]
+ ) {
+ assertEquals(parseBuildTimeoutFlag(value), null, value);
+ }
+});