fix(release): move github release creation to git tasks (#21510)
This commit is contained in:
parent
f4dd4403f5
commit
b625a79cca
@ -9,7 +9,7 @@ Description of a file change in the Nx virtual file system/
|
||||
- [content](../../devkit/documents/FileChange#content): Buffer
|
||||
- [options](../../devkit/documents/FileChange#options): TreeWriteOptions
|
||||
- [path](../../devkit/documents/FileChange#path): string
|
||||
- [type](../../devkit/documents/FileChange#type): "DELETE" | "CREATE" | "UPDATE"
|
||||
- [type](../../devkit/documents/FileChange#type): "CREATE" | "DELETE" | "UPDATE"
|
||||
|
||||
## Properties
|
||||
|
||||
@ -39,6 +39,6 @@ Path relative to the workspace root
|
||||
|
||||
### type
|
||||
|
||||
• **type**: `"DELETE"` \| `"CREATE"` \| `"UPDATE"`
|
||||
• **type**: `"CREATE"` \| `"DELETE"` \| `"UPDATE"`
|
||||
|
||||
Type of change: 'CREATE' | 'DELETE' | 'UPDATE'
|
||||
|
||||
@ -105,16 +105,12 @@ import * as yargs from 'yargs';
|
||||
verbose: options.verbose,
|
||||
});
|
||||
|
||||
// The returned number value from releaseChangelog will be non-zero if something went wrong
|
||||
const changelogStatus = await releaseChangelog({
|
||||
await releaseChangelog({
|
||||
versionData: projectsVersionData,
|
||||
version: workspaceVersion,
|
||||
dryRun: options.dryRun,
|
||||
verbose: options.verbose,
|
||||
});
|
||||
if (changelogStatus !== 0) {
|
||||
process.exit(changelogStatus);
|
||||
}
|
||||
|
||||
// The returned number value from releasePublish will be zero if all projects are published successfully, non-zero if not
|
||||
const publishStatus = await releasePublish({
|
||||
|
||||
165
e2e/release/src/create-github-release.test.ts
Normal file
165
e2e/release/src/create-github-release.test.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import { NxJsonConfiguration } from '@nx/devkit';
|
||||
import {
|
||||
cleanupProject,
|
||||
newProject,
|
||||
runCLI,
|
||||
runCommandAsync,
|
||||
uniq,
|
||||
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 create github release', () => {
|
||||
let pkg1: string;
|
||||
let pkg2: string;
|
||||
let pkg3: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
newProject({
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
packages: ['@nx/js'],
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
// 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"`);
|
||||
|
||||
// update my-pkg-1 with a fix commit
|
||||
updateJson(`${pkg1}/package.json`, (json) => ({
|
||||
...json,
|
||||
license: 'MIT',
|
||||
}));
|
||||
await runCommandAsync(`git add ${pkg1}/package.json`);
|
||||
await runCommandAsync(`git commit -m "fix(${pkg1}): fix 1"`);
|
||||
|
||||
// update my-pkg-2 with a breaking change
|
||||
updateJson(`${pkg2}/package.json`, (json) => ({
|
||||
...json,
|
||||
license: 'GNU GPLv3',
|
||||
}));
|
||||
await runCommandAsync(`git add ${pkg2}/package.json`);
|
||||
await runCommandAsync(`git commit -m "feat(${pkg2})!: breaking change 2"`);
|
||||
|
||||
// update my-pkg-3 with a feature commit
|
||||
updateJson(`${pkg3}/package.json`, (json) => ({
|
||||
...json,
|
||||
license: 'GNU GPLv3',
|
||||
}));
|
||||
await runCommandAsync(`git add ${pkg3}/package.json`);
|
||||
await runCommandAsync(`git commit -m "feat(${pkg3}): feat 3"`);
|
||||
|
||||
// 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`
|
||||
);
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
it('should create github release for the first release', async () => {
|
||||
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
|
||||
nxJson.release = {
|
||||
changelog: {
|
||||
workspaceChangelog: {
|
||||
createRelease: 'github',
|
||||
},
|
||||
},
|
||||
};
|
||||
return nxJson;
|
||||
});
|
||||
const result = runCLI('release patch -d --first-release --verbose');
|
||||
|
||||
expect(
|
||||
result.match(new RegExp(`> NX Pushing to git remote`, 'g')).length
|
||||
).toEqual(1);
|
||||
expect(
|
||||
result.match(new RegExp(`> NX Creating GitHub Release`, 'g')).length
|
||||
).toEqual(1);
|
||||
|
||||
// should have two occurrences of each - one for the changelog file, one for the github release
|
||||
expect(result.match(new RegExp(`### 🚀 Features`, 'g')).length).toEqual(2);
|
||||
expect(result.match(new RegExp(`### 🩹 Fixes`, 'g')).length).toEqual(2);
|
||||
expect(
|
||||
result.match(new RegExp(`#### ⚠️ Breaking Changes`, 'g')).length
|
||||
).toEqual(2);
|
||||
});
|
||||
|
||||
it('should create github releases for all independent packages', async () => {
|
||||
updateJson<NxJsonConfiguration>('nx.json', (nxJson) => {
|
||||
nxJson.release = {
|
||||
projectsRelationship: 'independent',
|
||||
version: {
|
||||
conventionalCommits: true,
|
||||
},
|
||||
changelog: {
|
||||
projectChangelogs: {
|
||||
file: false,
|
||||
createRelease: 'github',
|
||||
},
|
||||
},
|
||||
};
|
||||
return nxJson;
|
||||
});
|
||||
|
||||
const result = runCLI('release -d --first-release --verbose');
|
||||
|
||||
expect(
|
||||
result.match(new RegExp(`> NX Pushing to git remote`, 'g')).length
|
||||
).toEqual(1);
|
||||
expect(
|
||||
result.match(new RegExp(`> NX Creating GitHub Release`, 'g')).length
|
||||
).toEqual(3);
|
||||
|
||||
// should have one occurrence of each because files are disabled
|
||||
expect(result.match(new RegExp(`### 🚀 Features`, 'g')).length).toEqual(2);
|
||||
expect(result.match(new RegExp(`### 🩹 Fixes`, 'g')).length).toEqual(1);
|
||||
expect(
|
||||
result.match(new RegExp(`#### ⚠️ Breaking Changes`, 'g')).length
|
||||
).toEqual(1);
|
||||
});
|
||||
});
|
||||
@ -726,7 +726,7 @@ ${JSON.stringify(
|
||||
|
||||
|
||||
|
||||
> NX Previewing a GitHub release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
|
||||
> NX Previewing an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
|
||||
|
||||
|
||||
+ ## 1000.0.0-next.0
|
||||
@ -734,7 +734,7 @@ ${JSON.stringify(
|
||||
+ This was a version bump only for {project-name} to align it with other projects, there were no code changes.
|
||||
|
||||
|
||||
> NX Previewing a GitHub release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
|
||||
> NX Previewing an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
|
||||
|
||||
|
||||
+ ## 1000.0.0-next.0
|
||||
@ -742,7 +742,7 @@ ${JSON.stringify(
|
||||
+ This was a version bump only for {project-name} to align it with other projects, there were no code changes.
|
||||
|
||||
|
||||
> NX Previewing a GitHub release and an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
|
||||
> NX Previewing an entry in {project-name}/CHANGELOG.md for v1000.0.0-next.0
|
||||
|
||||
|
||||
+ ## 1000.0.0-next.0
|
||||
@ -756,6 +756,33 @@ ${JSON.stringify(
|
||||
> NX Tagging commit with git
|
||||
|
||||
|
||||
> NX Pushing to git remote
|
||||
|
||||
|
||||
> NX Creating GitHub Release
|
||||
|
||||
|
||||
+ ## 1000.0.0-next.0
|
||||
+
|
||||
+ This was a version bump only for {project-name} to align it with other projects, there were no code changes.
|
||||
|
||||
|
||||
> NX Creating GitHub Release
|
||||
|
||||
|
||||
+ ## 1000.0.0-next.0
|
||||
+
|
||||
+ This was a version bump only for {project-name} to align it with other projects, there were no code changes.
|
||||
|
||||
|
||||
> NX Creating GitHub Release
|
||||
|
||||
|
||||
+ ## 1000.0.0-next.0
|
||||
+
|
||||
+ This was a version bump only for {project-name} to align it with other projects, there were no code changes.
|
||||
|
||||
|
||||
`);
|
||||
|
||||
// port and process cleanup
|
||||
|
||||
@ -3,7 +3,10 @@ import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { valid } from 'semver';
|
||||
import { dirSync } from 'tmp';
|
||||
import type { ChangelogRenderer } from '../../../release/changelog-renderer';
|
||||
import { readNxJson } from '../../config/nx-json';
|
||||
import {
|
||||
NxReleaseChangelogConfiguration,
|
||||
readNxJson,
|
||||
} from '../../config/nx-json';
|
||||
import {
|
||||
ProjectGraph,
|
||||
ProjectGraphProjectNode,
|
||||
@ -38,17 +41,10 @@ import {
|
||||
gitTag,
|
||||
parseCommits,
|
||||
} from './utils/git';
|
||||
import {
|
||||
GithubRelease,
|
||||
GithubRequestConfig,
|
||||
createOrUpdateGithubRelease,
|
||||
getGitHubRepoSlug,
|
||||
getGithubReleaseByTag,
|
||||
resolveGithubToken,
|
||||
} from './utils/github';
|
||||
import { createOrUpdateGithubRelease, getGitHubRepoSlug } from './utils/github';
|
||||
import { launchEditor } from './utils/launch-editor';
|
||||
import { parseChangelogMarkdown } from './utils/markdown';
|
||||
import { printAndFlushChanges, printDiff } from './utils/print-changes';
|
||||
import { printAndFlushChanges } from './utils/print-changes';
|
||||
import { resolveNxJsonConfigErrorMessage } from './utils/resolve-nx-json-error-message';
|
||||
import {
|
||||
ReleaseVersion,
|
||||
@ -57,8 +53,22 @@ import {
|
||||
createCommitMessageValues,
|
||||
createGitTagValues,
|
||||
handleDuplicateGitTags,
|
||||
noDiffInChangelogMessage,
|
||||
} from './utils/shared';
|
||||
|
||||
export interface NxReleaseChangelogResult {
|
||||
workspaceChangelog?: {
|
||||
releaseVersion: ReleaseVersion;
|
||||
contents: string;
|
||||
};
|
||||
projectChangelogs?: {
|
||||
[projectName: string]: {
|
||||
releaseVersion: ReleaseVersion;
|
||||
contents: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
type PostGitTask = (latestCommit: string) => Promise<void>;
|
||||
|
||||
export const releaseChangelogCLIHandler = (args: ChangelogOptions) =>
|
||||
@ -71,7 +81,7 @@ export const releaseChangelogCLIHandler = (args: ChangelogOptions) =>
|
||||
*/
|
||||
export async function releaseChangelog(
|
||||
args: ChangelogOptions
|
||||
): Promise<number> {
|
||||
): Promise<NxReleaseChangelogResult> {
|
||||
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||
const nxJson = readNxJson();
|
||||
|
||||
@ -134,7 +144,7 @@ export async function releaseChangelog(
|
||||
`To explicitly enable changelog generation, configure "release.changelog.workspaceChangelog" or "release.changelog.projectChangelogs" in nx.json.`,
|
||||
],
|
||||
});
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
const useAutomaticFromRef =
|
||||
@ -230,16 +240,51 @@ export async function releaseChangelog(
|
||||
toSHA
|
||||
);
|
||||
|
||||
await generateChangelogForWorkspace(
|
||||
const workspaceChangelog = await generateChangelogForWorkspace(
|
||||
tree,
|
||||
args,
|
||||
projectGraph,
|
||||
nxReleaseConfig,
|
||||
workspaceChangelogVersion,
|
||||
workspaceChangelogCommits,
|
||||
postGitTasks
|
||||
workspaceChangelogCommits
|
||||
);
|
||||
|
||||
if (
|
||||
workspaceChangelog &&
|
||||
shouldCreateGitHubRelease(
|
||||
nxReleaseConfig.changelog.workspaceChangelog,
|
||||
args.createRelease
|
||||
)
|
||||
) {
|
||||
let hasPushed = false;
|
||||
|
||||
postGitTasks.push(async (latestCommit) => {
|
||||
if (!hasPushed) {
|
||||
output.logSingleLine(`Pushing to git remote`);
|
||||
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush({
|
||||
gitRemote: args.gitRemote,
|
||||
dryRun: args.dryRun,
|
||||
verbose: args.verbose,
|
||||
});
|
||||
hasPushed = true;
|
||||
}
|
||||
|
||||
output.logSingleLine(`Creating GitHub Release`);
|
||||
|
||||
await createOrUpdateGithubRelease(
|
||||
workspaceChangelog.releaseVersion,
|
||||
workspaceChangelog.contents,
|
||||
latestCommit,
|
||||
{ dryRun: args.dryRun }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const allProjectChangelogs: NxReleaseChangelogResult['projectChangelogs'] =
|
||||
{};
|
||||
|
||||
for (const releaseGroup of releaseGroups) {
|
||||
const config = releaseGroup.changelog;
|
||||
// The entire feature is disabled at the release group level, exit early
|
||||
@ -292,7 +337,7 @@ export async function releaseChangelog(
|
||||
commits = await getCommits(fromRef, toSHA);
|
||||
}
|
||||
|
||||
await generateChangelogForProjects(
|
||||
const projectChangelogs = await generateChangelogForProjects(
|
||||
tree,
|
||||
args,
|
||||
projectGraph,
|
||||
@ -302,6 +347,43 @@ export async function releaseChangelog(
|
||||
releaseGroup,
|
||||
[project]
|
||||
);
|
||||
|
||||
let hasPushed = false;
|
||||
for (const [projectName, projectChangelog] of Object.entries(
|
||||
projectChangelogs
|
||||
)) {
|
||||
if (
|
||||
projectChangelogs &&
|
||||
shouldCreateGitHubRelease(
|
||||
releaseGroup.changelog,
|
||||
args.createRelease
|
||||
)
|
||||
) {
|
||||
postGitTasks.push(async (latestCommit) => {
|
||||
if (!hasPushed) {
|
||||
output.logSingleLine(`Pushing to git remote`);
|
||||
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush({
|
||||
gitRemote: args.gitRemote,
|
||||
dryRun: args.dryRun,
|
||||
verbose: args.verbose,
|
||||
});
|
||||
hasPushed = true;
|
||||
}
|
||||
|
||||
output.logSingleLine(`Creating GitHub Release`);
|
||||
|
||||
await createOrUpdateGithubRelease(
|
||||
projectChangelog.releaseVersion,
|
||||
projectChangelog.contents,
|
||||
latestCommit,
|
||||
{ dryRun: args.dryRun }
|
||||
);
|
||||
});
|
||||
}
|
||||
allProjectChangelogs[projectName] = projectChangelog;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const fromRef =
|
||||
@ -318,7 +400,7 @@ export async function releaseChangelog(
|
||||
|
||||
const commits = await getCommits(fromSHA, toSHA);
|
||||
|
||||
await generateChangelogForProjects(
|
||||
const projectChangelogs = await generateChangelogForProjects(
|
||||
tree,
|
||||
args,
|
||||
projectGraph,
|
||||
@ -328,10 +410,44 @@ export async function releaseChangelog(
|
||||
releaseGroup,
|
||||
projectNodes
|
||||
);
|
||||
|
||||
let hasPushed = false;
|
||||
for (const [projectName, projectChangelog] of Object.entries(
|
||||
projectChangelogs
|
||||
)) {
|
||||
if (
|
||||
projectChangelogs &&
|
||||
shouldCreateGitHubRelease(releaseGroup.changelog, args.createRelease)
|
||||
) {
|
||||
postGitTasks.push(async (latestCommit) => {
|
||||
if (!hasPushed) {
|
||||
output.logSingleLine(`Pushing to git remote`);
|
||||
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush({
|
||||
gitRemote: args.gitRemote,
|
||||
dryRun: args.dryRun,
|
||||
verbose: args.verbose,
|
||||
});
|
||||
hasPushed = true;
|
||||
}
|
||||
|
||||
output.logSingleLine(`Creating GitHub Release`);
|
||||
|
||||
await createOrUpdateGithubRelease(
|
||||
projectChangelog.releaseVersion,
|
||||
projectChangelog.contents,
|
||||
latestCommit,
|
||||
{ dryRun: args.dryRun }
|
||||
);
|
||||
});
|
||||
}
|
||||
allProjectChangelogs[projectName] = projectChangelog;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await applyChangesAndExit(
|
||||
await applyChangesAndExit(
|
||||
args,
|
||||
nxReleaseConfig,
|
||||
tree,
|
||||
@ -340,6 +456,11 @@ export async function releaseChangelog(
|
||||
commitMessageValues,
|
||||
gitTagValues
|
||||
);
|
||||
|
||||
return {
|
||||
workspaceChangelog,
|
||||
projectChangelogs: allProjectChangelogs,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveChangelogVersions(
|
||||
@ -429,7 +550,7 @@ async function applyChangesAndExit(
|
||||
`No changes were detected for any changelog files, so no changelog entries will be generated.`,
|
||||
],
|
||||
});
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate a new commit for the changes, if configured to do so
|
||||
@ -475,7 +596,7 @@ async function applyChangesAndExit(
|
||||
await postGitTask(latestCommit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
function resolveChangelogRenderer(
|
||||
@ -504,9 +625,8 @@ async function generateChangelogForWorkspace(
|
||||
projectGraph: ProjectGraph,
|
||||
nxReleaseConfig: NxReleaseConfig,
|
||||
workspaceChangelogVersion: (string | null) | undefined,
|
||||
commits: GitCommit[],
|
||||
postGitTasks: PostGitTask[]
|
||||
) {
|
||||
commits: GitCommit[]
|
||||
): Promise<NxReleaseChangelogResult['workspaceChangelog']> {
|
||||
const config = nxReleaseConfig.changelog.workspaceChangelog;
|
||||
// The entire feature is disabled at the workspace level, exit early
|
||||
if (config === false) {
|
||||
@ -572,27 +692,15 @@ async function generateChangelogForWorkspace(
|
||||
releaseTagPattern: nxReleaseConfig.releaseTagPattern,
|
||||
});
|
||||
|
||||
// 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(
|
||||
if (interpolatedTreePath) {
|
||||
const prefix = dryRun ? 'Previewing' : 'Generating';
|
||||
output.log({
|
||||
title: `${prefix} an entry in ${interpolatedTreePath} for ${chalk.white(
|
||||
releaseVersion.gitTag
|
||||
)}`;
|
||||
break;
|
||||
case !!interpolatedTreePath:
|
||||
logTitle += `n entry in ${interpolatedTreePath} for ${chalk.white(
|
||||
releaseVersion.gitTag
|
||||
)}`;
|
||||
break;
|
||||
case config.createRelease === 'github':
|
||||
logTitle += ` GitHub release for ${chalk.white(releaseVersion.gitTag)}`;
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
|
||||
output.log({
|
||||
title: logTitle,
|
||||
});
|
||||
|
||||
const githubRepoSlug = getGitHubRepoSlug(gitRemote);
|
||||
|
||||
let contents = await changelogRenderer({
|
||||
@ -621,15 +729,6 @@ async function generateChangelogForWorkspace(
|
||||
contents = readFileSync(changelogPath, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
let printSummary = () => {};
|
||||
const noDiffInChangelogMessage = chalk.yellow(
|
||||
`NOTE: There was no diff detected for the changelog entry. Maybe you intended to pass alternative git references via --from and --to?`
|
||||
);
|
||||
|
||||
if (interpolatedTreePath) {
|
||||
let rootChangelogContents = tree.exists(interpolatedTreePath)
|
||||
? tree.read(interpolatedTreePath).toString()
|
||||
@ -659,104 +758,13 @@ async function generateChangelogForWorkspace(
|
||||
|
||||
tree.write(interpolatedTreePath, rootChangelogContents);
|
||||
|
||||
printSummary = () =>
|
||||
printAndFlushChanges(tree, !!dryRun, 3, false, noDiffInChangelogMessage);
|
||||
printAndFlushChanges(tree, !!dryRun, 3, false, noDiffInChangelogMessage);
|
||||
}
|
||||
|
||||
if (config.createRelease === 'github') {
|
||||
if (!githubRepoSlug) {
|
||||
output.error({
|
||||
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.`,
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const token = await resolveGithubToken();
|
||||
const githubRequestConfig: GithubRequestConfig = {
|
||||
repo: githubRepoSlug,
|
||||
token,
|
||||
};
|
||||
|
||||
let existingGithubReleaseForVersion: GithubRelease;
|
||||
try {
|
||||
existingGithubReleaseForVersion = await getGithubReleaseByTag(
|
||||
githubRequestConfig,
|
||||
releaseVersion.gitTag
|
||||
);
|
||||
} 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:`,
|
||||
bodyLines: [
|
||||
'- 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',
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
if (err.response?.status === 404) {
|
||||
// No existing release found, this is fine
|
||||
} else {
|
||||
// Rethrow unknown errors for now
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
let existingPrintSummaryFn = printSummary;
|
||||
printSummary = () => {
|
||||
const logTitle = `https://github.com/${githubRepoSlug}/releases/tag/${releaseVersion.gitTag}`;
|
||||
if (existingGithubReleaseForVersion) {
|
||||
console.error(
|
||||
`${chalk.white('UPDATE')} ${logTitle}${
|
||||
dryRun ? chalk.keyword('orange')(' [dry-run]') : ''
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
`${chalk.green('CREATE')} ${logTitle}${
|
||||
dryRun ? chalk.keyword('orange')(' [dry-run]') : ''
|
||||
}`
|
||||
);
|
||||
}
|
||||
// Only print the diff here if we are not already going to be printing changes from the Tree
|
||||
if (!interpolatedTreePath) {
|
||||
console.log('');
|
||||
printDiff(
|
||||
existingGithubReleaseForVersion
|
||||
? existingGithubReleaseForVersion.body
|
||||
: '',
|
||||
contents,
|
||||
3,
|
||||
noDiffInChangelogMessage
|
||||
);
|
||||
}
|
||||
existingPrintSummaryFn();
|
||||
};
|
||||
|
||||
// Only schedule the actual GitHub update when not in dry-run mode
|
||||
if (!dryRun) {
|
||||
postGitTasks.push(async (latestCommit) => {
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush();
|
||||
|
||||
await createOrUpdateGithubRelease(
|
||||
githubRequestConfig,
|
||||
{
|
||||
version: releaseVersion.gitTag,
|
||||
prerelease: releaseVersion.isPrerelease,
|
||||
body: contents,
|
||||
commit: latestCommit,
|
||||
},
|
||||
existingGithubReleaseForVersion
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
printSummary();
|
||||
return {
|
||||
releaseVersion,
|
||||
contents,
|
||||
};
|
||||
}
|
||||
|
||||
async function generateChangelogForProjects(
|
||||
@ -768,7 +776,7 @@ async function generateChangelogForProjects(
|
||||
postGitTasks: PostGitTask[],
|
||||
releaseGroup: ReleaseGroupWithName,
|
||||
projects: ProjectGraphProjectNode[]
|
||||
) {
|
||||
): Promise<NxReleaseChangelogResult['projectChangelogs']> {
|
||||
const config = releaseGroup.changelog;
|
||||
// The entire feature is disabled at the release group level, exit early
|
||||
if (config === false) {
|
||||
@ -783,6 +791,8 @@ async function generateChangelogForProjects(
|
||||
|
||||
const changelogRenderer = resolveChangelogRenderer(config.renderer);
|
||||
|
||||
const projectChangelogs: NxReleaseChangelogResult['projectChangelogs'] = {};
|
||||
|
||||
for (const project of projects) {
|
||||
let interpolatedTreePath = config.file || '';
|
||||
if (interpolatedTreePath) {
|
||||
@ -807,27 +817,15 @@ async function generateChangelogForProjects(
|
||||
projectName: project.name,
|
||||
});
|
||||
|
||||
// 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(
|
||||
if (interpolatedTreePath) {
|
||||
const prefix = dryRun ? 'Previewing' : 'Generating';
|
||||
output.log({
|
||||
title: `${prefix} an entry in ${interpolatedTreePath} for ${chalk.white(
|
||||
releaseVersion.gitTag
|
||||
)}`;
|
||||
break;
|
||||
case !!interpolatedTreePath:
|
||||
logTitle += `n entry in ${interpolatedTreePath} for ${chalk.white(
|
||||
releaseVersion.gitTag
|
||||
)}`;
|
||||
break;
|
||||
case config.createRelease === 'github':
|
||||
logTitle += ` GitHub release for ${chalk.white(releaseVersion.gitTag)}`;
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
|
||||
output.log({
|
||||
title: logTitle,
|
||||
});
|
||||
|
||||
const githubRepoSlug =
|
||||
config.createRelease === 'github'
|
||||
? getGitHubRepoSlug(gitRemote)
|
||||
@ -866,15 +864,6 @@ async function generateChangelogForProjects(
|
||||
contents = readFileSync(changelogPath, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
let printSummary = () => {};
|
||||
const noDiffInChangelogMessage = chalk.yellow(
|
||||
`NOTE: There was no diff detected for the changelog entry. Maybe you intended to pass alternative git references via --from and --to?`
|
||||
);
|
||||
|
||||
if (interpolatedTreePath) {
|
||||
let changelogContents = tree.exists(interpolatedTreePath)
|
||||
? tree.read(interpolatedTreePath).toString()
|
||||
@ -903,113 +892,24 @@ async function generateChangelogForProjects(
|
||||
|
||||
tree.write(interpolatedTreePath, changelogContents);
|
||||
|
||||
printSummary = () =>
|
||||
printAndFlushChanges(
|
||||
tree,
|
||||
!!dryRun,
|
||||
3,
|
||||
false,
|
||||
noDiffInChangelogMessage,
|
||||
// Only print the change for the current changelog file at this point
|
||||
(f) => f.path === interpolatedTreePath
|
||||
);
|
||||
printAndFlushChanges(
|
||||
tree,
|
||||
!!dryRun,
|
||||
3,
|
||||
false,
|
||||
noDiffInChangelogMessage,
|
||||
// Only print the change for the current changelog file at this point
|
||||
(f) => f.path === interpolatedTreePath
|
||||
);
|
||||
}
|
||||
|
||||
if (config.createRelease === 'github') {
|
||||
if (!githubRepoSlug) {
|
||||
output.error({
|
||||
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.`,
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const token = await resolveGithubToken();
|
||||
const githubRequestConfig: GithubRequestConfig = {
|
||||
repo: githubRepoSlug,
|
||||
token,
|
||||
};
|
||||
|
||||
let existingGithubReleaseForVersion: GithubRelease;
|
||||
try {
|
||||
existingGithubReleaseForVersion = await getGithubReleaseByTag(
|
||||
githubRequestConfig,
|
||||
releaseVersion.gitTag
|
||||
);
|
||||
} 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:`,
|
||||
bodyLines: [
|
||||
'- 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',
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
if (err.response?.status === 404) {
|
||||
// No existing release found, this is fine
|
||||
} else {
|
||||
// Rethrow unknown errors for now
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
let existingPrintSummaryFn = printSummary;
|
||||
printSummary = () => {
|
||||
const logTitle = `https://github.com/${githubRepoSlug}/releases/tag/${releaseVersion.gitTag}`;
|
||||
if (existingGithubReleaseForVersion) {
|
||||
console.error(
|
||||
`${chalk.white('UPDATE')} ${logTitle}${
|
||||
dryRun ? chalk.keyword('orange')(' [dry-run]') : ''
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
`${chalk.green('CREATE')} ${logTitle}${
|
||||
dryRun ? chalk.keyword('orange')(' [dry-run]') : ''
|
||||
}`
|
||||
);
|
||||
}
|
||||
// Only print the diff here if we are not already going to be printing changes from the Tree
|
||||
if (!interpolatedTreePath) {
|
||||
console.log('');
|
||||
printDiff(
|
||||
existingGithubReleaseForVersion
|
||||
? existingGithubReleaseForVersion.body
|
||||
: '',
|
||||
contents,
|
||||
3,
|
||||
noDiffInChangelogMessage
|
||||
);
|
||||
}
|
||||
existingPrintSummaryFn();
|
||||
};
|
||||
|
||||
// Only schedule the actual GitHub update when not in dry-run mode
|
||||
if (!dryRun) {
|
||||
postGitTasks.push(async (latestCommit) => {
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush(gitRemote);
|
||||
|
||||
await createOrUpdateGithubRelease(
|
||||
githubRequestConfig,
|
||||
{
|
||||
version: releaseVersion.gitTag,
|
||||
prerelease: releaseVersion.isPrerelease,
|
||||
body: contents,
|
||||
commit: latestCommit,
|
||||
},
|
||||
existingGithubReleaseForVersion
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
printSummary();
|
||||
projectChangelogs[project.name] = {
|
||||
releaseVersion,
|
||||
contents,
|
||||
};
|
||||
}
|
||||
|
||||
return projectChangelogs;
|
||||
}
|
||||
|
||||
function checkChangelogFilesEnabled(nxReleaseConfig: NxReleaseConfig): boolean {
|
||||
@ -1040,3 +940,14 @@ async function getCommits(fromSHA: string, toSHA: string) {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export function shouldCreateGitHubRelease(
|
||||
changelogConfig: NxReleaseChangelogConfiguration | false | undefined,
|
||||
createReleaseArg: ChangelogOptions['createRelease'] | undefined = undefined
|
||||
): boolean {
|
||||
if (createReleaseArg !== undefined) {
|
||||
return createReleaseArg === 'github';
|
||||
}
|
||||
|
||||
return (changelogConfig || {}).createRelease === 'github';
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ export type ChangelogOptions = NxReleaseArgs &
|
||||
from?: string;
|
||||
interactive?: string;
|
||||
gitRemote?: string;
|
||||
createRelease?: false | 'github';
|
||||
};
|
||||
|
||||
export type PublishOptions = NxReleaseArgs &
|
||||
|
||||
@ -3,7 +3,7 @@ import { readNxJson } from '../../config/nx-json';
|
||||
import { output } from '../../devkit-exports';
|
||||
import { createProjectGraphAsync } from '../../project-graph/project-graph';
|
||||
import { handleErrors } from '../../utils/params';
|
||||
import { releaseChangelog } from './changelog';
|
||||
import { releaseChangelog, shouldCreateGitHubRelease } from './changelog';
|
||||
import { ReleaseOptions, VersionOptions } from './command-object';
|
||||
import {
|
||||
createNxReleaseConfig,
|
||||
@ -11,7 +11,8 @@ import {
|
||||
} from './config/config';
|
||||
import { filterReleaseGroups } from './config/filter-release-groups';
|
||||
import { releasePublish } from './publish';
|
||||
import { gitCommit, gitTag } from './utils/git';
|
||||
import { getCommitHash, gitCommit, gitPush, gitTag } from './utils/git';
|
||||
import { createOrUpdateGithubRelease } from './utils/github';
|
||||
import { resolveNxJsonConfigErrorMessage } from './utils/resolve-nx-json-error-message';
|
||||
import {
|
||||
createCommitMessageValues,
|
||||
@ -74,13 +75,14 @@ export async function release(
|
||||
gitTag: false,
|
||||
});
|
||||
|
||||
await releaseChangelog({
|
||||
const changelogResult = await releaseChangelog({
|
||||
...args,
|
||||
versionData: versionResult.projectsVersionData,
|
||||
version: versionResult.workspaceVersion,
|
||||
stageChanges: shouldStage,
|
||||
gitCommit: false,
|
||||
gitTag: false,
|
||||
createRelease: false,
|
||||
});
|
||||
|
||||
const {
|
||||
@ -140,6 +142,82 @@ export async function release(
|
||||
}
|
||||
}
|
||||
|
||||
const shouldCreateWorkspaceRelease = shouldCreateGitHubRelease(
|
||||
nxReleaseConfig.changelog.workspaceChangelog
|
||||
);
|
||||
|
||||
let hasPushedChanges = false;
|
||||
let latestCommit: string | undefined;
|
||||
|
||||
if (shouldCreateWorkspaceRelease && changelogResult.workspaceChangelog) {
|
||||
output.logSingleLine(`Pushing to git remote`);
|
||||
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush({
|
||||
dryRun: args.dryRun,
|
||||
verbose: args.verbose,
|
||||
});
|
||||
|
||||
hasPushedChanges = true;
|
||||
|
||||
output.logSingleLine(`Creating GitHub Release`);
|
||||
|
||||
latestCommit = await getCommitHash('HEAD');
|
||||
await createOrUpdateGithubRelease(
|
||||
changelogResult.workspaceChangelog.releaseVersion,
|
||||
changelogResult.workspaceChangelog.contents,
|
||||
latestCommit,
|
||||
{ dryRun: args.dryRun }
|
||||
);
|
||||
}
|
||||
|
||||
for (const releaseGroup of releaseGroups) {
|
||||
const shouldCreateProjectReleases = shouldCreateGitHubRelease(
|
||||
releaseGroup.changelog
|
||||
);
|
||||
|
||||
if (shouldCreateProjectReleases && changelogResult.projectChangelogs) {
|
||||
const projects = args.projects?.length
|
||||
? // If the user has passed a list of projects, we need to use the filtered list of projects within the release group
|
||||
Array.from(releaseGroupToFilteredProjects.get(releaseGroup))
|
||||
: // Otherwise, we use the full list of projects within the release group
|
||||
releaseGroup.projects;
|
||||
const projectNodes = projects.map((name) => projectGraph.nodes[name]);
|
||||
|
||||
for (const project of projectNodes) {
|
||||
const changelog = changelogResult.projectChangelogs[project.name];
|
||||
if (!changelog) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasPushedChanges) {
|
||||
output.logSingleLine(`Pushing to git remote`);
|
||||
|
||||
// Before we can create/update the release we need to ensure the commit exists on the remote
|
||||
await gitPush({
|
||||
dryRun: args.dryRun,
|
||||
verbose: args.verbose,
|
||||
});
|
||||
|
||||
hasPushedChanges = true;
|
||||
}
|
||||
|
||||
output.logSingleLine(`Creating GitHub Release`);
|
||||
|
||||
if (!latestCommit) {
|
||||
latestCommit = await getCommitHash('HEAD');
|
||||
}
|
||||
|
||||
await createOrUpdateGithubRelease(
|
||||
changelog.releaseVersion,
|
||||
changelog.contents,
|
||||
latestCommit,
|
||||
{ dryRun: args.dryRun }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hasNewVersion = false;
|
||||
// null means that all projects are versioned together but there were no changes
|
||||
if (versionResult.workspaceVersion !== null) {
|
||||
|
||||
@ -249,17 +249,40 @@ export async function gitTag({
|
||||
}
|
||||
}
|
||||
|
||||
export async function gitPush(gitRemote?: string) {
|
||||
export async function gitPush({
|
||||
gitRemote,
|
||||
dryRun,
|
||||
verbose,
|
||||
}: {
|
||||
gitRemote?: string;
|
||||
dryRun?: boolean;
|
||||
verbose?: boolean;
|
||||
}) {
|
||||
const commandArgs = [
|
||||
'push',
|
||||
// NOTE: It's important we use --follow-tags, and not --tags, so that we are precise about what we are pushing
|
||||
'--follow-tags',
|
||||
'--no-verify',
|
||||
'--atomic',
|
||||
// Set custom git remote if provided
|
||||
...(gitRemote ? [gitRemote] : []),
|
||||
];
|
||||
|
||||
if (verbose) {
|
||||
console.log(
|
||||
dryRun
|
||||
? `Would push the current branch to the remote with the following command, but --dry-run was set:`
|
||||
: `Pushing the current branch to the remote with the following command:`
|
||||
);
|
||||
console.log(`git ${commandArgs.join(' ')}`);
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await execCommand('git', [
|
||||
'push',
|
||||
// NOTE: It's important we use --follow-tags, and not --tags, so that we are precise about what we are pushing
|
||||
'--follow-tags',
|
||||
'--no-verify',
|
||||
'--atomic',
|
||||
// Set custom git remote if provided
|
||||
...(gitRemote ? [gitRemote] : []),
|
||||
]);
|
||||
await execCommand('git', commandArgs);
|
||||
} catch (err) {
|
||||
throw new Error(`Unexpected git push error: ${err}`);
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ import { homedir } from 'node:os';
|
||||
import { output } from '../../../utils/output';
|
||||
import { joinPathFragments } from '../../../utils/path';
|
||||
import { Reference } from './git';
|
||||
import { printDiff } from './print-changes';
|
||||
import { ReleaseVersion, noDiffInChangelogMessage } from './shared';
|
||||
|
||||
// axios types and values don't seem to match
|
||||
import _axios = require('axios');
|
||||
@ -56,6 +58,91 @@ export function getGitHubRepoSlug(remoteName = 'origin'): RepoSlug {
|
||||
}
|
||||
}
|
||||
|
||||
export async function createOrUpdateGithubRelease(
|
||||
releaseVersion: ReleaseVersion,
|
||||
changelogContents: string,
|
||||
latestCommit: string,
|
||||
{ dryRun }: { dryRun: boolean }
|
||||
): Promise<void> {
|
||||
const githubRepoSlug = getGitHubRepoSlug();
|
||||
if (!githubRepoSlug) {
|
||||
output.error({
|
||||
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.`,
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const token = await resolveGithubToken();
|
||||
const githubRequestConfig: GithubRequestConfig = {
|
||||
repo: githubRepoSlug,
|
||||
token,
|
||||
};
|
||||
|
||||
let existingGithubReleaseForVersion: GithubRelease;
|
||||
try {
|
||||
existingGithubReleaseForVersion = await getGithubReleaseByTag(
|
||||
githubRequestConfig,
|
||||
releaseVersion.gitTag
|
||||
);
|
||||
} 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:`,
|
||||
bodyLines: [
|
||||
'- 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',
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
if (err.response?.status === 404) {
|
||||
// No existing release found, this is fine
|
||||
} else {
|
||||
// Rethrow unknown errors for now
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const logTitle = `https://github.com/${githubRepoSlug}/releases/tag/${releaseVersion.gitTag}`;
|
||||
if (existingGithubReleaseForVersion) {
|
||||
console.error(
|
||||
`${chalk.white('UPDATE')} ${logTitle}${
|
||||
dryRun ? chalk.keyword('orange')(' [dry-run]') : ''
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
`${chalk.green('CREATE')} ${logTitle}${
|
||||
dryRun ? chalk.keyword('orange')(' [dry-run]') : ''
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
printDiff(
|
||||
existingGithubReleaseForVersion ? existingGithubReleaseForVersion.body : '',
|
||||
changelogContents,
|
||||
3,
|
||||
noDiffInChangelogMessage
|
||||
);
|
||||
|
||||
if (!dryRun) {
|
||||
await createOrUpdateGithubReleaseInternal(
|
||||
githubRequestConfig,
|
||||
{
|
||||
version: releaseVersion.gitTag,
|
||||
prerelease: releaseVersion.isPrerelease,
|
||||
body: changelogContents,
|
||||
commit: latestCommit,
|
||||
},
|
||||
existingGithubReleaseForVersion
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface GithubReleaseOptions {
|
||||
version: string;
|
||||
body: string;
|
||||
@ -63,7 +150,7 @@ interface GithubReleaseOptions {
|
||||
commit: string;
|
||||
}
|
||||
|
||||
export async function createOrUpdateGithubRelease(
|
||||
async function createOrUpdateGithubReleaseInternal(
|
||||
githubRequestConfig: GithubRequestConfig,
|
||||
release: GithubReleaseOptions,
|
||||
existingGithubReleaseForVersion?: GithubRelease
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import * as chalk from 'chalk';
|
||||
import { prerelease } from 'semver';
|
||||
import { ProjectGraph } from '../../../config/project-graph';
|
||||
import { Tree } from '../../../generators/tree';
|
||||
@ -7,6 +8,10 @@ import { output } from '../../../utils/output';
|
||||
import type { ReleaseGroupWithName } from '../config/filter-release-groups';
|
||||
import { GitCommit, gitAdd, gitCommit } from './git';
|
||||
|
||||
export const noDiffInChangelogMessage = chalk.yellow(
|
||||
`NOTE: There was no diff detected for the changelog entry. Maybe you intended to pass alternative git references via --from and --to?`
|
||||
);
|
||||
|
||||
export type ReleaseVersionGeneratorResult = {
|
||||
data: VersionData;
|
||||
callback: (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user