wip: add confirmation before updating major
This commit is contained in:
parent
2a75c745eb
commit
bca381144e
6 changed files with 67 additions and 20 deletions
|
@ -32,9 +32,11 @@
|
||||||
"//b": "DEPENDENCIES IS FOR UNBUNDLED PACKAGES",
|
"//b": "DEPENDENCIES IS FOR UNBUNDLED PACKAGES",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cli-kit": "^0.2.3",
|
"@astrojs/cli-kit": "^0.2.3",
|
||||||
|
"semver": "^7.5.4",
|
||||||
"which-pm-runs": "^1.1.0"
|
"which-pm-runs": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/semver": "^7.5.2",
|
||||||
"@types/which-pm-runs": "^1.0.0",
|
"@types/which-pm-runs": "^1.0.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
"astro-scripts": "workspace:*",
|
"astro-scripts": "workspace:*",
|
||||||
|
|
|
@ -21,6 +21,7 @@ export interface PackageInfo {
|
||||||
currentVersion: string;
|
currentVersion: string;
|
||||||
targetVersion: string;
|
targetVersion: string;
|
||||||
isDevDependency?: boolean;
|
isDevDependency?: boolean;
|
||||||
|
isMajor?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getContext(argv: string[]): Promise<Context> {
|
export async function getContext(argv: string[]): Promise<Context> {
|
||||||
|
|
|
@ -4,12 +4,17 @@ import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { color } from '@astrojs/cli-kit';
|
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 { shell } from '../shell.js';
|
||||||
import { random, sleep } from '@astrojs/cli-kit/utils';
|
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(
|
export async function install(
|
||||||
ctx: Pick<Context, 'version' | 'packages' | 'packageManager' | 'dryRun' | 'cwd'>
|
ctx: Pick<Context, 'version' | 'packages' | 'packageManager' | 'prompt' | 'dryRun' | 'exit' | 'cwd'>
|
||||||
) {
|
) {
|
||||||
await banner();
|
await banner();
|
||||||
log('')
|
log('')
|
||||||
|
@ -18,17 +23,30 @@ export async function install(
|
||||||
for (const packageInfo of current) {
|
for (const packageInfo of current) {
|
||||||
const tag = /^\d/.test(packageInfo.targetVersion) ? packageInfo.targetVersion : packageInfo.targetVersion.slice(1)
|
const tag = /^\d/.test(packageInfo.targetVersion) ? packageInfo.targetVersion : packageInfo.targetVersion.slice(1)
|
||||||
await info(`${packageInfo.name}`, `is up to date on`, `v${tag}`)
|
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) {
|
if (toInstall.length === 0 && !ctx.dryRun) {
|
||||||
log('')
|
log('')
|
||||||
await success(random(celebrations), random(done))
|
await success(random(celebrations), random(done))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const majors: PackageInfo[] = []
|
||||||
for (const packageInfo of [...dependencies, ...devDependencies]) {
|
for (const packageInfo of [...dependencies, ...devDependencies]) {
|
||||||
const tag = /^\d/.test(packageInfo.targetVersion) ? packageInfo.targetVersion : packageInfo.targetVersion.slice(1)
|
const word = ctx.dryRun ? 'can' : 'will';
|
||||||
const word = ctx.dryRun ? 'can' : 'will'
|
await upgrade(packageInfo, `${word} be updated to`)
|
||||||
await upgrade(`${packageInfo.name}`, `${word} be updated to`, `v${tag}`)
|
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('')
|
log('')
|
||||||
|
@ -65,10 +83,10 @@ async function runInstallCommand(ctx: Pick<Context, 'cwd' | 'packageManager'>, d
|
||||||
while: async () => {
|
while: async () => {
|
||||||
try {
|
try {
|
||||||
if (dependencies.length > 0) {
|
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) {
|
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) {
|
} catch (e) {
|
||||||
const packages = [...dependencies, ...devDependencies].map(({ name, targetVersion }) => `${name}@${targetVersion}`).join(' ')
|
const packages = [...dependencies, ...devDependencies].map(({ name, targetVersion }) => `${name}@${targetVersion}`).join(' ')
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { existsSync } from 'node:fs';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { color } from '@astrojs/cli-kit';
|
import { color } from '@astrojs/cli-kit';
|
||||||
import { bannerAbort, error, getRegistry, info, log } from '../messages.js';
|
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(
|
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
|
// Side-effect! Persist dependency info to the shared context
|
||||||
collectPackageInfo(ctx, dependencies, devDependencies);
|
collectPackageInfo(ctx, dependencies, devDependencies);
|
||||||
ctx.packages.sort((a: PackageInfo) => {
|
|
||||||
if (a.name === 'astro') return -1;
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -122,5 +120,9 @@ async function resolveTargetVersion(packageInfo: PackageInfo, registry: string):
|
||||||
}
|
}
|
||||||
const prefix = packageInfo.targetVersion === 'latest' ? '^' : '';
|
const prefix = packageInfo.targetVersion === 'latest' ? '^' : '';
|
||||||
packageInfo.targetVersion = `${prefix}${version}`;
|
packageInfo.targetVersion = `${prefix}${version}`;
|
||||||
|
const fromVersion = semverCoerce(packageInfo.currentVersion)!;
|
||||||
|
const toVersion = semverCoerce(packageInfo.targetVersion)!;
|
||||||
|
const bump = semverDiff(fromVersion, toVersion);
|
||||||
|
packageInfo.isMajor = bump === 'major';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
/* eslint no-console: 'off' */
|
/* eslint no-console: 'off' */
|
||||||
|
import type { PackageInfo } from './actions/context.js';
|
||||||
import { color, label, spinner as load } from '@astrojs/cli-kit';
|
import { color, label, spinner as load } from '@astrojs/cli-kit';
|
||||||
|
import { align } from '@astrojs/cli-kit/utils';
|
||||||
import detectPackageManager from 'which-pm-runs';
|
import detectPackageManager from 'which-pm-runs';
|
||||||
import { shell } from './shell.js';
|
import { shell } from './shell.js';
|
||||||
|
import semverCoerce from 'semver/functions/coerce.js';
|
||||||
|
|
||||||
// Users might lack access to the global npm registry, this function
|
// Users might lack access to the global npm registry, this function
|
||||||
// checks the user's project type and will return the proper npm registry
|
// checks the user's project type and will return the proper npm registry
|
||||||
|
@ -61,27 +64,38 @@ export const banner = async () =>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const bannerAbort = () =>
|
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 = '') => {
|
export const info = async (prefix: string, text: string, version = '') => {
|
||||||
const length = 11 + prefix.length + text.length + version?.length;
|
const length = 11 + prefix.length + text.length + version?.length;
|
||||||
|
const symbol = '◼';
|
||||||
if (length > stdout.columns) {
|
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)}`);
|
log(`${' '.repeat(9)}${color.dim(text)} ${color.reset(version)}`);
|
||||||
} else {
|
} 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 = '') => {
|
export const upgrade = async (packageInfo: PackageInfo, text: string) => {
|
||||||
const length = 11 + prefix.length + text.length + version.length;
|
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) {
|
if (length > stdout.columns) {
|
||||||
log(`${' '.repeat(5)} ${color.magenta('▲')} ${prefix}`);
|
log(`${' '.repeat(5)} ${style(symbol)} ${name}`);
|
||||||
log(`${' '.repeat(9)}${color.dim(text)} ${color.magenta(version)}`);
|
log(`${' '.repeat(9)}${color.dim(text)} ${bg(version)}`);
|
||||||
} else {
|
} 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) => {
|
export const success = async (prefix: string, text: string) => {
|
||||||
const length = 10 + prefix.length + text.length;
|
const length = 10 + prefix.length + text.length;
|
||||||
if (length > stdout.columns) {
|
if (length > stdout.columns) {
|
||||||
|
|
|
@ -5088,10 +5088,16 @@ importers:
|
||||||
'@astrojs/cli-kit':
|
'@astrojs/cli-kit':
|
||||||
specifier: ^0.2.3
|
specifier: ^0.2.3
|
||||||
version: 0.2.3
|
version: 0.2.3
|
||||||
|
semver:
|
||||||
|
specifier: ^7.5.4
|
||||||
|
version: 7.5.4
|
||||||
which-pm-runs:
|
which-pm-runs:
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.0
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/semver':
|
||||||
|
specifier: ^7.5.2
|
||||||
|
version: 7.5.2
|
||||||
'@types/which-pm-runs':
|
'@types/which-pm-runs':
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
@ -9058,6 +9064,10 @@ packages:
|
||||||
resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==}
|
resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/semver@7.5.2:
|
||||||
|
resolution: {integrity: sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/send@0.17.1:
|
/@types/send@0.17.1:
|
||||||
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
|
resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue