diff --git a/commands/mod.ts b/commands/mod.ts index d05b7f2..f1fe700 100644 --- a/commands/mod.ts +++ b/commands/mod.ts @@ -1,4 +1,5 @@ import { app, completion, fmt, version } from "../zcli.ts"; +import { semver } from "../deps.ts"; import { VERSION } from "../version.ts"; import { AppError } from "../errors.ts"; import { logger } from "../logger.ts"; @@ -126,7 +127,7 @@ export const root = app const latestVersion = await getLatestVersion(ctx); const currentVersion = VERSION; - if (currentVersion === latestVersion) { + if (semver.gte(currentVersion, latestVersion)) { return; } diff --git a/commands/upgrade/mod.ts b/commands/upgrade/mod.ts index c9e07f6..57665cd 100644 --- a/commands/upgrade/mod.ts +++ b/commands/upgrade/mod.ts @@ -4,6 +4,7 @@ import { unzip } from "../../lib/unzip.ts"; import { logger } from "../../logger.ts"; import { path } from "../../deps.ts"; import { cache } from "../../cache.ts"; +import { AppError } from "../../errors.ts"; /** * This variable is automatically generated by `zcli add`. Do not remove this @@ -105,7 +106,7 @@ export async function getLatestVersion(ctx: Context, options: { logger.info("Checking for the latest version..."); const response = await fetch( - "https://api.github.com/repos/paperspace/cli/releases/latest", + "https://api.github.com/repos/paperspace/cli/releases", ); if (!response.ok) { @@ -114,21 +115,33 @@ export async function getLatestVersion(ctx: Context, options: { } const json = githubReleaseSchema.parse(await response.json()); - - await cache.set( - "updateAvailable", - { - version: json.tag_name, - assets: json.assets.map((asset) => ({ - name: asset.name, - url: asset.browser_download_url, - })), - }, - 60 * 60 * 8, // 8 hours - ); - - logger.info(`Found the latest version: ${json.tag_name}`); - return json.tag_name; + const latest = json.find((release) => !release.prerelease); + + if (latest) { + await cache.set( + "updateAvailable", + { + version: latest.tag_name, + assets: latest.assets.map((asset) => ({ + name: asset.name, + url: asset.browser_download_url, + })), + }, + 60 * 60 * 8, // 8 hours + ); + + logger.info(`Found the latest version: ${latest.tag_name}`); + return latest.tag_name; + } else { + logger.error( + `Failed to find the latest version in the response: ${json}`, + ); + + throw new AppError({ + message: "Failed to find the latest version.", + exitCode: 1, + }); + } } logger.info(`Returning the latest version from cache: ${cached.version}`); @@ -153,7 +166,7 @@ export async function getLatestVersionAssets( logger.info("Checking for the latest version..."); const response = await fetch( - "https://api.github.com/repos/paperspace/cli/releases/latest", + "https://api.github.com/repos/paperspace/cli/releases", ); if (!response.ok) { @@ -162,25 +175,37 @@ export async function getLatestVersionAssets( } const json = githubReleaseSchema.parse(await response.json()); - - await cache.set( - "updateAvailable", - { - version: json.tag_name, - assets: json.assets.map((asset) => ({ - name: asset.name, - url: asset.browser_download_url, - })), - }, - 60 * 60 * 8, // 8 hours - ); - - logger.info(`Found the latest version: ${json.tag_name}`); - - return json.assets.map((asset) => ({ - name: asset.name, - url: asset.browser_download_url, - })); + const latest = json.find((release) => !release.prerelease); + + if (latest) { + await cache.set( + "updateAvailable", + { + version: latest.tag_name, + assets: latest.assets.map((asset) => ({ + name: asset.name, + url: asset.browser_download_url, + })), + }, + 60 * 60 * 8, // 8 hours + ); + + logger.info(`Found the latest version: ${latest.tag_name}`); + + return latest.assets.map((asset) => ({ + name: asset.name, + url: asset.browser_download_url, + })); + } else { + logger.error( + `Failed to find the latest version in the response: ${json}`, + ); + + throw new AppError({ + message: "Failed to find the latest version.", + exitCode: 1, + }); + } } logger.info(`Returning the latest version from cache: ${cached.version}`); @@ -201,7 +226,7 @@ export const _internals = { getLatestVersionAssets, }; -const githubReleaseSchema = z.object({ +const githubReleaseSchema = z.array(z.object({ tag_name: z.string(), assets: z.array( z.object({ @@ -209,4 +234,5 @@ const githubReleaseSchema = z.object({ browser_download_url: z.string(), }), ), -}); + prerelease: z.boolean(), +})); diff --git a/deno.lock b/deno.lock index 90fa522..01f9855 100644 --- a/deno.lock +++ b/deno.lock @@ -82,6 +82,7 @@ "https://deno.land/std@0.179.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", "https://deno.land/std@0.179.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", "https://deno.land/std@0.179.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", + "https://deno.land/std@0.179.0/semver/mod.ts": "409a2691f5a411c34e917c1e6d445a6d1d53f3fadf660e44a99dd0bf9b2ef412", "https://deno.land/std@0.179.0/streams/_common.ts": "f45cba84f0d813de3326466095539602364a9ba521f804cc758f7a475cda692d", "https://deno.land/std@0.179.0/streams/buffer.ts": "7e7676c29e0e72f6821c3b5fede2540886a216bb91c849bb5db20bb82a01d8a1", "https://deno.land/std@0.179.0/streams/byte_slice_stream.ts": "cf5785b0d9223ebb51fcf6679d881dfaf614c3b288fb4577b511b6f7801a01aa", diff --git a/deps.ts b/deps.ts index 32172bf..c02a08b 100644 --- a/deps.ts +++ b/deps.ts @@ -17,6 +17,7 @@ export { format as formatBytes } from "https://deno.land/std@0.179.0/fmt/bytes.t export * as YAML from "https://deno.land/std@0.179.0/encoding/yaml.ts"; export * as TOML from "https://deno.land/std@0.179.0/encoding/toml.ts"; export * as JSONc from "https://deno.land/std@0.179.0/encoding/jsonc.ts"; +export * as semver from "https://deno.land/std@0.179.0/semver/mod.ts"; export * as Sentry from "https://deno.land/x/sentry_deno@v0.2.2/main.ts"; export { ms } from "https://deno.land/x/ms@v0.1.0/ms.ts"; diff --git a/test/upgrade.test.ts b/test/upgrade.test.ts index 641a914..5be16dc 100644 --- a/test/upgrade.test.ts +++ b/test/upgrade.test.ts @@ -443,7 +443,32 @@ describe("pspace upgrade", () => { function mockReleaseApi(config: { version?: string } = {}) { const { version = "v1.0.0" } = config; - return jsonOk({ + return jsonOk([{ + tag_name: version, + assets: [ + { + name: "pspace-linux.zip", + browser_download_url: + `https://raw.github.com/paperspace/cli/${version}-beta/pspace-linux.zip`, + }, + { + name: "pspace-macos.zip", + browser_download_url: + `https://raw.github.com/paperspace/cli/${version}-beta/pspace-macos.zip`, + }, + { + name: "pspace-macos-arm.zip", + browser_download_url: + `https://raw.github.com/paperspace/cli/${version}-beta/pspace-macos-arm.zip`, + }, + { + name: "pspace-windows.zip", + browser_download_url: + `https://raw.github.com/paperspace/cli/${version}-beta/pspace-windows.zip`, + }, + ], + prerelease: true, + }, { tag_name: version, assets: [ { @@ -467,7 +492,8 @@ function mockReleaseApi(config: { version?: string } = {}) { `https://raw.github.com/paperspace/cli/${version}/pspace-windows.zip`, }, ], - }); + prerelease: false, + }]); } function mergeContext(