nx/e2e/release/src/preserve-local-dependency-protocols.test.ts

353 lines
13 KiB
TypeScript

import { NxJsonConfiguration } from '@nx/devkit';
import {
cleanupProject,
getPackageManagerCommand,
newProject,
readJson,
runCLI,
runCommandAsync,
tmpProjPath,
uniq,
updateFile,
updateJson,
} from '@nx/e2e/utils';
import { execSync } from 'node:child_process';
import { join } from 'node: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\s+project\.json/g, 'XXB project.json')
.replaceAll(/\d*B\s+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(/\d*B\s+src\//g, 'XXB src/')
.replaceAll(/\d*B\s+index/g, 'XXB index')
.replaceAll(/total files:\s+\d*/g, 'total files: X')
.replaceAll(/\d*B\s+README.md/g, 'XXB README.md')
.replaceAll(/Test @[\w\d]+/g, 'Test @{COMMIT_AUTHOR}')
.replaceAll(/(\w+) lock file/g, 'PM lock file')
// 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
// Slightly different handling needed for bun (length can be 8)
.replaceAll(/[a-fA-F0-9]{7,8}/g, '{COMMIT_SHA}')
.replaceAll(/bun publish v\d+\.\d+\.\d+/g, 'bun publish vX.X.X')
.replaceAll(
/Integrity:\s*.*/g,
'Integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
)
.split('\n')
.map((r) => r.trim())
.filter(Boolean)
.join('\n')
);
},
test(val: string) {
return val != null && typeof val === 'string';
},
});
describe('nx release preserve local dependency protocols', () => {
let previousPackageManager: string;
let e2eRegistryUrl: string;
beforeAll(() => {
previousPackageManager = process.env.SELECTED_PM;
// This is the verdaccio instance that the e2e tests themselves are working from
e2eRegistryUrl = execSync('npm config get registry').toString().trim();
});
afterEach(() => cleanupProject());
afterAll(() => {
process.env.SELECTED_PM = previousPackageManager;
});
/**
* Initialize each test with a fresh workspace using the specified
* package manager.
*/
const initializeProject = async (packageManager: 'pnpm' | 'bun') => {
process.env.SELECTED_PM = packageManager;
console.log(`Creating workspace with package manager: ${packageManager}`);
newProject({
packages: ['@nx/js'],
packageManager,
});
const pkg1 = uniq('my-pkg-1');
runCLI(`generate @nx/workspace:npm-package ${pkg1}`);
const pkg2 = uniq('my-pkg-2');
runCLI(`generate @nx/workspace:npm-package ${pkg2}`);
// Set up a workspace dependency using the workspace protocol
updateJson(join(pkg1, 'package.json'), (packageJson) => {
packageJson.dependencies = {
[`@proj/${pkg2}`]: 'workspace:*',
};
return packageJson;
});
// Add workspaces config
if (packageManager === 'pnpm') {
updateFile('pnpm-workspace.yaml', `packages:\n - ${pkg1}\n - ${pkg2}\n`);
} else {
updateJson('package.json', (packageJson) => {
packageJson.workspaces = [pkg1, pkg2];
return packageJson;
});
}
// workaround for NXC-143
runCLI('reset');
await runCommandAsync(getPackageManagerCommand({ packageManager }).install);
return { workspacePath: tmpProjPath(), pkg1, pkg2 };
};
it('should replace local dependency protocols with the actual version number when version.generatorOptions.preserveLocalDependencyProtocols is not set to true', async () => {
// The package manager currently does not matter for the versioning behavior, it's imperatively controlled by the user
const { workspacePath } = await initializeProject('pnpm');
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release = {};
return nxJson;
});
// Show the dependency being updated
expect(runCLI(`release version minor -d --verbose`, { cwd: workspacePath }))
.toMatchInlineSnapshot(`
NX Running release version for project: {project-name}
{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json
{project-name} 📄 Resolved the current version as 0.0.0 from {project-name}/package.json
{project-name} 📄 Using the provided version specifier "minor".
{project-name} ✍️ New version 0.1.0 written to {project-name}/package.json
NX Running release version for project: {project-name}
{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json
{project-name} 📄 Resolved the current version as 0.0.0 from {project-name}/package.json
{project-name} 📄 Using the provided version specifier "minor".
{project-name} ✍️ New version 0.1.0 written to {project-name}/package.json
{project-name} ✍️ Applying new version 0.1.0 to 1 package which depends on {project-name}
"name": "@proj/{project-name}",
- "version": "0.0.0",
+ "version": "0.1.0",
"scripts": {
"dependencies": {
- "@proj/{project-name}": "workspace:*"
+ "@proj/{project-name}": "0.1.0"
}
}
+
"name": "@proj/{project-name}",
- "version": "0.0.0",
+ "version": "0.1.0",
"scripts": {
NX Updating PM lock file
Would update pnpm-lock.yaml with the following command, but --dry-run was set:
pnpm install --lockfile-only
NX Staging changed files with git
Would stage files in git with the following command, but --dry-run was set:
git add {project-name}/package.json {project-name}/package.json
`);
});
it('should preserve local dependency protocols when version.generatorOptions.preserveLocalDependencyProtocols is set to true', async () => {
// The package manager currently does not matter for the versioning behavior, it's imperatively controlled by the user
const { workspacePath } = await initializeProject('pnpm');
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
nxJson.release = {
version: {
generatorOptions: {
preserveLocalDependencyProtocols: true,
},
},
};
return nxJson;
});
// Show that the dependency has not been updated
expect(runCLI(`release version minor -d --verbose`, { cwd: workspacePath }))
.toMatchInlineSnapshot(`
NX Running release version for project: {project-name}
{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json
{project-name} 📄 Resolved the current version as 0.0.0 from {project-name}/package.json
{project-name} 📄 Using the provided version specifier "minor".
{project-name} ✍️ New version 0.1.0 written to {project-name}/package.json
NX Running release version for project: {project-name}
{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json
{project-name} 📄 Resolved the current version as 0.0.0 from {project-name}/package.json
{project-name} 📄 Using the provided version specifier "minor".
{project-name} ✍️ New version 0.1.0 written to {project-name}/package.json
{project-name} ✍️ Applying new version 0.1.0 to 1 package which depends on {project-name}
"name": "@proj/{project-name}",
- "version": "0.0.0",
+ "version": "0.1.0",
"scripts": {
}
+
"name": "@proj/{project-name}",
- "version": "0.0.0",
+ "version": "0.1.0",
"scripts": {
NX Updating PM lock file
Would update pnpm-lock.yaml with the following command, but --dry-run was set:
pnpm install --lockfile-only
NX Staging changed files with git
Would stage files in git with the following command, but --dry-run was set:
git add {project-name}/package.json {project-name}/package.json
`);
});
describe('pnpm publish', () => {
it('should replace local dependency protocols dynamically during publishing', async () => {
const { workspacePath, pkg1 } = await initializeProject('pnpm');
// Prove that the local dependency protocol is present in the pkg1 package.json
expect(readJson(join(workspacePath, pkg1, 'package.json')))
.toMatchInlineSnapshot(`
{
dependencies: {
@proj/{project-name}: workspace:*,
},
name: @proj/{project-name},
scripts: {
test: node index.js,
},
version: 0.0.0,
}
`);
// Publish the packages
expect(
runCLI(`release publish`, { silenceError: true, cwd: workspacePath })
).toMatchInlineSnapshot(`
NX Running target nx-release-publish for 2 projects:
- {project-name}
- {project-name}
> nx run {project-name}:nx-release-publish
📦 @proj/{project-name}@0.0.0
=== Tarball Contents ===
XXXXB index.js
XXXB package.json
XXB project.json
=== Tarball Details ===
name: @proj/{project-name}
version: 0.0.0
filename: proj-{project-name}-0.0.0.tgz
package size: XXXB
unpacked size: XXXB
shasum: {SHASUM}
integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
total files: X
Published to ${e2eRegistryUrl} with tag "latest"
> nx run {project-name}:nx-release-publish
📦 @proj/{project-name}@0.0.0
=== Tarball Contents ===
XXXXB index.js
XXXB package.json
XXB project.json
=== Tarball Details ===
name: @proj/{project-name}
version: 0.0.0
filename: proj-{project-name}-0.0.0.tgz
package size: XXXB
unpacked size: XXXB
shasum: {SHASUM}
integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
total files: X
Published to ${e2eRegistryUrl} with tag "latest"
NX Successfully ran target nx-release-publish for 2 projects
`);
// Ensure that the dependency on pkg2 specified on the registry was replaced with the actual version number during publishing
expect(
(await runCommandAsync(`npm view @proj/${pkg1} dependencies`))
.combinedOutput
).toMatchInlineSnapshot(`{ '@proj/{project-name}': '0.0.0' }`);
});
});
describe('bun publish', () => {
it('should replace local dependency protocols dynamically during publishing', async () => {
const { workspacePath, pkg1 } = await initializeProject('bun');
// Prove that the local dependency protocol is present in the pkg1 package.json
expect(readJson(join(workspacePath, pkg1, 'package.json')))
.toMatchInlineSnapshot(`
{
dependencies: {
@proj/{project-name}: workspace:*,
},
name: @proj/{project-name},
scripts: {
test: node index.js,
},
version: 0.0.0,
}
`);
// Publish the packages
expect(
runCLI(`release publish`, { silenceError: true, cwd: workspacePath })
).toMatchInlineSnapshot(`
NX Running target nx-release-publish for 2 projects:
- {project-name}
- {project-name}
> nx run {project-name}:nx-release-publish
bun publish vX.X.X ({COMMIT_SHA})
packed XXXB package.json
packed XXB index.js
packed XXB project.json
Total files: 3
Shasum: {SHASUM}
Integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Unpacked size: XXXB
Packed size: XXXB
Tag: latest
Access: default
Registry: ${e2eRegistryUrl}
+ @proj/{project-name}@0.0.0
Published to ${e2eRegistryUrl} with tag "latest"
> nx run {project-name}:nx-release-publish
bun publish vX.X.X ({COMMIT_SHA})
packed XXXB package.json
packed XXB index.js
packed XXB project.json
Total files: 3
Shasum: {SHASUM}
Integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Unpacked size: XXXB
Packed size: XXXB
Tag: latest
Access: default
Registry: ${e2eRegistryUrl}
+ @proj/{project-name}@0.0.0
Published to ${e2eRegistryUrl} with tag "latest"
NX Successfully ran target nx-release-publish for 2 projects
`);
// Ensure that the dependency on pkg2 specified on the registry was replaced with the actual version number during publishing
expect(
(await runCommandAsync(`npm view @proj/${pkg1} dependencies`))
.combinedOutput
).toMatchInlineSnapshot(`{ '@proj/{project-name}': '0.0.0' }`);
});
});
});