Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
derekju committed Feb 6, 2020
2 parents 005997c + ce30b24 commit 94a1c1f
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .buildkite/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fi

# Get the change set
git diff-tree --no-commit-id --name-only -r $SHA1 $SHA2 | grep -v "\.md$" > changes.txt
CHANGES=$(jazelle changes ./changes.txt)
CHANGES=$(jazelle changes --format dirs ./changes.txt)

for DIR in $CHANGES ; do (
PROJECT=$(basename "$DIR");
Expand Down
19 changes: 19 additions & 0 deletions jazelle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ If you get into a bad state, here are some things you can try:
- [`jazelle dedupe`](#jazelle-dedupe)
- [`jazelle purge`](#jazelle-purge)
- [`jazelle check`](#jazelle-check)
- [`jazelle align`](#jazelle-align)
- [`jazelle chunk`](#jazelle-chunk)
- [`jazelle changes`](#jazelle-changes)
- [`jazelle plan`](#jazelle-plan)
Expand Down Expand Up @@ -461,6 +462,14 @@ Shows a report of out-of-sync top level dependencies across projects
}
```

### `jazelle align`

Align a project's dependency versions to respect the version policy, if there is one

`jazelle align --cwd [cwd]`

- `--cwd` - Project folder (absolute or relative to shell `cwd`). Defaults to `process.cwd()`

### `jazelle chunk`

Prints a glob pattern representing a chunk of files matching a given list of glob patterns. Useful for splitting tests across multiple CI jobs.
Expand Down Expand Up @@ -673,6 +682,7 @@ If you want commands to display colorized output, run their respective NPM scrip
- [dedupe](#dedupe)
- [purge](#purge)
- [check](#check)
- [align](#align)
- [chunk](#chunk)
- [changes](#changes)
- [plan](#plan)
Expand Down Expand Up @@ -820,6 +830,15 @@ let check: ({root: string, projects: Array<string>, versionPolicy: VersionPolicy

- `root` - Monorepo root folder (absolute path)

### `align`

Align a project's dependency versions to respect the version policy, if there is one

`let align: ({root: string, cwd: string}) => Promise<void>`

- `root` - Monorepo root folder (absolute path)
- `cwd` - Project folder (absolute path)

### `chunk`

Returns a glob pattern representing a chunk of files matching a given list of glob patterns. Useful for splitting tests across multiple CI jobs.
Expand Down
4 changes: 2 additions & 2 deletions jazelle/bin/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async function runMaster() {
for (const data of payload) {
if (data.length > 0) {
const requiredCores = Math.min(availableCores, data.length);
const workers = [...Array(requiredCores)].map(() => fork());
const workers = [...Array(requiredCores)].map(() => fork(process.env));

await install({
root,
Expand Down Expand Up @@ -79,7 +79,7 @@ async function runMaster() {
const command = data.shift();
const log = `${tmpdir()}/${Math.random() * 1e17}`;

if (worker.state === 'dead') worker = fork();
if (worker.state === 'dead') worker = fork(process.env);

worker.send({command, log});
worker.once('exit', async () => {
Expand Down
72 changes: 38 additions & 34 deletions jazelle/commands/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,73 @@ const {resolve} = require('path');
const {assertProjectDir} = require('../utils/assert-project-dir.js');
const {getManifest} = require('../utils/get-manifest.js');
const {getLocalDependencies} = require('../utils/get-local-dependencies.js');
const {node, yarn} = require('../utils/binary-paths.js');
const {exec, read, write} = require('../utils/node-helpers.js');
const {read, write} = require('../utils/node-helpers.js');
const {findLocalDependency} = require('../utils/find-local-dependency.js');
const {add: addDep} = require('../utils/lockfile.js');
const {install} = require('./install.js');

/*
adding local dep should:
- add it to the project's package.json, pointing to the exact local version
- update the BUILD.bazel file `deps` field
- not add it to the project's yarn.lock
*/

/*::
export type AddArgs = {
root: string,
cwd: string,
name: string,
args: Array<string>,
version?: string,
dev?: boolean,
};
export type Add = (AddArgs) => Promise<void>;
*/
const add /*: Add */ = async ({
root,
cwd,
name: nameWithVersion,
dev = false,
}) => {
const add /*: Add */ = async ({root, cwd, args, dev = false}) => {
await assertProjectDir({dir: cwd});
let [, name, version] = nameWithVersion.match(/(@?[^@]*)@?(.*)/) || [];

const type = dev ? 'devDependencies' : 'dependencies';
const local = await findLocalDependency({root, name});
if (local) {
if (version && version !== local.meta.version) {
throw new Error(`You must use version ${local.meta.version}`);
}

// group by whether the dep is local (listed in manifest.json) or external (from registry)
const locals = [];
const externals = [];
for (const arg of args) {
let [, name, version] = arg.match(/(@?[^@]*)@?(.*)/) || [];
const local = await findLocalDependency({root, name});
if (local) locals.push({local, name});
else externals.push({name, range: version, type});
}

// add local deps
if (locals.length > 0) {
const meta = JSON.parse(await read(`${cwd}/package.json`, 'utf8'));
if (!meta[type]) meta[type] = {};
const types = [
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
];
for (const t of types) {
if (meta[t] && meta[t][name]) {
meta[t][name] = local.meta.version;

for (const {local, name} of locals) {
// update existing entries
const types = [
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
'resolutions',
];
for (const t of types) {
if (meta[t] && meta[t][name]) {
meta[t][name] = local.meta.version;
}
}
meta[type][name] = local.meta.version;
}
meta[type][name] = local.meta.version;
await write(
`${cwd}/package.json`,
`${JSON.stringify(meta, null, 2)}\n`,
'utf8'
);
} else {
// adding does not dedupe transitives, since consumers will rarely want to check if they introduced regressions in unrelated projects
if (!version) {
version = JSON.parse(
await exec(`${node} ${yarn} info ${name} version --json 2>/dev/null`)
).data;
}
const additions = [{name, range: version, type}];
}

// add external deps
if (externals.length > 0) {
const {projects} = await getManifest({root});
const deps = await getLocalDependencies({
dirs: projects.map(dir => `${root}/${dir}`),
Expand All @@ -75,11 +78,12 @@ const add /*: Add */ = async ({
const tmp = `${root}/third_party/jazelle/temp/yarn-utilities-tmp`;
await addDep({
roots: [cwd],
additions,
additions: externals,
ignore: deps.map(d => d.meta.name),
tmp,
});
}

await install({root, cwd});
};

Expand Down
63 changes: 63 additions & 0 deletions jazelle/commands/align.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// @flow
const {getManifest} = require('../utils/get-manifest.js');
const {getAllDependencies} = require('../utils/get-all-dependencies.js');
const {read, write} = require('../utils/node-helpers.js');
const {install} = require('./install.js');

/*::
type AlignArgs = {
root: string,
cwd: string,
}
type Align = (AlignArgs) => Promise<void>
*/
const align /*: Align */ = async ({root, cwd}) => {
const {projects, versionPolicy} = /*:: await */ await getManifest({root});
if (versionPolicy) {
const deps = await getAllDependencies({root, projects});
const meta = JSON.parse(await read(`${cwd}/package.json`, 'utf8'));
const others = deps.filter(dep => dep.meta.name !== meta.name);
const types = ['dependencies', 'devDependencies', 'resolutions'];
let changed = false;
for (const type of types) {
if (meta[type]) {
for (const name in meta[type]) {
if (shouldSyncVersions({versionPolicy, name})) {
const version = getVersion({name, deps: others});
if (version !== null) {
meta[type][name] = version;
changed = true;
}
}
}
}
}
if (changed) {
const modified = JSON.stringify(meta, null, 2) + '\n';
await write(`${cwd}/package.json`, modified, 'utf8');
}
}
await install({root, cwd});
};

const shouldSyncVersions = ({versionPolicy, name}) => {
const {lockstep = false, exceptions = []} = versionPolicy;
return (
(lockstep && !exceptions.includes(name)) ||
(!lockstep && exceptions.includes(name))
);
};

const getVersion = ({name, deps}) => {
const types = ['dependencies', 'devDependencies', 'resolutions'];
for (const {meta} of deps) {
for (const type of types) {
for (const key in meta[type]) {
if (name === key) return meta[type][key];
}
}
}
return null;
};

module.exports = {align};
55 changes: 55 additions & 0 deletions jazelle/commands/outdated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// @flow
const {getManifest} = require('../utils/get-manifest.js');
const {getAllDependencies} = require('../utils/get-all-dependencies.js');
const {exec} = require('../utils/node-helpers.js');
const {node, yarn} = require('../utils/binary-paths.js');
const {minVersion, gt} = require('semver');

/*::
type OutdatedArgs = {
root: string,
};
type Outdated = (OutdatedArgs) => Promise<void>
*/
const outdated /*: Outdated */ = async ({root}) => {
const {projects} = await getManifest({root});
const locals = await getAllDependencies({root, projects});
const map = {};
const types = ['dependencies', 'devDependencies'];
for (const local of locals) {
for (const type of types) {
if (local.meta[type]) {
for (const name in local.meta[type]) {
if (!map[name]) map[name] = new Set();
map[name].add(local.meta[type][name]);
}
}
}
}
// report local discrepancies
for (const name in map) {
const local = locals.find(local => local.meta.name === name);
if (local) {
const {version} = local.meta;
for (const range of map[name]) {
if (version !== range) console.log(name, range, version);
}
}
}
// then report registry discrepancies
for (const name in map) {
const local = locals.find(local => local.meta.name === name);
if (!local) {
const query = `${node} ${yarn} info ${name} version --json 2>/dev/null`;
const registryVersion = await exec(query);
if (registryVersion) {
const latest = JSON.parse(registryVersion).data;
for (const range of map[name]) {
if (gt(latest, minVersion(range))) console.log(name, range, latest);
}
}
}
}
};

module.exports = {outdated};
17 changes: 15 additions & 2 deletions jazelle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const {upgrade} = require('./commands/upgrade.js');
const {dedupe} = require('./commands/dedupe.js');
const {purge} = require('./commands/purge.js');
const {check} = require('./commands/check.js');
const {outdated} = require('./commands/outdated.js');
const {align} = require('./commands/align.js');
const {chunk} = require('./commands/chunk.js');
const {changes} = require('./commands/changes.js');
const {plan} = require('./commands/plan.js');
Expand Down Expand Up @@ -77,11 +79,11 @@ const runCLI /*: RunCLI */ = async argv => {
[name] Package to add at a specific version. ie., foo@1.2.3
--dev Whether to install as devDependency
--cwd [cwd] Project directory to use`,
async ({cwd, name, dev}) =>
async ({cwd, dev}) =>
add({
root: await rootOf(args),
cwd,
name: name || dev, // if dev is passed before name, resolve to correct value
args: rest.filter(arg => arg != '--dev'), // if dev is passed before name, resolve to correct value
dev: Boolean(dev), // FIXME all args can technically be boolean, but we don't want Flow complaining about it everywhere
}),
],
Expand Down Expand Up @@ -113,6 +115,16 @@ const runCLI /*: RunCLI */ = async argv => {
async ({json}) =>
check({root: await rootOf(args), json: Boolean(json)}),
],
outdated: [
`Displays deps whose version is behind the latest version`,
async () => outdated({root: await rootOf(args)}),
],
align: [
`Align a project's dependency versions to respect the version policy, if there is one
--cwd [cwd] Project directory to use`,
async ({cwd}) => await align({root: await rootOf(args), cwd}),
],
chunk: [
`Print a glob pattern representing a chunk of a set of files
Expand Down Expand Up @@ -264,6 +276,7 @@ module.exports = {
dedupe,
purge,
check: reportMismatchedTopLevelDeps,
align,
chunk: getChunkPattern,
changes: findChangedTargets,
plan: getTestGroups,
Expand Down
10 changes: 7 additions & 3 deletions jazelle/rules/execute-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const {execSync: exec} = require('child_process');
const {dirname, basename} = require('path');

const root = process.cwd();
const [node, , main, bin, command, dist, out, ...args] = process.argv;
const [node, , main, bin, command, distPaths, out, ...args] = process.argv;
const dists = distPaths.split('|');

const files = exec(`find . -name output.tgz`, {cwd: bin, encoding: 'utf8'})
.split('\n')
Expand All @@ -22,9 +23,12 @@ files.map(f => {
const {scripts = {}} = JSON.parse(read(`${main}/package.json`, 'utf8'));

if (out) {
exec(`mkdir -p "${dist}"`, {cwd: main});
for (const dist of dists) {
exec(`mkdir -p "${dist}"`, {cwd: main});
}
runCommands();
exec(`tar czf "${out}" "${dist}"`, {cwd: main});
const dirs = dists.map(dist => `"${dist}"`).join(' ');
exec(`tar czf "${out}" ${dirs}`, {cwd: main});
} else {
try {
runCommands();
Expand Down
2 changes: 1 addition & 1 deletion jazelle/rules/web-monorepo.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def _web_binary_impl(ctx):
node = ctx.files._node[0].path,
srcdir = ctx.build_file_path,
command = ctx.attr.build,
dist = ctx.attr.dist[0],
dist = "|".join(ctx.attr.dist),
output = build_ouput.path,
bindir = ctx.bin_dir.path,
build = ctx.files._script[0].path,
Expand Down

0 comments on commit 94a1c1f

Please sign in to comment.