Skip to content

Commit

Permalink
fix(release): store rawVersionSpec on versionData (#22071)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHenry committed Mar 4, 2024
1 parent 1961bb5 commit 6957937
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let originalExit = process.exit;
const originalExit = process.exit;
let stubProcessExit = false;

const processExitSpy = jest
Expand All @@ -12,10 +12,10 @@ const processExitSpy = jest

import { ProjectGraph, Tree, output, readJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import * as enquirer from 'enquirer';
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';
import * as enquirer from 'enquirer';

jest.mock('enquirer');

Expand All @@ -27,7 +27,7 @@ describe('release-version', () => {
let projectGraph: ProjectGraph;

beforeEach(() => {
// @ts-ignore
// @ts-expect-error read-only property
process.exit = processExitSpy;

tree = createTreeWithEmptyWorkspace();
Expand Down Expand Up @@ -95,12 +95,14 @@ describe('release-version', () => {
"dependentProjects": [
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "0.0.1",
"source": "project-with-dependency-on-my-pkg",
"target": "my-lib",
"type": "static",
},
{
"dependencyCollection": "devDependencies",
"rawVersionSpec": "0.0.1",
"source": "project-with-devDependency-on-my-pkg",
"target": "my-lib",
"type": "static",
Expand Down Expand Up @@ -314,7 +316,7 @@ To fix this you will either need to add a package.json file at that location, or
describe('independent release group', () => {
describe('specifierSource: prompt', () => {
it(`should appropriately prompt for each project independently and apply the version updates across all package.json files`, async () => {
// @ts-ignore
// @ts-expect-error read-only property
enquirer.prompt = jest
.fn()
// First project will be minor
Expand Down Expand Up @@ -920,13 +922,13 @@ To fix this you will either need to add a package.json file at that location, or
specifier: 'major',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup('fixed'),
versionPrefix: '$',
versionPrefix: '$' as any,
});

expect(outputSpy).toHaveBeenCalledWith({
title: `Invalid value for version.generatorOptions.versionPrefix: "$"
Valid values are: "auto", "", "~", "^"`,
Valid values are: "auto", "", "~", "^", "="`,
});

outputSpy.mockRestore();
Expand Down
86 changes: 56 additions & 30 deletions packages/js/src/generators/release-version/release-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
writeJson,
} from '@nx/devkit';
import * as chalk from 'chalk';
import { exec } from 'child_process';
import { exec } from 'node:child_process';
import { relative } from 'node:path';
import { IMPLICIT_DEFAULT_RELEASE_GROUP } from 'nx/src/command-line/release/config/config';
import {
getFirstGitCommit,
Expand All @@ -27,10 +28,8 @@ import {
deriveNewSemverVersion,
validReleaseVersionPrefixes,
} from 'nx/src/command-line/release/version';

import { interpolate } from 'nx/src/tasks-runner/utils';
import * as ora from 'ora';
import { relative } from 'path';
import { prerelease } from 'semver';
import { ReleaseVersionGeneratorSchema } from './schema';
import { resolveLocalPackageDependencies } from './utils/resolve-local-package-dependencies';
Expand Down Expand Up @@ -76,20 +75,6 @@ Valid values are: ${validReleaseVersionPrefixes

const projects = options.projects;

const createResolvePackageRoot =
(customPackageRoot?: string) =>
(projectNode: ProjectGraphProjectNode): string => {
// Default to the project root if no custom packageRoot
if (!customPackageRoot) {
return projectNode.data.root;
}
return interpolate(customPackageRoot, {
workspaceRoot: '',
projectRoot: projectNode.data.root,
projectName: projectNode.name,
});
};

const resolvePackageRoot = createResolvePackageRoot(options.packageRoot);

// Resolve any custom package roots for each project upfront as they will need to be reused during dependency resolution
Expand All @@ -101,20 +86,31 @@ Valid values are: ${validReleaseVersionPrefixes
);
}

let currentVersion: string;
let currentVersionResolvedFromFallback: boolean = false;
let currentVersion: string | undefined = undefined;
let currentVersionResolvedFromFallback = false;

// only used for options.currentVersionResolver === 'git-tag', but
// must be declared here in order to reuse it for additional projects
let latestMatchingGitTag: { tag: string; extractedVersion: string };
let latestMatchingGitTag:
| { tag: string; extractedVersion: string }
| null
| undefined = undefined;

// if specifier is undefined, then we haven't resolved it yet
// if specifier is null, then it has been resolved and no changes are necessary
let specifier = options.specifier ? options.specifier : undefined;
let specifier: string | null | undefined = options.specifier
? options.specifier
: undefined;

for (const project of projects) {
const projectName = project.name;
const packageRoot = projectNameToPackageRootMap.get(projectName);
if (!packageRoot) {
throw new Error(
`The project "${projectName}" does not have a packageRoot available. Please report this issue on https://github.com/nrwl/nx`
);
}

const packageJsonPath = joinPathFragments(packageRoot, 'package.json');
const workspaceRelativePackageJsonPath = relative(
workspaceRoot,
Expand Down Expand Up @@ -268,7 +264,10 @@ To fix this you will either need to add a package.json file at that location, or
);
} else {
log(
`📄 Using the current version ${currentVersion} already resolved from git tag "${latestMatchingGitTag.tag}".`
// In this code path we know that latestMatchingGitTag is defined, because we are not relying on the fallbackCurrentVersionResolver, so we can safely use the non-null assertion operator
`📄 Using the current version ${currentVersion} already resolved from git tag "${
latestMatchingGitTag!.tag
}".`
);
}
}
Expand Down Expand Up @@ -301,7 +300,7 @@ To fix this you will either need to add a package.json file at that location, or
) {
const specifierSource = options.specifierSource;
switch (specifierSource) {
case 'conventional-commits':
case 'conventional-commits': {
if (options.currentVersionResolver !== 'git-tag') {
throw new Error(
`Invalid currentVersionResolver "${options.currentVersionResolver}" provided for release group "${options.releaseGroup.name}". Must be "git-tag" when "specifierSource" is "conventional-commits"`
Expand Down Expand Up @@ -347,7 +346,7 @@ To fix this you will either need to add a package.json file at that location, or
//
// Always assume that if the current version is a prerelease, then the next version should be a prerelease.
// Users must manually graduate from a prerelease to a release by providing an explicit specifier.
if (prerelease(currentVersion)) {
if (prerelease(currentVersion ?? '')) {
specifier = 'prerelease';
log(
`📄 Resolved the specifier as "${specifier}" since the current version is a prerelease.`
Expand All @@ -358,6 +357,7 @@ To fix this you will either need to add a package.json file at that location, or
);
}
break;
}
case 'prompt': {
// Only add the release group name to the log if it is one set by the user, otherwise it is useless noise
const maybeLogReleaseGroup = (log: string): string => {
Expand Down Expand Up @@ -413,9 +413,16 @@ To fix this you will either need to add a package.json file at that location, or
return localPackageDependency.target === project.name;
});

if (!currentVersion) {
throw new Error(
`The current version for project "${project.name}" could not be resolved. Please report this on https://github.com/nrwl/nx`
);
}

versionData[projectName] = {
currentVersion,
dependentProjects,
// @ts-ignore: The types will be updated in a future version of Nx
newVersion: null, // will stay as null in the final result in the case that no changes are detected
};

Expand Down Expand Up @@ -455,12 +462,17 @@ To fix this you will either need to add a package.json file at that location, or
}

for (const dependentProject of dependentProjects) {
const dependentPackageRoot = projectNameToPackageRootMap.get(
dependentProject.source
);
if (!dependentPackageRoot) {
throw new Error(
`The dependent project "${dependentProject.source}" does not have a packageRoot available. Please report this issue on https://github.com/nrwl/nx`
);
}
updateJson(
tree,
joinPathFragments(
projectNameToPackageRootMap.get(dependentProject.source),
'package.json'
),
joinPathFragments(dependentPackageRoot, 'package.json'),
(json) => {
// Auto (i.e.infer existing) by default
let versionPrefix = options.versionPrefix ?? 'auto';
Expand Down Expand Up @@ -490,7 +502,7 @@ To fix this you will either need to add a package.json file at that location, or
}

/**
* Ensure that formatting is applied so that version bump diffs are as mimimal as possible
* Ensure that formatting is applied so that version bump diffs are as minimal as possible
* within the context of the user's workspace.
*/
await formatFiles(tree);
Expand All @@ -504,7 +516,7 @@ To fix this you will either need to add a package.json file at that location, or
return updatedFiles;
},
};
} catch (e) {
} catch (e: any) {
if (process.env.NX_VERBOSE_LOGGING === 'true') {
output.error({
title: e.message,
Expand All @@ -522,6 +534,20 @@ To fix this you will either need to add a package.json file at that location, or

export default releaseVersionGenerator;

function createResolvePackageRoot(customPackageRoot?: string) {
return (projectNode: ProjectGraphProjectNode): string => {
// Default to the project root if no custom packageRoot
if (!customPackageRoot) {
return projectNode.data.root;
}
return interpolate(customPackageRoot, {
workspaceRoot: '',
projectRoot: projectNode.data.root,
projectName: projectNode.name,
});
};
}

const colors = [
{ instance: chalk.green, spinnerColor: 'green' },
{ instance: chalk.greenBright, spinnerColor: 'green' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,21 @@ describe('resolveLocalPackageDependencies()', () => {
"projectA": [
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "1.0.0",
"source": "projectA",
"target": "projectB",
"type": "static",
},
{
"dependencyCollection": "devDependencies",
"rawVersionSpec": "1.0.0",
"source": "projectA",
"target": "projectC",
"type": "static",
},
{
"dependencyCollection": "optionalDependencies",
"rawVersionSpec": "1.0.0",
"source": "projectA",
"target": "projectD",
"type": "static",
Expand Down Expand Up @@ -220,24 +223,28 @@ describe('resolveLocalPackageDependencies()', () => {
"projectA": [
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "file:../projectB",
"source": "projectA",
"target": "projectB",
"type": "static",
},
{
"dependencyCollection": "devDependencies",
"rawVersionSpec": "workspace:*",
"source": "projectA",
"target": "projectC",
"type": "static",
},
{
"dependencyCollection": "optionalDependencies",
"rawVersionSpec": "workspace:../projectD",
"source": "projectA",
"target": "projectD",
"type": "static",
},
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "link:../projectE",
"source": "projectA",
"target": "projectE",
"type": "static",
Expand All @@ -246,6 +253,7 @@ describe('resolveLocalPackageDependencies()', () => {
"projectB": [
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "workspace:1.0.0",
"source": "projectB",
"target": "projectC",
"type": "static",
Expand Down Expand Up @@ -305,6 +313,7 @@ describe('resolveLocalPackageDependencies()', () => {
"projectA": [
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "1.0.0",
"source": "projectA",
"target": "projectB",
"type": "static",
Expand Down Expand Up @@ -394,18 +403,21 @@ describe('resolveLocalPackageDependencies()', () => {
"projectA": [
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "1.0.0",
"source": "projectA",
"target": "projectB",
"type": "static",
},
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "1.0.0",
"source": "projectA",
"target": "projectC",
"type": "static",
},
{
"dependencyCollection": "dependencies",
"rawVersionSpec": "file:../../../packages/projectD",
"source": "projectA",
"target": "projectD",
"type": "static",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { Package } from './package';
import { resolveVersionSpec } from './resolve-version-spec';

interface LocalPackageDependency extends ProjectGraphDependency {
/**
* The rawVersionSpec contains the value of the version spec as it was defined in the package.json
* of the dependent project. This can be useful in cases where the version spec is a range, path or
* workspace reference, and it needs to be be reverted to that original value as part of the release.
*/
rawVersionSpec: string;
dependencyCollection:
| 'dependencies'
| 'devDependencies'
Expand Down Expand Up @@ -104,6 +110,7 @@ export function resolveLocalPackageDependencies(
{
...dep,
dependencyCollection: sourceNpmDependency.collection,
rawVersionSpec: sourceNpmDependency.spec,
},
];
}
Expand Down
6 changes: 5 additions & 1 deletion packages/nx/src/command-line/release/utils/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ export type ReleaseVersionGeneratorResult = {
export type VersionData = Record<
string,
{
newVersion: string;
/**
* newVersion will be null in the case that no changes are detected for the project,
* e.g. when using conventional commits
*/
newVersion: string | null;
currentVersion: string;
dependentProjects: any[]; // TODO: investigate generic type for this once more ecosystems are explored
}
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/release/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export type {
VersionData,
} from './utils/shared';

export const validReleaseVersionPrefixes = ['auto', '', '~', '^'];
export const validReleaseVersionPrefixes = ['auto', '', '~', '^', '='] as const;

export interface ReleaseVersionGeneratorSchema {
// The projects being versioned in the current execution
Expand Down

0 comments on commit 6957937

Please sign in to comment.