feat(release): add nx release plan:check command to ensure relevant version plans exist (#27343)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> When using version plans as a versioning strategy with `nx release`, there is no way to enforce that version plan files are created when changing files. ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> There is a new `nx release plan:check` subcommand, intended to be run in CI (and/or potentially a git hook) which will return with exit code 1 if touched projects are not represented in at least one version plan file on disk. What constitutes a touched file is shared with our `affected` logic in other commands, with the additionally capability to be able to ignore file patterns from consideration. This would be useful for not requiring version plans when only documentation or spec files change, for example. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
add5a675c3
commit
4108bfc8a6
789
e2e/release/src/version-plans-check.test.ts
Normal file
789
e2e/release/src/version-plans-check.test.ts
Normal file
@ -0,0 +1,789 @@
|
||||
import { NxJsonConfiguration } from '@nx/devkit';
|
||||
import {
|
||||
cleanupProject,
|
||||
newProject,
|
||||
runCLI,
|
||||
runCommandAsync,
|
||||
tmpProjPath,
|
||||
uniq,
|
||||
updateJson,
|
||||
} from '@nx/e2e/utils';
|
||||
import { readdirSync, readFileSync, writeFileSync } from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
|
||||
expect.addSnapshotSerializer({
|
||||
serialize(str: string) {
|
||||
return (
|
||||
str
|
||||
// Remove all output unique to specific projects to ensure deterministic snapshots
|
||||
.replaceAll(/my-pkg-\d+/g, '{project-name}')
|
||||
.replaceAll(
|
||||
/integrity:\s*.*/g,
|
||||
'integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
||||
)
|
||||
.replaceAll(/\b[0-9a-f]{40}\b/g, '{SHASUM}')
|
||||
.replaceAll(/\d*B index\.js/g, 'XXB index.js')
|
||||
.replaceAll(/\d*B project\.json/g, 'XXB project.json')
|
||||
.replaceAll(/\d*B package\.json/g, 'XXXB package.json')
|
||||
.replaceAll(/size:\s*\d*\s?B/g, 'size: XXXB')
|
||||
.replaceAll(/\d*\.\d*\s?kB/g, 'XXX.XXX kb')
|
||||
.replaceAll(/version-plan-\d*\.md/g, 'version-plan-{RANDOM_NUMBER}.md')
|
||||
.replaceAll(/[a-fA-F0-9]{7}/g, '{COMMIT_SHA}')
|
||||
.replaceAll(/Test @[\w\d]+/g, 'Test @{COMMIT_AUTHOR}')
|
||||
// Normalize the version title date.
|
||||
.replaceAll(/\(\d{4}-\d{2}-\d{2}\)/g, '(YYYY-MM-DD)')
|
||||
// We trim each line to reduce the chances of snapshot flakiness
|
||||
.split('\n')
|
||||
.map((r) => r.trim())
|
||||
.join('\n')
|
||||
);
|
||||
},
|
||||
test(val: string) {
|
||||
return val != null && typeof val === 'string';
|
||||
},
|
||||
});
|
||||
|
||||
describe('nx release version plans check command', () => {
|
||||
let pkg1: string;
|
||||
let pkg2: string;
|
||||
let pkg3: string;
|
||||
let pkg4: string;
|
||||
let pkg5: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
newProject({
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
packages: ['@nx/js'],
|
||||
});
|
||||
|
||||
pkg1 = uniq('my-pkg-1');
|
||||
runCLI(`generate @nx/workspace:npm-package ${pkg1}`);
|
||||
|
||||
pkg2 = uniq('my-pkg-2');
|
||||
runCLI(`generate @nx/workspace:npm-package ${pkg2}`);
|
||||
|
||||
pkg3 = uniq('my-pkg-3');
|
||||
runCLI(`generate @nx/workspace:npm-package ${pkg3}`);
|
||||
|
||||
pkg4 = uniq('my-pkg-4');
|
||||
runCLI(`generate @nx/workspace:npm-package ${pkg4}`);
|
||||
|
||||
pkg5 = uniq('my-pkg-5');
|
||||
runCLI(`generate @nx/workspace:npm-package ${pkg5}`);
|
||||
|
||||
await runCommandAsync(`git add .`);
|
||||
await runCommandAsync(`git commit -m "chore: initial commit"`);
|
||||
await runCommandAsync(`git tag -a v0.0.0 -m "v0.0.0"`);
|
||||
await runCommandAsync(`git tag -a ${pkg3}@0.0.0 -m "${pkg3}@0.0.0"`);
|
||||
await runCommandAsync(`git tag -a ${pkg4}@0.0.0 -m "${pkg4}@0.0.0"`);
|
||||
await runCommandAsync(`git tag -a ${pkg5}@0.0.0 -m "${pkg5}@0.0.0"`);
|
||||
}, 60000);
|
||||
|
||||
afterEach(() => cleanupProject());
|
||||
|
||||
it('should work as expected when there are no version plan files on disk', async () => {
|
||||
// it should error if version plans are not yet enabled
|
||||
expect(runCLI('release plan:check', { silenceError: true }))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX Version plans are not enabled
|
||||
|
||||
Please ensure at least one release group has version plans enabled in your Nx release configuration if you want to use this command.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// Enable version plans
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
versionPlans: true,
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX No touched projects found based on changed files
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// it should provide more information about changed files and base and head used when --verbose is passed
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
|
||||
|
||||
NX No touched projects found based on changed files
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
});
|
||||
|
||||
it('should work as expected when there are version plans on disk for the default release group', async () => {
|
||||
// Enable version plans
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
versionPlans: true,
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
// Create a version plan
|
||||
runCLI(`release plan minor --message "This is an awesome change"`);
|
||||
|
||||
// it should show information about the pending bumps and print a success message
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX There are pending bumps in version plan(s)
|
||||
|
||||
- "minor" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// it should provide more information about changed files and base and head used when --verbose is passed
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- .nx/version-plans/version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX There are pending bumps in version plan(s)
|
||||
|
||||
- "minor" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// it should allow configuring a custom base and head via CLI args
|
||||
expect(runCLI('release plan:check --base=HEAD~1 --head=HEAD~1'))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX There are pending bumps in version plan(s)
|
||||
|
||||
- "minor" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --base=HEAD~1 --head=HEAD~1 --verbose'))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX No changed files found based on resolved "base" and "head"
|
||||
|
||||
|
||||
NX There are pending bumps in version plan(s)
|
||||
|
||||
- "minor" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// it should allow configuring a custom base and head via env vars
|
||||
expect(
|
||||
runCLI('release plan:check', {
|
||||
env: { NX_BASE: 'HEAD~1', NX_HEAD: 'HEAD~1' },
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
|
||||
NX There are pending bumps in version plan(s)
|
||||
|
||||
- "minor" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(
|
||||
runCLI('release plan:check --verbose', {
|
||||
env: { NX_BASE: 'HEAD~1', NX_HEAD: 'HEAD~1' },
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
|
||||
NX No explicit --base argument provided, but found environment variable NX_BASE so using its value as the affected base: HEAD~1
|
||||
|
||||
|
||||
NX No explicit --head argument provided, but found environment variable NX_HEAD so using its value as the affected head: HEAD~1
|
||||
|
||||
|
||||
NX No changed files found based on resolved "base" and "head"
|
||||
|
||||
|
||||
NX There are pending bumps in version plan(s)
|
||||
|
||||
- "minor" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
});
|
||||
|
||||
it('should work as expected when there are version plans on disk for multiple release groups', async () => {
|
||||
// Enable version plans and configure release groups
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
versionPlans: true,
|
||||
groups: {
|
||||
'fixed-group': {
|
||||
projects: [pkg1, pkg2],
|
||||
},
|
||||
'independent-group': {
|
||||
projects: [pkg3, pkg4, pkg5],
|
||||
projectsRelationship: 'independent',
|
||||
},
|
||||
},
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
// it should provide logs about each release group not having any touched projects
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX No touched projects found based on changed files under release group "fixed-group"
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "fixed-group"
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// create a version plan which references fixed-group directly by name
|
||||
runCLI(
|
||||
`release plan patch --message "A change for fixed-group" -g fixed-group`
|
||||
);
|
||||
|
||||
// it should provide logs about the pending bump for fixed-group
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Release group "fixed-group" has pending bumps in version plan(s)
|
||||
|
||||
- "patch" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- .nx/version-plans/version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX Release group "fixed-group" has pending bumps in version plan(s)
|
||||
|
||||
- "patch" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// patch the version plan file to reference an individual project within the fixed-group
|
||||
patchVersionPlanFile((currentContents) =>
|
||||
currentContents.replaceAll('fixed-group', pkg1)
|
||||
);
|
||||
|
||||
// it should provide logs about the pending bump for fixed-group in the same way as when the group was mentioned by name (because of the fixed relationship)
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Release group "fixed-group" has pending bumps in version plan(s)
|
||||
|
||||
- "patch" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- .nx/version-plans/version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX Release group "fixed-group" has pending bumps in version plan(s)
|
||||
|
||||
- "patch" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// should error if the independent release group is referenced directly by name in a version plan
|
||||
patchVersionPlanFile((currentContents) =>
|
||||
currentContents.replaceAll(pkg1, 'independent-group')
|
||||
);
|
||||
expect(runCLI('release plan:check', { silenceError: true }))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX Found a version bump for group 'independent-group' in 'version-plan-{RANDOM_NUMBER}.md' but the group's projects are independently versioned. Individual projects of 'independent-group' should be bumped instead.
|
||||
|
||||
Pass --verbose to see the stacktrace.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// patch the version plan file to reference one of the independent packages
|
||||
patchVersionPlanFile((currentContents) =>
|
||||
currentContents.replaceAll('independent-group', pkg3)
|
||||
);
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX No touched projects found based on changed files under release group "fixed-group"
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX Project "{project-name}" has pending bumps in version plan(s)
|
||||
|
||||
- "patch" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- .nx/version-plans/version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "fixed-group"
|
||||
|
||||
|
||||
NX No touched projects found based on changed files under release group "independent-group"
|
||||
|
||||
|
||||
NX Project "{project-name}" has pending bumps in version plan(s)
|
||||
|
||||
- "patch" in version-plan-{RANDOM_NUMBER}.md
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
});
|
||||
|
||||
it('should take "ignorePatternsForPlanCheck" into account when determining if projects are touched', async () => {
|
||||
// Enable version plans for the default release group
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
versionPlans: true,
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
// Update a file in pkg1
|
||||
const pkg1Dir = join(tmpProjPath(), pkg1);
|
||||
writeFileSync(join(pkg1Dir, 'file.ts'), 'const a = 1;');
|
||||
|
||||
// it should show information about the missing version plan and show an error message
|
||||
expect(runCLI('release plan:check', { silenceError: true }))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX Touched projects based on changed files
|
||||
|
||||
- {project-name}
|
||||
|
||||
NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects missing version plans
|
||||
|
||||
The following touched projects do not feature in any version plan files:
|
||||
- {project-name}
|
||||
|
||||
Please use \`nx release plan\` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose', { silenceError: true }))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- {project-name}/file.ts
|
||||
|
||||
|
||||
NX Touched projects based on changed files
|
||||
|
||||
- {project-name}
|
||||
|
||||
NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects missing version plans
|
||||
|
||||
The following touched projects do not feature in any version plan files:
|
||||
- {project-name}
|
||||
|
||||
Please use \`nx release plan\` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// Configure ignorePatternsForPlanCheck to ignore the change in pkg1
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: ['*.ts'],
|
||||
},
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
// it should show information about the ignore patterns being applied and now show a success message
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Applying configured ignore patterns to changed files
|
||||
|
||||
- *.ts
|
||||
|
||||
|
||||
NX No touched projects found based on changed files combined with configured ignore patterns
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- {project-name}/file.ts
|
||||
|
||||
|
||||
NX Applying configured ignore patterns to changed files
|
||||
|
||||
- *.ts
|
||||
|
||||
|
||||
NX No touched projects found based on changed files combined with configured ignore patterns
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// Configure release groups with no ignore patterns initially
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
versionPlans: true,
|
||||
groups: {
|
||||
'fixed-group': {
|
||||
projects: [pkg1, pkg2],
|
||||
},
|
||||
'independent-group': {
|
||||
projects: [pkg3, pkg4, pkg5],
|
||||
projectsRelationship: 'independent',
|
||||
},
|
||||
},
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
// Update a file in pkg3 to affect the independent release group as well
|
||||
const pkg3Dir = join(tmpProjPath(), pkg3);
|
||||
writeFileSync(join(pkg3Dir, 'file.css'), '.foo { color: red; }');
|
||||
|
||||
// it should show information about the missing version plans and show an error message
|
||||
expect(runCLI('release plan:check', { silenceError: true }))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX Touched projects based on changed files under release group "fixed-group"
|
||||
|
||||
- {project-name}
|
||||
|
||||
NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects missing version plans
|
||||
|
||||
The following touched projects under release group "fixed-group" do not feature in any version plan files:
|
||||
- {project-name}
|
||||
|
||||
Please use \`nx release plan\` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
NX Touched projects based on changed files under release group "independent-group"
|
||||
|
||||
- {project-name}
|
||||
|
||||
NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects missing version plans
|
||||
|
||||
The following touched projects under release group "independent-group" do not feature in any version plan files:
|
||||
- {project-name}
|
||||
|
||||
Please use \`nx release plan\` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose', { silenceError: true }))
|
||||
.toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- {project-name}/file.ts
|
||||
- {project-name}/file.css
|
||||
|
||||
|
||||
NX Touched projects based on changed files under release group "fixed-group"
|
||||
|
||||
- {project-name}
|
||||
|
||||
NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects missing version plans
|
||||
|
||||
The following touched projects under release group "fixed-group" do not feature in any version plan files:
|
||||
- {project-name}
|
||||
|
||||
Please use \`nx release plan\` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects based on changed files under release group "independent-group"
|
||||
|
||||
- {project-name}
|
||||
|
||||
NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.
|
||||
|
||||
|
||||
NX Touched projects missing version plans
|
||||
|
||||
The following touched projects under release group "independent-group" do not feature in any version plan files:
|
||||
- {project-name}
|
||||
|
||||
Please use \`nx release plan\` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// Configure release groups with different ignore patterns to each other
|
||||
updateJson<NxJsonConfiguration>('nx.json', (json) => {
|
||||
json.release = {
|
||||
groups: {
|
||||
'fixed-group': {
|
||||
projects: [pkg1, pkg2],
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: ['*.ts'],
|
||||
},
|
||||
},
|
||||
'independent-group': {
|
||||
projects: [pkg3, pkg4, pkg5],
|
||||
projectsRelationship: 'independent',
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: ['*.css'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return json;
|
||||
});
|
||||
|
||||
// it should show information about the ignore patterns being applied to each one and now show a success message
|
||||
expect(runCLI('release plan:check')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Applying configured ignore patterns to changed files for release group "fixed-group"
|
||||
|
||||
- *.ts
|
||||
|
||||
|
||||
NX No touched projects found based on changed files combined with configured ignore patterns under release group "fixed-group"
|
||||
|
||||
|
||||
NX Applying configured ignore patterns to changed files for release group "independent-group"
|
||||
|
||||
- *.css
|
||||
|
||||
|
||||
NX No touched projects found based on changed files combined with configured ignore patterns under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
Run with --verbose to see the full list of changed files used for the touched projects logic.
|
||||
|
||||
|
||||
`);
|
||||
expect(runCLI('release plan:check --verbose')).toMatchInlineSnapshot(`
|
||||
|
||||
NX Affected criteria defaulted to --base=main --head=HEAD
|
||||
|
||||
|
||||
NX Changed files based on resolved "base" ({SHASUM}) and "head" (HEAD)
|
||||
|
||||
- nx.json
|
||||
- {project-name}/file.ts
|
||||
- {project-name}/file.css
|
||||
|
||||
|
||||
NX Applying configured ignore patterns to changed files for release group "fixed-group"
|
||||
|
||||
- *.ts
|
||||
|
||||
|
||||
NX No touched projects found based on changed files combined with configured ignore patterns under release group "fixed-group"
|
||||
|
||||
|
||||
NX Applying configured ignore patterns to changed files for release group "independent-group"
|
||||
|
||||
- *.css
|
||||
|
||||
|
||||
NX No touched projects found based on changed files combined with configured ignore patterns under release group "independent-group"
|
||||
|
||||
|
||||
NX All touched projects have, or do not require, version plans.
|
||||
|
||||
|
||||
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
function patchVersionPlanFile(replacer: (currentContents: string) => string) {
|
||||
const versionPlansDir = join(tmpProjPath(), '.nx', 'version-plans');
|
||||
const versionPlanFiles = readdirSync(versionPlansDir);
|
||||
const versionPlanFilePath = join(versionPlansDir, versionPlanFiles[0]);
|
||||
const versionPlanContents = readFileSync(versionPlanFilePath, 'utf-8');
|
||||
writeFileSync(versionPlanFilePath, replacer(versionPlanContents));
|
||||
}
|
||||
@ -463,7 +463,8 @@ To fix this you will either need to add a package.json file at that location, or
|
||||
|
||||
if (options.releaseGroup.projectsRelationship === 'independent') {
|
||||
specifier = (
|
||||
options.releaseGroup.versionPlans as ProjectsVersionPlan[]
|
||||
options.releaseGroup
|
||||
.resolvedVersionPlans as ProjectsVersionPlan[]
|
||||
).reduce((spec: ReleaseType, plan: ProjectsVersionPlan) => {
|
||||
if (!spec) {
|
||||
return plan.projectVersionBumps[projectName];
|
||||
@ -482,7 +483,7 @@ To fix this you will either need to add a package.json file at that location, or
|
||||
}, null);
|
||||
} else {
|
||||
specifier = (
|
||||
options.releaseGroup.versionPlans as GroupVersionPlan[]
|
||||
options.releaseGroup.resolvedVersionPlans as GroupVersionPlan[]
|
||||
).reduce((spec: ReleaseType, plan: GroupVersionPlan) => {
|
||||
if (!spec) {
|
||||
return plan.groupVersionBump;
|
||||
@ -522,7 +523,7 @@ To fix this you will either need to add a package.json file at that location, or
|
||||
}
|
||||
|
||||
if (options.deleteVersionPlans) {
|
||||
options.releaseGroup.versionPlans.forEach((p) => {
|
||||
(options.releaseGroup.resolvedVersionPlans || []).forEach((p) => {
|
||||
deleteVersionPlanCallbacks.push(async (dryRun?: boolean) => {
|
||||
if (!dryRun) {
|
||||
await remove(p.absolutePath);
|
||||
@ -599,14 +600,14 @@ To fix this you will either need to add a package.json file at that location, or
|
||||
|
||||
// For version-plans, we don't just need to consider the current batch of projects, but also the ones that are actually being updated as part of the plan file(s)
|
||||
if (isInCurrentBatch && options.specifierSource === 'version-plans') {
|
||||
isInCurrentBatch = (options.releaseGroup.versionPlans || []).some(
|
||||
(plan) => {
|
||||
if ('projectVersionBumps' in plan) {
|
||||
return plan.projectVersionBumps[dependentProject.source];
|
||||
}
|
||||
return true;
|
||||
isInCurrentBatch = (
|
||||
options.releaseGroup.resolvedVersionPlans || []
|
||||
).some((plan) => {
|
||||
if ('projectVersionBumps' in plan) {
|
||||
return plan.projectVersionBumps[dependentProject.source];
|
||||
}
|
||||
);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (!isInCurrentBatch) {
|
||||
|
||||
@ -186,8 +186,15 @@
|
||||
"type": "string"
|
||||
},
|
||||
"versionPlans": {
|
||||
"type": "boolean",
|
||||
"description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/NxReleaseVersionPlansConfiguration"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["projects"]
|
||||
@ -239,8 +246,15 @@
|
||||
"$ref": "#/definitions/NxReleaseVersionConfiguration"
|
||||
},
|
||||
"versionPlans": {
|
||||
"type": "boolean",
|
||||
"description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/NxReleaseVersionPlansConfiguration"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
|
||||
}
|
||||
]
|
||||
},
|
||||
"releaseTagPattern": {
|
||||
"type": "string"
|
||||
@ -698,6 +712,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"NxReleaseVersionPlansConfiguration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ignorePatternsForPlanCheck": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Changes to files matching any of these optional patterns will be excluded from the affected project logic within the `nx release plan:check` command. This is useful for ignoring files that are not relevant to the versioning process, such as documentation or configuration files."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChangelogRenderOptions": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
|
||||
@ -43,7 +43,7 @@ import {
|
||||
GroupVersionPlan,
|
||||
ProjectsVersionPlan,
|
||||
readRawVersionPlans,
|
||||
setVersionPlansOnGroups,
|
||||
setResolvedVersionPlansOnGroups,
|
||||
} from './config/version-plans';
|
||||
import {
|
||||
GitCommit,
|
||||
@ -179,7 +179,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
process.exit(1);
|
||||
}
|
||||
const rawVersionPlans = await readRawVersionPlans();
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
Object.keys(projectGraph.nodes)
|
||||
@ -274,12 +274,13 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
|
||||
// If there are multiple release groups, we'll just skip the workspace changelog anyway.
|
||||
const versionPlansEnabledForWorkspaceChangelog =
|
||||
releaseGroups[0].versionPlans;
|
||||
releaseGroups[0].resolvedVersionPlans;
|
||||
if (versionPlansEnabledForWorkspaceChangelog) {
|
||||
if (releaseGroups.length === 1) {
|
||||
const releaseGroup = releaseGroups[0];
|
||||
if (releaseGroup.projectsRelationship === 'fixed') {
|
||||
const versionPlans = releaseGroup.versionPlans as GroupVersionPlan[];
|
||||
const versionPlans =
|
||||
releaseGroup.resolvedVersionPlans as GroupVersionPlan[];
|
||||
workspaceChangelogChanges = versionPlans
|
||||
.flatMap((vp) => {
|
||||
const releaseType = versionPlanSemverReleaseTypeToChangelogType(
|
||||
@ -490,8 +491,10 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
// TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
|
||||
let commits: GitCommit[];
|
||||
|
||||
if (releaseGroup.versionPlans) {
|
||||
changes = (releaseGroup.versionPlans as ProjectsVersionPlan[])
|
||||
if (releaseGroup.resolvedVersionPlans) {
|
||||
changes = (
|
||||
releaseGroup.resolvedVersionPlans as ProjectsVersionPlan[]
|
||||
)
|
||||
.map((vp) => {
|
||||
const bumpForProject = vp.projectVersionBumps[project.name];
|
||||
if (!bumpForProject) {
|
||||
@ -637,8 +640,8 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
let changes: ChangelogChange[] = [];
|
||||
// TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
|
||||
let commits: GitCommit[] = [];
|
||||
if (releaseGroup.versionPlans) {
|
||||
changes = (releaseGroup.versionPlans as GroupVersionPlan[])
|
||||
if (releaseGroup.resolvedVersionPlans) {
|
||||
changes = (releaseGroup.resolvedVersionPlans as GroupVersionPlan[])
|
||||
.flatMap((vp) => {
|
||||
const releaseType = versionPlanSemverReleaseTypeToChangelogType(
|
||||
vp.groupVersionBump
|
||||
@ -918,8 +921,8 @@ async function applyChangesAndExit(
|
||||
if (args.deleteVersionPlans && !args.dryRun) {
|
||||
const planFiles = new Set<string>();
|
||||
releaseGroups.forEach((group) => {
|
||||
if (group.versionPlans) {
|
||||
group.versionPlans.forEach((plan) => {
|
||||
if (group.resolvedVersionPlans) {
|
||||
group.resolvedVersionPlans.forEach((plan) => {
|
||||
removeSync(plan.absolutePath);
|
||||
planFiles.add(plan.relativePath);
|
||||
});
|
||||
|
||||
@ -1,23 +1,28 @@
|
||||
import { Argv, CommandModule, showHelp } from 'yargs';
|
||||
import { readNxJson } from '../../config/nx-json';
|
||||
import { readParallelFromArgsAndEnv } from '../../utils/command-line-utils';
|
||||
import { logger } from '../../utils/logger';
|
||||
import {
|
||||
OutputStyle,
|
||||
RunManyOptions,
|
||||
parseCSV,
|
||||
withAffectedOptions,
|
||||
withOutputStyleOption,
|
||||
withOverrides,
|
||||
withRunManyOptions,
|
||||
} from '../yargs-utils/shared-options';
|
||||
import { VersionData } from './utils/shared';
|
||||
import { readParallelFromArgsAndEnv } from '../../utils/command-line-utils';
|
||||
|
||||
export interface NxReleaseArgs {
|
||||
// Implemented by every command and subcommand
|
||||
export interface BaseNxReleaseArgs {
|
||||
verbose?: boolean;
|
||||
printConfig?: boolean | 'debug';
|
||||
}
|
||||
|
||||
export interface NxReleaseArgs extends BaseNxReleaseArgs {
|
||||
groups?: string[];
|
||||
projects?: string[];
|
||||
dryRun?: boolean;
|
||||
verbose?: boolean;
|
||||
printConfig?: boolean | 'debug';
|
||||
}
|
||||
|
||||
interface GitCommitAndTagOptions {
|
||||
@ -66,6 +71,14 @@ export type PlanOptions = NxReleaseArgs & {
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type PlanCheckOptions = BaseNxReleaseArgs & {
|
||||
base?: string;
|
||||
head?: string;
|
||||
files?: string;
|
||||
uncommitted?: boolean;
|
||||
untracked?: boolean;
|
||||
};
|
||||
|
||||
export type ReleaseOptions = NxReleaseArgs &
|
||||
FirstReleaseArgs & {
|
||||
yes?: boolean;
|
||||
@ -94,6 +107,7 @@ export const yargsReleaseCommand: CommandModule<
|
||||
.command(changelogCommand)
|
||||
.command(publishCommand)
|
||||
.command(planCommand)
|
||||
.command(planCheckCommand)
|
||||
.demandCommand()
|
||||
// Error on typos/mistyped CLI args, there is no reason to support arbitrary unknown args for these commands
|
||||
.strictOptions()
|
||||
@ -337,6 +351,7 @@ const publishCommand: CommandModule<NxReleaseArgs, PublishOptions> = {
|
||||
const planCommand: CommandModule<NxReleaseArgs, PlanOptions> = {
|
||||
command: 'plan [bump]',
|
||||
aliases: ['pl'],
|
||||
// TODO: Remove this when docs are added
|
||||
// Create a plan to pick a new version and generate a changelog entry.
|
||||
// Hidden for now until the feature is more stable
|
||||
describe: false,
|
||||
@ -371,6 +386,20 @@ const planCommand: CommandModule<NxReleaseArgs, PlanOptions> = {
|
||||
},
|
||||
};
|
||||
|
||||
const planCheckCommand: CommandModule<NxReleaseArgs, PlanCheckOptions> = {
|
||||
command: 'plan:check',
|
||||
// TODO: Remove this when docs are added
|
||||
// Create a plan to pick a new version and generate a changelog entry.
|
||||
// Hidden for now until the feature is more stable
|
||||
describe: false,
|
||||
builder: (yargs) => withAffectedOptions(yargs),
|
||||
handler: async (args) => {
|
||||
const release = await import('./plan-check');
|
||||
const result = await release.releasePlanCheckCLIHandler(args);
|
||||
process.exit(result);
|
||||
},
|
||||
};
|
||||
|
||||
function coerceParallelOption(args: any) {
|
||||
return {
|
||||
...args,
|
||||
|
||||
@ -8412,13 +8412,17 @@ describe('createNxReleaseConfig()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('versionPlans shorthand', () => {
|
||||
describe('versionPlans', () => {
|
||||
it('should respect user "versionPlans" set at root level', async () => {
|
||||
const res = await createNxReleaseConfig(projectGraph, projectFileMap, {
|
||||
versionPlans: true,
|
||||
});
|
||||
const resBoolean = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
{
|
||||
versionPlans: true,
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
expect(resBoolean).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": null,
|
||||
"nxReleaseConfig": {
|
||||
@ -8594,23 +8598,222 @@ describe('createNxReleaseConfig()', () => {
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
const resObject = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
{
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: ['**/*.spec.ts'],
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(resObject).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": null,
|
||||
"nxReleaseConfig": {
|
||||
"changelog": {
|
||||
"automaticFromRef": false,
|
||||
"git": {
|
||||
"commit": true,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": false,
|
||||
"tag": true,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"projectChangelogs": false,
|
||||
"workspaceChangelog": {
|
||||
"createRelease": false,
|
||||
"entryWhenNoChanges": "This was a version bump only, there were no code changes.",
|
||||
"file": "{workspaceRoot}/CHANGELOG.md",
|
||||
"renderOptions": {
|
||||
"authors": true,
|
||||
"commitReferences": true,
|
||||
"mapAuthorsToGitHubUsernames": true,
|
||||
"versionTitleDate": true,
|
||||
},
|
||||
"renderer": "<dirname>/release/changelog-renderer",
|
||||
},
|
||||
},
|
||||
"conventionalCommits": {
|
||||
"types": {
|
||||
"build": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "📦 Build",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"chore": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🏡 Chore",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"ci": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🤖 CI",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"docs": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "📖 Documentation",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"examples": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🏀 Examples",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"feat": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🚀 Features",
|
||||
},
|
||||
"semverBump": "minor",
|
||||
},
|
||||
"fix": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🩹 Fixes",
|
||||
},
|
||||
"semverBump": "patch",
|
||||
},
|
||||
"perf": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🔥 Performance",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"refactor": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "💅 Refactors",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"revert": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "⏪ Revert",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"style": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🎨 Styles",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"test": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "✅ Tests",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"types": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🌊 Types",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
"git": {
|
||||
"commit": false,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": false,
|
||||
"tag": false,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"groups": {
|
||||
"__default__": {
|
||||
"changelog": false,
|
||||
"projects": [
|
||||
"lib-a",
|
||||
"lib-b",
|
||||
"nx",
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {
|
||||
"specifierSource": "version-plans",
|
||||
},
|
||||
},
|
||||
"versionPlans": {
|
||||
"ignorePatternsForPlanCheck": [
|
||||
"**/*.spec.ts",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {
|
||||
"specifierSource": "version-plans",
|
||||
},
|
||||
"git": {
|
||||
"commit": false,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": true,
|
||||
"tag": false,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"preVersionCommand": "",
|
||||
},
|
||||
"versionPlans": {
|
||||
"ignorePatternsForPlanCheck": [
|
||||
"**/*.spec.ts",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should respect user "versionPlans" set at group level', async () => {
|
||||
const res = await createNxReleaseConfig(projectGraph, projectFileMap, {
|
||||
groups: {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: true,
|
||||
const resBoolean = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
{
|
||||
groups: {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: true,
|
||||
},
|
||||
'group-2': {
|
||||
projects: 'lib-a',
|
||||
versionPlans: false,
|
||||
},
|
||||
},
|
||||
'group-2': {
|
||||
projects: 'lib-a',
|
||||
versionPlans: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
expect(resBoolean).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": null,
|
||||
"nxReleaseConfig": {
|
||||
@ -8785,23 +8988,226 @@ describe('createNxReleaseConfig()', () => {
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
const resObject = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
{
|
||||
groups: {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: ['**/eslint.config.js'],
|
||||
},
|
||||
},
|
||||
'group-2': {
|
||||
projects: 'lib-a',
|
||||
versionPlans: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(resObject).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": null,
|
||||
"nxReleaseConfig": {
|
||||
"changelog": {
|
||||
"automaticFromRef": false,
|
||||
"git": {
|
||||
"commit": true,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": false,
|
||||
"tag": true,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"projectChangelogs": false,
|
||||
"workspaceChangelog": false,
|
||||
},
|
||||
"conventionalCommits": {
|
||||
"types": {
|
||||
"build": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "📦 Build",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"chore": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🏡 Chore",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"ci": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🤖 CI",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"docs": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "📖 Documentation",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"examples": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🏀 Examples",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"feat": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🚀 Features",
|
||||
},
|
||||
"semverBump": "minor",
|
||||
},
|
||||
"fix": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🩹 Fixes",
|
||||
},
|
||||
"semverBump": "patch",
|
||||
},
|
||||
"perf": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🔥 Performance",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"refactor": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "💅 Refactors",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"revert": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "⏪ Revert",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"style": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🎨 Styles",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"test": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "✅ Tests",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"types": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🌊 Types",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
"git": {
|
||||
"commit": false,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": false,
|
||||
"tag": false,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"groups": {
|
||||
"group-1": {
|
||||
"changelog": false,
|
||||
"projects": [
|
||||
"nx",
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {
|
||||
"specifierSource": "version-plans",
|
||||
},
|
||||
},
|
||||
"versionPlans": {
|
||||
"ignorePatternsForPlanCheck": [
|
||||
"**/eslint.config.js",
|
||||
],
|
||||
},
|
||||
},
|
||||
"group-2": {
|
||||
"changelog": false,
|
||||
"projects": [
|
||||
"lib-a",
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {},
|
||||
},
|
||||
"versionPlans": false,
|
||||
},
|
||||
},
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {},
|
||||
"git": {
|
||||
"commit": false,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": true,
|
||||
"tag": false,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"preVersionCommand": "",
|
||||
},
|
||||
"versionPlans": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should override "versionPlans" with false when set at the group level', async () => {
|
||||
const res = await createNxReleaseConfig(projectGraph, projectFileMap, {
|
||||
versionPlans: true,
|
||||
groups: {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: false,
|
||||
const resBoolean = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
{
|
||||
versionPlans: true,
|
||||
groups: {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: false,
|
||||
},
|
||||
'group-2': {
|
||||
projects: 'lib-a',
|
||||
},
|
||||
},
|
||||
'group-2': {
|
||||
projects: 'lib-a',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
expect(resBoolean).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": null,
|
||||
"nxReleaseConfig": {
|
||||
@ -8978,6 +9384,213 @@ describe('createNxReleaseConfig()', () => {
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
const resObject = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
projectFileMap,
|
||||
{
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: [
|
||||
'**/?(*.)+(spec|test).[jt]s?(x)?(.snap)',
|
||||
],
|
||||
},
|
||||
groups: {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: false,
|
||||
},
|
||||
'group-2': {
|
||||
projects: 'lib-a',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(resObject).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": null,
|
||||
"nxReleaseConfig": {
|
||||
"changelog": {
|
||||
"automaticFromRef": false,
|
||||
"git": {
|
||||
"commit": true,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": false,
|
||||
"tag": true,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"projectChangelogs": false,
|
||||
"workspaceChangelog": false,
|
||||
},
|
||||
"conventionalCommits": {
|
||||
"types": {
|
||||
"build": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "📦 Build",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"chore": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🏡 Chore",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"ci": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🤖 CI",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"docs": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "📖 Documentation",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"examples": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🏀 Examples",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"feat": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🚀 Features",
|
||||
},
|
||||
"semverBump": "minor",
|
||||
},
|
||||
"fix": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🩹 Fixes",
|
||||
},
|
||||
"semverBump": "patch",
|
||||
},
|
||||
"perf": {
|
||||
"changelog": {
|
||||
"hidden": false,
|
||||
"title": "🔥 Performance",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"refactor": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "💅 Refactors",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"revert": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "⏪ Revert",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"style": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🎨 Styles",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"test": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "✅ Tests",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
"types": {
|
||||
"changelog": {
|
||||
"hidden": true,
|
||||
"title": "🌊 Types",
|
||||
},
|
||||
"semverBump": "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
"git": {
|
||||
"commit": false,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": false,
|
||||
"tag": false,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"groups": {
|
||||
"group-1": {
|
||||
"changelog": false,
|
||||
"projects": [
|
||||
"nx",
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {},
|
||||
},
|
||||
"versionPlans": false,
|
||||
},
|
||||
"group-2": {
|
||||
"changelog": false,
|
||||
"projects": [
|
||||
"lib-a",
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {
|
||||
"specifierSource": "version-plans",
|
||||
},
|
||||
},
|
||||
"versionPlans": {
|
||||
"ignorePatternsForPlanCheck": [
|
||||
"**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "v{version}",
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "@nx/js:release-version",
|
||||
"generatorOptions": {
|
||||
"specifierSource": "version-plans",
|
||||
},
|
||||
"git": {
|
||||
"commit": false,
|
||||
"commitArgs": "",
|
||||
"commitMessage": "chore(release): publish {version}",
|
||||
"stageChanges": true,
|
||||
"tag": false,
|
||||
"tagArgs": "",
|
||||
"tagMessage": "",
|
||||
},
|
||||
"preVersionCommand": "",
|
||||
},
|
||||
"versionPlans": {
|
||||
"ignorePatternsForPlanCheck": [
|
||||
"**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -260,7 +260,8 @@ export async function createNxReleaseConfig(
|
||||
? defaultIndependentReleaseTagPattern
|
||||
: defaultFixedReleaseTagPattern),
|
||||
conventionalCommits: DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
|
||||
versionPlans: false,
|
||||
versionPlans: (userConfig.versionPlans ||
|
||||
false) as NxReleaseConfig['versionPlans'],
|
||||
};
|
||||
|
||||
const groupProjectsRelationship =
|
||||
@ -340,7 +341,8 @@ export async function createNxReleaseConfig(
|
||||
);
|
||||
|
||||
const rootVersionPlansConfig: NxReleaseConfig['versionPlans'] =
|
||||
userConfig.versionPlans ?? WORKSPACE_DEFAULTS.versionPlans;
|
||||
(userConfig.versionPlans ??
|
||||
WORKSPACE_DEFAULTS.versionPlans) as NxReleaseConfig['versionPlans'];
|
||||
|
||||
const rootConventionalCommitsConfig: NxReleaseConfig['conventionalCommits'] =
|
||||
deepMergeDefaults(
|
||||
|
||||
@ -160,6 +160,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -175,6 +176,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -194,6 +196,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -211,6 +214,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -331,6 +335,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "independent",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -350,6 +355,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "independent",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -419,6 +425,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
@ -438,6 +445,7 @@ describe('filterReleaseGroups()', () => {
|
||||
],
|
||||
"projectsRelationship": "fixed",
|
||||
"releaseTagPattern": "",
|
||||
"resolvedVersionPlans": false,
|
||||
"version": {
|
||||
"conventionalCommits": false,
|
||||
"generator": "",
|
||||
|
||||
@ -4,12 +4,9 @@ import { output } from '../../../utils/output';
|
||||
import { IMPLICIT_DEFAULT_RELEASE_GROUP, NxReleaseConfig } from './config';
|
||||
import { GroupVersionPlan, ProjectsVersionPlan } from './version-plans';
|
||||
|
||||
export type ReleaseGroupWithName = Omit<
|
||||
NxReleaseConfig['groups'][string],
|
||||
'versionPlans'
|
||||
> & {
|
||||
export type ReleaseGroupWithName = NxReleaseConfig['groups'][string] & {
|
||||
name: string;
|
||||
versionPlans: (ProjectsVersionPlan | GroupVersionPlan)[] | false;
|
||||
resolvedVersionPlans: (ProjectsVersionPlan | GroupVersionPlan)[] | false;
|
||||
};
|
||||
|
||||
export function filterReleaseGroups(
|
||||
@ -28,7 +25,7 @@ export function filterReleaseGroups(
|
||||
return {
|
||||
...group,
|
||||
name,
|
||||
versionPlans: group.versionPlans ? [] : false,
|
||||
resolvedVersionPlans: group.versionPlans ? [] : false,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { ReleaseGroupWithName } from './filter-release-groups';
|
||||
import {
|
||||
RawVersionPlan,
|
||||
readRawVersionPlans,
|
||||
setVersionPlansOnGroups,
|
||||
setResolvedVersionPlansOnGroups,
|
||||
} from './version-plans';
|
||||
|
||||
expect.addSnapshotSerializer({
|
||||
@ -143,7 +143,7 @@ describe('version-plans', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setVersionPlansOnGroups', () => {
|
||||
describe('setResolvedVersionPlansOnGroups', () => {
|
||||
describe('error cases', () => {
|
||||
describe('for default group', () => {
|
||||
describe('when bump "key" is a group name', () => {
|
||||
@ -163,14 +163,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: [],
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -194,14 +194,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'independent',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -225,14 +225,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -257,14 +257,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -290,14 +290,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = [];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -321,14 +321,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -352,14 +352,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1', 'pkg2'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -383,14 +383,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -415,14 +415,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1', 'pkg2'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1', 'pkg2'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -450,14 +450,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -481,14 +481,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'independent',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -512,14 +512,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -544,14 +544,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -577,20 +577,20 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
releaseGroup({
|
||||
name: 'group2',
|
||||
projects: ['pkg2'],
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1', 'pkg2'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -614,14 +614,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = [];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -645,13 +645,13 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
releaseGroup({
|
||||
name: 'group2',
|
||||
projects: ['pkg2'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
@ -662,7 +662,7 @@ describe('version-plans', () => {
|
||||
];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -686,14 +686,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -718,14 +718,14 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1', 'pkg2'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
const allProjectNamesInWorkspace: string[] = ['pkg1', 'pkg2'];
|
||||
|
||||
expect(() =>
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -767,7 +767,7 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1', 'pkg2', 'pkg3'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
@ -775,7 +775,7 @@ describe('version-plans', () => {
|
||||
|
||||
expect(
|
||||
peelResultFromGroups(
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -786,7 +786,7 @@ describe('version-plans', () => {
|
||||
[
|
||||
{
|
||||
name: __default__,
|
||||
versionPlans: [
|
||||
resolvedVersionPlans: [
|
||||
{
|
||||
absolutePath: <workspace-root>/version-plans/plan1.md,
|
||||
createdOnMs: 20,
|
||||
@ -853,7 +853,7 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
projects: ['pkg1', 'pkg2', 'pkg3'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'independent',
|
||||
}),
|
||||
];
|
||||
@ -861,7 +861,7 @@ describe('version-plans', () => {
|
||||
|
||||
expect(
|
||||
peelResultFromGroups(
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -871,7 +871,7 @@ describe('version-plans', () => {
|
||||
[
|
||||
{
|
||||
name: __default__,
|
||||
versionPlans: [
|
||||
resolvedVersionPlans: [
|
||||
{
|
||||
absolutePath: <workspace-root>/version-plans/plan3.md,
|
||||
createdOnMs: 23,
|
||||
@ -951,31 +951,31 @@ describe('version-plans', () => {
|
||||
releaseGroup({
|
||||
name: 'group1',
|
||||
projects: ['pkg1'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
releaseGroup({
|
||||
name: 'group2',
|
||||
projects: ['pkg2'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
releaseGroup({
|
||||
name: 'group3',
|
||||
projects: ['pkg3'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
releaseGroup({
|
||||
name: 'group4',
|
||||
projects: ['pkg4', 'pkg5'],
|
||||
versionPlans: [],
|
||||
resolvedVersionPlans: [],
|
||||
projectsRelationship: 'independent',
|
||||
}),
|
||||
releaseGroup({
|
||||
name: 'group5',
|
||||
projects: ['pkg6'],
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
projectsRelationship: 'fixed',
|
||||
}),
|
||||
];
|
||||
@ -989,7 +989,7 @@ describe('version-plans', () => {
|
||||
|
||||
expect(
|
||||
peelResultFromGroups(
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
allProjectNamesInWorkspace
|
||||
@ -999,7 +999,7 @@ describe('version-plans', () => {
|
||||
[
|
||||
{
|
||||
name: group1,
|
||||
versionPlans: [
|
||||
resolvedVersionPlans: [
|
||||
{
|
||||
absolutePath: <workspace-root>/version-plans/plan3.md,
|
||||
createdOnMs: 26,
|
||||
@ -1031,7 +1031,7 @@ describe('version-plans', () => {
|
||||
},
|
||||
{
|
||||
name: group2,
|
||||
versionPlans: [
|
||||
resolvedVersionPlans: [
|
||||
{
|
||||
absolutePath: <workspace-root>/version-plans/plan3.md,
|
||||
createdOnMs: 26,
|
||||
@ -1055,7 +1055,7 @@ describe('version-plans', () => {
|
||||
},
|
||||
{
|
||||
name: group3,
|
||||
versionPlans: [
|
||||
resolvedVersionPlans: [
|
||||
{
|
||||
absolutePath: <workspace-root>/version-plans/plan1.md,
|
||||
createdOnMs: 25,
|
||||
@ -1079,7 +1079,7 @@ describe('version-plans', () => {
|
||||
},
|
||||
{
|
||||
name: group4,
|
||||
versionPlans: [
|
||||
resolvedVersionPlans: [
|
||||
{
|
||||
absolutePath: <workspace-root>/version-plans/plan3.md,
|
||||
createdOnMs: 26,
|
||||
@ -1105,7 +1105,7 @@ describe('version-plans', () => {
|
||||
},
|
||||
{
|
||||
name: group5,
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
},
|
||||
]
|
||||
`);
|
||||
@ -1145,10 +1145,10 @@ function releaseGroup(
|
||||
|
||||
function peelResultFromGroups(releaseGroups: ReleaseGroupWithName[]): {
|
||||
name: string;
|
||||
versionPlans: ReleaseGroupWithName['versionPlans'];
|
||||
resolvedVersionPlans: ReleaseGroupWithName['resolvedVersionPlans'];
|
||||
}[] {
|
||||
return releaseGroups.map((g) => ({
|
||||
name: g.name,
|
||||
versionPlans: g.versionPlans,
|
||||
resolvedVersionPlans: g.resolvedVersionPlans,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ export async function readRawVersionPlans(): Promise<RawVersionPlan[]> {
|
||||
return versionPlans;
|
||||
}
|
||||
|
||||
export function setVersionPlansOnGroups(
|
||||
export function setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans: RawVersionPlan[],
|
||||
releaseGroups: ReleaseGroupWithName[],
|
||||
allProjectNamesInWorkspace: string[]
|
||||
@ -89,7 +89,7 @@ export function setVersionPlansOnGroups(
|
||||
if (groupsByName.has(key)) {
|
||||
const group = groupsByName.get(key);
|
||||
|
||||
if (!group.versionPlans) {
|
||||
if (!group.resolvedVersionPlans) {
|
||||
if (isDefaultGroup) {
|
||||
throw new Error(
|
||||
`Found a version bump in '${rawVersionPlan.fileName}' but version plans are not enabled.`
|
||||
@ -134,7 +134,7 @@ export function setVersionPlansOnGroups(
|
||||
}
|
||||
|
||||
const existingPlan = <GroupVersionPlan>(
|
||||
group.versionPlans.find(
|
||||
group.resolvedVersionPlans.find(
|
||||
(plan) => plan.fileName === rawVersionPlan.fileName
|
||||
)
|
||||
);
|
||||
@ -151,7 +151,7 @@ export function setVersionPlansOnGroups(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
group.versionPlans.push(<GroupVersionPlan>{
|
||||
group.resolvedVersionPlans.push(<GroupVersionPlan>{
|
||||
absolutePath: rawVersionPlan.absolutePath,
|
||||
relativePath: rawVersionPlan.relativePath,
|
||||
fileName: rawVersionPlan.fileName,
|
||||
@ -182,7 +182,7 @@ export function setVersionPlansOnGroups(
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupForProject.versionPlans) {
|
||||
if (!groupForProject.resolvedVersionPlans) {
|
||||
if (isDefaultGroup) {
|
||||
throw new Error(
|
||||
`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but version plans are not enabled.`
|
||||
@ -206,14 +206,14 @@ export function setVersionPlansOnGroups(
|
||||
|
||||
if (groupForProject.projectsRelationship === 'independent') {
|
||||
const existingPlan = <ProjectsVersionPlan>(
|
||||
groupForProject.versionPlans.find(
|
||||
groupForProject.resolvedVersionPlans.find(
|
||||
(plan) => plan.fileName === rawVersionPlan.fileName
|
||||
)
|
||||
);
|
||||
if (existingPlan) {
|
||||
existingPlan.projectVersionBumps[key] = value;
|
||||
} else {
|
||||
groupForProject.versionPlans.push(<ProjectsVersionPlan>{
|
||||
groupForProject.resolvedVersionPlans.push(<ProjectsVersionPlan>{
|
||||
absolutePath: rawVersionPlan.absolutePath,
|
||||
relativePath: rawVersionPlan.relativePath,
|
||||
fileName: rawVersionPlan.fileName,
|
||||
@ -226,7 +226,7 @@ export function setVersionPlansOnGroups(
|
||||
}
|
||||
} else {
|
||||
const existingPlan = <GroupVersionPlan>(
|
||||
groupForProject.versionPlans.find(
|
||||
groupForProject.resolvedVersionPlans.find(
|
||||
(plan) => plan.fileName === rawVersionPlan.fileName
|
||||
)
|
||||
);
|
||||
@ -247,7 +247,7 @@ export function setVersionPlansOnGroups(
|
||||
existingPlan.triggeredByProjects.push(key);
|
||||
}
|
||||
} else {
|
||||
groupForProject.versionPlans.push(<GroupVersionPlan>{
|
||||
groupForProject.resolvedVersionPlans.push(<GroupVersionPlan>{
|
||||
absolutePath: rawVersionPlan.absolutePath,
|
||||
relativePath: rawVersionPlan.relativePath,
|
||||
fileName: rawVersionPlan.fileName,
|
||||
@ -266,8 +266,8 @@ export function setVersionPlansOnGroups(
|
||||
|
||||
// Order the plans from newest to oldest
|
||||
releaseGroups.forEach((group) => {
|
||||
if (group.versionPlans) {
|
||||
group.versionPlans.sort((a, b) => b.createdOnMs - a.createdOnMs);
|
||||
if (group.resolvedVersionPlans) {
|
||||
group.resolvedVersionPlans.sort((a, b) => b.createdOnMs - a.createdOnMs);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
343
packages/nx/src/command-line/release/plan-check.ts
Normal file
343
packages/nx/src/command-line/release/plan-check.ts
Normal file
@ -0,0 +1,343 @@
|
||||
import { NxReleaseConfiguration, readNxJson } from '../../config/nx-json';
|
||||
import { getTouchedProjects } from '../../project-graph/affected/locators/workspace-projects';
|
||||
import { createProjectFileMapUsingProjectGraph } from '../../project-graph/file-map-utils';
|
||||
import { calculateFileChanges } from '../../project-graph/file-utils';
|
||||
import { createProjectGraphAsync } from '../../project-graph/project-graph';
|
||||
import { allFileData } from '../../utils/all-file-data';
|
||||
import {
|
||||
parseFiles,
|
||||
splitArgsIntoNxArgsAndOverrides,
|
||||
} from '../../utils/command-line-utils';
|
||||
import { getIgnoreObject } from '../../utils/ignore';
|
||||
import { output } from '../../utils/output';
|
||||
import { handleErrors } from '../../utils/params';
|
||||
import { PlanCheckOptions, PlanOptions } from './command-object';
|
||||
import {
|
||||
createNxReleaseConfig,
|
||||
handleNxReleaseConfigError,
|
||||
IMPLICIT_DEFAULT_RELEASE_GROUP,
|
||||
} from './config/config';
|
||||
import { deepMergeJson } from './config/deep-merge-json';
|
||||
import { filterReleaseGroups } from './config/filter-release-groups';
|
||||
import {
|
||||
readRawVersionPlans,
|
||||
setResolvedVersionPlansOnGroups,
|
||||
} from './config/version-plans';
|
||||
import { printConfigAndExit } from './utils/print-config';
|
||||
|
||||
export const releasePlanCheckCLIHandler = (args: PlanCheckOptions) =>
|
||||
handleErrors(args.verbose, () => createAPI({})(args));
|
||||
|
||||
export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
return async function releasePlanCheck(args: PlanOptions): Promise<number> {
|
||||
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||
const nxJson = readNxJson();
|
||||
const userProvidedReleaseConfig = deepMergeJson(
|
||||
nxJson.release ?? {},
|
||||
overrideReleaseConfig ?? {}
|
||||
);
|
||||
|
||||
if (args.verbose) {
|
||||
process.env.NX_VERBOSE_LOGGING = 'true';
|
||||
}
|
||||
|
||||
// Apply default configuration to any optional user configuration
|
||||
const { error: configError, nxReleaseConfig } = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
await createProjectFileMapUsingProjectGraph(projectGraph),
|
||||
userProvidedReleaseConfig
|
||||
);
|
||||
if (configError) {
|
||||
return await handleNxReleaseConfigError(configError);
|
||||
}
|
||||
// --print-config exits directly as it is not designed to be combined with any other programmatic operations
|
||||
if (args.printConfig) {
|
||||
return printConfigAndExit({
|
||||
userProvidedReleaseConfig,
|
||||
nxReleaseConfig,
|
||||
isDebug: args.printConfig === 'debug',
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
error: filterError,
|
||||
releaseGroups,
|
||||
releaseGroupToFilteredProjects,
|
||||
} = filterReleaseGroups(
|
||||
projectGraph,
|
||||
nxReleaseConfig,
|
||||
args.projects,
|
||||
args.groups
|
||||
);
|
||||
if (filterError) {
|
||||
output.error(filterError);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// If no release groups have version plans enabled, provide an explicit error
|
||||
if (!releaseGroups.some((group) => group.versionPlans)) {
|
||||
output.error({
|
||||
title: 'Version plans are not enabled',
|
||||
bodyLines: [
|
||||
'Please ensure at least one release group has version plans enabled in your Nx release configuration if you want to use this command.',
|
||||
// TODO: Add docs link here once it is available
|
||||
],
|
||||
});
|
||||
return 1;
|
||||
}
|
||||
|
||||
const rawVersionPlans = await readRawVersionPlans();
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
Object.keys(projectGraph.nodes)
|
||||
);
|
||||
|
||||
// Resolve the final values for base, head etc to use when resolving the changes to consider
|
||||
const { nxArgs } = splitArgsIntoNxArgsAndOverrides(
|
||||
args,
|
||||
'affected',
|
||||
{
|
||||
printWarnings: args.verbose,
|
||||
},
|
||||
nxJson
|
||||
);
|
||||
|
||||
const changedFiles = parseFiles(nxArgs).files;
|
||||
if (nxArgs.verbose) {
|
||||
if (changedFiles.length) {
|
||||
output.log({
|
||||
title: `Changed files based on resolved "base" (${
|
||||
nxArgs.base
|
||||
}) and "head" (${nxArgs.head ?? 'HEAD'})`,
|
||||
bodyLines: changedFiles.map((file) => ` - ${file}`),
|
||||
});
|
||||
} else {
|
||||
output.warn({
|
||||
title: 'No changed files found based on resolved "base" and "head"',
|
||||
});
|
||||
}
|
||||
}
|
||||
const resolvedAllFileData = await allFileData();
|
||||
|
||||
/**
|
||||
* Create a minimal subset of touched projects based on the configured ignore patterns, we only need
|
||||
* to recompute when the ignorePatternsForPlanCheck differs between release groups.
|
||||
*/
|
||||
const serializedIgnorePatternsToTouchedProjects = new Map<
|
||||
string,
|
||||
Record<string, true> // project names -> true for O(N) lookup later
|
||||
>();
|
||||
|
||||
const NOTE_ABOUT_VERBOSE_LOGGING =
|
||||
'Run with --verbose to see the full list of changed files used for the touched projects logic.';
|
||||
|
||||
let hasErrors = false;
|
||||
|
||||
for (const releaseGroup of releaseGroups) {
|
||||
// The current release group doesn't leverage version plans
|
||||
if (!releaseGroup.versionPlans) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const resolvedVersionPlans = releaseGroup.resolvedVersionPlans || [];
|
||||
|
||||
// Check upfront if the release group as a whole is featured in any version plan files
|
||||
const matchingVersionPlanFiles = resolvedVersionPlans.filter(
|
||||
(plan) => 'groupVersionBump' in plan
|
||||
);
|
||||
if (matchingVersionPlanFiles.length) {
|
||||
output.log({
|
||||
title: `${
|
||||
releaseGroup.name === IMPLICIT_DEFAULT_RELEASE_GROUP
|
||||
? `There are`
|
||||
: `Release group "${releaseGroup.name}" has`
|
||||
} pending bumps in version plan(s)`,
|
||||
bodyLines: [
|
||||
...matchingVersionPlanFiles.map(
|
||||
(plan) => ` - "${plan.groupVersionBump}" in ${plan.fileName}`
|
||||
),
|
||||
],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude patterns from .nxignore, .gitignore and explicit version plan config
|
||||
let serializedIgnorePatterns = '[]';
|
||||
const ignore = getIgnoreObject();
|
||||
|
||||
if (
|
||||
typeof releaseGroup.versionPlans !== 'boolean' &&
|
||||
Array.isArray(releaseGroup.versionPlans.ignorePatternsForPlanCheck) &&
|
||||
releaseGroup.versionPlans.ignorePatternsForPlanCheck.length
|
||||
) {
|
||||
output.note({
|
||||
title: `Applying configured ignore patterns to changed files${
|
||||
releaseGroup.name !== IMPLICIT_DEFAULT_RELEASE_GROUP
|
||||
? ` for release group "${releaseGroup.name}"`
|
||||
: ''
|
||||
}`,
|
||||
bodyLines: [
|
||||
...releaseGroup.versionPlans.ignorePatternsForPlanCheck.map(
|
||||
(pattern) => ` - ${pattern}`
|
||||
),
|
||||
],
|
||||
});
|
||||
ignore.add(releaseGroup.versionPlans.ignorePatternsForPlanCheck);
|
||||
serializedIgnorePatterns = JSON.stringify(
|
||||
releaseGroup.versionPlans.ignorePatternsForPlanCheck
|
||||
);
|
||||
}
|
||||
|
||||
let touchedProjects = {};
|
||||
if (
|
||||
serializedIgnorePatternsToTouchedProjects.has(serializedIgnorePatterns)
|
||||
) {
|
||||
touchedProjects = serializedIgnorePatternsToTouchedProjects.get(
|
||||
serializedIgnorePatterns
|
||||
);
|
||||
} else {
|
||||
// We only care about directly touched projects, not implicitly affected ones etc
|
||||
const touchedProjectsArr = await getTouchedProjects(
|
||||
calculateFileChanges(
|
||||
changedFiles,
|
||||
resolvedAllFileData,
|
||||
nxArgs,
|
||||
undefined,
|
||||
ignore
|
||||
),
|
||||
projectGraph.nodes
|
||||
);
|
||||
touchedProjects = touchedProjectsArr.reduce(
|
||||
(acc, project) => ({ ...acc, [project]: true }),
|
||||
{}
|
||||
);
|
||||
serializedIgnorePatternsToTouchedProjects.set(
|
||||
serializedIgnorePatterns,
|
||||
touchedProjects
|
||||
);
|
||||
}
|
||||
|
||||
const touchedProjectsUnderReleaseGroup = releaseGroup.projects.filter(
|
||||
(project) => touchedProjects[project]
|
||||
);
|
||||
if (touchedProjectsUnderReleaseGroup.length) {
|
||||
output.log({
|
||||
title: `Touched projects based on changed files${
|
||||
releaseGroup.name !== IMPLICIT_DEFAULT_RELEASE_GROUP
|
||||
? ` under release group "${releaseGroup.name}"`
|
||||
: ''
|
||||
}`,
|
||||
bodyLines: [
|
||||
...touchedProjectsUnderReleaseGroup.map(
|
||||
(project) => ` - ${project}`
|
||||
),
|
||||
'',
|
||||
'NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.',
|
||||
],
|
||||
});
|
||||
} else {
|
||||
output.log({
|
||||
title: `No touched projects found based on changed files${
|
||||
typeof releaseGroup.versionPlans !== 'boolean' &&
|
||||
Array.isArray(
|
||||
releaseGroup.versionPlans.ignorePatternsForPlanCheck
|
||||
) &&
|
||||
releaseGroup.versionPlans.ignorePatternsForPlanCheck.length
|
||||
? ' combined with configured ignore patterns'
|
||||
: ''
|
||||
}${
|
||||
releaseGroup.name !== IMPLICIT_DEFAULT_RELEASE_GROUP
|
||||
? ` under release group "${releaseGroup.name}"`
|
||||
: ''
|
||||
}`,
|
||||
});
|
||||
}
|
||||
|
||||
const projectsInResolvedVersionPlans: Record<
|
||||
string,
|
||||
{ bump: string; fileName: string }[]
|
||||
> = resolvedVersionPlans.reduce((acc, plan) => {
|
||||
if ('projectVersionBumps' in plan) {
|
||||
for (const project in plan.projectVersionBumps) {
|
||||
acc[project] = acc[project] || [];
|
||||
acc[project].push({
|
||||
bump: plan.projectVersionBumps[project],
|
||||
fileName: plan.fileName,
|
||||
});
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Ensure each touched project under this release group features in at least one version plan file
|
||||
let touchedProjectsNotFoundInVersionPlans = [];
|
||||
for (const touchedProject of touchedProjectsUnderReleaseGroup) {
|
||||
if (!resolvedVersionPlans.length) {
|
||||
touchedProjectsNotFoundInVersionPlans.push(touchedProject);
|
||||
continue;
|
||||
}
|
||||
const matchingVersionPlanFileEntries =
|
||||
projectsInResolvedVersionPlans[touchedProject];
|
||||
if (!matchingVersionPlanFileEntries?.length) {
|
||||
touchedProjectsNotFoundInVersionPlans.push(touchedProject);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Log any resolved pending bumps, regardless of whether the projects were directly touched or not
|
||||
for (const [projectName, entries] of Object.entries(
|
||||
projectsInResolvedVersionPlans
|
||||
)) {
|
||||
output.log({
|
||||
title: `Project "${projectName}" has pending bumps in version plan(s)`,
|
||||
bodyLines: [
|
||||
...entries.map(
|
||||
({ bump, fileName }) => ` - "${bump}" in ${fileName}`
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (touchedProjectsNotFoundInVersionPlans.length) {
|
||||
const bodyLines = [
|
||||
`The following touched projects${
|
||||
releaseGroup.name !== IMPLICIT_DEFAULT_RELEASE_GROUP
|
||||
? ` under release group "${releaseGroup.name}"`
|
||||
: ''
|
||||
} do not feature in any version plan files:`,
|
||||
...touchedProjectsNotFoundInVersionPlans.map(
|
||||
(project) => ` - ${project}`
|
||||
),
|
||||
'',
|
||||
'Please use `nx release plan` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.',
|
||||
];
|
||||
if (!nxArgs.verbose) {
|
||||
bodyLines.push('', NOTE_ABOUT_VERBOSE_LOGGING);
|
||||
}
|
||||
output.error({
|
||||
title: 'Touched projects missing version plans',
|
||||
bodyLines,
|
||||
});
|
||||
// At least one project in one release group has an issue
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not print success message if any projects are missing version plans
|
||||
if (hasErrors) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const bodyLines = [];
|
||||
if (!nxArgs.verbose) {
|
||||
bodyLines.push(NOTE_ABOUT_VERBOSE_LOGGING);
|
||||
}
|
||||
output.success({
|
||||
title: 'All touched projects have, or do not require, version plans.',
|
||||
bodyLines,
|
||||
});
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
@ -44,7 +44,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
const { error: configError, nxReleaseConfig } = await createNxReleaseConfig(
|
||||
projectGraph,
|
||||
await createProjectFileMapUsingProjectGraph(projectGraph),
|
||||
nxJson.release
|
||||
userProvidedReleaseConfig
|
||||
);
|
||||
if (configError) {
|
||||
return await handleNxReleaseConfigError(configError);
|
||||
|
||||
@ -19,7 +19,7 @@ import { deepMergeJson } from './config/deep-merge-json';
|
||||
import { filterReleaseGroups } from './config/filter-release-groups';
|
||||
import {
|
||||
readRawVersionPlans,
|
||||
setVersionPlansOnGroups,
|
||||
setResolvedVersionPlansOnGroups,
|
||||
} from './config/version-plans';
|
||||
import { createAPI as createReleasePublishAPI } from './publish';
|
||||
import { getCommitHash, gitAdd, gitCommit, gitPush, gitTag } from './utils/git';
|
||||
@ -135,7 +135,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
process.exit(1);
|
||||
}
|
||||
const rawVersionPlans = await readRawVersionPlans();
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
Object.keys(projectGraph.nodes)
|
||||
@ -143,7 +143,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
|
||||
const planFiles = new Set<string>();
|
||||
releaseGroups.forEach((group) => {
|
||||
if (group.versionPlans) {
|
||||
if (group.resolvedVersionPlans) {
|
||||
if (group.name === IMPLICIT_DEFAULT_RELEASE_GROUP) {
|
||||
output.logSingleLine(`Removing version plan files`);
|
||||
} else {
|
||||
@ -151,7 +151,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
`Removing version plan files for group ${group.name}`
|
||||
);
|
||||
}
|
||||
group.versionPlans.forEach((plan) => {
|
||||
group.resolvedVersionPlans.forEach((plan) => {
|
||||
if (!args.dryRun) {
|
||||
removeSync(plan.absolutePath);
|
||||
if (args.verbose) {
|
||||
|
||||
@ -18,6 +18,7 @@ describe('shared', () => {
|
||||
changelog: false,
|
||||
releaseTagPattern: '{projectName}-{version}',
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
},
|
||||
{
|
||||
name: 'two',
|
||||
@ -31,6 +32,7 @@ describe('shared', () => {
|
||||
changelog: false,
|
||||
releaseTagPattern: '{projectName}-{version}',
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
},
|
||||
];
|
||||
const releaseGroupToFilteredProjects = new Map()
|
||||
@ -84,6 +86,7 @@ describe('shared', () => {
|
||||
changelog: false,
|
||||
releaseTagPattern: '{projectName}-{version}',
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
},
|
||||
{
|
||||
name: 'two',
|
||||
@ -97,6 +100,7 @@ describe('shared', () => {
|
||||
changelog: false,
|
||||
releaseTagPattern: '{projectName}-{version}',
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
},
|
||||
];
|
||||
const releaseGroupToFilteredProjects = new Map()
|
||||
@ -169,6 +173,7 @@ describe('shared', () => {
|
||||
releaseTagPattern: '{projectName}-{version}',
|
||||
name: '__default__',
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
},
|
||||
];
|
||||
|
||||
@ -259,6 +264,7 @@ describe('shared', () => {
|
||||
changelog: undefined,
|
||||
version: undefined,
|
||||
versionPlans: false,
|
||||
resolvedVersionPlans: false,
|
||||
};
|
||||
const releaseGroupToFilteredProjects = new Map().set(
|
||||
releaseGroup,
|
||||
|
||||
@ -37,7 +37,7 @@ import {
|
||||
} from './config/filter-release-groups';
|
||||
import {
|
||||
readRawVersionPlans,
|
||||
setVersionPlansOnGroups,
|
||||
setResolvedVersionPlansOnGroups,
|
||||
} from './config/version-plans';
|
||||
import { batchProjectsByGeneratorConfig } from './utils/batch-projects-by-generator-config';
|
||||
import { gitAdd, gitTag } from './utils/git';
|
||||
@ -190,7 +190,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
process.exit(1);
|
||||
}
|
||||
const rawVersionPlans = await readRawVersionPlans();
|
||||
setVersionPlansOnGroups(
|
||||
setResolvedVersionPlansOnGroups(
|
||||
rawVersionPlans,
|
||||
releaseGroups,
|
||||
Object.keys(projectGraph.nodes)
|
||||
|
||||
@ -174,6 +174,14 @@ export interface NxReleaseConventionalCommitsConfiguration {
|
||||
>;
|
||||
}
|
||||
|
||||
export interface NxReleaseVersionPlansConfiguration {
|
||||
/**
|
||||
* Changes to files matching any of these optional patterns will be excluded from the affected project logic within the `nx release plan:check`
|
||||
* command. This is useful for ignoring files that are not relevant to the versioning process, such as documentation or configuration files.
|
||||
*/
|
||||
ignorePatternsForPlanCheck?: string[];
|
||||
}
|
||||
|
||||
export interface NxReleaseConfiguration {
|
||||
/**
|
||||
* Shorthand for amending the projects which will be included in the implicit default release group (all projects by default).
|
||||
@ -223,7 +231,7 @@ export interface NxReleaseConfiguration {
|
||||
* Enables using version plans as a specifier source for versioning and
|
||||
* to determine changes for changelog generation.
|
||||
*/
|
||||
versionPlans?: boolean;
|
||||
versionPlans?: NxReleaseVersionPlansConfiguration | boolean;
|
||||
}
|
||||
>;
|
||||
/**
|
||||
@ -295,7 +303,7 @@ export interface NxReleaseConfiguration {
|
||||
* Enables using version plans as a specifier source for versioning and
|
||||
* to determine changes for changelog generation.
|
||||
*/
|
||||
versionPlans?: boolean;
|
||||
versionPlans?: NxReleaseVersionPlansConfiguration | boolean;
|
||||
}
|
||||
|
||||
export interface NxSyncConfiguration {
|
||||
|
||||
@ -120,7 +120,7 @@ export function splitArgsIntoNxArgsAndOverrides(
|
||||
});
|
||||
}
|
||||
|
||||
// Allow setting base and head via environment variables (lower priority then direct command arguments)
|
||||
// Allow setting base and head via environment variables (lower priority than direct command arguments)
|
||||
if (!nxArgs.base && process.env.NX_BASE) {
|
||||
nxArgs.base = process.env.NX_BASE;
|
||||
if (options.printWarnings) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user