angular/aio/tools/examples/shared/sync-boilerplate-dependencies.js
George Kalpakas 7243564499 build(docs-infra): ensure all boilerplate dependencies are in sync (#47009)
All docs examples share the same `node_modules/` (symlinked into each
example from `aio/tools/examples/shared/node_modules/`). However, each
example type has a different `package.json`, which comes from
`aio/tools/examples/shared/boilerplate/*`). In order to ensure that the
dependencies in each example's `package.json` are the same as the ones
in the symlinked `node_modules/` (i.e. the ones that CI tests are run
with), we have a script (`yarn run sync-deps`) that can sync
dependencies from `shared/package.json` into the boilerplate
`package.json` files.

Previously, this script had to be run manually, which was easy to
forget/not know about and resulted in the boilerplate dependencies
often being out-of-sync with the ones in `shared/package.json` (and by
extension, the ones that were actually installed in `node_modules/`).

This commit helps keep the boilerplate dependencies up-to-date in the
following ways:
- Adds the `sync-deps` script to the `postinstall` scripts.
  This ensures that dependencies remain in sync whenever someone
  manually updates dependencies in `shared/package.json`.
- Runs the `sync-deps` script as a Renovate post-upgrade task.
  This ensures that the depenencies remain in sync whenever Renovate
  updates dependencies in `shared/package.json`.
  For more info on configuring post-upgrade tasks in Renovate, see:
  - [postUpgradeTasks][1]
  - [allowedPostUpgradeCommands][2]
  - [allowPostUpgradeCommandTemplating][3]

NOTE:
For the Renovate change to take effect, the [global config][4] in
`angular/dev-infra` also needs to be updated. This will be done in a
separate PR.

[1]: https://docs.renovatebot.com/configuration-options/#postupgradetasks
[2]: https://docs.renovatebot.com/self-hosted-configuration/#allowedpostupgradecommands
[3]: https://docs.renovatebot.com/self-hosted-configuration/#allowpostupgradecommandtemplating
[4]: 22d3067021/.github/ng-renovate/runner-config.js

PR Close #47009
2022-08-02 11:58:51 -07:00

70 lines
2.3 KiB
JavaScript

#!/usr/bin/env node
/**
* Usage:
* ```sh
* node sync-boilerplate-dependencies
* ```
*
* Updates the dependency versions of the top-level `package.json` files in each sub-folder of
* `./boilerplate/` to match the ones in `./package.json`.
*/
// !!! WARNING !!!
//
// This script is run as a [post-upgrade Renovate task][1]. Therefore, it needs to be able to run
// even when dependencies are not installed (i.e. only rely on Node.js built-in modules).
// See [here][2] for more info on why dependencies may not have been installed.
//
// [1]: https://docs.renovatebot.com/configuration-options/#postupgradetasks
// [2]: https://docs.renovatebot.com/self-hosted-configuration/#skipinstalls
const fs = require('fs');
const path = require('path');
const BOILERPLATE_DIR = `${__dirname}/boilerplate`;
const SHARED_PACKAGE_JSON_PATH = `${__dirname}/package.json`;
const sharedPkgJson = loadJsonFile(SHARED_PACKAGE_JSON_PATH);
const boilerplatePkgJsonPaths = collectPackageJsonFiles(BOILERPLATE_DIR);
boilerplatePkgJsonPaths.forEach(syncDependencies);
// Helpers
function collectPackageJsonFiles(dirPath) {
return fs.readdirSync(dirPath)
.map(childName => `${dirPath}/${childName}`)
.filter(childPath => fs.statSync(childPath).isDirectory())
.map(subDirPath => `${subDirPath}/package.json`)
.filter(pkgJsonPath => fs.existsSync(pkgJsonPath));
}
function loadJsonFile(filePath) {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
function syncDependencies(boilerplatePkgJsonPath) {
console.log(`Syncing '${path.relative(__dirname, boilerplatePkgJsonPath)}'...`);
const boilerplatePkgJson = loadJsonFile(boilerplatePkgJsonPath);
['dependencies', 'devDependencies', 'peerDependencies']
.filter(depsProp => boilerplatePkgJson.hasOwnProperty(depsProp))
.forEach(depsProp => {
const srcDeps = sharedPkgJson[depsProp];
const dstDeps = boilerplatePkgJson[depsProp];
for (const dep of Object.keys(dstDeps)) {
if (!srcDeps.hasOwnProperty(dep)) {
throw new Error(
`Unable to update dependency '${dep}' in '${boilerplatePkgJsonPath} > ${depsProp}'. ` +
`The dependency is missing from '${SHARED_PACKAGE_JSON_PATH}'.`);
}
dstDeps[dep] = srcDeps[dep];
}
});
fs.writeFileSync(boilerplatePkgJsonPath, `${JSON.stringify(boilerplatePkgJson, null, 2)}\n`);
}