基于 ImmortalWrt master 的通用固件构建基座。它负责公共构建逻辑、软件包选择和 CI;设备仓库通过 rebase 继承本仓库,并只维护设备差异。实际构建提交由 upstream.lock 固定,不会在普通构建时跟随 master 漂移。
- Base:
build.sh、DIY 脚本、通用scripts/、Docker 和 GitHub Actions。Base 不包含files/。 - 设备仓库:
config.seed、openwrt-device.conf、files/、端口映射、恢复脚本和硬件专用模块。 - 同步方式:设备仓库执行
git fetch base main && git rebase base/main,解决冲突时保留设备参数,不把设备配置回流到 Base。 - 构建输入:只从干净且已提交的工作树构建,避免无法复现的本地试验进入固件。
- 上游边界:不修改 ImmortalWrt 源码。只有完成候选版本的配置解析和固件验证后,才更新
upstream.lock;设备仓库随 Base rebase 继承同一提交。 - 通用组件:MMC/SDHCI、i915 GuC/HuC、Intel HDA 等通用覆盖由 Base 统一维护,设备仓库只追加硬件专用模块。
build.sh 是唯一的构建编排入口,流程如下:
- 读取
upstream.lock固定 ImmortalWrt 源码版本并恢复可复用缓存。 - 更新 feeds,运行
diy-part1.sh获取自定义软件包,只注册config.seed请求的软件包及 feeds 自动解析出的依赖。 - 两次解析
config.seed:先验证 feed 依赖,再验证 DIY 新增的软件包和设备 overlay。 - 并行下载、并行编译,并在失败时保存完整日志。
- 导出镜像、manifest、展开后的配置和 MD5;验证 SquashFS 与 LuCI 静态资源后才视为成功。
日常构建不更新上游提交。升级时先在 Base 修改 upstream.lock 为候选 ImmortalWrt commit,完成配置解析、Base 构建和至少一个设备固件验证后再提交;随后将 Base rebase 到各设备仓库并逐个构建。构建环境变量 REPO_COMMIT 仅用于这种候选验证,不能替代提交锁定值。
merge_upstream.yml 保留“上游有更新则自动 rebase 到下游”的设计:Base 每 8 小时检查 P3TERX/Actions-OpenWrt 的 main,也可在 GitHub Actions 手动触发。没有新提交时直接跳过,不产生无意义的历史重写;有更新时执行 git rebase upstream/main,随后用 git push --force-with-lease 推送。冲突时工作流失败并保留人工处理,不允许用 merge=ours 静默覆盖。因为这是历史重写,任何本地克隆在自动同步后都必须重新 fetch 并对齐 origin/main,不要基于旧历史继续提交。
Docker 容器以宿主 UID/GID 运行,避免 bind mount 中的产物变成 nobody。首次构建使用当前 Dockerfile 创建镜像;Dockerfile 更新后需显式重建镜像。
cd docker
docker compose up buildDockerfile 更新后:
cd docker
docker compose up --build build仅在工具链损坏或构建路径改变后清理陈旧产物:
cd docker
docker compose up clean可选的持久缓存会复用 dl/、staging_dir/ 和 build_dir/:
mkdir -p /data/build-cache
docker compose run --rm \
-e BUILD_CACHE_DIR=/workspace/cache \
-v /data/build-cache:/workspace/cache \
build构建产物位于 release/:
<设备名>.img.gz:刷写镜像。<设备名>.manifest:实际写入镜像的软件包清单。config.buildinfo和<设备名>.target.config.buildinfo:展开后的构建配置。.md5:压缩镜像和解压后镜像的校验值。
| 文件 | 职责 |
|---|---|
config.seed |
公共软件包选择模板;设备仓库可在此添加硬件驱动。 |
diy-part1.sh |
feeds 更新前获取第三方软件包和源码。 |
diy-part2.sh |
feeds 安装后设置默认配置、软件包覆盖和构建元数据。 |
scripts/ |
可复用的构建、验证和设备运行时辅助脚本。 |
openwrt-device.conf |
仅设备仓库维护的发布名、磁盘、版本和私有运行参数。 |
files/ |
仅设备仓库维护的 rootfs overlay,按原路径写入镜像。 |
当前基座选择 CONFIG_USE_APK=y,目标系统使用 apk,不包含旧版 opkg。上游 default-settings-chn 首启时会设置 system.@imm_init[0].apk_mirror,并据此重写自动生成的 /etc/apk/repositories.d/distfeeds.list;不要手改该文件。在线安装前先检查该 UCI 值和 distfeeds.list,自定义源放入 customfeeds.list。
diy-part1.sh 查询 PikuZheng SmartDNS 最新的非预发布 _with_ui Release,只接受与该 tag 精确对应的 x86_64 UI 包,并以同一 tag 固定源码。UI 下载会重试瞬态网络错误;只有元数据、下载或包校验最终失败时,才回退到 ImmortalWrt feed 的 smartdns、smartdns-ui 和 luci-app-smartdns。构建后以 release manifest 核对实际打入的版本。
每次变更后至少完成以下检查:
git diff --check
find . -path './openwrt' -prune -o -name '*.sh' -type f -print0 | xargs -0 -r -n1 bash -n
docker compose -f docker/docker-compose.yml config >/dev/null固件构建成功后,scripts/verify_firmware.sh 会验证:选中软件包是否出现在 manifest、压缩镜像是否完整、SquashFS 是否可完整提取,以及 LuCI 所需资源是否存在。刷机前仍应结合目标设备日志、网络接口和关键服务做实际验证。
GitHub Actions 调用同一份 build.sh,不复制构建逻辑。CI 只读取 openwrt-device.conf 中格式受限的 RELEASE_NAME,不会打印或 source 整个设备配置,从而避免私有运行参数进入日志。