mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
2c343d18a7
Summary: This change implements the automatic generation of the Changelog in CI while doing a release. The output is a PR opened by the react-native-bot that can be manipulated and imported by Meta engineers ## Changelog: [Internal] - Generate the changelog automatically Pull Request resolved: https://github.com/facebook/react-native/pull/50323 Test Plan: Tested as a separated workflow first: <img width="1624" alt="Screenshot 2025-03-27 at 17 44 47" src="https://github.com/user-attachments/assets/b8877cdb-f63b-4d82-b340-54f612ac0cd4" /> this generated the PR: https://github.com/facebook/react-native/pull/50328 I also added jest tests: <img width="516" alt="Screenshot 2025-03-27 at 17 45 39" src="https://github.com/user-attachments/assets/7ebbc310-e41e-48fc-997e-21366c7306cf" /> Reviewed By: cortinico Differential Revision: D71986909 Pulled By: cipolleschi fbshipit-source-id: 10ffaf342bb0642a6992a107185b6704815b16e3
252 lines
6.8 KiB
JavaScript
252 lines
6.8 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
*/
|
|
|
|
const {
|
|
generateChangelog,
|
|
_computePreviousVersionFrom,
|
|
_generateChangelog,
|
|
_pushCommit,
|
|
_createPR,
|
|
} = require('../generateChangelog');
|
|
|
|
const silence = () => {};
|
|
const mockGetNpmPackageInfo = jest.fn();
|
|
const mockExecSync = jest.fn();
|
|
const mockRun = jest.fn();
|
|
const mockFetch = jest.fn();
|
|
const mockExit = jest.fn();
|
|
|
|
jest.mock('../utils.js', () => ({
|
|
log: silence,
|
|
run: mockRun,
|
|
getNpmPackageInfo: mockGetNpmPackageInfo,
|
|
}));
|
|
|
|
process.exit = mockExit;
|
|
global.fetch = mockFetch;
|
|
|
|
describe('Generate Changelog', () => {
|
|
beforeEach(jest.clearAllMocks);
|
|
|
|
describe('_computePreviousVersionFrom', () => {
|
|
it('returns rc.0 when rc is 1', async () => {
|
|
const currentVersion = '0.78.0-rc.1';
|
|
const expectedVersion = '0.78.0-rc.0';
|
|
|
|
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
|
|
|
|
expect(receivedVersion).toEqual(expectedVersion);
|
|
});
|
|
|
|
it('returns previous rc version when rc is > 1', async () => {
|
|
const currentVersion = '0.78.0-rc.5';
|
|
const expectedVersion = '0.78.0-rc.4';
|
|
|
|
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
|
|
|
|
expect(receivedVersion).toEqual(expectedVersion);
|
|
});
|
|
|
|
it('returns previous patch version when rc is 0', async () => {
|
|
const currentVersion = '0.78.0-rc.0';
|
|
const expectedVersion = '0.77.1';
|
|
|
|
mockGetNpmPackageInfo.mockReturnValueOnce(
|
|
Promise.resolve({version: '0.77.1'}),
|
|
);
|
|
|
|
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
|
|
|
|
expect(receivedVersion).toEqual(expectedVersion);
|
|
});
|
|
|
|
it('returns patch 0 when patch is 1', async () => {
|
|
const currentVersion = '0.78.1';
|
|
const expectedVersion = '0.78.0';
|
|
|
|
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
|
|
|
|
expect(receivedVersion).toEqual(expectedVersion);
|
|
});
|
|
|
|
it('returns previous patch when patch is > 1', async () => {
|
|
const currentVersion = '0.78.5';
|
|
const expectedVersion = '0.78.4';
|
|
|
|
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
|
|
|
|
expect(receivedVersion).toEqual(expectedVersion);
|
|
});
|
|
|
|
it('returns null when patch is 0', async () => {
|
|
const currentVersion = '0.78.0';
|
|
|
|
const receivedVersion = await _computePreviousVersionFrom(currentVersion);
|
|
|
|
expect(receivedVersion).toBeNull();
|
|
});
|
|
|
|
it("throws an error when the version can't be parsed", async () => {
|
|
const currentVersion = '0.78.0-rc0';
|
|
|
|
await expect(
|
|
_computePreviousVersionFrom(currentVersion),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('_generateChangelog', () => {
|
|
it('calls git in the right order', async () => {
|
|
const currentVersion = '0.79.0-rc5';
|
|
const previousVersion = '0.79.0-rc4';
|
|
const token = 'token';
|
|
|
|
expectedCommandArgs = [
|
|
'@rnx-kit/rn-changelog-generator',
|
|
'--base',
|
|
`v${previousVersion}`,
|
|
'--compare',
|
|
`v${currentVersion}`,
|
|
'--repo',
|
|
'.',
|
|
'--changelog',
|
|
'./CHANGELOG.md',
|
|
'--token',
|
|
`${token}`,
|
|
];
|
|
|
|
_generateChangelog(previousVersion, currentVersion, token);
|
|
|
|
expect(mockRun).toHaveBeenCalledTimes(4);
|
|
expect(mockRun).toHaveBeenNthCalledWith(1, 'git checkout main');
|
|
expect(mockRun).toHaveBeenNthCalledWith(2, 'git fetch');
|
|
expect(mockRun).toHaveBeenNthCalledWith(3, 'git pull origin main');
|
|
expect(mockRun).toHaveBeenNthCalledWith(
|
|
4,
|
|
`npx ${expectedCommandArgs.join(' ')}`,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('_pushCommit', () => {
|
|
it('calls git in the right order', async () => {
|
|
const currentVersion = '0.79.0-rc5';
|
|
|
|
_pushCommit(currentVersion);
|
|
|
|
expect(mockRun).toHaveBeenCalledTimes(4);
|
|
expect(mockRun).toHaveBeenNthCalledWith(
|
|
1,
|
|
`git checkout -b changelog/v${currentVersion}`,
|
|
);
|
|
expect(mockRun).toHaveBeenNthCalledWith(2, 'git add CHANGELOG.md');
|
|
expect(mockRun).toHaveBeenNthCalledWith(
|
|
3,
|
|
`git commit -m "[RN][Changelog] Add changelog for v${currentVersion}"`,
|
|
);
|
|
expect(mockRun).toHaveBeenNthCalledWith(
|
|
4,
|
|
`git push origin changelog/v${currentVersion}`,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('_createPR', () => {
|
|
it('throws error when status is not 201', async () => {
|
|
const currentVersion = '0.79.0-rc5';
|
|
const token = 'token';
|
|
|
|
mockFetch.mockReturnValueOnce(Promise.resolve({status: 401}));
|
|
|
|
const headers = {
|
|
Accept: 'Accept: application/vnd.github+json',
|
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
Authorization: `Bearer ${token}`,
|
|
};
|
|
|
|
const content = `
|
|
## Summary
|
|
Add Changelog for ${currentVersion}
|
|
|
|
## Changelog:
|
|
[Internal] - Add Changelog for ${currentVersion}
|
|
|
|
## Test Plan:
|
|
N/A`;
|
|
|
|
const body = {
|
|
title: `[RN][Changelog] Add changelog for v${currentVersion}`,
|
|
head: `changelog/v${currentVersion}`,
|
|
base: 'main',
|
|
body: content,
|
|
};
|
|
|
|
await expect(_createPR(currentVersion, token)).rejects.toThrow();
|
|
|
|
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
'https://api.github.com/repos/facebook/react-native/pulls',
|
|
{
|
|
method: 'POST',
|
|
headers: headers,
|
|
body: JSON.stringify(body),
|
|
},
|
|
);
|
|
});
|
|
it('Returns the pr url', async () => {
|
|
const currentVersion = '0.79.0-rc5';
|
|
const token = 'token';
|
|
const expectedPrURL =
|
|
'https://github.com/facebook/react-native/pulls/1234';
|
|
|
|
const returnedObject = {
|
|
status: 201,
|
|
json: () => Promise.resolve({html_url: expectedPrURL}),
|
|
};
|
|
mockFetch.mockReturnValueOnce(Promise.resolve(returnedObject));
|
|
|
|
const headers = {
|
|
Accept: 'Accept: application/vnd.github+json',
|
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
Authorization: `Bearer ${token}`,
|
|
};
|
|
|
|
const content = `
|
|
## Summary
|
|
Add Changelog for ${currentVersion}
|
|
|
|
## Changelog:
|
|
[Internal] - Add Changelog for ${currentVersion}
|
|
|
|
## Test Plan:
|
|
N/A`;
|
|
|
|
const body = {
|
|
title: `[RN][Changelog] Add changelog for v${currentVersion}`,
|
|
head: `changelog/v${currentVersion}`,
|
|
base: 'main',
|
|
body: content,
|
|
};
|
|
|
|
const receivedPrURL = await _createPR(currentVersion, token);
|
|
|
|
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
'https://api.github.com/repos/facebook/react-native/pulls',
|
|
{
|
|
method: 'POST',
|
|
headers: headers,
|
|
body: JSON.stringify(body),
|
|
},
|
|
);
|
|
expect(receivedPrURL).toEqual(expectedPrURL);
|
|
});
|
|
});
|
|
});
|