Skip to content
This repository has been archived by the owner on Mar 4, 2022. It is now read-only.

Commit

Permalink
Merge f0141f1 into 390eaa4
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-roemer committed Feb 14, 2018
2 parents 390eaa4 + f0141f1 commit 25e24e6
Show file tree
Hide file tree
Showing 29 changed files with 1,175 additions and 508 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

.DS_Store
.project
.vscode
bower_components
node_modules
npm-debug.log*
Expand Down
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ script:
- npm --version
- npm run builder:check-ci

# Do some primitive bash functional tests.
- bash test/func/test.sh

# Manually send coverage reports to coveralls.
# - Aggregate client results
# - Single server and func test results
Expand Down
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
History
=======

## Unreleased MAJOR

* **Breaking**: Add `pre|post` lifecycle commands.
[#68](https://github.com/FormidableLabs/builder/issues/68)
* **Breaking**: Error out on invalid or conflicting command line flags passed
to `builder`.
* Add more explicit behavior and tests for `--setup` flag.

## 3.2.3

* Account for `PATH` vs. `Path` env var differences in MinGW vs. cmd on
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015-2016 Formidable Labs
Copyright (c) 2015-2018 Formidable Labs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
168 changes: 168 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ the rough goals and motivations behind the project.
- [builder run](#builder-run)
- [builder concurrent](#builder-concurrent)
- [builder envs](#builder-envs)
- [Setup Task](#setup-task)
- [Task Lifecycle](#task-lifecycle)
- [The Basics](#the-basics)
- [Other Builder Actions](#other-builder-actions)
- [Builder Flags During Pre and Post](#builder-flags-during-pre-and-post)
- [Task Prefix Complexities](#task-prefix-complexities)
- [Custom Flags](#custom-flags)
- [Expanding the Archetype Path](#expanding-the-archetype-path)
- [Tasks](#tasks)
Expand Down Expand Up @@ -375,6 +381,168 @@ The environment variable `FOO` will have a value of `"ENVS"` with the single
environment object array item given to `builder envs` overriding the `--env`
flag value.

#### Setup Task

A task specified in `--setup <task>` will have the following flags apply to
the setup task as apply to the main task:

* `--env`
* `--env-path`
* `--quiet`
* `--log-level`

The following flags do _not_ apply to a setup task:

* `--` custom flags
* `--tries`
* `--expand-archetype`

That said, if you need things like `--tries`, etc., these can be always coded
into a wrapped task like:

```js
"scripts": {
"setup-alone": "while sleep 1; do echo SETUP; done",
"setup": "builder run --tries=5 setup-alone",
"test": "mocha",
"test-full": "builder run --setup=setup test"
}
```

#### Task Lifecycle

Builder executes `pre<task>` and `post<task>` tasks the same as `npm` does,
with some perhaps not completely obvious corner cases.

##### The Basics

If you have:

```js
"scripts": {
"prefoo": "echo PRE",
"foo": "echo TEMP",
"postfoo": "echo POST"
}
```

And run `builder run foo`, then just like `npm`, builder will run in order:

1. `prefoo`
2. `foo`
3. `postfoo`

assuming each task succeeds, otherwise execution is terminated.

`pre` and `post` tasks can be provided in an archetype and overridden in a root
`package.json` in the exact same manner as normal Builder tasks.

##### Other Builder Actions

`builder run` works essentially the same as `npm run`. Things get a little
messy with Builder's other execution options:

`builder envs` runs `pre|post` tasks exactly **once** regardless of how many
concurrent executions of the underlying task (with different environment
variables) occur.

`builder concurrent` runs appropriate `pre|post` tasks for each independent
task. So, for something like:

```js
"scripts": {
"prefoo": "echo PRE FOO",
"foo": "echo TEMP FOO",
"postfoo": "echo POST FOO",
"prebar": "echo PRE BAR",
"bar": "echo TEMP BAR",
"postbar": "echo POST BAR"
}
```

running `builder concurrent foo bar` would run **all** of the above tasks at
the appropriate lifecycle moment.

##### Builder Flags During Pre and Post

*Applicable Flags*

When executing a `<task>` that has `pre<task>` and/or `post<task>` entries, the
following execution flags **do** apply to the `pre|post` tasks.

* `--env` TODO_TEST
* `--env-path` TODO_TEST
* `--quiet` TODO_TEST
* `--log-level` TODO_TEST

The following flags do _not_ apply to a setup task:

* `--` custom flags TODO_TEST
* `--tries` TODO_TEST
* `--expand-archetype` TODO_TEST/DECIDE ?
* `--setup`: A task specified in `--setup <task>` will not have `pre|post`
tasks apply. TODO_TEST

We will explain a few of these situations in a bit more depth:

*Custom Flags*

The special `--` flag with any subsequent custom flags to the underlying task
are only passed to the the main `<task>` and not `pre<task>` or `post<task>`.
The rationale here is that custom command line flags most likely just apply to
a single shell command (the main one).

So, for example

```js
"scripts": {
"prefoo": "echo PRE",
"foo": "echo TEMP",
"postfoo": "echo POST"
}
```

running `builder run foo -- --hi` would produce:

```
PRE
TEMP --hi
POST
```

*Other Flags*

By contrast, the various other Builder-specific flags that can be applied to a
task like `--tries`, `--env`, etc., **will** apply to `pre|post` tasks, under
the assumption that control flags + environment variables will most likely
want to be used for the execution of all commands in the workflow.

So, for example:

```js
"scripts": {
"prefoo": "echo PRE $VAR",
"foo": "echo TEMP $VAR",
"postfoo": "echo POST $VAR"
}
```

running `builder run foo --env '{"VAR":"HI"}'` would produce:

```
PRE HI
TEMP HI
POST HI
```

##### Task Prefix Complexities

For the above example, if you have a task named `preprefoo`, then running
`foo` **or** even `prefoo` directly will **not** run `preprefoo`. Builder
follows `npm`'s current implementation which is roughly "add `pre|post` tasks
to current execution as long as the task itself is not prefixed with
`pre|post`". (_Note_ that `yarn` does not follow this logic in task execution).

#### Custom Flags

Just like [`npm run <task> [-- <args>...]`](https://docs.npmjs.com/cli/run-script),
Expand Down
4 changes: 2 additions & 2 deletions bin/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var msgs = [];

// Infer if we are global and there is a local version available.
var builderPath = require.resolve("./builder-core");
var localPath = path.resolve(process.cwd(), "node_modules/builder/bin/builder-core.js");
var localPath = path.resolve("node_modules/builder/bin/builder-core.js");

// Swap to local path if different.
if (builderPath !== localPath) {
Expand All @@ -28,7 +28,7 @@ if (builderPath !== localPath) {
});
} catch (err) {
msgs.push({
level: "warn", type: "local-detect",
level: "info", type: "local-detect",
msg: "Error importing local builder: " + err.message
});
msgs.push({
Expand Down
37 changes: 33 additions & 4 deletions lib/args.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
/*eslint max-statements:[2, 20]*/
/*eslint max-statements:[2, 30]*/

/**
* Argv command flags.
Expand Down Expand Up @@ -115,6 +115,13 @@ var FLAGS = {
}
};

// Get all possible flag fields.
var FLAGS_ALL_FIELDS = _.chain(FLAGS) // Use `_.chain` not `_()` because of `reduce`
.values()
.reduce(function (memo, obj) { return memo.concat(_.keys(obj)); }, [])
.uniq()
.value();

// Convert our bespoke flags object into `nopt` options.
var getOpts = function (obj) {
return _.mapValues(obj, function (val) {
Expand Down Expand Up @@ -146,11 +153,13 @@ var version = function () {
};

// Option parser.
var createFn = function (flags) {
var createFn = function (flags, isGeneral) {
var opts = getOpts(flags);
var flagKeys = _.keys(flags);

return function (argv) {
argv = argv || process.argv;
var unparsedArgv = argv;

// Capture any flags after `--` like `npm run <task> -- <args>` does.
// See: https://docs.npmjs.com/cli/run-script#description
Expand All @@ -167,6 +176,9 @@ var createFn = function (flags) {
// Parse.
var parsedOpts = nopt(opts, {}, argv);

// Hack in unparsed for pristine version.
parsedOpts.argv.unparsed = unparsedArgv;

// Stash if log-level was actually set.
var logLevel = parsedOpts["log-level"];

Expand Down Expand Up @@ -196,6 +208,19 @@ var createFn = function (flags) {
parsedOpts["log-level"] = "none";
}

// Validate no invalid or ambiguous flags.

var parsedKeys = _(parsedOpts).keys().without("argv").value();
var extraKeys = _.difference(parsedKeys, flagKeys);
if (isGeneral) {
// If the general command, allow flags from *any* other action.
// We'll eventually hit the right action with tighter enforcement.
extraKeys = _.difference(extraKeys, FLAGS_ALL_FIELDS);
}
if (extraKeys.length) {
throw new Error("Found invalid/conflicting keys: " + extraKeys.join(", "));
}

return _(parsedOpts)
// Camel-case flags.
.mapKeys(function (val, key) { return _.camelCase(key); })
Expand All @@ -209,7 +234,11 @@ module.exports = _.extend({
FLAGS: FLAGS,
help: help,
version: version
}, _.mapValues(FLAGS, function (flags) {
}, _.mapValues(FLAGS, function (flags, key) {
// Merge in general flags to all other configs.
var isGeneral = key === "general";
flags = isGeneral ? flags : _.extend({}, FLAGS.general, flags);

// Add in `KEY()` methods.
return createFn(flags);
return createFn(flags, isGeneral);
}));
2 changes: 1 addition & 1 deletion lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Config.prototype._loadArchetypePkg = function (name) {
*/
Config.prototype._loadPkgs = function (archetypes) {
var self = this;
var CWD_PKG = this._lazyRequire(path.join(process.cwd(), "package.json")) || {};
var CWD_PKG = this._lazyRequire(path.resolve("package.json")) || {};

// Load base packages.
var pkgs = [_.extend({ name: "ROOT" }, CWD_PKG)].concat(_.chain(archetypes)
Expand Down
5 changes: 3 additions & 2 deletions lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ var splitPath = function (val) {
var uniqueVals = function (val, i, vals) { return vals.indexOf(val) === i; };

// Node directories.
var CWD_BIN = path.join(process.cwd(), "node_modules/.bin");
var CWD_NODE_PATH = path.join(process.cwd(), "node_modules");
var CWD_BIN = path.resolve("node_modules/.bin");
var CWD_NODE_PATH = path.resolve("node_modules");

/**
* Environment wrapper.
Expand All @@ -35,6 +35,7 @@ var CWD_NODE_PATH = path.join(process.cwd(), "node_modules");
*
* @param {Object} opts Options object
* @param {Object} opts.config Configuration object
* @param {Object} opts.argv Argv object (Default `process.argv`)
* @param {Object} opts.env Environment object to mutate (Default `process.env`)
* @returns {void}
*/
Expand Down

0 comments on commit 25e24e6

Please sign in to comment.