wip: add confirmation before updating major

This commit is contained in:
Nate Moore 2023-09-18 11:05:07 -05:00
parent 2a75c745eb
commit bca381144e
6 changed files with 67 additions and 20 deletions

View file

@ -32,9 +32,11 @@
"//b": "DEPENDENCIES IS FOR UNBUNDLED PACKAGES",
"dependencies": {
"@astrojs/cli-kit": "^0.2.3",
"semver": "^7.5.4",
"which-pm-runs": "^1.1.0"
},
"devDependencies": {
"@types/semver": "^7.5.2",
"@types/which-pm-runs": "^1.0.0",
"arg": "^5.0.2",
"astro-scripts": "workspace:*",

View file

@ -21,6 +21,7 @@ export interface PackageInfo {
currentVersion: string;
targetVersion: string;
isDevDependency?: boolean;
isMajor?: boolean;
}
export async function getContext(argv: string[]): Promise<Context> {

View file

@ -4,12 +4,17 @@ import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { color } from '@astrojs/cli-kit';
import { celebrations, done, error, info, log, spinner, success, upgrade, banner } from '../messages.js';
import { celebrations, done, error, info, log, spinner, success, upgrade, banner, title } from '../messages.js';
import { shell } from '../shell.js';
import { random, sleep } from '@astrojs/cli-kit/utils';
const pluralize = (n: number) => {
if (n === 1) return `One package has`;
return `Some packages have`;
}
export async function install(
ctx: Pick<Context, 'version' | 'packages' | 'packageManager' | 'dryRun' | 'cwd'>
ctx: Pick<Context, 'version' | 'packages' | 'packageManager' | 'prompt' | 'dryRun' | 'exit' | 'cwd'>
) {
await banner();
log('')
@ -18,17 +23,30 @@ export async function install(
for (const packageInfo of current) {
const tag = /^\d/.test(packageInfo.targetVersion) ? packageInfo.targetVersion : packageInfo.targetVersion.slice(1)
await info(`${packageInfo.name}`, `is up to date on`, `v${tag}`)
await sleep(random(0, 100));
await sleep(random(50, 150));
}
if (toInstall.length === 0 && !ctx.dryRun) {
log('')
await success(random(celebrations), random(done))
return;
}
const majors: PackageInfo[] = []
for (const packageInfo of [...dependencies, ...devDependencies]) {
const tag = /^\d/.test(packageInfo.targetVersion) ? packageInfo.targetVersion : packageInfo.targetVersion.slice(1)
const word = ctx.dryRun ? 'can' : 'will'
await upgrade(`${packageInfo.name}`, `${word} be updated to`, `v${tag}`)
const word = ctx.dryRun ? 'can' : 'will';
await upgrade(packageInfo, `${word} be updated to`)
if (packageInfo.isMajor) {
majors.push(packageInfo)
}
}
if (majors.length > 0) {
const { proceed } = await ctx.prompt({
name: 'proceed',
type: 'confirm',
label: title('WARN!'),
message: `Continue? ${pluralize(majors.length)} breaking changes!`,
initial: true,
});
if (!proceed) ctx.exit(0);
}
log('')
@ -65,10 +83,10 @@ async function runInstallCommand(ctx: Pick<Context, 'cwd' | 'packageManager'>, d
while: async () => {
try {
if (dependencies.length > 0) {
await shell(ctx.packageManager, ['install', ...dependencies.map(({ name, targetVersion }) => `${name}@${targetVersion}`)], { cwd, timeout: 90_000, stdio: 'ignore' })
await shell(ctx.packageManager, ['install', ...dependencies.map(({ name, targetVersion }) => `${name}@${targetVersion.replace(/^\^/, '')}`)], { cwd, timeout: 90_000, stdio: 'ignore' })
}
if (devDependencies.length > 0) {
await shell(ctx.packageManager, ['install', '--save-dev', ...devDependencies.map(({ name, targetVersion }) => `${name}@${targetVersion}`)], { cwd, timeout: 90_000, stdio: 'ignore' })
await shell(ctx.packageManager, ['install', '--save-dev', ...devDependencies.map(({ name, targetVersion }) => `${name}@${targetVersion.replace(/^\^/, '')}`)], { cwd, timeout: 90_000, stdio: 'ignore' })
}
} catch (e) {
const packages = [...dependencies, ...devDependencies].map(({ name, targetVersion }) => `${name}@${targetVersion}`).join(' ')

View file

@ -5,6 +5,8 @@ import { existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { color } from '@astrojs/cli-kit';
import { bannerAbort, error, getRegistry, info, log } from '../messages.js';
import semverDiff from 'semver/functions/diff.js';
import semverCoerce from 'semver/functions/coerce.js'
export async function verify(
@ -60,10 +62,6 @@ async function verifyAstroProject(ctx: Pick<Context, 'cwd' | 'version' | 'packag
// Side-effect! Persist dependency info to the shared context
collectPackageInfo(ctx, dependencies, devDependencies);
ctx.packages.sort((a: PackageInfo) => {
if (a.name === 'astro') return -1;
return 0;
})
return true;
}
@ -122,5 +120,9 @@ async function resolveTargetVersion(packageInfo: PackageInfo, registry: string):
}
const prefix = packageInfo.targetVersion === 'latest' ? '^' : '';
packageInfo.targetVersion = `${prefix}${version}`;
const fromVersion = semverCoerce(packageInfo.currentVersion)!;
const toVersion = semverCoerce(packageInfo.targetVersion)!;
const bump = semverDiff(fromVersion, toVersion);
packageInfo.isMajor = bump === 'major';
}

View file

@ -1,7 +1,10 @@
/* eslint no-console: 'off' */
import type { PackageInfo } from './actions/context.js';
import { color, label, spinner as load } from '@astrojs/cli-kit';
import { align } from '@astrojs/cli-kit/utils';
import detectPackageManager from 'which-pm-runs';
import { shell } from './shell.js';
import semverCoerce from 'semver/functions/coerce.js';
// Users might lack access to the global npm registry, this function
// checks the user's project type and will return the proper npm registry
@ -61,27 +64,38 @@ export const banner = async () =>
);
export const bannerAbort = () =>
log(`\n${label('astro', color.bgRed)} ${color.bold('Launch sequence aborted.')}`);
log(`\n${label('astro', color.bgRed)} ${color.bold('Integration upgrade aborted.')}`);
export const info = async (prefix: string, text: string, version = '') => {
const length = 11 + prefix.length + text.length + version?.length;
const symbol = '◼';
if (length > stdout.columns) {
log(`${' '.repeat(5)} ${color.cyan('◼')} ${prefix}`);
log(`${' '.repeat(5)} ${color.cyan(symbol)} ${prefix}`);
log(`${' '.repeat(9)}${color.dim(text)} ${color.reset(version)}`);
} else {
log(`${' '.repeat(5)} ${color.cyan('◼')} ${prefix} ${color.dim(text)} ${color.reset(version)}`);
log(`${' '.repeat(5)} ${color.cyan(symbol)} ${prefix} ${color.dim(text)} ${color.reset(version)}`);
}
}
export const upgrade = async (prefix: string, text: string, version = '') => {
const length = 11 + prefix.length + text.length + version.length;
export const upgrade = async (packageInfo: PackageInfo, text: string) => {
const { name, isMajor = false, targetVersion } = packageInfo;
const bg = isMajor ? (v: string) => color.bgYellow(color.black(` ${v} `)) : color.green;
const style = isMajor ? color.yellow : color.green;
const symbol = isMajor ? '▲' : '●';
const toVersion = semverCoerce(targetVersion)!;
const version = `v${toVersion.version}`;
const length = 12 + name.length + text.length + version.length;
if (length > stdout.columns) {
log(`${' '.repeat(5)} ${color.magenta('▲')} ${prefix}`);
log(`${' '.repeat(9)}${color.dim(text)} ${color.magenta(version)}`);
log(`${' '.repeat(5)} ${style(symbol)} ${name}`);
log(`${' '.repeat(9)}${color.dim(text)} ${bg(version)}`);
} else {
log(`${' '.repeat(5)} ${color.magenta('▲')} ${prefix} ${color.dim(text)} ${color.magenta(version)}`);
log(`${' '.repeat(5)} ${style(symbol)} ${name} ${color.dim(text)} ${bg(version)}`);
}
}
export const title = (text: string) => align(label(text, color.bgYellow, color.black), 'end', 7) + ' ';
export const success = async (prefix: string, text: string) => {
const length = 10 + prefix.length + text.length;
if (length > stdout.columns) {

View file

@ -5088,10 +5088,16 @@ importers:
'@astrojs/cli-kit':
specifier: ^0.2.3
version: 0.2.3
semver:
specifier: ^7.5.4
version: 7.5.4
which-pm-runs:
specifier: ^1.1.0
version: 1.1.0
devDependencies:
'@types/semver':
specifier: ^7.5.2
version: 7.5.2
'@types/which-pm-runs':
specifier: ^1.0.0
version: 1.0.0
@ -9058,6 +9064,10 @@ packages:
resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==}
dev: true
/@types/semver@7.5.2:
resolution: {integrity: sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==}
dev: true
/@types/send@0.17.1:
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
dependencies: