自动从Github安装NPM包时构建模块。

20 浏览
0 Comments

自动从Github安装NPM包时构建模块。

考虑到在构建过程中,项目的lib/目录中包含的文件是派生文件,因此不应将其检入Git。在安装项目的github包(例如在开发过程中)时,lib/目录将不存在,因此如果包的package.json文件的main字段指向(例如)lib/index.js,当导入时,该包无法编译,因为这些文件在代码库和因此安装到node_modules的包中不存在。这意味着需要构建该包(就像发布之前一样),只不过这次是本地构建,以便将lib目录(或在构建过程中生成的其他文件)添加到模块的目录中。

假设在package.json文件的scripts字段中有一个build脚本,能否在仅从github安装时自动配置包以运行此脚本?如果不能,确保从github安装时该包构建的最佳方法是什么?

现在有prepublishprepublishOnlyprepare生命周期钩子,但它们都没有解决此问题,因为它们不允许区分安装来源。简而言之,是的,它们允许您在安装时构建,但它们不允许您仅从github安装时构建。不仅没有理由强制在人们从npm安装时构建,更重要的是,开发依赖项将不会被安装(例如对于构建很关键的babel)。

我知道有一种处理此问题的策略:

  • 派生 / 分支 repo
  • 在本地构建
  • .gitignore 中移除 lib/ 目录,并将其检入。
  • 从您的 fork / branch 安装模块
  • 当您准备好 PR / rebase 时,将 lib/ 目录添加到 .gitignore 中并从 git 中删除目录。

但那远非理想。我想这可以使用 githook 自动化处理。因此每次推送到主分支时,项目也会构建并推送到一个单独的分支。

NPM 的 Github 上有一个已关闭的问题,没有解决方案,只有许多希望得到解决的人。从这个问题可以看出,使用 prepare 并不是答案。

我的使用案例是,我正在开发一个在许多其他项目中使用的模块。我想消耗最新版本的模块,而不必在更新代码库时每次推出一个新版本到 NPM - 我宁愿在准备好时推出较少的版本,但我仍然需要消耗 Github 上最新版本的 lib。

注意:我也向 NPM 支持团队反映了这个问题,如果得到回复,我将添加他们的回答。

admin 更改状态以发布 2023年5月24日
0
0 Comments

prepare是正确的方式,但可能看起来有问题

如果您有一个包含源文件的仓库,但需要“构建”步骤才能使用它,
prepare在所有情况下都能做到您想要的(截至npm 4版本)。

prepare:在本地npm install没有任何参数的情况下,在打包和发布程序包之前以及安装Git依赖项时运行。

您甚至可以将构建依赖项放入devDependencies中,它们将在执行prepare之前安装。

这里是我一个使用这种方法的包的示例


.gitignore的问题 - prepare似乎有问题

有一个问题让许多人感到头疼。

当准备一个依赖关系时,Npm和Yarn将仅保留package.jsonfiles部分中列出的文件。

有人可能会看到files默认包含所有文件,并认为自己已经完成了。
容易忽略的是:

  • .npmignore大多数情况下会覆盖files指令并且,
  • 如果.npmignore不存在,则使用.gitignore

因此,如果您在.gitignore中列出已构建的文件,就像一个理智的人所做的那样,并且没有做任何其他事情,prepare将看起来有问题

如果您修复files以仅包括已构建的文件,或者添加一个空的.npmignore,您已准备就绪。

我建议设置files(或倒置地设置.npmignore),以使只有发布包的用户需要的文件实际上被发布。在我看来,没有必要在发布包中包含未编译的源文件。

0
0 Comments

编辑:检测包是否正在从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 pushnpm 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-linuxinstall 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准备阶段脚本是一个选项。
你也可以尝试其他方法。

每种描述的方法都有其缺点,并且可以根据你想要实现的目标来使用。

我只想提供一些替代方案,希望其中一些能够解决你的问题!

0