GitHub Actions 编译 OpenWrt 固件(树莓派 4B)
引言
但是呢, 重新编译又又又太麻烦了, Mac
本地虽然可以通过 Docker
进行编译, 但是限制了 CPU
类型必须得是 Intel
版本才行, 而目前我常用的 Mac
是 ARM
版的....
所以下面就介绍一个新的 openWrt
固件编译方案: 通过 GitHub Actions
来自动编译 openWrt
固件, 一次设置, 终身享用
本文仓库地址: MoYuanJun/OpenWrt-RaspberryPi
一、创建个空项目
在开始之前我们需要创建一个 GitHub
仓库, 基于该仓库我们去编写 Actions
, 大概步骤如下(简单贴几个图):
二、创建工作流
将创建好的项目, 拷贝到本地, 并在项目根目录下创建文件 .github/workflows/Generate.yml
补充说明: 这里目录是要求固定的, 文件名可随意, 一个文件代表一个工作流
三、工作流详解
下面就工作流内容部分, 进行简单阐述, 当然你可以直接翻到底部获取完整的内容
3.1 触发逻辑
如下代码所示:
name
表示当前工作流名称on
则是用于设置监听事件, 在监听到相关事件发生, 则执行该工作流push - tags
则表示提交新的tags
# 工作流名称
name: OpenWrt RaspberryPi
# 工作流触发时机, see: https://docs.github.com/zh/actions/using-workflows/triggering-a-workflow
# 触发条件修改为: 当 main 分支有 push 操作 || 每天 0 点
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
那么上面的配置则表示工作流将在我们提交新的 tag
时触发工作流的执行
而这里之所以没有设置定时执行, 是因为觉得没有必要, 我只需要在我需要编译最新的固件时, 手动提交一个 tag
就行, 毕竟我也不会闲得没事天天折腾
当然如果你需要定时执行, 可以参考下面代码:
# 工作流名称
name: Generate
# 工作流触发时机, see: https://docs.github.com/zh/actions/using-workflows/triggering-a-workflow
on:
schedule:
# 定时执行: 但由于不支持秒级的定时设定, 使得这里 cron 格式与标准的 cron 有所不同, 在使用过程中需要注意
- cron: '30 22 * * *'
3.2 检出仓库代码
如下代码所示:
jobs
定义当前工作流中所有具体的工作OpenWrt-RaspberryPi
则定义一个新的工作runs-on
定义当前job
所运行的环境steps
则定义当前job
所有要执行的步骤steps - name
定义当前步骤名steps - uses
则使用第三方Actions
# 作业, see: https://docs.github.com/zh/actions/using-jobs/using-jobs-in-a-workflow
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
# step 1. 切到当前仓库
- name: Checkout
uses: actions/checkout@v3
补充说明: actions/checkout 是
GitHub Actions
的一个官方Actions
, 它的作用是检出当前仓库的代码。在GitHub Actions
的工作流中, 每个工作流都会运行在一个全新的环境中, 这个环境并不会自动包含你的仓库代码, 所以我们需要使用 actions/checkout 来将代码检出, 以便后续步骤可以使用。简单理解就是拉取当前仓库的代码, 并进入到项目目录内。
3.3 更新相关依赖
下面, 我们通过 env
新增了两个环境变量
REPO_URL
:openWrt
仓库TZ
: 时区
同时在 OpenWrt-RaspberryPi
中新增步骤二:
name:
用于定义步骤名run: |
定义需要执行的shell
命令, 这后面跟着是shell
脚本内容, 脚本内容的就是更新下载编译固件所需要的相关依赖包, 同时设置了下时区
# 工作流名称
name: OpenWrt RaspberryPi
on:
....
+ env:
+ REPO_URL: https://github.com/coolsnowwolf/lede
+ TZ: Asia/Shanghai
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
...
# step 2. 更新相关依赖
+ - name: Space cleanup
+ run: |
+ sudo -E apt-get update -y
+ sudo -E apt-get full-upgrade -y
+ sudo -E apt-get install -y ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
+ bzip2 ccache cmake cpio curl device-tree-compiler fastjar flex gawk gettext gcc-multilib g++-multilib \
+ git gperf haveged help2man intltool libc6-dev-i386 libelf-dev libfuse-dev libglib2.0-dev libgmp3-dev \
+ libltdl-dev libmpc-dev libmpfr-dev libncurses5-dev libncursesw5-dev libpython3-dev libreadline-dev \
+ libssl-dev libtool lrzsz mkisofs msmtp ninja-build p7zip p7zip-full patch pkgconf python2.7 python3 \
+ python3-pyelftools python3-setuptools qemu-utils rsync scons squashfs-tools subversion swig texinfo \
+ uglifyjs upx-ucl unzip vim wget xmlto xxd zlib1g-dev
+ sudo -E apt-get clean
+ sudo timedatectl set-timezone "$TZ"
3.4 克隆仓库
第三步骤没啥好说的, 就是直接执行 git clone
去克隆 openWrt
仓库代码
# 工作流名称
name: OpenWrt RaspberryPi
on:
....
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
...
+ # step 3. 克隆仓库
+ - name: Clone openWrt
+ run: |
+ git clone $REPO_URL openWrt
3.5 更新「feeds」
第四步骤, 进入 openWrt
目录, 并执行 scripts/feeds
脚本(更新 & 安装)
# 工作流名称
name: OpenWrt RaspberryPi
on:
....
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
...
+ # step 4. 更新 feeds
+ - name: Update feeds
+ run: |
+ cd openWrt
+ ./scripts/feeds update -a
+ ./scripts/feeds install -a
3.6 更新配置文件
在编译 openWrt
固件我们需要为不同的目标设备, 设置不同的配置参数, 比如: 我编译出的固件是要在 树莓派 4B
上进行安装的, 所以这里我就需要按照 树莓派 4B
进行配置, 这样编译出的固件才会兼容 树莓派 4B
如果你不确定你的目标设备对应参数配置可以参考 OpenWrt/tree/main/configs
步骤五, 进入 openWrt
目录, 删除相关配置文件, 并新建配置文件, 并写入 树莓派 4B
相关配置
# 工作流名称
name: OpenWrt RaspberryPi
on:
....
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
...
+ # step 5. 生成配置文件
+ - name: Generate config file
+ run: |
+ cd openWrt
+ rm -f ./.config*
+ touch ./.config
+ # 在 cat >> .config <<EOF 到 EOF 之间粘贴你的编译配置, 需注意缩进关系, 例如:
+ cat >> .config <<EOF
+ # RaspberryPi 4B 配置
+ CONFIG_TARGET_bcm27xx=y
+ CONFIG_TARGET_bcm27xx_bcm2711=y
+ CONFIG_TARGET_bcm27xx_bcm2711_DEVICE_rpi-4=y
+ # Set Firmware Size
+ CONFIG_TARGET_KERNEL_PARTSIZE=64
+ CONFIG_TARGET_ROOTFS_PARTSIZE=960
+ # Firmware Type
+ CONFIG_TARGET_ROOTFS_TARGZ=y
+ # CONFIG_TARGET_ROOTFS_EXT4FS is not set
+ CONFIG_TARGET_ROOTFS_SQUASHFS=y
+ EOF
+ # ===============================================================
+ sed -i 's/^[ \t]*//g' ./.config # 删除文件中每行的所有前导空格
+ make defconfig
3.7 编译
第六步骤, 进入 openWrt
目录, 使用 make
进行开始编译, 编译完成所以产物都会输出到 bin/targets/bcm27xx/bcm2711
目录下, 在最后我还执行了 cd bin/targets/bcm27xx/bcm2711 && ls -n
其实没啥用只是为了在终端中列出所有产物, 目的是为了后面排查问题方便
# 工作流名称
name: OpenWrt RaspberryPi
on:
....
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
...
+ # step 6. 编译
+ - name: Make download
+ run: |
+ cd openWrt
+ make download -j8
+ make V=s -j8
+ cd bin/targets/bcm27xx/bcm2711 && ls -n
补充说明: 不同的配置, 最后输出产物的目录是不一样的,
bin/targets/bcm27xx/bcm2711
是我的配置最终输出目录, 但是肯定都会放在 ``bin/targets` 目录下, 更深的层级则是由配置决定
3.8 创建「Release」& 上传资源
编译完成后, 我还希望将目标产物上传到 Release
上。在开始前我们还需要做个简单配置, 默认情况下 GitHub Actions
对仓库是没有写入权限的, 所以我们需要设置下 GitHub Actions
的权限, 设置写入权限。具体设置方法参考下图:
下面我们调整工作流配置, 如下配置我们新增步骤七, 这里我们使用了 actions/github-script, 通过它我们可以很方便的调用 GitHub API
来操作仓库。
甚至我们可以直接运行 Node
代码, 如下配置所示, with
用于给第三方 Actions
设置参数, github-token
照着写就是, 其实就是操作仓库的一个 TOKEN
(不用任何配置, 按上面设置下工作流对仓库的读写权限就行) script: |
后面跟着就是要执行的脚本, 这里我们使用 Node
来调用 GitHub API
来创建了 Release
并上传相关产物
# 工作流名称
name: OpenWrt RaspberryPi
on:
....
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
...
+ # step 7. 创建 Release & 上传资源
+ - name: Upload release
+ uses: actions/github-script@v3
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ const fs = require('fs');
+ const path = require('path');
+ const { repo: { owner, repo }, sha } = context;
+ console.log({ owner, repo, sha });
+ const release = await github.repos.createRelease({
+ owner, repo,
+ draft: true,
+ target_commitish: sha,
+ tag_name: process.env.GITHUB_REF,
+ });
+ for (let file of await fs.readdirSync('./openWrt/bin/targets/bcm27xx/bcm2711')) {
+ const filePath = `./openWrt/bin/targets/bcm27xx/bcm2711/${file}`
+ if (/\.gz$/.test(file)) {
+ await github.repos.uploadReleaseAsset({
+ repo,
+ owner,
+ name: file,
+ release_id: release.data.id,
+ data: await fs.readFileSync(filePath)
+ });
+ }
+ }
四、完整代码
下面是所有的工作流内容, 记得配置下工作流对仓库的读写权限, 不知道怎么弄的可以翻到上面看 3.8
部分内容
# 工作流名称
name: OpenWrt RaspberryPi
# 工作流触发时机, see: https://docs.github.com/zh/actions/using-workflows/triggering-a-workflow
# 触发条件修改为: 当 main 分支有 push 操作 || 每天 0 点
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
env:
REPO_URL: https://github.com/coolsnowwolf/lede
TZ: Asia/Shanghai
# 作业, see: https://docs.github.com/zh/actions/using-jobs/using-jobs-in-a-workflow
jobs:
OpenWrt-RaspberryPi:
runs-on: ubuntu-latest
steps:
# step 1. 切到当前仓库
- name: Checkout
uses: actions/checkout@v3
# step 2. 更新相关依赖
- name: Space cleanup
run: |
sudo -E apt-get update -y
sudo -E apt-get full-upgrade -y
sudo -E apt-get install -y ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
bzip2 ccache cmake cpio curl device-tree-compiler fastjar flex gawk gettext gcc-multilib g++-multilib \
git gperf haveged help2man intltool libc6-dev-i386 libelf-dev libfuse-dev libglib2.0-dev libgmp3-dev \
libltdl-dev libmpc-dev libmpfr-dev libncurses5-dev libncursesw5-dev libpython3-dev libreadline-dev \
libssl-dev libtool lrzsz mkisofs msmtp ninja-build p7zip p7zip-full patch pkgconf python2.7 python3 \
python3-pyelftools python3-setuptools qemu-utils rsync scons squashfs-tools subversion swig texinfo \
uglifyjs upx-ucl unzip vim wget xmlto xxd zlib1g-dev
sudo -E apt-get clean
sudo timedatectl set-timezone "$TZ"
# step 3. 克隆仓库
- name: Clone openWrt
run: |
git clone $REPO_URL openWrt
# step 4. 更新 feeds
- name: Update feeds
run: |
cd openWrt
./scripts/feeds update -a
./scripts/feeds install -a
# step 5. 生成配置文件
- name: Generate config file
run: |
cd openWrt
rm -f ./.config*
touch ./.config
# 在 cat >> .config <<EOF 到 EOF 之间粘贴你的编译配置, 需注意缩进关系, 例如:
cat >> .config <<EOF
# RaspberryPi 4B 配置
CONFIG_TARGET_bcm27xx=y
CONFIG_TARGET_bcm27xx_bcm2711=y
CONFIG_TARGET_bcm27xx_bcm2711_DEVICE_rpi-4=y
# Set Firmware Size
CONFIG_TARGET_KERNEL_PARTSIZE=64
CONFIG_TARGET_ROOTFS_PARTSIZE=960
# Firmware Type
CONFIG_TARGET_ROOTFS_TARGZ=y
# CONFIG_TARGET_ROOTFS_EXT4FS is not set
CONFIG_TARGET_ROOTFS_SQUASHFS=y
EOF
# ===============================================================
sed -i 's/^[ \t]*//g' ./.config # 删除文件中每行的所有前导空格
make defconfig
# step 6. 编译
- name: Make download
run: |
cd openWrt
make download -j8
make V=s -j8
cd bin/targets/bcm27xx/bcm2711 && ls -n
# step 7. 创建 Release & 上传资源
- name: Upload release
uses: actions/github-script@v3
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs');
const path = require('path');
const { repo: { owner, repo }, sha } = context;
console.log({ owner, repo, sha });
const release = await github.repos.createRelease({
owner, repo,
draft: true,
target_commitish: sha,
tag_name: process.env.GITHUB_REF,
});
for (let file of await fs.readdirSync('./openWrt/bin/targets/bcm27xx/bcm2711')) {
const filePath = `./openWrt/bin/targets/bcm27xx/bcm2711/${file}`
if (/\.gz$/.test(file)) {
await github.repos.uploadReleaseAsset({
repo,
owner,
name: file,
release_id: release.data.id,
data: await fs.readFileSync(filePath)
});
}
}
顺便看下工作流的执行情况吧:
看下创建的 Release
:
五、参考
转载自:https://juejin.cn/post/7373961220582522932