Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(release): skip lock file update if workspaces are not enabled #22055

Merged
merged 7 commits into from Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/generated/devkit/README.md
Expand Up @@ -122,6 +122,7 @@ It only uses language primitives and immutable objects
- [glob](../../devkit/documents/glob)
- [hashArray](../../devkit/documents/hashArray)
- [installPackagesTask](../../devkit/documents/installPackagesTask)
- [isWorkspacesEnabled](../../devkit/documents/isWorkspacesEnabled)
- [joinPathFragments](../../devkit/documents/joinPathFragments)
- [moveFilesToNewDirectory](../../devkit/documents/moveFilesToNewDirectory)
- [names](../../devkit/documents/names)
Expand Down
16 changes: 16 additions & 0 deletions docs/generated/devkit/isWorkspacesEnabled.md
@@ -0,0 +1,16 @@
# Function: isWorkspacesEnabled

▸ **isWorkspacesEnabled**(`packageManager?`, `root?`): `boolean`

Returns true if the workspace is using npm workspaces, yarn workspaces, or pnpm workspaces.

#### Parameters

| Name | Type | Default value | Description |
| :--------------- | :-------------------------------------------------------- | :-------------- | :------------------------------------------------------------------------------------------ |
| `packageManager` | [`PackageManager`](../../devkit/documents/PackageManager) | `undefined` | The package manager to use. If not provided, it will be detected based on the lock file. |
| `root` | `string` | `workspaceRoot` | The directory the commands will be ran inside of. Defaults to the current workspace's root. |

#### Returns

`boolean`
1 change: 1 addition & 0 deletions docs/generated/packages/devkit/documents/nx_devkit.md
Expand Up @@ -122,6 +122,7 @@ It only uses language primitives and immutable objects
- [glob](../../devkit/documents/glob)
- [hashArray](../../devkit/documents/hashArray)
- [installPackagesTask](../../devkit/documents/installPackagesTask)
- [isWorkspacesEnabled](../../devkit/documents/isWorkspacesEnabled)
- [joinPathFragments](../../devkit/documents/joinPathFragments)
- [moveFilesToNewDirectory](../../devkit/documents/moveFilesToNewDirectory)
- [names](../../devkit/documents/names)
Expand Down
28 changes: 5 additions & 23 deletions e2e/release/src/independent-projects.test.ts
Expand Up @@ -139,9 +139,6 @@ describe('nx release - independent projects', () => {
"scripts": {


NX Updating {package-manager} lock file


NX Staging changed files with git


Expand Down Expand Up @@ -174,9 +171,6 @@ describe('nx release - independent projects', () => {
+


NX Updating {package-manager} lock file


NX Staging changed files with git


Expand Down Expand Up @@ -213,9 +207,6 @@ describe('nx release - independent projects', () => {
}


NX Updating {package-manager} lock file


NX Staging changed files with git


Expand Down Expand Up @@ -255,15 +246,12 @@ describe('nx release - independent projects', () => {
"scripts": {


NX Updating {package-manager} lock file

Updating {lock-file} with the following command:
{lock-file-command}
Skipped lock file update because {package-manager} workspaces are not enabled.

NX Committing changes with git

Staging files in git with the following command:
git add {project-name}/package.json {lock-file}
git add {project-name}/package.json

Committing files in git with the following command:
git commit --message chore(release): publish --message - project: {project-name} 999.9.9-version-git-operations-test.2
Expand Down Expand Up @@ -363,20 +351,14 @@ describe('nx release - independent projects', () => {
"scripts": {


NX Updating {package-manager} lock file

Updating {lock-file} with the following command:
{lock-file-command}

NX Updating {package-manager} lock file
Skipped lock file update because {package-manager} workspaces are not enabled.

Updating {lock-file} with the following command:
{lock-file-command}
Skipped lock file update because {package-manager} workspaces are not enabled.

NX Committing changes with git

Staging files in git with the following command:
git add {project-name}/package.json {project-name}/package.json {project-name}/package.json {lock-file}
git add {project-name}/package.json {project-name}/package.json {project-name}/package.json

Committing files in git with the following command:
git commit --message chore(release): publish --message - project: {project-name} 999.9.9-version-git-operations-test.3 --message - project: {project-name} 999.9.9-version-git-operations-test.3 --message - release-group: fixed 999.9.9-version-git-operations-test.3
Expand Down
39 changes: 39 additions & 0 deletions e2e/release/src/lock-file-updates.test.ts
Expand Up @@ -93,6 +93,11 @@ describe('nx release lock file updates', () => {
it('should update package-lock.json when package manager is npm', async () => {
initializeProject('npm');

updateJson('package.json', (json) => {
json.workspaces = [pkg1, pkg2, pkg3];
return json;
});

runCommand(`npm install`);

// workaround for NXC-143
Expand All @@ -116,6 +121,40 @@ describe('nx release lock file updates', () => {
`);
});

it('should not update package-lock.json when package manager is npm and workspaces are not enabled', async () => {
initializeProject('npm');

updateJson('package.json', (json) => {
delete json.workspaces;
return json;
});

runCommand(`npm install`);

// workaround for NXC-143
runCLI('reset');

runCommand(`git add .`);
runCommand(`git commit -m "chore: initial commit"`);

const versionOutput = runCLI(`release version 999.9.9 --verbose`);

expect(
versionOutput.match(
/Skipped lock file update because npm workspaces are not enabled./g
).length
).toBe(1);

const filesChanges = runCommand('git diff --name-only HEAD');

expect(filesChanges).toMatchInlineSnapshot(`
{project-name}/package.json
{project-name}/package.json
{project-name}/package.json

`);
});

it('should not update lock file when package manager is yarn classic', async () => {
initializeProject('yarn');

Expand Down
Expand Up @@ -2,6 +2,7 @@ import {
detectPackageManager,
getPackageManagerCommand,
getPackageManagerVersion,
isWorkspacesEnabled,
output,
} from '@nx/devkit';
import { execSync } from 'child_process';
Expand Down Expand Up @@ -46,6 +47,16 @@ export async function updateLockFile(
return [];
}

const workspacesEnabled = isWorkspacesEnabled(packageManager, cwd);
if (!workspacesEnabled) {
if (verbose) {
console.log(
`\nSkipped lock file update because ${packageManager} workspaces are not enabled.`
);
}
return [];
}

const isDaemonEnabled = daemonClient.enabled();
if (!dryRun && isDaemonEnabled) {
// if not in dry-run temporarily stop the daemon, as it will error if the lock file is updated
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/devkit-exports.ts
Expand Up @@ -100,6 +100,7 @@ export {
getPackageManagerCommand,
detectPackageManager,
getPackageManagerVersion,
isWorkspacesEnabled,
} from './utils/package-manager';

/**
Expand Down
42 changes: 42 additions & 0 deletions packages/nx/src/utils/package-manager.spec.ts
@@ -1,7 +1,9 @@
import * as fs from 'fs';
import * as configModule from '../config/configuration';
import * as projectGraphFileUtils from '../project-graph/file-utils';
import {
detectPackageManager,
isWorkspacesEnabled,
modifyYarnRcToFitNewDirectory,
modifyYarnRcYmlToFitNewDirectory,
} from './package-manager';
Expand Down Expand Up @@ -76,6 +78,46 @@ describe('package-manager', () => {
});
});

describe('isWorkspacesEnabled', () => {
it('should return true if package manager is pnpm and pnpm-workspace.yaml exists', () => {
jest.spyOn(fs, 'existsSync').mockReturnValueOnce(true);
expect(isWorkspacesEnabled('pnpm')).toEqual(true);
});

it('should return false if package manager is pnpm and pnpm-workspace.yaml does not exist', () => {
jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false);
expect(isWorkspacesEnabled('pnpm')).toEqual(false);
});

it('should return true if package manager is yarn and workspaces exists in package.json', () => {
jest
.spyOn(projectGraphFileUtils, 'readPackageJson')
.mockReturnValueOnce({ workspaces: ['packages/*'] });
expect(isWorkspacesEnabled('yarn')).toEqual(true);
});

it('should return false if package manager is yarn and workspaces does not exist in package.json', () => {
jest
.spyOn(projectGraphFileUtils, 'readPackageJson')
.mockReturnValueOnce({});
expect(isWorkspacesEnabled('yarn')).toEqual(false);
});

it('should return true if package manager is npm and workspaces exists in package.json', () => {
jest
.spyOn(projectGraphFileUtils, 'readPackageJson')
.mockReturnValueOnce({ workspaces: ['packages/*'] });
expect(isWorkspacesEnabled('npm')).toEqual(true);
});

it('should return false if package manager is npm and workspaces does not exist in package.json', () => {
jest
.spyOn(projectGraphFileUtils, 'readPackageJson')
.mockReturnValueOnce({});
expect(isWorkspacesEnabled('npm')).toEqual(false);
});
});

describe('modifyYarnRcYmlToFitNewDirectory', () => {
it('should update paths properly', () => {
expect(
Expand Down
21 changes: 20 additions & 1 deletion packages/nx/src/utils/package-manager.ts
Expand Up @@ -6,8 +6,9 @@ import { gte, lt } from 'semver';
import { dirSync } from 'tmp';
import { promisify } from 'util';
import { readNxJson } from '../config/configuration';
import { readPackageJson } from '../project-graph/file-utils';
import { readFileIfExisting, writeJsonFile } from './fileutils';
import { readModulePackageJson } from './package-json';
import { PackageJson, readModulePackageJson } from './package-json';
import { workspaceRoot } from './workspace-root';

const execAsync = promisify(exec);
Expand Down Expand Up @@ -43,6 +44,24 @@ export function detectPackageManager(dir: string = ''): PackageManager {
);
}

/**
* Returns true if the workspace is using npm workspaces, yarn workspaces, or pnpm workspaces.
* @param packageManager The package manager to use. If not provided, it will be detected based on the lock file.
* @param root The directory the commands will be ran inside of. Defaults to the current workspace's root.
*/
export function isWorkspacesEnabled(
packageManager: PackageManager = detectPackageManager(),
root: string = workspaceRoot
): boolean {
if (packageManager === 'pnpm') {
return existsSync(join(root, 'pnpm-workspace.yaml'));
}

// yarn and pnpm both use the same 'workspaces' property in package.json
const packageJson: PackageJson = readPackageJson();
return !!packageJson?.workspaces;
}

/**
* Returns commands for the package manager used in the workspace.
* By default, the package manager is derived based on the lock file,
Expand Down