Compare commits

..

1 Commits

Author SHA1 Message Date
251b54937b Use --detached worktrees 2025-08-05 14:13:27 -04:00
3 changed files with 93 additions and 139 deletions

View File

@@ -13,9 +13,9 @@ Build and deploy OpenWRT images using shell-style device profiles, via source co
--release,-r,--version,-v RELEASE ("snapshot", "22.03.3")
--buildroot,-b PATH (Default: script directory)
--source
Build image from source code, not from Image Builder.
Allows make config options to be passed in profile.
Uses git worktree for multi-profile deduplication.
Build image from source code, not from Image Builder
Allows make config options to be passed in profile
Uses git worktree for multi-profile deduplication
--ssh-upgrade HOST
Example: root@192.168.1.1
--ssh-backup SSH_PATH

View File

@@ -1,11 +1,10 @@
#!/usr/bin/env bash
# Build and deploy OpenWRT images using shell-style device profiles, via source code or the official Image Builder.
# Copyright 2022-25 Bryan C. Roessler
# Build and deploy OpenWRT images using shell-style device profiles, via source code or the official Image Builder.# Copyright 2022-25 Bryan C. Roessler
# Apache 2.0 License
# See README and ./profiles for device configuration
# See README and profiles for device configuration
# Set default release
: "${RELEASE:="24.10.4"}"
: "${RELEASE:="24.10.2"}"
# @internal
print_help() {
@@ -22,16 +21,18 @@ print_help() {
--release,-r,--version,-v RELEASE ("snapshot", "22.03.5")
--buildroot,-b PATH
Default: location of openwrtbuilder script
--source[=CLEAN]
--source
Build image from source, not from Image Builder
Optional CLEAN runs the given clean mode before building
(clean|targetclean|dirclean|distclean)
Allows make config options to be passed in profile
Uses git worktree for multi-profile deduplication
--ssh-upgrade HOST
Examples: root@192.168.1.1, root@router.lan
--ssh-backup SSH_PATH
Enabled by default for --ssh-upgrade
--flash,-f DEVICE
Example: /dev/sdX
--reset
Cleanup all source and output files
--depends
Force dependency installation
--yes,-y
@@ -56,7 +57,7 @@ init() {
# Save the script directory
# https://stackoverflow.com/a/4774063
SCRIPT_DIR=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")
SCRIPT_DIR="$(cd -- "$(dirname "$0")" >/dev/null 2>&1 || exit $? ; pwd -P)"
if [[ -e "/etc/os-release" ]]; then
source "/etc/os-release"
@@ -125,8 +126,8 @@ parse_input() {
debug "${FUNCNAME[0]}" "$*"
declare -ga PROFILES
declare -gi RESET=0 FROM_SOURCE=0 YES=0 DEBUG=0 FORCE_DEPENDS=0
declare -g USER_RELEASE SSH_UPGRADE_PATH SSH_BACKUP_PATH FLASH_DEV SOURCE_CLEAN
local long_opts='release:,version:,profile:,buildroot:,source::,'
declare -g USER_RELEASE SSH_UPGRADE_PATH SSH_BACKUP_PATH FLASH_DEV
local long_opts='release:,version:,profile:,buildroot:,source,'
long_opts+='ssh-upgrade:,ssh-backup:,flash:,reset,depends,yes,debug,help'
if _input=$(getopt -o +r:v:p:b:sf:ydh -l $long_opts -- "$@"); then
@@ -136,12 +137,7 @@ parse_input() {
--release|-r|--version|-v) shift; USER_RELEASE="$1" ;;
--profile|-p) shift; PROFILES+=("$1") ;;
--buildroot|-b) shift; BUILD_ROOT="$1" ;;
--source|-s) FROM_SOURCE=1
case "$1" in
-*|"") ;; # if empty
*) SOURCE_CLEAN="$1"; shift ;;
esac
;;
--source|-s) FROM_SOURCE=1 ;;
--ssh-upgrade) shift; SSH_UPGRADE_PATH="$1" ;;
--ssh-backup) shift; SSH_BACKUP_PATH="$1" ;;
--flash|-f) shift; FLASH_DEV="$1" ;;
@@ -175,7 +171,7 @@ install_dependencies() {
lock_file="$BUILD_ROOT/.dependencies_ib.lock"
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" && return 0
if [[ "$mode" == "source" ]]; then
# For building from source code see:
@@ -189,7 +185,6 @@ install_dependencies() {
gcc
gcc-c++
git
golang
llvm15-libs # for qosify
make
ncurses-devel
@@ -208,7 +203,6 @@ install_dependencies() {
python3-devel
python3-pyelftools
python3-setuptools
quilt
rsync
swig
tar
@@ -227,12 +221,10 @@ install_dependencies() {
gcc-multilib
gettext
git
golang
liblzma-dev
libncurses5-dev
libssl-dev
python3-distutils
quilt
rsync
patch
unzip
@@ -257,7 +249,6 @@ install_dependencies() {
gcc
gettext
git
golang
grep
groff
gzip
@@ -272,7 +263,6 @@ install_dependencies() {
patch
pkgconf
python
quilt
rsync
sed
texinfo
@@ -517,29 +507,11 @@ from_source() {
local seed_file="$BUILD_DIR/.config"
local worktree_meta="$SRC_DIR/.git/worktrees/source-$ref"
local pkg config commit seed_file description
local -a make_opts
local -a config_opts=(
"CONFIG_TARGET_${TARGET%%/*}=y"
"CONFIG_TARGET_${TARGET//\//_}=y"
"CONFIG_TARGET_PROFILE=DEVICE_$DEVICE"
"CONFIG_TARGET_${TARGET//\//_}_DEVICE_$DEVICE=y"
"CONFIG_TARGET_ROOTFS_${FILESYSTEM^^}=y"
"CONFIG_TARGET_MULTI_PROFILE=n"
"CONFIG_BUILDBOT=n"
"CONFIG_ALL_KMODS=n"
"CONFIG_ALL_NONSHARED=n"
"CONFIG_DEVEL=n"
"CONFIG_COLLECT_KERNEL_DEBUG=n"
"CONFIG_SDK=n"
"CONFIG_SDK_LLVM_BPF=n"
"CONFIG_IB=n"
"CONFIG_MAKE_TOOLCHAIN=n"
"CONFIG_TARGET_PER_DEVICE_ROOTFS=n"
)
local -a make_opts config_opts
echo "Building from source is under development"
# Remove all build directories and worktrees if --reset
# Remove all build directories and worktrees
if ((RESET)); then
if [[ -d "$BUILD_DIR" || -d "$worktree_meta" ]]; then
execute git -C "$SRC_DIR" worktree remove --force --force "$BUILD_DIR"
@@ -549,43 +521,24 @@ from_source() {
[[ -d "$BUILD_DIR" ]] && execute rm -rf "$BUILD_DIR"
fi
# Fetch or clone source repo (no local merges)
# Pull or clone source repo
if [[ -d "$SRC_DIR" ]]; then
execute git -C "$SRC_DIR" fetch origin --tags --prune
execute git -C "$SRC_DIR" pull
else
execute mkdir -p "$SRC_DIR"
execute git clone "$src_url" "$SRC_DIR"
fi
# Reuse worktree if present; otherwise create it (support branches and tags)
if git -C "$BUILD_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
execute git -C "$BUILD_DIR" fetch origin --tags --prune
execute git -C "$BUILD_DIR" reset --hard "origin/$ref" || \
execute git -C "$BUILD_DIR" reset --hard "$ref" || \
execute git -C "$BUILD_DIR" checkout --detach "$ref"
else
# Remove existing build dir and add new worktree
if [[ -d "$BUILD_DIR" ]]; then
execute rm -rf "$BUILD_DIR"
fi
execute git -C "$SRC_DIR" worktree prune --verbose
# Prefer local tag/branch if present, otherwise use remote-tracking branch
if ! execute git -C "$SRC_DIR" worktree add --detach "$BUILD_DIR" "$ref"; then
execute git -C "$SRC_DIR" worktree add --detach "$BUILD_DIR" "origin/$ref"
fi
fi
# Add cherrypick commits if specified in profile (skip if already included)
for entry in ${P_ARR[cherrypicks]}; do
remote="${entry%%:*}"
commit="${entry##*:}"
if ! git -C "$BUILD_DIR" remote | grep -q "^$remote$"; then
execute git -C "$BUILD_DIR" remote add "$remote" "https://github.com/$remote/openwrt.git"
fi
execute git -C "$BUILD_DIR" fetch "$remote"
execute git -C "$BUILD_DIR" merge-base --is-ancestor "$commit" HEAD ||
execute git -C "$BUILD_DIR" cherry-pick "$commit"
done
execute git -C "$SRC_DIR" worktree add --detached "$BUILD_DIR" "$ref"
# Print commit info
commit=$(git -C "$BUILD_DIR" rev-parse HEAD)
description=$(git -C "$BUILD_DIR" describe --always --dirty)
description=$(git -C "$BUILD_DIR" describe)
echo "Current commit hash: $commit"
echo "Git worktree description: $description"
@@ -597,24 +550,29 @@ from_source() {
# Begin OpenWRT build process
((DEBUG)) && make_opts+=("V=sc")
# Cleanup build environment: heavy clean only when --reset was used earlier
# Cleanup build environment
execute make "${make_opts[@]}" "-j1" distclean
# make clean # compiled output
# make targetclean # compiled output, toolchain
# make dirclean # compiled output, toolchain, build tools
# make distclean # compiled output, toolchain, build tools, .config, feeds, .ccache
if [[ -n $SOURCE_CLEAN ]]; then
execute make "${make_opts[@]}" "-j1" "$SOURCE_CLEAN"
else
debug "Skipping cleanup step"
fi
# Use a custom (faster) mirror
execute sed -i -E 's;git.openwrt.org/(feed|project);github.com/openwrt;' feeds.conf.default
# execute sed -i -E 's;git.openwrt.org/(feed|project);github.com/openwrt;' feeds.conf.default
# Update package feed
./scripts/feeds update -a -f &&
./scripts/feeds install -a -f
# Grab the release seed config
if ! execute "$DL_TOOL" "-o" "$seed_file" "$seed_url"; then
echo "Could not obtain $seed_file from $seed_url"
return 1
fi
# Set compilation output dir
config_opts+=("CONFIG_BINARY_FOLDER=\"$BIN_DIR\"")
# Add custom packages
for pkg in $PACKAGES; do
if [[ $pkg == -* ]]; then
@@ -624,37 +582,54 @@ from_source() {
fi
done
# Add profile config options
# Add config options from profile
for config in ${P_ARR[config]}; do
config_opts+=("$config")
done
# Reset and write options to config seed file
[[ -f $seed_file ]] && execute rm -f "$seed_file"
# Only compile selected fs
execute sed -i '/CONFIG_TARGET_ROOTFS_/d' "$seed_file"
config_opts+=("CONFIG_TARGET_PER_DEVICE_ROOTFS=n")
if [[ $FILESYSTEM == "squashfs" ]]; then
config_opts+=("CONFIG_TARGET_ROOTFS_EXT4FS=n")
config_opts+=("CONFIG_TARGET_ROOTFS_SQUASHFS=y")
elif [[ $FILESYSTEM == "ext4" ]]; then
config_opts+=("CONFIG_TARGET_ROOTFS_SQUASHFS=n")
config_opts+=("CONFIG_TARGET_ROOTFS_EXT4FS=y")
fi
# Only compile selected target image
execute sed -i '/CONFIG_TARGET_DEVICE_/d' "$seed_file"
config_opts+=("CONFIG_TARGET_MULTI_PROFILE=n")
config_opts+=("CONFIG_TARGET_PROFILE=DEVICE_$DEVICE")
config_opts+=("CONFIG_TARGET_${TARGET//\//_}_DEVICE_$DEVICE=y")
config_opts+=("CONFIG_SDK=n")
config_opts+=("CONFIG_SDK_LLVM_BPF=n")
config_opts+=("CONFIG_IB=n")
config_opts+=("CONFIG_MAKE_TOOLCHAIN=n")
# Write options to config seed file
for config in "${config_opts[@]}"; do
debug "Writing $config to $seed_file"
echo "$config" >> "$seed_file"
done
# Expand seed into full config
# Serial make prep is more reliable
execute make "${make_opts[@]}" "-j1" defconfig
# Run serial make download for better reliability
execute make "${make_opts[@]}" "-j1" download
# (Optional) Disable multicore make world
# ((DEBUG)) && make_opts+=("-j1") || make_opts+=("-j$(($(nproc)-2))")
make_opts+=("-j$(($(nproc)-2))")
# make_opts+=("-j$(($(nproc)-1))")
((DEBUG)) && make_opts+=("-j1") || make_opts+=("-j$(($(nproc)-1))")
# Make image
if ! execute ionice -c2 -n7 nice -n19 make "${make_opts[@]}" BIN_DIR="$BIN_DIR" world; then
if ! execute ionice -c 3 chrt --idle 0 nice -n19 make "${make_opts[@]}" world; then
echo "Error: make failed"
return 1
fi
execute popd || return 1
# Symlink output images to root of BIN_DIR (match Image Builder behavior)
# Symlink output images to root of BIN_DIR (match Image Builder)
shopt -s nullglob
for image in "$BIN_DIR/targets/${TARGET}/"*.{img,img.gz,ubi}; do
execute ln -fs "$image" "$BIN_DIR/${image##*/}"
@@ -778,7 +753,7 @@ main() {
# Store profile in P_ARR nameref
local -n P_ARR="$profile"
local mode="${P_ARR[mode]:-"imagebuilder"}"
local mode="${P_ARR[mode]:="imagebuilder"}"
((FROM_SOURCE)) && mode="source" # allow cli override
install_dependencies "$mode"
local repo="${P_ARR[repo]:-}"
@@ -794,8 +769,8 @@ main() {
read -r release ref < <(normalize_and_ref "$raw_release" "$mode")
declare -g SRC_DIR="$BUILD_ROOT/src/.openwrt"
declare -g BUILD_DIR="$BUILD_ROOT/src/$profile/$ref-$mode"
declare -g BIN_DIR="$BUILD_ROOT/bin/$profile/$ref-$mode"
declare -g BUILD_DIR="$BUILD_ROOT/src/$profile/$mode-$ref"
declare -g BIN_DIR="$BUILD_ROOT/bin/$profile/$mode-$ref"
if [[ "$release" == "snapshot" ]]; then
local url_prefix="https://downloads.openwrt.org/snapshots/targets/$TARGET"
@@ -819,7 +794,7 @@ main() {
declare -g SYSUPGRADEIMGGZ="$BUILD_DIR/$img_fname-sysupgrade.img.gz"
fi
backup "$SYSUPGRADEIMGGZ" "$BACKUP_DIR/$profile/$ref-$mode"
backup "$SYSUPGRADEIMGGZ" "$BACKUP_DIR/$profile/$mode-$ref"
if [[ "$mode" == "source" ]]; then
from_source "$seed_url" "$profile" "$ref" || return $?
@@ -853,9 +828,5 @@ main() {
done
}
# Roughly turn debugging on for pre-init
# Reset and reparse in parse_input() with getopt
[[ " $* " =~ ( --debug | -d ) ]] && DEBUG=1
main "$@"
exit

View File

@@ -1,16 +1,12 @@
#!/usr/bin/env bash
# Device profiles for openwrtbuilder
# shellcheck disable=SC2034
# Device profiles for openwrtbuilder
# Default (but optional) packages (precede with "-" to exclude)
default_packages=(nano htop diffutils tar iperf3 zsh rsync curl tcpdump
openssh-sftp-server luci luci-ssl luci-proto-wireguard luci-app-statistics
collectd-mod-sensors collectd-mod-thermal collectd-mod-conntrack collectd-mod-cpu
)
# Default (but optional) kernel configs
default_configs=(
)
# Default packages
default_packages="luci luci-ssl luci-proto-wireguard luci-app-statistics \
collectd-mod-sensors collectd-mod-thermal collectd-mod-conntrack \
collectd-mod-cpu nano htop diffutils tar iperf3 zsh rsync \
openssh-sftp-server"
# Current devices
declare -Ag r4s=(
@@ -18,18 +14,14 @@ declare -Ag r4s=(
[device]="friendlyarm_nanopi-r4s"
[target]="rockchip/armv8"
[filesystem]="ext4"
[packages]="${default_packages[*]} \
luci-app-ddns luci-app-sqm \
adblock luci-app-adblock \
[packages]="$default_packages luci-app-ddns luci-app-sqm irqbalance \
collectd-mod-df usbutils kmod-usb-storage kmod-usb-storage-uas \
kmod-fs-btrfs btrfs-progs block-mount smcroute avahi-daemon \
ethtool ca-bundle tailscale"
[config]="${default_configs[*]} \
CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \
CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32"
curl ethtool ca-bundle tailscale"
[config]="CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \
CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32 \
CONFIG_BUILDBOT=n"
[files]="/mnt/backup"
# For 24.10 branch (Linux 6.6)
# [cherrypicks]="wurzerj:59d6e31 wurzerj:bb251b8" # fix inconsistent reboot
)
declare -Ag ax6000=(
@@ -38,8 +30,7 @@ declare -Ag ax6000=(
[target]="mediatek/filogic"
[release]="snapshot"
[filesystem]="squashfs"
[packages]="${default_packages[*]} \
-dnsmasq -odhcpd-ipv6only -nftables -firewall4 tailscale"
[packages]="$default_packages -dnsmasq -odhcpd-ipv6only -nftables -firewall4 tailscale"
)
declare -Ag ax6000_uboot=(
@@ -48,22 +39,19 @@ declare -Ag ax6000_uboot=(
[target]="mediatek/filogic"
[release]="snapshot"
[filesystem]="squashfs"
[packages]="${default_packages[*]} \
-dnsmasq -odhcpd-ipv6only -nftables -firewall4"
[packages]="$default_packages -dnsmasq -odhcpd-ipv6only -nftables -firewall4"
)
declare -Ag n5100=(
[device]="generic"
[target]="x86/64"
[filesystem]="squashfs"
[packages]="${default_packages[*]} \
luci-app-ddns irqbalance collectd-mod-df \
[packages]="$default_packages luci-app-ddns irqbalance collectd-mod-df \
usbutils kmod-usb-storage kmod-usb-storage-uas kmod-fs-btrfs \
btrfs-progs block-mount cryptsetup kmod-crypto-xts smcroute \
avahi-daemon curl ethtool ca-bundle smartmontools intel-microcode \
lm-sensors samba4-server luci-app-samba4 tailscale shadow-useradd"
[config]="${default_configs[*]} \
CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \
[config]="CONFIG_KERNEL_BTRFS_FS_POSIX_ACL=y CONFIG_BTRFS_PROGS_ZSTD=y \
CONFIG_TARGET_ROOTFS_PARTSIZE=512 CONFIG_TARGET_KERNEL_PARTSIZE=32"
# [files]="/mnt/backup"
)
@@ -72,9 +60,8 @@ declare -Ag rpi4=(
[device]="rpi-4"
[target]="bcm27xx/bcm2711"
[filesystem]="ext4"
[packages]="${default_packages[*]} \
luci-app-upnp luci-app-pbr -dnsmasq dnsmasq-full luci-app-ddns luci-app-sqm \
kmod-usb-net-asix-ax88179 kmod-usb-net-rtl8152"
[packages]="$default_packages kmod-usb-net-asix-ax88179 kmod-usb-net-rtl8152 \
luci-app-upnp luci-app-pbr -dnsmasq dnsmasq-full luci-app-ddns luci-app-sqm"
)
declare -Ag r4s_stock=(
@@ -88,8 +75,7 @@ declare -Ag totolink=(
[device]="totolink_x5000r"
[target]="ramips/mt7621"
[filesystem]="squashfs"
[packages]="${default_packages[*]} \
-dnsmasq -odhcpd-ipv6only -nftables -firewall4 \
[packages]="$default_packages -dnsmasq -odhcpd-ipv6only -nftables -firewall4 \
-kmod-nft-offload collectd-mod-iwinfo"
)
@@ -97,7 +83,7 @@ declare -Ag archer=(
[device]="tplink_archer-c7-v2"
[target]="ath79/generic"
[filesystem]="squashfs"
[packages]="${default_packages[*]} -dnsmasq -odhcpd -iptables \
[packages]="$default_packages -dnsmasq -odhcpd -iptables \
-ath10k-firmware-qca988x-ct ath10k-firmware-qca988x-ct-full-htt"
)
@@ -105,16 +91,14 @@ declare -Ag linksys=(
[device]="linksys_ea8300"
[target]="ipq40xx/generic"
[filesystem]="squashfs"
[packages]="${default_packages[*]} \
-dnsmasq -odhcpd -iptables"
[packages]="$default_packages -dnsmasq -odhcpd -iptables"
)
declare -Ag r2s=(
[device]="friendlyarm_nanopi-r2s"
[target]="rockchip/armv8"
[filesystem]="ext4"
[packages]="${default_packages[*]} \
luci-app-upnp luci-app-pbr -dnsmasq dnsmasq-full \
[packages]="$default_packages luci-app-upnp luci-app-pbr -dnsmasq dnsmasq-full \
luci-app-ddns luci-app-sqm luci-app-statistics collectd-mod-sensors \
collectd-mod-thermal collectd-mod-conntrack smcroute curl ethtool"
)
@@ -123,8 +107,7 @@ declare -Ag r2s_tr=(
[device]="friendlyarm_nanopi-r2s"
[target]="rockchip/armv8"
[filesystem]="ext4"
[packages]="${default_packages[*]} \
luci-app-upnp luci-app-pbr luci-app-ddns \
[packages]="$default_packages luci-app-upnp luci-app-pbr luci-app-ddns \
luci-app-statistics collectd-mod-sensors collectd-mod-thermal \
collectd-mod-conntrack curl ethtool travelmate"
)