CodingTour
ARTS #75 | 回顾 monorepo

Algorithm

本周选择的算法题是:Spiral Matrix II

规则如下:

Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.

Example:

Input: 3
Output:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
]

Solution

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        ans = [[0] * n for i in range(n)]
        
        def get_num(r1, c1, r2, c2):
            for r in range(r1, r2 + 1):
                yield c1, r
            for c in range(c1 + 1, c2 + 1):
                yield c, r2
            for r in range(r2 - 1, r1, -1):
                yield c2, r
            for c in range(c2, c1, -1):
                yield c, r1
        r1, r2 = 0, n - 1
        c1, c2 = 0, n - 1
        i = 1
        while r1 <= r2 and c1 <= c2:
            for c, r in get_num(r1, c1, r2, c2):
                ans[c][r] = i
                i += 1
            r1 += 1; r2 -=1; c1 += 1; c2 -= 1
        return ans

Review

Why doesn’t set -e (or set -o errexit, or trap ERR) do what I expected?

这篇文章介绍了 bash 严格模式 set -e 的不可预测性。

我们一开始使用 set -e 的初衷是好的,希望找到一种高效的方式处理错误,尽可能将错误提前暴露,避免向下传递。set -e 在此场景下很有用,同时它的副作用也很明显,那就是不可预测性。

实际上并不是所有的错误都需要处理,比如:

  • flutter clean 会在没有 .packages 时报错(第一次当然不会有了,这个错误忽略即可)
  • rm file 文件不存在时
  • grep a b 找不到对应的字符时

想用好 set -e 就要保证每一个潜在的错误都有对应的错误处理程序,如:

  • grep cat food || echo "$0: no cat in the food" >&2
  • trap 'do_something' ERR

一个不算复杂的 sh 文件都会有很多意想不到的错误 case 需要处理,所以 set -e 在开发者群体内并不流行,大多数时候还是靠手动处理更高效、可靠。

部分例子来自于:What does set -e mean in a bash script?

Tip

macOS 虚拟化改进

早前我们将 macOS 也打包成了虚拟化环境,然后通过「自动登录+launchd」实现自启动服务。

但是实践下来总体感觉不够稳定,主要原因是想通过 launchd 完全模拟一个登录用户很麻烦,比如我们的虚拟化环境主要是为 CI 服务,包含了打包、自动化测试等基本服务,需要用到 xcodebuild 等基础软件,通过 xcodebuild 调用模拟器时又需要 Aqua 的支持,于是 launchd 中还需要增加 LimitLoadToSessionType 的配置。环境变量也一样,如果 EnvironmentVariables 不包含合适的配置连最基本的像 python3 之类的软件也无法运行。

我们目前正在尝试的解决方案也很简单,把直接通过 launchd 启动的服务用 Terminal 来启动:

<key>ProgramArguments</key>
<array>
  <string>open</string>
  <string>-a</string>
  <string>Terminal</string>
  <string>/path/to/run_service.sh</string>
</array>

确保是一次完整的登录用户调用以及 GUI 是可用的。

Share

CocoaPods 两周前发布了 1.10 的正式版本,相比我们目前使用的 1.8.4 有许多更新:

  • 增加了对 Xcode 12 完整的支持
  • 增加了对 XCFramework 的支持(1.8.4 对此会编译报错)
  • 支持在 Test Specs 里配置代码覆盖率(我们目前的单元测试方案)
  • 支持配置 use_frameworks! 是以静态还是动态库的方式链接
  • 从 Ruby 2.0 升级到了 2.7(macOS 很多年前就去掉了对 2.0 的支持)

在 CocoaPods 1.10 文末 What’s Next 里有这么一段话:

Additionally, we are still considering adding support for local sources that would be helpful for monorepos and allow CocoaPods to discover local pods automatically.

from: https://blog.cocoapods.org/CocoaPods-1.10.0-beta/

在开源社区里有很多采用了 monorepo 的代码结构管理方式,比如像 React 这类前端库,他们往往会以「组件库」为单位一起发布,避免每个组件都有自己的独立版本号,修复 Bugs 时也能在同一条 commit 里看到多个组件的修改记录(原子性的提交),维护起来方便许多,同时也适合做大规模的重构,尤其是对小团队而言。

monorepo 虽然在物理上没有将组件之间隔离开,但是每个组件还是应该保持独立迭代的特点,尽量减少组件之间的耦合。

我们从去年开始实践 monorepo,由于 CocoaPods 原生不支持,我们为此开发了相关的插件,希望早日看到官方的实现,如此便能帮助更多团队落地 monorepo。