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

时间:2018-01-16 18:37:08

标签: javascript github npm build package

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

假设lib文件的build字段中有package.json脚本,是否可以将程序包配置为在其所在的情况下自动运行仅从github安装?如果没有,从github安装时确保构建的最佳方法是什么?

现在有scriptsprepublishprepublishOnly生命周期挂钩,但没有人提供这个问题的答案,因为他们不允许任何方式区分来源安装。简而言之,是的,它们允许您构建安装,但它们不允许您仅从github构建安装。当人们从npm安装时,不仅没有理由强制构建,更重要的是,不会安装开发依赖项(例如prepare,这对构建至关重要。)

我知道有一个策略可以解决这个问题:

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

但这远非理想。我想这可以通过githook实现自动化。所以每一个你掌握项目的人都会建立并推动到一个单独的分支。

NPM的Github上有closed issue没有解决方案 - 只有很多人想要解决方案。从这个问题可以清楚地看出,使用.gitignore不是答案。

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

注意:我也就此问题与NPM的支持联系,如果我收到回复,我会添加他们的回复。

4 个答案:

答案 0 :(得分:15)

编辑:检测是否正在从git repo

安装软件包

我没有正确理解这个问题。 以下是我写的东西,但有点偏离主题。 目前,如果您想从repo安装时仅运行build.js

回购文件:

 .gitignore
 .npmignore
 ThisIsNpmPackage
 build.js
 package.json

.gitginore

ThisIsNpmPackage

.npmignore

!ThisIsNpmPackage

在package.json中:

"scripts": {
    "install": "( [ ! -f ./ThisIsNpmPackage ] && [ ! -f ./AlreadyInstalled ] && echo \"\" > ./AlreadyInstalled && npm install . && node ./build.js ) || echo \"SKIP: NON GIT SOURCE\""
}

我们的想法是在repo上提供文件ThisIsNpmPackage,而不是在npm包中。(
安装钩子它只是一个bashy脚本来检查是否存在ThisIsNpmPackage。如果是,那么我们执行npm install .(这将确保我们devDependencies。生成文件AlreadyInstalled以防止无限循环 (npm install将以递归方式调用install hook)

发布时,我会git pushnpm publish 请注意,npm发布可以通过CI工具自动生成 - githooks

文件ThisIsNpmPackage的这个小问题使得源检测可用。

调用npm install dumb-package

的结果
  

"跳过:非GIT来源"

执行npm install https://github.com/styczynski/dumb-package

  

将构建文件

问题

我们在这里面临的主要问题是:

  • 每次都必须npm publish ...

    有时候修复一个小bug会有太大的痛苦,然后推送到repo并忘记在npm上发布。当我使用基于微服务的项目时,大约有5个独立子项目被分成模块,我发现问题,修复它并忘记在我所遇到的每个地方发布都非常烦人。

  • 不想将lib推入回购广告,因为它来源于

  • 重新定位和合并更令人讨厌。

  • .gitgnore

    没有混乱

    哎呀,我知道这个问题,当你有一个你必须包含在repo中但却永远不会修改它们,或者有时候会删除的一些文件?那只是病了。

编辑:npm hooks

正如@Roy Tinker所说,安装时存在执行命令的能力 它可以通过npm钩子实现。

"install": "npm run build"

我们执行:

npm install https://github.com/<user>/<package>

修改
来自评论的OP问题:

  

但是对于每个从npm下载模块的人来说这都会安装吗?这是非常有问题的,因为从npm下载模块的任何人都不会安装dev依赖项。用于构建应用程序的库 - babel等将不会安装。

注意:如果您需要特定版本的软件包(生产/开发),有或没有开发依赖项,您可以通过以下方式安装:

npm install --only=dev

  

--only = {prod [uction] | dev [elopment]}参数将导致只安装devDependencies或者只安装非devDependencies,无论NODE_ENV如何。

我认为更好的解决方案是使用:

npm install <git remote url>

然后在package.json中指定:

"scripts": {
    "prepare": "npm run build"
}
  

如果正在安装的软件包包含一个prepare脚本,那么将安装其依赖项和devDependencies,并在打包和安装软件包之前运行prepare脚本。

示例:

npm install git+https://isaacs@github.com/npm/npm.git

在那里阅读npm文档:npm install

编辑:代理模块(高级技术)

这是一种不好的做法,但很高兴知道。

有时(如 Electron framework 的情况,您需要根据不同的条件安装其他外部软件包或资源/模块)。

在这种情况下,使用代理的想法:

  • 您创建一个行为类似于安装程序的模块,并根据您的需要安装所有依赖
  • 的模块

在您的情况下准备脚本就足够了,但我保留此选项,因为它有时可能会有所帮助。

我的想法是你编写一个模块并为它编写一个 install kook

"scripts": {
    "install": "<do the install>"
}

在这种情况下,你可以放在那里:

npm install . && npm run build

无论如何都安装所有devDependencies(如前所述准备案例),但它有点黑客攻击。

如果你想在那里做真正的黑客攻击

 "scripts": {
    "install": "curl -L -J -O \"<some_url>\""
 }

使用-nix命令curl

手动下载文件

应该避免这种情况,但如果模块的每个平台都有巨大的二进制文件,并且您不想全部安装它们,那么它是一个选项。

就像 Electron 的情况一样,你已经编译了二进制文件(每个用于单独的平台)

因此,您希望人们install package而不是install package-linuxpackage-window等。

因此,您在install

中提供自定义package.json脚本
{
  ...
  "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服务&#39;主要目的是在推送到分支机构或使用repo执行其他操作时测试/构建/发布您的代码。

我们的想法是你提供设置文件(比如 travis.yml .gitlab-ci.yml ),这些工具会处理剩下的工作。

如果确实不希望将lib包含到项目中,只需信任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运行build阶段
  • 如果构建成功,则启动test阶段
  • 如果test阶段正常,则最终触发阶段deploy

脚本是要执行的unix命令列表。

您可以在配置中指定任何Docker镜像,因此实际上使用您想要的任何unix版本与一些(或不是)预安装的工具。

添加命令将构建推送到另一个分支是没有问题的。

有一个包deploy-to-git,可以将人工制品部署到所需的仓库分支。

或者在这里(对于Travis CI)将工件发布到repo的配置:

travis-publish-to-git

(我自己用它)

然后,当然,你可以让CI运行:

npm publish .

因为CI执行unix命令,所以它可以(至少有一堆CI提供者):

  • 发布标签(发布标签可能?)
  • 触发脚本以更新所有自述文件和所有地方的项目版本
  • 如果所有阶段都成功,则向您发送通知

所以我做了什么:
我承诺,推动并让工具做我想做的一切 与此同时,我做了其他更改,并在一到十分钟后通过邮件获得更新报告。

那里有很多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 prepare script 是一个选项 您也可以尝试其他人
每种描述的方法都有它的缺点,可以根据你想要实现的目的使用。
我只想提供一些替代方案,希望其中一些能适合您的问题!

我希望我能帮助你一点 祝你有愉快的一天!

答案 1 :(得分:4)

prepare是正确的方法

如果您的存储库中包含源文件,但需要使用“构建”步骤来使用它,则prepare会在所有情况下(从npm 4开始)完全满足您的要求。

  

prepare:在打包和发布该软件包之前,在没有任何参数的本地npm install上,以及在安装git依赖项时都运行。

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

这里是an example个使用此方法的我的软件包。


.gitignore的问题

此选项有一个吸引很多人的问题。 准备依赖项时,Npm和Yarn将保留在files的{​​{1}}部分列出的文件。

可能会看到files defaults to all files being included并认为已完成。 容易遗漏的是package.json 主要是覆盖.npmignore指令,如果files不存在,则{{1} }代替。

因此,如果您的构建文件像理智的人一样在.npmignore中列出,请不要在.gitignore中列出您的构建文件,也不要使用.gitignore文件, files似乎损坏

如果您将.npmignore修复为仅包含已构建的文件或添加空的prepare,则一切就绪。

答案 2 :(得分:3)

  

假设package.json文件的脚本字段中有build脚本,是否可以将包配置为在这种情况下自动运行?

是。您需要做两件事:

  1. 确保您的系统使用npmyarn从GitHub安装软件包。如果此包是另一个包的依赖项,则可以使用GitHub URL代替package.json中的版本号。否则,以下命令将起作用:

    npm install https://github.com/YourUser/your-package
    

    如果您在特定标记或分支之后,可以在网址末尾添加/tags/v1.0.0或其他内容。

  2. 将以下内容添加到模块scripts中的package.json

    "install": "npm run build"
    
  3. install是程序包管理器在安装模块后执行的挂钩。 (preinstallpostinstall也 - 请参阅文档。)

    文档:https://docs.npmjs.com/misc/scripts

答案 3 :(得分:2)

已编辑2

这是一个很好的问题。遗憾的是,没有公认的可靠解决方案,但以下方法似乎可行。

创建一个.buildme标记文件,并提交到git。

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"
  },

这里是要注意的事情。

  1. 特殊的.buildme标记文件应使用"files"键或通过.npmignore从npm软件包中排除。

  2. prepack钩子在您发布时运行(prepublishOnly也可以工作,但是很高兴使用prepacknpm pack将产生正确的tarball)。

  3. 从npm安装时,preinstall可以运行,但由于缺少.buildme(由于[ ! -f .buildme ]子句)而没有执行任何操作。

  4. 从github安装时,.buildme确实存在。在npm6上,prepack挂钩运行构建(并生成不包含.buildme的程序包),而preinstall不执行任何操作。在纱线1.12上,preinstall进行构建。

  5. 如果您从github安装更新版本,preinstall将再次运行,并重新构建。

注意:从github安装时,安装人员必须已经安装了软件包的devDependencies才能使构建正常工作。 (此解决方案不会尝试自动安装devDependencies。)

就是这样。它似乎可以与npm 6和纱线1.12的杂色组合一起使用。