Skip to content

Commit a9594ab

Browse files
kantheshaMajorLiftmcmire
authored
package rename changelog validation (#157)
* allows changelog to maintain the history when package has been renamed * Update src/changelog.ts removing redundant null check. Co-authored-by: Jongsun Suh <[email protected]> * Update src/changelog.ts Co-authored-by: Jongsun Suh <[email protected]> * validation test added for renamed package changelog * Update src/changelog-config.ts Co-authored-by: Jongsun Suh <[email protected]> * Update src/changelog-config.ts Co-authored-by: Jongsun Suh <[email protected]> * command line params for version and tag prefix of the package before rename * refactor unreleasedLinkReferenceDefinition * refactor released link reference definition * Apply JSDoc syntax for optional params - see: https://jsdoc.app/tags-param.html#optional-parameters-and-default-values * rename versionBeforePkgRename and tagPrefixBeforePkgRename and released links code refactor * Revert "Apply JSDoc syntax for optional params " This reverts commit b51a4fa. * added PackageRename type with version and tagPrefix properties * parse changelog test * Update src/changelog.ts Co-authored-by: Elliot Winkler <[email protected]> * Update src/changelog.ts Co-authored-by: Elliot Winkler <[email protected]> --------- Co-authored-by: Jongsun Suh <[email protected]> Co-authored-by: Elliot Winkler <[email protected]>
1 parent 4804d3e commit a9594ab

7 files changed

+311
-37
lines changed

src/changelog.ts

+131-36
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
unreleased,
88
Version,
99
} from './constants';
10+
import { PackageRename } from './shared-types';
1011

1112
const changelogTitle = '# Changelog';
1213
const changelogDescription = `All notable changes to this project will be documented in this file.
@@ -165,24 +166,50 @@ function getTagUrl(repoUrl: string, tag: string) {
165166
* @param repoUrl - The URL for the GitHub repository.
166167
* @param tagPrefix - The prefix used in tags before the version number.
167168
* @param releases - The releases to generate link definitions for.
169+
* @param packageRename - The package rename properties
170+
* An optional, which is required only in case of package renamed.
168171
* @returns The stringified release link definitions.
169172
*/
170173
function stringifyLinkReferenceDefinitions(
171174
repoUrl: string,
172175
tagPrefix: string,
173176
releases: ReleaseMetadata[],
177+
packageRename?: PackageRename,
174178
) {
175-
// A list of release versions in descending SemVer order
176-
const descendingSemverVersions = releases
177-
.map(({ version }) => version)
178-
.sort((a: Version, b: Version) => {
179-
return semver.gt(a, b) ? -1 : 1;
180-
});
181-
const latestSemverVersion = descendingSemverVersions[0];
182-
// A list of release versions in chronological order
183-
const chronologicalVersions = releases.map(({ version }) => version);
184-
const hasReleases = chronologicalVersions.length > 0;
179+
const unreleasedLinkReferenceDefinition =
180+
getUnreleasedLinkReferenceDefinition(
181+
repoUrl,
182+
tagPrefix,
183+
releases,
184+
packageRename,
185+
);
186+
187+
const releaseLinkReferenceDefinitions = getReleaseLinkReferenceDefinitions(
188+
repoUrl,
189+
tagPrefix,
190+
releases,
191+
packageRename,
192+
).join('\n');
193+
return `${unreleasedLinkReferenceDefinition}\n${releaseLinkReferenceDefinitions}${
194+
releases.length > 0 ? '\n' : ''
195+
}`;
196+
}
185197

198+
/**
199+
* Get a string of unreleased link reference definition.
200+
*
201+
* @param repoUrl - The URL for the GitHub repository.
202+
* @param tagPrefix - The prefix used in tags before the version number.
203+
* @param releases - The releases to generate link definitions for.
204+
* @param packageRename - The package rename properties.
205+
* @returns A unreleased link reference definition string.
206+
*/
207+
function getUnreleasedLinkReferenceDefinition(
208+
repoUrl: string,
209+
tagPrefix: string,
210+
releases: ReleaseMetadata[],
211+
packageRename?: PackageRename,
212+
): string {
186213
// The "Unreleased" section represents all changes made since the *highest*
187214
// release, not the most recent release. This is to accomodate patch releases
188215
// of older versions that don't represent the latest set of changes.
@@ -193,42 +220,102 @@ function stringifyLinkReferenceDefinitions(
193220
//
194221
// If there have not been any releases yet, the repo URL is used directly as
195222
// the link definition.
196-
const unreleasedLinkReferenceDefinition = `[${unreleased}]: ${
223+
224+
// A list of release versions in descending SemVer order
225+
const descendingSemverVersions = releases
226+
.map(({ version }) => version)
227+
.sort((a: Version, b: Version) => {
228+
return semver.gt(a, b) ? -1 : 1;
229+
});
230+
const latestSemverVersion = descendingSemverVersions[0];
231+
const hasReleases = descendingSemverVersions.length > 0;
232+
// if there is a package renamed, the tag prefix before the rename will be considered for compare
233+
// [Unreleased]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/compare/[email protected]
234+
const tagPrefixToCompare =
235+
packageRename && packageRename.versionBeforeRename === latestSemverVersion
236+
? packageRename.tagPrefixBeforeRename
237+
: tagPrefix;
238+
239+
return `[${unreleased}]: ${
197240
hasReleases
198-
? getCompareUrl(repoUrl, `${tagPrefix}${latestSemverVersion}`, 'HEAD')
241+
? getCompareUrl(
242+
repoUrl,
243+
`${tagPrefixToCompare}${latestSemverVersion}`,
244+
'HEAD',
245+
)
199246
: withTrailingSlash(repoUrl)
200247
}`;
248+
}
201249

250+
/**
251+
* Get a list of release link reference definitions.
252+
*
253+
* @param repoUrl - The URL for the GitHub repository.
254+
* @param tagPrefix - The prefix used in tags before the version number.
255+
* @param releases - The releases to generate link definitions for.
256+
* @param packageRename - The package rename properties.
257+
* @returns A list of release link reference definitions.
258+
*/
259+
function getReleaseLinkReferenceDefinitions(
260+
repoUrl: string,
261+
tagPrefix: string,
262+
releases: ReleaseMetadata[],
263+
packageRename?: PackageRename,
264+
): string[] {
202265
// The "previous" release that should be used for comparison is not always
203266
// the most recent release chronologically. The _highest_ version that is
204267
// lower than the current release is used as the previous release, so that
205268
// patch releases on older releases can be accomodated.
206-
const releaseLinkReferenceDefinitions = releases
207-
.map(({ version }) => {
208-
let diffUrl;
209-
if (version === chronologicalVersions[chronologicalVersions.length - 1]) {
210-
diffUrl = getTagUrl(repoUrl, `${tagPrefix}${version}`);
269+
const chronologicalVersions = releases.map(({ version }) => version);
270+
let tagPrefixToCompare = tagPrefix;
271+
const releaseLinkReferenceDefinitions = releases.map(({ version }) => {
272+
let diffUrl;
273+
// once the version matches with versionBeforeRename, rest of the lines in changelog will be assumed as migrated tags
274+
if (packageRename && packageRename.versionBeforeRename === version) {
275+
tagPrefixToCompare = packageRename.tagPrefixBeforeRename;
276+
}
277+
278+
if (version === chronologicalVersions[chronologicalVersions.length - 1]) {
279+
diffUrl = getTagUrl(repoUrl, `${tagPrefixToCompare}${version}`);
280+
} else {
281+
const versionIndex = chronologicalVersions.indexOf(version);
282+
const previousVersion = chronologicalVersions
283+
.slice(versionIndex)
284+
.find((releaseVersion: Version) => {
285+
return semver.gt(version, releaseVersion);
286+
});
287+
288+
if (previousVersion) {
289+
if (
290+
packageRename &&
291+
packageRename.versionBeforeRename === previousVersion
292+
) {
293+
// The package was renamed at this version
294+
// (the tag prefix holds the new name).
295+
diffUrl = getCompareUrl(
296+
repoUrl,
297+
`${packageRename.tagPrefixBeforeRename}${previousVersion}`,
298+
`${tagPrefix}${version}`,
299+
);
300+
} else {
301+
// If the package was ever renamed, it was not renamed at this version,
302+
// so use either the old tag prefix or the new tag prefix.
303+
// If the package was never renamed, use the tag prefix as it is.
304+
diffUrl = getCompareUrl(
305+
repoUrl,
306+
`${tagPrefixToCompare}${previousVersion}`,
307+
`${tagPrefixToCompare}${version}`,
308+
);
309+
}
211310
} else {
212-
const versionIndex = chronologicalVersions.indexOf(version);
213-
const previousVersion = chronologicalVersions
214-
.slice(versionIndex)
215-
.find((releaseVersion: Version) => {
216-
return semver.gt(version, releaseVersion);
217-
});
218-
diffUrl = previousVersion
219-
? getCompareUrl(
220-
repoUrl,
221-
`${tagPrefix}${previousVersion}`,
222-
`${tagPrefix}${version}`,
223-
)
224-
: getTagUrl(repoUrl, `${tagPrefix}${version}`);
311+
// This is the smallest release.
312+
diffUrl = getTagUrl(repoUrl, `${tagPrefixToCompare}${version}`);
225313
}
226-
return `[${version}]: ${diffUrl}`;
227-
})
228-
.join('\n');
229-
return `${unreleasedLinkReferenceDefinition}\n${releaseLinkReferenceDefinitions}${
230-
releases.length > 0 ? '\n' : ''
231-
}`;
314+
}
315+
return `[${version}]: ${diffUrl}`;
316+
});
317+
318+
return releaseLinkReferenceDefinitions;
232319
}
233320

234321
type AddReleaseOptions = {
@@ -265,28 +352,35 @@ export default class Changelog {
265352

266353
#formatter: Formatter;
267354

355+
readonly #packageRename: PackageRename | undefined;
356+
268357
/**
269358
* Construct an empty changelog.
270359
*
271360
* @param options - Changelog options.
272361
* @param options.repoUrl - The GitHub repository URL for the current project.
273362
* @param options.tagPrefix - The prefix used in tags before the version number.
274363
* @param options.formatter - A function that formats the changelog string.
364+
* @param options.packageRename - The package rename properties.
365+
* An optional, which is required only in case of package renamed.
275366
*/
276367
constructor({
277368
repoUrl,
278369
tagPrefix = 'v',
279370
formatter = (changelog) => changelog,
371+
packageRename,
280372
}: {
281373
repoUrl: string;
282374
tagPrefix?: string;
283375
formatter?: Formatter;
376+
packageRename?: PackageRename;
284377
}) {
285378
this.#releases = [];
286379
this.#changes = { [unreleased]: {} };
287380
this.#repoUrl = repoUrl;
288381
this.#tagPrefix = tagPrefix;
289382
this.#formatter = formatter;
383+
this.#packageRename = packageRename;
290384
}
291385

292386
/**
@@ -468,6 +562,7 @@ ${stringifyLinkReferenceDefinitions(
468562
this.#repoUrl,
469563
this.#tagPrefix,
470564
this.#releases,
565+
this.#packageRename,
471566
)}`;
472567

473568
return this.#formatter(changelog);

src/cli.ts

+36
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { unreleased, Version } from './constants';
1313
import { generateDiff } from './generate-diff';
1414
import { createEmptyChangelog } from './init';
1515
import { getRepositoryUrl } from './repo';
16+
import { PackageRename } from './shared-types';
1617
import { updateChangelog } from './update-changelog';
1718
import {
1819
ChangelogFormattingError,
@@ -142,6 +143,10 @@ type ValidateOptions = {
142143
tagPrefix: string;
143144
fix: boolean;
144145
formatter: Formatter;
146+
/**
147+
* The package rename properties, used in case of package is renamed
148+
*/
149+
packageRename?: PackageRename;
145150
};
146151

147152
/**
@@ -155,6 +160,8 @@ type ValidateOptions = {
155160
* @param options.tagPrefix - The prefix used in tags before the version number.
156161
* @param options.fix - Whether to attempt to fix the changelog or not.
157162
* @param options.formatter - A custom Markdown formatter to use.
163+
* @param options.packageRename - The package rename properties.
164+
* An optional, which is required only in case of package renamed.
158165
*/
159166
async function validate({
160167
changelogPath,
@@ -164,6 +171,7 @@ async function validate({
164171
tagPrefix,
165172
fix,
166173
formatter,
174+
packageRename,
167175
}: ValidateOptions) {
168176
const changelogContent = await readChangelog(changelogPath);
169177

@@ -175,6 +183,7 @@ async function validate({
175183
isReleaseCandidate,
176184
tagPrefix,
177185
formatter,
186+
packageRename,
178187
});
179188
return undefined;
180189
} catch (error) {
@@ -257,6 +266,14 @@ function configureCommonCommandOptions(_yargs: Argv) {
257266
default: 'v',
258267
description: 'The prefix used in tags before the version number.',
259268
type: 'string',
269+
})
270+
.option('versionBeforePackageRename', {
271+
description: 'A version of the package before being renamed.',
272+
type: 'string',
273+
})
274+
.option('tagPrefixBeforePackageRename', {
275+
description: 'A tag prefix of the package before being renamed.',
276+
type: 'string',
260277
});
261278
}
262279

@@ -332,6 +349,8 @@ async function main() {
332349
tagPrefix,
333350
fix,
334351
prettier: usePrettier,
352+
versionBeforePackageRename,
353+
tagPrefixBeforePackageRename,
335354
} = argv;
336355
let { currentVersion } = argv;
337356

@@ -408,6 +427,15 @@ async function main() {
408427
return exitWithError(`Invalid repo URL: '${repoUrl}'`);
409428
}
410429

430+
if (
431+
(versionBeforePackageRename && !tagPrefixBeforePackageRename) ||
432+
(!versionBeforePackageRename && tagPrefixBeforePackageRename)
433+
) {
434+
return exitWithError(
435+
'--version-before-package-rename and --tag-prefix-before-package-rename must be given together or not at all.',
436+
);
437+
}
438+
411439
let changelogPath = changelogFilename;
412440
if (!path.isAbsolute(changelogFilename) && projectRootDirectory) {
413441
changelogPath = path.resolve(projectRootDirectory, changelogFilename);
@@ -447,6 +475,13 @@ async function main() {
447475
formatter,
448476
});
449477
} else if (command === 'validate') {
478+
let packageRename: PackageRename | undefined;
479+
if (versionBeforePackageRename && tagPrefixBeforePackageRename) {
480+
packageRename = {
481+
versionBeforeRename: versionBeforePackageRename,
482+
tagPrefixBeforeRename: tagPrefixBeforePackageRename,
483+
};
484+
}
450485
await validate({
451486
changelogPath,
452487
currentVersion,
@@ -455,6 +490,7 @@ async function main() {
455490
tagPrefix,
456491
fix,
457492
formatter,
493+
packageRename,
458494
});
459495
} else if (command === 'init') {
460496
await init({

0 commit comments

Comments
 (0)