Skip to content

Commit

Permalink
Check for new versions when running CLI (#3465)
Browse files Browse the repository at this point in the history
* Simple function to call new on every cli invocation to update blitz

* Fix printing of how to

* Catch network errors

* Cleanup

* Loop through all locally installed and packages and check if an update is needed

* cache checkForLatestVersion so it runs only on every 24 hours

* make update message pretty

* uncomment shouldUpdate boolean

* run checkLatestVersion on dev command and new

* Dont run in our monorepo + small changes

* Create curly-seas-serve.md

Co-authored-by: Dillon Raphael <[email protected]>
  • Loading branch information
Zeko369 and Dillon Raphael authored Jul 16, 2022
1 parent df7cee8 commit 8f166a5
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 64 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-seas-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"blitz": patch
---

Check for new versions when running CLI
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ public-hoist-pattern[]=*types*
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=@prettier/plugin-*
public-hoist-pattern[]=*prettier-plugin-*
strict-peer-dependencies=false

3 changes: 2 additions & 1 deletion packages/blitz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"dependencies": {
"@blitzjs/generator": "2.0.0-alpha.54",
"arg": "5.0.1",
"boxen": "7.0.0",
"chalk": "^4.1.0",
"console-table-printer": "2.10.0",
"cross-spawn": "7.0.3",
Expand All @@ -37,6 +38,7 @@
"find-up": "4.1.0",
"fs-extra": "10.0.1",
"hasbin": "1.2.3",
"node-fetch": "3.2.3",
"npm-which": "3.0.1",
"ora": "5.3.0",
"os-name": "5.0.1",
Expand Down Expand Up @@ -69,7 +71,6 @@
"@types/test-listen": "1.1.0",
"@types/watchpack": "1.1.1",
"express": "4.17.3",
"node-fetch": "3.2.3",
"react": "18.0.0",
"test-listen": "1.1.0",
"typescript": "^4.5.3",
Expand Down
3 changes: 2 additions & 1 deletion packages/blitz/src/cli/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {CliCommand} from "../index"
import arg from "arg"
import {AppGenerator, AppGeneratorOptions, getLatestVersion} from "@blitzjs/generator"
import {runPrisma} from "../../prisma-utils"
import {checkLatestVersion} from "../utils/check-latest-version"

const forms = {
"react-final-form": "React Final Form" as const,
Expand Down Expand Up @@ -220,7 +221,7 @@ const determinePkgManagerToInstallDeps = async () => {
const newApp: CliCommand = async (argv) => {
const shouldUpgrade = !args["--skip-upgrade"]
if (shouldUpgrade) {
//TODO: Handle checking for updates
await checkLatestVersion()
}

await determineProjectName()
Expand Down
34 changes: 11 additions & 23 deletions packages/blitz/src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {NON_STANDARD_NODE_ENV} from "./utils/constants"
import arg from "arg"
import spawn from "cross-spawn"

import {loadEnvConfig} from "../env-utils"
import {NON_STANDARD_NODE_ENV} from "./utils/constants"
import {getCommandBin} from "./utils/config"
import spawn from "cross-spawn"
import {readdirSync} from "fs-extra"
import resolveFrom from "resolve-from"
import pkgDir from "pkg-dir"
import {join} from "path"
import {readVersions} from "./utils/read-versions"

import {getPkgManager} from "./utils/helpers"

const commonArgs = {
// Flags
Expand Down Expand Up @@ -57,9 +57,6 @@ if (aliases[args._[0] as Alias]) {

const forwardedArgs = blitzCommand ? args._.slice(1) : args._

const globalBlitzPath = resolveFrom(__dirname, "blitz")
const localBlitzPath = resolveFrom.silent(process.cwd(), "blitz")

async function runCommandFromBin() {
if (!args._[0]) {
console.log("No command specified")
Expand All @@ -83,12 +80,8 @@ async function runCommandFromBin() {
async function printEnvInfo() {
const osName = await import("os-name")
const envinfo = await import("envinfo")
const pkgManager = readdirSync(process.cwd()).includes("pnpm-lock.yaml")
? "pnpm"
: readdirSync(process.cwd()).includes("yarn-lock.yaml")
? "yarn"
: "npm"

const pkgManager = getPkgManager()
const env = await envinfo.default.run(
{
System: ["OS", "CPU", "Memory", "Shell"],
Expand All @@ -106,18 +99,13 @@ async function printEnvInfo() {
{showNotFound: true},
)

const globalBlitzPkgJsonPath = pkgDir.sync(globalBlitzPath)
const localBlitzPkgJsonPath = pkgDir.sync(localBlitzPath)

if (globalBlitzPkgJsonPath && globalBlitzPkgJsonPath !== localBlitzPkgJsonPath) {
// This branch won't run if user does `npx blitz` or `yarn blitz`
const globalVersion = require(join(globalBlitzPkgJsonPath, "package.json")).version
const {globalVersion, localVersions} = readVersions()
if (globalVersion) {
console.log(`Blitz version: ${globalVersion} (global)`)
}

if (localBlitzPkgJsonPath) {
const localVersion = require(join(localBlitzPkgJsonPath, "package.json")).version
console.log(`Blitz version: ${localVersion} (local)`)
if (localVersions.blitz) {
console.log(`Blitz version: ${localVersions.blitz} (local)`)
}

console.log(
Expand Down
185 changes: 185 additions & 0 deletions packages/blitz/src/cli/utils/check-latest-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import findUp from "find-up"
import resolveFrom from "resolve-from"
import {join, dirname} from "path"
import fs from "fs"
import {readVersions, resolveVersionType} from "./read-versions"
import {getPkgManager} from "./helpers"
import superjson from "superjson"

const returnNpmEndpoint = (packageName: string) => {
return `https://registry.npmjs.org/-/package/${packageName}/dist-tags`
}

function getUpdateString(packageName: string, tag: string, isGlobal?: boolean) {
const pkgManager = getPkgManager()
switch (pkgManager) {
case "npm":
return `npm install${isGlobal ? " -g" : ""} ${packageName}@${tag}`
case "yarn":
return `yarn${isGlobal ? " global" : ""} add ${packageName}@${tag}`
case "pnpm":
return `pnpm install${isGlobal ? " -g" : ""} ${packageName}@${tag}`
}
}
const isInternalBlitzMonorepoDevelopment = fs.existsSync(
join(process.cwd(), "..", "..", "packages", "blitz", "dist", "chunks"),
)

async function findNodeModulesRoot(src: string) {
const blitzPkgLocation = dirname(
(await findUp("package.json", {
cwd: resolveFrom(src, "blitz"),
})) ?? "",
)

if (!blitzPkgLocation) {
throw new Error("Internal Blitz Error: unable to find 'blitz' package location")
}

return blitzPkgLocation.includes(".pnpm")
? join(blitzPkgLocation, "../../../../")
: join(blitzPkgLocation, "../")
}

export async function checkLatestVersion() {
if (!isInternalBlitzMonorepoDevelopment) {
const fetch = await import("node-fetch")
const boxen = await import("boxen")
const versions = readVersions()
const nodeModulesRoot = await findNodeModulesRoot(process.cwd())
const dotBlitzCacheExists = fs.existsSync(
join(nodeModulesRoot, ".blitz", "checkUpdateCache.json"),
)
let dotBlitzCache
let shouldRun = true

if (dotBlitzCacheExists) {
dotBlitzCache = fs.readFileSync(join(nodeModulesRoot, ".blitz", "checkUpdateCache.json"))
const now = new Date()
const msBetweenTimes = Math.abs(
superjson.parse<{lastUpdated: Date}>(dotBlitzCache.toString()).lastUpdated.getTime() -
now.getTime(),
)
const hoursBetweenTimes = msBetweenTimes / (60 * 60 * 1000)
shouldRun = hoursBetweenTimes > 24
}

if (shouldRun) {
let errors: {message: string; instructions: string}[] = []
try {
const blitzResponse = await fetch.default(returnNpmEndpoint("blitz"))
const remoteBlitzVersions = (await blitzResponse.json()) as Record<string, string>

const blitzNextResponse = await fetch.default(returnNpmEndpoint("@blitzjs/next"))
const remoteBlitzNextVersions = (await blitzNextResponse.json()) as Record<string, string>

const blitzAuthResponse = await fetch.default(returnNpmEndpoint("@blitzjs/auth"))
const remoteBlitzAuthVersions = (await blitzAuthResponse.json()) as Record<string, string>

const blitzRpcResponse = await fetch.default(returnNpmEndpoint("@blitzjs/rpc"))
const remoteBlitzRpcVersions = (await blitzRpcResponse.json()) as Record<string, string>

for (const version of Object.entries(versions)) {
if (version[0] === "globalVersion") {
const versionType = resolveVersionType(version[1] as string)

if (remoteBlitzVersions.hasOwnProperty("beta") && versionType !== "beta") {
errors.push({
message: `blitz(global) (current) ${version[1]} -> (latest) ${remoteBlitzVersions["beta"]}`,
instructions: `${getUpdateString("blitz", "beta", true)}`,
})
} else if (remoteBlitzVersions[versionType] !== version[1]) {
errors.push({
message: `blitz(global) (current) ${version[1]} -> (latest) ${remoteBlitzVersions[versionType]}`,
instructions: `${getUpdateString("blitz", versionType, true)}`,
})
}
} else if (version[0] === "localVersions") {
for (const localVersion of Object.entries(version[1])) {
const versionType = resolveVersionType(localVersion[1] as string)

switch (localVersion[0]) {
case "blitz":
if (remoteBlitzVersions.hasOwnProperty("beta") && versionType !== "beta") {
errors.push({
message: `blitz (current) ${localVersion[1]} -> (latest) ${remoteBlitzVersions["beta"]}`,
instructions: `${getUpdateString("blitz", "beta", false)}`,
})
} else if (remoteBlitzVersions[versionType] !== localVersion[1]) {
errors.push({
message: `blitz (current) ${localVersion[1]} -> (latest) ${remoteBlitzVersions[versionType]}`,
instructions: `${getUpdateString("blitz", versionType, false)}`,
})
}
break
case "blitzAuth":
if (remoteBlitzAuthVersions.hasOwnProperty("beta") && versionType !== "beta") {
errors.push({
message: `@blitzjs/auth (current) ${localVersion[1]} -> (latest) ${remoteBlitzAuthVersions["beta"]}`,
instructions: `${getUpdateString("@blitzjs/auth", "beta", false)}`,
})
} else if (remoteBlitzAuthVersions[versionType] !== localVersion[1]) {
errors.push({
message: `@blitzjs/auth (current) ${localVersion[1]} -> (latest) ${remoteBlitzAuthVersions[versionType]}`,
instructions: `${getUpdateString("@blitzjs/auth", versionType, false)}`,
})
}
break
case "blitzNext":
if (remoteBlitzNextVersions.hasOwnProperty("beta") && versionType !== "beta") {
errors.push({
message: `@blitzjs/next (current) ${localVersion[1]} -> (latest) ${remoteBlitzNextVersions["beta"]}`,
instructions: `${getUpdateString("@blitzjs/next", "beta", false)}`,
})
} else if (remoteBlitzNextVersions[versionType] !== localVersion[1]) {
errors.push({
message: `@blitzjs/next (current) ${localVersion[1]} -> (latest) ${remoteBlitzNextVersions[versionType]}`,
instructions: `${getUpdateString("@blitzjs/next", versionType, false)}`,
})
}
break
case "blitzRpc":
if (remoteBlitzRpcVersions.hasOwnProperty("beta") && versionType !== "beta") {
errors.push({
message: `@blitzjs/rpc (current) ${localVersion[1]} -> (latest) ${remoteBlitzRpcVersions["beta"]}`,
instructions: `${getUpdateString("@blitzjs/rpc", "beta", false)}`,
})
} else if (remoteBlitzRpcVersions[versionType] !== localVersion[1]) {
errors.push({
message: `@blitzjs/rpc (current) ${localVersion[1]} -> (latest) ${remoteBlitzRpcVersions[versionType]}`,
instructions: `${getUpdateString("@blitzjs/rpc", versionType, false)}`,
})
}
break
}
}
}
}

console.log(
boxen.default(
`You are running outdated blitz packages\n\n ${errors
.map((e) => e.message)
.join("\n")} \n\n Run the following to update:\n ${errors
.map((e) => e.instructions)
.join("\n")}`,
{padding: 1},
),
)

const dotBlitz = join(nodeModulesRoot, ".blitz")
fs.writeFileSync(
join(dotBlitz, "checkUpdateCache.json"),
superjson.stringify({lastUpdated: new Date()}),
)
} catch (err) {
if (err instanceof fetch.FetchError) {
// TODO: Check if network error and throw otherwise
// pass fetch error
} else {
console.log(err)
}
}
}
}
}
9 changes: 9 additions & 0 deletions packages/blitz/src/cli/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {readdirSync} from "fs-extra"

export function getPkgManager() {
return readdirSync(process.cwd()).includes("pnpm-lock.yaml")
? "pnpm"
: readdirSync(process.cwd()).includes("yarn-lock.yaml")
? "yarn"
: "npm"
}
3 changes: 2 additions & 1 deletion packages/blitz/src/cli/utils/next-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
startCustomServer,
buildCustomServer,
} from "./next-utils"
import {checkLatestVersion} from "./check-latest-version"
import {readBlitzConfig} from "../../server-utils"

export async function build(config: ServerConfig) {
Expand All @@ -18,7 +19,7 @@ export async function build(config: ServerConfig) {

export async function dev(config: ServerConfig) {
const {rootFolder, nextBin} = await normalize({...config, env: "dev"})

void checkLatestVersion()
if (customServerExists()) {
console.log("Using your custom server")

Expand Down
Loading

0 comments on commit 8f166a5

Please sign in to comment.