使用 Travis CI 自动维护软件仓库 (Homebrew)
前段时间的一篇文章介绍了如何使用 Homebrew 建立自己的软件仓库,目的是让大家能轻松的发布软件。但同时如何让仓库里的软件保持最新又成为了新的问题。
我一直没有找到合适的答案,直到看到了这个 Issues。受此启发,我编写了一个利用 Travis CI 自动更新 Formula 的 shell 脚本。它的基本原理就是利用 Travis CI 的 Cron Jobs
功能,每天定期运行更新脚本,如果有更新,就根据修改 Formula 脚本,并推送到 Tap (GitHub) 仓库。
不同的仓库需要修改脚本以适配不同的项目,但我会在后文中详细介绍脚本是如何工作的,保证你读完后也能编写出自动维护 Formula 的脚本。
完整的更新脚本参考和 .travis.yml
在附录里。
手动更新🔗
首先我们要了解一下如何手动更新 Formula。还是以 frp 为例。
假如 frp 发布了新的更新,我们首先要去 frp 的 GitHub Releases 页面获取最新版本的源码。
wget https://github.com/fatedier/frp/archive/v0.25.3.tar.gz
然后利用 sha256sum
计算源码的 sha256 校验值。
$ sha256sum v0.25.3.tar.gz
68281965d04567d55f143b4a4c4d4369c1962937d80484b6b48e96a5dcf0b2e4 v0.25.3.tar.gz
再将计算好的校验码和下载链接替换到 frp.rb
中。
总结一下就是:下载最新的源码 -> 计算校验码 -> 替换下载地址和校验码
而我们要做的就是用脚本来执行上面这一过程,让计算机来做重复又无味的工作。
自动更新🔗
在 Tap 的根目录新建一文件,命名为 auto-update.sh
定义变量 (常量)🔗
首先我们定义一些变量 (常量) 来储存必要的信息。
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 仓库了,但总觉得在一个镜像仓库中操作文件更放心一点
git clone https://${GH_REF}
获取最新的版本号🔗
我们需要获取最新版本的版本号,可以通过 GitHub API 来获取,我们先在终端中试验一下
$ 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
过滤一下,让大家看清楚一点
$ curl -s https://api.github.com/repos/fatedier/frp/releases/latest
"tag_name": "v0.25.3",
然后用 cut
处理一下字符串,并加上一个 loop
循环 (排除网络环境的影响) 就可以用了。反映到脚本上是这样的
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
等信息组合一下就可以获得源文件的下载链接
DOWNLOAD_URL="https://github.com/$AUTHUR_NAME/$FORMULA_NAME/archive/"$V_VERSION".tar.gz"
用 curl
下载源文件
curl -L $DOWNLOAD_URL > $FORMULA_NAME.$V_VERSION.tar.gz
计算校验码🔗
然后就可以计算 sha256 校验码了,还是用 sha256sum
计算,用 cut
处理字符串
V_HASH256=$(sha256sum $FORMULA_NAME.$V_VERSION.tar.gz |cut -d ' ' -f 1)
替换下载链接和校验码🔗
用 sed
替换 Formula 中需要更新的信息,如果不需要更新,那么替换后还是原来的 Formula。
# 如果 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 中添加)
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
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
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
#!/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