Compare commits

..

12 Commits

Author SHA1 Message Date
2d83b552c7 Cleanup usage() 2026-05-12 10:43:23 -04:00
ec8fb8f960 r4s: remove very erroneous /mnt/backup files dir 2026-05-11 21:57:10 -04:00
2ce592b693 Document profiles keys 2026-05-11 21:29:53 -04:00
4d70450fe8 Fix files overlay and flash_images() 2026-05-11 21:29:19 -04:00
2245e7feef r4s: add luci-app-sqm 2026-05-11 21:28:15 -04:00
f5f32ed8d4 Pass ROOTFS_PARTSIZE to IB 2026-05-11 19:12:42 -04:00
0485481023 r4s: remove irqbalance 2026-05-11 18:13:26 -04:00
dea1912670 Update print_help() 2026-05-08 01:20:55 -04:00
e402c76da1 Bump default openwrt release 2026-05-08 01:19:05 -04:00
75aee07a26 Bump default openwrt release 2026-03-27 10:20:55 -04:00
c508654e79 w1700K: use official airoha 2026-03-18 11:39:43 -04:00
b9701e3ab6 Update default openwrt release 2026-03-18 11:18:07 -04:00
3 changed files with 110 additions and 65 deletions

View File

@@ -41,12 +41,37 @@ See `profiles` for example device profile definitions. Multiple `--profile` can
The default build mode is `imagebuilder` unless `--mode=source` is passed. Default profile modes can be set individually in `profiles`. The default build mode is `imagebuilder` unless `--mode=source` is passed. Default profile modes can be set individually in `profiles`.
`--mode=imagebuilder` inherits `CONFIG_TARGET_ROOTFS_PARTSIZE=`, but all other kconfigs only work in `--mode=source`.
Profile keys:
| Key | Required | Description |
|---|---|---|
| `mode` | No | Build mode for this profile: `imagebuilder` or `source`. CLI `--mode` overrides profile value. |
| `device` | Yes | OpenWrt device/profile id used by build commands (for example `friendlyarm_nanopi-r4s`). |
| `target` | Yes | OpenWrt target/subtarget (for example `rockchip/armv8`, `x86/64`). |
| `filesystem` | No | Root filesystem type (`squashfs`, `ext4`, etc). Defaults to `squashfs`. |
| `packages` | No | Space-separated package list. Prefix with `-` to remove a package in Image Builder or source config generation. |
| `kconfigs` | No | Space-separated Kconfig symbols. In `imagebuilder` mode only `CONFIG_TARGET_ROOTFS_PARTSIZE=<MB>` is used (mapped to `ROOTFS_PARTSIZE`). In `source` mode all entries are written to `.config` seed. |
| `files` | No | Host directory containing custom overlay files. In `imagebuilder` mode this is passed as `FILES=<dir>`. In `source` mode contents are synced into `<build dir>/files/` before build. Defaults to `<buildroot>/src/files`. |
| `cherrypicks` | No | Space-separated entries in `URL@branch:commit` form. Each commit is fetched and cherry-picked in `source` mode. |
| `branches` | No | Space-separated `URL@branch` entries to merge into the source worktree in `source` mode. |
| `release` | No | Default release/ref for the profile (for example `snapshot`, `25.12.3`). CLI `--release` overrides it. |
| `clean` | No | Optional source cleanup step (`clean`, `targetclean`, `dirclean`, `distclean`). CLI `--clean` overrides it. |
| `repo` | No | Extra Image Builder repository line appended to `repositories.conf` before build. |
Notes:
* The profile file uses associative arrays (`declare -Ag name=( [key]="value" ... )`).
* `packages`, `kconfigs`, `cherrypicks`, and `branches` are parsed as scalar whitespace-separated strings.
* If a profile-specific `files` path is configured, it must exist.
## Examples ## Examples
* `openwrtbuilder -p r4s -p ax6000` * `openwrtbuilder -p r4s -p ax6000`
* `openwrtbuilder -p r4s -r snapshot --debug` * `openwrtbuilder -p r4s -r snapshot --debug`
* `openwrtbuilder -p ax6000 -r 23.05.5 --mode source --debug` * `openwrtbuilder -p ax6000 -r 25.12.3 --mode source --debug`
* `openwrtbuilder -p rpi4 -r 23.05.5 --flash /dev/sdX` * `openwrtbuilder -p rpi4 -r 25.12.3 --flash /dev/sdX`
* `openwrtbuilder -p linksys -r snapshot --ssh-upgrade root@192.168.1.1` * `openwrtbuilder -p linksys -r snapshot --ssh-upgrade root@192.168.1.1`
## Additional Info ## Additional Info

View File

@@ -1,25 +1,25 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Build and deploy OpenWRT images using shell-style device profiles, via source code or the official Image Builder. # Build and deploy OpenWRT images using shell-style device profiles, via source code or the official Image Builder.
# Copyright 2022-25 Bryan C. Roessler # Copyright 2022-26 Bryan C. Roessler
# Apache 2.0 License # Apache 2.0 License
# See README and ./profiles for device configuration # See README and ./profiles for device configuration
# Set default release # Set default release
: "${DEFAULT_RELEASE:=${RELEASE:="24.10.5"}}" : "${DEFAULT_RELEASE:=${RELEASE:="25.12.3"}}" # do find all replace
# @internal # @internal
print_help() { usage() {
debug "${FUNCNAME[0]}" debug "${FUNCNAME[0]}"
cat <<-'EOF' cat <<-'EOF'
Build and deploy OpenWRT images using convenient profiles. Build and deploy OpenWRT images using convenient profiles.
USAGE: USAGE:
openwrtbuilder [OPTION [VALUE]]... -p PROFILE [-p PROFILE]... openwrtbuilder [OPTIONS] [-p PROFILE]...
OPTIONS OPTIONS
--profile,-p PROFILE --profile,-p PROFILE
--release,-r,--version,-v RELEASE ("snapshot", "24.10.5") --release,-r,--version,-v RELEASE ("snapshot", "25.12.3")
Default: From profile or hardcoded RELEASE Default: From profile or hardcoded RELEASE
--buildroot,-b PATH --buildroot,-b PATH
Default: location of openwrtbuilder script Default: location of openwrtbuilder script
@@ -44,8 +44,8 @@ print_help() {
EXAMPLES EXAMPLES
openwrtbuilder -p r4s -r snapshot openwrtbuilder -p r4s -r snapshot
openwrtbuilder -p ax6000 -r 23.05.0-rc3 --source --debug openwrtbuilder -p ax6000 -r 23.05.0-rc3 --mode source --debug
openwrtbuilder -p rpi4 -r 24.10.0 --flash /dev/sdX openwrtbuilder -p rpi4 -r 25.12.3 --flash /dev/sdX
openwrtbuilder -p linksys -r snapshot --ssh-upgrade root@192.168.1.1 openwrtbuilder -p linksys -r snapshot --ssh-upgrade root@192.168.1.1
EOF EOF
} }
@@ -66,7 +66,7 @@ init() {
else else
echo "/etc/os-release not found" echo "/etc/os-release not found"
echo "Your OS is unsupported" echo "Your OS is unsupported"
print_help usage
exit 1 exit 1
fi fi
@@ -149,14 +149,14 @@ parse_input() {
--depends) FORCE_DEPENDS=1 ;; --depends) FORCE_DEPENDS=1 ;;
--yes|-y) YES=1 ;; --yes|-y) YES=1 ;;
--debug|-d) echo "Debugging on"; DEBUG=1 ;; --debug|-d) echo "Debugging on"; DEBUG=1 ;;
--help|-h) print_help; exit 0 ;; --help|-h) usage; exit 0 ;;
--) shift; break ;; --) shift; break ;;
esac esac
shift shift
done done
else else
echo "Incorrect options provided" echo "Incorrect options provided"
print_help; exit 1 usage; exit 1
fi fi
} }
@@ -175,7 +175,7 @@ install_dependencies() {
lock_file="$BUILD_ROOT/.dependencies_imagebuilder.lock" lock_file="$BUILD_ROOT/.dependencies_imagebuilder.lock"
fi fi
[[ -f $lock_file ]] && debug "$lock_file lock file exists but skipping for --debug" && return 0 [[ -f $lock_file ]] && debug "$lock_file lock file exists, skipping dependency install" && return 0
if [[ "$mode" == "source" ]]; then if [[ "$mode" == "source" ]]; then
# For building from source code see: # For building from source code see:
@@ -403,31 +403,51 @@ ssh_backup() {
[[ -d "$FILES_DIR" ]] || execute mkdir -p "$FILES_DIR" [[ -d "$FILES_DIR" ]] || execute mkdir -p "$FILES_DIR"
# Make backup archive on remote # Make backup archive on remote
if ! execute "ssh -t $SSH_BACKUP_PATH sysupgrade -b /tmp/$backup_fname"; then if ! execute ssh -t "$SSH_BACKUP_PATH" sysupgrade -b "/tmp/$backup_fname"; then
echo "SSH backup failed" echo "SSH backup failed"
exit 1 exit 1
fi fi
# Move backup archive locally # Move backup archive locally
if ! execute "rsync -avz --remove-source-files $SSH_BACKUP_PATH:/tmp/$backup_fname $BUILD_DIR/"; then if ! execute rsync -avz --remove-source-files "$SSH_BACKUP_PATH:/tmp/$backup_fname" "$BUILD_DIR/"; then
echo "Could not copy SSH backup" echo "Could not copy SSH backup"
exit 1 exit 1
fi fi
# Extract backup archive # Extract backup archive
if ! execute "tar -C $FILES_DIR -xzf $BUILD_DIR/$backup_fname"; then if ! execute tar -C "$FILES_DIR" -xzf "$BUILD_DIR/$backup_fname"; then
echo "Could not extract SSH backup" echo "Could not extract SSH backup"
exit 1 exit 1
fi fi
execute "rm $BUILD_DIR/$backup_fname" execute rm "$BUILD_DIR/$backup_fname"
} }
make_images() { make_images() {
debug "${FUNCNAME[0]}" debug "${FUNCNAME[0]}"
local -a make_opts local -a make_opts; ((DEBUG)) && make_opts+=("V=sc")
local rootfs_partsize
# Image Builder accepts ROOTFS_PARTSIZE, not CONFIG_TARGET_ROOTFS_PARTSIZE.
# Parse profile KCONFIGS (scalar string) and extract only this value.
for kconfig in $KCONFIGS; do
case "$kconfig" in
CONFIG_TARGET_ROOTFS_PARTSIZE=*) rootfs_partsize="${kconfig#*=}" ;;
esac
done
local -a make_cmd=(make "${make_opts[@]}" image
BIN_DIR="$BIN_DIR"
PROFILE="$DEVICE"
PACKAGES="$PACKAGES"
FILES="$FILES_DIR"
--directory="$BUILD_DIR"
--jobs="$JOBS")
[[ -n "$rootfs_partsize" ]] && make_cmd+=("ROOTFS_PARTSIZE=$rootfs_partsize")
# Reuse the existing output # Reuse the existing output
# TODO Disable for now since it was causing issues
# if [[ -d "$BIN_DIR" ]]; then # if [[ -d "$BIN_DIR" ]]; then
# if ask_ok "$BIN_DIR exists. Rebuild?"; then # if ask_ok "$BIN_DIR exists. Rebuild?"; then
# execute rm -rf "$BIN_DIR" # execute rm -rf "$BIN_DIR"
@@ -436,21 +456,10 @@ make_images() {
# fi # fi
# fi # fi
((DEBUG)) && make_opts+=("V=sc") # Debug manually so we can log output
debug "${make_cmd[*]}"
debug make "${make_opts[@]}" image BIN_DIR="$BIN_DIR" \ "${make_cmd[@]}" 2>&1 | tee "$BUILD_DIR/make.log"
PROFILE="$DEVICE" PACKAGES="$PACKAGES" \ return "${PIPESTATUS[0]}" # bash-specific way to return exit code of piped command
FILES="$FILES_DIR" --directory="$BUILD_DIR" \
--jobs="$JOBS"
make "${make_opts[@]}" image \
BIN_DIR="$BIN_DIR" \
PROFILE="$DEVICE" \
PACKAGES="$PACKAGES" \
FILES="$FILES_DIR" \
--directory="$BUILD_DIR" \
--jobs="$JOBS" \
> "$BUILD_DIR/make.log"
} }
flash_images() { flash_images() {
@@ -458,9 +467,8 @@ flash_images() {
local img_gz="$1" local img_gz="$1"
local dev="$2" local dev="$2"
local img="${img_gz%.gz}" local img="${img_gz%.gz}"
local partitions
if [[ ! -e "$dev" ]]; then if [[ ! -b "$dev" ]]; then
echo "The device specified by --flash could not be found" echo "The device specified by --flash could not be found"
return 1 return 1
fi fi
@@ -468,13 +476,13 @@ flash_images() {
[[ -f $img_gz ]] || { echo "$img_gz does not exist"; return 1; } [[ -f $img_gz ]] || { echo "$img_gz does not exist"; return 1; }
execute gunzip -qfk "$img_gz" execute gunzip -qfk "$img_gz"
[[ -f "$img" ]] || { echo "Extracted image '$img' is missing"; return 1; }
echo "Unmounting target device $dev partitions"
partitions=("$dev"?*)
execute sudo umount "${partitions[@]}"
if execute sudo dd if="$img" of="$dev" bs=2M conv=fsync; then if execute sudo dd if="$img" of="$dev" bs=2M conv=fsync; then
sync sync
if command -v partprobe &>/dev/null; then
execute sudo partprobe "$dev" || debug "partprobe failed for $dev"
fi
echo "Image flashed successfully!" echo "Image flashed successfully!"
else else
echo "dd failed!" echo "dd failed!"
@@ -504,15 +512,14 @@ ssh_upgrade() {
} }
# @description Builds OpenWRT from source code using the the default buildbot as base # @description Builds OpenWRT from source code using the the default buildbot as base
# This enables the use of kernel config options in profiles
from_source() { from_source() {
debug "${FUNCNAME[0]}" debug "${FUNCNAME[0]}"
local src_url="https://github.com/openwrt/openwrt.git" local src_url="https://github.com/openwrt/openwrt.git"
local seed_file="$BUILD_DIR/.config" local seed_file="$BUILD_DIR/.config"
local worktree_meta="$SRC_DIR/.git/worktrees/source-$REF" local worktree_meta="$SRC_DIR/.git/worktrees/source-$REF"
local pkg config commit description local pkg kconfig commit description
local -a make_opts local -a make_opts
local -a config_opts=( local -a kconfigs=(
"CONFIG_TARGET_${TARGET%%/*}=y" "CONFIG_TARGET_${TARGET%%/*}=y"
"CONFIG_TARGET_${TARGET//\//_}=y" "CONFIG_TARGET_${TARGET//\//_}=y"
"CONFIG_TARGET_PROFILE=DEVICE_$DEVICE" "CONFIG_TARGET_PROFILE=DEVICE_$DEVICE"
@@ -667,25 +674,33 @@ from_source() {
./scripts/feeds update -a -f && ./scripts/feeds update -a -f &&
./scripts/feeds install -a -f ./scripts/feeds install -a -f
# Apply custom files overlay for source builds.
execute rm -rf "$BUILD_DIR/files"
if [[ -d "$FILES_DIR" ]]; then
execute mkdir -p "$BUILD_DIR/files"
execute rsync -a "$FILES_DIR/" "$BUILD_DIR/files/"
fi
# Add custom packages # Add custom packages
for pkg in $PACKAGES; do for pkg in $PACKAGES; do
if [[ $pkg == -* ]]; then if [[ $pkg == -* ]]; then
config_opts+=("CONFIG_PACKAGE_${pkg#-}=n") # remove package kconfigs+=("CONFIG_PACKAGE_${pkg#-}=n") # remove package
else else
config_opts+=("CONFIG_PACKAGE_$pkg=y") # add package kconfigs+=("CONFIG_PACKAGE_$pkg=y") # add package
fi fi
done done
# Add profile config options # Add profile kconfig options
for config in $CONFIGS; do # $KCONFIGS is a scalar, use a loop to split and avoid SC2068
config_opts+=("$config") for kconfig in $KCONFIGS; do
kconfigs+=("$kconfig")
done done
# Reset and write options to config seed file # Reset and write options to config seed file
[[ -f $seed_file ]] && execute rm -f "$seed_file" [[ -f $seed_file ]] && execute rm -f "$seed_file"
for config in "${config_opts[@]}"; do for kconfig in "${kconfigs[@]}"; do
debug "Writing $config to $seed_file" debug "Writing $kconfig to $seed_file"
echo "$config" >> "$seed_file" echo "$kconfig" >> "$seed_file"
done done
# Expand seed into full config # Expand seed into full config
@@ -766,7 +781,6 @@ main() {
# Fallback to SCRIPT_DIR if BUILD_ROOT has not been set # Fallback to SCRIPT_DIR if BUILD_ROOT has not been set
declare -g BUILD_ROOT="${BUILD_ROOT:=$SCRIPT_DIR}" declare -g BUILD_ROOT="${BUILD_ROOT:=$SCRIPT_DIR}"
declare -g FILES_DIR="${FILES_DIR:=$BUILD_ROOT/src/files}"
declare -g BACKUP_DIR="$SCRIPT_DIR/backups" declare -g BACKUP_DIR="$SCRIPT_DIR/backups"
# This could be dangerous # This could be dangerous
@@ -813,7 +827,16 @@ main() {
declare -g PACKAGES="${P_ARR[packages]:-}" # scalar declare -g PACKAGES="${P_ARR[packages]:-}" # scalar
declare -g CHERRYPICKS="${P_ARR[cherrypicks]:-}" # scalar declare -g CHERRYPICKS="${P_ARR[cherrypicks]:-}" # scalar
declare -g BRANCHES="${P_ARR[branches]:-}" # scalar declare -g BRANCHES="${P_ARR[branches]:-}" # scalar
declare -g CONFIGS="${P_ARR[configs]:-}" # scalar declare -g KCONFIGS="${P_ARR[kconfigs]:-}" # scalar
declare -g FILES_DIR="${P_ARR[files]:-$BUILD_ROOT/src/files}"
if [[ ! -d "$FILES_DIR" ]]; then
if [[ -v P_ARR[files] ]]; then
echo "Profile '$PROFILE' files directory does not exist: $FILES_DIR"
return 1
fi
execute mkdir -p "$FILES_DIR"
fi
install_dependencies "$MODE" install_dependencies "$MODE"

View File

@@ -2,7 +2,7 @@
# Device profiles for openwrtbuilder # Device profiles for openwrtbuilder
# shellcheck disable=SC2034 # shellcheck disable=SC2034
DEFAULT_RELEASE="25.12.0-rc4" # overrides default release in openwrtbuilder DEFAULT_RELEASE="25.12.3" # overrides default release in openwrtbuilder
# Default packages (precede with "-" to exclude) # Default packages (precede with "-" to exclude)
default_packages=( default_packages=(
@@ -13,7 +13,7 @@ default_packages=(
) )
# Default kernel configs # Default kernel configs
default_configs=( default_kconfigs=(
) )
# Current devices # Current devices
@@ -24,14 +24,13 @@ declare -Ag r4s=(
[filesystem]="ext4" [filesystem]="ext4"
[packages]="${default_packages[*]} \ [packages]="${default_packages[*]} \
adblock luci-app-adblock \ adblock luci-app-adblock \
irqbalance luci-app-irqbalance \ luci-app-sqm \
collectd-mod-df usbutils kmod-usb-storage kmod-usb-storage-uas \ collectd-mod-df usbutils kmod-usb-storage kmod-usb-storage-uas \
kmod-fs-btrfs btrfs-progs block-mount smcroute avahi-daemon \ kmod-fs-btrfs btrfs-progs block-mount smcroute avahi-daemon \
ethtool ca-bundle tailscale" ethtool ca-bundle tailscale"
[configs]="${default_configs[*]} \ [kconfigs]="${default_kconfigs[*]} \
CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \ CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32 \
CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32" CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y"
[files]="/mnt/backup"
# For 24.10 branch (Linux 6.6) # For 24.10 branch (Linux 6.6)
# [cherrypicks]="https://github.com/wurzerj/openwrt.git:59d6e31 \ # [cherrypicks]="https://github.com/wurzerj/openwrt.git:59d6e31 \
# https://github.com/wurzerj/openwrt.git:bb251b8" # fix inconsistent reboot # https://github.com/wurzerj/openwrt.git:bb251b8" # fix inconsistent reboot
@@ -46,7 +45,7 @@ declare -Ag ax6000=(
tailscale" tailscale"
) )
declare -Ag ax6000_uboot=( declare -Ag ax6000_uboot_ap=(
[mode]="imagebuilder" [mode]="imagebuilder"
[device]="xiaomi_redmi-router-ax6000-ubootmod" [device]="xiaomi_redmi-router-ax6000-ubootmod"
[target]="mediatek/filogic" [target]="mediatek/filogic"
@@ -66,10 +65,9 @@ declare -Ag n5100=(
btrfs-progs block-mount cryptsetup kmod-crypto-xts smcroute \ btrfs-progs block-mount cryptsetup kmod-crypto-xts smcroute \
avahi-daemon ethtool ca-bundle smartmontools intel-microcode \ avahi-daemon ethtool ca-bundle smartmontools intel-microcode \
lm-sensors samba4-server luci-app-samba4 tailscale shadow-useradd" lm-sensors samba4-server luci-app-samba4 tailscale shadow-useradd"
[configs]="${default_configs[*]} \ [kconfigs]="${default_kconfigs[*]} \
CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \ CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \
CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32" CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32"
# [files]="/mnt/backup"
) )
declare -Ag w1700k=( declare -Ag w1700k=(
@@ -97,7 +95,7 @@ declare -Ag w1700k=(
# https://github.com/OpenWRT-fanboy/OpenW1700k.git@lumos:9841a707a577385498591bcfb56b836176325c2f \ # https://github.com/OpenWRT-fanboy/OpenW1700k.git@lumos:9841a707a577385498591bcfb56b836176325c2f \
# https://github.com/OpenWRT-fanboy/OpenW1700k.git@lumos:8446ec6431a3247683a27070d3c69f2789b52c70 \ # https://github.com/OpenWRT-fanboy/OpenW1700k.git@lumos:8446ec6431a3247683a27070d3c69f2789b52c70 \
# https://github.com/OpenWRT-fanboy/OpenW1700k.git@lumos:05380e2ef5fb96c171da23453ba32aa349a4b126" # https://github.com/OpenWRT-fanboy/OpenW1700k.git@lumos:05380e2ef5fb96c171da23453ba32aa349a4b126"
[branches]="https://github.com/OpenWRT-fanboy/OpenW1700k.git@minimal" # [branches]="https://github.com/OpenWRT-fanboy/OpenW1700k.git@minimal"
) )
declare -Ag w1700k_ap=( declare -Ag w1700k_ap=(
@@ -108,10 +106,9 @@ declare -Ag w1700k_ap=(
[release]="snapshot" [release]="snapshot"
[packages]="${default_packages[*]} \ [packages]="${default_packages[*]} \
-dnsmasq -odhcpd-ipv6only -nftables -firewall4 \ -dnsmasq -odhcpd-ipv6only -nftables -firewall4 \
irqblance luci-app-irqbalance \
wpad-openssl libiwinfo-data tailscale bridger switch smp_util \ wpad-openssl libiwinfo-data tailscale bridger switch smp_util \
kmod-crypto-hw-eip93" kmod-crypto-hw-eip93"
[branches]="https://github.com/OpenWRT-fanboy/OpenW1700k.git@minimal" # [branches]="https://github.com/OpenWRT-fanboy/OpenW1700k.git@minimal"
) )
declare -Ag rpi4=( declare -Ag rpi4=(