From ebab7d3213fe759d86fd3d757cfaec09db7588f0 Mon Sep 17 00:00:00 2001 From: lingkai5wu Date: Sat, 10 Oct 2026 17:37:11 +0800 Subject: [PATCH] refactor: simplify staged stack configuration --- manage.conf | 30 +++++--------- manage.sh | 114 ++++++++++++++++++++++++++-------------------------- 2 files changed, 65 insertions(+), 79 deletions(-) diff --git a/manage.conf b/manage.conf index 613ae9d..a593340 100644 --- a/manage.conf +++ b/manage.conf @@ -1,26 +1,14 @@ -# Global maximum number of stacks executed concurrently within a group. +# Maximum number of stacks executed concurrently within each stage. # Use 0 for unlimited concurrency. -max_parallel 2 +max_parallel 3 -# Groups run from top to bottom for up/start/restart/pull/update. -# They run from bottom to top for stop/down. -group gateway --parallel 1 \ - nginx - -group network \ - adguardhome \ - ddns-go - -group services --parallel 3 \ - samba \ - immich \ - gitea \ - openlist \ - homeassistant \ - qbittorrent - -group backup --parallel 1 \ - backrest +# Each declaration is an ordered stage; stacks on the same line run in parallel. +# Add --parallel N before the stacks to override max_parallel for that stage. +# Example: before --parallel 2 stack-a stack-b +# Unlisted stacks run in the default stage. +before nginx +before adguardhome ddns-go +after backrest # Excluded stacks are ignored by all bulk commands. # exclude experimental-stack diff --git a/manage.sh b/manage.sh index 4d75a76..bc15eed 100755 --- a/manage.sh +++ b/manage.sh @@ -10,9 +10,10 @@ LOCK_FILE="${STACKS_LOCK_FILE:-/run/lock/stacks-manage.lock}" DEFAULT_MAX_PARALLEL=2 -CONFIG_GROUP_NAMES=() -CONFIG_GROUP_LIMITS=() -CONFIG_GROUP_STACKS=() +BEFORE_GROUPS=() +BEFORE_LIMITS=() +AFTER_GROUPS=() +AFTER_LIMITS=() CONFIGURED_STACKS=() EXCLUDED_STACKS=() @@ -21,7 +22,6 @@ MANAGED_STACKS=() GROUP_NAMES=() GROUP_LIMITS=() GROUP_STACKS=() -UNASSIGNED_STACKS=() RUN_SUCCESS=0 RUN_FAILED=0 @@ -52,9 +52,10 @@ Commands: list List discovered stacks and their group/status Bulk execution: - up/start/restart/pull/update run groups in declaration order. - stop/down run groups in reverse order. - Stacks within a group run in parallel up to that group's limit. + up/start/restart/pull/update run before, default, then after. + stop/down run the same groups in reverse order. + Each before/after declaration creates an ordered group. + Stacks within a group run in parallel up to its configured limit. ps/images/list keep ordered output. Configuration: $CONFIG_FILE @@ -88,52 +89,48 @@ validate_name() { die "invalid $kind name: $value" } -# Set the default concurrency for groups without an override. +# Set the maximum concurrency within each stage. max_parallel() { [[ $# -eq 1 ]] || die "max_parallel expects exactly one value" validate_parallel_limit "$1" DEFAULT_MAX_PARALLEL="$1" } -# Declare an ordered group and its stacks. -# Usage: group NAME [--parallel N] STACK... -group() { - [[ $# -ge 2 ]] || die "group expects NAME [--parallel N] STACK..." - - local name="$1" +add_stage_group() { + local groups_name="$1" + local limits_name="$2" + local stage="$3" local limit="" local stack - shift - - validate_name "group" "$name" - [[ "$name" != "unassigned" ]] || - die "group name is reserved: $name" - contains "$name" "${CONFIG_GROUP_NAMES[@]}" && - die "group is declared more than once: $name" + local -n groups="$groups_name" + local -n limits="$limits_name" + shift 3 if [[ "${1:-}" == "--parallel" ]]; then - [[ $# -ge 3 ]] || die "group $name: --parallel expects a value and at least one stack" + [[ $# -ge 3 ]] || die "$stage --parallel expects a value and at least one stack" limit="$2" validate_parallel_limit "$limit" shift 2 fi - [[ $# -ge 1 ]] || die "group has no stacks: $name" + [[ $# -ge 1 ]] || die "$stage expects at least one stack" for stack in "$@"; do validate_name "stack" "$stack" contains "$stack" "${CONFIGURED_STACKS[@]}" && - die "stack is assigned to more than one group: $stack" + die "stack is assigned more than once: $stack" contains "$stack" "${EXCLUDED_STACKS[@]}" && - die "stack is both grouped and excluded: $stack" + die "stack is both assigned and excluded: $stack" CONFIGURED_STACKS+=("$stack") done - CONFIG_GROUP_NAMES+=("$name") - CONFIG_GROUP_LIMITS+=("$limit") - CONFIG_GROUP_STACKS+=("$*") + groups+=("$*") + limits+=("$limit") } +before() { add_stage_group BEFORE_GROUPS BEFORE_LIMITS before "$@"; } +after() { add_stage_group AFTER_GROUPS AFTER_LIMITS after "$@"; } + # Exclude stacks from all bulk operations. exclude() { [[ $# -ge 1 ]] || die "exclude expects at least one stack" @@ -144,7 +141,7 @@ exclude() { contains "$stack" "${EXCLUDED_STACKS[@]}" && die "stack is excluded more than once: $stack" contains "$stack" "${CONFIGURED_STACKS[@]}" && - die "stack is both grouped and excluded: $stack" + die "stack is both assigned and excluded: $stack" EXCLUDED_STACKS+=("$stack") done } @@ -152,7 +149,7 @@ exclude() { load_config() { [[ -f "$CONFIG_FILE" ]] || return 0 - # The trusted config calls max_parallel, group, and exclude above. + # The trusted config calls max_parallel, before, after, and exclude above. source "$CONFIG_FILE" || die "cannot load config file: $CONFIG_FILE" } @@ -187,45 +184,52 @@ validate_config_stacks() { done } -build_execution_groups() { - local i stack limit +append_stage_groups() { + local stage="$1" + local groups_name="$2" + local limits_name="$3" + local i name local -a stacks=() + local -n groups="$groups_name" + local -n limits="$limits_name" + + for ((i = 0; i < ${#groups[@]}; i++)); do + name="$stage" + ((${#groups[@]} > 1)) && name+="-$((i + 1))" + GROUP_NAMES+=("$name") + GROUP_LIMITS+=("${limits[i]:-$DEFAULT_MAX_PARALLEL}") + GROUP_STACKS+=("${groups[i]}") + read -r -a stacks <<<"${groups[i]}" + MANAGED_STACKS+=("${stacks[@]}") + done +} + +build_execution_groups() { + local stack + local -a default_stacks=() GROUP_NAMES=() GROUP_LIMITS=() GROUP_STACKS=() MANAGED_STACKS=() - UNASSIGNED_STACKS=() - for ((i = 0; i < ${#CONFIG_GROUP_NAMES[@]}; i++)); do - limit="${CONFIG_GROUP_LIMITS[i]:-$DEFAULT_MAX_PARALLEL}" - GROUP_NAMES+=("${CONFIG_GROUP_NAMES[i]}") - GROUP_LIMITS+=("$limit") - GROUP_STACKS+=("${CONFIG_GROUP_STACKS[i]}") - - read -r -a stacks <<<"${CONFIG_GROUP_STACKS[i]}" - MANAGED_STACKS+=("${stacks[@]}") - done + append_stage_groups before BEFORE_GROUPS BEFORE_LIMITS for stack in "${DISCOVERED_STACKS[@]}"; do if ! contains "$stack" "${CONFIGURED_STACKS[@]}" && ! contains "$stack" "${EXCLUDED_STACKS[@]}"; then - UNASSIGNED_STACKS+=("$stack") - MANAGED_STACKS+=("$stack") + default_stacks+=("$stack") fi done - if ((${#UNASSIGNED_STACKS[@]} > 0)); then - GROUP_NAMES+=("unassigned") + if ((${#default_stacks[@]} > 0)); then + GROUP_NAMES+=("default") GROUP_LIMITS+=("$DEFAULT_MAX_PARALLEL") - GROUP_STACKS+=("${UNASSIGNED_STACKS[*]}") + GROUP_STACKS+=("${default_stacks[*]}") + MANAGED_STACKS+=("${default_stacks[@]}") fi -} -warn_unassigned() { - ((${#UNASSIGNED_STACKS[@]} == 0)) && return 0 - - echo "WARNING: unassigned stacks will run in the final implicit group: ${UNASSIGNED_STACKS[*]}" >&2 + append_stage_groups after AFTER_GROUPS AFTER_LIMITS } stack_group() { @@ -463,8 +467,6 @@ run_all_grouped() { RUN_FAILED=0 RUN_FAILED_STACKS=() RUN_SKIPPED_GROUPS=() - warn_unassigned - if [[ "$action" == "stop" || "$action" == "down" ]]; then direction="reverse" start=$((end - 1)) @@ -487,8 +489,6 @@ run_all_sequential() { local action="$1" local stack local -a failed_stacks=() - warn_unassigned - for stack in "${MANAGED_STACKS[@]}"; do run_stack "$stack" "$action" || failed_stacks+=("$stack") done @@ -503,7 +503,6 @@ run_all_sequential() { list_stacks() { local stack group_name status - warn_unassigned printf '%-24s %-16s %s\n' "STACK" "GROUP" "STATUS" printf '%-24s %-16s %s\n' "------------------------" "----------------" "---------------" @@ -546,7 +545,6 @@ list_images() { return fi - warn_unassigned for stack in "${MANAGED_STACKS[@]}"; do stack_images "$stack" || ((failed += 1)) done