diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index e914b6557b9..6440e7dc5b2 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -30,6 +30,9 @@ runs: - name: Lint java shell: bash run: ./scripts/circleci/exec_swallow_error.sh yarn lint-java --check + - name: Verify not committing repo after running build + shell: bash + run: yarn run build --check - name: Run flowcheck shell: bash run: yarn flow-check diff --git a/scripts/build/build.js b/scripts/build/build.js index 99bfd9d7524..4cb9de68a0a 100644 --- a/scripts/build/build.js +++ b/scripts/build/build.js @@ -36,13 +36,14 @@ const config = { allowPositionals: true, options: { help: {type: 'boolean'}, + check: {type: 'boolean'}, }, }; async function build() { const { positionals: packageNames, - values: {help}, + values: {help, check}, } = parseArgs(config); if (help) { @@ -58,17 +59,35 @@ async function build() { return; } - console.log('\n' + chalk.bold.inverse('Building packages') + '\n'); + if (!check) { + console.log('\n' + chalk.bold.inverse('Building packages') + '\n'); + } const packagesToBuild = packageNames.length ? packageNames.filter(packageName => packageName in buildConfig.packages) : Object.keys(buildConfig.packages); + let ok = true; for (const packageName of packagesToBuild) { - await buildPackage(packageName); + if (check) { + ok &&= await checkPackage(packageName); + } else { + await buildPackage(packageName); + } } - process.exitCode = 0; + process.exitCode = ok ? 0 : 1; +} + +async function checkPackage(packageName /*: string */) /*: Promise */ { + const artifacts = await exportedBuildArtifacts(packageName); + if (artifacts.length > 0) { + console.log( + `${chalk.bgRed(packageName)}: has been build and the ${chalk.bold('build artifacts')} committed to the repository. This will break Flow checks.`, + ); + return false; + } + return true; } async function buildPackage(packageName /*: string */) { @@ -183,6 +202,33 @@ type PackageJson = { }; */ +function isStringOnly(entries /*: mixed */) /*: entries is string */ { + return typeof entries === 'string'; +} + +async function exportedBuildArtifacts( + packageName /*: string */, +) /*: Promise */ { + const packagePath = path.resolve(PACKAGES_DIR, packageName, 'package.json'); + const pkg /*: PackageJson */ = JSON.parse( + await fs.readFile(packagePath, 'utf8'), + ); + if (pkg.exports == null) { + throw new Error( + packageName + + ' does not define an "exports" field in its package.json. As part ' + + 'of the build setup, this field must be used in order to rewrite ' + + 'paths to built files in production.', + ); + } + + return Object.values(pkg.exports) + .filter(isStringOnly) + .filter(filepath => + path.dirname(filepath).split(path.sep).includes(BUILD_DIR), + ); +} + /** * Get the set of Flow entry points to build. * diff --git a/scripts/run-ci-javascript-tests.js b/scripts/run-ci-javascript-tests.js index 0a6a1351355..c0eefa9432a 100644 --- a/scripts/run-ci-javascript-tests.js +++ b/scripts/run-ci-javascript-tests.js @@ -47,6 +47,13 @@ try { throw Error(exitCode); } + describe('Test: No JS build artifacts'); + if (exec(`${YARN_BINARY} run build --check`).code) { + echo('Failed, there are build artifacts in this commit.'); + exitCode = 1; + throw Error(exitCode); + } + describe('Test: Flow check'); if (exec(`${YARN_BINARY} run flow-check`).code) { echo('Failed to run flow.');