Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
44 changes: 44 additions & 0 deletions src/changelog-config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import fs from 'fs';
import path from 'path';

import {
getOriginalLatestVersion,
getOriginalTagPrefix,
} from './changelog-config';

const packageJson = {
autoChangelog: {
packageRename: {
originalLastestVersion: '1.0.0',
originalTagPrefix: 'test-package',
},
},
};

describe('getOriginalLatestVersion', () => {
it('reads the original latest version from an package.json', () => {
jest.spyOn(path, 'resolve').mockReturnValue('/fakepath');
jest.spyOn(fs, 'readFileSync').mockReturnValue(JSON.stringify(packageJson));
expect(getOriginalLatestVersion()).toBe('1.0.0');
});

it('returns null when there is no org latest version in the package.json', () => {
jest.spyOn(path, 'resolve').mockReturnValue('/fakepath');
jest.spyOn(fs, 'readFileSync').mockReturnValue('{}');
expect(getOriginalLatestVersion()).toBeNull();
});
});

describe('getOriginalTagPrefix', () => {
it('reads the original tag prefix from an package.json', () => {
jest.spyOn(path, 'resolve').mockReturnValue('/fakepath');
jest.spyOn(fs, 'readFileSync').mockReturnValue(JSON.stringify(packageJson));
expect(getOriginalTagPrefix()).toBe('test-package');
});

it('returns null when there is no tag prefix in the package.json', () => {
jest.spyOn(path, 'resolve').mockReturnValue('/fakepath');
jest.spyOn(fs, 'readFileSync').mockReturnValue('{}');
expect(getOriginalTagPrefix()).toBeNull();
});
});
63 changes: 63 additions & 0 deletions src/changelog-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable node/no-process-env, node/no-sync */

import fs from 'fs';
import path from 'path';

type PackageJson = {
autoChangelog: {
packageRename: {
originalLastestVersion: string;
originalTagPrefix: string;
};
};
};

/**
* Returns the latest version of the original package in case of package renamed.
*
* @returns The latest version string of the original package, or null.
*/
export function getOriginalLatestVersion(): string | null {
// Set automatically by Yarn 3.x
const packageJsonPath = process.env.npm_package_json;
if (packageJsonPath) {
const packageJson = path.resolve(packageJsonPath);
const packageJsonContent = JSON.parse(
fs.readFileSync(packageJson, 'utf8'),
) as PackageJson;

if (
typeof packageJsonContent.autoChangelog?.packageRename
?.originalLastestVersion === 'string'
) {
return packageJsonContent.autoChangelog.packageRename
.originalLastestVersion;
}
}

return null;
}

/**
* Returns the original package tag prefix in case of package renamed.
*
* @returns The tag prefix string for the original package, or null.
*/
export function getOriginalTagPrefix(): string | null {
// Set automatically by Yarn 3.x
const packageJsonPath = process.env.npm_package_json;
if (packageJsonPath) {
const packageJson = path.resolve(packageJsonPath);
const packageJsonContent = JSON.parse(
fs.readFileSync(packageJson, 'utf8'),
) as PackageJson;
if (
typeof packageJsonContent.autoChangelog?.packageRename
?.originalTagPrefix === 'string'
) {
return packageJsonContent.autoChangelog.packageRename.originalTagPrefix;
}
}

return null;
}
55 changes: 45 additions & 10 deletions src/changelog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import semver from 'semver';

import {
getOriginalLatestVersion,
getOriginalTagPrefix,
} from './changelog-config';
import {
ChangeCategory,
orderedChangeCategories,
Expand Down Expand Up @@ -181,7 +185,6 @@ function stringifyLinkReferenceDefinitions(
// A list of release versions in chronological order
const chronologicalVersions = releases.map(({ version }) => version);
const hasReleases = chronologicalVersions.length > 0;

// The "Unreleased" section represents all changes made since the *highest*
// release, not the most recent release. This is to accomodate patch releases
// of older versions that don't represent the latest set of changes.
Expand All @@ -192,35 +195,67 @@ function stringifyLinkReferenceDefinitions(
//
// If there have not been any releases yet, the repo URL is used directly as
// the link definition.
const orgLatestVersion = getOriginalLatestVersion();
const orgTagPrefix = getOriginalTagPrefix();
let tagPrefixToCompare = tagPrefix;
// if there is a package renamed and version and tag prefix set in package.json
// original package tag prefix will be considered for compare
// below if is for an example from changelog [Unreleased]: https://github.com/MetaMask/core/compare/[email protected]
if (orgTagPrefix && orgLatestVersion === latestSemverVersion) {
tagPrefixToCompare = orgTagPrefix;
}
const unreleasedLinkReferenceDefinition = `[${unreleased}]: ${
hasReleases
? getCompareUrl(repoUrl, `${tagPrefix}${latestSemverVersion}`, 'HEAD')
? getCompareUrl(
repoUrl,
`${tagPrefixToCompare}${latestSemverVersion}`,
'HEAD',
)
: withTrailingSlash(repoUrl)
}`;

// The "previous" release that should be used for comparison is not always
// the most recent release chronologically. The _highest_ version that is
// lower than the current release is used as the previous release, so that
// patch releases on older releases can be accomodated.
// by default tag prefix from new package will be used
tagPrefixToCompare = tagPrefix;
const releaseLinkReferenceDefinitions = releases
.map(({ version }) => {
let diffUrl;
// once the version matches with original version, rest of the lines in changelog will be assumed as migrated tags
if (orgTagPrefix && orgLatestVersion === version) {
tagPrefixToCompare = orgTagPrefix;
}
if (version === chronologicalVersions[chronologicalVersions.length - 1]) {
diffUrl = getTagUrl(repoUrl, `${tagPrefix}${version}`);
diffUrl = getTagUrl(repoUrl, `${tagPrefixToCompare}${version}`);
} else {
const versionIndex = chronologicalVersions.indexOf(version);
const previousVersion = chronologicalVersions
.slice(versionIndex)
.find((releaseVersion: Version) => {
return semver.gt(version, releaseVersion);
});
diffUrl = previousVersion
? getCompareUrl(
repoUrl,
`${tagPrefix}${previousVersion}`,
`${tagPrefix}${version}`,
)
: getTagUrl(repoUrl, `${tagPrefix}${version}`);
// if there is a package renamed and version and tag prefix set in config
// this if condition will fix the validation for original package's first release from new/renamed package
// [6.0.0]: https://github.com/MetaMask/core/compare/[email protected]...@metamask/[email protected]
if (orgTagPrefix && orgLatestVersion === previousVersion) {
diffUrl = previousVersion
? getCompareUrl(
repoUrl,
`${orgTagPrefix}${previousVersion}`,
`${tagPrefixToCompare}${version}`,
)
: getTagUrl(repoUrl, `${tagPrefixToCompare}${version}`);
} else {
diffUrl = previousVersion
? getCompareUrl(
repoUrl,
`${tagPrefixToCompare}${previousVersion}`,
`${tagPrefixToCompare}${version}`,
)
: getTagUrl(repoUrl, `${tagPrefixToCompare}${version}`);
}
}
return `[${version}]: ${diffUrl}`;
})
Expand Down
53 changes: 53 additions & 0 deletions src/validate-changelog.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import prettier from 'prettier';

import * as ChangelogConfig from './changelog-config';
import { validateChangelog } from './validate-changelog';

jest.mock('./changelog-config');

const emptyChangelog = `# Changelog
All notable changes to this project will be documented in this file.

Expand Down Expand Up @@ -132,6 +135,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[1.0.0]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/releases/tag/v1.0.0
`;

const changelogWithRenamedPackage = `# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.0.0] - 2020-01-01
### Changed
- package renamed

## [0.0.2] - 2020-01-01
### Fixed
- Something

## [0.0.1] - 2020-01-01
### Changed
- Something

[Unreleased]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/compare/@metamask/[email protected]
[1.0.0]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/compare/[email protected]...@metamask/[email protected]
[0.0.2]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/compare/[email protected]@0.0.2
[0.0.1]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/releases/tag/[email protected]
`;

describe('validateChangelog', () => {
it('should not throw for any empty valid changelog', () => {
expect(() =>
Expand Down Expand Up @@ -686,4 +715,28 @@ describe('validateChangelog', () => {
).not.toThrow();
});
});

// when the package has been renamed from `test` to `@metamast/test`
it('should not throw for a valid changelog with renamed package', () => {
jest
.spyOn(ChangelogConfig, 'getOriginalLatestVersion')
.mockReturnValue('0.0.2');
jest
.spyOn(ChangelogConfig, 'getOriginalTagPrefix')
.mockReturnValue('test@');
expect(() =>
validateChangelog({
changelogContent: changelogWithRenamedPackage,
currentVersion: '1.0.0',
repoUrl:
'https://github.com/ExampleUsernameOrOrganization/ExampleRepository',
isReleaseCandidate: false,
tagPrefix: '@metamask/test@',
}),
).not.toThrow();
expect(ChangelogConfig.getOriginalLatestVersion).toHaveBeenCalled();
expect(ChangelogConfig.getOriginalLatestVersion()).toBe('0.0.2');
expect(ChangelogConfig.getOriginalTagPrefix).toHaveBeenCalled();
expect(ChangelogConfig.getOriginalTagPrefix()).toBe('test@');
});
});