javascript 从 Github 安装时自动构建 NPM 模块
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48287776/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Automatically Build NPM Module On Install From Github
提问by Undistraction
Given that a project's lib/dir shouldn't be checked into Git because the files it contains are derived files (from the build process). When installing a package from the project's github (during development for example) the lib/dir will not exist, so if the package's package.json's mainfield points to (for example) lib/index.js, the package cannot be compiled when imported because these files do not exist within the repository and therefore within the package installed into node_modules. This means the package needs to built (just as it would be before release), only this time locally so that the libdirectory (or whatever other files are generated during the build process) are added to the module's directory.
鉴于lib/不应将项目的目录签入 Git,因为它包含的文件是派生文件(来自构建过程)。当(在开发为例)安装从项目的github上一包lib/目录将不存在,因此,如果包的package.json的main场点(例如)lib/index.js,包不能当导入,因为这些文件并不存储库中存在编译因此在包内安装到node_modules. 这意味着包需要构建(就像在发布之前一样),只有这一次在本地,以便将lib目录(或在构建过程中生成的任何其他文件)添加到模块的目录中。
Assuming there is a buildscript within the package.jsonfile's scriptsfield, can the package be configured to run this automatically in the situation where it is installed from github only? If not, what is the the best approach to ensuring it is built when installed from github?
假设文件字段中有一个build脚本,在仅从 github 安装的情况下,是否可以将包配置为自动运行?如果没有,确保从 github 安装时构建它的最佳方法是什么?package.jsonscripts
There are now prepublish, prepublishOnlyand preparelifecycle hooks, but none provide an answer to this problem because they don't allow any way to differentiate between the source of the install. In short, yes they allow you to build on install, but they don't allow you to build on install from github only. There is not only no reason to force a build when people install from npm, but more importantly, development dependencies will not be installed (for example babelwhich is critical to the build).
现在有prepublish,prepublishOnly和prepare生命周期钩子,但没有一个提供这个问题的答案,因为它们不允许以任何方式区分安装源。简而言之,是的,它们允许您在安装时构建,但它们不允许您仅从 github 安装时构建。当人们从 npm 安装时,不仅没有理由强制构建,而且更重要的是,不会安装开发依赖项(例如babel,这对构建至关重要)。
I am aware of one strategy to deal with this problem:
我知道处理这个问题的一种策略:
- Fork / branch the repo
- build locally
- remove the
lib/dir from.gitignoreand check it in. - install module from your fork/branch
- When you are ready for a PR / rebase add
lib/dir to.gitignoreand remove dir from git.
- 分叉/分支回购
- 本地构建
lib/从中删除目录.gitignore并签入。- 从你的 fork/branch 安装模块
- 当您准备好进行 PR/rebase 时,将
lib/dir添加到.gitignoregit 并从 git 中删除 dir。
But that is far from ideal. I guess this could be automated with a githook though. So every you push to master the project also builds and pushes to a separate branch.
但这远非理想。不过,我想这可以通过 githook 实现自动化。因此,您为掌握项目而推送的每个内容也会构建并推送到单独的分支。
There is a closed issueon NPM's Github with no resolution - just lots of people who want a solution. From this issue it is clear that using prepareis not the answer.
NPM 的 Github 上有一个没有解决方案的封闭问题——只有很多人想要解决方案。从这个问题很明显,使用prepare不是答案。
My usecase is that I am developing a module that is used in a number of other projects. I want to consume the latest version of the module without having to push out a release to NPM whenever I update the codebase - I would rather push out fewer releases when I am ready, but I still need to consume whatever the latest version of the lib is on Github.
我的用例是我正在开发一个用于许多其他项目的模块。我想使用最新版本的模块,而不必在更新代码库时向 NPM 推送一个版本 - 我宁愿在准备好时推送更少的版本,但我仍然需要使用最新版本的 lib在 Github 上。
Note: I also reached out to NPM's support regarding this issue and I'll add their response if I receive one.
注意:我还就这个问题联系了 NPM 的支持,如果我收到他们的回复,我会添加他们的回复。
回答by Piotr Styczyński
Edit: Detecting if the package is being installed from git repo
编辑:检测是否从 git repo 安装包
I didn't understand the question properly. Below are things that I wrote but are a bit off-topic. For now if you want to run build.js onlywhen installing from repo:
我没有正确理解这个问题。下面是我写的东西,但有点跑题了。现在,如果你想运行build.js只从回购安装时:
Files in repo:
仓库中的文件:
.gitignore
.npmignore
ThisIsNpmPackage
build.js
package.json
The .gitginore:
的.gitginore:
ThisIsNpmPackage
The .npmignore:
的.npmignore:
!ThisIsNpmPackage
In the package.json:
在 package.json 中:
"scripts": {
"install": "( [ ! -f ./ThisIsNpmPackage ] && [ ! -f ./AlreadyInstalled ] && echo \"\" > ./AlreadyInstalled && npm install . && node ./build.js ) || echo \"SKIP: NON GIT SOURCE\""
}
The idea is to make file ThisIsNpmPackageavailable on the repo, but not in npm package.
Install hook it's just a piece of bashy script to check if ThisIsNpmPackageexists. If yes then we execute npm install .(this will ensure we have devDependencies. File AlreadyInstalledis generated to prevent infinite looping
(npm install will recursively invoke install hook)
这个想法是让文件ThisIsNpmPackage在 repo 上可用,但不在 npm 包中。
安装钩子它只是一个 bashy 脚本来检查是否ThisIsNpmPackage存在。如果是,那么我们执行npm install .(这将确保我们有devDependencies. 文件AlreadyInstalled被生成以防止无限循环(npm install 将递归调用安装钩子)
When publishing I do git pushand npm publish
Note that npm publish can be automated via CI tools - githooks
发布当我这样做git push,并npm publish
注意NPM发布可以通过CI工具实现自动化- githooks
This little hack with file ThisIsNpmPackagemakes the source detection available.
这个对文件的小技巧ThisIsNpmPackage使源检测可用。
Results of invoking npm install dumb-package:
调用结果npm install dumb-package:
"SKIP: NON-GIT SOURCE"
“跳过:非 GIT 源”
And executing npm install https://github.com/styczynski/dumb-package
并执行 npm install https://github.com/styczynski/dumb-package
Files will be built
文件将被构建
The issues
问题
The main issues we are facing here are the following ones:
我们在这里面临的主要问题如下:
Have to do
npm publish ...everytime
Sometimes it's too much pain to fix a small bug, then push to the repo and forget to publish on npm. When I was working with a microservices-based project that has about 5 standalone subprojects divided into modules the problem that I found an issue, fixed it and forget to publish in every place I had to was really annoying.Don't want to push
libinto the repo, because it's derived from sourcesRebasing and merging is even more annoying.
No mess with
.gitgnoreHeck, I know that problem when you have a troublesome files that you have to include inside repo but never modify them, or sometimes remove? That's just sick.
npm publish ...每次都必须做
有时候修复一个小错误太痛苦了,然后推送到 repo 并忘记在 npm 上发布。当我使用一个基于微服务的项目时,该项目有大约 5 个独立的子项目分为模块,当我发现一个问题,修复它并忘记在我不得不在每个地方发布的问题时,真的很烦人。不想推
lib入 repo,因为它来自源变基和合并更烦人。
不乱
.gitgnore哎呀,当你有一个麻烦的文件,你必须包含在 repo 中但从不修改它们时,我知道这个问题,或者有时删除?那只是生病了。
Edit: npm hooks
编辑:npm 钩子
As @Roy Tinker mentioned there exist ability for a package to execute a command when installed.
It can be achieved via npm hooks.
正如@Roy Tinker 提到的,包存在安装时执行命令的能力。
它可以通过 npm hooks 实现。
"install": "npm run build"
And we execute the:
我们执行:
npm install https://github.com/<user>/<package>
npm install https://github.com/<user>/<package>
Edit:
OP question from comments:
编辑:
来自评论的 OP 问题:
But this will run an install for everyone downloading the module from npm right? This is hugely problematic given that dev dependencies will not be installed for anyone downloading the module from npm. The libs used to build the app - babel etc will not be installed.
但这将为每个从 npm 下载模块的人运行安装,对吗?鉴于不会为从 npm 下载模块的任何人安装开发依赖项,这是一个很大的问题。用于构建应用程序的库 - babel 等将不会被安装。
Note:But if you want a specific version of the package (production/dev)with or without dev dependencies you can install it via:
注意:但是如果你想要一个特定版本的包(生产/开发)有或没有开发依赖项,你可以通过以下方式安装它:
npm install --only=dev
npm install --only=dev
The --only={prod[uction]|dev[elopment]} argument will cause either only devDependencies or only non-devDependencies to be installed regardless of the NODE_ENV.
--only={prod[uction]|dev[elopment]} 参数将导致只安装 devDependencies 或只安装非 devDependencies,而不管 NODE_ENV。
A better solution, in my opinion, is to use:
在我看来,更好的解决方案是使用:
npm install <git remote url>
And then inside package.json specify:
然后在 package.json 中指定:
"scripts": {
"prepare": "npm run build"
}
If the package being installed contains a prepare script, its dependencies and devDependencies will be installed, and the prepare script will be run, before the package is packaged and installed.
如果正在安装的包包含准备脚本,则在打包和安装包之前,将安装其依赖项和 devDependencies,并运行准备脚本。
Example:
例子:
npm install git+https://[email protected]/npm/npm.git
Read the npm docs there: npm install
在那里阅读 npm 文档: npm install
Edit: proxy module (advanced technique)
编辑:代理模块(高级技术)
It's kind of bad practice, but good to know.
这是一种不好的做法,但很高兴知道。
Sometimes (as in case of Electron frameworkyou need to install other external packages or resources/modules depending on various conditions).
有时(例如在Electron 框架的情况下,您需要根据各种条件安装其他外部包或资源/模块)。
In these cases the proxy idea is used:
在这些情况下,使用代理思想:
- You make a module that behaves like installer and installs all depending things you want
- 您制作一个模块,其行为类似于安装程序并安装您想要的所有相关内容
In your case prepare scriptwill be enough, but I leave this option, because it may be sometimes helpful.
在您的情况下准备脚本就足够了,但我保留此选项,因为它有时可能会有所帮助。
The idea is that you write a module and write a install kook for it:
这个想法是你编写一个模块并为它编写一个安装 kook:
"scripts": {
"install": "<do the install>"
}
In this scenario you can place there:
在这种情况下,您可以在那里放置:
npm install . && npm run build
Which install all devDependencies anyway (as beforementioned prepare case do), but it's a bit of hacking.
无论如何都要安装所有 devDependencies(如前面提到的准备案例所做的那样),但这有点黑客攻击。
If you want do the real hackingthere:
如果你想在那里进行真正的黑客攻击:
"scripts": {
"install": "curl -L -J -O \"<some_url>\""
}
which manually download files using -nix command curl
使用 -nix 命令手动下载文件 curl
It should be avoided but it's an option in case of the module that has huge binary files for each platform and you don't want to install them all.
应该避免这种情况,但如果模块的每个平台都有巨大的二进制文件并且您不想全部安装它们,则可以选择它。
Like in case of Electronwhere you have compiled binaries (each for the separate platform)
就像在Electron 的情况下你编译了二进制文件(每个用于单独的平台)
So you want people to make install packagenot install package-linuxor package-windowetc.
所以你希望人们install package不做install package-linux或package-window等等。
So you provide custom installscript in the package.json
所以你提供自定义install脚本package.json
{
...
"scripts": {
"install": "node ./install_platform_dep.js"
}
}
Then when installing modulethe install_platform_dep.jsscript will be executed. Inside install_platform_dep.jsyou place:
然后安装时module的install_platform_dep.js脚本将被执行。在install_platform_dep.js你的地方:
// For Windows...
if(process.platform === 'win32') {
// Trigger Windows module installation
exec('npm install fancy-module-windows', (err, stdout, stderr) => {
// Some error handling...
}
} else if ... // Process other OS'es
And this in purely manual wayinstalls everything.
这以纯手动方式安装所有内容。
Note:Once again this approach is usable with platform-depending modules and if you use that it's probably design issue with your code.
注意:这种方法再次适用于依赖平台的模块,如果您使用它,则可能是您的代码的设计问题。
Build on CI
以 CI 为基础
What comes to my mind is the solution that I used really for a long time (automatic building with CI services).
我想到的是我真正使用了很长时间的解决方案(使用CI服务自动构建)。
Most of the CI services' main purpose is to test/build/publishyour code when pushing to the branch or doing other actions with the repo.
大多数 CI 服务的主要目的是在推送到分支或对 repo 执行其他操作时测试/构建/发布您的代码。
The idea is that you provide settings file (like travis.ymlor .gitlab-ci.yml) and the tools take care of the rest.
这个想法是您提供设置文件(如travis.yml或.gitlab-ci.yml),其余的由工具处理。
If you reallydon't want to include the lib into the project, just trust CI to do everything:
如果您真的不想将 lib 包含到项目中,只需信任 CI 来完成所有工作:
- Githook will trigger building on commit (on a branch or any other - it's just a matter of configs)
- CI will build your files then pass them to the test phase and publish
- Githook 将在提交时触发构建(在分支或任何其他分支上 - 这只是配置问题)
- CI 将构建您的文件,然后将它们传递到测试阶段并发布
Now i'm working on Gitlabon my own project doing (as a part of hobby) some webpage. The Gitlabconfiguration that builds the project looks like this:
现在我正在我自己的项目上使用Gitlab做一些网页(作为爱好的一部分)。构建项目的Gitlab配置如下所示:
image: tetraweb/php
cache:
untracked: true
paths:
- public_html/
- node_modules/
before_script:
- apt-get update
stages:
- build
- test
- deploy
build_product:
stage: build
script:
- npm run test
build_product:
stage: build
script:
- npm run build
deploy_product:
stage: deploy
script:
- npm run deploy
When I merge into the main branch the following events happen:
当我合并到主分支时,会发生以下事件:
- CI runs
buildstage - If the build succeeds then
teststage is launched - If
testphase is ok then finally the stagedeployis triggered
- CI运行
build阶段 - 如果构建成功,则
test启动阶段 - 如果
test阶段没问题,那么最终阶段会deploy被触发
The script is the list of unix commands to be executed.
该脚本是要执行的 unix 命令列表。
You can specify any Docker image in the config, so use in fact any Unix version you want with some (or not) preinstalled tools.
您可以在配置中指定任何 Docker 映像,因此实际上可以使用您想要的任何 Unix 版本以及一些(或不)预安装的工具。
There is a package deploy-to-gitwhich deploys artefacts to the desired repo branch.
有一个包deploy-to-git可以将人工制品部署到所需的 repo 分支。
Or here (for Travis CI) the piece of config that publishes artefacts to the repo:
或者在这里(对于 Travis CI)将人工制品发布到 repo 的一段配置:
(I used it by myself)
(我自己用的)
Then, of course, you can let CI run:
然后,当然,您可以让 CI 运行:
npm publish .
Because CI executes Unix commands then it can (at least a bunch of CI providers there):
因为 CI 执行 Unix 命令,所以它可以(至少有一堆 CI 提供者):
- Publish tags (release tag maybe?)
- Trigger script to update version of the project in all READMEs and everywhere
- Send you a notification if all phases succeeded
- 发布标签(可能是发布标签?)
- 触发脚本以更新所有自述文件和任何地方的项目版本
- 如果所有阶段都成功,则向您发送通知
So what I do:
I commit, push and let the tools do everything else I want.
In the meantime, I make other changes and after one to ten minutes get update report by mail.
所以我做什么:
我提交、推送并让工具做我想做的一切。
与此同时,我进行了其他更改,并在一到十分钟后通过邮件收到更新报告。
There is plenty of CI provider there:
那里有很多 CI 提供者:
Here I attach another example of my other project (.travis.yml):
在这里,我附上我的另一个项目的另一个示例(.travis.yml):
language: generic
install:
- npm install
script:
- chmod u+x ./utest.sh
- chmod u+x ./self_test/autodetection_cli/someprogram.sh
- cd self_test && bash ../utest.sh --ttools stime --tno-spinner
If you set up CI to push and publish your package you can be always sure to use the latest cutting-edge version of your code without worrying about eh I have to run also this command now...problem.
如果您设置 CI 来推送和发布您的包,您可以始终确保使用最新的尖端版本的代码,而不必担心eh 我现在还必须运行此命令...问题。
I recommend you to choose one of the CI providers out there.
The best ones offer you hundreds of abilities!
我建议您选择其中一个 CI 提供商。
最好的为您提供数百种能力!
When you get used to automatically doing publish, test and build phases you will see how it helps to enjoy the life!
Then to start another project with automatic scripts just copy the configs!
当您习惯于自动执行发布、测试和构建阶段时,您将看到它如何帮助您享受生活!
然后要使用自动脚本启动另一个项目,只需复制配置即可!
Summary
概括
In my opinion npm prepare scriptis an option.
You can also maybe want to try others.
Each of the described methods has it's drawbacks and can be used depending on what you want to achieve.
I just want to provide some alternatives hope some of them will fit your problem!
我认为npm prepare script是一种选择。
您也可以尝试其他人。
所描述的每种方法都有其缺点,可以根据您想要实现的目的使用。
我只是想提供一些替代方案,希望其中一些适合您的问题!
回答by Cameron Tacklind
prepareis the correct way
prepare是正确的方法
If you have a repository with source files but a "build" step is necessary to use it,preparedoes exactly what you want in all cases(as of npm 4).
如果您有一个包含源文件的存储库,但必须执行“构建”步骤才能使用它,prepare那么在所有情况下都完全符合您的要求(从 npm 4 开始)。
prepare: Run both BEFORE the package is packed and published, on localnpm installwithout any arguments, and when installing git dependencies.
prepare:在打包和发布包之前,在npm install没有任何参数的本地运行,以及在安装 git 依赖项时运行。
You can even put your build dependencies into devDependenciesand they will be installed before prepareis executed.
您甚至可以将构建依赖项放入其中devDependencies,它们将在prepare执行之前安装。
Here is an exampleof a package of mine that uses this method.
这是我使用此方法的包的示例。
Problems with .gitignore
问题 .gitignore
There is one issue with this option that gets many people.
When preparing a dependency, Npm and Yarn will keep onlythe files that are listed in the filessection of package.json.
这个选项有一个问题会吸引很多人。当准备依赖性,NPM和纱将保持只有那些在列出的文件files的部分package.json。
One might see that filesdefaults to all files being includedand think they're done.
What is easily missed is that .npmignoremostlyoverrides the filesdirective and, if .npmignoredoes not exist, .gitignoreis used instead.
人们可能会看到files默认包含所有文件并认为它们已完成。什么是容易错过的是.npmignore大多覆盖files指令和,如果.npmignore不存在,.gitignore来代替。
So, if you have your built files listed in .gitignorelike a sane person, and don't do anything else, preparewill seembroken.
所以,如果你有在列出的内置文件.gitignore就像一个理智的人,并没有其他人做任何事情,prepare会似乎打破。
If you fix filesto only include the built files or add an empty .npmignore, you're all set.
如果您修复files为仅包含构建的文件或添加一个空的.npmignore,则一切就绪。
My recommendation is to set files(or, by inversion, .npmignore) such that the only files actually publishedare those needed by users of the published package. Imho, there is no need to include uncompiled sources in published packages.
我的建议是设置files(或通过倒置.npmignore),以便实际发布的唯一文件是已发布包的用户需要的文件。恕我直言,无需在已发布的包中包含未编译的源代码。
回答by Roy Tinker
Assuming there is a
buildscript within the package.json file's scripts field, can the package be configured to run this automatically in this situation?
假设
buildpackage.json 文件的脚本字段中有一个脚本,在这种情况下,可以将包配置为自动运行吗?
Yes. There are 2 things you need to do:
是的。您需要做两件事:
Make sure your system uses
npmoryarnto install the package from GitHub. If this package is a dependency of another package, you can use the GitHub URL in place of the version number inpackage.json. Otherwise, the following command will work:npm install https://github.com/YourUser/your-packageYou can add
/tags/v1.0.0or whatever to the end of the URL if you're after a specific tag or branch.Add the following to the
scriptsin your module'spackage.json:"install": "npm run build"
确保您的系统使用
npm或yarn从 GitHub 安装包。如果此包是另一个包的依赖项,则可以使用 GitHub URL 代替 中的版本号package.json。否则,以下命令将起作用:npm install https://github.com/YourUser/your-package/tags/v1.0.0如果您在特定标签或分支之后,您可以在 URL 末尾添加或添加任何内容。将以下内容添加到
scripts您的模块中package.json:"install": "npm run build"
installis a hook that the package manager executes after installing the module. (preinstalland postinstallalso -- see documentation).
install是包管理器在安装模块后执行的钩子。(preinstall和postinstall也-见文档)。
Documentation: https://docs.npmjs.com/misc/scripts
回答by DS.
EDITED 2
编辑 2
It's a great question. It's too bad there isn't a recognized reliable solution, but the following seems to work.
这是一个很好的问题。太糟糕了,没有公认的可靠解决方案,但以下似乎有效。
Create a .buildmemarker file, and commit to git.
创建一个.buildme标记文件,并提交到 git。
In package.json:
在package.json:
"files": ["lib"],
"scripts": {
"build": "echo DO WHAT YOU NEED TO BUILD",
"prepack": "[ ! -f .buildme ] || npm run build",
"preinstall": "[ ! -f .buildme ] || npm run build"
},
Here are the things to note.
以下是需要注意的事项。
The special
.buildmemarker file should be excluded from the npm package with either the"files"key, or via.npmignore.The
prepackhook runs when you publish (prepublishOnlymight also work, but it's nice that withprepack,npm packwill produce a correct tarball).When installing from npm,
preinstallruns, but does nothing because.buildmeis missing (thanks to the the[ ! -f .buildme ]clause).When installing from github,
.buildmedoes exist. On npm6,prepackhook runs the build (and produces a package without.buildme), andpreinstalldoes nothing. On yarn 1.12,preinstalldoes the build.If you install an updated version from github,
preinstallwill run again, and will build again.
特殊
.buildme标记文件应该被排除在NPM包与任一"files"键,或通过.npmignore。该
prepack钩子在您发布时运行(prepublishOnly可能也可以工作,但使用prepack,npm pack会产生正确的 tarball很好)。从 npm 安装时,会
preinstall运行,但不执行任何操作,因为.buildme缺少(感谢该[ ! -f .buildme ]子句)。从github安装时,
.buildme确实存在。在 npm6 上,prepackhook 运行构建(并生成一个没有 的包.buildme),并且preinstall什么都不做。在纱线 1.12 上,preinstall进行构建。如果您从 github 安装更新版本,
preinstall将再次运行,并将再次构建。
NOTE: When installing from github, it's up to the person installing to have enough of your package's devDependenciesalready installed for the build to work. (This solution doesn't attempt to auto-install devDependencies.)
注意:从 github 安装时,由安装人员决定是否devDependencies已经安装了足够的软件包以使构建工作。(此解决方案不会尝试自动安装devDependencies。)
That's it. It seems to work with varios combinations of npm 6 and yarn 1.12.
而已。它似乎适用于 npm 6 和 yarn 1.12 的各种组合。

