Skip to content

Commit

Permalink
feat(exec-and-magic): inject useful env-vars
Browse files Browse the repository at this point in the history
Fixes #41
  • Loading branch information
ianwremmel committed Mar 28, 2018
1 parent 198cefb commit 3019ea4
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 4 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,29 @@ clark hoist
clark hoist --package
```

### Run a command each package directory
### Run a command in each package directory

```bash
clark exec <command>
```

The following environment variables will be available to your script:

* `CLARK_ROOT_PATH`: The monorepo's root path.
* `CLARK_PACKAGE_REL_PATH`: The relative path within the monorepo to the package currently being acted upon.
* `CLARK_PACKAGE_ABS_PATH`: The absolute path to the package currently being acted upon.
* `CLARK_PACKAGE_NAME`: The name of the package being acted upon according to its `package.json`

The script will be invoked from within the package's directory.

### Run a command in a single package directory

```bash
clark exec <command> --package <packagename>
```

The script will be invoked from within the package's directory.

### Magic

While Clark, obviously, provides its own commands, there's a set of very project specific commands that we simply can't dictate for you. These are commands like `build`, `lint`, and `test` that you want to run each independently against each package.
Expand All @@ -99,6 +110,8 @@ clark init --script test='mocha test/*/spec/**/*.js' --script build='babel -d di

Package commands are executed sequentially in each package directory. They may be overridden with an entry in the package's package.json.

> Note: magic scripts receive the same environment variables as `clark exec` and are executed within each package directory.
For example, your repository might use [mocha](https://mochajs.org/) to run your integration tests

```json
Expand Down
46 changes: 44 additions & 2 deletions src/lib/packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@ import debugFactory from 'debug';
import {sync as glob} from 'glob';
import {readFile, writeFile} from 'mz/fs';
import {dirname, resolve} from 'path';
import {read as readRootPackage, write as writeRootPackage} from './project';
import {
findProjectRoot,
read as readRootPackage,
write as writeRootPackage,
} from './project';
import {spawn} from './spawn';

const debug = debugFactory('clark:lib:packages');

const cwd = 'packages/node_modules';

/**
* Finds the relative path to the specified package.
* @param packageName
*/
export async function findPackagePath(packageName: string): Promise<string> {
return `./packages/node_modules/${packageName}`;
}

/**
* Lists all packages in the monorepo
*/
Expand Down Expand Up @@ -202,11 +214,22 @@ export async function exec(cmd: string, packageName: string): Promise<void> {
const bin = 'bash';
const args = ['-c', cmd];
const {PATH, ...env} = process.env;
const clarkEnv = {
CLARK_PACKAGE_ABS_PATH: resolve(
await findProjectRoot(),
await findPackagePath(packageName),
),
CLARK_PACKAGE_NAME: packageName,
CLARK_PACKAGE_REL_PATH: await findPackagePath(packageName),
CLARK_ROOT_PATH: await findProjectRoot(),
...filterEnv(env),
};

try {
const result = await spawn(bin, args, {
cwd: resolve(cwd, packageName),
env: {
...env,
...clarkEnv,
PATH: `${PATH}:${resolve(process.cwd(), 'node_modules', '.bin')}`,
},
});
Expand All @@ -217,3 +240,22 @@ export async function exec(cmd: string, packageName: string): Promise<void> {
throw err;
}
}

/**
* Removes any `CLARK_` prefixed variables from env before passing them to
* `spawn()`.
* @param env
*/
function filterEnv(env: object): object {
return Object.entries(env).reduce<EnvObject>((acc, [key, value]) => {
if (!key.startsWith('CLARK_')) {
acc[key] = value;
}

return acc;
}, {});
}

interface EnvObject {
[key: string]: string;
}
35 changes: 34 additions & 1 deletion test/integration/spec/exec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import {assert} from 'chai';
import {resolve} from 'path';

import run from '../lib/run';

function stringToObject(str: string) {
return str
.split('\n')
.map(row => row.split('='))
.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
}

describe('exec', () => {
it('executes a command in every directory', async () => {
const result = await run('exec pwd');
Expand Down Expand Up @@ -41,6 +51,29 @@ describe('exec', () => {
);
});

it('injects useful environment variables', async () => {
const result = await run(
'exec --package @example/scoped-package-the-first "env | grep CLARK"',
);
// strip out this project's path so we can write consistent assertions
const modified = result.replace(
new RegExp(resolve(__dirname, '..', '..'), 'g'),
'REPLACED',
);

// different versions of node unexpectedly impact the order of the results,
// so we need to do an object comparison instead of a string comparison.

assert.deepEqual(stringToObject(modified), {
CLARK_ROOT_PATH: 'REPLACED/integration/fixtures/monorepo',
CLARK_PACKAGE_REL_PATH:
'./packages/node_modules/@example/scoped-package-the-first',
CLARK_PACKAGE_ABS_PATH:
'REPLACED/integration/fixtures/monorepo/packages/node_modules/@example/scoped-package-the-first',
CLARK_PACKAGE_NAME: '@example/scoped-package-the-first',
});
});

describe('with --package', () => {
it('executes a comand in the specified package directory', async () => {
const result = await run('exec --package-name not-scoped pwd');
Expand Down

0 comments on commit 3019ea4

Please sign in to comment.