diff --git a/scripts/releases-ci/__tests__/publish-npm-test.js b/scripts/releases-ci/__tests__/publish-npm-test.js index bf73c8917b4c85..78de2089177083 100644 --- a/scripts/releases-ci/__tests__/publish-npm-test.js +++ b/scripts/releases-ci/__tests__/publish-npm-test.js @@ -19,6 +19,9 @@ const publishAndroidArtifactsToMavenMock = jest.fn(); const removeNewArchFlags = jest.fn(); const env = process.env; +const publishPackageMock = jest.fn(); +const getNpmInfoMock = jest.fn(); + jest .mock('shelljs', () => ({ exec: execMock, @@ -96,10 +99,34 @@ describe('publish-npm', () => { }); describe('nightly', () => { + let consoleLog; + beforeAll(() => { + consoleLog = console.log; + console.log = jest.fn(); + jest.mock('../../npm-utils', () => ({ + ...jest.requireActual('../../npm-utils'), + publishPackage: publishPackageMock, + getNpmInfo: getNpmInfoMock, + })); + }); + + afterAll(() => { + console.log = consoleLog; + jest.unmock('../../npm-utils'); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + it('should publish', async () => { - execMock - .mockReturnValueOnce({stdout: '0.81.0-rc.1\n', code: 0}) - .mockReturnValueOnce({code: 0}); + publishPackageMock.mockImplementation(() => ({ + code: 0, + })); + getNpmInfoMock.mockImplementation(() => ({ + version: expectedVersion, + tag: 'nightly', + })); const expectedVersion = '0.82.0-nightly-20230420-currentco'; await publishNpm('nightly'); @@ -110,10 +137,16 @@ describe('publish-npm', () => { expectedVersion, 'nightly', ); - expect(execMock.mock.calls[0][0]).toBe( - `npm view react-native@next version`, + publishPackageMock.mock.calls.forEach(params => { + expect(params[1]).toEqual({ + tags: ['nightly'], + otp: undefined, + }); + }); + expect(publishPackageMock).toHaveBeenCalledWith( + path.join(REPO_ROOT, 'packages/react-native'), + {otp: undefined, tags: ['nightly']}, ); - expect(execMock.mock.calls[1][0]).toBe('npm publish --tag nightly'); expect(echoMock).toHaveBeenCalledWith( `Published to npm ${expectedVersion}`, ); @@ -121,8 +154,14 @@ describe('publish-npm', () => { }); it('should fail to set version', async () => { - execMock.mockReturnValueOnce({stdout: '0.81.0-rc.1\n', code: 0}); const expectedVersion = '0.82.0-nightly-20230420-currentco'; + publishPackageMock.mockImplementation(() => ({ + code: 0, + })); + getNpmInfoMock.mockImplementation(() => ({ + version: expectedVersion, + tag: 'nightly', + })); setVersionMock.mockImplementation(() => { throw new Error('something went wrong'); }); @@ -131,14 +170,40 @@ describe('publish-npm', () => { expect(removeNewArchFlags).not.toHaveBeenCalled(); expect(publishAndroidArtifactsToMavenMock).not.toBeCalled(); - expect(execMock.mock.calls[0][0]).toBe( - `npm view react-native@next version`, - ); expect(consoleErrorMock).toHaveBeenCalledWith( `Failed to set version number to ${expectedVersion}`, ); expect(exitMock).toHaveBeenCalledWith(1); }); + it('should fail to publish react-native if some monorepo packages fail', async () => { + publishPackageMock.mockImplementation(packagePath => ({ + code: 1, + })); + + getNpmInfoMock.mockImplementation(() => ({ + version: expectedVersion, + tag: 'nightly', + })); + + const expectedVersion = '0.82.0-nightly-20230420-currentco'; + + await publishNpm('nightly'); + + expect(removeNewArchFlags).not.toHaveBeenCalled(); + expect(setVersionMock).toBeCalledWith(expectedVersion); + expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith( + expectedVersion, + 'nightly', + ); + expect(exitMock).toHaveBeenCalledWith(1); + publishPackageMock.mock.calls.forEach(params => { + expect(params[1]).toEqual({ + tags: ['nightly'], + otp: undefined, + }); + }); + expect(echoMock).toHaveBeenCalledWith('Failed to publish package to npm'); + }); }); describe('release', () => { diff --git a/scripts/releases-ci/publish-npm.js b/scripts/releases-ci/publish-npm.js index 17a663665f1b29..6ce3231a7cd6b0 100755 --- a/scripts/releases-ci/publish-npm.js +++ b/scripts/releases-ci/publish-npm.js @@ -19,6 +19,7 @@ const {getNpmInfo, publishPackage} = require('../npm-utils'); const {removeNewArchFlags} = require('../releases/remove-new-arch-flags'); const {setReactNativeVersion} = require('../releases/set-rn-version'); const setVersion = require('../releases/set-version'); +const {getPackages} = require('../releases/utils/monorepo'); const { generateAndroidArtifacts, publishAndroidArtifactsToMaven, @@ -67,6 +68,32 @@ async function main() { await publishNpm(buildType); } +async function publishMonorepoPackages(tag /*: ?string */) { + const projectInfo = await getPackages({ + includePrivate: false, + includeReactNative: false, + }); + + for (const packageInfo of Object.values(projectInfo)) { + console.log(`Publishing ${packageInfo.name}...`); + const result = publishPackage(packageInfo.path, { + // $FlowFixMe[incompatible-call] + tags: [tag], + otp: process.env.NPM_CONFIG_OTP, + }); + + const spec = `${packageInfo.name}@${packageInfo.packageJson.version}`; + + if (result.code) { + echo(`Failed to publish ${spec} to npm. Stopping all nightly publishes`); + exit(1); + } else { + echo(`Published ${spec} to npm`); + exit(0); + } + } +} + async function publishNpm(buildType /*: BuildType */) /*: Promise */ { const {version, tag} = getNpmInfo(buildType); @@ -80,6 +107,7 @@ async function publishNpm(buildType /*: BuildType */) /*: Promise */ { if (buildType === 'nightly') { // Set same version for all monorepo packages await setVersion(version); + await publishMonorepoPackages(tag); } else { await setReactNativeVersion(version, null, buildType); }