Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download binary on npm postinstall hook #43

Closed
wants to merge 6 commits into from
Closed

Download binary on npm postinstall hook #43

wants to merge 6 commits into from

Conversation

fleischie
Copy link

As was discussed in issue #16 the lefthook npm package contains all the binaries, this postinstall hook downloads only the appropriate binary for the host OS (or in case of CI: none).

Note: I submit my PR in accordance to #16 (comment).
Note 2: I did not run any go/ruby command as this PR mainly changes the postinstall script in the .npm directory. If this is still necessary I will update my PR, just let me know what is missing. ✌️

@Arkweid
Copy link
Collaborator

Arkweid commented Aug 2, 2019

/cc @HellSquirrel
#16

@HellSquirrel
Copy link
Contributor

Thanks! I will test it in a few days

Copy link
Contributor

@HellSquirrel HellSquirrel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fleischie
I tested this solution and found some problems. See comments below

.npm/postinstall.js Outdated Show resolved Hide resolved

function downloadBinary(os, callback) {
// construct single binary file path
const binaryPath = join(process.cwd(), 'lefthook');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.cwd() is our current working directory. Consider some examples
npm i @arkweid/lefhook -- it will upload the binary to the project root
cd src && npm i @arkweid/lefhook -- it will upload the binary to 'src'
We want the binary to be uploaded to ./node_modules/@arkweid/lefhook/...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, whoops. I guess that was just the leftover binaryPath from my local tests. Will update immediately.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process.cwd() should also give us the base path of the project, as the main function calls process.chdir(process.env.INIT_CWD).

But if you want I can rewrite this, so that both the downloadBinary function takes process.env.INIT_CWD into consideration for the path, as well as call process.chdir(process.env.INIT_CWD) inside installGitHooks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two problems:

  1. process.env.INIT_CWD refers to the current folder. (It could be a subfolder of the root folder.) So we need to look for node_modules recursively
  2. Some older versions of yarn don't set this variable at all

Copy link

@levenkov levenkov Aug 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you shouldn't search the folder node_modules at all (e.g., name of "node_modules" folder can be changed). There is simple and steady solution: https://github.com/levenkov/lefthook/pull/1/files#diff-5fe0be6c71fdcee66beee780a898073fR167

  1. Download it in preinstall hook.
  2. preinstall hook executed in the package directory.
  3. It's always true, that if you placed downloaded binary in ./bin in the preinstall hook, this binary after install stage will be in the right place (usually here: <project dir>/node_modules/@arkweid/lefthook/bin/...).

I almost done the work, but was overwhelmed by the deadline :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under the impression that the INIT_CWD is set to the base path of the newly installed package (see https://github.com/yarnpkg/yarn/blob/cfb560e1b2e37ed5cfe5d2374a806ac3ae512c11/src/util/execute-lifecycle-script.js#L40 similar for npm), hence INIT_CWD for lefthook would always be <project-dir>/node_modules/@Arkweid/lefthook.

(Also the change was (according to GitHub) over a year ago, so I would assume most users have a current enough yarn/npm installation)

So tl;dr: As long as we don't process.chdir or cd <somewhere> && ... in the postinstall script, we should be able to assume we are in <project-dir>/node_modules/@Arkweid/lefthook, no?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also the change was (according to GitHub) over a year ago, so I would assume most users have a current enough yarn/npm installation)

It's better to support as earlier as you can version of node. It's common place to support >=6.0.

INIT_CWD is working after 8.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@levenkov @HellSquirrel

I honestly don't know how to fix this INIT_CWD issue. I tried to use the ./dir approach (i.e. const path = process.env.INIT_CWD || path.resolve(".");), but in my locally installation tests, this always resolved to the path the script resides in (i.e. <path>/lefthook/.npm, instead of <path>/tmp where I called npm install ../lefthook/.npm from).

Maybe you can review the latest PR version and give me some pointers.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fleischie I'll see that in 1-2 days. Maybe I'll try to change the approach in your code to mine and suggest small patch. I don't claim to replace all your work, just this problem.

@HellSquirrel
Copy link
Contributor

HellSquirrel commented Aug 11, 2019

Here how you can load unzip and execute lefthook binary on mac

const https = require("https");
const fs = require("fs");
const zlib = require("zlib");

const url =
  "https://github.com/Arkweid/lefthook/releases/download/v0.6.3/lefthook_0.6.3_MacOS_x86_64.gz";

const fileName = "lefthook-local";

https
  .get(url, res => {
    if (res.statusCode === 302) {
      const originalUrl = res.headers.location;
      console.log("redirect, redirect");

      https
        .get(originalUrl, res => {
          if (res.statusCode === 200) {
            res
              .pipe(zlib.createGunzip())
              .pipe(fs.createWriteStream(fileName))
              .on("finish", res =>
                fs.chmod(fileName, 755, err => {
                  if (err) {
                    console.log("error changing rights");
                  }

                  console.log("done");
                })
              );
          }
        })
        .on("error", e => {
          console.log("error after redirect!", e);
        });
    }
  })
  .on("error", e => {
    console.error(e);
  });

@HellSquirrel
Copy link
Contributor

For Windows we should change the url variable to https://github.com/Arkweid/lefthook/releases/download/v0.6.3/lefthook_0.6.3_Windows_x86_64.gz

@fleischie
Copy link
Author

(I will have time (maybe today but definitely) on thursday to continue working on this PR. Sorry for the inconvenience. 🙇 )

@ai
Copy link
Member

ai commented Sep 26, 2019

Is it ready?

@fleischie
Copy link
Author

I finished with the PR for now (as in "I tried to implement the feature while considering all raised concerns").

I think someone should (or maybe also wanted to) test the current state, as I do not know how to test the postinstallation hook from an actual npm install/yarn add. Let me know, if the PR still needs work to be done.

@HellSquirrel
Copy link
Contributor

I'm still going to test it :) I can get here in a week or two

@ai
Copy link
Member

ai commented Sep 26, 2019

There is a simple way to test postinstall

  1. npm pack to get tgz file
  2. Use link to this tar file in demo project npm install ../lefthook/lefthook.tgz

@yumauri
Copy link

yumauri commented Oct 23, 2019

Still there? :)

@fleischie
Copy link
Author

Oh hey, yes. I actually overlooked the last message from Andrey, so I will check today/tomorrow on some systems whether it works ok, and then report back. ✌️

@fleischie
Copy link
Author

fleischie commented Oct 28, 2019

I did now test my PR on mac and Windows 7 VM:
The downloading and calling of lefthook install -f seems to work alright. Unfortunately as my VM image is 32-bit, I couldn't verify that the complete process works (edit 2: on Windows that is).

It does for mac (if I change the index.js appropriately).

Edit: I will hence update the PR, and need to test on my Windows machine at home (if I find the time that is).

@fleischie
Copy link
Author

I also rebased the PR on the latest master branch, removed the [WIP] from the commit-in-question, and some further improvements to the postinstall script:

  • only allow 64bit downloads on windows (there are no 32bit executables in the releases tab),
  • remove the pre-build executables from the binary folder, and
  • consolidate the naming of the executable (rename the downloaded executable lefthook/lefthook.exe for MacOS/Linux resp. Windows), and call them by name/platform from index.js.

@yumauri
Copy link

yumauri commented Oct 28, 2019

I'm not sure, but is it possible to point package.json's bin.lefthook property right into binary, instead of index.js, to eliminate node spawning for simply choose right binary?

UPD: actually, that is what actually mentioned in #16 as second point

@Arkweid Arkweid mentioned this pull request Nov 10, 2019
Try to fetch the binary files on postinstall for each supoorted OS and
if not CI. This should reduce the npm package size (as you only ever
download one binary for your host system).
- Add function to dynamically return path of `lefthook` binary depending
  on `os`, package `version`, and architecture,
- add extra redirection-download step for GitHub releases (as they are
  served from AWS, which is dynamically redirected on request), and
- add more event-based file-handling (e.g. unlinking in 'close'-handler,
  calling the callback on 'end'-event, etc.).
If `process.env.INIT_CWD` is not set fall back to `resolve(".")` to try
to emulate the behavior.
Exit early and notify user of issue, because currently there is only a
windows 64bit version of lefthook.
- Remove pre-build binaries in `bin/` folder,
- rename downloaded executable to `lefthook` (or `lefthook.exe` on
  windows), and
- call the appropriate executable by name (and platform) in `index.js`.
@fleischie
Copy link
Author

Updated the PR again to fit snugly on the latest master.

@yumauri I am uncertain whether package.json#bin.lefthook can be the lefthook binary directly, as firstly the docs on that field give the impression that it must be a node script, and secondly when i attempted to do it npm (at least) complained about a missing script, although the binary existed. 🤷‍♂

@fleischie fleischie mentioned this pull request Nov 11, 2019
@fleischie
Copy link
Author

Amendum: This PR works on the assumption, that binaries are always available for the most recent version as a gzipped package. That's why there is a commit aborting on 32bit Windows.

I understand that this is an added burden on the maintainer, as there are more and more diverse packages to be produced. The other solution would be to handle the zips as well, but require an additional (potentially heavy) node zip-library, which I tried to avoid until now.

Let me know what you think.

@yumauri
Copy link

yumauri commented Nov 11, 2019

Github actions are publicly available ;) Theoretically it is possible to make an action to build and upload binaries somewhere on every push

@ai
Copy link
Member

ai commented Jan 15, 2020

@yumauri I am uncertain whether package.json#bin.lefthook can be the lefthook binary directly, as firstly the docs on that field give the impression that it must be a node script, and secondly when i attempted to do it npm (at least) complained about a missing script, although the binary existed. man_shrugging

You can test npm features by installing npm package from local file npm i test@../test.

Github actions are publicly available ;) Theoretically it is possible to make an action to build and upload binaries somewhere on every push

Yeap, .gz files is not a problem. We will find a solution for them.

@ringods
Copy link

ringods commented Feb 26, 2020

Please, please, please do NOT merge this. Not everybody is working in an environment where all machines have direct internet connectivity!

I have customers in highly regulated environments where their working machines are isolated from the internet. Development is done with an internal package repository, acting as a proxy for upstream repositories. All package downloads come from this proxy.

The current situation, with the binaries included in the lefthook package, allow these customers to work with lefthook correctly. If you change it to downloading during the postinstall, their setups will break and they have no way to work around it as you hardcode the download URL.

Making the URL configurable also pushes a lot of chore onto the users as they have to copy the binaries, put them on an internal site and communicate the link. Quite error prone and not usage friendly.

To wrap up: can you please leave it as it is?

@ai
Copy link
Member

ai commented Feb 26, 2020

Yeap, I changed my mind. Let's close it.

@aminya
Copy link
Contributor

aminya commented May 10, 2021

Please, please, please do NOT merge this. Not everybody is working in an environment where all machines have direct internet connectivity!

I have customers in highly regulated environments where their working machines are isolated from the internet. Development is done with an internal package repository, acting as a proxy for upstream repositories. All package downloads come from this proxy.

The current situation, with the binaries included in the lefthook package, allow these customers to work with lefthook correctly. If you change it to downloading during the postinstall, their setups will break and they have no way to work around it as you hardcode the download URL.

Making the URL configurable also pushes a lot of chore onto the users as they have to copy the binaries, put them on an internal site and communicate the link. Quite error prone and not usage friendly.

To wrap up: can you please leave it as it is?

Well, not everyone has high-speed internet to download all the binaries at once!

Your workflow seems specialized enough (running npm locally) that you need to manage the post-install issue yourself instead of preventing people from benefiting from this improvement. You can simply fork this package and create one package that has all the binaries included.

Because of the huge size of the package, I will not use lefthook in any of my public repositories that rely on the open-source contribution of people from around the world that have different connection speeds.

Looking at the number of dependants of lefthook and comparing it with Husky, shows the same thing. Only 2 packages depend on lefthook while 2000 depend on husky!

https://www.npmjs.com/package/@arkweid/lefthook
https://www.npmjs.com/package/husky

@privatenumber
Copy link

privatenumber commented Jun 14, 2021

Seems @ringods's concern was neglected in #188:

I have customers in highly regulated environments where their working machines are isolated from the internet. Development is done with an internal package repository, acting as a proxy for upstream repositories. All package downloads come from this proxy.

I think this issue is more relevant than it appears. Many environments use a npm proxy in case npm goes down or becomes inaccessible (eg. CI deploys, China, etc). In the case where GitHub becomes unavailable, (which happens more than it should...), this will break npm install.

If the npm registry is proxied, I think this can be easily addressed by publishing the binaries to npm instead of using hosting them via GitHub release.

For example, playwright does this with their browser flavors (eg. playwright-chromium).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants