fix(js): allow publishable library to run release command (#29775)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> - there is no option `fallbackCurrentVersionResolver: 'disk',` - can't run release for newly created publishable libraries ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> - move code to add release for publishable libraries into its own file to be reused by other stacks - add `fallbackCurrentVersionResolver: 'disk',` to project's release.version. generatorOptions ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes https://github.com/nrwl/nx/issues/29723
This commit is contained in:
parent
48421fdb9e
commit
540aeff488
124
e2e/release/src/release publishable-libraries.test.ts
Normal file
124
e2e/release/src/release publishable-libraries.test.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import {
|
||||||
|
cleanupProject,
|
||||||
|
newProject,
|
||||||
|
runCLI,
|
||||||
|
runCommandAsync,
|
||||||
|
uniq,
|
||||||
|
} from '@nx/e2e/utils';
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
|
||||||
|
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}')
|
||||||
|
.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
|
||||||
|
.split('\n')
|
||||||
|
.map((r) => r.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
test(val: string) {
|
||||||
|
return val != null && typeof val === 'string';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('release publishable libraries', () => {
|
||||||
|
let e2eRegistryUrl: string;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
newProject({
|
||||||
|
packages: ['@nx/js'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Normalize git committer information so it is deterministic in snapshots
|
||||||
|
await runCommandAsync(`git config user.email "test@test.com"`);
|
||||||
|
await runCommandAsync(`git config user.name "Test"`);
|
||||||
|
// Create a baseline version tag
|
||||||
|
await runCommandAsync(`git tag v0.0.0`);
|
||||||
|
|
||||||
|
// We need a valid git origin to exist for the commit references to work (and later the test for createRelease)
|
||||||
|
await runCommandAsync(
|
||||||
|
`git remote add origin https://github.com/nrwl/fake-repo.git`
|
||||||
|
);
|
||||||
|
|
||||||
|
// This is the verdaccio instance that the e2e tests themselves are working from
|
||||||
|
e2eRegistryUrl = execSync('npm config get registry').toString().trim();
|
||||||
|
});
|
||||||
|
afterAll(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should be able to release publishable js library', async () => {
|
||||||
|
const jsLib = uniq('my-pkg-');
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/js:lib ${jsLib} --publishable --importPath=@proj/${jsLib}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let releaseOutput = runCLI(`release --first-release`);
|
||||||
|
expect(releaseOutput).toContain('Executing pre-version command');
|
||||||
|
releaseOutput = runCLI(`release --specifier 0.0.2 --yes`);
|
||||||
|
expect(releaseOutput).toMatchInlineSnapshot(`
|
||||||
|
NX Executing pre-version command
|
||||||
|
NX Running release version for project: {project-name}
|
||||||
|
{project-name} 🔍 Reading data for package "@proj/{project-name}" from dist/{project-name}/package.json
|
||||||
|
{project-name} 📄 Resolved the current version as 0.0.0 from git tag "v0.0.0".
|
||||||
|
{project-name} 📄 Using the provided version specifier "0.0.2".
|
||||||
|
{project-name} ✍️ New version 0.0.2 written to dist/{project-name}/package.json
|
||||||
|
"name": "@proj/{project-name}",
|
||||||
|
- "version": "0.0.1",
|
||||||
|
+ "version": "0.0.2",
|
||||||
|
"type": "commonjs",
|
||||||
|
}
|
||||||
|
+
|
||||||
|
NX Staging changed files with git
|
||||||
|
No files to stage. Skipping git add.
|
||||||
|
NX Generating an entry in CHANGELOG.md for v0.0.2
|
||||||
|
+ ## 0.0.2 (YYYY-MM-DD)
|
||||||
|
+
|
||||||
|
+ This was a version bump only, there were no code changes.
|
||||||
|
NX Staging changed files with git
|
||||||
|
NX Committing changes with git
|
||||||
|
NX Tagging commit with git
|
||||||
|
NX Running target nx-release-publish for project {project-name}:
|
||||||
|
- {project-name}
|
||||||
|
> nx run {project-name}:nx-release-publish
|
||||||
|
📦 @proj/{project-name}@0.0.2
|
||||||
|
=== Tarball Contents ===
|
||||||
|
248B README.md
|
||||||
|
XXXB package.json
|
||||||
|
38B src/index.d.ts
|
||||||
|
208B src/index.js
|
||||||
|
137B src/index.js.map
|
||||||
|
48B src/lib/{project-name}.d.ts
|
||||||
|
213B src/lib/{project-name}.js
|
||||||
|
210B src/lib/{project-name}.js.map
|
||||||
|
=== Tarball Details ===
|
||||||
|
name: @proj/{project-name}
|
||||||
|
version: 0.0.2
|
||||||
|
filename: proj-{project-name}-0.0.2.tgz
|
||||||
|
package size: XXXB
|
||||||
|
unpacked size: XXX.XXX kb
|
||||||
|
shasum: {SHASUM}
|
||||||
|
integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
|
total files: 8
|
||||||
|
Published to ${e2eRegistryUrl} with tag "latest"
|
||||||
|
NX Successfully ran target nx-release-publish for project {project-name}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
import {
|
||||||
|
cleanupProject,
|
||||||
|
newProject,
|
||||||
|
runCLI,
|
||||||
|
runCommandAsync,
|
||||||
|
uniq,
|
||||||
|
} from '@nx/e2e/utils';
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
|
||||||
|
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}')
|
||||||
|
.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
|
||||||
|
.split('\n')
|
||||||
|
.map((r) => r.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
test(val: string) {
|
||||||
|
return val != null && typeof val === 'string';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('release publishable libraries in workspace with ts solution setup', () => {
|
||||||
|
let e2eRegistryUrl: string;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
newProject({
|
||||||
|
packages: ['@nx/js'],
|
||||||
|
preset: 'ts',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Normalize git committer information so it is deterministic in snapshots
|
||||||
|
await runCommandAsync(`git config user.email "test@test.com"`);
|
||||||
|
await runCommandAsync(`git config user.name "Test"`);
|
||||||
|
// Create a baseline version tag
|
||||||
|
await runCommandAsync(`git tag v0.0.0`);
|
||||||
|
|
||||||
|
// We need a valid git origin to exist for the commit references to work (and later the test for createRelease)
|
||||||
|
await runCommandAsync(
|
||||||
|
`git remote add origin https://github.com/nrwl/fake-repo.git`
|
||||||
|
);
|
||||||
|
|
||||||
|
// This is the verdaccio instance that the e2e tests themselves are working from
|
||||||
|
e2eRegistryUrl = execSync('npm config get registry').toString().trim();
|
||||||
|
});
|
||||||
|
afterAll(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should be able to release publishable js library', async () => {
|
||||||
|
const jsLib = uniq('my-pkg-');
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/js:lib ${jsLib} --publishable --importPath=@proj/${jsLib}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let releaseOutput = runCLI(`release --first-release`);
|
||||||
|
expect(releaseOutput).toContain('Executing pre-version command');
|
||||||
|
releaseOutput = runCLI(`release --specifier 0.0.2 --yes`);
|
||||||
|
expect(releaseOutput).toMatchInlineSnapshot(`
|
||||||
|
NX Executing pre-version command
|
||||||
|
NX Running release version for project: @proj/{project-name}
|
||||||
|
@proj/{project-name} 🔍 Reading data for package "@proj/{project-name}" from {project-name}/package.json
|
||||||
|
@proj/{project-name} 📄 Resolved the current version as 0.0.1 from {project-name}/package.json
|
||||||
|
@proj/{project-name} 📄 Using the provided version specifier "0.0.2".
|
||||||
|
@proj/{project-name} ✍️ New version 0.0.2 written to {project-name}/package.json
|
||||||
|
"name": "@proj/{project-name}",
|
||||||
|
- "version": "0.0.1",
|
||||||
|
+ "version": "0.0.2",
|
||||||
|
"type": "module",
|
||||||
|
NX Updating PM lock file
|
||||||
|
NX Staging changed files with git
|
||||||
|
NX Generating an entry in CHANGELOG.md for v0.0.2
|
||||||
|
+ ## 0.0.2 (YYYY-MM-DD)
|
||||||
|
+
|
||||||
|
+ This was a version bump only, there were no code changes.
|
||||||
|
NX Staging changed files with git
|
||||||
|
NX Committing changes with git
|
||||||
|
NX Tagging commit with git
|
||||||
|
NX Running target nx-release-publish for project @proj/{project-name}:
|
||||||
|
- @proj/{project-name}
|
||||||
|
> nx run @proj/{project-name}:nx-release-publish
|
||||||
|
📦 @proj/{project-name}@0.0.2
|
||||||
|
=== Tarball Contents ===
|
||||||
|
138B README.md
|
||||||
|
76B dist/index.d.ts
|
||||||
|
125B dist/index.d.ts.map
|
||||||
|
41B dist/index.js
|
||||||
|
92B dist/lib/{project-name}.d.ts
|
||||||
|
161B dist/lib/{project-name}.d.ts.map
|
||||||
|
64B dist/lib/{project-name}.js
|
||||||
|
XXXB package.json
|
||||||
|
=== Tarball Details ===
|
||||||
|
name: @proj/{project-name}
|
||||||
|
version: 0.0.2
|
||||||
|
filename: proj-{project-name}-0.0.2.tgz
|
||||||
|
package size: XXXB
|
||||||
|
unpacked size: XXX.XXX kb
|
||||||
|
shasum: {SHASUM}
|
||||||
|
integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
|
total files: 8
|
||||||
|
Published to ${e2eRegistryUrl} with tag "latest"
|
||||||
|
NX Successfully ran target nx-release-publish for project @proj/{project-name}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -31,7 +31,6 @@ describe('lib', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tree = createTreeWithEmptyWorkspace();
|
tree = createTreeWithEmptyWorkspace();
|
||||||
tree.write('/.gitignore', '');
|
tree.write('/.gitignore', '');
|
||||||
tree.write('/.gitignore', '');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
|
|||||||
@ -5,14 +5,11 @@ import {
|
|||||||
formatFiles,
|
formatFiles,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
GeneratorCallback,
|
GeneratorCallback,
|
||||||
getPackageManagerCommand,
|
|
||||||
installPackagesTask,
|
installPackagesTask,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
names,
|
names,
|
||||||
offsetFromRoot,
|
offsetFromRoot,
|
||||||
output,
|
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
ProjectGraphProjectNode,
|
|
||||||
readNxJson,
|
readNxJson,
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
@ -69,6 +66,11 @@ import type {
|
|||||||
} from './schema';
|
} from './schema';
|
||||||
import { sortPackageJsonFields } from '../../utils/package-json/sort-fields';
|
import { sortPackageJsonFields } from '../../utils/package-json/sort-fields';
|
||||||
import { getImportPath } from '../../utils/get-import-path';
|
import { getImportPath } from '../../utils/get-import-path';
|
||||||
|
import {
|
||||||
|
addReleaseConfigForNonTsSolution,
|
||||||
|
addReleaseConfigForTsSolution,
|
||||||
|
releaseTasks,
|
||||||
|
} from './utils/add-release-config';
|
||||||
|
|
||||||
const defaultOutputDirectory = 'dist';
|
const defaultOutputDirectory = 'dist';
|
||||||
|
|
||||||
@ -115,10 +117,6 @@ export async function libraryGeneratorInternal(
|
|||||||
tasks.push(addProjectDependencies(tree, options));
|
tasks.push(addProjectDependencies(tree, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.publishable) {
|
|
||||||
tasks.push(await setupVerdaccio(tree, { ...options, skipFormat: true }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.bundler === 'rollup') {
|
if (options.bundler === 'rollup') {
|
||||||
const { configurationGenerator } = ensurePackage('@nx/rollup', nxVersion);
|
const { configurationGenerator } = ensurePackage('@nx/rollup', nxVersion);
|
||||||
await configurationGenerator(tree, {
|
await configurationGenerator(tree, {
|
||||||
@ -249,9 +247,7 @@ export async function libraryGeneratorInternal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.publishable) {
|
if (options.publishable) {
|
||||||
tasks.push(() => {
|
tasks.push(await releaseTasks(tree));
|
||||||
logNxReleaseDocsInfo();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always run install to link packages.
|
// Always run install to link packages.
|
||||||
@ -337,32 +333,20 @@ async function configureProject(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.publishable) {
|
if (options.publishable) {
|
||||||
if (!options.isUsingTsSolutionConfig) {
|
if (options.isUsingTsSolutionConfig) {
|
||||||
const packageRoot = joinPathFragments(
|
await addReleaseConfigForTsSolution(
|
||||||
defaultOutputDirectory,
|
tree,
|
||||||
'{projectRoot}'
|
options.name,
|
||||||
|
projectConfiguration
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await addReleaseConfigForNonTsSolution(
|
||||||
|
tree,
|
||||||
|
options.name,
|
||||||
|
projectConfiguration,
|
||||||
|
defaultOutputDirectory
|
||||||
);
|
);
|
||||||
|
|
||||||
projectConfiguration.targets ??= {};
|
|
||||||
projectConfiguration.targets['nx-release-publish'] = {
|
|
||||||
options: {
|
|
||||||
packageRoot,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
projectConfiguration.release = {
|
|
||||||
version: {
|
|
||||||
generatorOptions: {
|
|
||||||
packageRoot,
|
|
||||||
// using git tags to determine the current version is required here because
|
|
||||||
// the version in the package root is overridden with every build
|
|
||||||
currentVersionResolver: 'git-tag',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await addProjectToNxReleaseConfig(tree, options, projectConfiguration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.useProjectJson) {
|
if (!options.useProjectJson) {
|
||||||
@ -1259,120 +1243,6 @@ function determineEntryFields(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectsConfigMatchesProject(
|
|
||||||
projectsConfig: string | string[] | undefined,
|
|
||||||
project: ProjectGraphProjectNode
|
|
||||||
): boolean {
|
|
||||||
if (!projectsConfig) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof projectsConfig === 'string') {
|
|
||||||
projectsConfig = [projectsConfig];
|
|
||||||
}
|
|
||||||
|
|
||||||
const graph: Record<string, ProjectGraphProjectNode> = {
|
|
||||||
[project.name]: project,
|
|
||||||
};
|
|
||||||
|
|
||||||
const matchingProjects = findMatchingProjects(projectsConfig, graph);
|
|
||||||
|
|
||||||
return matchingProjects.includes(project.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addProjectToNxReleaseConfig(
|
|
||||||
tree: Tree,
|
|
||||||
options: NormalizedLibraryGeneratorOptions,
|
|
||||||
projectConfiguration: ProjectConfiguration
|
|
||||||
) {
|
|
||||||
const nxJson = readNxJson(tree);
|
|
||||||
|
|
||||||
const addPreVersionCommand = () => {
|
|
||||||
const pmc = getPackageManagerCommand();
|
|
||||||
|
|
||||||
nxJson.release = {
|
|
||||||
...nxJson.release,
|
|
||||||
version: {
|
|
||||||
preVersionCommand: `${pmc.dlx} nx run-many -t build`,
|
|
||||||
...nxJson.release?.version,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!nxJson.release || (!nxJson.release.projects && !nxJson.release.groups)) {
|
|
||||||
// skip adding any projects configuration since the new project should be
|
|
||||||
// automatically included by nx release's default project detection logic
|
|
||||||
addPreVersionCommand();
|
|
||||||
writeJson(tree, 'nx.json', nxJson);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const project: ProjectGraphProjectNode = {
|
|
||||||
name: options.name,
|
|
||||||
type: 'lib' as const,
|
|
||||||
data: {
|
|
||||||
root: projectConfiguration.root,
|
|
||||||
tags: projectConfiguration.tags,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (projectsConfigMatchesProject(nxJson.release.projects, project)) {
|
|
||||||
output.log({
|
|
||||||
title: `Project already included in existing release configuration`,
|
|
||||||
});
|
|
||||||
addPreVersionCommand();
|
|
||||||
writeJson(tree, 'nx.json', nxJson);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(nxJson.release.projects)) {
|
|
||||||
nxJson.release.projects.push(options.name);
|
|
||||||
addPreVersionCommand();
|
|
||||||
writeJson(tree, 'nx.json', nxJson);
|
|
||||||
output.log({
|
|
||||||
title: `Added project to existing release configuration`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nxJson.release.groups) {
|
|
||||||
const allGroups = Object.entries(nxJson.release.groups);
|
|
||||||
|
|
||||||
for (const [name, group] of allGroups) {
|
|
||||||
if (projectsConfigMatchesProject(group.projects, project)) {
|
|
||||||
addPreVersionCommand();
|
|
||||||
writeJson(tree, 'nx.json', nxJson);
|
|
||||||
return `Project already included in existing release configuration for group ${name}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.warn({
|
|
||||||
title: `Could not find a release group that includes ${options.name}`,
|
|
||||||
bodyLines: [
|
|
||||||
`Ensure that ${options.name} is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
addPreVersionCommand();
|
|
||||||
writeJson(tree, 'nx.json', nxJson);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof nxJson.release.projects === 'string') {
|
|
||||||
nxJson.release.projects = [nxJson.release.projects, options.name];
|
|
||||||
addPreVersionCommand();
|
|
||||||
writeJson(tree, 'nx.json', nxJson);
|
|
||||||
output.log({
|
|
||||||
title: `Added project to existing release configuration`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function logNxReleaseDocsInfo() {
|
|
||||||
output.log({
|
|
||||||
title: `📦 To learn how to publish this library, see https://nx.dev/core-features/manage-releases.`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function findRootJestPreset(tree: Tree): string | null {
|
function findRootJestPreset(tree: Tree): string | null {
|
||||||
const ext = ['js', 'cjs', 'mjs'].find((ext) =>
|
const ext = ['js', 'cjs', 'mjs'].find((ext) =>
|
||||||
tree.exists(`jest.preset.${ext}`)
|
tree.exists(`jest.preset.${ext}`)
|
||||||
|
|||||||
@ -0,0 +1,598 @@
|
|||||||
|
import {
|
||||||
|
getPackageManagerCommand,
|
||||||
|
readJson,
|
||||||
|
Tree,
|
||||||
|
updateJson,
|
||||||
|
output,
|
||||||
|
ProjectConfiguration,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';
|
||||||
|
import {
|
||||||
|
addReleaseConfigForNonTsSolution,
|
||||||
|
addReleaseConfigForTsSolution,
|
||||||
|
} from './add-release-config';
|
||||||
|
|
||||||
|
describe('add release config', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write('/.gitignore', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addReleaseConfigForNonTsSolution', () => {
|
||||||
|
it('should update the nx-release-publish target to specify dist/{projectRoot} as the package root', async () => {
|
||||||
|
const projectConfig: ProjectConfiguration = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
expect(projectConfig.targets?.['nx-release-publish']).toEqual({
|
||||||
|
options: {
|
||||||
|
packageRoot: 'dist/{projectRoot}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change preVersionCommand if it already exists', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
version: {
|
||||||
|
preVersionCommand: 'echo "hello world"',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
version: {
|
||||||
|
preVersionCommand: 'echo "hello world"',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add projects if no release config exists', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
delete json.release;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not add projects if release config exists but doesn't specify groups or projects", async () => {
|
||||||
|
const existingReleaseConfig = {
|
||||||
|
version: {
|
||||||
|
git: {},
|
||||||
|
},
|
||||||
|
changelog: {
|
||||||
|
projectChangelogs: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = existingReleaseConfig;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
...existingReleaseConfig,
|
||||||
|
version: {
|
||||||
|
...existingReleaseConfig.version,
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists as a string and matches the new project', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: '*',
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: '*',
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists as an array and matches the new project by name', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['something-else', 'my-lib'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['something-else', 'my-lib'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists and matches the new project by tag', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['tag:one'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig: ProjectConfiguration = {
|
||||||
|
root: 'libs/my-lib',
|
||||||
|
tags: ['one', 'two'],
|
||||||
|
};
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['tag:one'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists and matches the new project by root directory', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['packages/*'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'packages/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['packages/*'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should append project to projects if projects exists as an array, but doesn't already match the new project", async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['something-else'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['something-else', 'my-lib'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert projects to an array and append the new project to it if projects exists as a string, but doesn't already match the new project", async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: 'packages',
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['packages', 'my-lib'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists as groups config and matches the new project', async () => {
|
||||||
|
const existingReleaseConfig = {
|
||||||
|
groups: {
|
||||||
|
group1: {
|
||||||
|
projects: ['something-else'],
|
||||||
|
},
|
||||||
|
group2: {
|
||||||
|
projects: ['my-lib'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = existingReleaseConfig;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
groups: existingReleaseConfig.groups,
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should warn the user if their defined groups don't match the new project", async () => {
|
||||||
|
const outputSpy = jest
|
||||||
|
.spyOn(output, 'warn')
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return undefined as never;
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingReleaseConfig = {
|
||||||
|
groups: {
|
||||||
|
group1: {
|
||||||
|
projects: ['something-else'],
|
||||||
|
},
|
||||||
|
group2: {
|
||||||
|
projects: ['other-thing'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = existingReleaseConfig;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForNonTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
groups: existingReleaseConfig.groups,
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(outputSpy).toHaveBeenCalledWith({
|
||||||
|
title: `Could not find a release group that includes my-lib`,
|
||||||
|
bodyLines: [
|
||||||
|
`Ensure that my-lib is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
outputSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addReleaseConfigForTsSolution', () => {
|
||||||
|
it('should not update set nx-release-publish target', async () => {
|
||||||
|
const projectConfig: ProjectConfiguration = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
expect(projectConfig.targets?.['nx-release-publish']).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change preVersionCommand if it already exists', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
version: {
|
||||||
|
preVersionCommand: 'echo "hello world"',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
version: {
|
||||||
|
preVersionCommand: 'echo "hello world"',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add projects if no release config exists', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
delete json.release;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not add projects if release config exists but doesn't specify groups or projects", async () => {
|
||||||
|
const existingReleaseConfig = {
|
||||||
|
version: {
|
||||||
|
git: {},
|
||||||
|
},
|
||||||
|
changelog: {
|
||||||
|
projectChangelogs: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = existingReleaseConfig;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
...existingReleaseConfig,
|
||||||
|
version: {
|
||||||
|
...existingReleaseConfig.version,
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists as a string and matches the new project', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: '*',
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: '*',
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists as an array and matches the new project by name', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['something-else', 'my-lib'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['something-else', 'my-lib'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists and matches the new project by tag', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['tag:one'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig: ProjectConfiguration = {
|
||||||
|
root: 'libs/my-lib',
|
||||||
|
tags: ['one', 'two'],
|
||||||
|
};
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['tag:one'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists and matches the new project by root directory', async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['packages/*'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'packages/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['packages/*'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should append project to projects if projects exists as an array, but doesn't already match the new project", async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: ['something-else'],
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['something-else', 'my-lib'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert projects to an array and append the new project to it if projects exists as a string, but doesn't already match the new project", async () => {
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = {
|
||||||
|
projects: 'packages',
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
projects: ['packages', 'my-lib'],
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change projects if it already exists as groups config and matches the new project', async () => {
|
||||||
|
const existingReleaseConfig = {
|
||||||
|
groups: {
|
||||||
|
group1: {
|
||||||
|
projects: ['something-else'],
|
||||||
|
},
|
||||||
|
group2: {
|
||||||
|
projects: ['my-lib'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = existingReleaseConfig;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
groups: existingReleaseConfig.groups,
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should warn the user if their defined groups don't match the new project", async () => {
|
||||||
|
const outputSpy = jest
|
||||||
|
.spyOn(output, 'warn')
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return undefined as never;
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingReleaseConfig = {
|
||||||
|
groups: {
|
||||||
|
group1: {
|
||||||
|
projects: ['something-else'],
|
||||||
|
},
|
||||||
|
group2: {
|
||||||
|
projects: ['other-thing'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateJson(tree, 'nx.json', (json) => {
|
||||||
|
json.release = existingReleaseConfig;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfig = { root: 'libs/my-lib' };
|
||||||
|
await addReleaseConfigForTsSolution(tree, 'my-lib', projectConfig);
|
||||||
|
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.release).toEqual({
|
||||||
|
groups: existingReleaseConfig.groups,
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${
|
||||||
|
getPackageManagerCommand().dlx
|
||||||
|
} nx run-many -t build`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(outputSpy).toHaveBeenCalledWith({
|
||||||
|
title: `Could not find a release group that includes my-lib`,
|
||||||
|
bodyLines: [
|
||||||
|
`Ensure that my-lib is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
outputSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
183
packages/js/src/generators/library/utils/add-release-config.ts
Normal file
183
packages/js/src/generators/library/utils/add-release-config.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import {
|
||||||
|
GeneratorCallback,
|
||||||
|
getPackageManagerCommand,
|
||||||
|
joinPathFragments,
|
||||||
|
output,
|
||||||
|
ProjectConfiguration,
|
||||||
|
ProjectGraphProjectNode,
|
||||||
|
readNxJson,
|
||||||
|
runTasksInSerial,
|
||||||
|
Tree,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { findMatchingProjects } from 'nx/src/utils/find-matching-projects';
|
||||||
|
import setupVerdaccio from '../../setup-verdaccio/generator';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds release option in nx.json to build the project before versioning
|
||||||
|
*/
|
||||||
|
export async function addReleaseConfigForTsSolution(
|
||||||
|
tree: Tree,
|
||||||
|
projectName: string,
|
||||||
|
projectConfiguration: ProjectConfiguration
|
||||||
|
): Promise<void> {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
|
||||||
|
const addPreVersionCommand = () => {
|
||||||
|
const pmc = getPackageManagerCommand();
|
||||||
|
|
||||||
|
nxJson.release = {
|
||||||
|
...nxJson.release,
|
||||||
|
version: {
|
||||||
|
preVersionCommand: `${pmc.dlx} nx run-many -t build`,
|
||||||
|
...nxJson.release?.version,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// if the release configuration does not exist, it will be created
|
||||||
|
if (!nxJson.release || (!nxJson.release.projects && !nxJson.release.groups)) {
|
||||||
|
// skip adding any projects configuration since the new project should be
|
||||||
|
// automatically included by nx release's default project detection logic
|
||||||
|
addPreVersionCommand();
|
||||||
|
writeJson(tree, 'nx.json', nxJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const project: ProjectGraphProjectNode = {
|
||||||
|
name: projectName,
|
||||||
|
type: 'lib' as const,
|
||||||
|
data: {
|
||||||
|
root: projectConfiguration.root,
|
||||||
|
tags: projectConfiguration.tags,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// if the project is already included in the release configuration, it will not be added again
|
||||||
|
if (projectsConfigMatchesProject(nxJson.release.projects, project)) {
|
||||||
|
output.log({
|
||||||
|
title: `Project already included in existing release configuration`,
|
||||||
|
});
|
||||||
|
addPreVersionCommand();
|
||||||
|
writeJson(tree, 'nx.json', nxJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the release configuration is a string, it will be converted to an array and added to it
|
||||||
|
if (Array.isArray(nxJson.release.projects)) {
|
||||||
|
nxJson.release.projects.push(projectName);
|
||||||
|
addPreVersionCommand();
|
||||||
|
writeJson(tree, 'nx.json', nxJson);
|
||||||
|
output.log({
|
||||||
|
title: `Added project to existing release configuration`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxJson.release.groups) {
|
||||||
|
const allGroups = Object.entries(nxJson.release.groups);
|
||||||
|
|
||||||
|
for (const [name, group] of allGroups) {
|
||||||
|
if (projectsConfigMatchesProject(group.projects, project)) {
|
||||||
|
addPreVersionCommand();
|
||||||
|
writeJson(tree, 'nx.json', nxJson);
|
||||||
|
output.log({
|
||||||
|
title: `Project already included in existing release configuration for group ${name}`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.warn({
|
||||||
|
title: `Could not find a release group that includes ${projectName}`,
|
||||||
|
bodyLines: [
|
||||||
|
`Ensure that ${projectName} is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
addPreVersionCommand();
|
||||||
|
writeJson(tree, 'nx.json', nxJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof nxJson.release.projects === 'string') {
|
||||||
|
nxJson.release.projects = [nxJson.release.projects, projectName];
|
||||||
|
addPreVersionCommand();
|
||||||
|
writeJson(tree, 'nx.json', nxJson);
|
||||||
|
output.log({
|
||||||
|
title: `Added project to existing release configuration`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add release configuration for non-ts solution projects
|
||||||
|
* Add release option in project.json and add packageRoot to nx-release-publish target
|
||||||
|
*/
|
||||||
|
export async function addReleaseConfigForNonTsSolution(
|
||||||
|
tree: Tree,
|
||||||
|
projectName: string,
|
||||||
|
projectConfiguration: ProjectConfiguration,
|
||||||
|
defaultOutputDirectory: string = 'dist'
|
||||||
|
) {
|
||||||
|
const packageRoot = joinPathFragments(
|
||||||
|
defaultOutputDirectory,
|
||||||
|
'{projectRoot}'
|
||||||
|
);
|
||||||
|
|
||||||
|
projectConfiguration.targets ??= {};
|
||||||
|
projectConfiguration.targets['nx-release-publish'] = {
|
||||||
|
options: {
|
||||||
|
packageRoot,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
projectConfiguration.release = {
|
||||||
|
version: {
|
||||||
|
generatorOptions: {
|
||||||
|
packageRoot,
|
||||||
|
// using git tags to determine the current version is required here because
|
||||||
|
// the version in the package root is overridden with every build
|
||||||
|
currentVersionResolver: 'git-tag',
|
||||||
|
fallbackCurrentVersionResolver: 'disk',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await addReleaseConfigForTsSolution(tree, projectName, projectConfiguration);
|
||||||
|
|
||||||
|
return projectConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
function projectsConfigMatchesProject(
|
||||||
|
projectsConfig: string | string[] | undefined,
|
||||||
|
project: ProjectGraphProjectNode
|
||||||
|
): boolean {
|
||||||
|
if (!projectsConfig) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof projectsConfig === 'string') {
|
||||||
|
projectsConfig = [projectsConfig];
|
||||||
|
}
|
||||||
|
|
||||||
|
const graph: Record<string, ProjectGraphProjectNode> = {
|
||||||
|
[project.name]: project,
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchingProjects = findMatchingProjects(projectsConfig, graph);
|
||||||
|
|
||||||
|
return matchingProjects.includes(project.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function releaseTasks(tree: Tree): Promise<GeneratorCallback> {
|
||||||
|
return runTasksInSerial(
|
||||||
|
await setupVerdaccio(tree, { skipFormat: true }),
|
||||||
|
() => logNxReleaseDocsInfo()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logNxReleaseDocsInfo() {
|
||||||
|
output.log({
|
||||||
|
title: `📦 To learn how to publish this library, see https://nx.dev/core-features/manage-releases.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -110,10 +110,7 @@ async function execAsync(command: string, cwd: string): Promise<string> {
|
|||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
exec(command, { cwd, windowsHide: false }, (error, stdout, stderr) => {
|
exec(command, { cwd, windowsHide: false }, (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return reject(error);
|
return reject((stderr ? `${stderr}\n` : '') + error);
|
||||||
}
|
|
||||||
if (stderr) {
|
|
||||||
return reject(stderr);
|
|
||||||
}
|
}
|
||||||
return resolve(stdout.trim());
|
return resolve(stdout.trim());
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user