Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/37029

Changelog: [Internal] - Add tests to publish-npm

Reviewed By: hoxyq

Differential Revision: D45171640

fbshipit-source-id: b70bb478ae6b08891f1e82088bb0e07d5d68b4e5
This commit is contained in:
Luna Wei 2023-04-25 10:13:34 -07:00 коммит произвёл Facebook GitHub Bot
Родитель c2be0ad40e
Коммит 7b62bcbf9d
3 изменённых файлов: 332 добавлений и 115 удалений

Просмотреть файл

@ -171,6 +171,10 @@ describe('promise tasks', () => {
sequenceId = 0;
});
afterEach(() => {
jest.useRealTimers();
});
it('should run a basic promise task', () => {
const task1 = jest.fn(() => {
expect(++sequenceId).toBe(1);

Просмотреть файл

@ -0,0 +1,210 @@
/**
* 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 execMock = jest.fn();
const echoMock = jest.fn();
const exitMock = jest.fn();
const isTaggedLatestMock = jest.fn();
const publishAndroidArtifactsToMavenMock = jest.fn();
const env = process.env;
jest
.mock('shelljs', () => ({
exec: execMock,
echo: echoMock,
exit: exitMock,
}))
.mock('./../scm-utils', () => ({
exitIfNotOnGit: command => command(),
getCurrentCommit: () => 'currentco_mmit',
isTaggedLatest: isTaggedLatestMock,
}))
.mock('path', () => ({
join: () => '../packages/react-native',
}))
.mock('fs')
.mock('./../release-utils', () => ({
generateAndroidArtifacts: jest.fn(),
publishAndroidArtifactsToMaven: publishAndroidArtifactsToMavenMock,
}));
const date = new Date('2023-04-20T23:52:39.543Z');
const publishNpm = require('../publish-npm');
describe('publish-npm', () => {
beforeAll(() => {
jest.useFakeTimers({legacyFakeTimers: false});
jest.setSystemTime(date);
});
afterAll(() => {
jest.useRealTimers();
});
afterEach(() => {
process.env = env;
});
afterEach(() => {
jest.resetModules();
jest.resetAllMocks();
});
describe('dry-run', () => {
it('should set version and not publish', () => {
execMock.mockReturnValueOnce({code: 0});
publishNpm('dry-run');
expect(exitMock).toHaveBeenCalledWith(0);
expect(isTaggedLatestMock.mock.calls).toHaveLength(0);
expect(echoMock).toHaveBeenCalledWith(
'Skipping `npm publish` because --dry-run is set.',
);
expect(execMock).toHaveBeenCalledWith(
'node scripts/set-rn-version.js --to-version 1000.0.0-currentco --build-type dry-run',
);
expect(execMock.mock.calls).toHaveLength(1);
});
});
describe('nightly', () => {
it('should publish', () => {
execMock.mockReturnValueOnce({code: 0}).mockReturnValueOnce({code: 0});
publishNpm('nightly');
const expectedVersion = '0.0.0-20230420-2352-currentco';
expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith(
expectedVersion,
true,
);
expect(execMock.mock.calls[0][0]).toBe(
`node scripts/set-rn-version.js --to-version ${expectedVersion} --build-type nightly`,
);
expect(execMock.mock.calls[1][0]).toBe('npm publish --tag nightly');
expect(echoMock).toHaveBeenCalledWith(
`Published to npm ${expectedVersion}`,
);
expect(exitMock).toHaveBeenCalledWith(0);
expect(execMock.mock.calls).toHaveLength(2);
});
it('should fail to set version', () => {
execMock.mockReturnValueOnce({code: 1});
publishNpm('nightly');
const expectedVersion = '0.0.0-20230420-2352-currentco';
expect(publishAndroidArtifactsToMavenMock).not.toBeCalled();
expect(execMock.mock.calls[0][0]).toBe(
`node scripts/set-rn-version.js --to-version ${expectedVersion} --build-type nightly`,
);
expect(echoMock).toHaveBeenCalledWith(
`Failed to set version number to ${expectedVersion}`,
);
expect(exitMock).toHaveBeenCalledWith(1);
expect(execMock.mock.calls).toHaveLength(1);
});
});
describe('release', () => {
it('should publish non-latest', () => {
execMock.mockReturnValueOnce({code: 0});
isTaggedLatestMock.mockReturnValueOnce(false);
process.env.CIRCLE_TAG = '0.81.1';
process.env.NPM_CONFIG_OTP = 'otp';
publishNpm('release');
const expectedVersion = '0.81.1';
expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith(
expectedVersion,
false,
);
expect(execMock).toHaveBeenCalledWith(
`npm publish --tag 0.81-stable --otp otp`,
{cwd: '../packages/react-native'},
);
expect(echoMock).toHaveBeenCalledWith(
`Published to npm ${expectedVersion}`,
);
expect(exitMock).toHaveBeenCalledWith(0);
expect(execMock.mock.calls).toHaveLength(1);
});
it('should publish latest stable', () => {
execMock.mockReturnValueOnce({code: 0});
isTaggedLatestMock.mockReturnValueOnce(true);
process.env.CIRCLE_TAG = '0.81.1';
process.env.NPM_CONFIG_OTP = 'otp';
publishNpm('release');
const expectedVersion = '0.81.1';
expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith(
expectedVersion,
false,
);
expect(execMock).toHaveBeenCalledWith(
`npm publish --tag latest --otp ${process.env.NPM_CONFIG_OTP}`,
{cwd: '../packages/react-native'},
);
expect(echoMock).toHaveBeenCalledWith(
`Published to npm ${expectedVersion}`,
);
expect(exitMock).toHaveBeenCalledWith(0);
expect(execMock.mock.calls).toHaveLength(1);
});
it('should fail to publish latest stable', () => {
execMock.mockReturnValueOnce({code: 1});
isTaggedLatestMock.mockReturnValueOnce(true);
process.env.CIRCLE_TAG = '0.81.1';
process.env.NPM_CONFIG_OTP = 'otp';
publishNpm('release');
const expectedVersion = '0.81.1';
expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith(
expectedVersion,
false,
);
expect(execMock).toHaveBeenCalledWith(
`npm publish --tag latest --otp ${process.env.NPM_CONFIG_OTP}`,
{cwd: '../packages/react-native'},
);
expect(echoMock).toHaveBeenCalledWith(`Failed to publish package to npm`);
expect(exitMock).toHaveBeenCalledWith(1);
expect(execMock.mock.calls).toHaveLength(1);
});
it('should publish next', () => {
execMock.mockReturnValueOnce({code: 0});
isTaggedLatestMock.mockReturnValueOnce(true);
process.env.CIRCLE_TAG = '0.81.0-rc.4';
process.env.NPM_CONFIG_OTP = 'otp';
publishNpm('release');
const expectedVersion = '0.81.0-rc.4';
expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith(
expectedVersion,
false,
);
expect(execMock).toHaveBeenCalledWith(
`npm publish --tag next --otp ${process.env.NPM_CONFIG_OTP}`,
{cwd: '../packages/react-native'},
);
expect(echoMock).toHaveBeenCalledWith(
`Published to npm ${expectedVersion}`,
);
expect(exitMock).toHaveBeenCalledWith(0);
expect(execMock.mock.calls).toHaveLength(1);
});
});
});

Просмотреть файл

@ -46,133 +46,136 @@ const fs = require('fs');
const path = require('path');
const yargs = require('yargs');
const buildTag = process.env.CIRCLE_TAG;
const otp = process.env.NPM_CONFIG_OTP;
const RN_PACKAGE_DIR = path.join(__dirname, '..', 'packages', 'react-native');
const argv = yargs
.option('n', {
alias: 'nightly',
type: 'boolean',
default: false,
})
.option('d', {
alias: 'dry-run',
type: 'boolean',
default: false,
})
.option('r', {
alias: 'release',
type: 'boolean',
default: false,
})
.strict().argv;
const nightlyBuild = argv.nightly;
const dryRunBuild = argv.dryRun;
const releaseBuild = argv.release;
const isCommitly = nightlyBuild || dryRunBuild;
function publishNpm(buildType) {
// 34c034298dc9cad5a4553964a5a324450fda0385
const currentCommit = getCurrentCommit();
const shortCommit = currentCommit.slice(0, 9);
const buildType = releaseBuild
? 'release'
: nightlyBuild
? 'nightly'
: 'dry-run';
const rawVersion =
// 0.0.0 triggers issues with cocoapods for codegen when building template project.
buildType === 'dry-run'
? '1000.0.0'
: // For nightly we continue to use 0.0.0 for clarity for npm
buildType === 'nightly'
? '0.0.0'
: // For pre-release and stable releases, we use the git tag of the version we're releasing (set in bump-oss-version)
process.env.CIRCLE_TAG;
// 34c034298dc9cad5a4553964a5a324450fda0385
const currentCommit = getCurrentCommit();
const shortCommit = currentCommit.slice(0, 9);
let version,
major,
minor,
prerelease = null;
try {
({version, major, minor, prerelease} = parseVersion(rawVersion, buildType));
} catch (e) {
echo(e.message);
return exit(1);
}
const rawVersion =
// 0.0.0 triggers issues with cocoapods for codegen when building template project.
dryRunBuild
? '1000.0.0'
: // For nightly we continue to use 0.0.0 for clarity for npm
nightlyBuild
? '0.0.0'
: // For pre-release and stable releases, we use the git tag of the version we're releasing (set in bump-oss-version)
buildTag;
let releaseVersion;
if (buildType === 'dry-run') {
releaseVersion = `${version}-${shortCommit}`;
} else if (buildType === 'nightly') {
// 2021-09-28T05:38:40.669Z -> 20210928-0538
const dateIdentifier = new Date()
.toISOString()
.slice(0, -8)
.replace(/[-:]/g, '')
.replace(/[T]/g, '-');
releaseVersion = `${version}-${dateIdentifier}-${shortCommit}`;
} else {
releaseVersion = version;
}
let version,
major,
minor,
prerelease = null;
try {
({version, major, minor, prerelease} = parseVersion(rawVersion, buildType));
} catch (e) {
echo(e.message);
exit(1);
}
// Bump version number in various files (package.json, gradle.properties etc)
// For stable, pre-release releases, we rely on CircleCI job `prepare_package_for_release` to handle this
if (buildType === 'nightly' || buildType === 'dry-run') {
if (
exec(
`node scripts/set-rn-version.js --to-version ${releaseVersion} --build-type ${buildType}`,
).code
) {
echo(`Failed to set version number to ${releaseVersion}`);
return exit(1);
}
}
let releaseVersion;
if (dryRunBuild) {
releaseVersion = `${version}-${shortCommit}`;
} else if (nightlyBuild) {
// 2021-09-28T05:38:40.669Z -> 20210928-0538
const dateIdentifier = new Date()
.toISOString()
.slice(0, -8)
.replace(/[-:]/g, '')
.replace(/[T]/g, '-');
releaseVersion = `${version}-${dateIdentifier}-${shortCommit}`;
} else {
releaseVersion = version;
}
generateAndroidArtifacts(releaseVersion);
// Bump version number in various files (package.json, gradle.properties etc)
// For stable, pre-release releases, we rely on CircleCI job `prepare_package_for_release` to handle this
if (isCommitly) {
if (
exec(
`node scripts/set-rn-version.js --to-version ${releaseVersion} --build-type ${buildType}`,
).code
) {
echo(`Failed to set version number to ${releaseVersion}`);
exit(1);
// Write version number to the build folder
const releaseVersionFile = path.join('build', '.version');
fs.writeFileSync(releaseVersionFile, releaseVersion);
if (buildType === 'dry-run') {
echo('Skipping `npm publish` because --dry-run is set.');
return exit(0);
}
// Running to see if this commit has been git tagged as `latest`
const isLatest = exitIfNotOnGit(
() => isTaggedLatest(currentCommit),
'Not in git. We do not want to publish anything',
);
// We first publish on Maven Central all the necessary artifacts.
// NPM publishing is done just after.
publishAndroidArtifactsToMaven(releaseVersion, buildType === 'nightly');
const releaseBranch = `${major}.${minor}-stable`;
// Set the right tag for nightly and prerelease builds
// If a release is not git-tagged as `latest` we use `releaseBranch` to prevent
// npm from overriding the current `latest` version tag, which it will do if no tag is set.
const tagFlag =
buildType === 'nightly'
? '--tag nightly'
: prerelease != null
? '--tag next'
: isLatest
? '--tag latest'
: `--tag ${releaseBranch}`;
// use otp from envvars if available
const otp = process.env.NPM_CONFIG_OTP;
const otpFlag = otp ? ` --otp ${otp}` : '';
if (exec(`npm publish ${tagFlag}${otpFlag}`, {cwd: RN_PACKAGE_DIR}).code) {
echo('Failed to publish package to npm');
return exit(1);
} else {
echo(`Published to npm ${releaseVersion}`);
return exit(0);
}
}
generateAndroidArtifacts(releaseVersion);
if (require.main === module) {
const argv = yargs
.option('n', {
alias: 'nightly',
type: 'boolean',
default: false,
})
.option('d', {
alias: 'dry-run',
type: 'boolean',
default: false,
})
.option('r', {
alias: 'release',
type: 'boolean',
default: false,
})
.strict().argv;
// Write version number to the build folder
const releaseVersionFile = path.join('build', '.version');
fs.writeFileSync(releaseVersionFile, releaseVersion);
const buildType = argv.release
? 'release'
: argv.nightly
? 'nightly'
: 'dry-run';
if (dryRunBuild) {
echo('Skipping `npm publish` because --dry-run is set.');
exit(0);
publishNpm(buildType);
}
// Running to see if this commit has been git tagged as `latest`
const isLatest = exitIfNotOnGit(
() => isTaggedLatest(currentCommit),
'Not in git. We do not want to publish anything',
);
// We first publish on Maven Central all the necessary artifacts.
// NPM publishing is done just after.
publishAndroidArtifactsToMaven(releaseVersion, nightlyBuild);
const releaseBranch = `${major}.${minor}-stable`;
// Set the right tag for nightly and prerelease builds
// If a release is not git-tagged as `latest` we use `releaseBranch` to prevent
// npm from overriding the current `latest` version tag, which it will do if no tag is set.
const tagFlag = nightlyBuild
? '--tag nightly'
: prerelease != null
? '--tag next'
: isLatest
? '--tag latest'
: `--tag ${releaseBranch}`;
// use otp from envvars if available
const otpFlag = otp ? `--otp ${otp}` : '';
if (exec(`npm publish ${tagFlag} ${otpFlag}`, {cwd: RN_PACKAGE_DIR}).code) {
echo('Failed to publish package to npm');
exit(1);
} else {
echo(`Published to npm ${releaseVersion}`);
exit(0);
}
module.exports = publishNpm;