зеркало из https://github.com/microsoft/beachball.git
Validation improvements (#878)
This commit is contained in:
Родитель
bd352a4732
Коммит
8eb91797c1
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "Improve validation logging and performance",
|
||||
"packageName": "beachball",
|
||||
"email": "elcraig@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -4,24 +4,28 @@ export function bumpMinSemverRange(minVersion: string, semverRange: string) {
|
|||
if (semverRange === '*') {
|
||||
return semverRange;
|
||||
}
|
||||
|
||||
if (new Set(['workspace:*', 'workspace:~', 'workspace:^']).has(semverRange)) {
|
||||
if (['workspace:*', 'workspace:~', 'workspace:^'].includes(semverRange)) {
|
||||
// For basic workspace ranges we can just preserve current value and replace during publish
|
||||
// https://pnpm.io/workspaces#workspace-protocol-workspace
|
||||
return semverRange;
|
||||
} else if (semverRange.startsWith('~') || semverRange.startsWith('^')) {
|
||||
}
|
||||
if (semverRange[0] === '~' || semverRange[0] === '^') {
|
||||
// ~1.0.0
|
||||
// ^1.0.0
|
||||
return semverRange[0] + minVersion;
|
||||
} else if (semverRange.startsWith('workspace:~') || semverRange.startsWith('workspace:^')) {
|
||||
}
|
||||
if (semverRange.startsWith('workspace:~') || semverRange.startsWith('workspace:^')) {
|
||||
// workspace:~1.0.0
|
||||
// workspace:^1.0.0
|
||||
return `workspace:${semverRange[10]}${minVersion}`;
|
||||
} else if (semverRange.includes('>')) {
|
||||
// Less frequently used, but we treat any of these kinds of ranges to be within a minor band for now: more complex understand of the semver range utility is needed to do more
|
||||
}
|
||||
if (semverRange.includes('>')) {
|
||||
// Less frequently used, but we treat any of these kinds of ranges to be within a minor band for now:
|
||||
// more complex understanding of the semver range utility is needed to do more
|
||||
// >=1.0.0 <2.0.0
|
||||
return `>=${minVersion} <${semver.inc(minVersion, 'major')}`;
|
||||
} else if (semverRange.includes(' - ')) {
|
||||
}
|
||||
if (semverRange.includes(' - ')) {
|
||||
// 1.0.0 - 2.0.0
|
||||
return `${minVersion} - ${semver.inc(minVersion, 'major')}`;
|
||||
}
|
||||
|
|
|
@ -9,19 +9,15 @@ export function bumpPackageInfoVersion(pkgName: string, bumpInfo: BumpInfo, opti
|
|||
const { calculatedChangeTypes, packageInfos, modifiedPackages } = bumpInfo;
|
||||
const info = packageInfos[pkgName];
|
||||
const changeType = calculatedChangeTypes[pkgName];
|
||||
|
||||
if (!info) {
|
||||
console.log(`Unknown package named "${pkgName}" detected from change files, skipping!`);
|
||||
return;
|
||||
}
|
||||
if (changeType === 'none') {
|
||||
} else if (changeType === 'none') {
|
||||
console.log(`"${pkgName}" has a "none" change type, no version bump is required.`);
|
||||
return;
|
||||
}
|
||||
if (info.private) {
|
||||
} else if (info.private) {
|
||||
console.log(`Skipping bumping private package "${pkgName}"`);
|
||||
return;
|
||||
}
|
||||
if (!info.private) {
|
||||
} else {
|
||||
// Version should be updated
|
||||
info.version = semver.inc(
|
||||
info.version,
|
||||
options.prereleasePrefix ? 'prerelease' : changeType,
|
||||
|
|
|
@ -3,9 +3,9 @@ import { BumpInfo } from '../types/BumpInfo';
|
|||
import { getPackageGroups } from '../monorepo/getPackageGroups';
|
||||
|
||||
export function setGroupsInBumpInfo(bumpInfo: BumpInfo, options: BeachballOptions) {
|
||||
bumpInfo.packageGroups = getPackageGroups(bumpInfo.packageInfos, options.path, options.groups);
|
||||
|
||||
if (options.groups) {
|
||||
bumpInfo.packageGroups = getPackageGroups(bumpInfo.packageInfos, options.path, options.groups);
|
||||
|
||||
for (const grpName of Object.keys(bumpInfo.packageGroups)) {
|
||||
const grpOptions = options.groups.find(groupItem => groupItem.name === grpName)!;
|
||||
bumpInfo.groupOptions[grpName] = grpOptions;
|
||||
|
|
|
@ -21,11 +21,6 @@ import { ChangeType } from '../types/ChangeInfo';
|
|||
* - bumpInfo.calculatedChangeTypes: will not mutate the entryPoint `pkgName` change type
|
||||
*
|
||||
* Inputs from bumpInfo are listed in the [^1] below in the function body
|
||||
*
|
||||
* @param entryPointPackageName
|
||||
* @param bumpInfo
|
||||
* @param bumpDeps
|
||||
* @returns
|
||||
*/
|
||||
export function updateRelatedChangeType(changeFile: string, bumpInfo: BumpInfo, bumpDeps: boolean) {
|
||||
/** [^1]: all the information needed from `bumpInfo` */
|
||||
|
|
|
@ -129,13 +129,13 @@ async function writeChangelogFiles(
|
|||
try {
|
||||
previousJson = fs.existsSync(changelogJsonFile) ? fs.readJSONSync(changelogJsonFile) : undefined;
|
||||
} catch (e) {
|
||||
console.warn('CHANGELOG.json is invalid:', e);
|
||||
console.warn(`${changelogJsonFile} is invalid: ${e}`);
|
||||
}
|
||||
try {
|
||||
const nextJson = renderJsonChangelog(newVersionChangelog, previousJson);
|
||||
fs.writeJSONSync(changelogJsonFile, nextJson, { spaces: 2 });
|
||||
} catch (e) {
|
||||
console.warn('Problem writing to CHANGELOG.json:', e);
|
||||
console.warn(`Problem writing to ${changelogJsonFile}: ${e}`);
|
||||
}
|
||||
|
||||
// Update CHANGELOG.md
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
/** Format strings as a bulleted list with line breaks */
|
||||
export function formatList(items: string[]) {
|
||||
return items.map(item => `- ${item}`).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an object on a single line with spaces between the properties and brackets
|
||||
* (similar to `JSON.stringify(obj, null, 2)` but without the line breaks).
|
||||
*/
|
||||
export function singleLineStringify(obj: any) {
|
||||
return JSON.stringify(obj, null, 2).replace(/\n\s*/g, ' ');
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export function getPackageGroups(packageInfos: PackageInfos, root: string, group
|
|||
|
||||
const packageNameToGroup: { [packageName: string]: string } = {};
|
||||
|
||||
let hasError = false;
|
||||
const errorPackages: Record<string, VersionGroupOptions[]> = {};
|
||||
|
||||
// Check every package to see which group it belongs to
|
||||
for (const [pkgName, info] of Object.entries(packageInfos)) {
|
||||
|
@ -22,10 +22,7 @@ export function getPackageGroups(packageInfos: PackageInfos, root: string, group
|
|||
const groupsForPkg = groups.filter(group => isPathIncluded(relativePath, group.include, group.exclude));
|
||||
if (groupsForPkg.length > 1) {
|
||||
// Keep going after this error to ensure we report all errors
|
||||
console.error(
|
||||
`ERROR: "${pkgName}" cannot belong to multiple groups: [${groupsForPkg.map(g => g.name).join(', ')}]`
|
||||
);
|
||||
hasError = true;
|
||||
errorPackages[pkgName] = groupsForPkg;
|
||||
} else if (groupsForPkg.length === 1) {
|
||||
const group = groupsForPkg[0];
|
||||
packageNameToGroup[pkgName] = group.name;
|
||||
|
@ -38,7 +35,14 @@ export function getPackageGroups(packageInfos: PackageInfos, root: string, group
|
|||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
if (errorPackages.length) {
|
||||
console.error(
|
||||
`ERROR: Found package(s) belonging to multiple groups:\n` +
|
||||
Object.entries(errorPackages)
|
||||
.map(([pkgName, groups]) => `- ${pkgName}: [${groups.map(g => g.name).join(', ')}]`)
|
||||
.sort()
|
||||
.join('\n')
|
||||
);
|
||||
// TODO: probably more appropriate to throw here and let the caller handle it?
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ const BUMP_PUSH_RETRIES = 5;
|
|||
/** Use verbose logging for these steps to make it easier to debug if something goes wrong */
|
||||
const verbose = true;
|
||||
|
||||
/**
|
||||
* Bump versions locally, commit, optionally tag, and push to git.
|
||||
*/
|
||||
export async function bumpAndPush(bumpInfo: BumpInfo, publishBranch: string, options: BeachballOptions) {
|
||||
const { path: cwd, branch, depth, gitTimeout } = options;
|
||||
const { remote, remoteBranch } = parseRemoteBranch(branch);
|
||||
|
@ -25,7 +28,7 @@ export async function bumpAndPush(bumpInfo: BumpInfo, publishBranch: string, opt
|
|||
while (tryNumber < BUMP_PUSH_RETRIES && !completed) {
|
||||
tryNumber++;
|
||||
console.log('-'.repeat(80));
|
||||
console.log(`Trying to push to git. Attempt ${tryNumber}/${BUMP_PUSH_RETRIES}`);
|
||||
console.log(`Bumping versions and pushing to git (attempt ${tryNumber}/${BUMP_PUSH_RETRIES})`);
|
||||
console.log('Reverting');
|
||||
revertLocalChanges(cwd);
|
||||
|
||||
|
@ -47,7 +50,7 @@ export async function bumpAndPush(bumpInfo: BumpInfo, publishBranch: string, opt
|
|||
}
|
||||
|
||||
// bump the version
|
||||
console.log('\nBumping the versions for git push');
|
||||
console.log('\nBumping versions locally (and writing changelogs if requested)');
|
||||
await performBump(bumpInfo, options);
|
||||
|
||||
// checkin
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { BumpInfo } from '../types/BumpInfo';
|
||||
|
||||
export function displayManualRecovery(bumpInfo: BumpInfo, succeededPackages: Set<string> = new Set<string>()) {
|
||||
const errorLines = ['Something went wrong with publishing! Manually update these package and versions:'];
|
||||
const errorLines: string[] = [];
|
||||
const succeededLines: string[] = [];
|
||||
|
||||
bumpInfo.modifiedPackages.forEach(pkg => {
|
||||
|
@ -14,13 +14,15 @@ export function displayManualRecovery(bumpInfo: BumpInfo, succeededPackages: Set
|
|||
}
|
||||
});
|
||||
|
||||
console.error(errorLines.join('\n') + '\n');
|
||||
console.error(
|
||||
'Something went wrong with publishing! Manually update these package and versions:\n' + errorLines.sort().join('\n')
|
||||
);
|
||||
|
||||
if (succeededLines.length) {
|
||||
console.warn(
|
||||
'These packages and versions were successfully published, but may be invalid if they depend on ' +
|
||||
'package versions for which publishing failed:\n' +
|
||||
succeededLines.join('\n') +
|
||||
succeededLines.sort().join('\n') +
|
||||
'\n\nTo recover from this, run "beachball sync" to synchronize local package.json files with the registry. ' +
|
||||
'If necessary, unpublish or deprecate any invalid packages from the above list after "beachball sync".'
|
||||
);
|
||||
|
|
|
@ -43,7 +43,7 @@ export function getPackagesToPublish(bumpInfo: BumpInfo, validationMode?: boolea
|
|||
|
||||
// this log is not helpful when called from `validate`
|
||||
if (skippedPackageReasons.length && !validationMode) {
|
||||
console.log(`\nSkipping publishing the following packages:\n${formatList(skippedPackageReasons)}`);
|
||||
console.log(`\nSkipping publishing the following packages:\n${formatList(skippedPackageReasons.sort())}`);
|
||||
}
|
||||
|
||||
return packagesToPublish;
|
||||
|
|
|
@ -18,7 +18,10 @@ export function validatePackageDependencies(packagesToValidate: string[], packag
|
|||
if (errorDeps.length) {
|
||||
console.error(
|
||||
`ERROR: Found private packages among published package dependencies:\n` +
|
||||
errorDeps.map(dep => `- ${dep}: used by ${allDeps[dep].join(', ')}`).join('\n')
|
||||
errorDeps
|
||||
.map(dep => `- ${dep}: used by ${allDeps[dep].join(', ')}`)
|
||||
.sort()
|
||||
.join('\n')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -29,12 +29,13 @@ export async function validatePackageVersions(
|
|||
}
|
||||
|
||||
if (okVersions.length) {
|
||||
// keep the original order here to show what order they'll be published in
|
||||
console.log(`\nPackage versions are OK to publish:\n${formatList(okVersions)}`);
|
||||
}
|
||||
if (errorVersions.length) {
|
||||
console.error(
|
||||
`\nERROR: Attempting to publish package versions that already exist in the registry:\n` +
|
||||
formatList(errorVersions)
|
||||
formatList(errorVersions.sort())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,6 @@ export function areChangeFilesDeleted(options: BeachballOptions): boolean {
|
|||
const { branch, path: cwd } = options;
|
||||
|
||||
const root = findProjectRoot(cwd);
|
||||
if (!root) {
|
||||
console.error('Failed to find the project root');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const changePath = getChangePath(cwd);
|
||||
|
||||
console.log(`Checking for deleted change files against "${branch}"`);
|
||||
|
@ -24,19 +19,11 @@ export function areChangeFilesDeleted(options: BeachballOptions): boolean {
|
|||
root
|
||||
);
|
||||
|
||||
// if this value is undefined, git has failed to execute the command above.
|
||||
if (!changeFilesDeletedSinceRef) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const changeFilesDeleted = changeFilesDeletedSinceRef.length > 0;
|
||||
|
||||
if (changeFilesDeleted) {
|
||||
if (changeFilesDeletedSinceRef.length) {
|
||||
const changeFiles = changeFilesDeletedSinceRef.map(file => `- ${file}`);
|
||||
const errorMessage = 'The following change files were deleted:';
|
||||
|
||||
console.error(`${errorMessage}\n${changeFiles.join('\n')}\n`);
|
||||
console.error(`ERROR: The following change files were deleted:\n${changeFiles.join('\n')}\n`);
|
||||
return true;
|
||||
}
|
||||
|
||||
return changeFilesDeleted;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ export function isChangeFileNeeded(options: BeachballOptions, packageInfos: Pack
|
|||
.map(pkg => `\n ${pkg}`)
|
||||
.join('')}`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return changedPackages.length > 0;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { SortedChangeTypes } from '../changefile/changeTypes';
|
||||
|
||||
export function isValidChangeType(changeType: string) {
|
||||
return ['patch', 'major', 'minor', 'prerelease', 'none'].includes(changeType);
|
||||
return SortedChangeTypes.includes(changeType as any);
|
||||
}
|
||||
|
|
|
@ -1,30 +1,20 @@
|
|||
import { ChangelogOptions, ChangelogGroupOptions } from '../types/ChangelogOptions';
|
||||
import { singleLineStringify } from '../logging/format';
|
||||
import { ChangelogOptions } from '../types/ChangelogOptions';
|
||||
|
||||
export function isValidChangelogOptions(options: ChangelogOptions): boolean {
|
||||
if (options.groups) {
|
||||
if (!isValidChangelogGroupOptions(options.groups)) {
|
||||
return false;
|
||||
}
|
||||
if (!options.groups) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isValidChangelogGroupOptions(groupOptions: ChangelogGroupOptions[]): boolean {
|
||||
for (const options of groupOptions) {
|
||||
if (!options.changelogPath) {
|
||||
console.log('changelog group options cannot contain empty changelogPath.');
|
||||
return false;
|
||||
}
|
||||
const badGroups = options.groups.filter(group => !group.changelogPath || !group.masterPackageName || !group.include);
|
||||
|
||||
if (!options.masterPackageName) {
|
||||
console.log('changelog group options cannot contain empty masterPackageName.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!options.include) {
|
||||
console.log('changelog group options cannot contain empty include.');
|
||||
return false;
|
||||
}
|
||||
if (badGroups.length) {
|
||||
console.error(
|
||||
'ERROR: "changelog.groups" entries must define "changelogPath", "masterPackageName", and "include". ' +
|
||||
'Found invalid groups:\n' +
|
||||
badGroups.map(group => ' ' + singleLineStringify(group)).join('\n')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1,34 +1,47 @@
|
|||
import { formatList, singleLineStringify } from '../logging/format';
|
||||
import { VersionGroupOptions } from '../types/BeachballOptions';
|
||||
import { getPackageGroups } from '../monorepo/getPackageGroups';
|
||||
import { getPackageInfos } from '../monorepo/getPackageInfos';
|
||||
import { PackageGroups, PackageInfos } from '../types/PackageInfo';
|
||||
|
||||
export function isValidGroupOptions(root: string, groups: VersionGroupOptions[]) {
|
||||
export function isValidGroupOptions(groups: VersionGroupOptions[]) {
|
||||
// Values that violate types could happen in a user-provided object
|
||||
if (!Array.isArray(groups)) {
|
||||
console.error(
|
||||
'ERROR: Expected "groups" configuration setting to be an array. Received:\n' + JSON.stringify(groups)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
if (!group.include || !group.name) {
|
||||
return false;
|
||||
}
|
||||
const badGroups = groups.filter(group => !group.include || !group.name);
|
||||
if (badGroups.length) {
|
||||
console.error(
|
||||
'ERROR: "groups" configuration entries must define "include" and "name". Found invalid groups:\n' +
|
||||
badGroups.map(group => ' ' + singleLineStringify(group)).join('\n')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const packageInfos = getPackageInfos(root);
|
||||
const packageGroups = getPackageGroups(packageInfos, root, groups);
|
||||
// make sure no disallowed changetype options exist inside an individual package
|
||||
/** Validate per-package beachball options are valid for packages in groups */
|
||||
export function isValidGroupedPackageOptions(packageInfos: PackageInfos, packageGroups: PackageGroups) {
|
||||
const errorPackages: string[] = [];
|
||||
|
||||
for (const grp of Object.keys(packageGroups)) {
|
||||
const pkgs = packageGroups[grp].packageNames;
|
||||
for (const pkgName of pkgs) {
|
||||
// make sure no disallowed change type options exist inside an individual package
|
||||
for (const [groupName, { packageNames }] of Object.entries(packageGroups)) {
|
||||
for (const pkgName of packageNames) {
|
||||
if (packageInfos[pkgName].packageOptions.disallowedChangeTypes) {
|
||||
console.error(
|
||||
`Cannot have a disallowedChangeType inside a package config (${pkgName}) when there is a group defined; use the groups.disallowedChangeTypes instead.`
|
||||
);
|
||||
|
||||
return false;
|
||||
errorPackages.push(`${pkgName} in group "${groupName}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorPackages.length) {
|
||||
console.error(
|
||||
'ERROR: Found package configs that define disallowedChangeTypes and are also part of a group. ' +
|
||||
'Define disallowedChangeTypes in the group instead.\n' +
|
||||
formatList(errorPackages.sort())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { getUntrackedChanges } from 'workspace-tools';
|
|||
import { isValidAuthType } from './isValidAuthType';
|
||||
import { isValidChangeType } from './isValidChangeType';
|
||||
import { isChangeFileNeeded } from './isChangeFileNeeded';
|
||||
import { isValidGroupOptions } from './isValidGroupOptions';
|
||||
import { isValidGroupedPackageOptions, isValidGroupOptions } from './isValidGroupOptions';
|
||||
import { BeachballOptions } from '../types/BeachballOptions';
|
||||
import { isValidChangelogOptions } from './isValidChangelogOptions';
|
||||
import { readChangeFiles } from '../changefile/readChangeFiles';
|
||||
|
@ -15,114 +15,143 @@ import { validatePackageDependencies } from '../publish/validatePackageDependenc
|
|||
import { gatherBumpInfo } from '../bump/gatherBumpInfo';
|
||||
import { isValidDependentChangeType } from './isValidDependentChangeType';
|
||||
import { getPackagesToPublish } from '../publish/getPackagesToPublish';
|
||||
import { env } from '../env';
|
||||
|
||||
type ValidationOptions = {
|
||||
allowMissingChangeFiles?: boolean;
|
||||
/**
|
||||
* Defaults to true. If false, skip fetching the latest from the remote and don't check whether
|
||||
* changes files are needed (or whether change files are deleted).
|
||||
*/
|
||||
allowFetching?: boolean;
|
||||
/**
|
||||
* If true, skip checking whether change files are needed. Ignored if `allowFetching` is false.
|
||||
*/
|
||||
allowMissingChangeFiles?: boolean;
|
||||
};
|
||||
|
||||
export function validate(options: BeachballOptions, validateOptions?: Partial<ValidationOptions>) {
|
||||
const { allowMissingChangeFiles = false, allowFetching = true } = validateOptions || {};
|
||||
|
||||
console.log('\nValidating options and change files...');
|
||||
|
||||
// Run the validation checks in stages and wait to exit until the end of the stage.
|
||||
// This provides more potentially useful info the user rather than hiding errors.
|
||||
let hasError = false;
|
||||
|
||||
const logValidationError = (message: string) => {
|
||||
console.error(`ERROR: ${message}`);
|
||||
hasError = true;
|
||||
};
|
||||
|
||||
if (!isGitAvailable(options.path)) {
|
||||
console.error('ERROR: Please make sure git is installed and initialize the repository with "git init".');
|
||||
logValidationError('Please make sure git is installed and initialize the repository with "git init"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const untracked = getUntrackedChanges(options.path);
|
||||
|
||||
if (untracked && untracked.length > 0) {
|
||||
console.warn('WARN: There are untracked changes in your repository:');
|
||||
console.warn('- ' + untracked.join('\n- '));
|
||||
console.warn('Changes in these files will not trigger a prompt for change descriptions');
|
||||
if (untracked.length) {
|
||||
console.warn('WARN: There are untracked changes in your repository:\n' + untracked.join('\n- '));
|
||||
!env.isCI && console.warn('Changes in these files will not trigger a prompt for change descriptions');
|
||||
}
|
||||
|
||||
const packageInfos = getPackageInfos(options.path);
|
||||
|
||||
if (typeof options.package === 'string' && !packageInfos[options.package]) {
|
||||
console.error(`ERROR: package "${options.package}" was not found`);
|
||||
process.exit(1);
|
||||
}
|
||||
const invalidPackages = Array.isArray(options.package)
|
||||
? options.package.filter(pkg => !packageInfos[pkg])
|
||||
: undefined;
|
||||
if (invalidPackages?.length) {
|
||||
console.error(`ERROR: package(s) ${invalidPackages.map(pkg => `"${pkg}"`).join(', ')} were not found`);
|
||||
process.exit(1);
|
||||
logValidationError(`package "${options.package}" was not found`);
|
||||
} else {
|
||||
const invalidPackages = Array.isArray(options.package)
|
||||
? options.package.filter(pkg => !packageInfos[pkg])
|
||||
: undefined;
|
||||
if (invalidPackages?.length) {
|
||||
logValidationError(`package(s) ${invalidPackages.map(pkg => `"${pkg}"`).join(', ')} were not found`);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.authType && !isValidAuthType(options.authType)) {
|
||||
console.error(`ERROR: auth type "${options.authType}" is not valid`);
|
||||
process.exit(1);
|
||||
logValidationError(`authType "${options.authType}" is not valid`);
|
||||
}
|
||||
|
||||
if (options.dependentChangeType && !isValidChangeType(options.dependentChangeType)) {
|
||||
console.error(`ERROR: dependent change type "${options.dependentChangeType}" is not valid`);
|
||||
process.exit(1);
|
||||
logValidationError(`dependentChangeType "${options.dependentChangeType}" is not valid`);
|
||||
}
|
||||
|
||||
if (options.type && !isValidChangeType(options.type)) {
|
||||
console.error(`ERROR: change type "${options.type}" is not valid`);
|
||||
logValidationError(`Change type "${options.type}" is not valid`);
|
||||
}
|
||||
|
||||
if (options.changelog && !isValidChangelogOptions(options.changelog)) {
|
||||
hasError = true; // the helper logs this
|
||||
}
|
||||
|
||||
if (options.groups && !isValidGroupOptions(options.groups)) {
|
||||
hasError = true; // the helper logs this
|
||||
}
|
||||
|
||||
// this exits the process if any package belongs to multiple groups
|
||||
const packageGroups = getPackageGroups(packageInfos, options.path, options.groups);
|
||||
|
||||
if (options.groups && !isValidGroupedPackageOptions(packageInfos, packageGroups)) {
|
||||
hasError = true; // the helper logs this
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
// If any of the above basic checks failed, it doesn't make sense to check if change files are needed
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let isChangeNeeded = false;
|
||||
|
||||
if (allowFetching) {
|
||||
// This has the side effect of fetching, so call it even if !allowMissingChangeFiles for now
|
||||
isChangeNeeded = isChangeFileNeeded(options, packageInfos);
|
||||
|
||||
if (isChangeNeeded && !allowMissingChangeFiles) {
|
||||
console.error('ERROR: Change files are needed!');
|
||||
logValidationError('Change files are needed!');
|
||||
console.log(options.changehint);
|
||||
process.exit(1);
|
||||
process.exit(1); // exit here (this is the main poin)
|
||||
}
|
||||
|
||||
if (options.disallowDeletedChangeFiles && areChangeFilesDeleted(options)) {
|
||||
console.error('ERROR: Change files must not be deleted!');
|
||||
logValidationError('Change files must not be deleted!');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.groups && !isValidGroupOptions(options.path, options.groups)) {
|
||||
console.error('ERROR: Groups defined inside the configuration is invalid');
|
||||
console.log(options.groups);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.changelog && !isValidChangelogOptions(options.changelog)) {
|
||||
console.error('ERROR: Changelog defined inside the configuration is invalid');
|
||||
console.log(options.changelog);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const changeSet = readChangeFiles(options, packageInfos);
|
||||
const packageGroups = getPackageGroups(packageInfos, options.path, options.groups);
|
||||
|
||||
for (const { changeFile, change } of changeSet) {
|
||||
const disallowedChangeTypes = getDisallowedChangeTypes(change.packageName, packageInfos, packageGroups);
|
||||
|
||||
if (!change.type || !isValidChangeType(change.type) || disallowedChangeTypes?.includes(change.type)) {
|
||||
console.error(
|
||||
`ERROR: there is an invalid change type detected ${changeFile}: "${change.type}" is not a valid change type`
|
||||
);
|
||||
process.exit(1);
|
||||
if (!change.type) {
|
||||
logValidationError(`Change type is missing in ${changeFile}`);
|
||||
hasError = true;
|
||||
} else if (!isValidChangeType(change.type)) {
|
||||
logValidationError(`Invalid change type detected in ${changeFile}: "${change.type}"`);
|
||||
hasError = true;
|
||||
} else if (disallowedChangeTypes?.includes(change.type)) {
|
||||
logValidationError(`Disallowed change type detected in ${changeFile}: "${change.type}"`);
|
||||
hasError = true;
|
||||
}
|
||||
if (!change.dependentChangeType || !isValidDependentChangeType(change.dependentChangeType, disallowedChangeTypes)) {
|
||||
console.error(
|
||||
`ERROR: there is an invalid dependentChangeType detected ${changeFile}: "${change.dependentChangeType}" is not a valid dependentChangeType`
|
||||
);
|
||||
process.exit(1);
|
||||
|
||||
if (!change.dependentChangeType) {
|
||||
logValidationError(`dependentChangeType is missing in ${changeFile}`);
|
||||
hasError = true;
|
||||
} else if (!isValidDependentChangeType(change.dependentChangeType, disallowedChangeTypes)) {
|
||||
logValidationError(`Invalid dependentChangeType detected in ${changeFile}: "${change.dependentChangeType}"`);
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isChangeNeeded) {
|
||||
console.log('\nValidating package dependencies...');
|
||||
// TODO: It would be preferable if this could be done without getting the full bump info,
|
||||
// or at least if the bump info could be passed back out to other methods which currently
|
||||
// duplicate the calculation (it can be expensive, especially in large repos).
|
||||
const bumpInfo = gatherBumpInfo(options, packageInfos);
|
||||
const packagesToPublish = getPackagesToPublish(bumpInfo, true /*validationMode*/);
|
||||
if (!validatePackageDependencies(packagesToPublish, bumpInfo.packageInfos)) {
|
||||
console.error(`ERROR: one or more published packages depend on an unpublished package!
|
||||
logValidationError(`One or more published packages depend on an unpublished package!
|
||||
|
||||
Consider one of the following solutions:
|
||||
- If the unpublished package should be published, remove \`"private": true\` from its package.json.
|
||||
|
@ -132,6 +161,8 @@ Consider one of the following solutions:
|
|||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
return {
|
||||
isChangeNeeded,
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче