diff --git a/.circleci/config.yml b/.circleci/config.yml index 535563556ee95..ad8a52d1b5125 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -74,6 +74,18 @@ jobs: - *run_yarn - run: yarn test --maxWorkers=2 + test_source_experimental: + docker: *docker + environment: *environment + steps: + - checkout + - *restore_yarn_cache + - *run_yarn + - run: + environment: + RELEASE_CHANNEL: experimental + command: yarn test --maxWorkers=2 + test_source_persistent: docker: *docker environment: *environment @@ -114,6 +126,30 @@ jobs: - dist - sizes/*.json + build_experimental: + docker: *docker + environment: *environment + parallelism: 20 + steps: + - checkout + - *restore_yarn_cache + - *run_yarn + - run: + environment: + RELEASE_CHANNEL: experimental + command: | + ./scripts/circleci/add_build_info_json.sh + ./scripts/circleci/update_package_versions.sh + - run: yarn build + - persist_to_workspace: + root: build + paths: + - facebook-www + - node_modules + - react-native + - dist + - sizes/*.json + process_artifacts: docker: *docker environment: *environment @@ -208,7 +244,7 @@ jobs: workflows: version: 2 - commit: + stable: jobs: - setup - lint: @@ -247,9 +283,24 @@ workflows: - test_dom_fixtures: requires: - build - hourly: + + experimental: + jobs: + - setup + - test_source_experimental: + requires: + - setup + - build_experimental: + requires: + - setup + - process_artifacts: + requires: + - build_experimental + + fuzz_tests: triggers: - schedule: + # Fuzz tests run hourly cron: "0 * * * *" filters: branches: diff --git a/.eslintrc.js b/.eslintrc.js index 603f20a3cacba..df147c90bd3e6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -149,6 +149,7 @@ module.exports = { spyOnProd: true, __PROFILE__: true, __UMD__: true, + __EXPERIMENTAL__: true, trustedTypes: true, }, }; diff --git a/dangerfile.js b/dangerfile.js index 985c6b675626d..3eee061d5cf34 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -124,7 +124,7 @@ function git(args) { let previousBuildResults = null; try { - let baseCIBuildId = null; + let baseArtifactsInfo = null; const statusesResponse = await fetch( `https://api.github.com/repos/facebook/react/commits/${baseCommit}/status` ); @@ -133,33 +133,54 @@ function git(args) { warn(`Base commit is broken: ${baseCommit}`); return; } - for (let i = 0; i < statuses.length; i++) { + findArtifactsInfo: for (let i = 0; i < statuses.length; i++) { const status = statuses[i]; - // This must match the name of the CI job that creates the build artifacts - if (status.context === 'ci/circleci: process_artifacts') { - if (status.state === 'success') { - baseCIBuildId = /\/facebook\/react\/([0-9]+)/.exec( - status.target_url - )[1]; - break; - } - if (status.state === 'pending') { - warn(`Build job for base commit is still pending: ${baseCommit}`); - return; + // CircleCI doesn't have an API to retrieve a workflow ID for a given + // commit, so we have to resort to some trickery. Use the statuses + // endpoint to find a recent status that matches the "stable" workflow. + // It must be a job that doesn't also run in the "experimental" workflow. + if (status.context === 'ci/circleci: build') { + // Scrape the job ID from the url. + const buildJobID = /\/facebook\/react\/([0-9]+)/.exec( + status.target_url + )[1]; + + // Get the workflow that this job belongs to + const buildJobMetadataResponse = await fetch( + `https://circleci.com/api/v1.1/project/gh/facebook/react/${buildJobID}` + ); + const buildJobMetadata = await buildJobMetadataResponse.json(); + const workflowID = buildJobMetadata.workflows.workflow_id; + + // Now we can get the jobs that are part of this workflow. + const jobsResponse = await fetch( + `https://circleci.com/api/v2/workflow/${workflowID}/jobs?circle-token=${ + process.env.CIRCLE_CI_API_TOKEN + }` + ); + const {items: jobs} = await jobsResponse.json(); + for (let j = 0; j < jobs.length; j++) { + const job = jobs[j]; + if (job.name === 'process_artifacts') { + // Found it! + const baseCIBuildId = job.job_number; + const baseArtifactsInfoResponse = await fetch( + `https://circleci.com/api/v1.1/project/github/facebook/react/${baseCIBuildId}/artifacts?circle-token=${ + process.env.CIRCLE_CI_API_TOKEN + }` + ); + baseArtifactsInfo = await baseArtifactsInfoResponse.json(); + break findArtifactsInfo; + } } } } - if (baseCIBuildId === null) { + if (baseArtifactsInfo === null) { warn(`Could not find build artifacts for base commit: ${baseCommit}`); return; } - const baseArtifactsInfoResponse = await fetch( - `https://circleci.com/api/v1.1/project/github/facebook/react/${baseCIBuildId}/artifacts` - ); - const baseArtifactsInfo = await baseArtifactsInfoResponse.json(); - for (let i = 0; i < baseArtifactsInfo.length; i++) { const info = baseArtifactsInfo[i]; if (info.path === 'home/circleci/project/build/bundle-sizes.json') { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index c1645f0497674..ddb12f71b7993 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -52,7 +52,7 @@ export const disableInputAttributeSyncing = false; // These APIs will no longer be "unstable" in the upcoming 16.7 release, // Control this behavior with a flag to support 16.6 minor releases in the meanwhile. -export const enableStableConcurrentModeAPIs = false; +export const enableStableConcurrentModeAPIs = __EXPERIMENTAL__; export const warnAboutShorthandPropertyCollision = false; diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 8eeda9784000d..0a742aad4022f 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -11,6 +11,7 @@ declare var __PROFILE__: boolean; declare var __UMD__: boolean; +declare var __EXPERIMENTAL__: boolean; declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ inject: ?((stuff: Object) => void) diff --git a/scripts/jest/setupEnvironment.js b/scripts/jest/setupEnvironment.js index 10ff1234b4c72..1e694b2d31575 100644 --- a/scripts/jest/setupEnvironment.js +++ b/scripts/jest/setupEnvironment.js @@ -7,6 +7,7 @@ if (NODE_ENV !== 'development' && NODE_ENV !== 'production') { global.__DEV__ = NODE_ENV === 'development'; global.__PROFILE__ = NODE_ENV === 'development'; global.__UMD__ = false; +global.__EXPERIMENTAL__ = process.env.RELEASE_CHANNEL === 'experimental'; if (typeof window !== 'undefined') { global.requestIdleCallback = function(callback) { diff --git a/scripts/release/prepare-canary-commands/get-latest-master-build-number.js b/scripts/release/prepare-canary-commands/get-latest-master-build-number.js index a87361ecbc74a..574ec98184c74 100644 --- a/scripts/release/prepare-canary-commands/get-latest-master-build-number.js +++ b/scripts/release/prepare-canary-commands/get-latest-master-build-number.js @@ -13,6 +13,7 @@ const run = async () => { entry => entry.branch === 'master' && entry.status === 'success' && + entry.workflows.workflow_name === 'build_stable' && entry.workflows.job_name === 'process_artifacts' ).build_num; diff --git a/scripts/release/utils.js b/scripts/release/utils.js index 861b83ad34d6c..816f0fdfc2853 100644 --- a/scripts/release/utils.js +++ b/scripts/release/utils.js @@ -78,12 +78,16 @@ const getArtifactsList = async buildID => { const getBuildInfo = async () => { const cwd = join(__dirname, '..', '..'); + const isExperimental = process.env.RELEASE_CHANNEL === 'experimental'; + const branch = await execRead('git branch | grep \\* | cut -d " " -f2', { cwd, }); const commit = await execRead('git show -s --format=%h', {cwd}); const checksum = await getChecksumForCurrentRevision(cwd); - const version = `0.0.0-${commit}`; + const version = isExperimental + ? `0.0.0-experimental-${commit}` + : `0.0.0-${commit}`; // Only available for Circle CI builds. // https://circleci.com/docs/2.0/env-vars/ @@ -94,7 +98,9 @@ const getBuildInfo = async () => { const packageJSON = await readJson( join(cwd, 'packages', 'react', 'package.json') ); - const reactVersion = `${packageJSON.version}-canary-${commit}`; + const reactVersion = isExperimental + ? `${packageJSON.version}-experimental-canary-${commit}` + : `${packageJSON.version}-canary-${commit}`; return {branch, buildNumber, checksum, commit, reactVersion, version}; }; diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 92f1f3a754648..34184efab63e1 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -304,7 +304,8 @@ function getPlugins( bundleType, globalName, moduleType, - pureExternalModules + pureExternalModules, + isExperimentalBuild ) { const findAndRecordErrorCodes = extractErrorCodes(errorCodeOpts); const forks = Modules.getForks(bundleType, entry, moduleType); @@ -362,6 +363,7 @@ function getPlugins( __PROFILE__: isProfiling || !isProduction ? 'true' : 'false', __UMD__: isUMDBundle ? 'true' : 'false', 'process.env.NODE_ENV': isProduction ? "'production'" : "'development'", + __EXPERIMENTAL__: isExperimentalBuild, }), // We still need CommonJS for external deps like object-assign. commonjs(), @@ -485,6 +487,8 @@ async function createBundle(bundle, bundleType) { module => !importSideEffects[module] ); + const isExperimentalBuild = process.env.RELEASE_CHANNEL === 'experimental'; + const rollupConfig = { input: resolvedEntry, treeshake: { @@ -508,7 +512,8 @@ async function createBundle(bundle, bundleType) { bundleType, bundle.global, bundle.moduleType, - pureExternalModules + pureExternalModules, + isExperimentalBuild ), // We can't use getters in www. legacy: