Skip to content

Commit

Permalink
feat(release): add 'git-tag' currentVersionResolver and conventional …
Browse files Browse the repository at this point in the history
…commits support (#19267)

Co-authored-by: James Henry <[email protected]>
  • Loading branch information
fahslaj and JamesHenry authored Oct 26, 2023
1 parent a7e0abd commit dbb73aa
Show file tree
Hide file tree
Showing 19 changed files with 1,216 additions and 365 deletions.
16 changes: 13 additions & 3 deletions docs/generated/packages/js/generators/release-version.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@
},
"specifier": {
"type": "string",
"description": "Exact version or semver keyword to apply to the selected release group. NOTE: This should be set on the release group level, not the project level."
"description": "Exact version or semver keyword to apply to the selected release group. Overrides specifierSource."
},
"releaseGroup": {
"type": "object",
"description": "The resolved release group configuration, including name, relevant to all projects in the current execution."
},
"specifierSource": {
"type": "string",
"default": "prompt",
"description": "Which approach to use to determine the semver specifier used to bump the version of the project.",
"enum": ["prompt", "conventional-commits"]
},
"preid": {
"type": "string",
Expand All @@ -34,15 +44,15 @@
"type": "string",
"default": "disk",
"description": "Which approach to use to determine the current version of the project.",
"enum": ["registry", "disk"]
"enum": ["registry", "disk", "git-tag"]
},
"currentVersionResolverMetadata": {
"type": "object",
"description": "Additional metadata to pass to the current version resolver.",
"default": {}
}
},
"required": ["projects", "projectGraph", "specifier"],
"required": ["projects", "projectGraph", "releaseGroup"],
"presets": []
},
"description": "DO NOT INVOKE DIRECTLY WITH `nx generate`. Use `nx release version` instead.",
Expand Down
138 changes: 138 additions & 0 deletions e2e/release/src/release.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,5 +639,143 @@ describe('nx release', () => {

// port and process cleanup
await killProcessAndPorts(process.pid, verdaccioPort);

// Add custom nx release config to control version resolution
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release = {
groups: {
default: {
// @proj/source will be added as a project by the verdaccio setup, but we aren't versioning or publishing it, so we exclude it here
projects: ['*', '!@proj/source'],
releaseTagPattern: 'xx{version}',
version: {
generator: '@nx/js:release-version',
generatorOptions: {
// Resolve the latest version from the git tag
currentVersionResolver: 'git-tag',
},
},
},
},
};
return nxJson;
});

// Add a git tag to the repo
await runCommandAsync(`git tag xx1100.0.0`);

const versionOutput3 = runCLI(`release version minor`);
expect(
versionOutput3.match(/Running release version for project: my-pkg-\d*/g)
.length
).toEqual(3);
expect(
versionOutput3.match(
/Reading data for package "@proj\/my-pkg-\d*" from my-pkg-\d*\/package.json/g
).length
).toEqual(3);

// It should resolve the current version from the git tag once...
expect(
versionOutput3.match(
new RegExp(
`Resolved the current version as 1100.0.0 from git tag "xx1100.0.0"`,
'g'
)
).length
).toEqual(1);
// ...and then reuse it twice
expect(
versionOutput3.match(
new RegExp(
`Using the current version 1100.0.0 already resolved from git tag "xx1100.0.0"`,
'g'
)
).length
).toEqual(2);

expect(
versionOutput3.match(
/New version 1100.1.0 written to my-pkg-\d*\/package.json/g
).length
).toEqual(3);

// Only one dependency relationship exists, so this log should only match once
expect(
versionOutput3.match(
/Applying new version 1100.1.0 to 1 package which depends on my-pkg-\d*/g
).length
).toEqual(1);

createFile(
`${pkg1}/my-file.txt`,
'update for conventional-commits testing'
);

// Add custom nx release config to control version resolution
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release = {
groups: {
default: {
// @proj/source will be added as a project by the verdaccio setup, but we aren't versioning or publishing it, so we exclude it here
projects: ['*', '!@proj/source'],
releaseTagPattern: 'xx{version}',
version: {
generator: '@nx/js:release-version',
generatorOptions: {
specifierSource: 'conventional-commits',
currentVersionResolver: 'git-tag',
},
},
},
},
};
return nxJson;
});

const versionOutput4 = runCLI(`release version`);

expect(
versionOutput4.match(/Running release version for project: my-pkg-\d*/g)
.length
).toEqual(3);
expect(
versionOutput4.match(
/Reading data for package "@proj\/my-pkg-\d*" from my-pkg-\d*\/package.json/g
).length
).toEqual(3);

// It should resolve the current version from the git tag once...
expect(
versionOutput4.match(
new RegExp(
`Resolved the current version as 1100.0.0 from git tag "xx1100.0.0"`,
'g'
)
).length
).toEqual(1);
// ...and then reuse it twice
expect(
versionOutput4.match(
new RegExp(
`Using the current version 1100.0.0 already resolved from git tag "xx1100.0.0"`,
'g'
)
).length
).toEqual(2);

expect(versionOutput4.match(/Skipping versioning/g).length).toEqual(3);

await runCommandAsync(
`git add ${pkg1}/my-file.txt && git commit -m "feat!: add new file"`
);

const versionOutput5 = runCLI(`release version`);

expect(
versionOutput5.match(
/New version 1101.0.0 written to my-pkg-\d*\/package.json/g
).length
).toEqual(3);
}, 500000);
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export default async function runExecutor(

const packageJsonPath = joinPathFragments(packageRoot, 'package.json');
const projectPackageJson = readJsonFile(packageJsonPath);
const name = projectPackageJson.name;
const packageName = projectPackageJson.name;

// If package and project name match, we can make log messages terser
let packageTxt =
name === context.projectName
? `package "${name}"`
: `package "${name}" from project "${context.projectName}"`;
packageName === context.projectName
? `package "${packageName}"`
: `package "${packageName}" from project "${context.projectName}"`;

if (projectPackageJson.private === true) {
console.warn(
Expand Down Expand Up @@ -80,7 +80,7 @@ export default async function runExecutor(
const stdoutData = JSON.parse(output.toString());

// If npm workspaces are in use, the publish output will nest the data under the package name, so we normalize it first
const normalizedStdoutData = stdoutData[context.projectName!] ?? stdoutData;
const normalizedStdoutData = stdoutData[packageName] ?? stdoutData;
logTar(normalizedStdoutData);

if (options.dryRun) {
Expand Down
57 changes: 43 additions & 14 deletions packages/js/src/generators/release-version/release-version.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ProjectGraph, Tree, readJson } from '@nx/devkit';
import { ProjectGraph, Tree, output, readJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { ReleaseGroupWithName } from 'nx/src/command-line/release/config/filter-release-groups';
import { releaseVersionGenerator } from './release-version';
import { createWorkspaceWithPackageDependencies } from './test-utils/create-workspace-with-package-dependencies';

Expand Down Expand Up @@ -58,6 +59,7 @@ describe('release-version', () => {
projectGraph,
specifier: 'major',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});
expect(readJson(tree, 'libs/my-lib/package.json').version).toEqual('1.0.0');

Expand All @@ -66,6 +68,7 @@ describe('release-version', () => {
projectGraph,
specifier: 'minor',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});
expect(readJson(tree, 'libs/my-lib/package.json').version).toEqual('1.1.0');

Expand All @@ -74,6 +77,7 @@ describe('release-version', () => {
projectGraph,
specifier: 'patch',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});
expect(readJson(tree, 'libs/my-lib/package.json').version).toEqual('1.1.1');

Expand All @@ -82,6 +86,7 @@ describe('release-version', () => {
projectGraph,
specifier: '1.2.3', // exact version
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});
expect(readJson(tree, 'libs/my-lib/package.json').version).toEqual('1.2.3');
});
Expand All @@ -92,6 +97,7 @@ describe('release-version', () => {
projectGraph,
specifier: 'major',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});

expect(readJson(tree, 'libs/my-lib/package.json')).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -130,19 +136,42 @@ describe('release-version', () => {
tree.delete('libs/my-lib/package.json');
});

it(`should error with guidance when not all of the given projects are appropriate for JS versioning`, async () => {
await expect(
releaseVersionGenerator(tree, {
projects: Object.values(projectGraph.nodes), // version all projects
projectGraph,
specifier: 'major',
currentVersionResolver: 'disk',
})
).rejects.toThrowErrorMatchingInlineSnapshot(`
"The project "my-lib" does not have a package.json available at libs/my-lib/package.json.
To fix this you will either need to add a package.json file at that location, or configure "release" within your nx.json to exclude "my-lib" from the current release group, or amend the packageRoot configuration to point to where the package.json should be."
`);
it(`should exit with code one and print guidance when not all of the given projects are appropriate for JS versioning`, async () => {
const processSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
return undefined as never;
});
const outputSpy = jest.spyOn(output, 'error').mockImplementation(() => {
return undefined as never;
});

await releaseVersionGenerator(tree, {
projects: Object.values(projectGraph.nodes), // version all projects
projectGraph,
specifier: 'major',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});

expect(outputSpy).toHaveBeenCalledWith({
title: `The project "my-lib" does not have a package.json available at libs/my-lib/package.json.
To fix this you will either need to add a package.json file at that location, or configure "release" within your nx.json to exclude "my-lib" from the current release group, or amend the packageRoot configuration to point to where the package.json should be.`,
});

expect(processSpy).toHaveBeenCalledWith(1);

processSpy.mockRestore();
outputSpy.mockRestore();
});
});
});

function createReleaseGroup(
partialGroup: Partial<ReleaseGroupWithName> = {}
): ReleaseGroupWithName {
return {
name: 'default',
releaseTagPattern: '{projectName}@v{version}',
...partialGroup,
} as ReleaseGroupWithName;
}
Loading

1 comment on commit dbb73aa

@vercel
Copy link

@vercel vercel bot commented on dbb73aa Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx.dev

Please sign in to comment.