From 4b82896390592f2b1678ac96aaa4b35cbb97f64c Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Wed, 26 Mar 2025 08:26:01 -0700 Subject: [PATCH] Automate checking for artifacts on Maven (#50275) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50275 This change adds a check to automate a step in the release process: https://github.com/reactwg/react-native-releases/blob/main/docs/guide-release-process.md#verify-assets-have-been-uploaded-to-maven The script will poll maven for 90 minutes and return when the artifacts are available. If, after 90 minutes, artifacts are not available, it exits with code 1 that should fail the Release workflow. The Release Crew should have a look at what's happened. ## Changelog: [Internal] - Automate the check for artifacts being on Maven Reviewed By: fabriziocucci Differential Revision: D71825014 fbshipit-source-id: 8879bf9c8fc4519e86b55ad8f9bd3ecf3f8ecfb7 --- .../verifyArtifactsAreOnMaven-test.js | 87 +++++++++++++++++++ .../verifyArtifactsAreOnMaven.js | 42 +++++++++ .github/workflows/publish-release.yml | 7 ++ 3 files changed, 136 insertions(+) create mode 100644 .github/workflow-scripts/__tests__/verifyArtifactsAreOnMaven-test.js create mode 100644 .github/workflow-scripts/verifyArtifactsAreOnMaven.js diff --git a/.github/workflow-scripts/__tests__/verifyArtifactsAreOnMaven-test.js b/.github/workflow-scripts/__tests__/verifyArtifactsAreOnMaven-test.js new file mode 100644 index 00000000000..df1a332ac22 --- /dev/null +++ b/.github/workflow-scripts/__tests__/verifyArtifactsAreOnMaven-test.js @@ -0,0 +1,87 @@ +/** + * 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 {verifyArtifactsAreOnMaven} = require('../verifyArtifactsAreOnMaven'); + +const mockSleep = jest.fn(); +const silence = () => {}; +const mockFetch = jest.fn(); +const mockExit = jest.fn(); + +jest.mock('../utils.js', () => ({ + log: silence, + sleep: mockSleep, +})); + +process.exit = mockExit; +global.fetch = mockFetch; + +describe('#verifyArtifactsAreOnMaven', () => { + beforeEach(jest.clearAllMocks); + + it('waits for the packages to be published on maven when version has no v', async () => { + mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => { + throw new Error('Should not be called again!'); + }); + mockFetch + .mockReturnValueOnce(Promise.resolve({status: 404})) + .mockReturnValueOnce(Promise.resolve({status: 200})); + + const version = '0.78.1'; + await verifyArtifactsAreOnMaven(version); + + expect(mockSleep).toHaveBeenCalledTimes(1); + expect(mockFetch).toHaveBeenCalledWith( + 'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1', + ); + }); + + it('waits for the packages to be published on maven, when version starts with v', async () => { + mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => { + throw new Error('Should not be called again!'); + }); + mockFetch + .mockReturnValueOnce(Promise.resolve({status: 404})) + .mockReturnValueOnce(Promise.resolve({status: 200})); + + const version = 'v0.78.1'; + await verifyArtifactsAreOnMaven(version); + + expect(mockSleep).toHaveBeenCalledTimes(1); + expect(mockFetch).toHaveBeenCalledWith( + 'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1', + ); + }); + + it('passes immediately if packages are already on Maven', async () => { + mockFetch.mockReturnValueOnce(Promise.resolve({status: 200})); + + const version = '0.78.1'; + await verifyArtifactsAreOnMaven(version); + + expect(mockSleep).toHaveBeenCalledTimes(0); + expect(mockFetch).toHaveBeenCalledWith( + 'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1', + ); + }); + + it('tries 90 times and then exits', async () => { + mockSleep.mockReturnValue(Promise.resolve()); + mockFetch.mockReturnValue(Promise.resolve({status: 404})); + + const version = '0.78.1'; + await verifyArtifactsAreOnMaven(version); + + expect(mockSleep).toHaveBeenCalledTimes(90); + expect(mockExit).toHaveBeenCalledWith(1); + expect(mockFetch).toHaveBeenCalledWith( + 'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1', + ); + }); +}); diff --git a/.github/workflow-scripts/verifyArtifactsAreOnMaven.js b/.github/workflow-scripts/verifyArtifactsAreOnMaven.js new file mode 100644 index 00000000000..d2091b00cde --- /dev/null +++ b/.github/workflow-scripts/verifyArtifactsAreOnMaven.js @@ -0,0 +1,42 @@ +/** + * 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 {log, sleep} = require('./utils'); + +const SLEEP_S = 60; // 1 minute +const MAX_RETRIES = 90; // 90 attempts. Waiting between attempt: 1 min. Total time: 90 min. +const ARTIFACT_URL = + 'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/'; + +async function verifyArtifactsAreOnMaven(version, retries = MAX_RETRIES) { + if (version.startsWith('v')) { + version = version.substring(1); + } + + const artifactUrl = `${ARTIFACT_URL}${version}`; + for (let currentAttempt = 1; currentAttempt <= retries; currentAttempt++) { + const response = await fetch(artifactUrl); + + if (response.status !== 200) { + log( + `${currentAttempt}) Artifact's for version ${version} are not on maven yet.\nURL: ${artifactUrl}\nLet's wait a minute and try again.\n`, + ); + await sleep(SLEEP_S); + } else { + return; + } + } + + log( + `We waited 90 minutes for the artifacts to be on Maven. Check https://status.maven.org/ if there are issues wth the service.`, + ); + process.exit(1); +} + +module.exports = {verifyArtifactsAreOnMaven}; diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 15c7b3ab9e5..3ad9cb32b48 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -230,3 +230,10 @@ jobs: 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@v6 + with: + script: | + const {verifyArtifactsAreOnMaven} = require('./.github/workflow-scripts/verifyArtifactsAreOnMaven.js'); + const version = "${{ github.ref_name }}"; + await verifyArtifactsAreOnMaven(version);