forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace sh scripts with tested JS scripts to release template
The previous scripts to trigger the @react-native-communty/template release workflow has not been working. This is a rewrite is js, along with some testing to make this more robust. TODO: - Still needs to be used in the GH release workflow. - Template release workflow needs to land the dry_run input change. Changelog: [Internal]
- Loading branch information
Showing
3 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
122 changes: 122 additions & 0 deletions
122
.github/workflow-scripts/__tests__/publishTemplate-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/** | ||
* 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 {publishTemplate, verifyPublishedTemplate} = require('../publishTemplate'); | ||
|
||
const mockRun = jest.fn(); | ||
const mockSleep = jest.fn(); | ||
const mockGetNpmPackageInfo = jest.fn(); | ||
const silence = () => {}; | ||
|
||
jest.mock('../utils.js', () => ({ | ||
log: silence, | ||
run: mockRun, | ||
sleep: mockSleep, | ||
getNpmPackageInfo: mockGetNpmPackageInfo, | ||
})); | ||
|
||
const getMockGithub = () => ({ | ||
rest: { | ||
actions: { | ||
createWorkflowDispatch: jest.fn(), | ||
} | ||
} | ||
}); | ||
|
||
describe("#publishTemplate", () => { | ||
beforeEach(jest.clearAllMocks); | ||
|
||
it("checks commits for magic #publish-package-to-npm&latest string and sets latest", async () => { | ||
mockRun.mockReturnValueOnce(` | ||
The commit message | ||
#publish-packages-to-npm&latest`); | ||
|
||
const github = getMockGithub(); | ||
await publishTemplate( | ||
github, | ||
'0.76.0', | ||
true, | ||
); | ||
expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({ | ||
owner: 'react-native-community', | ||
repo: 'template', | ||
workflow_id: 'release.yml', | ||
ref: '0.76-stable', | ||
inputs: { | ||
dry_run: true, | ||
is_latest_on_npm: true, | ||
version: '0.76.0', | ||
}, | ||
}); | ||
}); | ||
|
||
it("pubished as is_latest_on_npm = false if missing magic string", async () => { | ||
mockRun.mockReturnValueOnce(` | ||
The commit message without magic | ||
`); | ||
|
||
const github = getMockGithub(); | ||
await publishTemplate( | ||
github, | ||
'0.76.0', | ||
false, | ||
); | ||
expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({ | ||
owner: 'react-native-community', | ||
repo: 'template', | ||
workflow_id: 'release.yml', | ||
ref: '0.76-stable', | ||
inputs: { | ||
dry_run: false, | ||
is_latest_on_npm: false, | ||
version: '0.76.0', | ||
}, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("#verifyPublishedTemplate", () => { | ||
beforeEach(jest.clearAllMocks); | ||
|
||
it("waits on npm updating for version but skips if not latest", async () => { | ||
mockGetNpmPackageInfo | ||
// template@<version> | ||
.mockReturnValueOnce(Promise.reject('mock http/404')) | ||
.mockReturnValueOnce(Promise.resolve()); | ||
mockSleep | ||
.mockReturnValueOnce(Promise.resolve()) | ||
.mockImplementation(() => { throw new Error('Should not be called again!') }); | ||
|
||
const version = '0.77.0'; | ||
await verifyPublishedTemplate(version); | ||
|
||
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith('@react-native-community/template', version); | ||
}); | ||
|
||
it("waits on npm updating version and latest tag", async () => { | ||
const version = '0.77.0'; | ||
mockGetNpmPackageInfo | ||
// template@<version> | ||
.mockReturnValueOnce(Promise.reject('mock http/404')) | ||
.mockReturnValueOnce(Promise.resolve()) | ||
// template@latest != version | ||
.mockReturnValueOnce(Promise.resolve({ version: '0.76.5' })) | ||
// template@latest == version | ||
.mockReturnValueOnce(Promise.resolve({ version })); | ||
mockSleep | ||
.mockReturnValueOnce(Promise.resolve()) | ||
.mockReturnValueOnce(Promise.resolve()) | ||
.mockImplementation(() => { throw new Error('Should not be called again!') }); | ||
|
||
await verifyPublishedTemplate(version, true); | ||
|
||
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith('@react-native-community/template', version); | ||
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith('@react-native-community/template', 'latest'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* 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 {run, sleep, getNpmPackageInfo, log} = require('./utils.js'); | ||
|
||
const TAG_AS_LATEST_REGEX=/#publish-packages-to-npm&latest/ | ||
|
||
/** | ||
* Create a Github Action to publish the community template matching the released version | ||
* of React Native. | ||
*/ | ||
module.exports.publishTemplate = async ( | ||
github, | ||
version, // 0.75.0-rc.0, note no 'v' prefix | ||
dryRun = true, | ||
) => { | ||
log(`📤 Get the ${TEMPLATE_NPM_PKG} repo to publish ${version}`); | ||
|
||
const commitMessage = run('git log -n1 --pretty=%B'); | ||
const isLatest = TAG_AS_LATEST_REGEX.test(commitMessage); | ||
|
||
const majorMinor = /^v?(\d+\.\d+)/.exec(version); | ||
|
||
if (!majorMinor) { | ||
log(`🔥 can't capture MAJOR.MINOR from '${version}', giving up.`); | ||
process.exit(1); | ||
} | ||
|
||
// MAJOR.MINOR-stable | ||
const ref = `${majorMinor[1]}-stable`; | ||
|
||
await github.rest.actions.createWorkflowDispatch({ | ||
owner: 'react-native-community', | ||
repo: 'template', | ||
workflow_id: 'release.yml', | ||
ref, | ||
inputs: { | ||
dry_run: dryRun, | ||
is_latest_on_npm: isLatest, | ||
version, | ||
}, | ||
}); | ||
}; | ||
|
||
const SLEEP_S = 10; | ||
const TEMPLATE_NPM_PKG = '@react-native-community/template'; | ||
|
||
/** | ||
* Will verify that @latest and the @<version> have been published. | ||
* | ||
* NOTE: This will infinitely query each step until successful, make sure the | ||
* calling job has a timeout. | ||
*/ | ||
module.exports.verifyPublishedTemplate = async (version, latest = false) => { | ||
log(`🔍 Is ${TEMPLATE_NPM_PKG}@${version} on npm?`); | ||
|
||
while (true) { | ||
try { | ||
await getNpmPackageInfo(TEMPLATE_NPM_PKG, version); | ||
log(`🎉 Found ${TEMPLATE_NPM_PKG}@${version} on npm`); | ||
break; | ||
} catch(e) { | ||
log(`Nope, fetch failed: ${e.message}`); | ||
} | ||
await sleep(SLEEP_S); | ||
} | ||
|
||
log(`🔍 Does latest → ${version} on npm?`); | ||
|
||
while (latest) { | ||
try { | ||
const pkg = await getNpmPackageInfo(TEMPLATE_NPM_PKG, 'latest'); | ||
if (pkg.version === version) { | ||
log(`🎉 ${TEMPLATE_NPM_PKG}@latest → ${version} on npm`); | ||
break; | ||
} else { | ||
log(`🐌 ${TEMPLATE_NPM_PKG}@latest → ${pkg.version} on npm, retrying...`); | ||
} | ||
} catch(e) { | ||
log(`🚨 Fetch failed (will retry): ${e.message}`); | ||
} | ||
await sleep(SLEEP_S); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* 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 {execSync} = require('child_process'); | ||
|
||
function run(...cmd) { | ||
return execSync(cmd, 'utf8').toString().trim(); | ||
} | ||
module.exports.run = run; | ||
|
||
await function sleep(seconds) { | ||
return new Promise(resolve => setTimeout(resolve, seconds * 1000)); | ||
} | ||
module.exports.sleep = sleep; | ||
|
||
async function getNpmPackageInfo(pkg, versionOrTag) { | ||
return fetch(`https://registry.npmjs.org/${pkg}/${versionOrTag}`).then(resp => res.json()); | ||
} | ||
module.exports.getNpmPackageInfo = getNpmPackageInfo; | ||
|
||
module.exports.log = (...args) => console.log(...args); | ||
|