提交 Composer 供应商目录
安装Composer依赖项时,这些依赖项被下载并存储在“供应商”目录中。可以选择将其安装到与composer.json文件不同的位置,但通常可以在同一目录中找到。
vendor目录包含相当多的代码,如果您的项目包含相当多的依赖项,则尤其如此。但通常情况下,该目录不会包含您实际编写的任何代码。它将包含必要的第三方库,使您的代码能够正常工作。
我一直被告知不要提交供应商目录,并且多年来我一直遵循这个建议。这在构建开源库时很有意义,因为如果您的库包含一个流行的库并且该库又被其他库包含,它会很快造成混乱。拥有包含不同版本库的代码库会很快导致混乱甚至灾难。
但是项目呢?最近与其他一些开发人员的讨论让我怀疑,提交供应商目录对于项目是否总是一个坏主意,所以我想我会讨论这样做的利弊。虽然将供应商目录提交到您的代码库对于库来说是一个糟糕的主意,但在使用这些库构建项目时并不是那么明确。在这种情况下,代码库中将只有一个供应商目录,最终目标(无论是网站还是其他工具)需要包含这些库才能运行。
提交供应商目录
根据作曲家网站,基于作曲家的项目的最佳实践是不提交供应商目录。该站点指出,提交供应商目录可能会导致以下问题。
大型VCS存储库大小和更新代码时的差异。
在您自己的VCS中复制所有依赖项的历史记录。
将通过git安装的依赖项添加到gitrepo会将它们显示为子模块。这是有问题的,因为它们不是真正的子模块,您会遇到问题。
让我们依次访问这些项目中的每一个。
大型VCS存储库大小和更新代码时的差异。
大的存储库大小并不是世界末日,因为存储相当便宜,但它真的膨胀这么多吗?为了测试这一点,我查看了我自己的(Drupal9)站点代码库,其中不包含任何供应商目录或第三方代码(除了一些配置项)。如果我将我的站点项目检出到一个空目录中,则整个项目大约为90MB。运行composerinstall后,我变成了150MB。虽然这是一个增加,但它仍然没有引起很多问题,因为没有一个文件大于2MB(最大的文件是字体文件)。将诸如数据库备份之类的内容提交到您的存储库中仍然是不好的做法,因为这往往会使git和GitHub等网站抱怨很多。
差异虽然,这对我来说是一个问题。假设您正在运行一个典型的Drupal站点,其中包含20到30个贡献的模块或一个具有一些依赖项的Symfony项目。对这些模块的任何更新都将构成您的存储库中的更改,而您的提交将是对composer.lock文件的更改以及您没有编写和维护的整个代码负载。至少对我来说,看到所有这些代码的价值是微乎其微的。虽然密切关注您的依赖项正在做什么是一个好主意,但您应该对您和您的团队添加到系统中的代码更感兴趣。自定义代码是大多数错误或安全漏洞的来源。
在您自己的VCS中复制所有依赖项的历史记录。
这本身不是问题,但与第一点密切相关。我认为他们在谈论图书馆而不是项目。提交供应商确实使查看项目历史和查看自定义代码发生了什么变得困难。将一个版本与另一个版本进行比较会引入许多您没有编写的不同更改。
将通过git安装的依赖项添加到gitrepo会将它们显示为子模块。这是有问题的,因为它们不是真正的子模块,您会遇到问题。
这可能是一个真正的问题。他们在这里谈论的问题是当composer将依赖项下载为git存储库时,这意味着您的vendor目录包含一个辅助git存储库。Git倾向于将其视为子模块,因此会有些困惑。通常会发生的情况是,如果您在其他地方查看git存储库,供应商目录将包含损坏的git子模块。在与同事共享代码甚至部署代码时,这让我感到震惊。在我发现这个问题之前,我花了很长时间摸索为什么我的本地包含代码,但开发站点没有。
虽然这很痛苦,并且一直是许多开发人员的绊脚石,但有几种方法可以解决这个问题。首先,您可以在需要或安装软件包时使用--prefer-distcomposer标志。这会强制Composer下载依赖项的存档版本,而不是源代码。
此外,通过在文件中添加post-update-cmd和post-install-cmd,您composer.json可以在尝试提交之前强制删除任何下载的git存储库。
"scripts": { "post-update-cmd": ["echo Delete all .git directories in vendor.", "rm -rf vendor/**/**/.git"], "post-install-cmd": ["echo Delete all .git directories in vendor.", "rm -rf vendor/**/**/.git"] },
除了这些点,我认为另一个问题是机器生成代码的提交。例如,假设开发人员开始处理一个项目并需要包含另一个依赖项。他们运行composerrequire并安装依赖项,发现除了新包外,其他文件的负载也发生了变化。Composer有一些内部文件,用于映射内容在供应商目录中的位置,并且对包的任何更新或添加都会更新这些文件。开发人员自然会提交这些文件,因为它们是他们刚刚引入的更改的一部分。然后,这些更改将构成对项目提出的任何拉取请求的一部分。这些对Composer生成的文件的更改对检查有用吗?它们是否应该包含在代码分析工具中?我不这么认为。
还有合并代码的问题。如果发生合并冲突,将大量依赖项提交给您的repo将代码合并到分支中会变得更加棘手。如果没有供应商文件夹,您会发现的唯一冲突是您的composer.json和composer.lock文件,这相对容易解决。有了你的包的源代码,它可能会导致更复杂的合并问题。我经常发现开发人员会看到这种合并冲突并尝试解决它,但最终会向源代码添加损坏的甚至提交不同版本的依赖项。这可能会导致您的composer.lock文件与您的供应商目录不同步,这确实非常危险。
最后,您的依赖项和开发依赖项之间存在差异。当您提交您的供应商目录时,这意味着它已被锁定。由于您可能正在本地处理该项目,因此您可能会安装所有依赖项。使用带有--no-dev标志的composerinstall运行部署是很正常的。
composerinstall--no-dev
这样做可以防止不打算部署到生产环境的包被部署到实时站点中。但是,如果您提交了供应商目录,那么您别无选择。您的所有代码必须同时部署。虽然这似乎不是问题,但多年来存在一些由开发模块中的代码引起的重大安全漏洞。像phpunit和DrupalCoder模块这样的包存在缺陷,这意味着只要存在于服务器上,攻击者就可以破坏服务器,而且由于它们是开发包,因此一开始就不应该真正部署。
不过,这种方法有一些积极的一面。由于所有依赖项都包含在代码库中,因此可以更轻松地在版本之间进行交换。如果您遇到问题,您可以轻松地恢复到以前版本的代码,而无需运行composerinstall来恢复您的依赖项。这使得像gitbisect这样的操作更容易执行。
总结一下:
❌提交是杂乱无章的,并且充满了第三方代码。
❌杂乱的拉取请求。
❌机器生成的Composer代码,可在版本和平台之间更改。
❌合并冲突更难解决。
❌ Composer.lock文件并不总是显示您系统中的内容。
❌依赖和开发依赖没有区别。
✅更容易在版本之间切换。
✅所有代码都在存储库中,可以检查。
✅易于部署。
不提交供应商目录
有趣的是,提交供应商目录的替代方法是不提交供应商目录。这被认为是使用Composer的最佳实践方法,本质上意味着使用以下.gitignore文件创建项目。
vendor
如果您正在运行Drupal或WordPress站点,您可能会在其中包含一些其他条目,但核心思想是没有第三方依赖代码提交到您的存储库。当您需要一个新的依赖项时,唯一改变的是composer.json和composer.lock文件。当您更新依赖项时,唯一需要更改的是composer.lock文件。
缺少第三方代码意味着您的存储库更小,但这确实意味着当您想要处理项目时,您需要运行composerinstall来下载并安装所有依赖项。如果您在本地安装或部署到生产环境,这同样适用。安装依赖项这很好,虽然偶尔我发现composerinstall会由于随机原因失败。诸如github宕机或只是随机网络问题之类的事情可能会导致Composer安装失败。当这种情况发生在本地时很烦人,如果在部署期间发生这种情况,则部署将失败。您将需要一个健壮的部署过程,以允许诸如composer安装失败之类的事情发生。
我还听说安装Composer依赖项会惹恼系统管理员,因为他们必须处理这消耗的带宽。我认为从源下载Composer依赖项或将它们作为repo的一部分下载之间几乎没有区别。您仍然需要下载完整大小的存储库。在我看来,您不应该使用生产服务器来安装Composer依赖项,这应该在构建器服务器和部署到生产环境的工件上完成。在生产环境中运行composerinstall的额外压力绝对不值得。
由于您需要创建一个部署流程,因此您可以创建一个不会将您的开发依赖项部署到生产环境的流程。这不仅减少了项目的整体规模,而且可以防止生产服务器上存在第三方测试和开发代码引起的一些安全问题。如果您只想安装生产Composer软件包,请确保使用--no-dev标志运行composerinstall。
composerinstall--no-dev
此外,您还可以添加--prefer-dist标志以进一步加快安装过程。使用--prefer-dist意味着composer将尝试使用GitHub或其他可用API下载和解压缩依赖项的存档。在大多数情况下,这用于更快地下载依赖项。此方法不会克隆整个存储库,因此当您只需要特定版本时,您不会获得项目的整个历史记录。此外,存档往往以这样一种方式准备,即它们不包含任何依赖项中不需要的不必要文件,例如.gitattributes。
这一切都是为了拥有一个强大的部署过程,该过程将处理Composer环境中存在的任何情况或问题。
Whataboutreviewingallofthecodeinthirdpartdependencies?Whilstthisisalaudableact,Ifeelsorryforthedeveloperwhoneedstoreviewapullrequestwiththousandsoflinesofcodecontaininganewthirdpartydependency.Itwouldbeveryeasytomissthesecurityproblemintroducedbyasinglelineofcustomcodeburiedinthismess.Withthevendordirectorynotinthepullrequestitwouldbeveryeasytoreviewthecustomcodeandspottheproblem.
由于安全顾问项目,实际上可以防止Composer包中的安全问题,而无需手动检查每一行代码。这是一个packagist.org实际上不包含任何代码的项目。它基本上在您的项目中包含一个composer依赖项,以防止您包含任何其他包含已知安全问题的包。只需将其包含在您的项目中,如下所示:
composerrequire--devroave/security-advisories:dev-master
现在,如果您尝试在依赖项中安装安全问题,您会遇到冲突,而composer只会拒绝安装该软件包。
如果你使用Drupal,你应该知道Drush也有一个检查,你可以用来发现有安全问题的模块,只需运行以下命令,看看是否有任何安全更新。
drushpm:security
您可以与构建过程联系起来,因为如果有可用的安全版本,这将返回失败状态。
我还听到开发人员谈论第三方代码“丢失”,以及为此采取了哪些保护措施。例如,假设您正在使用一个Drupal模块,有一天该模块的维护者决定他们不想维护它并从站点中删除了该模块。现在,当您运行composerinstall时,由于缺少模块,您将失败。这可能是Synfony组件或其他依赖项,但关键是开发人员可能会从互联网上删除他们的代码并破坏您的网站。由于供应商目录未提交,该依赖项中的代码就消失了。然而,在我看来,这种情况实际发生的可能性非常小。如果确实发生了这种情况,那么该模块或组件中的代码本质上就变成了自定义代码,您应该这样对待它。查找您网站的备份(您正在服用的备份,右)和来自供应商的目录代码转移到你的src目录或任何你正在存储自定义代码。由于没有第三方维护者来维护该项目,您现在负责该代码,就像它是您自己的一样。
总结一下:
❌不太容易查看项目中的实际第三方代码。
❌如果任何依赖站点关闭,Composer安装可能会失败。
❌依赖项可能会丢失。
❌需要设置托管部署。
✅托管部署更强大。
✅合并冲突更容易解决。
✅更清洁的承诺。
✅更清洁的拉取请求。
✅对生产没有开发依赖。
结论
当我开始思考这个问题时,我意识到实际情况并不像你想象的那么明确。这两种方法都有其优点和缺点,但根据我的经验,提交供应商目录会导致更多问题。
我过去参与过使用这两种系统的项目。当我刚开始使用Drupal8时,我没有部署系统设置,所以我选择提交供应商目录,以便部署可以模仿我已经拥有的Drupal7部署风格。根据我使用这两种风格的经验,我发现提交供应商目录比不提交会导致更多的问题。除了提交开发依赖项的潜在安全风险之外,管理代码也很麻烦。
更新或添加依赖项可能会导致大量更改,存储库中包含供应商目录,这使得查看提交或拉取请求非常痛苦。我完全同意理解系统中包含的代码很重要,但实际上你自己的团队编写的代码更重要,应该接受更多的审查。虽然您绝对应该及时了解依赖项的开发及其更新和安全性,但大多数开发团队没有时间分析其依赖项中的每一行代码。
一旦我开始使用更强大的部署机制,我就从代码库中删除了vendor目录。在我看来,提交供应商目录感觉像是一种懒惰的方法,可以通过使用像样的部署工具(例如Deployer)来解决。
我也意识到不提交供应商目录意味着你的作曲家文件不会骗你。您可以使用像composershow这样的命令来查看安装了哪些软件包,但是我经常发现,当供应商目录已提交时,这些报告是错误的。作为测试,我将删除供应商目录并运行composerinstall以确保没有任何更改,并且我经常发现整个代码库都有重大更改。这些更改可能源于composer生成的自动生成文件的差异,但更常见(且危险)的更改是依赖项中的代码。这些更改的原因很多,但可能是由于管理不当的合并冲突或开发人员提交了供应商目录而不是composer.lock文件(发生了!)。自己试试吧。从你的repo中删除你的vendor目录并运行composerinstall。如果没有任何变化,我会给你买啤酒。
我希望对此有更多意见,尤其是来自行业专业人士的意见。请发表评论,让我知道您的想法。你提交供应商目录吗?如果是这样,为什么?你想解决什么问题?您的团队是否更高效地工作,或者您是否发现拉取请求充满噪音?请在评论中告诉我。