feat(release): update lockfile after version command (#21107)
This commit is contained in:
parent
6164481d57
commit
096cefb109
@ -2,6 +2,7 @@ import { joinPathFragments } from '@nx/devkit';
|
|||||||
import {
|
import {
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
exists,
|
exists,
|
||||||
|
getSelectedPackageManager,
|
||||||
newProject,
|
newProject,
|
||||||
readFile,
|
readFile,
|
||||||
runCLI,
|
runCLI,
|
||||||
@ -34,6 +35,16 @@ expect.addSnapshotSerializer({
|
|||||||
.replaceAll(/\d*\.\d*\s?kB/g, 'XXX.XXX kb')
|
.replaceAll(/\d*\.\d*\s?kB/g, 'XXX.XXX kb')
|
||||||
// Normalize the version title date
|
// Normalize the version title date
|
||||||
.replaceAll(/\(\d{4}-\d{2}-\d{2}\)/g, '(YYYY-MM-DD)')
|
.replaceAll(/\(\d{4}-\d{2}-\d{2}\)/g, '(YYYY-MM-DD)')
|
||||||
|
.replaceAll('package-lock.json', '{lock-file}')
|
||||||
|
.replaceAll('yarn.lock', '{lock-file}')
|
||||||
|
.replaceAll('pnpm-lock.yaml', '{lock-file}')
|
||||||
|
.replaceAll('npm install --package-lock-only', '{lock-file-command}')
|
||||||
|
.replaceAll(
|
||||||
|
'yarn install --mode update-lockfile',
|
||||||
|
'{lock-file-command}'
|
||||||
|
)
|
||||||
|
.replaceAll('pnpm install --lockfile-only', '{lock-file-command}')
|
||||||
|
.replaceAll(getSelectedPackageManager(), '{package-manager}')
|
||||||
// We trim each line to reduce the chances of snapshot flakiness
|
// We trim each line to reduce the chances of snapshot flakiness
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((r) => r.trim())
|
.map((r) => r.trim())
|
||||||
@ -127,6 +138,9 @@ describe('nx release - independent projects', () => {
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
||||||
|
|
||||||
|
> NX Updating {package-manager} lock file
|
||||||
|
|
||||||
|
|
||||||
> NX Staging changed files with git
|
> NX Staging changed files with git
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +173,9 @@ describe('nx release - independent projects', () => {
|
|||||||
+
|
+
|
||||||
|
|
||||||
|
|
||||||
|
> NX Updating {package-manager} lock file
|
||||||
|
|
||||||
|
|
||||||
> NX Staging changed files with git
|
> NX Staging changed files with git
|
||||||
|
|
||||||
|
|
||||||
@ -198,6 +215,9 @@ describe('nx release - independent projects', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
> NX Updating {package-manager} lock file
|
||||||
|
|
||||||
|
|
||||||
> NX Staging changed files with git
|
> NX Staging changed files with git
|
||||||
|
|
||||||
|
|
||||||
@ -237,10 +257,15 @@ describe('nx release - independent projects', () => {
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
||||||
|
|
||||||
|
> NX Updating {package-manager} lock file
|
||||||
|
|
||||||
|
Updating {lock-file} with the following command:
|
||||||
|
{lock-file-command}
|
||||||
|
|
||||||
> NX Committing changes with git
|
> NX Committing changes with git
|
||||||
|
|
||||||
Staging files in git with the following command:
|
Staging files in git with the following command:
|
||||||
git add {project-name}/package.json
|
git add {project-name}/package.json {lock-file}
|
||||||
|
|
||||||
Committing files in git with the following command:
|
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
|
git commit --message chore(release): publish --message - project: {project-name} 999.9.9-version-git-operations-test.2
|
||||||
@ -340,10 +365,20 @@ describe('nx release - independent projects', () => {
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
||||||
|
|
||||||
|
> NX Updating {package-manager} lock file
|
||||||
|
|
||||||
|
Updating {lock-file} with the following command:
|
||||||
|
{lock-file-command}
|
||||||
|
|
||||||
|
> NX Updating {package-manager} lock file
|
||||||
|
|
||||||
|
Updating {lock-file} with the following command:
|
||||||
|
{lock-file-command}
|
||||||
|
|
||||||
> NX Committing changes with git
|
> NX Committing changes with git
|
||||||
|
|
||||||
Staging files in git with the following command:
|
Staging files in git with the following command:
|
||||||
git add {project-name}/package.json {project-name}/package.json {project-name}/package.json
|
git add {project-name}/package.json {project-name}/package.json {project-name}/package.json {lock-file}
|
||||||
|
|
||||||
Committing files in git with the following command:
|
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
|
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
|
||||||
|
|||||||
187
e2e/release/src/lock-file-updates.test.ts
Normal file
187
e2e/release/src/lock-file-updates.test.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import {
|
||||||
|
cleanupProject,
|
||||||
|
newProject,
|
||||||
|
runCLI,
|
||||||
|
runCommand,
|
||||||
|
uniq,
|
||||||
|
updateFile,
|
||||||
|
updateJson,
|
||||||
|
} from '@nx/e2e/utils';
|
||||||
|
|
||||||
|
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(/[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 lock file updates', () => {
|
||||||
|
let pkg1: string;
|
||||||
|
let pkg2: string;
|
||||||
|
let pkg3: string;
|
||||||
|
let previousPackageManager: string;
|
||||||
|
let previousYarnEnableImmutableInstalls: string;
|
||||||
|
let previousNodeOptions: string;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
previousPackageManager = process.env.SELECTED_PM;
|
||||||
|
previousYarnEnableImmutableInstalls =
|
||||||
|
process.env.YARN_ENABLE_IMMUTABLE_INSTALLS;
|
||||||
|
previousNodeOptions = process.env.NODE_OPTIONS;
|
||||||
|
});
|
||||||
|
|
||||||
|
// project will be created by each test individually
|
||||||
|
// in order to test different package managers
|
||||||
|
const initializeProject = (packageManager: 'npm' | 'yarn' | 'pnpm') => {
|
||||||
|
process.env.SELECTED_PM = packageManager;
|
||||||
|
|
||||||
|
newProject({
|
||||||
|
unsetProjectNameAndRootFormat: false,
|
||||||
|
packages: ['@nx/js'],
|
||||||
|
packageManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
|
||||||
|
// Update pkg2 to depend on pkg1
|
||||||
|
updateJson(`${pkg2}/package.json`, (json) => {
|
||||||
|
json.dependencies ??= {};
|
||||||
|
json.dependencies[`@proj/${pkg1}`] = '0.0.0';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
process.env.SELECTED_PM = previousPackageManager;
|
||||||
|
process.env.YARN_ENABLE_IMMUTABLE_INSTALLS =
|
||||||
|
previousYarnEnableImmutableInstalls;
|
||||||
|
process.env.NODE_OPTIONS = previousNodeOptions;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update package-lock.json when package manager is npm', async () => {
|
||||||
|
initializeProject('npm');
|
||||||
|
|
||||||
|
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`);
|
||||||
|
|
||||||
|
expect(versionOutput.match(/NX Updating npm lock file/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
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('should update yarn.lock when package manager is yarn', async () => {
|
||||||
|
process.env.YARN_ENABLE_IMMUTABLE_INSTALLS = 'false';
|
||||||
|
process.env.NODE_OPTIONS = '--no-enable-network-family-autoselection';
|
||||||
|
|
||||||
|
initializeProject('yarn');
|
||||||
|
|
||||||
|
updateJson('package.json', (json) => {
|
||||||
|
json.workspaces = [pkg1, pkg2, pkg3];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
runCommand(`yarn 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`);
|
||||||
|
|
||||||
|
expect(versionOutput.match(/NX Updating yarn lock file/g).length).toBe(1);
|
||||||
|
|
||||||
|
const filesChanges = runCommand('git diff --name-only HEAD');
|
||||||
|
|
||||||
|
expect(filesChanges).toMatchInlineSnapshot(`
|
||||||
|
.yarn/install-state.gz
|
||||||
|
{project-name}/package.json
|
||||||
|
{project-name}/package.json
|
||||||
|
{project-name}/package.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update pnpm-lock.yaml when package manager is pnpm', async () => {
|
||||||
|
initializeProject('pnpm');
|
||||||
|
|
||||||
|
updateFile(
|
||||||
|
'pnpm-workspace.yaml',
|
||||||
|
`packages:\n - ${pkg1}\n - ${pkg2}\n - ${pkg3}\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
// workaround for NXC-143
|
||||||
|
runCLI('reset');
|
||||||
|
|
||||||
|
runCommand(`pnpm install`);
|
||||||
|
|
||||||
|
runCommand(`git add .`);
|
||||||
|
runCommand(`git commit -m "chore: initial commit"`);
|
||||||
|
|
||||||
|
const versionOutput = runCLI(`release version 999.9.9`);
|
||||||
|
|
||||||
|
expect(versionOutput.match(/NX Updating pnpm lock file/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
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1146,17 +1146,14 @@ ${JSON.stringify(
|
|||||||
silenceError: true,
|
silenceError: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(releaseOutput6a).toMatchInlineSnapshot(`
|
expect(
|
||||||
|
releaseOutput6a.match(
|
||||||
> NX Running release version for project: {project-name}
|
new RegExp(
|
||||||
|
`> NX Unable to resolve the current version from the registry ${e2eRegistryUrl}. Please ensure that the package exists in the registry in order to use the "registry" currentVersionResolver. Alternatively, you can set the "version.generatorOptions.fallbackCurrentVersionResolver" to "disk" in order to fallback to the version on disk when the registry lookup fails.`,
|
||||||
{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json
|
'g'
|
||||||
|
)
|
||||||
> NX Unable to resolve the current version from the registry ${e2eRegistryUrl}. Please ensure that the package exists in the registry in order to use the "registry" currentVersionResolver. Alternatively, you can set the "version.generatorOptions.fallbackCurrentVersionResolver" to "disk" in order to fallback to the version on disk when the registry lookup fails.
|
).length
|
||||||
|
).toEqual(1);
|
||||||
- Resolving the current version for tag "other" on ${e2eRegistryUrl}
|
|
||||||
|
|
||||||
`);
|
|
||||||
|
|
||||||
const releaseOutput6b = runCLI(
|
const releaseOutput6b = runCLI(
|
||||||
`release patch --skip-publish --first-release`,
|
`release patch --skip-publish --first-release`,
|
||||||
|
|||||||
@ -88,33 +88,36 @@ describe('release-version', () => {
|
|||||||
})
|
})
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"my-lib": {
|
"callback": [Function],
|
||||||
"currentVersion": "0.0.1",
|
"data": {
|
||||||
"dependentProjects": [
|
"my-lib": {
|
||||||
{
|
"currentVersion": "0.0.1",
|
||||||
"dependencyCollection": "dependencies",
|
"dependentProjects": [
|
||||||
"source": "project-with-dependency-on-my-pkg",
|
{
|
||||||
"target": "my-lib",
|
"dependencyCollection": "dependencies",
|
||||||
"type": "static",
|
"source": "project-with-dependency-on-my-pkg",
|
||||||
},
|
"target": "my-lib",
|
||||||
{
|
"type": "static",
|
||||||
"dependencyCollection": "devDependencies",
|
},
|
||||||
"source": "project-with-devDependency-on-my-pkg",
|
{
|
||||||
"target": "my-lib",
|
"dependencyCollection": "devDependencies",
|
||||||
"type": "static",
|
"source": "project-with-devDependency-on-my-pkg",
|
||||||
},
|
"target": "my-lib",
|
||||||
],
|
"type": "static",
|
||||||
"newVersion": "1.0.0",
|
},
|
||||||
},
|
],
|
||||||
"project-with-dependency-on-my-pkg": {
|
"newVersion": "1.0.0",
|
||||||
"currentVersion": "0.0.1",
|
},
|
||||||
"dependentProjects": [],
|
"project-with-dependency-on-my-pkg": {
|
||||||
"newVersion": "1.0.0",
|
"currentVersion": "0.0.1",
|
||||||
},
|
"dependentProjects": [],
|
||||||
"project-with-devDependency-on-my-pkg": {
|
"newVersion": "1.0.0",
|
||||||
"currentVersion": "0.0.1",
|
},
|
||||||
"dependentProjects": [],
|
"project-with-devDependency-on-my-pkg": {
|
||||||
"newVersion": "1.0.0",
|
"currentVersion": "0.0.1",
|
||||||
|
"dependentProjects": [],
|
||||||
|
"newVersion": "1.0.0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|||||||
@ -19,21 +19,24 @@ import {
|
|||||||
} from 'nx/src/command-line/release/utils/resolve-semver-specifier';
|
} from 'nx/src/command-line/release/utils/resolve-semver-specifier';
|
||||||
import { isValidSemverSpecifier } from 'nx/src/command-line/release/utils/semver';
|
import { isValidSemverSpecifier } from 'nx/src/command-line/release/utils/semver';
|
||||||
import {
|
import {
|
||||||
|
ReleaseVersionGeneratorResult,
|
||||||
VersionData,
|
VersionData,
|
||||||
deriveNewSemverVersion,
|
deriveNewSemverVersion,
|
||||||
validReleaseVersionPrefixes,
|
validReleaseVersionPrefixes,
|
||||||
} from 'nx/src/command-line/release/version';
|
} from 'nx/src/command-line/release/version';
|
||||||
|
import { daemonClient } from 'nx/src/daemon/client/client';
|
||||||
import { interpolate } from 'nx/src/tasks-runner/utils';
|
import { interpolate } from 'nx/src/tasks-runner/utils';
|
||||||
import * as ora from 'ora';
|
import * as ora from 'ora';
|
||||||
import { relative } from 'path';
|
import { relative } from 'path';
|
||||||
import { prerelease } from 'semver';
|
import { prerelease } from 'semver';
|
||||||
import { ReleaseVersionGeneratorSchema } from './schema';
|
import { ReleaseVersionGeneratorSchema } from './schema';
|
||||||
import { resolveLocalPackageDependencies } from './utils/resolve-local-package-dependencies';
|
import { resolveLocalPackageDependencies } from './utils/resolve-local-package-dependencies';
|
||||||
|
import { updateLockFile } from './utils/update-lock-file';
|
||||||
|
|
||||||
export async function releaseVersionGenerator(
|
export async function releaseVersionGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: ReleaseVersionGeneratorSchema
|
options: ReleaseVersionGeneratorSchema
|
||||||
) {
|
): Promise<ReleaseVersionGeneratorResult> {
|
||||||
try {
|
try {
|
||||||
const versionData: VersionData = {};
|
const versionData: VersionData = {};
|
||||||
|
|
||||||
@ -473,7 +476,36 @@ To fix this you will either need to add a package.json file at that location, or
|
|||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
|
|
||||||
// Return the version data so that it can be leveraged by the overall version command
|
// Return the version data so that it can be leveraged by the overall version command
|
||||||
return versionData;
|
return {
|
||||||
|
data: versionData,
|
||||||
|
callback: async (tree, opts) => {
|
||||||
|
const cwd = tree.root;
|
||||||
|
|
||||||
|
const isDaemonEnabled = daemonClient.enabled();
|
||||||
|
if (isDaemonEnabled) {
|
||||||
|
// temporarily stop the daemon, as it will error if the lock file is updated
|
||||||
|
await daemonClient.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedFiles = updateLockFile(cwd, opts);
|
||||||
|
|
||||||
|
if (isDaemonEnabled) {
|
||||||
|
try {
|
||||||
|
await daemonClient.startInBackground();
|
||||||
|
} catch (e) {
|
||||||
|
// If the daemon fails to start, we don't want to prevent the user from continuing, so we just log the error and move on
|
||||||
|
if (opts.verbose) {
|
||||||
|
output.warn({
|
||||||
|
title:
|
||||||
|
'Unable to restart the Nx Daemon. It will be disabled until you run "nx reset"',
|
||||||
|
bodyLines: [e.message],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedFiles;
|
||||||
|
},
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||||
output.error({
|
output.error({
|
||||||
|
|||||||
@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
detectPackageManager,
|
||||||
|
getPackageManagerCommand,
|
||||||
|
getPackageManagerVersion,
|
||||||
|
output,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
|
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
|
||||||
|
import { gte } from 'semver';
|
||||||
|
|
||||||
|
export function updateLockFile(
|
||||||
|
cwd: string,
|
||||||
|
{
|
||||||
|
dryRun,
|
||||||
|
verbose,
|
||||||
|
generatorOptions,
|
||||||
|
}: {
|
||||||
|
dryRun?: boolean;
|
||||||
|
verbose?: boolean;
|
||||||
|
generatorOptions?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (generatorOptions?.skipLockFileUpdate) {
|
||||||
|
if (verbose) {
|
||||||
|
console.log(
|
||||||
|
'\nSkipped lock file update because skipLockFileUpdate was set.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageManager = detectPackageManager(cwd);
|
||||||
|
const packageManagerCommands = getPackageManagerCommand(packageManager);
|
||||||
|
|
||||||
|
let installArgs = generatorOptions?.installArgs || '';
|
||||||
|
|
||||||
|
output.logSingleLine(`Updating ${packageManager} lock file`);
|
||||||
|
|
||||||
|
let env: object = {};
|
||||||
|
|
||||||
|
if (generatorOptions?.installIgnoreScripts) {
|
||||||
|
if (
|
||||||
|
packageManager === 'yarn' &&
|
||||||
|
gte(getPackageManagerVersion(packageManager), '2.0.0')
|
||||||
|
) {
|
||||||
|
env = { YARN_ENABLE_SCRIPTS: 'false' };
|
||||||
|
} else {
|
||||||
|
// npm, pnpm, and yarn classic all use the same --ignore-scripts option
|
||||||
|
installArgs = `${installArgs} --ignore-scripts`.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lockFile = getLockFileName(packageManager);
|
||||||
|
const command =
|
||||||
|
`${packageManagerCommands.updateLockFile} ${installArgs}`.trim();
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
if (dryRun) {
|
||||||
|
console.log(
|
||||||
|
`Would update ${lockFile} with the following command, but --dry-run was set:`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(`Updating ${lockFile} with the following command:`);
|
||||||
|
}
|
||||||
|
console.log(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
execLockFileUpdate(command, cwd, env);
|
||||||
|
|
||||||
|
return [lockFile];
|
||||||
|
}
|
||||||
|
|
||||||
|
function execLockFileUpdate(
|
||||||
|
command: string,
|
||||||
|
cwd: string,
|
||||||
|
env: object = {}
|
||||||
|
): void {
|
||||||
|
try {
|
||||||
|
execSync(command, {
|
||||||
|
cwd,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...env,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
output.error({
|
||||||
|
title: `Error updating lock file with command '${command}'`,
|
||||||
|
bodyLines: [
|
||||||
|
`Verify that '${command}' succeeds when run from the workspace root.`,
|
||||||
|
`To configure a string of arguments to be passed to this command, set the 'release.version.generatorOptions.installArgs' property in nx.json.`,
|
||||||
|
`To ignore install lifecycle scripts, set 'release.version.generatorOptions.installIgnoreScripts' to true in nx.json.`,
|
||||||
|
`To disable this step entirely, set 'release.version.skipLockFileUpdate' to true in nx.json.`,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,24 @@
|
|||||||
import { prerelease } from 'semver';
|
import { prerelease } from 'semver';
|
||||||
import { ProjectGraph } from '../../../config/project-graph';
|
import { ProjectGraph } from '../../../config/project-graph';
|
||||||
|
import { Tree } from '../../../generators/tree';
|
||||||
import { createFileMapUsingProjectGraph } from '../../../project-graph/file-map-utils';
|
import { createFileMapUsingProjectGraph } from '../../../project-graph/file-map-utils';
|
||||||
import { interpolate } from '../../../tasks-runner/utils';
|
import { interpolate } from '../../../tasks-runner/utils';
|
||||||
import { output } from '../../../utils/output';
|
import { output } from '../../../utils/output';
|
||||||
import type { ReleaseGroupWithName } from '../config/filter-release-groups';
|
import type { ReleaseGroupWithName } from '../config/filter-release-groups';
|
||||||
import { GitCommit, gitAdd, gitCommit } from './git';
|
import { GitCommit, gitAdd, gitCommit } from './git';
|
||||||
|
|
||||||
|
export type ReleaseVersionGeneratorResult = {
|
||||||
|
data: VersionData;
|
||||||
|
callback: (
|
||||||
|
tree: Tree,
|
||||||
|
opts: {
|
||||||
|
dryRun?: boolean;
|
||||||
|
verbose?: boolean;
|
||||||
|
generatorOptions?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
) => Promise<string[]>;
|
||||||
|
};
|
||||||
|
|
||||||
export type VersionData = Record<
|
export type VersionData = Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import {
|
|||||||
import { gitAdd, gitTag } from './utils/git';
|
import { gitAdd, gitTag } from './utils/git';
|
||||||
import { printDiff } from './utils/print-changes';
|
import { printDiff } from './utils/print-changes';
|
||||||
import {
|
import {
|
||||||
|
ReleaseVersionGeneratorResult,
|
||||||
VersionData,
|
VersionData,
|
||||||
commitChanges,
|
commitChanges,
|
||||||
createCommitMessageValues,
|
createCommitMessageValues,
|
||||||
@ -43,7 +44,10 @@ import {
|
|||||||
|
|
||||||
// Reexport some utils for use in plugin release-version generator implementations
|
// Reexport some utils for use in plugin release-version generator implementations
|
||||||
export { deriveNewSemverVersion } from './utils/semver';
|
export { deriveNewSemverVersion } from './utils/semver';
|
||||||
export type { VersionData } from './utils/shared';
|
export type {
|
||||||
|
ReleaseVersionGeneratorResult,
|
||||||
|
VersionData,
|
||||||
|
} from './utils/shared';
|
||||||
|
|
||||||
export const validReleaseVersionPrefixes = ['auto', '', '~', '^'];
|
export const validReleaseVersionPrefixes = ['auto', '', '~', '^'];
|
||||||
|
|
||||||
@ -128,6 +132,8 @@ export async function releaseVersion(
|
|||||||
const versionData: VersionData = {};
|
const versionData: VersionData = {};
|
||||||
const commitMessage: string | undefined =
|
const commitMessage: string | undefined =
|
||||||
args.gitCommitMessage || nxReleaseConfig.version.git.commitMessage;
|
args.gitCommitMessage || nxReleaseConfig.version.git.commitMessage;
|
||||||
|
const changedLockFiles = new Set<string>();
|
||||||
|
const generatorCallbacks: (() => Promise<void>)[] = [];
|
||||||
|
|
||||||
if (args.projects?.length) {
|
if (args.projects?.length) {
|
||||||
/**
|
/**
|
||||||
@ -150,7 +156,7 @@ export async function releaseVersion(
|
|||||||
releaseGroupToFilteredProjects.get(releaseGroup)
|
releaseGroupToFilteredProjects.get(releaseGroup)
|
||||||
);
|
);
|
||||||
|
|
||||||
await runVersionOnProjects(
|
const generatorCallback = await runVersionOnProjects(
|
||||||
projectGraph,
|
projectGraph,
|
||||||
nxJson,
|
nxJson,
|
||||||
args,
|
args,
|
||||||
@ -160,6 +166,16 @@ export async function releaseVersion(
|
|||||||
releaseGroup,
|
releaseGroup,
|
||||||
versionData
|
versionData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
generatorCallbacks.push(async () =>
|
||||||
|
(
|
||||||
|
await generatorCallback(tree, {
|
||||||
|
dryRun: !!args.dryRun,
|
||||||
|
verbose: !!args.verbose,
|
||||||
|
generatorOptions: releaseGroup.version.generatorOptions,
|
||||||
|
})
|
||||||
|
).forEach((f) => changedLockFiles.add(f))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
|
// Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
|
||||||
@ -175,7 +191,14 @@ export async function releaseVersion(
|
|||||||
|
|
||||||
printAndFlushChanges(tree, !!args.dryRun);
|
printAndFlushChanges(tree, !!args.dryRun);
|
||||||
|
|
||||||
const changedFiles = tree.listChanges().map((f) => f.path);
|
for (const generatorCallback of generatorCallbacks) {
|
||||||
|
await generatorCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
const changedFiles = [
|
||||||
|
...tree.listChanges().map((f) => f.path),
|
||||||
|
...changedLockFiles,
|
||||||
|
];
|
||||||
|
|
||||||
// No further actions are necessary in this scenario (e.g. if conventional commits detected no changes)
|
// No further actions are necessary in this scenario (e.g. if conventional commits detected no changes)
|
||||||
if (!changedFiles.length) {
|
if (!changedFiles.length) {
|
||||||
@ -188,7 +211,7 @@ export async function releaseVersion(
|
|||||||
|
|
||||||
if (args.gitCommit ?? nxReleaseConfig.version.git.commit) {
|
if (args.gitCommit ?? nxReleaseConfig.version.git.commit) {
|
||||||
await commitChanges(
|
await commitChanges(
|
||||||
tree.listChanges().map((f) => f.path),
|
changedFiles,
|
||||||
!!args.dryRun,
|
!!args.dryRun,
|
||||||
!!args.verbose,
|
!!args.verbose,
|
||||||
createCommitMessageValues(
|
createCommitMessageValues(
|
||||||
@ -249,7 +272,7 @@ export async function releaseVersion(
|
|||||||
projects,
|
projects,
|
||||||
});
|
});
|
||||||
|
|
||||||
await runVersionOnProjects(
|
const callback = await runVersionOnProjects(
|
||||||
projectGraph,
|
projectGraph,
|
||||||
nxJson,
|
nxJson,
|
||||||
args,
|
args,
|
||||||
@ -259,6 +282,16 @@ export async function releaseVersion(
|
|||||||
releaseGroup,
|
releaseGroup,
|
||||||
versionData
|
versionData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
generatorCallbacks.push(async () =>
|
||||||
|
(
|
||||||
|
await callback(tree, {
|
||||||
|
dryRun: !!args.dryRun,
|
||||||
|
verbose: !!args.verbose,
|
||||||
|
generatorOptions: releaseGroup.version.generatorOptions,
|
||||||
|
})
|
||||||
|
).forEach((f) => changedLockFiles.add(f))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
|
// Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
|
||||||
@ -274,6 +307,10 @@ export async function releaseVersion(
|
|||||||
|
|
||||||
printAndFlushChanges(tree, !!args.dryRun);
|
printAndFlushChanges(tree, !!args.dryRun);
|
||||||
|
|
||||||
|
for (const generatorCallback of generatorCallbacks) {
|
||||||
|
await generatorCallback();
|
||||||
|
}
|
||||||
|
|
||||||
// Only applicable when there is a single release group with a fixed relationship
|
// Only applicable when there is a single release group with a fixed relationship
|
||||||
let workspaceVersion: string | null | undefined = undefined;
|
let workspaceVersion: string | null | undefined = undefined;
|
||||||
if (releaseGroups.length === 1) {
|
if (releaseGroups.length === 1) {
|
||||||
@ -286,7 +323,10 @@ export async function releaseVersion(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const changedFiles = tree.listChanges().map((f) => f.path);
|
const changedFiles = [
|
||||||
|
...tree.listChanges().map((f) => f.path),
|
||||||
|
...changedLockFiles,
|
||||||
|
];
|
||||||
|
|
||||||
// No further actions are necessary in this scenario (e.g. if conventional commits detected no changes)
|
// No further actions are necessary in this scenario (e.g. if conventional commits detected no changes)
|
||||||
if (!changedFiles.length) {
|
if (!changedFiles.length) {
|
||||||
@ -366,7 +406,7 @@ async function runVersionOnProjects(
|
|||||||
projectNames: string[],
|
projectNames: string[],
|
||||||
releaseGroup: ReleaseGroupWithName,
|
releaseGroup: ReleaseGroupWithName,
|
||||||
versionData: VersionData
|
versionData: VersionData
|
||||||
) {
|
): Promise<ReleaseVersionGeneratorResult['callback']> {
|
||||||
const generatorOptions: ReleaseVersionGeneratorSchema = {
|
const generatorOptions: ReleaseVersionGeneratorSchema = {
|
||||||
// Always ensure a string to avoid generator schema validation errors
|
// Always ensure a string to avoid generator schema validation errors
|
||||||
specifier: args.specifier ?? '',
|
specifier: args.specifier ?? '',
|
||||||
@ -395,20 +435,22 @@ async function runVersionOnProjects(
|
|||||||
|
|
||||||
const releaseVersionGenerator = generatorData.implementationFactory();
|
const releaseVersionGenerator = generatorData.implementationFactory();
|
||||||
|
|
||||||
// We expect all version generator implementations to return a VersionData object, rather than a GeneratorCallback
|
// We expect all version generator implementations to return a ReleaseVersionGeneratorResult object, rather than a GeneratorCallback
|
||||||
const versionDataForProjects = (await releaseVersionGenerator(
|
const versionResult = (await releaseVersionGenerator(
|
||||||
tree,
|
tree,
|
||||||
combinedOpts
|
combinedOpts
|
||||||
)) as unknown as VersionData;
|
)) as unknown as ReleaseVersionGeneratorResult;
|
||||||
|
|
||||||
if (typeof versionDataForProjects === 'function') {
|
if (typeof versionResult === 'function') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The version generator ${generatorData.collectionName}:${generatorData.normalizedGeneratorName} returned a function instead of an expected VersionData object`
|
`The version generator ${generatorData.collectionName}:${generatorData.normalizedGeneratorName} returned a function instead of an expected ReleaseVersionGeneratorResult`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge the extra version data into the existing
|
// Merge the extra version data into the existing
|
||||||
appendVersionData(versionData, versionDataForProjects);
|
appendVersionData(versionData, versionResult.data);
|
||||||
|
|
||||||
|
return versionResult.callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
function printAndFlushChanges(tree: Tree, isDryRun: boolean) {
|
function printAndFlushChanges(tree: Tree, isDryRun: boolean) {
|
||||||
|
|||||||
@ -2,13 +2,13 @@ import { exec, execSync } from 'child_process';
|
|||||||
import { copyFileSync, existsSync, writeFileSync } from 'fs';
|
import { copyFileSync, existsSync, writeFileSync } from 'fs';
|
||||||
import { remove } from 'fs-extra';
|
import { remove } from 'fs-extra';
|
||||||
import { dirname, join, relative } from 'path';
|
import { dirname, join, relative } from 'path';
|
||||||
|
import { gte, lt } from 'semver';
|
||||||
import { dirSync } from 'tmp';
|
import { dirSync } from 'tmp';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
import { readNxJson } from '../config/configuration';
|
||||||
import { readFileIfExisting, writeJsonFile } from './fileutils';
|
import { readFileIfExisting, writeJsonFile } from './fileutils';
|
||||||
import { readModulePackageJson } from './package-json';
|
import { readModulePackageJson } from './package-json';
|
||||||
import { gte, lt } from 'semver';
|
|
||||||
import { workspaceRoot } from './workspace-root';
|
import { workspaceRoot } from './workspace-root';
|
||||||
import { readNxJson } from '../config/configuration';
|
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ export interface PackageManagerCommands {
|
|||||||
preInstall?: string;
|
preInstall?: string;
|
||||||
install: string;
|
install: string;
|
||||||
ciInstall: string;
|
ciInstall: string;
|
||||||
|
updateLockFile: string;
|
||||||
add: string;
|
add: string;
|
||||||
addDev: string;
|
addDev: string;
|
||||||
rm: string;
|
rm: string;
|
||||||
@ -71,6 +72,9 @@ export function getPackageManagerCommand(
|
|||||||
ciInstall: useBerry
|
ciInstall: useBerry
|
||||||
? 'yarn install --immutable'
|
? 'yarn install --immutable'
|
||||||
: 'yarn install --frozen-lockfile',
|
: 'yarn install --frozen-lockfile',
|
||||||
|
updateLockFile: useBerry
|
||||||
|
? 'yarn install --mode update-lockfile'
|
||||||
|
: 'yarn install',
|
||||||
add: useBerry ? 'yarn add' : 'yarn add -W',
|
add: useBerry ? 'yarn add' : 'yarn add -W',
|
||||||
addDev: useBerry ? 'yarn add -D' : 'yarn add -D -W',
|
addDev: useBerry ? 'yarn add -D' : 'yarn add -D -W',
|
||||||
rm: 'yarn remove',
|
rm: 'yarn remove',
|
||||||
@ -89,6 +93,7 @@ export function getPackageManagerCommand(
|
|||||||
return {
|
return {
|
||||||
install: 'pnpm install --no-frozen-lockfile', // explicitly disable in case of CI
|
install: 'pnpm install --no-frozen-lockfile', // explicitly disable in case of CI
|
||||||
ciInstall: 'pnpm install --frozen-lockfile',
|
ciInstall: 'pnpm install --frozen-lockfile',
|
||||||
|
updateLockFile: 'pnpm install --lockfile-only',
|
||||||
add: isPnpmWorkspace ? 'pnpm add -w' : 'pnpm add',
|
add: isPnpmWorkspace ? 'pnpm add -w' : 'pnpm add',
|
||||||
addDev: isPnpmWorkspace ? 'pnpm add -Dw' : 'pnpm add -D',
|
addDev: isPnpmWorkspace ? 'pnpm add -Dw' : 'pnpm add -D',
|
||||||
rm: 'pnpm rm',
|
rm: 'pnpm rm',
|
||||||
@ -108,6 +113,7 @@ export function getPackageManagerCommand(
|
|||||||
return {
|
return {
|
||||||
install: 'npm install',
|
install: 'npm install',
|
||||||
ciInstall: 'npm ci',
|
ciInstall: 'npm ci',
|
||||||
|
updateLockFile: 'npm install --package-lock-only',
|
||||||
add: 'npm install',
|
add: 'npm install',
|
||||||
addDev: 'npm install -D',
|
addDev: 'npm install -D',
|
||||||
rm: 'npm rm',
|
rm: 'npm rm',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user