diff --git a/.circleci/config.yml b/.circleci/config.yml index c3e1b3415ace99..44982091cfbfad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,8 +7,8 @@ parameters: description: Whether to force browserstack usage. We have limited resources on browserstack so the pipeline might decide to skip browserstack if this parameter isn't set to true. type: boolean default: false - react-dist-tag: - description: The dist-tag of react to be used + react-version: + description: The version of react to be used type: string default: stable workflow: @@ -22,10 +22,10 @@ parameters: defaults: &defaults parameters: - react-dist-tag: - description: The dist-tag of react to be used + react-version: + description: The version of react to be used type: string - default: << pipeline.parameters.react-dist-tag >> + default: << pipeline.parameters.react-version >> test-gate: description: A particular type of tests that should be run type: string @@ -39,7 +39,7 @@ defaults: &defaults PLAYWRIGHT_BROWSERS_PATH: /tmp/pw-browsers # expose it globally otherwise we have to thread it from each job to the install command BROWSERSTACK_FORCE: << pipeline.parameters.browserstack-force >> - REACT_DIST_TAG: << parameters.react-dist-tag >> + REACT_VERSION: << parameters.react-version >> TEST_GATE: << parameters.test-gate >> AWS_REGION_ARTIFACTS: eu-central-1 working_directory: /tmp/material-ui @@ -72,9 +72,9 @@ commands: - run: name: Resolve react version command: | - node scripts/use-react-dist-tag + node scripts/use-react-version # log a patch for maintainers who want to check out this change - git --no-pager diff HEAD + git --no-pager diff HEAD - restore_cache: name: Restore yarn cache keys: @@ -132,7 +132,7 @@ jobs: - when: # Install can be "dirty" when running with non-default versions of React condition: - equal: [<< parameters.react-dist-tag >>, stable] + equal: [<< parameters.react-version >>, stable] steps: - run: name: Should not have any git not staged @@ -191,7 +191,7 @@ jobs: command: | curl -Os https://uploader.codecov.io/latest/linux/codecov chmod +x codecov - ./codecov -t ${CODECOV_TOKEN} -Z -F "$REACT_DIST_TAG-jsdom" + ./codecov -t ${CODECOV_TOKEN} -Z -F "$REACT_VERSION-jsdom" test_lint: <<: *defaults steps: @@ -363,7 +363,7 @@ jobs: command: | curl -Os https://uploader.codecov.io/latest/linux/codecov chmod +x codecov - ./codecov -t ${CODECOV_TOKEN} -Z -F "$REACT_DIST_TAG-browser" + ./codecov -t ${CODECOV_TOKEN} -Z -F "$REACT_VERSION-browser" - store_artifacts: # hardcoded in karma-webpack path: /tmp/_karma_webpack_ @@ -822,6 +822,23 @@ workflows: equal: [profile, << pipeline.parameters.workflow >>] jobs: - test_profile + react-17: + triggers: + - schedule: + cron: '0 0 * * *' + filters: + branches: + only: + - master + jobs: + - test_unit: + react-version: ^17.0.0 + - test_browser: + react-version: ^17.0.0 + - test_regressions: + react-version: ^17.0.0 + - test_e2e: + react-version: ^17.0.0 react-next: triggers: - schedule: @@ -832,13 +849,13 @@ workflows: - master jobs: - test_unit: - react-dist-tag: next + react-version: next - test_browser: - react-dist-tag: next + react-version: next - test_regressions: - react-dist-tag: next + react-version: next - test_e2e: - react-dist-tag: next + react-version: next typescript-next: triggers: - schedule: diff --git a/scripts/use-react-dist-tag.js b/scripts/use-react-dist-tag.js deleted file mode 100644 index 8e6cc91d27cd26..00000000000000 --- a/scripts/use-react-dist-tag.js +++ /dev/null @@ -1,64 +0,0 @@ -/* eslint-disable no-console */ -/** - * Given the dist tag fetch the corresponding - * version and make sure this version is used throughout the repository. - * - * If you work on this file: - * WARNING: This script can only use built-in modules since it has to run before - * `yarn install` - */ -const childProcess = require('child_process'); -const fs = require('fs'); -const os = require('os'); -const path = require('path'); -const { promisify } = require('util'); - -const exec = promisify(childProcess.exec); - -// packages published from the react monorepo using the same version -const reactPackageNames = ['react', 'react-dom', 'react-is', 'react-test-renderer', 'scheduler']; - -async function main(options) { - const { distTag } = options; - if (typeof distTag !== 'string') { - throw new TypeError(`expected distTag: string but got '${distTag}'`); - } - - if (distTag === 'stable') { - console.log('nothing to do with stable'); - return; - } - - const packageJsonPath = path.resolve(__dirname, '../package.json'); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, { encoding: 'utf8' })); - - await Promise.all( - reactPackageNames.map(async (reactPackageName) => { - const { stdout: versions } = await exec(`npm dist-tag ls ${reactPackageName} ${distTag}`); - const tagMapping = versions.split('\n').find((mapping) => { - return mapping.startsWith(`${distTag}: `); - }); - if (tagMapping === undefined) { - throw new Error(`Could not find '${distTag}' in "${versions}"`); - } - - const version = tagMapping.replace(`${distTag}: `, ''); - - packageJson.resolutions[reactPackageName] = version; - }), - ); - - // TODO: What is this doing? - // packageJson.devDependencies['@mnajdova/enzyme-adapter-react-18'] = - // 'npm:@mnajdova/enzyme-adapter-react-next'; - packageJson.devDependencies['@testing-library/react'] = 'alpha'; - - // add newline for clean diff - fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}${os.EOL}`); -} - -const [distTag = process.env.REACT_DIST_TAG] = process.argv.slice(2); -main({ distTag }).catch((error) => { - console.error(error); - process.exit(1); -}); diff --git a/scripts/use-react-version.js b/scripts/use-react-version.js new file mode 100644 index 00000000000000..d909666c2b89c7 --- /dev/null +++ b/scripts/use-react-version.js @@ -0,0 +1,108 @@ +/* eslint-disable no-console */ +/** + * Given the dist tag fetch the corresponding + * version and make sure this version is used throughout the repository. + * + * If you work on this file: + * WARNING: This script can only use built-in modules since it has to run before + * `yarn install` + */ +const childProcess = require('child_process'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { promisify } = require('util'); + +const exec = promisify(childProcess.exec); + +// packages published from the react monorepo using the same version +const reactPackageNames = ['react', 'react-dom', 'react-is', 'react-test-renderer', 'scheduler']; +const devDependenciesPackageNames = ['@mnajdova/enzyme-adapter-react-18', '@testing-library/react']; + +// if we need to support more versions we will need to add new mapping here +const additionalVersionsMappings = { + 17: { + '@mnajdova/enzyme-adapter-react-18': 'npm:@eps1lon/enzyme-adapter-react-17', + '@testing-library/react': '^12.1.0', + }, +}; + +async function main(version) { + if (typeof version !== 'string') { + throw new TypeError(`expected version: string but got '${version}'`); + } + + if (version === 'stable') { + console.log('Nothing to do with stable'); + return; + } + + const packageJsonPath = path.resolve(__dirname, '../package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, { encoding: 'utf8' })); + + // the version is something in format: "17.0.0" + let majorVersion = null; + + if (version.startsWith('^') || version.startsWith('~') || !Number.isNaN(version.charAt(0))) { + majorVersion = version.replace('^', '').replace('~', '').split('.')[0]; + } + + await Promise.all( + reactPackageNames.map(async (reactPackageName) => { + const { stdout: versions } = await exec(`npm dist-tag ls ${reactPackageName} ${version}`); + const tagMapping = versions.split('\n').find((mapping) => { + return mapping.startsWith(`${version}: `); + }); + + let packageVersion = null; + + if (tagMapping === undefined) { + // Some specific version is being requested + if (majorVersion) { + packageVersion = version; + if (reactPackageName === 'scheduler') { + // get the scheduler version from the react-dom's dependencies entry + const { stdout: reactDOMDependenciesString } = await exec( + `npm view --json react-dom@${version} dependencies`, + ); + packageVersion = JSON.parse(reactDOMDependenciesString).scheduler; + } + } else { + throw new Error(`Could not find '${version}' in "${versions}"`); + } + } else { + packageVersion = tagMapping.replace(`${version}: `, ''); + } + + packageJson.resolutions[reactPackageName] = packageVersion; + }), + ); + + // At this moment all dist tags reference React 18 version, so we don't need + // to update these dependencies unless an older version is used, or when the + // next/experimental dist tag reference to a future version of React + // packageJson.devDependencies['@mnajdova/enzyme-adapter-react-18'] = + // 'npm:@mnajdova/enzyme-adapter-react-next'; + // packageJson.devDependencies['@testing-library/react'] = 'alpha'; + + if (majorVersion && additionalVersionsMappings[majorVersion]) { + devDependenciesPackageNames.forEach((packageName) => { + if (!additionalVersionsMappings[majorVersion][packageName]) { + throw new Error( + `Version ${majorVersion} does not have version defined for the ${packageName}`, + ); + } + packageJson.devDependencies[packageName] = + additionalVersionsMappings[majorVersion][packageName]; + }); + } + + // add newline for clean diff + fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}${os.EOL}`); +} + +const [version = process.env.REACT_VERSION] = process.argv.slice(2); +main(version).catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/test/README.md b/test/README.md index 0dabd8a9cbfe0f..9a71ce0023e55c 100644 --- a/test/README.md +++ b/test/README.md @@ -242,16 +242,17 @@ For example, in https://app.circleci.com/pipelines/github/mui/material-ui/32796/ ### Testing multiple versions of React -You can check integration of different versions of React (e.g. different [release channels](https://reactjs.org/docs/release-channels.html) or PRs to React) by running `node scripts/use-react-dist-tag `. +You can check integration of different versions of React (e.g. different [release channels](https://reactjs.org/docs/release-channels.html) or PRs to React) by running `node scripts/use-react-version `. -Possible values for `dist-tag`: +Possible values for `version`: - default: `stable` (minimum supported React version) - a tag on npm e.g. `next`, `experimental` or `latest` +- an older version e.g `^17.0.0` #### CI -You can pass the same `dist-tag` to our CircleCI pipeline as well: +You can pass the same `version` to our CircleCI pipeline as well: With the following API request we're triggering a run of the default workflow in PR #24289 for `react@next` @@ -261,5 +262,5 @@ curl --request POST \ --url https://circleci.com/api/v2/project/gh/mui/material-ui/pipeline \ --header 'content-type: application/json' \ --header 'Circle-Token: $CIRCLE_TOKEN' \ - --data-raw '{"branch":"pull/24289/head","parameters":{"react-dist-tag":"next"}}' + --data-raw '{"branch":"pull/24289/head","parameters":{"react-version":"next"}}' ```