diff --git a/README.md b/README.md index 30dd458..cf0dab8 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,20 @@ You need to have Deno 1.46.0+ installed (latest version is recommended; just run deno install -gArf jsr:@deno/deployctl ``` +## Upgrade + +If you have `deployctl` already installed, you can upgrade to the latest version +by running: + +```shell +deployctl upgrade +``` + +> [!WARNING] +> If your `deployctl` is older than 1.13.0 and you are using Deno v2.x, +> `deployctl upgrade` does not work. You instead need to run +> `deno install -gArf jsr:@deno/deployctl` in this case. + ## Usage The easiest way to get started with `deployctl` is to deploy one of the examples diff --git a/src/args.ts b/src/args.ts index 90b40a0..08492a6 100644 --- a/src/args.ts +++ b/src/args.ts @@ -47,6 +47,7 @@ export function parseArgs(args: string[]) { "db", "env", "env-file", + "root", ], collect: [ "grep", diff --git a/src/subcommands/upgrade.ts b/src/subcommands/upgrade.ts index 81788c2..ce8a5bc 100644 --- a/src/subcommands/upgrade.ts +++ b/src/subcommands/upgrade.ts @@ -7,6 +7,7 @@ import { parse as semverParse, } from "@std/semver"; import { VERSION } from "../version.ts"; +import type { Args as RawArgs } from "../args.ts"; const help = `deployctl upgrade Upgrade deployctl to the given version (defaults to latest). @@ -29,24 +30,33 @@ ARGS: The version to upgrade to (defaults to latest) `; -export interface Args { +type UpgradeArgs = { help: boolean; -} + /** + * If present, this value will be provided to `deno install` command that the + * upgrade subcommand internally invokes. This option is not documented in the + * help message as its intended use is for testing. + */ + root: string | null; +}; -// deno-lint-ignore no-explicit-any -export default async function (rawArgs: Record): Promise { - const args: Args = { +export default async function (rawArgs: RawArgs): Promise { + const version = typeof rawArgs._[0] === "string" ? rawArgs._[0] : null; + const args: UpgradeArgs = { help: !!rawArgs.help, + root: rawArgs.root ?? null, }; - const version = typeof rawArgs._[0] === "string" ? rawArgs._[0] : null; + if (args.help) { console.log(help); - Deno.exit(); + Deno.exit(0); } + if (rawArgs._.length > 1) { console.error(help); error("Too many positional arguments given."); } + if (version && !semverValid(version)) { error(`The provided version is invalid.`); } @@ -70,17 +80,14 @@ export default async function (rawArgs: Record): Promise { const process = new Deno.Command(Deno.execPath(), { args: [ "install", - "--allow-read", - "--allow-write", - "--allow-env", - "--allow-net", - "--allow-run", - "--allow-sys", - "--no-check", + "-A", + "--global", + args.root ? `--root=${args.root}` : undefined, + "--reload", "--force", "--quiet", - `https://deno.land/x/deploy@${version ? version : latest}/deployctl.ts`, - ], + `jsr:@deno/deployctl@${version || latest}`, + ].filter((x) => x !== undefined), }).spawn(); await process.status; } diff --git a/tests/upgrade_test.ts b/tests/upgrade_test.ts new file mode 100644 index 0000000..7b688c4 --- /dev/null +++ b/tests/upgrade_test.ts @@ -0,0 +1,83 @@ +import { assert } from "@std/assert/assert"; +import { assertEquals } from "@std/assert/assert_equals"; +import { join } from "@std/path/join"; +import { VERSION } from "../src/version.ts"; + +// Install the current script, then upgrade it to 1.12.0 (effectively downgrade) +// to see if the upgrade command works as expected. +Deno.test("upgrade", async () => { + const decoder = new TextDecoder(); + + const tempDir = await Deno.makeTempDir(); + console.log(tempDir); + + const exe = Deno.build.os === "windows" ? "deployctl.cmd" : "deployctl"; + + // Install the current `deployctl.ts` + { + const installCmd = await new Deno.Command(Deno.execPath(), { + args: [ + "install", + "-A", + "--reload", + "--force", + "--global", + "--config", + join(Deno.cwd(), "deno.jsonc"), + "--root", + tempDir, + join(Deno.cwd(), "deployctl.ts"), + ], + stdout: "inherit", + stderr: "inherit", + }).output(); + assert(installCmd.success); + } + + // Check the version of the installed `deployctl` + { + const versionCmd = await new Deno.Command(`${tempDir}/bin/${exe}`, { + args: ["--version"], + }).output(); + const stdout = decoder.decode(versionCmd.stdout).trim(); + const stderr = decoder.decode(versionCmd.stderr).trim(); + assert(versionCmd.success, `stdout: ${stdout}\nstderr: ${stderr}`); + assertEquals( + stdout, + `deployctl ${VERSION}`, + `stdout: ${stdout}\nstderr: ${stderr}`, + ); + } + + const UPGRADE_VERSION = "1.12.0"; + + // "Upgrade" the installed `deployctl` to 1.12.0 + { + const upgradeCmd = await new Deno.Command(`${tempDir}/bin/${exe}`, { + args: [ + "upgrade", + "--root", + tempDir, + UPGRADE_VERSION, + ], + stdout: "inherit", + stderr: "inherit", + }).output(); + assert(upgradeCmd.success); + } + + // Check the version of the "upgraded" `deployctl` + { + const versionCmd = await new Deno.Command(`${tempDir}/bin/${exe}`, { + args: ["--version"], + }).output(); + const stdout = decoder.decode(versionCmd.stdout).trim(); + const stderr = decoder.decode(versionCmd.stderr).trim(); + assert(versionCmd.success); + assertEquals( + stdout, + `deployctl ${UPGRADE_VERSION}`, + `stdout: ${stdout}\nstderr: ${stderr}`, + ); + } +});