From 625f4e3285d4f7faf51d38c816326c6c3c3b1b2b Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Tue, 28 Jul 2026 10:25:13 +0100 Subject: [PATCH 1/5] [LOCAL] Set stagingProfileId to com.facebook --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index 2154d20f22ed..21672243c832 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,6 +59,7 @@ nexusPublishing { sonatype { username.set(sonatypeUsername) password.set(sonatypePassword) + stagingProfileId.set("com.facebook") nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) } From bb93fbfef6d99fbe4b324acb930bde374a7e9ba0 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 28 Jul 2026 10:25:14 +0100 Subject: [PATCH 2/5] Enable npm trusted publishing on 0.85 --- .github/actions/build-npm-package/action.yml | 16 +- .github/actions/setup-node/action.yml | 10 +- .github/workflows/bump-podfile-lock.yml | 2 +- .github/workflows/nightly.yml | 96 ------ .github/workflows/publish-bumped-packages.yml | 29 -- .github/workflows/publish-npm.yml | 300 ++++++++++++++++++ .github/workflows/publish-release.yml | 151 --------- .../package.json | 5 + .../releases-ci/__tests__/publish-npm-test.js | 20 ++ .../publish-updated-packages-test.js | 18 ++ .../validate-npm-package-metadata-test.js | 95 ++++++ scripts/releases-ci/publish-npm.js | 5 + .../releases-ci/publish-updated-packages.js | 5 + .../validate-npm-package-metadata.js | 82 +++++ 14 files changed, 546 insertions(+), 288 deletions(-) delete mode 100644 .github/workflows/nightly.yml delete mode 100644 .github/workflows/publish-bumped-packages.yml create mode 100644 .github/workflows/publish-npm.yml delete mode 100644 .github/workflows/publish-release.yml create mode 100644 scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js create mode 100644 scripts/releases-ci/validate-npm-package-metadata.js diff --git a/.github/actions/build-npm-package/action.yml b/.github/actions/build-npm-package/action.yml index 6bacb3d29dde..281347ae9621 100644 --- a/.github/actions/build-npm-package/action.yml +++ b/.github/actions/build-npm-package/action.yml @@ -4,10 +4,6 @@ inputs: release-type: required: true description: The type of release we are building. It could be nightly, release or dry-run - gha-npm-token: - required: false - description: The GHA npm token, required only to publish to npm - default: '' gradle-cache-encryption-key: description: The encryption key needed to store the Gradle Configuration cache @@ -38,6 +34,9 @@ runs: cache-encryption-key: ${{ inputs.gradle-cache-encryption-key }} - name: Setup node.js uses: ./.github/actions/setup-node + with: + node-version: '24' + registry-url: 'https://registry.npmjs.org' - name: Install dependencies uses: ./.github/actions/yarn-install - name: Build packages @@ -46,12 +45,9 @@ runs: - name: Build types shell: bash run: yarn build-types --skip-snapshot - # Continue with publish steps - - name: Set npm credentials - if: ${{ inputs.release-type == 'release' || - inputs.release-type == 'nightly' }} - shell: bash - run: echo "//registry.npmjs.org/:_authToken=${{ inputs.gha-npm-token }}" > ~/.npmrc + # `npm publish` below authenticates via npm Trusted Publishing (OIDC). + # The caller (the `publish-npm.yml` workflow) MUST grant `id-token: write`; + # this composite action runs inside that job. - name: Publish NPM shell: bash run: | diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index 7b5aa82470a4..49b773d4cef5 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -5,11 +5,19 @@ inputs: description: 'The node.js version to use' required: false default: '22.14.0' + registry-url: + description: | + Optional npm registry URL passed through to actions/setup-node. Set on + jobs that publish to npm so setup-node writes a `.npmrc` configured to + pick up the OIDC-minted token from npm Trusted Publishing. + required: false + default: '' runs: - using: "composite" + using: 'composite' steps: - name: Setup node.js uses: actions/setup-node@v6 with: node-version: ${{ inputs.node-version }} cache: yarn + registry-url: ${{ inputs.registry-url }} diff --git a/.github/workflows/bump-podfile-lock.yml b/.github/workflows/bump-podfile-lock.yml index 94895d94e19b..9e5c42764430 100644 --- a/.github/workflows/bump-podfile-lock.yml +++ b/.github/workflows/bump-podfile-lock.yml @@ -5,7 +5,7 @@ on: jobs: bump-podfile-lock: - runs-on: macos-latest + runs-on: macos-15 steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml deleted file mode 100644 index 155a3e2c871d..000000000000 --- a/.github/workflows/nightly.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Nightly - -on: - workflow_dispatch: - # nightly build @ 2:15 AM UTC - schedule: - - cron: "15 2 * * *" - -jobs: - set_release_type: - runs-on: ubuntu-latest - if: github.repository == 'react/react-native' - outputs: - RELEASE_TYPE: ${{ steps.set_release_type.outputs.RELEASE_TYPE }} - env: - EVENT_NAME: ${{ github.event_name }} - REF: ${{ github.ref }} - steps: - - id: set_release_type - run: | - echo "Setting release type to nightly" - echo "RELEASE_TYPE=nightly" >> $GITHUB_OUTPUT - - prebuild_apple_dependencies: - if: github.repository == 'react/react-native' - uses: ./.github/workflows/prebuild-ios-dependencies.yml - secrets: inherit - - prebuild_react_native_core: - uses: ./.github/workflows/prebuild-ios-core.yml - with: - version-type: nightly - secrets: inherit - needs: [prebuild_apple_dependencies] - - build_android: - runs-on: ubuntu-latest - if: github.repository == 'react/react-native' - needs: [set_release_type] - container: - image: reactnativecommunity/react-native-android:latest - env: - TERM: "dumb" - # Set the encoding to resolve a known character encoding issue with decompressing tar.gz files in containers - # via Gradle: https://github.com/gradle/gradle/issues/23391#issuecomment-1878979127 - LC_ALL: C.UTF8 - GRADLE_OPTS: "-Dorg.gradle.daemon=false" - ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} - ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} - ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }} - ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }} - REACT_NATIVE_DOWNLOADS_DIR: /opt/react-native-downloads - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Build Android - uses: ./.github/actions/build-android - with: - release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }} - gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} - - build_npm_package: - runs-on: ubuntu-latest - needs: - [ - set_release_type, - build_android, - prebuild_apple_dependencies, - prebuild_react_native_core, - ] - container: - image: reactnativecommunity/react-native-android:latest - env: - TERM: "dumb" - GRADLE_OPTS: "-Dorg.gradle.daemon=false" - # Set the encoding to resolve a known character encoding issue with decompressing tar.gz files in containers - # via Gradle: https://github.com/gradle/gradle/issues/23391#issuecomment-1878979127 - LC_ALL: C.UTF8 - # By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs. - ORG_GRADLE_PROJECT_reactNativeArchitectures: "arm64-v8a" - REACT_NATIVE_DOWNLOADS_DIR: /opt/react-native-downloads - env: - GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }} - ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} - ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} - ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }} - ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }} - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Build and Publish NPM Package - uses: ./.github/actions/build-npm-package - with: - release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }} - gha-npm-token: ${{ env.GHA_NPM_TOKEN }} - gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} diff --git a/.github/workflows/publish-bumped-packages.yml b/.github/workflows/publish-bumped-packages.yml deleted file mode 100644 index 612a86917631..000000000000 --- a/.github/workflows/publish-bumped-packages.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Publish Bumped Packages - -on: - push: - branches: - - "main" - - "*-stable" - -jobs: - publish_bumped_packages: - runs-on: ubuntu-latest - if: github.repository == 'react/react-native' - env: - GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }} - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Setup node.js - uses: ./.github/actions/setup-node - - name: Run Yarn Install - uses: ./.github/actions/yarn-install - - name: Build packages - run: yarn build - - name: Build types - run: yarn build-types --skip-snapshot - - name: Set NPM auth token - run: echo "//registry.npmjs.org/:_authToken=$GHA_NPM_TOKEN" > ~/.npmrc - - name: Find and publish all bumped packages - run: node ./scripts/releases-ci/publish-updated-packages.js diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml new file mode 100644 index 000000000000..9c1dbc3bcf4a --- /dev/null +++ b/.github/workflows/publish-npm.yml @@ -0,0 +1,300 @@ +# Single top-level workflow for every npm publish in this repo. +# +# Why: npmjs.com Trusted Publishing matches the `workflow_ref` OIDC claim, +# which is always the TOP-LEVEL workflow filename. npm allows only ONE +# trusted publisher per package, so every `npm publish` must originate +# from the same top-level file. By consolidating all publish triggers +# here, the OIDC claim is always `publish-npm.yml`. +# +# This replaces the previous separate entry points: +# - publish-release.yml (tag push) -> mode=release +# - nightly.yml (cron/dispatch) -> mode=nightly +# - publish-bumped-packages.yml (main/stable branch push) -> mode=bumped-packages +# +# See https://docs.npmjs.com/trusted-publishers +name: Publish to npm + +on: + push: + tags: + - 'v0.*.*' # This should match v0.X.Y + - 'v0.*.*-rc.*' # This should match v0.X.Y-RC.0 + branches: + - 'main' + - '*-stable' + workflow_dispatch: + # nightly build @ 2:15 AM UTC + schedule: + - cron: '15 2 * * *' + +permissions: + contents: read + +jobs: + # Determine what kind of publish this is. + determine_mode: + runs-on: ubuntu-latest + if: github.repository == 'react/react-native' + outputs: + mode: ${{ steps.mode.outputs.mode }} + release-type: ${{ steps.mode.outputs.release-type }} + steps: + - id: mode + run: | + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "mode=release" >> $GITHUB_OUTPUT + echo "release-type=release" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "schedule" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "mode=nightly" >> $GITHUB_OUTPUT + echo "release-type=nightly" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "push" ]]; then + echo "mode=bumped-packages" >> $GITHUB_OUTPUT + echo "release-type=" >> $GITHUB_OUTPUT + fi + - run: | + echo "Mode: ${{ steps.mode.outputs.mode }}" + echo "Release type: ${{ steps.mode.outputs.release-type }}" + + # Release only: extract Hermes version for the draft release. + set_hermes_versions: + runs-on: ubuntu-latest + if: github.ref_type == 'tag' + outputs: + HERMES_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_VERSION }} + HERMES_V1_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v6 + - id: set_hermes_versions + run: | + echo "Setting hermes versions to latest" + hermes_version=$(grep -oE 'HERMES_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2) + hermes_v1_version=$(grep -oE 'HERMES_V1_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2) + + echo "HERMES_VERSION=$hermes_version" >> $GITHUB_OUTPUT + echo "HERMES_V1_VERSION=$hermes_v1_version" >> $GITHUB_OUTPUT + - name: Print hermes versions + run: | + echo "HERMES_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}" + echo "HERMES_V1_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}" + + # Apple prebuilds (release and nightly). + prebuild_apple_dependencies: + needs: [determine_mode] + if: needs.determine_mode.outputs.mode == 'release' || needs.determine_mode.outputs.mode == 'nightly' + uses: ./.github/workflows/prebuild-ios-dependencies.yml + secrets: inherit + + prebuild_react_native_core: + needs: [determine_mode, prebuild_apple_dependencies] + if: needs.determine_mode.outputs.mode == 'release' || needs.determine_mode.outputs.mode == 'nightly' + uses: ./.github/workflows/prebuild-ios-core.yml + secrets: inherit + with: + version-type: ${{ needs.determine_mode.outputs.mode == 'nightly' && 'nightly' || '' }} + + # Android build (nightly only; releases handle this in the + # build-npm-package action's Gradle step). + build_android: + needs: [determine_mode] + if: needs.determine_mode.outputs.mode == 'nightly' + runs-on: ubuntu-latest + container: + image: reactnativecommunity/react-native-android:latest + env: + TERM: 'dumb' + # Set the encoding to resolve a known character encoding issue with decompressing tar.gz files in containers + # via Gradle: https://github.com/gradle/gradle/issues/23391#issuecomment-1878979127 + LC_ALL: C.UTF8 + GRADLE_OPTS: '-Dorg.gradle.daemon=false' + ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} + ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} + ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }} + ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }} + REACT_NATIVE_DOWNLOADS_DIR: /opt/react-native-downloads + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Build Android + uses: ./.github/actions/build-android + with: + release-type: nightly + gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} + + # Build and publish react-native and all @react-native/* packages + # (release and nightly modes). + publish_react_native: + needs: + [ + determine_mode, + build_android, + prebuild_apple_dependencies, + prebuild_react_native_core, + ] + # For nightly, also wait on build_android. Use always() so this + # job isn't skipped when build_android is skipped (release mode). + # The explicit status checks below handle the real gating. + if: | + always() && + (needs.determine_mode.outputs.mode == 'release' || needs.determine_mode.outputs.mode == 'nightly') && + needs.determine_mode.result == 'success' && + needs.prebuild_apple_dependencies.result == 'success' && + needs.prebuild_react_native_core.result == 'success' && + (needs.determine_mode.outputs.mode == 'release' || needs.build_android.result == 'success') + runs-on: ubuntu-latest + environment: npm-publish + # `id-token: write` is required so the npm CLI can mint the OIDC + # token that npm Trusted Publishing exchanges for a publish token. + permissions: + contents: read + id-token: write + container: + image: reactnativecommunity/react-native-android:latest + env: + TERM: 'dumb' + # Set the encoding to resolve a known character encoding issue with decompressing tar.gz files in containers + # via Gradle: https://github.com/gradle/gradle/issues/23391#issuecomment-1878979127 + LC_ALL: C.UTF8 + GRADLE_OPTS: '-Dorg.gradle.daemon=false' + # By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs. + ORG_GRADLE_PROJECT_reactNativeArchitectures: 'arm64-v8a' + REACT_NATIVE_DOWNLOADS_DIR: /opt/react-native-downloads + env: + ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} + ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} + ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }} + ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + - name: Build and Publish NPM Package + uses: ./.github/actions/build-npm-package + with: + release-type: ${{ needs.determine_mode.outputs.release-type }} + gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} + + # Publish bumped monorepo packages (main/stable push). + publish_bumped_packages: + needs: [determine_mode] + if: needs.determine_mode.outputs.mode == 'bumped-packages' + runs-on: ubuntu-latest + environment: npm-publish + # `id-token: write` is required so the npm CLI can mint the OIDC + # token that npm Trusted Publishing exchanges for a publish token. + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Setup node.js + uses: ./.github/actions/setup-node + with: + node-version: '24' + registry-url: 'https://registry.npmjs.org' + - name: Run Yarn Install + uses: ./.github/actions/yarn-install + - name: Build packages + run: yarn build + - name: Build types + run: yarn build-types --skip-snapshot + - name: Find and publish all bumped packages + run: node ./scripts/releases-ci/publish-updated-packages.js + + # Release-only post-publish steps. + post_publish: + runs-on: ubuntu-latest + needs: [determine_mode, publish_react_native] + if: | + always() && + needs.determine_mode.result == 'success' && + needs.publish_react_native.result == 'success' && + needs.determine_mode.outputs.mode == 'release' + env: + REACT_NATIVE_BOT_GITHUB_TOKEN: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + - name: Publish @react-native-community/template + id: publish-template-to-npm + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} + script: | + const {publishTemplate} = require('./.github/workflow-scripts/publishTemplate.js') + const version = "${{ github.ref_name }}" + const isDryRun = false + await publishTemplate(github, version, isDryRun); + - name: Wait for template to be published + timeout-minutes: 3 + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} + script: | + const {verifyPublishedTemplate, isLatest} = require('./.github/workflow-scripts/publishTemplate.js') + const version = "${{ github.ref_name }}" + await verifyPublishedTemplate(version, isLatest()); + - name: Update rn-diff-purge to generate upgrade-support diff + run: | + curl -X POST https://api.github.com/repos/react-native-community/rn-diff-purge/dispatches \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: Bearer $REACT_NATIVE_BOT_GITHUB_TOKEN" \ + -d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${{ github.ref_name }}\" }}" + - name: Verify Release is on NPM + timeout-minutes: 3 + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} + script: | + const {verifyReleaseOnNpm} = require('./.github/workflow-scripts/verifyReleaseOnNpm.js'); + const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js'); + const version = "${{ github.ref_name }}"; + await verifyReleaseOnNpm(version, isLatest()); + - name: Verify that artifacts are on Maven + uses: actions/github-script@v8 + with: + script: | + const {verifyArtifactsAreOnMaven} = require('./.github/workflow-scripts/verifyArtifactsAreOnMaven.js'); + const version = "${{ github.ref_name }}"; + await verifyArtifactsAreOnMaven(version); + + # Release-only changelog, Podfile bump, and draft release. + generate_changelog: + needs: [determine_mode, publish_react_native] + if: | + always() && + needs.determine_mode.result == 'success' && + needs.publish_react_native.result == 'success' && + needs.determine_mode.outputs.mode == 'release' + uses: ./.github/workflows/generate-changelog.yml + secrets: inherit + + bump_podfile_lock: + needs: [determine_mode, publish_react_native] + if: | + always() && + needs.determine_mode.result == 'success' && + needs.publish_react_native.result == 'success' && + needs.determine_mode.outputs.mode == 'release' + uses: ./.github/workflows/bump-podfile-lock.yml + secrets: inherit + + create_draft_release: + needs: [determine_mode, generate_changelog, set_hermes_versions] + if: | + always() && + needs.determine_mode.result == 'success' && + needs.generate_changelog.result == 'success' && + needs.set_hermes_versions.result == 'success' && + needs.determine_mode.outputs.mode == 'release' + uses: ./.github/workflows/create-draft-release.yml + secrets: inherit + with: + hermesVersion: ${{ needs.set_hermes_versions.outputs.HERMES_VERSION }} + hermesV1Version: ${{ needs.set_hermes_versions.outputs.HERMES_V1_VERSION }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml deleted file mode 100644 index a0729166534b..000000000000 --- a/.github/workflows/publish-release.yml +++ /dev/null @@ -1,151 +0,0 @@ -name: Publish Release -on: - push: - tags: - - "v0.*.*" # This should match v0.X.Y - - "v0.*.*-rc.*" # This should match v0.X.Y-RC.0 -jobs: - set_release_type: - runs-on: ubuntu-latest - if: github.repository == 'react/react-native' - outputs: - RELEASE_TYPE: ${{ steps.set_release_type.outputs.RELEASE_TYPE }} - env: - EVENT_NAME: ${{ github.event_name }} - REF: ${{ github.ref }} - steps: - - id: set_release_type - run: | - echo "Setting release type to release" - echo "RELEASE_TYPE=release" >> $GITHUB_OUTPUT - - set_hermes_versions: - runs-on: ubuntu-latest - if: github.repository == 'react/react-native' - outputs: - HERMES_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_VERSION }} - HERMES_V1_VERSION: ${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }} - steps: - - name: Checkout - uses: actions/checkout@v6 - - id: set_hermes_versions - run: | - echo "Setting hermes versions to latest" - hermes_version=$(grep -oE 'HERMES_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2) - hermes_v1_version=$(grep -oE 'HERMES_V1_VERSION_NAME=([0-9]+\.[0-9]+\.[0-9]+)' packages/react-native/sdks/hermes-engine/version.properties | cut -d'=' -f2) - - echo "HERMES_VERSION=$hermes_version" >> $GITHUB_OUTPUT - echo "HERMES_V1_VERSION=$hermes_v1_version" >> $GITHUB_OUTPUT - - name: Print hermes versions - run: | - echo "HERMES_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_VERSION }}" - echo "HERMES_V1_VERSION=${{ steps.set_hermes_versions.outputs.HERMES_V1_VERSION }}" - - prebuild_apple_dependencies: - if: github.repository == 'react/react-native' - uses: ./.github/workflows/prebuild-ios-dependencies.yml - secrets: inherit - - prebuild_react_native_core: - uses: ./.github/workflows/prebuild-ios-core.yml - secrets: inherit - needs: [prebuild_apple_dependencies] - - build_npm_package: - runs-on: ubuntu-latest - needs: - [ - set_release_type, - prebuild_apple_dependencies, - prebuild_react_native_core, - ] - container: - image: reactnativecommunity/react-native-android:latest - env: - TERM: "dumb" - # Set the encoding to resolve a known character encoding issue with decompressing tar.gz files in containers - # via Gradle: https://github.com/gradle/gradle/issues/23391#issuecomment-1878979127 - LC_ALL: C.UTF8 - GRADLE_OPTS: "-Dorg.gradle.daemon=false" - # By default we only build ARM64 to save time/resources. For release/nightlies, we override this value to build all archs. - ORG_GRADLE_PROJECT_reactNativeArchitectures: "arm64-v8a" - REACT_NATIVE_DOWNLOADS_DIR: /opt/react-native-downloads - env: - GHA_NPM_TOKEN: ${{ secrets.GHA_NPM_TOKEN }} - ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} - ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} - ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }} - ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }} - REACT_NATIVE_BOT_GITHUB_TOKEN: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - fetch-tags: true - - name: Build and Publish NPM Package - uses: ./.github/actions/build-npm-package - with: - release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }} - gha-npm-token: ${{ env.GHA_NPM_TOKEN }} - gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} - - name: Publish @react-native-community/template - id: publish-template-to-npm - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} - script: | - const {publishTemplate} = require('./.github/workflow-scripts/publishTemplate.js') - const version = "${{ github.ref_name }}" - const isDryRun = false - await publishTemplate(github, version, isDryRun); - - name: Wait for template to be published - timeout-minutes: 3 - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} - script: | - const {verifyPublishedTemplate, isLatest} = require('./.github/workflow-scripts/publishTemplate.js') - const version = "${{ github.ref_name }}" - await verifyPublishedTemplate(version, isLatest()); - - name: Update rn-diff-purge to generate upgrade-support diff - run: | - curl -X POST https://api.github.com/repos/react-native-community/rn-diff-purge/dispatches \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: Bearer $REACT_NATIVE_BOT_GITHUB_TOKEN" \ - -d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${{ github.ref_name }}\" }}" - - name: Verify Release is on NPM - timeout-minutes: 3 - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }} - script: | - const {verifyReleaseOnNpm} = require('./.github/workflow-scripts/verifyReleaseOnNpm.js'); - const {isLatest} = require('./.github/workflow-scripts/publishTemplate.js'); - const version = "${{ github.ref_name }}"; - await verifyReleaseOnNpm(version, isLatest()); - - name: Verify that artifacts are on Maven - uses: actions/github-script@v8 - with: - script: | - const {verifyArtifactsAreOnMaven} = require('./.github/workflow-scripts/verifyArtifactsAreOnMaven.js'); - const version = "${{ github.ref_name }}"; - await verifyArtifactsAreOnMaven(version); - - generate_changelog: - needs: build_npm_package - uses: ./.github/workflows/generate-changelog.yml - secrets: inherit - - bump_podfile_lock: - needs: build_npm_package - uses: ./.github/workflows/bump-podfile-lock.yml - secrets: inherit - - create_draft_release: - needs: [generate_changelog, set_hermes_versions] - uses: ./.github/workflows/create-draft-release.yml - secrets: inherit - with: - hermesVersion: ${{ needs.set_hermes_versions.outputs.HERMES_VERSION }} - hermesV1Version: ${{ needs.set_hermes_versions.outputs.HERMES_V1_VERSION }} diff --git a/packages/react-native-popup-menu-android/package.json b/packages/react-native-popup-menu-android/package.json index 5189f3005d57..42f24a3fcfb9 100644 --- a/packages/react-native-popup-menu-android/package.json +++ b/packages/react-native-popup-menu-android/package.json @@ -2,6 +2,11 @@ "name": "@react-native/popup-menu-android", "version": "0.85.3", "description": "PopupMenu for the Android platform", + "repository": { + "type": "git", + "url": "git+https://github.com/react/react-native.git", + "directory": "packages/react-native-popup-menu-android" + }, "main": "index.js", "files": [ "js", diff --git a/scripts/releases-ci/__tests__/publish-npm-test.js b/scripts/releases-ci/__tests__/publish-npm-test.js index 0eeb9a3489b8..ca42346b65fe 100644 --- a/scripts/releases-ci/__tests__/publish-npm-test.js +++ b/scripts/releases-ci/__tests__/publish-npm-test.js @@ -22,6 +22,11 @@ const generateAndroidArtifactsMock = jest.fn(); const getPackagesMock = jest.fn(); const updateHermesVersionsToNightlyMock = jest.fn(); const getBranchName = jest.fn(); +const validateNpmPackageMetadataInRepoMock = jest.fn(); + +jest.mock('../validate-npm-package-metadata', () => ({ + validateNpmPackageMetadataInRepo: validateNpmPackageMetadataInRepoMock, +})); const {REPO_ROOT} = require('../../shared/consts'); const {publishNpm} = require('../publish-npm'); @@ -80,6 +85,21 @@ describe('publish-npm', () => { jest.resetAllMocks(); }); + it('stops before publishing when package metadata is invalid', async () => { + validateNpmPackageMetadataInRepoMock.mockRejectedValueOnce( + new Error('Invalid npm package repository metadata'), + ); + + await expect(publishNpm('release')).rejects.toThrow( + 'Invalid npm package repository metadata', + ); + + expect(getNpmInfoMock).not.toHaveBeenCalled(); + expect(publishAndroidArtifactsToMavenMock).not.toHaveBeenCalled(); + expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled(); + expect(publishPackageMock).not.toHaveBeenCalled(); + }); + it('should fail when invalid build type is passed', async () => { // Call actual function // $FlowExpectedError[underconstrained-implicit-instantiation] diff --git a/scripts/releases-ci/__tests__/publish-updated-packages-test.js b/scripts/releases-ci/__tests__/publish-updated-packages-test.js index 379fc7cf9017..e16452fc7f36 100644 --- a/scripts/releases-ci/__tests__/publish-updated-packages-test.js +++ b/scripts/releases-ci/__tests__/publish-updated-packages-test.js @@ -17,12 +17,16 @@ const getPackagesMock = jest.fn(); const execSync = jest.fn(); const execMock = jest.fn(); const fetchMock = jest.fn(); +const validateNpmPackageMetadataInRepoMock = jest.fn(); jest.mock('child_process', () => ({execSync})); jest.mock('shelljs', () => ({exec: execMock})); jest.mock('../../shared/monorepoUtils', () => ({ getPackages: getPackagesMock, })); +jest.mock('../validate-npm-package-metadata', () => ({ + validateNpmPackageMetadataInRepo: validateNpmPackageMetadataInRepoMock, +})); // $FlowFixMe[cannot-write] global.fetch = fetchMock; @@ -73,6 +77,20 @@ describe('publishUpdatedPackages', () => { `); }); + test('stops before registry lookup when package metadata is invalid', async () => { + execSync.mockReturnValue(BUMP_COMMIT_MESSAGE); + validateNpmPackageMetadataInRepoMock.mockRejectedValueOnce( + new Error('Invalid npm package repository metadata'), + ); + + await expect(publishUpdatedPackages()).rejects.toThrow( + 'Invalid npm package repository metadata', + ); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(execMock).not.toHaveBeenCalled(); + }); + test('should throw an error if updated version is not 0.x.x', async () => { execSync.mockImplementation((command: string) => { switch (command) { diff --git a/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js b/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js new file mode 100644 index 000000000000..8d65f8c31ca3 --- /dev/null +++ b/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js @@ -0,0 +1,95 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +const { + validateNpmPackageMetadata, +} = require('../validate-npm-package-metadata'); + +/*:: +import type {PackageInfo} from '../../shared/monorepoUtils'; + +type Repository = { + type: string, + url: string, +}; +*/ + +function packageInfo( + name /*: string */, + repository /*: ?Repository */ = null, +) /*: PackageInfo */ { + return { + name, + path: `/packages/${name}`, + version: '0.85.0', + packageJson: { + name, + version: '0.85.0', + ...(repository == null ? {} : {repository}), + }, + }; +} + +describe('validateNpmPackageMetadata', () => { + test('accepts canonical HTTPS and SSH repository URLs', () => { + const packages = { + packageA: packageInfo('package-a', { + type: 'git', + url: 'git+https://github.com/react/react-native.git', + }), + packageB: packageInfo('package-b', { + type: 'git', + url: 'git+ssh://git@github.com/react/react-native.git', + }), + }; + + expect(() => validateNpmPackageMetadata(packages)).not.toThrow(); + }); + + test('rejects a package with no repository', () => { + const packages = { + packageA: packageInfo('package-a'), + }; + + expect(() => validateNpmPackageMetadata(packages)).toThrow( + '- package-a: repository is missing', + ); + }); + + test('rejects a package that uses the former repository owner', () => { + const packages = { + packageA: packageInfo('package-a', { + type: 'git', + url: 'git+https://github.com/facebook/react-native.git', + }), + }; + + expect(() => validateNpmPackageMetadata(packages)).toThrow( + '- package-a: repository.url must point to react/react-native', + ); + }); + + test('reports every invalid package in one error', () => { + const packages = { + packageB: packageInfo('package-b'), + packageA: packageInfo('package-a', { + type: 'git', + url: 'git+https://github.com/example/react-native.git', + }), + }; + + expect(() => validateNpmPackageMetadata(packages)).toThrow( + 'Invalid npm package repository metadata:\n' + + '- package-a: repository.url must point to react/react-native ' + + '(received "git+https://github.com/example/react-native.git")\n' + + '- package-b: repository is missing', + ); + }); +}); diff --git a/scripts/releases-ci/publish-npm.js b/scripts/releases-ci/publish-npm.js index 93cc4d178267..a0f2c2d165ac 100755 --- a/scripts/releases-ci/publish-npm.js +++ b/scripts/releases-ci/publish-npm.js @@ -29,6 +29,9 @@ const { const {getBranchName} = require('../releases/utils/scm-utils'); const {REACT_NATIVE_PACKAGE_DIR} = require('../shared/consts'); const {getPackages, getReactNativePackage} = require('../shared/monorepoUtils'); +const { + validateNpmPackageMetadataInRepo, +} = require('./validate-npm-package-metadata'); const yargs = require('yargs'); /** @@ -94,6 +97,8 @@ async function publishMonorepoPackages(tag /*: ?string */) { } async function publishNpm(buildType /*: BuildType */) /*: Promise */ { + await validateNpmPackageMetadataInRepo(); + const {version, tag} = getNpmInfo(buildType); // For stable releases, ci job `prepare_package_for_release` handles this diff --git a/scripts/releases-ci/publish-updated-packages.js b/scripts/releases-ci/publish-updated-packages.js index 1b47a44e795a..9f3bb534b855 100644 --- a/scripts/releases-ci/publish-updated-packages.js +++ b/scripts/releases-ci/publish-updated-packages.js @@ -10,6 +10,9 @@ const {publishPackage} = require('../releases/utils/npm-utils'); const {getPackages} = require('../shared/monorepoUtils'); +const { + validateNpmPackageMetadataInRepo, +} = require('./validate-npm-package-metadata'); const {execSync} = require('child_process'); const {parseArgs} = require('util'); @@ -57,6 +60,8 @@ async function publishUpdatedPackages() { return; } + await validateNpmPackageMetadataInRepo(); + console.log('Discovering updated packages'); const packages = await getPackages({ diff --git a/scripts/releases-ci/validate-npm-package-metadata.js b/scripts/releases-ci/validate-npm-package-metadata.js new file mode 100644 index 000000000000..f5c54e17ea0c --- /dev/null +++ b/scripts/releases-ci/validate-npm-package-metadata.js @@ -0,0 +1,82 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +const {getPackages} = require('../shared/monorepoUtils'); + +/*:: +import type {ProjectInfo} from '../shared/monorepoUtils'; + +type Repository = + | string + | { + type?: string, + url?: string, + ... + }; +*/ + +const CANONICAL_REPOSITORY_URLS = new Set([ + 'git+https://github.com/react/react-native.git', + 'git+ssh://git@github.com/react/react-native.git', +]); + +function getRepositoryError(repository /*: ?Repository */) /*: ?string */ { + if (repository == null) { + return 'repository is missing'; + } + if (typeof repository !== 'object') { + return 'repository must be an object'; + } + if (repository.type !== 'git') { + return 'repository.type must be "git"'; + } + if ( + repository.url == null || + !CANONICAL_REPOSITORY_URLS.has(repository.url) + ) { + return `repository.url must point to react/react-native (received ${JSON.stringify(repository.url)})`; + } + return null; +} + +function validateNpmPackageMetadata(packages /*: ProjectInfo */) /*: void */ { + const errors /*: Array */ = []; + for (const packageInfo of Object.values(packages)) { + const repository /*: ?Repository */ = packageInfo.packageJson.repository; + const error = getRepositoryError(repository); + if (error != null) { + errors.push(`- ${packageInfo.name}: ${error}`); + } + } + errors.sort(); + + if (errors.length > 0) { + throw new Error( + `Invalid npm package repository metadata:\n${errors.join('\n')}`, + ); + } +} + +async function validateNpmPackageMetadataInRepo() /*: Promise */ { + const packages = await getPackages({includeReactNative: true}); + validateNpmPackageMetadata(packages); + console.log( + `Validated repository metadata for ${Object.keys(packages).length} npm packages.`, + ); +} + +if (require.main === module) { + void validateNpmPackageMetadataInRepo(); +} + +module.exports = { + validateNpmPackageMetadata, + validateNpmPackageMetadataInRepo, +}; From fc59b89f90283cc65fff95ca591f678cee00f212 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 28 Jul 2026 10:41:12 +0100 Subject: [PATCH 3/5] Complete trusted publishing repository metadata --- packages/react-native-babel-preset/package.json | 2 +- .../react-native-babel-transformer/package.json | 2 +- ...load-release-assets-for-dotslash-test.js.snap | 8 ++++---- .../upload-release-assets-for-dotslash.js | 16 ++++++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/react-native-babel-preset/package.json b/packages/react-native-babel-preset/package.json index 342d09c1a89e..ef4e2a0061fd 100644 --- a/packages/react-native-babel-preset/package.json +++ b/packages/react-native-babel-preset/package.json @@ -4,7 +4,7 @@ "description": "Babel preset for React Native applications", "repository": { "type": "git", - "url": "git+ssh://git@github.com/facebook/react-native.git" + "url": "git+ssh://git@github.com/react/react-native.git" }, "keywords": [ "babel", diff --git a/packages/react-native-babel-transformer/package.json b/packages/react-native-babel-transformer/package.json index ab2f9740a0d0..57a0bf2997ae 100644 --- a/packages/react-native-babel-transformer/package.json +++ b/packages/react-native-babel-transformer/package.json @@ -4,7 +4,7 @@ "description": "Babel transformer for React Native applications.", "repository": { "type": "git", - "url": "git+ssh://git@github.com/facebook/react-native.git", + "url": "git+ssh://git@github.com/react/react-native.git", "directory": "packages/react-native-babel-transformer" }, "keywords": [ diff --git a/scripts/releases/__tests__/__snapshots__/upload-release-assets-for-dotslash-test.js.snap b/scripts/releases/__tests__/__snapshots__/upload-release-assets-for-dotslash-test.js.snap index 8caa220b6f97..bf9e038615c9 100644 --- a/scripts/releases/__tests__/__snapshots__/upload-release-assets-for-dotslash-test.js.snap +++ b/scripts/releases/__tests__/__snapshots__/upload-release-assets-for-dotslash-test.js.snap @@ -28,7 +28,7 @@ Array [ Array [ Object { "asset_id": "1", - "owner": "facebook", + "owner": "react", "repo": "react-native", }, ], @@ -47,7 +47,7 @@ Array [ "content-type": "text/plain", }, "name": "test.tar.gz", - "owner": "facebook", + "owner": "react", "release_id": "1", "repo": "react-native", }, @@ -114,7 +114,7 @@ Array [ "content-type": "text/plain", }, "name": "test.tar.gz", - "owner": "facebook", + "owner": "react", "release_id": "1", "repo": "react-native", }, @@ -194,7 +194,7 @@ Array [ "content-type": "text/plain", }, "name": "test.tar.gz", - "owner": "facebook", + "owner": "react", "release_id": "1", "repo": "react-native", }, diff --git a/scripts/releases/upload-release-assets-for-dotslash.js b/scripts/releases/upload-release-assets-for-dotslash.js index d990d12b1677..7a8fa791f0de 100644 --- a/scripts/releases/upload-release-assets-for-dotslash.js +++ b/scripts/releases/upload-release-assets-for-dotslash.js @@ -25,6 +25,10 @@ const nullthrows = require('nullthrows'); const path = require('path'); const {parseArgs} = require('util'); +// Upload POSTs are not preserved when GitHub redirects from the former owner. +const GITHUB_OWNER = 'react'; +const GITHUB_REPO = 'react-native'; + /*:: import type {DotSlashProvider, DotSlashHttpProvider, DotSlashArtifactInfo} from './utils/dotslash-utils'; import type {IOctokit} from './utils/octokit-utils'; @@ -138,8 +142,8 @@ async function getReleaseAssetMap( octokit /*: IOctokit */, ) /*: Promise */ { const existingAssets = await octokit.repos.listReleaseAssets({ - owner: 'facebook', - repo: 'react-native', + owner: GITHUB_OWNER, + repo: GITHUB_REPO, release_id: releaseId, }); return new Map(existingAssets.data.map(asset => [asset.name, asset])); @@ -305,8 +309,8 @@ async function maybeDeleteExistingReleaseAsset( } console.log(`[${name}] Deleting existing release asset...`); await octokit.repos.deleteReleaseAsset({ - owner: 'facebook', - repo: 'react-native', + owner: GITHUB_OWNER, + repo: GITHUB_REPO, asset_id: existingAsset.id, }); } @@ -360,8 +364,8 @@ async function uploadAndVerifyReleaseAsset( const { data: {browser_download_url}, } = await octokit.repos.uploadReleaseAsset({ - owner: 'facebook', - repo: 'react-native', + owner: GITHUB_OWNER, + repo: GITHUB_REPO, release_id: releaseId, name, data, From 1970c4479ff2c8f3958a8780ef2e96cf972378e5 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 28 Jul 2026 11:04:09 +0100 Subject: [PATCH 4/5] Fix trusted publishing Flow types --- scripts/releases-ci/validate-npm-package-metadata.js | 3 ++- scripts/shared/monorepoUtils.js | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/releases-ci/validate-npm-package-metadata.js b/scripts/releases-ci/validate-npm-package-metadata.js index f5c54e17ea0c..e0dda928c5a3 100644 --- a/scripts/releases-ci/validate-npm-package-metadata.js +++ b/scripts/releases-ci/validate-npm-package-metadata.js @@ -41,7 +41,8 @@ function getRepositoryError(repository /*: ?Repository */) /*: ?string */ { repository.url == null || !CANONICAL_REPOSITORY_URLS.has(repository.url) ) { - return `repository.url must point to react/react-native (received ${JSON.stringify(repository.url)})`; + const receivedUrl = JSON.stringify(repository.url) ?? 'undefined'; + return `repository.url must point to react/react-native (received ${receivedUrl})`; } return null; } diff --git a/scripts/shared/monorepoUtils.js b/scripts/shared/monorepoUtils.js index f61803bfaaae..43ef893eaf7f 100644 --- a/scripts/shared/monorepoUtils.js +++ b/scripts/shared/monorepoUtils.js @@ -20,6 +20,13 @@ export type PackageJson = { name: string, version: string, private?: boolean, + repository?: + | string + | { + type?: string, + url?: string, + ..., + }, dependencies?: Record, devDependencies?: Record, peerDependencies?: Record, From 993401f220a8531534b3f5bb04288747214d9493 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 28 Jul 2026 11:07:57 +0100 Subject: [PATCH 5/5] Use shared repository metadata type --- .../validate-npm-package-metadata-test.js | 7 +------ .../releases-ci/validate-npm-package-metadata.js | 10 +--------- scripts/shared/monorepoUtils.js | 16 +++++++++------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js b/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js index 8d65f8c31ca3..07f825ca022d 100644 --- a/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js +++ b/scripts/releases-ci/__tests__/validate-npm-package-metadata-test.js @@ -13,12 +13,7 @@ const { } = require('../validate-npm-package-metadata'); /*:: -import type {PackageInfo} from '../../shared/monorepoUtils'; - -type Repository = { - type: string, - url: string, -}; +import type {PackageInfo, Repository} from '../../shared/monorepoUtils'; */ function packageInfo( diff --git a/scripts/releases-ci/validate-npm-package-metadata.js b/scripts/releases-ci/validate-npm-package-metadata.js index e0dda928c5a3..56884994d409 100644 --- a/scripts/releases-ci/validate-npm-package-metadata.js +++ b/scripts/releases-ci/validate-npm-package-metadata.js @@ -11,15 +11,7 @@ const {getPackages} = require('../shared/monorepoUtils'); /*:: -import type {ProjectInfo} from '../shared/monorepoUtils'; - -type Repository = - | string - | { - type?: string, - url?: string, - ... - }; +import type {ProjectInfo, Repository} from '../shared/monorepoUtils'; */ const CANONICAL_REPOSITORY_URLS = new Set([ diff --git a/scripts/shared/monorepoUtils.js b/scripts/shared/monorepoUtils.js index 43ef893eaf7f..28e02ad0f0ab 100644 --- a/scripts/shared/monorepoUtils.js +++ b/scripts/shared/monorepoUtils.js @@ -16,17 +16,19 @@ const {globSync} = require('tinyglobby'); const WORKSPACES_CONFIG = '{packages,private}/*'; /*:: +export type Repository = + | string + | { + type?: string, + url?: string, + ..., + }; + export type PackageJson = { name: string, version: string, private?: boolean, - repository?: - | string - | { - type?: string, - url?: string, - ..., - }, + repository?: Repository, dependencies?: Record, devDependencies?: Record, peerDependencies?: Record,