From 7d73fda20c4be451d2bf609e2fcee71c88d46b84 Mon Sep 17 00:00:00 2001 From: Elizabeth Craig Date: Tue, 11 Jul 2023 14:22:29 -0700 Subject: [PATCH] Add --all option for change --- ...-9bdf704a-61ed-4b77-8dfa-26bc583976fa.json | 7 +++ src/__e2e__/getChangedPackages.test.ts | 9 +++ src/changefile/getChangedPackages.ts | 56 +++++++++++++------ src/commands/change.ts | 3 +- src/packageManager/listPackageVersions.ts | 9 ++- 5 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 change/beachball-9bdf704a-61ed-4b77-8dfa-26bc583976fa.json diff --git a/change/beachball-9bdf704a-61ed-4b77-8dfa-26bc583976fa.json b/change/beachball-9bdf704a-61ed-4b77-8dfa-26bc583976fa.json new file mode 100644 index 000000000..a601a6260 --- /dev/null +++ b/change/beachball-9bdf704a-61ed-4b77-8dfa-26bc583976fa.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Add --all option for `change` command", + "packageName": "beachball", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/src/__e2e__/getChangedPackages.test.ts b/src/__e2e__/getChangedPackages.test.ts index 80c493334..0b056b63e 100644 --- a/src/__e2e__/getChangedPackages.test.ts +++ b/src/__e2e__/getChangedPackages.test.ts @@ -192,4 +192,13 @@ describe('getChangedPackages', () => { expect(changedPackagesB).toStrictEqual([]); expect(changedPackagesRoot).toStrictEqual(['@workspace-a/foo']); }); + + it('returns all packages with --all option', () => { + repositoryFactory = new RepositoryFactory('monorepo'); + const repo = repositoryFactory.cloneRepository(); + const options = { all: true, fetch: false, path: repo.rootPath, branch: defaultBranchName } as BeachballOptions; + const packageInfos = getPackageInfos(repo.rootPath); + + expect(getChangedPackages(options, packageInfos).sort()).toStrictEqual(['a', 'b', 'bar', 'baz', 'foo']); + }); }); diff --git a/src/changefile/getChangedPackages.ts b/src/changefile/getChangedPackages.ts index cdca1e1d4..bfd2759ec 100644 --- a/src/changefile/getChangedPackages.ts +++ b/src/changefile/getChangedPackages.ts @@ -32,16 +32,49 @@ function getMatchingPackageInfo( return undefined; } +/** Determines whether the package is publishable based on private flags and scopedPackages */ +function isPackagePublishable( + packageInfo: PackageInfo | undefined, + scopedPackages: string[] +): { result: boolean; reason: string } { + const reason = !packageInfo + ? 'no corresponding package found' + : packageInfo.private + ? `${packageInfo.name} is private` + : packageInfo.combinedOptions.shouldPublish === false + ? `${packageInfo.name} has beachball.shouldPublish=false` + : !scopedPackages.includes(packageInfo.name) + ? `${packageInfo.name} is out of scope` + : ''; // not ignored + + return { result: !reason, reason }; +} + /** - * Gets all the changed package names, regardless of the change files + * Gets all the changed package names, regardless of the change files. + * If `options.all` is set, returns all the packages in scope, regardless of whether they've changed. */ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageInfos): string[] { - const { branch, path: cwd, verbose } = options; + const { branch, path: cwd, verbose, all } = options; const verboseLog = (msg: string) => verbose && console.log(msg); const logIgnored = (file: string, reason: string) => verboseLog(` - ~~${file}~~ (${reason})`); const logIncluded = (file: string) => verboseLog(` - ${file}`); + const scopedPackages = getScopedPackages(options, packageInfos); + + // If --all is set, return all the packages in scope rather than looking at which files changed + if (all) { + verboseLog('--all option was provided, so including all packages that are in scope (regardless of changes)'); + return Object.values(packageInfos) + .filter(pkg => { + const { result, reason } = isPackagePublishable(pkg, scopedPackages); + verboseLog(result ? ` - ${pkg.name}` : ` - ~~${pkg.name}~~ (${reason.replace(`${pkg.name} `, '')})`); + return result; + }) + .map(pkg => pkg.name); + } + const changes = [...(getChanges(branch, cwd) || []), ...(getStagedChanges(cwd) || [])]; verboseLog(`Found ${count(changes.length, 'changed file')} in branch "${branch}" (before filtering)`); @@ -66,7 +99,6 @@ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageI // and whether that package is in scope and not private const includedPackages = new Set(); let fileCount = 0; - const scopedPackages = getScopedPackages(options, packageInfos); const packageInfosByPath: { [packageAbsNormalizedPath: string]: PackageInfo } = {}; for (const info of Object.values(packageInfos)) { packageInfosByPath[path.normalize(path.dirname(info.packageJsonPath))] = info; @@ -74,18 +106,10 @@ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageI for (const moddedFile of nonIgnoredChanges) { const packageInfo = getMatchingPackageInfo(moddedFile, cwd, packageInfosByPath); - const omitReason = !packageInfo - ? 'no corresponding package found' - : packageInfo.private - ? `${packageInfo.name} is private` - : packageInfo.combinedOptions.shouldPublish === false - ? `${packageInfo.name} has beachball.shouldPublish=false` - : !scopedPackages.includes(packageInfo.name) - ? `${packageInfo.name} is out of scope` - : ''; // not ignored - - if (omitReason) { - logIgnored(moddedFile, omitReason); + const { result, reason } = isPackagePublishable(packageInfo, scopedPackages); + + if (!result) { + logIgnored(moddedFile, reason); } else { includedPackages.add(packageInfo!.name); fileCount++; @@ -101,7 +125,7 @@ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageI } /** - * Gets all the changed packages, accounting for change files + * Gets all the changed packages which do not already have a change file */ export function getChangedPackages(options: BeachballOptions, packageInfos: PackageInfos): string[] { const { path: cwd, branch } = options; diff --git a/src/commands/change.ts b/src/commands/change.ts index 3782378d6..e0295cd8e 100644 --- a/src/commands/change.ts +++ b/src/commands/change.ts @@ -7,8 +7,7 @@ import { getChangedPackages } from '../changefile/getChangedPackages'; import { getPackageGroups } from '../monorepo/getPackageGroups'; export async function change(options: BeachballOptions): Promise { - const { branch, path: cwd } = options; - const { package: specificPackage } = options; + const { branch, path: cwd, package: specificPackage } = options; const packageInfos = getPackageInfos(cwd); const packageGroups = getPackageGroups(packageInfos, cwd, options.groups); diff --git a/src/packageManager/listPackageVersions.ts b/src/packageManager/listPackageVersions.ts index 2a9684414..9930721f6 100644 --- a/src/packageManager/listPackageVersions.ts +++ b/src/packageManager/listPackageVersions.ts @@ -41,16 +41,19 @@ export async function listPackageVersionsByTag( packageInfos: PackageInfo[], tag: string | undefined, options: NpmOptions -): Promise<{ [pkg: string]: string | undefined }> { +): Promise<{ [pkg: string]: string }> { const limit = pLimit(NPM_CONCURRENCY); - const versions: { [pkg: string]: string | undefined } = {}; + const versions: { [pkg: string]: string } = {}; await Promise.all( packageInfos.map(pkg => limit(async () => { const info = await getNpmPackageInfo(pkg.name, options); const npmTag = tag || pkg.combinedOptions.tag || pkg.combinedOptions.defaultNpmTag; - versions[pkg.name] = (npmTag && info['dist-tags']?.[npmTag]) || undefined; + const version = npmTag && info['dist-tags']?.[npmTag]; + if (version) { + versions[pkg.name] = version; + } }) ) );