Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix(core): various release fixes #20478

Merged
merged 5 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions e2e/release/src/release.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ describe('nx release', () => {



> NX Previewing a Github release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
> NX Previewing a GitHub release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0


+ ## 1000.0.0-next.0
Expand All @@ -690,7 +690,7 @@ describe('nx release', () => {
+ - an awesome new feature ([{COMMIT_SHA}](https://github.com/nrwl/fake-repo/commit/{COMMIT_SHA}))


> NX Previewing a Github release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
> NX Previewing a GitHub release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0


+ ## 1000.0.0-next.0
Expand All @@ -701,7 +701,7 @@ describe('nx release', () => {
+ - an awesome new feature ([{COMMIT_SHA}](https://github.com/nrwl/fake-repo/commit/{COMMIT_SHA}))


> NX Previewing a Github release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
> NX Previewing a GitHub release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0


+ ## 1000.0.0-next.0
Expand Down
44 changes: 39 additions & 5 deletions packages/nx/changelog-renderer/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
import type { GitCommit } from '../src/command-line/release/utils/git';
import defaultChangelogRenderer from './index';

jest.mock('../src/project-graph/file-map-utils', () => ({
createFileMapUsingProjectGraph: jest.fn().mockImplementation(() => {
return Promise.resolve({
allWorkspaceFiles: [],
fileMap: {
nonProjectFiles: [],
projectFileMap: {
'pkg-a': [
{
file: 'packages/pkg-a/src/index.ts',
},
],
'pkg-b': [
{
file: 'packages/pkg-b/src/index.ts',
},
],
},
},
});
}),
}));

describe('defaultChangelogRenderer()', () => {
const projectGraph = {
nodes: {},
} as any;
const commits: GitCommit[] = [
{
message: 'fix: all packages fixed',
Expand All @@ -27,7 +53,10 @@ describe('defaultChangelogRenderer()', () => {
},
],
isBreaking: false,
affectedFiles: [],
affectedFiles: [
'packages/pkg-a/src/index.ts',
'packages/pkg-b/src/index.ts',
],
},
{
message: 'feat(pkg-b): and another new capability',
Expand All @@ -53,7 +82,7 @@ describe('defaultChangelogRenderer()', () => {
},
],
isBreaking: false,
affectedFiles: [],
affectedFiles: ['packages/pkg-b/src/index.ts'],
},
{
message: 'feat(pkg-a): new hotness',
Expand All @@ -79,7 +108,7 @@ describe('defaultChangelogRenderer()', () => {
},
],
isBreaking: false,
affectedFiles: [],
affectedFiles: ['packages/pkg-a/src/index.ts'],
},
{
message: 'feat(pkg-b): brand new thing',
Expand All @@ -105,7 +134,7 @@ describe('defaultChangelogRenderer()', () => {
},
],
isBreaking: false,
affectedFiles: [],
affectedFiles: ['packages/pkg-b/src/index.ts'],
},
{
message: 'fix(pkg-a): squashing bugs',
Expand All @@ -131,13 +160,14 @@ describe('defaultChangelogRenderer()', () => {
},
],
isBreaking: false,
affectedFiles: [],
affectedFiles: ['packages/pkg-a/src/index.ts'],
},
];

describe('workspaceChangelog', () => {
it('should generate markdown for all projects by organizing commits by type, then grouped by scope within the type (sorted alphabetically), then chronologically within the scope group', async () => {
const markdown = await defaultChangelogRenderer({
projectGraph,
commits,
releaseVersion: 'v1.1.0',
project: null,
Expand Down Expand Up @@ -169,6 +199,7 @@ describe('defaultChangelogRenderer()', () => {

it('should not generate a Thank You section when changelogRenderOptions.includeAuthors is false', async () => {
const markdown = await defaultChangelogRenderer({
projectGraph,
commits,
releaseVersion: 'v1.1.0',
project: null,
Expand Down Expand Up @@ -198,6 +229,7 @@ describe('defaultChangelogRenderer()', () => {
describe('project level configs', () => {
it('should generate markdown for the given project by organizing commits by type, then chronologically', async () => {
const otherOpts = {
projectGraph,
commits,
releaseVersion: 'v1.1.0',
entryWhenNoChanges: false as const,
Expand Down Expand Up @@ -288,6 +320,7 @@ describe('defaultChangelogRenderer()', () => {
describe('entryWhenNoChanges', () => {
it('should respect the entryWhenNoChanges option for the workspace changelog', async () => {
const otherOpts = {
projectGraph,
commits: [],
releaseVersion: 'v1.1.0',
project: null, // workspace changelog
Expand Down Expand Up @@ -317,6 +350,7 @@ describe('defaultChangelogRenderer()', () => {

it('should respect the entryWhenNoChanges option for project changelogs', async () => {
const otherOpts = {
projectGraph,
commits: [],
releaseVersion: 'v1.1.0',
project: 'pkg-a',
Expand Down
21 changes: 14 additions & 7 deletions packages/nx/changelog-renderer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
RepoSlug,
formatReferences,
} from '../src/command-line/release/utils/github';
import { getCommitsRelevantToProjects } from '../src/command-line/release/utils/shared';
import type { ProjectGraph } from '../src/config/project-graph';

// axios types and values don't seem to match
import _axios = require('axios');
Expand All @@ -19,13 +21,15 @@ export type ChangelogRenderOptions = Record<string, unknown>;
* and returns a string, or a Promise of a string of changelog contents (usually markdown).
*
* @param {Object} config The configuration object for the ChangelogRenderer
* @param {ProjectGraph} config.projectGraph The project graph for the workspace
* @param {GitCommit[]} config.commits The collection of extracted commits to generate a changelog for
* @param {string} config.releaseVersion The version that is being released
* @param {string | null} config.project The name of specific project to generate a changelog for, or `null` if the overall workspace changelog
* @param {string | false} config.entryWhenNoChanges The (already interpolated) string to use as the changelog entry when there are no changes, or `false` if no entry should be generated
* @param {ChangelogRenderOptions} config.changelogRenderOptions The options specific to the ChangelogRenderer implementation
*/
export type ChangelogRenderer = (config: {
projectGraph: ProjectGraph;
commits: GitCommit[];
releaseVersion: string;
project: string | null;
Expand All @@ -51,6 +55,7 @@ export interface DefaultChangelogRenderOptions extends ChangelogRenderOptions {
* from the given commits and other metadata.
*/
const defaultChangelogRenderer: ChangelogRenderer = async ({
projectGraph,
commits,
releaseVersion,
project,
Expand Down Expand Up @@ -129,13 +134,14 @@ const defaultChangelogRenderer: ChangelogRenderer = async ({
}
} else {
// project level changelog
const scopeGroups = groupBy(commits, 'scope');

// Treat unscoped commits as "global", and therefore also relevant to include in the project level changelog
const unscopedCommits = scopeGroups[''] || [];
const relevantCommits = await getCommitsRelevantToProjects(
projectGraph,
commits,
[project]
);

// Generating for a named project, but that project has no changes in the current set of commits, exit early
if (!scopeGroups[project] && unscopedCommits.length === 0) {
// Generating for a named project, but that project has no relevant changes in the current set of commits, exit early
if (relevantCommits.length === 0) {
if (entryWhenNoChanges) {
markdownLines.push(
'',
Expand All @@ -149,7 +155,8 @@ const defaultChangelogRenderer: ChangelogRenderer = async ({
markdownLines.push('', `## ${releaseVersion}`, '');

const typeGroups = groupBy(
[...(scopeGroups[project] || []), ...unscopedCommits],
// Sort the relevant commits to have the unscoped commits first, before grouping by type
relevantCommits.sort((a, b) => (b.scope ? 1 : 0) - (a.scope ? 1 : 0)),
'type'
);
for (const type of Object.keys(commitTypes)) {
Expand Down
44 changes: 27 additions & 17 deletions packages/nx/src/command-line/release/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { valid } from 'semver';
import { dirSync } from 'tmp';
import type { ChangelogRenderer } from '../../../changelog-renderer';
import { readNxJson } from '../../config/nx-json';
import { ProjectGraphProjectNode } from '../../config/project-graph';
import {
ProjectGraph,
ProjectGraphProjectNode,
} from '../../config/project-graph';
import { FsTree, Tree } from '../../generators/tree';
import { registerTsProject } from '../../plugins/js/utils/register';
import { createProjectGraphAsync } from '../../project-graph/project-graph';
Expand Down Expand Up @@ -188,6 +191,7 @@ export async function releaseChangelog(
await generateChangelogForWorkspace(
tree,
args,
projectGraph,
nxReleaseConfig,
workspaceChangelogVersion,
commits,
Expand All @@ -206,6 +210,7 @@ export async function releaseChangelog(
await generateChangelogForProjects(
tree,
args,
projectGraph,
commits,
projectsVersionData,
postGitTasks,
Expand Down Expand Up @@ -236,6 +241,7 @@ export async function releaseChangelog(
await generateChangelogForProjects(
tree,
args,
projectGraph,
commits,
projectsVersionData,
postGitTasks,
Expand Down Expand Up @@ -404,6 +410,7 @@ function resolveChangelogRenderer(
async function generateChangelogForWorkspace(
tree: Tree,
args: ChangelogOptions,
projectGraph: ProjectGraph,
nxReleaseConfig: NxReleaseConfig,
workspaceChangelogVersion: (string | null) | undefined,
commits: GitCommit[],
Expand Down Expand Up @@ -448,11 +455,11 @@ async function generateChangelogForWorkspace(
releaseTagPattern: nxReleaseConfig.releaseTagPattern,
});

// We are either creating/previewing a changelog file, a Github release, or both
// We are either creating/previewing a changelog file, a GitHub release, or both
let logTitle = dryRun ? 'Previewing a' : 'Generating a';
switch (true) {
case interpolatedTreePath && config.createRelease === 'github':
logTitle += ` Github release and an entry in ${interpolatedTreePath} for ${chalk.white(
logTitle += ` GitHub release and an entry in ${interpolatedTreePath} for ${chalk.white(
releaseVersion.gitTag
)}`;
break;
Expand All @@ -462,7 +469,7 @@ async function generateChangelogForWorkspace(
)}`;
break;
case config.createRelease === 'github':
logTitle += ` Github release for ${chalk.white(releaseVersion.gitTag)}`;
logTitle += ` GitHub release for ${chalk.white(releaseVersion.gitTag)}`;
}

output.log({
Expand All @@ -475,6 +482,7 @@ async function generateChangelogForWorkspace(
: undefined;

let contents = await changelogRenderer({
projectGraph,
commits,
releaseVersion: releaseVersion.rawVersion,
project: null,
Expand All @@ -501,7 +509,7 @@ async function generateChangelogForWorkspace(

/**
* The exact logic we use for printing the summary/diff to the user is dependent upon whether they are creating
* a changelog file, a Github release, or both.
* a changelog file, a GitHub release, or both.
*/
let printSummary = () => {};
const noDiffInChangelogMessage = chalk.yellow(
Expand Down Expand Up @@ -544,9 +552,9 @@ async function generateChangelogForWorkspace(
if (config.createRelease === 'github') {
if (!githubRepoSlug) {
output.error({
title: `Unable to create a Github release because the Github repo slug could not be determined.`,
title: `Unable to create a GitHub release because the GitHub repo slug could not be determined.`,
bodyLines: [
`Please ensure you have a valid Github remote configured. You can run \`git remote -v\` to list your current remotes.`,
`Please ensure you have a valid GitHub remote configured. You can run \`git remote -v\` to list your current remotes.`,
],
});
process.exit(1);
Expand All @@ -567,9 +575,9 @@ async function generateChangelogForWorkspace(
} catch (err) {
if (err.response?.status === 401) {
output.error({
title: `Unable to resolve data via the Github API. You can use any of the following options to resolve this:`,
title: `Unable to resolve data via the GitHub API. You can use any of the following options to resolve this:`,
bodyLines: [
'- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid Github token with `repo` scope',
'- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid GitHub token with `repo` scope',
'- Have an active session via the official gh CLI tool (https://cli.github.com) in your current terminal',
],
});
Expand Down Expand Up @@ -640,6 +648,7 @@ async function generateChangelogForWorkspace(
async function generateChangelogForProjects(
tree: Tree,
args: ChangelogOptions,
projectGraph: ProjectGraph,
commits: GitCommit[],
projectsVersionData: VersionData,
postGitTasks: PostGitTask[],
Expand Down Expand Up @@ -684,11 +693,11 @@ async function generateChangelogForProjects(
projectName: project.name,
});

// We are either creating/previewing a changelog file, a Github release, or both
// We are either creating/previewing a changelog file, a GitHub release, or both
let logTitle = dryRun ? 'Previewing a' : 'Generating a';
switch (true) {
case interpolatedTreePath && config.createRelease === 'github':
logTitle += ` Github release and an entry in ${interpolatedTreePath} for ${chalk.white(
logTitle += ` GitHub release and an entry in ${interpolatedTreePath} for ${chalk.white(
releaseVersion.gitTag
)}`;
break;
Expand All @@ -698,7 +707,7 @@ async function generateChangelogForProjects(
)}`;
break;
case config.createRelease === 'github':
logTitle += ` Github release for ${chalk.white(releaseVersion.gitTag)}`;
logTitle += ` GitHub release for ${chalk.white(releaseVersion.gitTag)}`;
}

output.log({
Expand All @@ -711,6 +720,7 @@ async function generateChangelogForProjects(
: undefined;

let contents = await changelogRenderer({
projectGraph,
commits,
releaseVersion: releaseVersion.rawVersion,
project: project.name,
Expand Down Expand Up @@ -744,7 +754,7 @@ async function generateChangelogForProjects(

/**
* The exact logic we use for printing the summary/diff to the user is dependent upon whether they are creating
* a changelog file, a Github release, or both.
* a changelog file, a GitHub release, or both.
*/
let printSummary = () => {};
const noDiffInChangelogMessage = chalk.yellow(
Expand Down Expand Up @@ -794,9 +804,9 @@ async function generateChangelogForProjects(
if (config.createRelease === 'github') {
if (!githubRepoSlug) {
output.error({
title: `Unable to create a Github release because the Github repo slug could not be determined.`,
title: `Unable to create a GitHub release because the GitHub repo slug could not be determined.`,
bodyLines: [
`Please ensure you have a valid Github remote configured. You can run \`git remote -v\` to list your current remotes.`,
`Please ensure you have a valid GitHub remote configured. You can run \`git remote -v\` to list your current remotes.`,
],
});
process.exit(1);
Expand All @@ -817,9 +827,9 @@ async function generateChangelogForProjects(
} catch (err) {
if (err.response?.status === 401) {
output.error({
title: `Unable to resolve data via the Github API. You can use any of the following options to resolve this:`,
title: `Unable to resolve data via the GitHub API. You can use any of the following options to resolve this:`,
bodyLines: [
'- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid Github token with `repo` scope',
'- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid GitHub token with `repo` scope',
'- Have an active session via the official gh CLI tool (https://cli.github.com) in your current terminal',
],
});
Expand Down
1 change: 0 additions & 1 deletion packages/nx/src/command-line/release/utils/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* https://github.com/unjs/changelogen
*/
import { interpolate } from '../../../tasks-runner/utils';
import { output } from '../../../utils/output';
import { execCommand } from './exec-command';

export interface GitCommitAuthor {
Expand Down
Loading