自动从Github安装NPM包时构建模块。
自动从Github安装NPM包时构建模块。
考虑到在构建过程中,项目的lib/
目录中包含的文件是派生文件,因此不应将其检入Git。在安装项目的github包(例如在开发过程中)时,lib/
目录将不存在,因此如果包的package.json
文件的main
字段指向(例如)lib/index.js
,当导入时,该包无法编译,因为这些文件在代码库和因此安装到node_modules
的包中不存在。这意味着需要构建该包(就像发布之前一样),只不过这次是本地构建,以便将lib
目录(或在构建过程中生成的其他文件)添加到模块的目录中。
假设在package.json
文件的scripts
字段中有一个build
脚本,能否在仅从github安装时自动配置包以运行此脚本?如果不能,确保从github安装时该包构建的最佳方法是什么?
现在有prepublish
、prepublishOnly
和prepare
生命周期钩子,但它们都没有解决此问题,因为它们不允许区分安装来源。简而言之,是的,它们允许您在安装时构建,但它们不允许您仅从github安装时构建。不仅没有理由强制在人们从npm安装时构建,更重要的是,开发依赖项将不会被安装(例如对于构建很关键的babel
)。
我知道有一种处理此问题的策略:
- 派生 / 分支 repo
- 在本地构建
- 从
.gitignore
中移除lib/
目录,并将其检入。 - 从您的 fork / branch 安装模块
- 当您准备好 PR / rebase 时,将
lib/
目录添加到.gitignore
中并从 git 中删除目录。
但那远非理想。我想这可以使用 githook 自动化处理。因此每次推送到主分支时,项目也会构建并推送到一个单独的分支。
NPM 的 Github 上有一个已关闭的问题,没有解决方案,只有许多希望得到解决的人。从这个问题可以看出,使用 prepare
并不是答案。
我的使用案例是,我正在开发一个在许多其他项目中使用的模块。我想消耗最新版本的模块,而不必在更新代码库时每次推出一个新版本到 NPM - 我宁愿在准备好时推出较少的版本,但我仍然需要消耗 Github 上最新版本的 lib。
注意:我也向 NPM 支持团队反映了这个问题,如果得到回复,我将添加他们的回答。
prepare
是正确的方式,但可能看起来有问题
如果您有一个包含源文件的仓库,但需要“构建”步骤才能使用它,
prepare
在所有情况下都能做到您想要的(截至npm 4版本)。
prepare
:在本地npm install
没有任何参数的情况下,在打包和发布程序包之前以及安装Git依赖项时运行。
您甚至可以将构建依赖项放入devDependencies
中,它们将在执行prepare
之前安装。
这里是我一个使用这种方法的包的示例。
.gitignore
的问题 - prepare
似乎有问题
有一个问题让许多人感到头疼。
当准备一个依赖关系时,Npm和Yarn将仅保留package.json
的files
部分中列出的文件。
有人可能会看到files
默认包含所有文件,并认为自己已经完成了。
容易忽略的是:
.npmignore
大多数情况下会覆盖files
指令并且,- 如果
.npmignore
不存在,则使用.gitignore
。
因此,如果您在.gitignore
中列出已构建的文件,就像一个理智的人所做的那样,并且没有做任何其他事情,prepare
将看起来有问题。
如果您修复files
以仅包括已构建的文件,或者添加一个空的.npmignore
,您已准备就绪。
我建议设置files
(或倒置地设置.npmignore
),以使只有发布包的用户需要的文件实际上被发布。在我看来,没有必要在发布包中包含未编译的源文件。
编辑:检测包是否正在从Git仓库安装
我没有正确理解问题。
以下是我写的内容,但有些离题。
现在,如果你想要在从仓库安装时仅运行build.js:
仓库中的文件:
.gitignore .npmignore ThisIsNpmPackage build.js package.json
代码库中的.gitignore
:
ThisIsNpmPackage
.npmignore
中的:
!ThisIsNpmPackage
在package.json中:
"scripts": { "install": "( [ ! -f ./ThisIsNpmPackage ] && [ ! -f ./AlreadyInstalled ] && echo \"\" > ./AlreadyInstalled && npm install . && node ./build.js ) || echo \"SKIP: NON GIT SOURCE\"" }
这个想法是使文件ThisIsNpmPackage
在仓库中可用,但不在npm包中。
安装挂钩只是一个检查ThisIsNpmPackage
是否存在的一段Bash脚本。如果是,则执行npm install .
(这将确保我们有devDependencies
)。生成文件AlreadyInstalled
以防止无限循环(npm install将递归调用install hook)
发布时我执行git push
和npm publish
请注意,可以通过CI工具-githooks自动化npm publish
这个小技巧使用文件ThisIsNpmPackage
使源检测可用。
调用npm install dumb-package
的结果:
“SKIP: NON-GIT SOURCE”
和执行npm install https://github.com/styczynski/dumb-package
文件将被构建
问题
我们在这里面面临的主要问题是:
-
每次都要执行
npm publish ...
有时修复一个小错误然后将其推送到仓库并忘记在npm上发布是一件非常痛苦的事情。当我使用基于微服务的项目并将其分成大约5个独立的子项目时,将问题修正并忘记在每个地方发布时会非常恼人。
-
不想将
lib
推入仓库,因为它是从源代码派生的 -
重新转基和合并更加令人讨厌。
-
不要带着.gitignore的混乱
我知道当你有一些麻烦的文件需要包含在存储库中但从不修改它们,或者有时删除它们时的问题?这很糟糕。
编辑:npm Hook
如@Roy Tinker所提到的,存在一种能使包在安装时执行命令的方法。
这可以通过npm挂钩实现。
"install": "npm run build"
并且我们执行:
npm install https://github.com/
编辑:
来自评论的OP问题:
但这将为从npm下载模块的所有人运行安装吧?这是非常棘手的,因为开发依赖项不会为从npm下载模块的任何人安装。用于构建应用程序的库 - babel等将不会安装。
注意:但是,如果你想安装特定版本的包(生产/开发),并且带有或不带有开发依赖项,你可以通过以下方式安装:
npm install --only=dev
--only={prod[uction]|dev[elopment]}参数将导致只安装devDependencies或只安装非devDependencies,不管NODE_ENV设置如何。
在我看来,更好的解决方案是使用:
npm install
然后在package.json内指定:
"scripts": { "prepare": "npm run build" }
如果安装的包包含prepare脚本,则会先安装其依赖项和devDependencies,然后运行prepare脚本,再将该包打包并安装。
示例:
npm install git+https://isaacs@github.com/npm/npm.git
阅读npm文档:npm install
编辑:代理模块(高级技巧)
这是一种不好的做法,但是很好知道。
有时候(例如在Electron框架中),您需要根据各种条件安装其他外部包或资源/模块)。
在这些情况下,使用代理思想:
- 您可以制作一个类似于安装程序并安装所有所需的东西的模块
在您的情况下,prepare脚本就足够了,但是我保留了这个选项,因为它有时可能很有帮助。
这个想法是你编写一个模块并为其编写一个安装脚本:
"scripts": { "install": "" }
在这种情况下,您可以放置:
npm install . && npm run build
无论如何都会安装所有的devDependencies(就像前面提到的prepare案例一样),但这是一点点的黑客。
如果您想要真正的黑客:
"scripts": { "install": "curl -L -J -O \"\"" }
使用-nix命令
curl
手动下载文件尽量避免这样做,但在模块具有每个平台的巨大二进制文件的情况下,这是一种选择,并且您不想全部安装它们。
例如,在Electron的情况下,您拥有已编译的二进制文件(每个单独的平台)
因此,您希望人们执行
install package
而不是install package-linux
或install package-window
等。因此,您在
package.json
中提供自定义install
脚本{ ... "scripts": { "install": "node ./install_platform_dep.js" } }
然后,在安装
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
这样可以完全手动安装所有内容。
注意:再次声明,这种方法只适用于依赖于平台的模块,如果您使用它,则可能是代码设计问题。
在CI上构建
我想到的解决方案是我曾经长期使用的解决方案(在CI服务上自动构建)。
大多数CI服务的主要目的是在推送到分支或对存储库进行其他操作时测试/构建/发布您的代码。
思路是提供设置文件(如.travis.yml或.gitlab-ci.yml),然后工具会处理其余的事情。
如果您真的不想将库包含到项目中,请相信CI来完成一切:
Githook将在提交时触发构建(在分支或其他分支上-只是配置的问题)
CI会构建您的文件,然后将它们传递到测试阶段并发布
现在,我正在自己的项目上使用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
当我合并到主要分支时,将发生以下事件:
CI运行构建阶段
如果构建成功,则启动测试阶段
如果测试阶段正常,则最终触发部署阶段
脚本是要执行的Unix命令列表。
您可以在配置中指定任何Docker映像,因此实际上可以使用任何Unix版,带有一些(或不带)预安装的工具。
有一个包(deploy-to-git),可以将构件部署到所需的存储库分支。
或者在这里(对于Travis CI),将发布构件到存储库的配置片段:
travis-publish-to-git
(我自己使用过它)
然后,当然,您可以让CI运行:
npm publish .
由于CI执行Unix命令,因此它可以(至少有大量CI提供程序):
发布标记(发布标记吗?)
触发脚本以在所有自述文件和其他地方中更新项目版本
如果所有阶段都成功,则向您发送通知
所以我做的事情是:
我提交,推送,然后让工具进行我想要的其他所有操作。
与此同时,我进行其他更改,然后在一到十分钟后通过邮件得到更新报告。
有很多CI提供程序:
Travis CI
Circle CI
Gitlab项目的Gitlab CI
在这里,我附加了我的另一个项目的另一个示例(.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
如果您设置了CI来推送和发布软件包,则始终可以放心使用最新的代码前沿版本,而不必担心“我现在还必须运行此命令......”问题。
我建议您选择其中一个CI提供程序。
最好的提供程序为您提供了数百种功能!
当你习惯了自动进行发布、测试和构建阶段,你会发现它有助于享受生活!
然后要开始另一个带有自动脚本的项目,只需复制配置文件!摘要
在我看来,npm准备阶段脚本是一个选项。
你也可以尝试其他方法。每种描述的方法都有其缺点,并且可以根据你想要实现的目标来使用。
我只想提供一些替代方案,希望其中一些能够解决你的问题!