зеркало из https://github.com/microsoft/beachball.git
Add --all option for change command (#891)
This commit is contained in:
Родитель
8dd65ddd8f
Коммит
73d3558a8e
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "minor",
|
||||
"comment": "Add --all option for `change` command",
|
||||
"packageName": "beachball",
|
||||
"email": "elcraig@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -16,9 +16,43 @@ $ beachball change
|
|||
|
||||
### Options
|
||||
|
||||
See the [options page](./options).
|
||||
Some [general options](./options) including `--branch` and `--scope` also apply for this command.
|
||||
|
||||
### Walkthrough
|
||||
| Option | Alias | Default | Description |
|
||||
| ------------- | ----- | -------------------- | --------------------------------------------------------------------------------- |
|
||||
| `--all` | | false | Generate change files for all packages |
|
||||
| `--message` | `-m` | (interactive prompt) | Description for all change files |
|
||||
| `--no-commit` | | false | Stage the change files rather than committing |
|
||||
| `--package` | | (changed packages) | Generate change files for these packages (option can be specified multiple times) |
|
||||
| `--type` | | (interactive prompt) | Type for all the change files (must be valid for each package) |
|
||||
|
||||
### Examples
|
||||
|
||||
Basic interactive prompt (see [walkthrough](#prompt-walkthrough) for details):
|
||||
|
||||
```
|
||||
beachball change
|
||||
```
|
||||
|
||||
Skip the interactive prompt by specifying a message and type for all changed packages:
|
||||
|
||||
```
|
||||
beachball change --type patch --message 'some message'
|
||||
```
|
||||
|
||||
Generate change file for specific package(s), regardless of changes, and even if a change file already exists for the package in this branch. Each package must be specified with a separate `--package` option. (You can also use the `--message` and `--type` options here.)
|
||||
|
||||
```
|
||||
beachball change --package foo --package bar
|
||||
```
|
||||
|
||||
Generate change files for all packages, regardless of changes. This would most often be used for build config updates which only touch a shared config file, but actually impact the output of all packages.
|
||||
|
||||
```
|
||||
beachball change --all --type patch --message 'update build output settings'
|
||||
```
|
||||
|
||||
### Prompt walkthrough
|
||||
|
||||
If you have changes that are not committed yet (i.e. `git status` reports changes), then `beachball change` will warn you about these:
|
||||
|
||||
|
@ -31,28 +65,34 @@ There are uncommitted changes in your repository. Please commit these files firs
|
|||
|
||||
Make sure to commit _all_ changes before proceeding with the `change` command.
|
||||
|
||||
Now we'll commit the changes we made and run `beachball change` again:
|
||||
After committing, run `beachball change`:
|
||||
|
||||
```
|
||||
$ beachball change
|
||||
Defaults to "origin/master"
|
||||
Checking for changes against "origin/master"
|
||||
|
||||
Please describe the changes for: single
|
||||
? Describe changes (type or choose one) ›
|
||||
adding a new file
|
||||
Validating options and change files...
|
||||
Checking for changes against "origin/main"
|
||||
Found changes in the following packages:
|
||||
some-pkg
|
||||
```
|
||||
|
||||
First, it will ask for a **description** of the change. You can enter any text, but `beachball` will also provide a list of recent commit messages to choose from.
|
||||
For each package, the prompt will start by asking for a change **type**. This should be chosen based on [semantic versioning rules](https://semver.org/) because it determines how to update the package version. If the change doesn't affect the published package at all (e.g. you just updated some comments), choose `none`.
|
||||
|
||||
```
|
||||
Please describe the changes for: some-pkg
|
||||
? Change type › - Use arrow-keys. Return to submit.
|
||||
❯ Patch - bug fixes; no backwards incompatible changes.
|
||||
Minor - small feature; backwards compatible changes.
|
||||
None - this change does not affect the published package in any way.
|
||||
Major - major feature; breaking changes.
|
||||
```
|
||||
|
||||
Next, it asks for a **description** of the change. You can type any text or choose from a list of recent commit messages.
|
||||
|
||||
> Tip: These descriptions will be collated into a changelog when the change is published by `beachball publish`, so think about how to describe your change in a way that's helpful and relevant for consumers of the package.
|
||||
|
||||
Next, the form will ask for a change **type**. This should be chosen based on [semantic versioning rules](https://semver.org/) because it determines how to update the package version. If the change doesn't affect the published package at all (e.g. you just updated some comments), choose `none`.
|
||||
|
||||
```bash
|
||||
? Change type › - Use arrow-keys. Return to submit.
|
||||
❯ Patch - bug fixes; no backwards incompatible changes.
|
||||
Minor - small feature; backwards compatible changes.
|
||||
None - this change does not affect the published package in any way.
|
||||
Major - major feature; breaking changes.
|
||||
```
|
||||
Please describe the changes for: some-pkg
|
||||
? Describe changes (type or choose one) ›
|
||||
adding a new file
|
||||
```
|
||||
|
|
|
@ -13,19 +13,14 @@ For the latest full list of supported options, see `CliOptions` [in this file](h
|
|||
|
||||
These apply to most CLI commands.
|
||||
|
||||
| Option | Alias | Default | Description |
|
||||
| ---------- | ---------- | ----------------- | ------------------------- |
|
||||
| `--branch` | `-b` | `'origin/master'` | target branch from origin |
|
||||
| `--help` | `-?`, `-h` | | show help message |
|
||||
| Option | Alias | Default | Description |
|
||||
| ------------ | ----- | ---------------------------------------------------------------------------- | ------------------------- |
|
||||
| `--branch` | `-b` | Detected default branch in default remote, falling back to `'origin/master'` | target branch from origin |
|
||||
| `--no-fetch` | | | Disable fetching |
|
||||
|
||||
## Change options
|
||||
## `change` options
|
||||
|
||||
These options are applicable to the `change` command.
|
||||
|
||||
| Option | Alias | Default | Description |
|
||||
| ----------- | ----- | -------------------- | -------------------------------------------------------------- |
|
||||
| `--message` | `-m` | (interactive prompt) | Description for all change files |
|
||||
| `--type` | | (interactive prompt) | Type for all the change files (must be valid for each package) |
|
||||
See the [`change` page](./change).
|
||||
|
||||
## Bumping and publishing options
|
||||
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,15 +33,51 @@ function getMatchingPackageInfo(
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets all the changed package names, regardless of the change files
|
||||
* Determines whether the package is included in the list of potentially-changed published packages,
|
||||
* based on private flags and scopedPackages.
|
||||
*/
|
||||
function isPackageIncluded(
|
||||
packageInfo: PackageInfo | undefined,
|
||||
scopedPackages: string[]
|
||||
): { isIncluded: 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 { isIncluded: !reason, reason };
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 { isIncluded, reason } = isPackageIncluded(pkg, scopedPackages);
|
||||
verboseLog(isIncluded ? ` - ${pkg.name}` : ` - ~~${pkg.name}~~ (${reason.replace(`${pkg.name} `, '')})`);
|
||||
return isIncluded;
|
||||
})
|
||||
.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 +102,6 @@ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageI
|
|||
// and whether that package is in scope and not private
|
||||
const includedPackages = new Set<string>();
|
||||
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 +109,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
|
||||
const { isIncluded, reason } = isPackageIncluded(packageInfo, scopedPackages);
|
||||
|
||||
if (omitReason) {
|
||||
logIgnored(moddedFile, omitReason);
|
||||
if (!isIncluded) {
|
||||
logIgnored(moddedFile, reason);
|
||||
} else {
|
||||
includedPackages.add(packageInfo!.name);
|
||||
fileCount++;
|
||||
|
@ -101,7 +128,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;
|
||||
|
|
|
@ -7,8 +7,7 @@ import { getChangedPackages } from '../changefile/getChangedPackages';
|
|||
import { getPackageGroups } from '../monorepo/getPackageGroups';
|
||||
|
||||
export async function change(options: BeachballOptions): Promise<void> {
|
||||
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);
|
||||
|
|
|
@ -42,16 +42,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;
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
|
@ -60,7 +60,9 @@ export function validate(
|
|||
|
||||
const packageInfos = getPackageInfos(options.path);
|
||||
|
||||
if (typeof options.package === 'string' && !packageInfos[options.package]) {
|
||||
if (options.all && options.package) {
|
||||
logValidationError('Cannot specify both "all" and "package" options');
|
||||
} else if (typeof options.package === 'string' && !packageInfos[options.package]) {
|
||||
logValidationError(`package "${options.package}" was not found`);
|
||||
} else {
|
||||
const invalidPackages = Array.isArray(options.package)
|
||||
|
|
Загрузка…
Ссылка в новой задаче