前段时间的一篇文章介绍了如何使用 Homebrew 建立自己的软件仓库,目的是让大家能轻松的发布软件。但同时如何让仓库里的软件保持最新又成为了新的问题。
我一直没有找到合适的答案,直到看到了这个 Issues。受此启发,我编写了一个利用 Travis CI 自动更新 Formula 的 shell 脚本。它的基本原理就是利用 Travis CI 的 Cron Jobs
功能,每天定期运行更新脚本,如果有更新,就根据修改 Formula 脚本,并推送到 Tap (GitHub) 仓库。
不同的仓库需要修改脚本以适配不同的项目,但我会在后文中详细介绍脚本是如何工作的,保证你读完后也能编写出自动维护 Formula 的脚本。
完整的更新脚本参考和 .travis.yml
在附录里。
手动更新
首先我们要了解一下如何手动更新 Formula。还是以 frp 为例。
假如 frp 发布了新的更新,我们首先要去 frp 的 GitHub Releases 页面获取最新版本的源码。

1
| wget https://github.com/fatedier/frp/archive/v0.25.3.tar.gz
|
然后利用 sha256sum
计算源码的 sha256 校验值。
1
2
| $ sha256sum v0.25.3.tar.gz
68281965d04567d55f143b4a4c4d4369c1962937d80484b6b48e96a5dcf0b2e4 v0.25.3.tar.gz
|
再将计算好的校验码和下载链接替换到 frp.rb
中。
总结一下就是:下载最新的源码 -> 计算校验码 -> 替换下载地址和校验码
而我们要做的就是用脚本来执行上面这一过程,让计算机来做重复又无味的工作。
自动更新
在 Tap 的根目录新建一文件,命名为 auto-update.sh
定义变量 (常量)
首先我们定义一些变量 (常量) 来储存必要的信息。
1
2
3
4
5
| typeset -l FILE_NAME # 将 $FILE_NAME 设为全部小写
AUTHUR_NAME="fatedier" # 软件拥有者的 GitHub 用户名
FORMULA_NAME="frp" # 软件仓库名
FILE_NAME=$FORMULA_NAME # Formula 的文件名 (等于全小写的 $FORMULA_NAME)
|
为什么要将 $FORMULA_NAME
和 $FILE_NAME
分开呢?因为软件仓库是允许大写的,但 Formula 的文件名不允许
克隆 Git 仓库
虽然我们有一个存放 Formula 的 Git 仓库了,但总觉得在一个镜像仓库中操作文件更放心一点
1
| git clone https://${GH_REF}
|
获取最新的版本号
我们需要获取最新版本的版本号,可以通过 GitHub API 来获取,我们先在终端中试验一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| $ curl -s https://api.github.com/repos/fatedier/frp/releases/latest
{
"url": "https://api.github.com/repos/fatedier/frp/releases/16350972",
"assets_url": "https://api.github.com/repos/fatedier/frp/releases/16350972/assets",
"upload_url": "https://uploads.github.com/repos/fatedier/frp/releases/16350972/assets{?name,label}",
"html_url": "https://github.com/fatedier/frp/releases/tag/v0.25.3",
"id": 16350972,
"node_id": "MDc6UmVsZWFzZTE2MzUwOTcy",
"tag_name": "v0.25.3",
"target_commitish": "master",
"name": "v0.25.3",
"draft": false,
"author": {
"login": "fatedier",
"id": 7346661,
"node_id": "MDQ6VXNlcjczNDY2NjE=",
"avatar_url": "https://avatars3.githubusercontent.com/u/7346661?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/fatedier",
"html_url": "https://github.com/fatedier",
"followers_url": "https://api.github.com/users/fatedier/followers",
"following_url": "https://api.github.com/users/fatedier/following{/other_user}",
"gists_url": "https://api.github.com/users/fatedier/gists{/gist_id}",
"starred_url": "https://api.github.com/users/fatedier/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/fatedier/subscriptions",
"organizations_url": "https://api.github.com/users/fatedier/orgs",
"repos_url": "https://api.github.com/users/fatedier/repos",
"events_url": "https://api.github.com/users/fatedier/events{/privacy}",
"received_events_url": "https://api.github.com/users/fatedier/received_events",
"type": "User",
"site_admin": false
},
...
|
返回的结果中就包含了我们需要的版本号。我在用 grep
过滤一下,让大家看清楚一点
1
2
| $ curl -s https://api.github.com/repos/fatedier/frp/releases/latest
"tag_name": "v0.25.3",
|
然后用 cut
处理一下字符串,并加上一个 loop
循环 (排除网络环境的影响) 就可以用了。反映到脚本上是这样的
1
2
3
4
5
6
7
8
9
10
11
12
| loop_parser(){
while true
do
result=$(curl -s https://api.github.com/repos/"$AUTHUR_NAME"/"$FORMULA_NAME"/releases/latest | grep "$1" | cut -d '"' -f 4)
if [ ! -z "$result" ]; then
echo $result
break
fi
done
}
V_VERSION=$( loop_parser "tag_name" )
|
下载源文件
然后我们将$V_VERSION
(版本号) 与 $AUTHUR_NAME
、$FORMULA_NAME
等信息组合一下就可以获得源文件的下载链接
1
| DOWNLOAD_URL="https://github.com/$AUTHUR_NAME/$FORMULA_NAME/archive/"$V_VERSION".tar.gz"
|
用 curl
下载源文件
1
| curl -L $DOWNLOAD_URL > $FORMULA_NAME.$V_VERSION.tar.gz
|
计算校验码
然后就可以计算 sha256 校验码了,还是用 sha256sum
计算,用 cut
处理字符串
1
| V_HASH256=$(sha256sum $FORMULA_NAME.$V_VERSION.tar.gz |cut -d ' ' -f 1)
|
替换下载链接和校验码
用 sed
替换 Formula 中需要更新的信息,如果不需要更新,那么替换后还是原来的 Formula。
1
2
3
4
| # 如果 Formula 中有 version 这一变量的话取消下面的注释
# sed -i "s#^\s*version.*# version \"$V_VERSION\"#g" homebrew-taps/Formula/$FILE_NAME.rb
sed -i "s#^\s*url.*# version \"$DOWNLOAD_URL\"#g" homebrew-taps/Formula/$FILE_NAME.rb
sed -i "s#^\s*sha256.*# sha256 \"$V_HASH256\"#g" homebrew-taps/Formula/$FILE_NAME.rb
|
推送
将更新好的 Formula 文件推送到 GitHub 上 (${GH_TOKEN}
和 ${GH_REF}
这两个变量需要在 Travis CI 中添加)
1
2
| git commit -am "travis automated update" || exit 0
git push --quiet "https://${GH_TOKEN}@${GH_REF}" master:master
|
配置 Travis CI
.travis.yml
这次的 .travis.yml
很简单,只需要做两件事:
设置 git config
执行放在根目录的 auto-update.sh
1
2
3
4
5
6
| before_install:
- git config --global user.name "$USERNAME"
- git config --global user.email "$EMAIL"
script:
- auto-update.sh
|
完整的 .travis.yml
文件在附录里。
设置环境变量
需要设置以下环境变量
$USERNAME
:你想记录在 git log 中的名字
$EMAIL
:你的邮箱
${GH_TOKEN}
:GitHub TOKEN,点满 repo
的所有权限 GitHub TOKEN 申请方法
${GH_REF}
:你存放 Formula 的 GitHub 仓库的地址,例如:github.com/:owner/:repo

设置 Cron Jobs
设置 Cron Jobs,让 Travis CI 定期运行脚本

Branch: master
Interval: daily
Options: Always run
附录
.travis.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
| language: generic
sudo: required
before_install:
- git config --global user.name "$USERNAME"
- git config --global user.email "$EMAIL"
before_script:
- chmod +x tools/auto-update.sh
script:
- tools/auto-update.sh
|
auto-update.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| #!/bin/bash
typeset -l FILE_NAME # 将 $FILE_NAME 设为全部小写
AUTHUR_NAME="" # 软件拥有者的 GitHub 用户名
FORMULA_NAME="" # 软件仓库名
FILE_NAME=$FORMULA_NAME # Formula 的文件名 (等于全小写的 $FORMULA_NAME)
log(){
echo ''
echo '-------------------------------------'
echo "$*"
echo '-------------------------------------'
echo ''
}
loop_parser(){
while true
do
result=$(curl -s https://api.github.com/repos/"$AUTHUR_NAME"/"$FORMULA_NAME"/releases/latest | grep "$1" | cut -d '"' -f 4)
if [ ! -z "$result" ]; then
echo $result
break
fi
done
}
git clone https://github.com/Mogeko/homebrew-taps.git
log "parser $FORMULA_NAME download url"
V_VERSION=$( loop_parser "tag_name" )
if [ -z "$V_VERSION" ]; then
log 'parser file version error, skip update.'
exit 0
fi
DOWNLOAD_URL="https://github.com/$AUTHUR_NAME/$FORMULA_NAME/archive/"$V_VERSION".tar.gz"
if [ -z "$DOWNLOAD_URL" ]; then
log 'parser download url error.'
exit 0
fi
log "download url: $DOWNLOAD_URL start downloading..."
# wget -c $DOWNLOAD_URL -O $FORMULA_NAME.$V_VERSION.tar.gz
curl -L $DOWNLOAD_URL > $FORMULA_NAME.$V_VERSION.tar.gz
if [ ! -e $FORMULA_NAME.$V_VERSION.tar.gz ]; then
log "file download failed!"
exit 1
fi
V_HASH256=$(sha256sum $FORMULA_NAME.$V_VERSION.tar.gz |cut -d ' ' -f 1)
log "file hash: $V_HASH256 parser $FORMULA_NAME version..."
log "file version: $V_VERSION start clone..."
log "update config...."
sed -i "s#^\s*version.*# version \"$V_VERSION\"#g" homebrew-taps/Formula/$FILE_NAME.rb
sed -i "s#^\s*sha256.*# sha256 \"$V_HASH256\"#g" homebrew-taps/Formula/$FILE_NAME.rb
git commit -am "travis automated update" || exit 0
git push --quiet "https://${GITHUB_TOKEN}@${GH_REF}" master:master
|
相关文章