├── AutoUpdate.sh ├── LICENSE └── README.md /AutoUpdate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # AutoBuild Module by Hyy2001 3 | # AutoUpdate for Openwrt 4 | # Dependences: bash wget-ssl/wget/uclient-fetch curl openssl jsonfilter 5 | 6 | Version=V6.5.7 7 | 8 | function TITLE() { 9 | clear && echo "Openwrt-AutoUpdate Script by Hyy2001 ${Version}" 10 | } 11 | 12 | function SHELL_HELP() { 13 | TITLE 14 | cat <] [--path ] 17 | bash $0 [-x] [--path ] [--url ] 18 | 19 | 更新固件: 20 | -n 不保留配置更新固件 * 21 | -u 适用于定时更新 LUCI 的参数 * 22 | -f 跳过版本号、SHA256 校验,并强制刷写固件 (危险) * 23 | -F, --force-write 强制刷写固件 * 24 | -P, --proxy 优先开启镜像加速下载固件 * 25 | -D 使用指定的下载器 * 26 | --decompress 解压 img.gz 固件后再更新固件 * 27 | --skip-verify 跳过固件 SHA256 校验 (危险) * 28 | --path 保存固件到提供的绝对路径 * 29 | 30 | 更新脚本: 31 | -x 更新 AutoUpdate.sh 脚本 32 | -x --path 更新 AutoUpdate.sh 脚本 (保存脚本到提供的绝对路径 ) * 33 | -x --url 更新 AutoUpdate.sh 脚本 (使用提供的地址 更新脚本) * 34 | 35 | 其他参数: 36 | -B, --boot-mode 指定 x86 设备下载 引导的固件 (e.g. UEFI Legacy) 37 | -C 更改 Github 地址为提供的 38 | -H, --help 打印 AutoUpdate 帮助信息 39 | -L, --log < | del> <打印 | 删除> AutoUpdate 历史运行日志 40 | --log --path 更改 AutoUpdate 运行日志路径为提供的绝对路径 41 | -O 打印云端可用固件名称 42 | -P 使用 镜像加速 * 43 | --backup --path 备份当前系统配置文件并移动到提供的绝对路径 (可选) 44 | --check 检查 AutoUpdate 运行环境 45 | --clean 清理 AutoUpdate 缓存 46 | --fw-log < | [Cc]loud | *> 打印 <当前 | 云端 | 指定版本> 版本的固件更新日志 47 | --list 打印当前系统信息 48 | --var 打印用户指定的环境变量 49 | --verbose 打印详细的下载信息 * 50 | -v < | [Cc]loud> 打印 <当前 | 云端> AutoUpdate.sh 版本 51 | -V < | [Cc]loud> 打印 <当前 | 云端> 固件版本 52 | 53 | 脚本、固件更新问题反馈请前往 ${Github}, 并附上 AutoUpdate 运行日志与系统信息 (见上方) 54 | 55 | EOF 56 | EXIT 57 | } 58 | 59 | function SHOW_VARIABLE() { 60 | TITLE 61 | cat < /dev/null 87 | [[ $? == 0 ]] && LOGGER "已删除文件: [$1]" || LOGGER "文件: [$1] 不存在或删除失败!" 88 | } 89 | 90 | function LIST_ENV() { 91 | local X 92 | cat /etc/AutoBuild/*_Variable | grep -v '#' | while read X;do 93 | [[ ${X} =~ "=" ]] && { 94 | case "$1" in 95 | 1 | 2) 96 | [[ -n $(echo ${X} | cut -d "=" -f1) ]] && echo "${X}" | cut -d "=" -f$1 97 | ;; 98 | 0) 99 | echo "${X}" 100 | ;; 101 | esac 102 | } 103 | done 104 | } 105 | 106 | function CHECK_ENV() { 107 | while [[ $1 ]];do 108 | [[ $(LIST_ENV 1) =~ $1 ]] && LOGGER "Checking env $1 ... true" || ECHO r "Checking env $1 ... false" 109 | shift 110 | done 111 | } 112 | 113 | function EXIT() { 114 | LOGGER "[${COMMAND}] 运行结束 $1" 115 | exit 116 | } 117 | 118 | function ECHO() { 119 | local Color Quiet_Mode 120 | [[ -z $1 ]] && { 121 | echo -ne "\n${Grey}[$(date "+%H:%M:%S")]${White} " 122 | } || { 123 | while [[ $1 ]];do 124 | case "$1" in 125 | r | g | b | y | x) 126 | case "$1" in 127 | r) Color="${Red}";; 128 | g) Color="${Green}";; 129 | b) Color="${Blue}";; 130 | y) Color="${Yellow}";; 131 | x) Color="${Grey}";; 132 | esac 133 | shift 134 | ;; 135 | quiet) 136 | Quiet_Mode=1 137 | shift 138 | ;; 139 | *) 140 | Message="$1" 141 | break 142 | ;; 143 | esac 144 | done 145 | [[ ! ${Quiet_Mode} == 1 ]] && { 146 | echo -e "\n${Grey}[$(date "+%H:%M:%S")]${White}${Color} ${Message}${White}" 147 | LOGGER "${Message}" 148 | } || LOGGER "[Quiet Mode] ${Message}" 149 | } 150 | } 151 | 152 | function LOGGER() { 153 | if [[ ! $* =~ (-H|--help|-L|--log) ]];then 154 | [[ ! -d ${Log_Path} ]] && mkdir -p ${Log_Path} 155 | [[ ! -f ${Log_Path}/AutoUpdate.log ]] && touch ${Log_Path}/AutoUpdate.log 156 | echo "[$(date "+%Y-%m-%d-%H:%M:%S")] [$$] $*" >> ${Log_Path}/AutoUpdate.log 157 | fi 158 | } 159 | 160 | function CHECK_PKG() { 161 | which $1 > /dev/null 2>&1 162 | [[ $? == 0 ]] && echo true || echo false 163 | } 164 | 165 | function RANDOM() { 166 | local Result=$(openssl rand -base64 $1 | md5sum | cut -c 1-$1) 167 | [[ -n ${Result} ]] && echo "${Result}" 168 | LOGGER "[RANDOM] $1 Bit 计算结果: [${Result}]" 169 | } 170 | 171 | function GET_SHA256SUM() { 172 | [[ ! -f $1 && ! -s $1 ]] && { 173 | LOGGER "未检测到文件 [$1],无法计算 SHA256 值!" 174 | EXIT 1 175 | } 176 | LOGGER "[GET_SHA256SUM] 目标文件: [$1]" 177 | local Result=$(sha256sum $1 | cut -c1-$2) 178 | [[ -n ${Result} ]] && echo "${Result}" 179 | LOGGER "[GET_SHA256SUM] 计算结果: [${Result}]" 180 | } 181 | 182 | function GET_VARIABLE() { 183 | [[ $# != 2 ]] && SHELL_HELP 184 | [[ ! -f $2 ]] && ECHO "未检测到定义文件: [$2] !" && EXIT 1 185 | local Result="$(grep "$1=" $2 | grep -v "#" | awk 'NR==1' | sed -r "s/$1=(.*)/\1/")" 186 | [[ -n ${Result} ]] && { 187 | echo "${Result}" 188 | LOGGER "[GET_VARIABLE] 获取到环境变量 $1=[${Result}]" 189 | } || { 190 | LOGGER "[GET_VARIABLE] 环境变量 [$1] 获取失败!" 191 | } 192 | } 193 | 194 | function LOAD_VARIABLE() { 195 | while [[ $1 ]];do 196 | [[ -f $1 ]] && { 197 | chmod 777 $1 198 | source $1 199 | } || LOGGER "未检测到环境变量列表: [$1]" 200 | shift 201 | done 202 | [[ -z ${TARGET_PROFILE} ]] && TARGET_PROFILE="$(jsonfilter -e '@.model.id' < /etc/board.json | tr ',' '_')" 203 | [[ -z ${TARGET_PROFILE} ]] && ECHO r "获取设备名称失败!" && EXIT 1 204 | [[ -z ${Github} ]] && ECHO "Github URL 获取失败!" && EXIT 1 205 | [[ -z ${CURRENT_Version} ]] && CURRENT_Version="未知" 206 | Firmware_Author="${Github##*com/}" 207 | Github_Release="${Github}/releases/download/AutoUpdate" 208 | Github_Raw="https://raw.githubusercontent.com/${Firmware_Author}/master" 209 | Github_API="https://api.github.com/repos/${Firmware_Author}/releases/latest" 210 | case "${TARGET_BOARD}" in 211 | x86) 212 | case "${Firmware_Format}" in 213 | img.gz | img) 214 | [[ -z ${x86_Boot} ]] && { 215 | [ -d /sys/firmware/efi ] && { 216 | x86_Boot=UEFI 217 | } || x86_Boot=Legacy 218 | } 219 | ;; 220 | *) 221 | ECHO r "[${TARGET_PROFILE}] 设备暂不支持当前固件格式!" 222 | EXIT 1 223 | ;; 224 | esac 225 | ;; 226 | *) 227 | [[ -z ${Firmware_Format} ]] && Firmware_Format=bin 228 | esac 229 | } 230 | 231 | function EDIT_VARIABLE() { 232 | local Mode="$1" 233 | shift 234 | [[ ! -f $1 ]] && ECHO r "未检测到环境变量文件: [$1] !" && EXIT 1 235 | case "${Mode}" in 236 | edit) 237 | [[ $# != 3 ]] && SHELL_HELP 238 | [[ -z $(GET_VARIABLE $2 $1) ]] && { 239 | LOGGER "[EDIT_VARIABLE] 新增环境变量 [$2=$3]" 240 | echo -e "\n$2=$3" >> $1 241 | } || { 242 | sed -i "s?$(GET_VARIABLE $2 $1)?$3?g" $1 243 | } 244 | ;; 245 | rm) 246 | [[ $# != 2 ]] && SHELL_HELP 247 | LOGGER "[EDIT_VARIABLE] 从 $1 删除环境变量 [$2] ..." 248 | sed -i "/$2/d" $1 249 | ;; 250 | esac 251 | } 252 | 253 | function CHANGE_GITHUB() { 254 | [[ ! $1 =~ https://github.com/ ]] && { 255 | ECHO r "Github 地址输入有误,正确示例: https://github.com/Hyy2001X/AutoBuild-Actions" 256 | EXIT 1 257 | } 258 | UCI_Github=$(uci get autoupdate.@common[0].github 2> /dev/null) 259 | [[ -n ${UCI_Github} && ! ${UCI_Github} == $1 ]] && { 260 | uci set autoupdate.@common[0].github=$1 2> /dev/null 261 | LOGGER "[CHANGE_GITHUB] UCI 配置已设定为 [$1]" 262 | } 263 | [[ ! ${Github} == $1 ]] && { 264 | EDIT_VARIABLE edit ${Custom_Variable} Github $1 265 | ECHO y "Github 地址已修改为: $1" 266 | REMOVE_CACHE 267 | } 268 | EXIT 0 269 | } 270 | 271 | function CHANGE_BOOT() { 272 | [[ -z $1 ]] && SHELL_HELP 273 | case "$1" in 274 | UEFI | Legacy) 275 | EDIT_VARIABLE edit ${Custom_Variable} x86_Boot $1 276 | ECHO r "警告: 更换引导方式后更新固件后可能导致设备无法正常启动!" 277 | ECHO y "固件引导格式已指定为: [$1]" 278 | EXIT 0 279 | ;; 280 | *) 281 | ECHO r "错误的参数: [$1],支持的启动方式: [UEFI/Legacy]" 282 | EXIT 1 283 | ;; 284 | esac 285 | } 286 | 287 | function UPDATE_SCRIPT() { 288 | [[ -f $1 ]] && { 289 | ECHO r "AutoUpdate 脚本保存路径有误,请重新输入!" 290 | EXIT 1 291 | } 292 | if [[ ! -d $1 ]];then 293 | mkdir -p $1 2> /dev/null || { 294 | ECHO r "脚本存放目录 [$1] 创建失败!" 295 | EXIT 1 296 | } 297 | fi 298 | DOWNLOADER --file-name AutoUpdate.sh --no-url-name --dl ${DOWNLOADERS} --url "$2" --path /tmp --timeout 5 --type 脚本 299 | if [[ -s /tmp/AutoUpdate.sh ]];then 300 | chmod +x /tmp/AutoUpdate.sh 301 | Script_Version=$(egrep -o "V[0-9]+.[0-9].+" /tmp/AutoUpdate.sh | awk 'NR==1') 302 | Banner_Version=$(egrep -o "V[0-9]+.[0-9].+" /etc/banner) 303 | mv -f /tmp/AutoUpdate.sh $1 304 | ECHO "脚本保存路径: [$1]" 305 | [[ -n ${Banner_Version} && $1 == /bin ]] && sed -i "s?${Banner_Version}?${Script_Version}?g" /etc/banner 306 | ECHO y "[${Banner_Version} > ${Script_Version}] AutoUpdate 脚本更新成功!" 307 | EXIT 0 308 | else 309 | ECHO r "AutoUpdate 脚本更新失败!" 310 | EXIT 1 311 | fi 312 | } 313 | 314 | function CHECK_DEPENDS() { 315 | TITLE 316 | local PKG Tab 317 | echo -e "\n软件包 检测结果" 318 | while [[ $1 ]];do 319 | if [[ $1 =~ : ]];then 320 | [[ $(echo $1 | cut -d ":" -f1) == ${TARGET_BOARD} ]] && { 321 | PKG="$(echo "$1" | cut -d ":" -f2)" 322 | [[ $(echo "${PKG}" | wc -c) -gt 8 ]] && Tab=" " || Tab=" " 323 | echo -e "${PKG}${Tab}$(CHECK_PKG ${PKG})" 324 | LOGGER "[CHECK_DEPENDS] 检查软件包: [${PKG}] ... $(CHECK_PKG ${PKG})" 325 | } 326 | else 327 | [[ $(echo "$1" | wc -c) -gt 8 ]] && Tab=" " || Tab=" " 328 | echo -e "$1${Tab}$(CHECK_PKG $1)" 329 | LOGGER "[CHECK_DEPENDS] 检查软件包: [$1] ... $(CHECK_PKG $1)" 330 | fi 331 | shift 332 | done 333 | ECHO y "AutoUpdate 依赖检测结束,请尝试手动安装测结果为 [false] 的项目!" 334 | } 335 | 336 | function FW_VERSION_CHECK() { 337 | [[ $# -gt 1 ]] && echo false && return 338 | [[ $1 =~ R[1-9.]{2}.+-[0-9]{8} ]] && { 339 | echo true 340 | LOGGER "[FW_VERSION_CHECK] 检查固件版本号: [$1] ... true" 341 | } || { 342 | echo false 343 | LOGGER "[FW_VERSION_CHECK] 检查固件版本号: [$1] ... false" 344 | } 345 | } 346 | 347 | function GET_FW_LOG() { 348 | local Result 349 | [[ ! $(cat ${API_File}) =~ Update_Logs.json ]] && return 1 350 | case "$1" in 351 | [Ll]ocal) 352 | FW_Version="${CURRENT_Version}" 353 | ;; 354 | [Cc]loud) 355 | FW_Version="$(GET_CLOUD_INFO version)" 356 | ;; 357 | -v) 358 | shift 359 | FW_Version="$1" 360 | ;; 361 | esac 362 | [[ $(CHECK_TIME ${Running_Path}/Update_Logs.json 2) == false ]] && { 363 | DOWNLOADER --path ${Running_Path} --file-name Update_Logs.json --dl ${DOWNLOADERS} --url "$(URL_X ${Github_Release} G@@1)" --timeout 3 --type 固件更新日志 --quiet 364 | } 365 | [[ -s ${Running_Path}/Update_Logs.json ]] && { 366 | Result=$(jsonfilter -e '@["'"""${TARGET_PROFILE}"""'"]["'"""${FW_Version}"""'"]' < ${Running_Path}/Update_Logs.json 2> /dev/null) 367 | [[ -n ${Result} ]] && { 368 | echo -e "\n${Grey}${FW_Version} for ${TARGET_PROFILE} 更新日志:" 369 | echo -e "\n${Green}${Result}${White}" 370 | } 371 | } 372 | } 373 | 374 | function CHECK_TIME() { 375 | [[ -s $1 && -n $(find $1 -type f -mmin -$2) ]] && { 376 | LOGGER "[CHECK_TIME] 文件: [$1] 距离修改时间小于 $2 分钟!" 377 | echo true 378 | } || { 379 | LOGGER "[CHECK_TIME] 文件: [$1] 验证失败!" 380 | RM $1 381 | echo false 382 | } 383 | } 384 | 385 | function GET_API() { 386 | local url name size version 387 | local API_Dump=${Running_Path}/API_Dump 388 | [[ $(CHECK_TIME ${API_File} 1) == false ]] && { 389 | DOWNLOADER --path ${Running_Path} --file-name API_Dump --dl ${DOWNLOADERS} --url "$(URL_X ${Github_Release}/API G@@1 F@@1) ${Github_API}@@1 " --no-url-name --timeout 3 --type 固件信息 --quiet 390 | [[ ! $? == 0 || -z $(cat ${API_Dump} 2> /dev/null) ]] && { 391 | ECHO r "Github API 请求错误,请检查网络后重试!" 392 | RM ${API_Dump} 393 | EXIT 1 394 | } 395 | RM ${API_File} && touch -a ${API_File} 396 | local i=1;while :;do 397 | url=$(jsonfilter -e '@["assets"]' < ${API_Dump} | jsonfilter -e '@['"""$i"""'].browser_download_url' 2> /dev/null) 398 | [[ ! $? == 0 ]] && break 399 | size=$(jsonfilter -e '@["assets"]' < ${API_Dump} | jsonfilter -e '@['"""$i"""'].size' 2> /dev/null) 400 | name=${url##*/} 401 | size=$(echo $size | awk '{a=$1/1048576} {printf("%.2f\n",a)}') 402 | version=$(echo $name | egrep -o "R[0-9.]+-[0-9]+") 403 | echo "${name} ${version} ${size}MB ${url}" | grep -v "API" >> ${API_File} 404 | i=$(($i + 1)) 405 | done 406 | } 407 | [[ -z $(cat ${API_File} 2> /dev/null) ]] && { 408 | ECHO r "Github API 解析失败,请尝试清理缓存后再试!" 409 | RM ${API_File} 410 | EXIT 1 411 | } 412 | } 413 | 414 | function GET_CLOUD_INFO() { 415 | local Info 416 | [[ ! -f ${API_File} ]] && return 417 | if [[ $1 =~ (All|all|-a) ]];then 418 | Info=$(grep "AutoBuild-${OP_REPO_NAME}-${TARGET_PROFILE}" ${API_File} | grep "${x86_Boot}" | sort | uniq) 419 | shift 420 | else 421 | Info=$(grep "AutoBuild-${OP_REPO_NAME}-${TARGET_PROFILE}" ${API_File} | grep "${x86_Boot}" | sort | uniq | awk 'END {print}') 422 | fi 423 | case "$1" in 424 | name) 425 | echo "${Info}" | awk '{print $1}' 426 | ;; 427 | version) 428 | echo "${Info}" | awk '{print $2}' 429 | ;; 430 | size) 431 | echo "${Info}" | awk '{print $3}' 432 | ;; 433 | url) 434 | echo "${Info}" | awk '{print $4}' 435 | ;; 436 | esac 437 | } 438 | 439 | function CHECK_UPDATES() { 440 | local Version 441 | Version="$(GET_CLOUD_INFO version)" 442 | [[ $(FW_VERSION_CHECK ${Version}) == false ]] && { 443 | ECHO r "固件版本合法性校验失败!" 444 | EXIT 1 445 | } 446 | [[ ${Version} == ${CURRENT_Version} ]] && { 447 | CURRENT_Type="${Yellow} [已是最新]${White}" 448 | Upgrade_Stopped=1 449 | } || { 450 | [[ $(echo ${Version} | cut -d "-" -f2) -gt $(echo ${CURRENT_Version} | cut -d "-" -f2) ]] && CURRENT_Type="${Green} [可更新]${White}" 451 | [[ $(echo ${Version} | cut -d "-" -f2) -lt $(echo ${CURRENT_Version} | cut -d "-" -f2) ]] && { 452 | CHECKED_Type="${Red} [旧版本]${White}" 453 | Upgrade_Stopped=2 454 | } 455 | } 456 | } 457 | 458 | function UPGRADE() { 459 | TITLE 460 | [[ $* =~ -f && $* =~ -F ]] && SHELL_HELP 461 | [[ $(NETWORK_CHECK 223.5.5.5 2) == false ]] && { 462 | ECHO r "网络连接错误,请稍后再试!" 463 | EXIT 1 464 | } 465 | Firmware_Path="${Running_Path}" 466 | Upgrade_Option="sysupgrade -q" 467 | MSG="更新固件" 468 | while [[ $1 ]];do 469 | case "$1" in 470 | -T | --test) 471 | Test_Mode=1 472 | Special_Commands="${Special_Commands} [测试模式]" 473 | ;; 474 | -P | --proxy) 475 | case "$2" in 476 | F | G) 477 | Proxy_Type="$2" 478 | shift 479 | ;; 480 | *) 481 | Proxy_Type="All" 482 | ;; 483 | esac 484 | Special_Commands="${Special_Commands} [镜像加速 ${Proxy_Type}]" 485 | ;; 486 | -D) 487 | DOWNLOADERS="$2" 488 | Special_Commands="${Special_Commands} [${DOWNLOADERS}]" 489 | shift 490 | ;; 491 | -F | --force-write) 492 | [[ -n ${Force_Mode} ]] && SHELL_HELP 493 | Only_Force_Write=1 494 | Special_Commands="${Special_Commands} [强制刷写]" 495 | Upgrade_Option="${Upgrade_Option} -F" 496 | ;; 497 | --decompress) 498 | Special_Commands="${Special_Commands} [解压固件]" 499 | Decompress_Mode=1 500 | ;; 501 | -f) 502 | [[ -n ${Only_Force_Write} ]] && SHELL_HELP 503 | Force_Mode=1 504 | Special_Commands="${Special_Commands} [强制模式]" 505 | Upgrade_Option="${Upgrade_Option} -F" 506 | ;; 507 | -n) 508 | Upgrade_Option="${Upgrade_Option} -n" 509 | Special_MSG=" (不保留配置)" 510 | ;; 511 | --path) 512 | Firmware_Path="$2" 513 | ECHO g "使用自定义固件保存路径: [${Firmware_Path}]" 514 | shift 515 | ;; 516 | --skip-verify) 517 | Skip_Verify=1 518 | Special_Commands="${Special_Commands} [跳过 SHA256 验证]" 519 | ;; 520 | -u) 521 | AutoUpdate_Mode=1 522 | Special_Commands="${Special_Commands} [定时更新]" 523 | ;; 524 | --verbose) 525 | Special_Commands="${Special_Commands} [详细信息]" 526 | ;; 527 | *) 528 | LOGGER "跳过未知参数: [$1] ..." 529 | shift 530 | esac 531 | shift 532 | done 533 | LOGGER "固件更新指令: [${Upgrade_Option}]" 534 | [[ -n "${Special_Commands}" ]] && ECHO g "特殊指令:${Special_Commands} / ${Upgrade_Option}" 535 | ECHO g "执行: ${MSG}${Special_MSG}" 536 | if [[ $(CHECK_PKG curl) == true && -z ${Proxy_Type} ]];then 537 | Google_Check=$(curl -I -s --connect-timeout 3 google.com -w %{http_code} | tail -n1) 538 | LOGGER "Google 连接检查结果: [${Google_Check}]" 539 | [[ ${Google_Check} != 301 ]] && { 540 | ECHO r "Google 连接失败,优先使用镜像加速下载" 541 | Proxy_Type="All" 542 | } 543 | fi 544 | ECHO "正在检查版本更新 ..." 545 | GET_API 546 | CHECK_UPDATES 547 | CLOUD_FW_Version="$(GET_CLOUD_INFO version)" 548 | CLOUD_FW_Name="$(GET_CLOUD_INFO name)" 549 | CLOUD_FW_Size="$(GET_CLOUD_INFO size)" 550 | CLOUD_FW_Url="$(GET_CLOUD_INFO url)" 551 | [[ -z ${CLOUD_FW_Name} ]] && { 552 | ECHO r "云端固件名称获取失败!" 553 | EXIT 1 554 | } 555 | [[ -z ${CLOUD_FW_Version} ]] && { 556 | ECHO r "云端固件版本获取失败!" 557 | EXIT 1 558 | } 559 | cat < ${Firmware_Path}/$(echo ${CLOUD_FW_Name} | sed -r 's/(.*).gz/\1/') 611 | [[ ! $? == 0 ]] && { 612 | ECHO r "固件解压失败!" 613 | EXIT 1 614 | } || { 615 | CLOUD_FW_Name="$(echo ${CLOUD_FW_Name} | sed -r 's/(.*).gz/\1/')" 616 | LOGGER "固件解压成功,固件已解压到: [${Firmware_Path}/${CLOUD_FW_Name}]" 617 | } 618 | else 619 | [[ $(CHECK_PKG gzip) == true ]] && opkg remove gzip > /dev/null 2>&1 620 | fi 621 | ;; 622 | esac 623 | [[ ${Test_Mode} != 1 ]] && { 624 | DO_UPGRADE ${Upgrade_Option} ${Firmware_Path}/${CLOUD_FW_Name} 625 | } || { 626 | ECHO x "[测试模式] ${Upgrade_Option} ${Firmware_Path}/${CLOUD_FW_Name}" 627 | EXIT 0 628 | } 629 | } 630 | 631 | function DOWNLOADER() { 632 | local DL_Downloader DL_Name DL_URL DL_Path DL_Retries DL_Timeout DL_Type DL_Final Quiet_Mode No_URL_Name Print_Mode DL_Retires_All DL_URL_Final 633 | LOGGER "开始解析传入参数 ..." 634 | LOGGER "[$*]" 635 | # --dl 下载器 --file-name 文件名称 --no-url-name --url 下载地址1@@重试次数 下载地址2@@重试次数 --path 保存位置 --timeout 超时 --type 类型 --quiet --print 636 | while [[ $1 ]];do 637 | case "$1" in 638 | --dl) 639 | shift 640 | while [[ $1 ]];do 641 | case "$1" in 642 | wget-ssl | curl | wget | uclient-fetch) 643 | [[ $(CHECK_PKG $1) == true ]] && { 644 | DL_Downloader="$1" 645 | break 646 | } 647 | shift 648 | ;; 649 | *) 650 | LOGGER "跳过未知下载器: [$1] ..." 651 | shift 652 | ;; 653 | esac 654 | done 655 | while [[ $1 ]];do 656 | [[ $1 =~ '--' ]] && break 657 | [[ ! $1 =~ '--' ]] && shift 658 | done 659 | [[ -z ${DL_Downloader} ]] && { 660 | ECHO r "没有可用的下载器,请尝试更换手动安装!" 661 | EXIT 1 662 | } 663 | LOGGER "[--D Finished] Downloader: [${DL_Downloader}]" 664 | ;; 665 | --file-name) 666 | shift 667 | DL_Name="$1" 668 | while [[ $1 ]];do 669 | [[ $1 =~ '--' ]] && break 670 | [[ ! $1 =~ '--' ]] && shift 671 | done 672 | LOGGER "[--file-name Finished] 文件名称: [${DL_Name}]" 673 | ;; 674 | --url) 675 | shift 676 | DL_URL=($(echo $@ | egrep -o "https://.*@@[0-9]+|https://.*@@[0-9]+|ftp://.*@@[0-9]+")) 677 | [[ -z ${DL_URL[*]} ]] && { 678 | DL_URL=($1) 679 | DL_URL_Count="${#DL_URL[@]}" 680 | DL_Retires_All="${DL_URL_Count}" 681 | } || { 682 | DL_Retires_All="$(echo ${DL_URL[*]} | egrep -o "@@[0-9]+" | egrep -o "[0-9]+" | awk '{Sum += $1};END {print Sum}')" 683 | DL_URL_Count="${#DL_URL[@]}" 684 | } 685 | LOGGER "URL 数量: [${DL_URL_Count}] 总重试次数: [${DL_Retires_All}]" 686 | while [[ $1 ]];do 687 | [[ $1 =~ '--' ]] && break 688 | [[ ! $1 =~ '--' ]] && shift 689 | done 690 | LOGGER "[--url Finished] DL_URL: ${DL_URL[*]}" 691 | ;; 692 | --no-url-name) 693 | shift 694 | LOGGER "Enabled No-Url-Filename Mode" 695 | No_URL_Name=1 696 | ;; 697 | --path) 698 | shift 699 | DL_Path="$1" 700 | if [[ ! -d ${DL_Path} ]];then 701 | mkdir -p ${DL_Path} 2> /dev/null || { 702 | ECHO r "下载目录 [${DL_Path}] 创建失败!" 703 | return 1 704 | } 705 | fi 706 | while [[ $1 ]];do 707 | [[ $1 =~ '--' ]] && break 708 | [[ ! $1 =~ '--' ]] && shift 709 | done 710 | LOGGER "[--DL_PATH Finished] 存放路径: ${DL_Path}" 711 | ;; 712 | --timeout) 713 | shift 714 | [[ ! $1 =~ [1-9] ]] && { 715 | LOGGER "参数: [$1] 不是正确的数字" 716 | shift 717 | } || { 718 | DL_Timeout="$1" 719 | while [[ $1 ]];do 720 | [[ $1 =~ '--' ]] && break 721 | [[ ! $1 =~ '--' ]] && shift 722 | done 723 | LOGGER "[--T Finished] 超时: ${DL_Timeout}s" 724 | } 725 | ;; 726 | --type) 727 | shift 728 | DL_Type="$1" 729 | while [[ $1 ]];do 730 | [[ $1 =~ '--' ]] && break 731 | [[ ! $1 =~ '--' ]] && shift 732 | done 733 | LOGGER "[--DL_Type Finished] 文件类型: ${DL_Type}" 734 | ;; 735 | --quiet) 736 | shift 737 | LOGGER "Enabled Quiet Mode" 738 | Quiet_Mode=quiet 739 | ;; 740 | --print) 741 | shift 742 | LOGGER "Enabled Print Mode && Quiet Mode" 743 | Print_Mode=1 744 | Quiet_Mode=quiet 745 | ;; 746 | *) 747 | LOGGER "跳过未知参数: [$1] ..." 748 | shift 749 | ;; 750 | esac 751 | done 752 | LOGGER "传入参数解析完成!" 753 | case "${DL_Downloader}" in 754 | wget | wget-ssl) 755 | DL_Template="wget-ssl --quiet --no-check-certificate --no-dns-cache -x -4 --tries 1 --timeout 5 -O" 756 | ;; 757 | curl) 758 | DL_Template="curl --silent --insecure -L -k --connect-timeout 5 --retry 1 -o" 759 | ;; 760 | uclient-fetch) 761 | DL_Template="uclient-fetch --quiet --no-check-certificate -4 --timeout 5 -O" 762 | ;; 763 | esac 764 | [[ ${Test_Mode} == 1 || ${Verbose_Mode} == 1 ]] && { 765 | DL_Template="${DL_Template/ --quiet / }" 766 | DL_Template="${DL_Template/ --silent / }" 767 | } 768 | [[ -n ${DL_Timeout} ]] && DL_Template="${DL_Template/-timeout 5/-timeout ${DL_Timeout}}" 769 | local E=0 u;while [[ ${E} != ${DL_URL_Count} ]];do 770 | DL_URL_Cache="${DL_URL[$E]}" 771 | DL_Retries="${DL_URL_Cache##*@@}" 772 | [[ -z ${DL_Retries} || ! ${DL_Retries} == [0-9] ]] && DL_Retries=1 773 | DL_URL_Final="${DL_URL_Cache%*@@*}" 774 | LOGGER "当前 URL: [${DL_URL_Final}] URL 重试次数: [${DL_Retries}]" 775 | for u in $(seq ${DL_Retries});do 776 | sleep 1 777 | [[ -z ${Failed} ]] && { 778 | ECHO ${Quiet_Mode} "正在下载${DL_Type},请耐心等待 ..." 779 | } || { 780 | ECHO ${Quiet_Mode} "尝试重新下载${DL_Type},剩余重试次数: [${DL_Retires_All}]" 781 | } 782 | if [[ -z ${DL_Name} ]];then 783 | DL_Name="${DL_URL_Final##*/}" 784 | DL_Final="${DL_Template} ${DL_Path}/${DL_Name} ${DL_URL_Final}" 785 | else 786 | [[ ${No_URL_Name} == 1 ]] && { 787 | DL_Final="${DL_Template} ${DL_Path}/${DL_Name} ${DL_URL_Final}" 788 | } || DL_Final="${DL_Template} ${DL_Path}/${DL_Name} ${DL_URL_Final}/${DL_Name}" 789 | fi 790 | [[ -f ${DL_Path}/${DL_Name} ]] && { 791 | LOGGER "删除已存在的文件: [${DL_Path}/${DL_Name}] ..." 792 | RM ${DL_Path}/${DL_Name} 793 | } 794 | LOGGER "执行下载: [${DL_Final}]" 795 | ${DL_Final} 796 | if [[ $? == 0 && -s ${DL_Path}/${DL_Name} ]];then 797 | ECHO y ${Quiet_Mode} "${DL_Type}下载成功!" 798 | [[ ${Print_Mode} == 1 ]] && { 799 | cat ${DL_Path}/${DL_Name} 2> /dev/null 800 | RM ${DL_Path}/${DL_Name} 801 | } 802 | touch -a ${DL_Path}/${DL_Name} 2> /dev/null 803 | return 0 804 | else 805 | [[ -z ${Failed} ]] && local Failed=1 806 | DL_Retires_All=$((${DL_Retires_All} - 1)) 807 | if [[ ${u} == ${DL_Retries} ]];then 808 | break 1 809 | else 810 | ECHO r ${Quiet_Mode} "下载失败!" 811 | u=$((${u} + 1)) 812 | fi 813 | fi 814 | done 815 | E=$((${E} + 1)) 816 | done 817 | RM ${DL_Path}/${DL_Name} 818 | ECHO r ${Quiet_Mode} "${DL_Type}下载失败,请检查网络后重试!" 819 | return 1 820 | } 821 | 822 | function DO_UPGRADE() { 823 | ECHO r "警告: 固件更新期间请不要断开电源或尝试重启设备!" 824 | sleep 3 825 | ECHO g "正在更新固件,请耐心等待 ..." 826 | $* 827 | [[ $? -ne 0 ]] && { 828 | ECHO r "固件刷写失败,请尝试使用 autoupdate -F 指令再次更新固件!" 829 | ECHO r "脚本与固件更新问题请前往 [${Github}] 进行反馈, 请附上 AutoUpdate 运行日志与系统信息" 830 | EXIT 1 831 | } || EXIT 0 832 | } 833 | 834 | function REMOVE_CACHE() { 835 | rm -rf ${Running_Path}/API \ 836 | ${Running_Path}/Update_Logs \ 837 | ${Running_Path}/API_Dump 2> /dev/null 838 | LOGGER "AutoUpdate 缓存清理完成!" 839 | } 840 | 841 | function LOG() { 842 | [[ -z $1 ]] && { 843 | [[ -s ${Log_Path}/AutoUpdate.log ]] && { 844 | TITLE && echo 845 | cat ${Log_Path}/AutoUpdate.log 846 | EXIT 0 847 | } 848 | } || { 849 | while [[ $1 ]];do 850 | case "$1" in 851 | --path) 852 | [[ $2 == ${Log_Path} ]] && { 853 | ECHO y "AutoUpdate 日志保存路径相同,无需修改!" 854 | EXIT 0 855 | } 856 | [[ -f $2 ]] && { 857 | ECHO r "AutoUpdate 日志保存路径有误,请重新输入!" 858 | EXIT 1 859 | } 860 | EDIT_VARIABLE rm ${Custom_Variable} Log_Path 861 | EDIT_VARIABLE edit ${Custom_Variable} Log_Path $2 862 | [[ ! -d $2 ]] && mkdir -p $2 863 | [[ -f $2/AutoUpdate.log ]] && mv ${Log_Path}/AutoUpdate.log $2 864 | Log_Path="$2" 865 | ECHO y "AutoUpdate 日志保存路径已修改为: [$2]!" 866 | ;; 867 | del | rm | clean) 868 | RM ${Log_Path}/AutoUpdate.log 869 | ;; 870 | *) 871 | SHELL_HELP 872 | ;; 873 | esac 874 | EXIT 0 875 | done 876 | } 877 | } 878 | 879 | URL_X() { 880 | #URL_X https://raw.githubusercontent.com/Hyy2001X/AutoBuild-Actions/master/Scripts/AutoUpdate.sh F@@1 G@@1 X@@1 881 | local URL=$1 Type URL_Final 882 | [[ ${URL} =~ raw.githubusercontent.com ]] && Type=raw 883 | [[ ${URL} =~ releases/download ]] && Type=release 884 | [[ ${URL} =~ codeload.github.com ]] && Type=codeload 885 | 886 | case "${Type}" in 887 | raw) 888 | FastGit=https://raw.fastgit.org/$(echo ${URL##*com/}) 889 | Ghproxy=https://ghproxy.com/${URL} 890 | ;; 891 | release) 892 | FastGit=https://download.fastgit.org/$(echo ${URL##*com/}) 893 | Ghproxy=https://ghproxy.com/${URL} 894 | ;; 895 | codeload) 896 | FastGit=https://download.fastgit.org/$(echo ${URL##*com/}) 897 | Ghproxy=https://ghproxy.com/${URL} 898 | ;; 899 | esac 900 | while [[ $1 ]];do 901 | local URL_Cache=$1 URL_Final 902 | case "$1" in 903 | F@@*) 904 | URL_Final="${URL_Cache/F/${FastGit}}" 905 | ;; 906 | G@@*) 907 | URL_Final="${URL_Cache/G/${Ghproxy}}" 908 | ;; 909 | X@@*) 910 | URL_Final="${URL_Cache/X/${URL}}" 911 | ;; 912 | esac 913 | [[ -n ${URL_Final} ]] && { 914 | echo "${URL_Final}" 915 | LOGGER "[URL_X] ${URL_Final}" 916 | } 917 | unset URL_Final 918 | shift 919 | done 920 | } 921 | 922 | function NETWORK_CHECK() { 923 | ping $1 -c 1 -W $2 > /dev/null 2>&1 924 | [[ $? == 0 ]] && echo true || echo false 925 | } 926 | 927 | function AutoUpdate_Main() { 928 | LOGGER "[${COMMAND}] 开始运行" 929 | if [[ ! $1 =~ (-H|--help) ]];then 930 | [[ ! -f ${Default_Variable} ]] && { 931 | ECHO r "脚本运行环境检测失败,无法正常运行脚本!" 932 | EXIT 1 933 | } 934 | [[ ! -f ${Custom_Variable} ]] && touch ${Custom_Variable} 935 | LOAD_VARIABLE ${Default_Variable} ${Custom_Variable} 936 | [[ ! -d ${Running_Path} ]] && { 937 | mkdir -p ${Running_Path} 938 | [[ ! $? == 0 ]] && { 939 | ECHO r "脚本运行目录 [${Running_Path}] 创建失败!" 940 | EXIT 1 941 | } 942 | } 943 | fi 944 | 945 | [[ -z $* ]] && UPGRADE $* 946 | 947 | local Input=($@) E=0 F Custom_Path Custom_URL 948 | while :;do 949 | F="${Input[${E}]}" 950 | case "${F}" in 951 | -T) 952 | Test_Mode=1 953 | ;; 954 | --verbose) 955 | Verbose_Mode=1 956 | ;; 957 | --path) 958 | Custom_Path="${Input[$((${E} + 1))]}" 959 | [[ -z ${Custom_Path} ]] && { 960 | ECHO r "请输入正确的路径!" 961 | } 962 | ;; 963 | --url) 964 | Custom_URL="${Input[$((${E} + 1))]}" 965 | [[ -z ${Custom_URL} || ! ${Custom_URL} =~ (https://*|http://*|ftp://*) ]] && { 966 | ECHO r "链接格式错误,请输入正确的链接!" 967 | EXIT 1 968 | } 969 | ;; 970 | -D) 971 | case "${Input[$((${E} + 1))]}" in 972 | wget | curl | wget-ssl | uclient-fetch) 973 | DOWNLOADERS=${Input[$((${E} + 1))]} 974 | ;; 975 | *) 976 | ECHO r "暂不支持当前下载器: [${Input[$((${E} + 1))]}]" 977 | EXIT 1 978 | ;; 979 | esac 980 | ;; 981 | esac 982 | [[ ${E} == ${#Input[@]} ]] && break 983 | E=$((${E} + 1)) 984 | done 985 | 986 | while [[ $1 ]];do 987 | case "$1" in 988 | -n | -f | -u | -T | -P | --proxy | -F | --force-write | --verbose | --decompress | --skip-verify | -D | --path) 989 | UPGRADE $* 990 | EXIT 2 991 | ;; 992 | --backup) 993 | local FILE="backup-$(uname -n)-$(date +%Y-%m-%d)-$(RANDOM 5).tar.gz" 994 | shift 995 | [[ $# -gt 1 ]] && SHELL_HELP 996 | [[ -z $1 ]] && { 997 | FILE=$(pwd)/${FILE} 998 | } || { 999 | if [[ ! -d $1 ]];then 1000 | mkdir -p $1 || { 1001 | ECHO r "备份存放目录 [$1] 创建失败!" 1002 | EXIT 1 1003 | } 1004 | fi 1005 | FILE=$1/${FILE} 1006 | } 1007 | ECHO "正在备份系统文件到 [${FILE}] ..." 1008 | sysupgrade -b "${FILE}" > /dev/null 2>&1 1009 | [[ $? == 0 ]] && { 1010 | ECHO y "备份文件创建成功!" 1011 | EXIT 0 1012 | } || { 1013 | ECHO r "备份文件 [${FILE}] 创建失败!" 1014 | EXIT 1 1015 | } 1016 | ;; 1017 | --clean) 1018 | shift && [[ -n $* ]] && SHELL_HELP 1019 | REMOVE_CACHE 1020 | EXIT 0 1021 | ;; 1022 | --check) 1023 | shift && [[ -n $* ]] && SHELL_HELP 1024 | CHECK_DEPENDS bash uclient-fetch curl wget openssl jsonfilter 1025 | [[ $(NETWORK_CHECK 223.5.5.5 2) == false ]] && { 1026 | ECHO r "网络连接错误!" 1027 | } || ECHO y "网络连接正常!" 1028 | CHECK_ENV ${ENV_DEPENDS} 1029 | EXIT 0 1030 | ;; 1031 | --env-list) 1032 | shift 1033 | [[ -z $* ]] && LIST_ENV 0 && EXIT 0 1034 | case "$1" in 1035 | 1 | 2) 1036 | LIST_ENV $1 1037 | ;; 1038 | *) 1039 | SHELL_HELP 1040 | ;; 1041 | esac 1042 | EXIT 2 1043 | ;; 1044 | -V) 1045 | shift 1046 | [[ -z $* ]] && echo "${CURRENT_Version}" && EXIT 1 1047 | case "$1" in 1048 | [Cc]loud) 1049 | shift 1050 | GET_API 1051 | GET_CLOUD_INFO $* version 1052 | ;; 1053 | *) 1054 | SHELL_HELP 1055 | ;; 1056 | esac 1057 | EXIT 2 1058 | ;; 1059 | --fw-log) 1060 | shift 1061 | GET_API 1062 | [[ -z $* ]] && GET_FW_LOG local 1063 | case "$1" in 1064 | [Cc]loud) 1065 | GET_FW_LOG $1 1066 | ;; 1067 | *) 1068 | [[ -z $* ]] && EXIT 0 1069 | [[ ! $(FW_VERSION_CHECK $1) == true ]] && { 1070 | ECHO r "固件版本号合法性检查失败!" 1071 | EXIT 1 1072 | } || { 1073 | GET_FW_LOG -v $1 1074 | } 1075 | ;; 1076 | esac 1077 | EXIT 2 1078 | ;; 1079 | --list) 1080 | shift 1081 | SHOW_VARIABLE 1082 | EXIT 0 1083 | ;; 1084 | --var) 1085 | local Result 1086 | shift 1087 | [[ $# != 1 ]] && SHELL_HELP 1088 | Result=$(GET_VARIABLE "$1" ${Custom_Variable}) 1089 | [[ -z ${Result} ]] && Result=$(GET_VARIABLE "$1" ${Default_Variable}) 1090 | [[ -n ${Result} ]] && echo "${Result}" 1091 | EXIT 2 1092 | ;; 1093 | -v) 1094 | shift 1095 | [[ -z $* ]] && echo ${Version} && EXIT 0 1096 | case "$1" in 1097 | [Cc]loud) 1098 | Script_URL="$(URL_X ${Github_Raw}/Scripts/AutoUpdate.sh G@@1)" 1099 | DOWNLOADER --dl ${DOWNLOADERS} --url ${Script_URL} --path /tmp --print | egrep -o "V[0-9].+" 1100 | ;; 1101 | *) 1102 | SHELL_HELP 1103 | esac 1104 | EXIT 2 1105 | ;; 1106 | -x) 1107 | shift 1108 | Script_URL="$(URL_X ${Github_Raw}/Scripts/AutoUpdate.sh G@@1 F@@1 X@@1)" 1109 | [[ $(NETWORK_CHECK 223.5.5.5 2) == false ]] && { 1110 | ECHO r "网络连接错误,请稍后再试!" 1111 | EXIT 1 1112 | } 1113 | Script_Path=/bin 1114 | [[ -n ${Custom_Path} ]] && Script_Path=${Custom_Path} 1115 | [[ -n ${Custom_URL} ]] && Script_URL=${Custom_URL} 1116 | UPDATE_SCRIPT ${Script_Path} ${Script_URL} 1117 | EXIT 2 1118 | ;; 1119 | -B | --boot-mode) 1120 | shift 1121 | [[ ${TARGET_BOARD} != x86 ]] && EXIT 1 1122 | CHANGE_BOOT $1 1123 | EXIT 2 1124 | ;; 1125 | -C) 1126 | shift 1127 | CHANGE_GITHUB $1 1128 | EXIT 2 1129 | ;; 1130 | -H | --help) 1131 | SHELL_HELP 1132 | EXIT 2 1133 | ;; 1134 | -L | --log) 1135 | shift 1136 | LOG $* 1137 | EXIT 2 1138 | ;; 1139 | -O) 1140 | GET_API 1141 | GET_CLOUD_INFO -a name 1142 | EXIT 0 1143 | ;; 1144 | *) 1145 | SHELL_HELP 1146 | EXIT 1 1147 | ;; 1148 | esac 1149 | done 1150 | } 1151 | 1152 | Running_Path=/tmp/AutoUpdate 1153 | Log_Path=/tmp 1154 | API_File=${Running_Path}/API 1155 | Default_Variable=/etc/AutoBuild/Default_Variable 1156 | Custom_Variable=/etc/AutoBuild/Custom_Variable 1157 | ENV_DEPENDS="Author Github TARGET_PROFILE TARGET_BOARD TARGET_SUBTARGET Firmware_Format CURRENT_Version OP_Maintainer OP_BRANCH OP_REPO_NAME" 1158 | DOWNLOADERS="wget-ssl curl wget uclient-fetch" 1159 | 1160 | White="\e[0m" 1161 | Yellow="\e[33m" 1162 | Red="\e[31m" 1163 | Blue="\e[34m" 1164 | Grey="\e[36m" 1165 | Green="\e[32m" 1166 | 1167 | [[ -n $* ]] && COMMAND="$0 $*" || COMMAND="$0" 1168 | AutoUpdate_Main $* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Hyy2001X 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoUpdate for Openwrt 2 | 3 | ## Usage: 4 | 5 | 打开 Openwrt 主页,点击`系统`-`TTYD 终端`或者在浏览器输入`192.168.1.1:7681`,按需输入下方指令: 6 | 7 | 更新固件(保留配置): `bash /bin/AutoUpdate.sh` 8 | 9 | 更新固件(不保留配置): `bash /bin/AutoUpdate.sh -n` 10 | 11 | 更多使用方法: `bash /bin/AutoUpdate.sh --help` 12 | 13 | ## 自动编译 [AutoBuild-Actions](https://github.com/Hyy2001X/AutoBuild-Actions) 14 | --------------------------------------------------------------------------------