microsoft / rushstack

Monorepo for tools developed by the Rush Stack community
https://rushstack.io/
Other
5.82k stars 592 forks source link

[rush] Independent shrinkwrap files for global commands #939

Open nickpape opened 5 years ago

nickpape commented 5 years ago

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 that rush install was run and that node_modules wasn't corrupted (e.g. put in a bad state by rush 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 invoking install-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.

nickpape commented 5 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:

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.

nickpape commented 5 years ago

@pgonzal FYI

nickpape commented 5 years ago

@daspek This is what we talked about in the CODAC.