/openwrt-fastbuild-actions

FAST building OpenWrt with Github Actions and Docker!

Primary LanguageShellOtherNOASSERTION

Building OpenWrt with GitHub Actions and Docker

I largely optimized the main building process. It is now easier to use.

This project is inspired by P3TERX's Actions-Openwrt.

With Github Actions and Actions-Openwrt, it is easy to build an OpenWrt firmware without running locally. However, Github Actions do not store cache and building files. This means it has to completely rebuild from source each time, even if it is a small change.

This project uses Docker Hub or any Docker registriy for storing previous building process, allowing incremental building.

Github Actions和Actions-Openwrt让我们可以很方便地自动化编译OpenWrt固件,而不必在本地编译。然而Github Actions不存储缓存,已编译过的文件也不会在下次编译重新被使用。这就意味着,即便只是很小的改动,每次编译我们要等上很久来重新编译整个固件。

本项目使用Docker Hub或任何Docker Registry存储编译状态,使得后续的编译可以增量进行。

Features 特点

  • Load and save building state to Docker Hub or other registries
  • Support building options
  • Various trigger methods
  • Two building modes (before colons are job names of Github Actions)
    • build-inc: Incrementally building firmware and packages (every push, about 40 minutes for standard config, about 3 hours for first-time building)
    • build-package: Incrementally building only packages (every push, about 25 minutes for standard config, useful when only enabling a package module)

  • 在Docker Hub或其他Registry加载和存储OpenWrt编译状态
  • 支持编译选项
  • 多种触发模式
  • 两个编译模式(冒号前是Github Actions中的job名称)
    • build-inc:增量编译固件和软件包(每次push自动进行,标准配置下大约每次40分钟,第一次编译时耗时大约3小时)
    • build-package:增量编译软件包(每次push自动进行,标准配置下大约每次25分钟,当仅需要编译软件安装包时比较有用)

Usage 用法

The default configuration uses coolsnowwolf/lede as the OpenWrt Repo (popular in China). If you want official OpenWrt 19.07, check out "openwrt_official" branch. (It's just changes of REPO_URL and REPO_BRANCH envs in .github/workflows/build-openwrt.yml.)

Check out my own configuration in "sample" branch.

Basic usage 基础用法

First-time building 第一次编译

The building process generally takes 1.5~3 hours depending on your config.

  1. Sign up for GitHub Actions
  2. Fork this repo
  3. Register a Docker Hub account. This is necessary.
  4. Get your Docker Hub personal access token. Fill your username and the generated token into the forked repo's Settings->Secrets page. Use docker_username for your username, and docker_password for your token. See Secrets page for correct settings.
  5. (Optional, not very useful) If you want the debug SSH command to be sent to Slack, you can generate a Slack Webhook URL and set the url as SLACK_WEBHOOK_URL in the Secrets page. Search in Google if you don't know how to do it.
  6. (Optional) Customize .github/workflows/build-openwrt.yml to change builder's name and other options.
  7. Generate your .config and rename it to config.diff. Put the file in the root dir of your forked repo.
  8. (Optional) Customize scripts/update_feeds.sh for additional packages you want to download.
  9. (Optional) Put any patch you want to patches dir. The patches are applied after update_feeds.sh and before download.sh.
  10. Commit and push your changes. This will automatically trigger an incremental building.
  11. Wait for build-inc job to finish.
  12. Collect your files in the build-inc job's Artifacts menu

  1. 注册GitHub Actions
  2. Fork
  3. 注册Docker Hub. 这步很重要
  4. 取得Docker Hub的personal access token。在你自己Fork的Repo中的Settings->Secrets页面填写你的Docker Hub用户名和token。使用“docker_username”填写用户名,使用“docker_password”填写token。详见Secrets page
  5. (可选,没什么用) 如果你想自动把SSH命令发送到Slack,你可以在Secrets页面设置SLACK_WEBHOOK_URL。详细方法请自行Google。
  6. (可选) 定制.github/workflows/build-openwrt.yml以修改你想在Docker Hub保存的builder名和其他选项
  7. 生成你的.config文件,并把它重命名为config.diff。把它放在根目录。
  8. (可选) 如果你想放置额外安装包,定制scripts/update_feeds.sh
  9. (可选) 在patches目录放置补丁文件。补丁会自动在update_feeds.sh之后,download.sh之前执行。
  10. Commit并Push。这一步骤会自动触发编译。
  11. 等待build-inc任务完成。
  12. build-inc任务的Artifacts目录下载编译好的文件。
Secrets page

Secrets page

Following building 后续编译

After the first-time building, you will only need the following steps to build your firmwares and packages when you change your config. The building process generally only takes 20 minutes ~ 1 hour depending on how much your config has changed.

  1. (Optional) Modify your config.diff if you want.
  2. (Optional) Customize scripts/update_feeds.sh for additional packages you want to download.
  3. (Optional) Put any patch you want to patches dir.
  4. Commit and push your changes. If you want to do build-inc, you don't need any special step. If you need build-package, you can include this string in your last commit message before push: #build-package#.
  5. Wait for build-inc or build-package to finish
  6. Collect your files in the build-inc or build-package job's "Artifacts" menu

  1. (可选) 根据需要修改你的config.diff
  2. (可选) 根据需要修改你的scripts/update_feeds.sh
  3. (可选) 根据需要添加新的补丁至patches目录
  4. Commit并Push。如果你想执行build-inc任务,你不需要进行任何特殊操作。如果你需要执行build-package,你可以在Push前的最后一个commit message中包含这一字符串:#build-package#
  5. 等待build-incbuild-package完成
  6. 在“Artifacts”目录收集文件

Advanced usage 高级用法

The following contents require your understanding of the mechanism. See Mechanism.

Re-create your building environment 重建编译环境

If you have largely modified your configurations, incremental building may fail as there could be old configurations remained. It's better to completely re-create your builder. You can specify the rebuild building option to achieve this. For usage of building options, refer to Manually trigger building and its options.

Technically, this is to "re-create your base builder". For definition of "base builder", refer to Mechanism.

Rebase your building environment 重设编译环境

Sometimes, you don't need to re-create the base builder. You just need to link previous base builder to current "incremental builder".

Because the incremental builders used in build-inc and build-package jobs are reusing previous building state, the builder image may grow larger and larger. The builder itself could also fall into some error state. If so, you can re-link them from the base builder.

For rebase build-inc, you can use the use_base option to base it on the base builder.

For rebase build-package, you can use the use_base option to base it on the base builder, or you can use the use_inc option to base it on previous incremental builder used in build-inc.

Manually trigger building and its options

There are two methods for manually triggering and specifying building options:

  • Use tete1030/github-repo-dispatcher.
    • Repository dispatch event ("Repo Dispatch" button, only support "master" branch)
      • Specify your job name in the "Type" prompt
      • Fill your options in the "Payload" prompt (in JSON), or leave "Payload" empty when no option is needed
    • Deployment event ("Deploy" button, support any commit/branch/tag)
      • Specify your job name in the "Task" prompt
      • Specify your branch in the "Ref" prompt
      • Fill your options in the "Payload" prompt (in JSON), or leave "Payload" empty when no option is needed
  • Including your command and options in your commit message (it must be the commit right before the push)
    • You can specify your job name by including a string "#JOB_NAME#" in your latest commit message, e.g. #build-package#
    • You can enable boolean options by including a string "#BOOL_OPTION_NAME#" in your latest commit message, e.g. #debug#
    • You can combine job name and options. e.g. #build-package##debug# or #build-package#debug# are both acceptable (because indexOf(commit_message, '#JOB_OR_OPT#') is used for searching them).

All boolean options are by default false. The following are options available.

Global options
  • debug(bool): entering tmate during and after building, allowing you to SSH into the Actions
  • push_when_fail(bool): always save the builder to Docker Hub even if the building process fails. Not recommended to use
Options only for build-inc
  • rebuild(bool): re-create the building environment completely
  • update_repo(bool): do git pull on main repo. It could fail if any tracked file of the repo has changed.
  • update_feeds(bool): do git pull on feeds and your manually added packages. It could fail if any tracked file changed.
  • use_base(bool): instead of using the job's own previous builder, use latest base builder
Options only for build-package
  • update_feeds(bool): same to previous section
  • use_base(bool): same to previous section
  • use_inc(bool): instead of using the job's own previous builder, use latest incremental builder generated by build-inc
Examples
Use github-repo-dispatcher to rebase building environment

To trigger rebase the incremental builder with SSH debugger enabled:

  1. Open your forked repo
  2. Click "Repo Dispatch" or "Deploy" at the top right corner (install at tete1030/github-repo-dispatcher)
  3. Fill build-inc in "Type/Task" prompt
  4. If using "Deploy" trigger, fill your branch/tag/commit in "Ref" prompt (e.g. master)
  5. Fill {"use_base": true, "debug": true} in "Payload" prompt
  6. Open the job's log page, wait for the SSH command showing up (when debugging, you are allowed to SSH into the job's runner, with the help of tmate.io)
Use commit message to re-create building environment
  1. Save all the files you changed
  2. At the last commit before push, commit with message "some message #build-inc#rebuild# some message"
  3. Push
  4. Wait for jobs to finish

Details

Mechanism

[TODO] Probably a figure is better

For convenience, assume docker image for storing builder

  • IMAGE_NAME=tete1030/openwrt_x86_64 (abbreviated to t/o)
  • IMAGE_TAG=latest

There are three builders:

  • When doing first-time building or rebuilding, the build-inc mode setups "base builder" and builds OpenWrt freshly. It produces a firmware and a "base builder". The builder is named as t/o:latest and stored in Docker Hub. It is further linked to t/o:latest-inc, which is an incremental build used for future use.
  • For every push, the build-inc mode setups "incremental builder t/o:latest-inc" based on its own previous "incremental builder t/o:latest-inc" (same name). It also builds a new firmware. Finally it saves back the new builder to Docker Hub, overwriting the old one.
  • For every push, the build-package mode setups "incremental builder t/o:latest-package" based on its own previous "incremental builder t/o:latest-package" (same name). If the previous one does not exist, it uses the base builder. This mode also builds packages (*.ipkg). Finally it saves back the new builder to Docker Hub, overwriting the old one.

Success building job examples

coolsnowwolf/lede:

openwrt/openwrt;openwrt-19.07:

Building process explained

I'll now explain here the detailed building process of each mode.

build-inc

  1. Pull ${BUILDER_NAME}:${BUILDER_TAG}-inc from Docker Hub. If the tag does not exist or use_base building option is set, link ${BUILDER_NAME}:${BUILDER_TAG} to ${BUILDER_NAME}:${BUILDER_TAG}-inc. If ${BUILDER_NAME}:${BUILDER_TAG} also does not exist, set building option rebuild to true.
  2. initenv.sh, set up building environment only when rebuild.
  3. update_repo.sh. It will do git clone or git pull for main repo only when update_repo or rebuild option is set
  4. update_feeds.sh. It will download you manually added packages, and do git pull for existing packages only when update_feeds option is set.
  5. customize.sh. Apply patches only when a patch has not been already applied. Load config.diff to .config, execute make defconfig
  6. download.sh, download/update package source code that are not already downloaded
  7. compile.sh, Multi/single-thread compile
  8. Save this new builder to Docker Hub's ${BUILDER_NAME}:${BUILDER_TAG}-inc. If rebuild, link it back to ${BUILDER_NAME}:${BUILDER_TAG}
  9. Copy out from docker and upload files to Artifacts
    • OpenWrt_bin: all binaries files, including packages and firmwares
    • OpenWrt_firmware: firmware only

build-package

  1. Pull ${BUILDER_NAME}:${BUILDER_TAG}-package from Docker Hub. If the tag does not exist or when use_base option is set, link current ${BUILDER_NAME}:${BUILDER_TAG} to ${BUILDER_NAME}:${BUILDER_TAG}-package (Or link ${BUILDER_NAME}:${BUILDER_TAG}-inc to ${BUILDER_NAME}:${BUILDER_TAG}-package when the use_inc option is set)
  2. Unlike other building processes, update_repo.sh is not executed
  3. update_feeds.sh. It will always download you manually added packages, and do git pull for existing packages only when update_feeds option is set.
  4. customize.sh, apply patches only when a patch has not been already applied
  5. download.sh, download/update package source code that are not already downloaded
  6. compile.sh, Multi/single-thread compile
  7. Save this new builder to Docker Hub's ${BUILDER_NAME}:${BUILDER_TAG}-package
  8. Upload files to Artifacts
    • OpenWrt_packages: all packages
    • OpenWrt_new_packages: only newly produced packages of this building

FAQs

Why I cannot see any tag on Docker Hub website?

All tags actually exist but could be invisible. Caused by known problem of buildx:

Todo

  • Merge three building modes into one to simplify the process
  • SSH into docker container instead of just the runner (for make menuconfig)
  • Allow customizing trigger event
  • Allow specify building job in commit message
  • Automatically linking from base builder to builders for build-inc and build-package when not existing
  • Optimize README
    • Simplfy documentation
    • Add Chinese version of "Usage"
    • Add a figure to "Mechanism"
    • Describe mechanism
    • Describe building process
    • Describe using tete1030/github-repo-dispatcher to trigger building with extra options
  • Optimize comments in build-openwrt.yml and docker.sh
  • Optimize build-openwrt.yml, making options cleaner
  • Allow deterministic building (by fixing commit of main repo and feeds)

Acknowledgments

License

Most files under

MIT © Texot

Original idea and some files under

MIT © P3TERX