diff --git a/.circleci/configurations/jobs.yml b/.circleci/configurations/jobs.yml index b97865fc450..4828dd2f958 100644 --- a/.circleci/configurations/jobs.yml +++ b/.circleci/configurations/jobs.yml @@ -1068,18 +1068,8 @@ jobs: export ORG_GRADLE_PROJECT_reactNativeArchitectures="armeabi-v7a,arm64-v8a,x86,x86_64" fi node ./scripts/releases-ci/publish-npm.js -t << parameters.release_type >> - - - run: - name: Zip Maven Artifacts from /tmp/maven-local - command: zip -r /tmp/maven-local.zip /tmp/maven-local - - store_artifacts: - path: /tmp/maven-local.zip - store_artifacts: path: /root/.npm/_logs - - persist_to_workspace: - root: /tmp - paths: - - maven-local # START: Dry-run # Provide a react-native package for this commit as a Circle CI release artifact. diff --git a/.github/actions/build-android/action.yml b/.github/actions/build-android/action.yml index 5db89fd1c0b..5295b8daf65 100644 --- a/.github/actions/build-android/action.yml +++ b/.github/actions/build-android/action.yml @@ -25,17 +25,23 @@ runs: - name: Build and publish all the Android Artifacts to /tmp/maven-local shell: bash run: | - # By default we only build ARM64 to save time/resources. For release/nightlies/prealpha, we override this value to build all archs. if [[ "${{ inputs.RELEASE_TYPE }}" == "dry-run" ]]; then + # dry-run: we only build ARM64 to save time/resources. For release/nightlies the default is to build all archs. export ORG_GRADLE_PROJECT_reactNativeArchitectures="arm64-v8a" + TASKS="publishAllToMavenTempLocal build" + elif [[ "${{ inputs.RELEASE_TYPE }}" == "nightly" ]]; then + # nightly: we set isSnapshot to true so artifacts are sent to the right repository on Maven Central. + export ORG_GRADLE_PROJECT_isSnapshot="true" + TASKS="publishAllToMavenTempLocal publishAndroidToSonatype build" else - export ORG_GRADLE_PROJECT_reactNativeArchitectures="armeabi-v7a,arm64-v8a,x86,x86_64" + # release: we want to build all archs (default) + TASKS="publishAllToMavenTempLocal publishAndroidToSonatype build" fi - ./gradlew publishAllToMavenTempLocal build -PenableWarningsAsErrors=true + ./gradlew $TASKS -PenableWarningsAsErrors=true - name: Upload Maven Artifacts uses: actions/upload-artifact@v4 with: - name: maven-local-build-android + name: maven-local path: /tmp/maven-local - name: Upload test results if: ${{ always() }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3d6dd7ec20e..6b9f0003314 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -280,14 +280,6 @@ jobs: echo "GRADLE_OPTS = $GRADLE_OPTS" export ORG_GRADLE_PROJECT_reactNativeArchitectures="armeabi-v7a,arm64-v8a,x86,x86_64" node ./scripts/releases-ci/publish-npm.js -t nightly - - name: Zip Maven Artifacts from /tmp/maven-local - working-directory: /tmp - run: zip -r maven-local.zip maven-local - - name: Upload Maven Artifacts - uses: actions/upload-artifact@v4 - with: - name: maven-local - path: /tmp/maven-local.zip - name: Upload npm logs uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 7cb71a5c643..9bfc27a2350 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -54,7 +54,6 @@ jobs: HERMES_VERSION: ${{ needs.prepare_hermes_workspace.output.HERMES_VERSION }} REACT_NATIVE_VERSION: ${{ needs.prepare_hermes_workspace.output.REACT_NATIVE_VERSION }} - build_apple_slices_hermes: runs-on: macos-14 needs: [build_hermesc_apple, prepare_hermes_workspace] @@ -286,14 +285,6 @@ jobs: echo "GRADLE_OPTS = $GRADLE_OPTS" export ORG_GRADLE_PROJECT_reactNativeArchitectures="armeabi-v7a,arm64-v8a,x86,x86_64" node ./scripts/releases-ci/publish-npm.js -t release - - name: Zip Maven Artifacts from /tmp/maven-local - working-directory: /tmp - run: zip -r maven-local.zip maven-local - - name: Upload Maven Artifacts - uses: actions/upload-artifact@v4 - with: - name: maven-local - path: /tmp/maven-local.zip - name: Upload npm logs uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index 866e9967588..a7ce2e0f054 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -212,7 +212,6 @@ jobs: HERMES_VERSION: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }} REACT_NATIVE_VERSION: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }} - build_android: runs-on: 8-core-ubuntu needs: [set_release_type] @@ -366,14 +365,6 @@ jobs: export ORG_GRADLE_PROJECT_reactNativeArchitectures="armeabi-v7a,arm64-v8a,x86,x86_64" fi node ./scripts/releases-ci/publish-npm.js -t ${{ needs.set_release_type.outputs.RELEASE_TYPE }} - - name: Zip Maven Artifacts from /tmp/maven-local - working-directory: /tmp - run: zip -r maven-local.zip maven-local - - name: Upload Maven Artifacts - uses: actions/upload-artifact@v4 - with: - name: maven-local - path: /tmp/maven-local.zip - name: Upload npm logs uses: actions/upload-artifact@v4 with: diff --git a/build.gradle.kts b/build.gradle.kts index b967428d102..d848efee0ab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -97,10 +97,9 @@ tasks.register("publishAllToMavenTempLocal") { ":packages:react-native:ReactAndroid:hermes-engine:publishAllPublicationsToMavenTempLocalRepository") } -tasks.register("publishAllToSonatype") { - description = "Publish all the artifacts to Sonatype (Maven Central or Snapshot repository)" +tasks.register("publishAndroidToSonatype") { + description = "Publish the Android artifacts to Sonatype (Maven Central or Snapshot repository)" dependsOn(":packages:react-native:ReactAndroid:publishToSonatype") - dependsOn(":packages:react-native:ReactAndroid:external-artifacts:publishToSonatype") dependsOn(":packages:react-native:ReactAndroid:hermes-engine:publishToSonatype") } diff --git a/scripts/releases-ci/__tests__/publish-npm-test.js b/scripts/releases-ci/__tests__/publish-npm-test.js index dfc2f0bbca5..742547e844e 100644 --- a/scripts/releases-ci/__tests__/publish-npm-test.js +++ b/scripts/releases-ci/__tests__/publish-npm-test.js @@ -15,6 +15,7 @@ const isTaggedLatestMock = jest.fn(); const setVersionMock = jest.fn(); const updateReactNativeArtifactsMock = jest.fn(); const publishAndroidArtifactsToMavenMock = jest.fn(); +const publishExternalArtifactsToMavenMock = jest.fn(); const removeNewArchFlags = jest.fn(); const env = process.env; const publishPackageMock = jest.fn(); @@ -42,6 +43,7 @@ describe('publish-npm', () => { .mock('../../releases/utils/release-utils', () => ({ generateAndroidArtifacts: generateAndroidArtifactsMock, publishAndroidArtifactsToMaven: publishAndroidArtifactsToMavenMock, + publishExternalArtifactsToMaven: publishExternalArtifactsToMavenMock, })) .mock('../../releases/set-version', () => ({ setVersion: setVersionMock, @@ -106,13 +108,16 @@ describe('publish-npm', () => { expect(setVersionMock).not.toBeCalled(); expect(updateReactNativeArtifactsMock).toBeCalledWith(version, 'dry-run'); - expect(generateAndroidArtifactsMock).toBeCalledWith(version); + // Generate Android artifacts is now delegate to build_android entirely + expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); + expect(consoleLogMock).toHaveBeenCalledWith( 'Skipping `npm publish` because --dry-run is set.', ); // Expect termination expect(publishAndroidArtifactsToMavenMock).not.toHaveBeenCalled(); + expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled(); expect(publishPackageMock).not.toHaveBeenCalled(); }); }); @@ -156,7 +161,10 @@ describe('publish-npm', () => { expect(removeNewArchFlags).not.toHaveBeenCalled(); expect(setVersionMock).toBeCalledWith(expectedVersion); - expect(generateAndroidArtifactsMock).toHaveBeenCalled(); + + // Generate Android artifacts is now delegate to build_android entirely + expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); + expect(publishPackageMock.mock.calls).toEqual([ [ 'path/to/monorepo/pkg-a', @@ -175,6 +183,10 @@ describe('publish-npm', () => { expectedVersion, 'nightly', ); + expect(publishExternalArtifactsToMavenMock).toHaveBeenCalledWith( + expectedVersion, + 'nightly', + ); expect(consoleLogMock.mock.calls).toEqual([ ['Publishing monorepo/pkg-a...'], [`Published monorepo/pkg-a@${expectedVersion} to npm`], @@ -205,6 +217,7 @@ describe('publish-npm', () => { expect(publishPackageMock).not.toBeCalled(); expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); expect(publishAndroidArtifactsToMavenMock).not.toBeCalled(); + expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled(); }); it('should fail to publish react-native if some monorepo packages fail', async () => { @@ -269,6 +282,7 @@ describe('publish-npm', () => { ['Publishing monorepo/pkg-b...'], ]); expect(publishAndroidArtifactsToMavenMock).not.toHaveBeenCalled(); + expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled(); }); }); @@ -290,11 +304,18 @@ describe('publish-npm', () => { expect(removeNewArchFlags).not.toHaveBeenCalled(); expect(updateReactNativeArtifactsMock).not.toHaveBeenCalled(); expect(setVersionMock).not.toBeCalled(); - expect(generateAndroidArtifactsMock).toHaveBeenCalled(); + + // Generate Android artifacts is now delegate to build_android entirely + expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); + expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith( expectedVersion, 'release', ); + expect(publishExternalArtifactsToMavenMock).toHaveBeenCalledWith( + expectedVersion, + 'release', + ); expect(publishPackageMock.mock.calls).toEqual([ [ @@ -325,11 +346,18 @@ describe('publish-npm', () => { expect(removeNewArchFlags).not.toHaveBeenCalled(); expect(setVersionMock).not.toBeCalled(); expect(updateReactNativeArtifactsMock).not.toBeCalled(); - expect(generateAndroidArtifactsMock).toHaveBeenCalled(); + + // Generate Android artifacts is now delegate to build_android entirely + expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); + expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith( expectedVersion, 'release', ); + expect(publishExternalArtifactsToMavenMock).toHaveBeenCalledWith( + expectedVersion, + 'release', + ); expect(publishPackageMock.mock.calls).toEqual([ [ @@ -367,11 +395,18 @@ describe('publish-npm', () => { expect(removeNewArchFlags).not.toHaveBeenCalled(); expect(setVersionMock).not.toBeCalled(); expect(updateReactNativeArtifactsMock).not.toHaveBeenCalled(); - expect(generateAndroidArtifactsMock).toHaveBeenCalled(); + + // Generate Android artifacts is now delegate to build_android entirely + expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); + expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith( expectedVersion, 'release', ); + expect(publishExternalArtifactsToMavenMock).toHaveBeenCalledWith( + expectedVersion, + 'release', + ); expect(publishPackageMock.mock.calls).toEqual([ [ @@ -399,11 +434,18 @@ describe('publish-npm', () => { expect(removeNewArchFlags).not.toHaveBeenCalled(); expect(updateReactNativeArtifactsMock).not.toHaveBeenCalled(); expect(setVersionMock).not.toBeCalled(); - expect(generateAndroidArtifactsMock).toHaveBeenCalled(); + + // Generate Android artifacts is now delegate to build_android entirely + expect(generateAndroidArtifactsMock).not.toHaveBeenCalled(); + expect(publishAndroidArtifactsToMavenMock).toHaveBeenCalledWith( expectedVersion, 'release', ); + expect(publishExternalArtifactsToMavenMock).toHaveBeenCalledWith( + expectedVersion, + 'release', + ); expect(publishPackageMock.mock.calls).toEqual([ [ diff --git a/scripts/releases-ci/publish-npm.js b/scripts/releases-ci/publish-npm.js index 6228fbbb5e3..9bede4388c7 100755 --- a/scripts/releases-ci/publish-npm.js +++ b/scripts/releases-ci/publish-npm.js @@ -23,8 +23,8 @@ const { } = require('../releases/set-rn-artifacts-version'); const {setVersion} = require('../releases/set-version'); const { - generateAndroidArtifacts, publishAndroidArtifactsToMaven, + publishExternalArtifactsToMaven, } = require('../releases/utils/release-utils'); const {getPackages} = require('../utils/monorepo'); const path = require('path'); @@ -111,14 +111,16 @@ async function publishNpm(buildType /*: BuildType */) /*: Promise */ { } } - generateAndroidArtifacts(version); - if (buildType === 'dry-run') { console.log('Skipping `npm publish` because --dry-run is set.'); return; } - // We first publish on Maven Central all the necessary artifacts. + // We first publish on Maven Central the external artifacts + // produced by iOS + publishExternalArtifactsToMaven(version, buildType); + + // We the publish on Maven Central all the Android artifacts. // NPM publishing is done just after. publishAndroidArtifactsToMaven(version, buildType); diff --git a/scripts/releases/utils/release-utils.js b/scripts/releases/utils/release-utils.js index 1f5e4304f27..92af4d088e7 100644 --- a/scripts/releases/utils/release-utils.js +++ b/scripts/releases/utils/release-utils.js @@ -62,24 +62,45 @@ function publishAndroidArtifactsToMaven( releaseVersion /*: string */, buildType /*: BuildType */, ) { - // -------- Publish every artifact to Maven Central - // The GPG key is base64 encoded on CircleCI and then decoded here - // $FlowFixMe[prop-missing] - let buff = Buffer.from(env.ORG_GRADLE_PROJECT_SIGNING_KEY_ENCODED, 'base64'); - // $FlowFixMe[prop-missing] - env.ORG_GRADLE_PROJECT_SIGNING_KEY = buff.toString('ascii'); - // We want to gate ourselves against accidentally publishing a 1.x or a 1000.x on // maven central which will break the semver for our artifacts. if (buildType === 'release' && releaseVersion.startsWith('0.')) { // -------- For stable releases, we also need to close and release the staging repository. if ( exec( - './gradlew publishAllToSonatype closeAndReleaseSonatypeStagingRepository', + './gradlew findSonatypeStagingRepository closeAndReleaseSonatypeStagingRepository', ).code ) { echo( - 'Failed to close and release the staging repository on Sonatype (Maven Central)', + 'Failed to close and release the staging repository on Sonatype (Maven Central) for Android artifacts', + ); + exit(1); + } + } else { + echo( + 'Nothing to do as this is not a stable release - Nightlies Android artifacts are published by build_android', + ); + } + + echo('Finished publishing Android artifacts to Maven Central'); +} + +function publishExternalArtifactsToMaven( + releaseVersion /*: string */, + buildType /*: BuildType */, +) { + // We want to gate ourselves against accidentally publishing a 1.x or a 1000.x on + // maven central which will break the semver for our artifacts. + if (buildType === 'release' && releaseVersion.startsWith('0.')) { + // -------- For stable releases, we do the publishing and close the staging repository. + // This can't be done earlier in build_android because this artifact are partially built by the iOS jobs. + if ( + exec( + './gradlew :packages:react-native:ReactAndroid:external-artifacts:publishToSonatype :packages:react-native:ReactAndroid:external-artifacts:closeAndReleaseSonatypeStagingRepository', + ).code + ) { + echo( + 'Failed to close and release the staging repository on Sonatype (Maven Central) for external artifacts', ); exit(1); } @@ -88,15 +109,16 @@ function publishAndroidArtifactsToMaven( // -------- For nightly releases, we only need to publish the snapshot to Sonatype snapshot repo. if ( exec( - './gradlew publishAllToSonatype -PisSnapshot=' + isSnapshot.toString(), + './gradlew :packages:react-native:ReactAndroid:external-artifacts:publishToSonatype -PisSnapshot=' + + isSnapshot.toString(), ).code ) { - echo('Failed to publish artifacts to Sonatype (Maven Central)'); + echo('Failed to publish external artifacts to Sonatype (Maven Central)'); exit(1); } } - echo('Published artifacts to Maven Central'); + echo('Finished publishing external artifacts to Maven Central'); } function generateiOSArtifacts( @@ -156,5 +178,6 @@ module.exports = { generateAndroidArtifacts, generateiOSArtifacts, publishAndroidArtifactsToMaven, + publishExternalArtifactsToMaven, failIfTagExists, };