本文由 简悦SimpRead 转码,原文地址 blog.codemagic.io
本文将特别关注Flutter/Dart的单体,它涉及到将你的应用程序分割成......。
在处理较大的代码库时,单体是非常有用的。但它们也会带来额外的管理成本。在这篇文章中,我们将通过使用Melos这样的工具来管理monorepo,并通过Codemagic为CI/CD设置我们的存储库的过程。
本文由Nils Reichardt撰写。
monorepos的介绍
现在,许多公司和项目都在使用monorepo的结构。几个例子包括Flutter本身,FlutterFire(一套用于Firebase的Flutter包),Riverpod,Flutter社区的PlusPlugins,以及Very Good Ventures在I/O Photo Booth等项目中。
但什么是monorepo?monorepo是一个单一的版本控制的仓库,可以存储许多不同的项目。
单库的优势
单版本库有一些有用的优势。
- 代码重复使用。单版本库使你能够将你的代码库分割成独立的小包,这对代码重用和测试非常有利。
- 更好的CI。有了monorepo,你可以在改变版本库中的其他东西时轻松触发CI(例如,你可以在改变后端时触发Flutter集成测试)。
- 依赖性管理。你有本地包,而不需要依赖管理器,如pub.dev
- 支持分层架构。你可以要求你自己和你的团队应用分层架构,将各层拆分成多个包
- 让所有东西都存储在一个地方。新的开发者只需克隆monorepo,就可以把所有东西都放在一个仓库里。
单一项目库的缺点
就像生活中的一切一样,单库也有一些缺点。
- 更多的开销。你需要设置工具来管理版本库。
- 没有每个项目的访问控制: 当你把所有东西都放在一个版本库里时,每个有版本库访问权的人都可以访问所有东西。
注意: 这些只是单版本库的一些优点和缺点。在比较单库和多库的时候,还有很多东西需要考虑。
这篇文章的范围
现在你已经对monorepo有了基本的了解,让我们来设定本文的范围,因为monorepos是一个大话题。你可以把你的前端、后端、内部工具、网站等存储在monorepo中。谷歌以拥有世界上最大的代码库而闻名--他们在一个仓库中拥有一切。因此,在一篇文章中涵盖关于monorepos的所有内容将是太多了。
本文将特别关注Flutter/Dart monorepos,它涉及到将你的应用程序分割成独立的小包。
示例应用程序
为了提供一个实际的例子,我们正在使用Flutter计数器应用程序,并做了一些调整。
apps/
counter_app
packages/
counter_widgets
counter_lint
在apps中,我们有实际部署的应用程序。我们也可以有一个内部的应用程序,网站,等等。
在packages中,我们有我们的本地包。
你可以查看完整的源代码这里。
正如刚才提到的,工具对于管理你的monorepo是非常有帮助的。你将面临以下的挑战。
- 获取所有软件包的依赖性
- 检查所有软件包的提示性内容
- 检查所有软件包的格式化
- 运行所有软件包的测试
- 在所有软件包中运行 "build_runner"。
- 合并所有软件包的代码覆盖率
你可以写你自己的bash脚本或CLI来帮助管理这些任务。然而,这要花费你一些时间。为了更快地处理这些任务,你可以使用社区工具,比如Melos,Very Good CLI,或者Sidekick。在这篇文章中,我们将使用Melos。Melos也被FlutterFire、AWS Amplify (Flutter)、Flame和Plus Plugins等软件库所使用。
设置Melos
首先,你需要在终端运行以下命令来安装Melos。
dart pub global activate melos
为了配置Melos,我们需要创建一个顶级的melos.yaml文件。目前的结构是这样的。
apps/
counter_app
packages/
counter_widgets
counter_lint
melos.yaml
现在,我们用一个基本配置来设置melos.yaml文件。
# The name of the project (required) is used for display purposes within IO environments and IDEs.
name: counter
# A list of paths to local packages that are included in the Melos workspace. Each entry can be a specific path or a glob pattern.
packages:
- "apps/*"
- "packages/**"
# Recommended option for projects with Dart 2.17.0 or greater.
#
# This enables a new mechanism for linking local packages, which integrates
# better with other tooling (e.g. dart tool, Flutter tool, IDE plugins) than the
# mechanism currently being used by default. Please read the documentation for
# usePubspecOverrides before enabling this feature.
#
# See https://melos.invertase.dev/getting-started#setup
command:
bootstrap:
usePubspecOverrides: true
设置完melos.yaml文件后,运行bootstrap命令,为你的项目初始化Melos。
melos bootstrap
Bootstrapping有两个主要作用。
- 安装所有的软件包依赖(内部使用pub get)。
- 在本地将任何软件包连接起来
在melos.yaml中,我们也可以定义我们的命令,这些命令在我们定义的Melos工作区的每个Dart/Flutter包中执行。
scripts:
analyze:
run: melos exec -- "flutter analyze"
description: Run `flutter analyze` in all packages
format:
run: melos exec -- "flutter format . --set-exit-if-changed"
description: Run `flutter format .` in all packages
test:
# Only run the test command when the package has a test directory
run: melos exec --dir-exists=test -- "flutter test"
description: Run `flutter test` in all packages
现在我们能够用melos run SCRIPT_NAME执行我们的脚本。要在所有软件包中运行flutter analyze命令,我们可以使用这个命令。
melos run analyze
你可以在melos.yaml文件中添加任何你想要的脚本,如build_runner。查看Melos的文档以了解更多关于scripts配置的信息。
另外,看看melos-code,这是Melos的VS Code扩展,帮助你在Melos和VS Code中工作。
为 CI/CD 设置您的 Flutter 单一项目
您应该能够用 Melos 在本地管理您的 monorepo。然而,您可能需要配置您的CI/CD环境以完全支持您的monorepo。我们将使用Codemagic作为CI/CD提供者。
我们的CI的范围
我们的CI管道应该对每个拉动请求执行以下检查。
- 运行 "melos run analyze "命令
- 运行 "melos run format "命令
- 运行 "melos run test "命令
- 上传失败的黄金测试的结果
为一个monorepo配置CI/CD
设置Codemagic
首先,你需要一个Codemagic账户。如果你还没有,你可以通过你的Git提供商注册Codemagic。用 Workflow Editor 或 codemagic.yaml 文件来设置Codemagic。如果你需要一个逐步的指导,你可以按照这篇文章来为Codemagic设置你的monorepo。
工作流编辑器对于一个基本的应用程序来说,使用起来很简单。然而,对于monorepo来说,最好使用codemagic.yaml,因为我们可以用Melos运行我们自己的命令。由于这个原因,这篇文章只涉及codemagic.yaml文件的设置。
为CI/CD设置Melos
在CI/CD中设置Melos与在你的本地机器上设置它相似。
- 运行
dart pub global activate melos。 - 运行
melos bootstrap。
让我们检查一下codemagic.yaml文件中的基本配置。
workflows:
ci:
name: CI
instance_type: mac_mini
# Setting the timeout for a build to 15 minutes.
max_build_duration: 15
environment:
# Using the latest Flutter version.
flutter: stable
# This workflow should trigger when a new pull request opens or updates.
triggering:
events:
- pull_request
scripts:
- name: Add Dart SDK to PATH
script: |
echo PATH="$PATH":"$FLUTTER_ROOT/.pub-cache/bin" >> $CM_ENV
echo PATH="$PATH":"$FLUTTER_ROOT/bin" >> $CM_ENV
- name: Melos Bootstrap
script: |
dart pub global activate melos
melos bootstrap
如果你仔细看一下这个脚本,你会注意到这些行。
echo 'export PATH="$PATH":"$FLUTTER_ROOT/.pub-cache/bin"' >> $CM_ENV
echo 'export PATH="$PATH":"$FLUTTER_ROOT/bin"' >> $CM_ENV
我们需要将Dart SDK的路径添加到PATH中,以便能够运行dart命令。否则,我们会得到一个错误,如dart: command not found。
运行Melos脚本
让我们把我们的Melos脚本添加到codemagic.yaml文件中。现在codemagic.yaml是这样的。
workflows:
ci:
name: CI
instance_type: mac_mini
# Setting the timeout for a build to 15 minutes.
max_build_duration: 15
environment:
# Using the latest Flutter version.
flutter: stable
# This workflow should trigger when a new pull request opens or updates.
triggering:
events:
- pull_request
scripts:
- name: Add Dart SDK to PATH
script: |
echo PATH="$PATH":"$FLUTTER_ROOT/.pub-cache/bin" >> $CM_ENV
echo PATH="$PATH":"$FLUTTER_ROOT/bin" >> $CM_ENV
- name: Melos Bootstrap
script: |
dart pub global activate melos
melos bootstrap
- name: Run Analyze
script: melos run analyze
- name: Run Format
script: melos run format
- name: Run Tests
script: melos run test
获取失败的Golden测试的结果
通过Golden测试,您可以渲染一个小部件,并将其与屏幕截图进行比较。要了解更多关于Golden测试的信息,请查看这篇关于如何用Codemagic CI/CD运行Flutter Golden(Snapshot)测试的博客文章。如果你的Flutter repo中有Golden测试,你可能想访问失败的Golden测试的结果。当你使用monorepo时,你需要检查每一个包是否有失败的Golden测试结果。要做到这一点,你可以直接运行这个脚本。
...
- name: Run Tests
script: |
melos run test
# Upload results of failed Golden tests if test command failed.
if [ $? -ne 0 ]; then
# Finds all "failures" folders and copies them to the export
# directory. Therefore, we are able to view the results of the
# failed Golden tests.
#
# The command will use the exit code 0 (success) even when there are
# no failures folders.
find * -path '**/failures' -execdir bash -c "cp -r failures $FCI_EXPORT_DIR" \;
# Because we caught the exit code of the test command, we need to
# set manually again.
exit 1
fi
配置路径条件
目前,我们为每个拉动请求运行CI,不管这个拉动请求有什么变化,即使我们只是改变了文档文件或后端文件(假设我们的monorepo中也有我们的后端)。
然而,你正在使用不必要的Codemagic构建时间。
为了更有效地使用你的构建分钟,你可以设置路径条件。通过路径条件,你可以定义CI只在特定路径发生变化时运行。
只要使用when关键字来配置路径。
...
environment:
# Using the latest Flutter version.
flutter: stable
when:
changeset:
includes:
# Only run the CI when a file in one of the following directories
# changed.
- "apps/**"
- "packages/**"
- "codemagic.yaml"
excludes:
# Don't run the CI when only .md files have changed.
- "**/*.md"
# This workflow should trigger when a new pull request opens or updates.
triggering:
...
你也可以查看Codemagic关于有条件地运行构建和构建步骤的文档,了解更多关于有条件运行的信息。
结论
单核处理器对于较大的代码库是很好的。然而,正如你在这篇文章中注意到的,它们需要更多的努力来管理。尽管如此,你现在应该能够用Melos来管理你自己的Flutter monorepo并配置你的CI/CD。
你可以查看存储库的全部源代码这里。
如果你在Codemagic遇到配置问题,只需在Codemagic Slack中寻求帮助。
本文作者Nils Reichardt是Sharezone的联合创始人,Sharezone是一个拥有30万注册用户的Android、iOS、Web和macOS的协作式学校计划器。从2018年3月的第一个测试版开始,他就爱上了Flutter。你可以在Twitter、GitHub和LinkedIn找到Nils。