Open nickpape opened 6 years ago
Okay, first iteration design proposal..
Like other parts of the Rush ecosystem, the config files (i.e. package.json
and shrinkwrap.yaml
) should be stored under /common/config/rush
. Each package.json
and shrinkwrap.yaml
will be stored under a named folder called command-assets
(? better name?). E.g., the package.json
for a custom tool called "prettifier" would be stored in /common/config/rush/command-assets/prettifier/package.json
.
We then modify the custom commands configuration schema to allow a commandAssetsName
which corresponds with the folder name under /common/config/rush/command-assets/
. Multiple different global commands can reuse the same commandAssetsName
, for example if they were just different flavors of the same command and used the same dependencies.
When the custom command is invoked (e.g. rush prettifier
), we will copy the package.json
(and shrinkwrap) into /common/temp/command-assets/prettifier
and use the same mechanisms that rush install
uses to manage the integrity of the installation.
The shellCommand
field poses some problems for this approach. If the shellCommand
is invoking node
to run a script, that file is unlikely to be stored in the same location where we are installing the assets and therefore unable to reach them. My first thought was to create a symlink to the location in the temp folder where the assets are installed, but this would totally break down if there were multiple global scripts using different "command-assets" in the same folder (e.g. /common/scripts
). It's pretty difficult to explain and awkward to check for this, so I'm proposing that all the scripts (and other resources) for a custom command with fixed assets also be checked into /common/config/command-assets/prettifier
. These would be copied to the /common/temp/command-assets/prettifier
folder as well.
When we invoke the shellCommand
, we would invoke it using that folder as the current working directory. A few examples:
"shellCommand": "node run-prettifier.js"
runs the script /common/config/command-assets/prettifier/run-prettifier.js
"shellCommand": "./node_modules/.bin/prettifier"
might end up running /common/config/command-assets/prettifier/node_modules/.bin/prettifier.cmd
The major downside to this approach is that the CWD will be the root of the monorepo when not using command assets, and will be an obscure folder under the temp directory when you are using them.
Lastly, people will sometimes want to upgrade the shrinkwrap file. It is a bit odd to make them do this manually, so we will add a flag for rush update
that allows someone to specify a command assets shrinkwrap that needs to be updated, e.g. rush update --command-assets prettifier
. This will have the same semantics as a normal update.
@pgonzal FYI
@daspek This is what we talked about in the CODAC.
We've had an interesting case recently where we need to run a script as part of a Git commit hook (see Installing Git Hooks).
Most of the times these scripts are simple and don't have any dependencies, but sometimes they do have some dependencies. You could add these dependencies to the
common-versions.json
file, but then you have to be sure thatrush install
was run and thatnode_modules
wasn't corrupted (e.g. put in a bad state byrush update
). Also, you may not wish to download all these dependencies during a regular build.Another option is to publish a full NPM package and then use
install-run.js
to install and run the file. However, this also has a couple downsides, such as that the version you are using is stored in whatever is invokinginstall-run.js
and this approach lacks determinism as there is no shrinkwrap file checked into git. Another downside is you have to publish and maintain a full NPM package for a script that could be really small. [Although we recommend this approach generally.]Pete and I discussed and came up with an idea to associate a package.json and shrinkwrap file with a particular custom global command. Rush would ensure that the installation was done properly whenever invoking the command.