Tests for publish-npm (#37029)
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:
Родитель
c2be0ad40e
Коммит
7b62bcbf9d
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче