likes
comments
collection
share

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

作者站长头像
站长
· 阅读数 66

引言

但是呢, 重新编译又又又太麻烦了, Mac 本地虽然可以通过 Docker 进行编译, 但是限制了 CPU 类型必须得是 Intel 版本才行, 而目前我常用的 MacARM 版的....

所以下面就介绍一个新的 openWrt 固件编译方案: 通过 GitHub Actions 来自动编译 openWrt 固件, 一次设置, 终身享用

本文仓库地址: MoYuanJun/OpenWrt-RaspberryPi

一、创建个空项目

在开始之前我们需要创建一个 GitHub 仓库, 基于该仓库我们去编写 Actions, 大概步骤如下(简单贴几个图):

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

二、创建工作流

将创建好的项目, 拷贝到本地, 并在项目根目录下创建文件 .github/workflows/Generate.yml

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

补充说明: 这里目录是要求固定的, 文件名可随意, 一个文件代表一个工作流

三、工作流详解

下面就工作流内容部分, 进行简单阐述, 当然你可以直接翻到底部获取完整的内容

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/checkoutGitHub 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 的权限, 设置写入权限。具体设置方法参考下图:

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

下面我们调整工作流配置, 如下配置我们新增步骤七, 这里我们使用了 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)
                }); 
              }
            }

顺便看下工作流的执行情况吧:

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

看下创建的 Release:

GitHub Actions 编译 OpenWrt 固件(树莓派 4B)

五、参考

转载自:https://juejin.cn/post/7373961220582522932
评论
请登录