apply_patchset (4348B)
1 #!/bin/zsh 2 . $0:h/common.zsh || exit $? 3 4 main() { 5 if (( $# < 1 )); then 6 warn_msg usage: "cd <patchdir> && apply_patchset <one or more patch names>" 7 exit 100 8 fi 9 local patch 10 for patch in "$@"; do 11 if [[ $patch == *[\\\']* ]]; then 12 err_msg ERROR: "disallowed character in patch name: $1" 13 exit 100 14 fi 15 done 16 - parse_patch_set $@ 17 - load_installed_patches 18 - verify_patch_set $@ 19 - install_patch_set $@ 20 } 21 typeset -f -t main 22 23 # --- 24 25 load_installed_patches() { 26 typeset -ga installed_patches conflicting_patches 27 installed_patches=( ${(f)"$( psql -AP tuples_only=on -c 'select patch_name from _v.patches' )"} ) || \ 28 exit $? 29 conflicting_patches=( ${(f)"$( psql -AP tuples_only=on -c 'select distinct con from _v.patches, lateral unnest(conflicts) con;' )"} ) || \ 30 exit $? 31 } 32 33 # --- 34 35 parse_patch_set() { 36 typeset -gA patch_parsed patch_deps patch_cons 37 local patch 38 for patch in "$@"; do 39 - parse_patch $patch 40 done 41 } 42 43 parse_patch() { 44 (( $+patch_parsed[$1] )) && return 0 45 if [[ $1 == *[\\\']* ]]; then 46 printf >&2 'ERROR: disallowed character in patch name: %s\n' $1 47 exit 1 48 fi 49 50 local line have_start=false 51 local -a deps cons 52 <$1 while IFS= read line; do 53 if ! $have_start; then 54 if [[ $line == "-- VPATCH" ]]; then 55 have_start=true 56 continue 57 else 58 die "$1 doesn't have VPATCH header" 59 fi 60 else 61 if [[ $line == '' ]]; then 62 break 63 fi 64 line=${line##--[[:space:]]#} 65 case $line in 66 (VDEP:*) deps+=${line#VDEP:};; 67 (VCON:*) cons+=${line#VCON:};; 68 (*) die "unexpected line in $1: ${(qqq)line}"; 69 esac 70 fi 71 done 72 73 patch_deps[$1]=${(pj:\0:)deps} 74 patch_cons[$1]=${(pj:\0:)cons} 75 patch_parsed[$1]=1 76 77 local dep 78 for dep in $deps; do 79 - parse_patch $dep 80 done 81 } 82 83 # --- 84 85 verify_patch_set() { 86 unset verify_install 87 unset verify_conflict 88 typeset -gA verify_install 89 typeset -gA verify_conflict 90 local patch 91 for patch in "$@"; do 92 - verify_patch $patch '<commandline>' 93 done 94 } 95 96 print_dep_traceback() { 97 local dep indent 98 indent=${1:->} 99 (($+verify_install[$1])) || return 100 for dep in ${(f)verify_install[$1]}; do 101 - warn_msg $indent patch ${(qqq)1} was pulled in by ${(qqq)dep} 102 - print_dep_traceback $dep "$indent >" 103 done 104 } 105 106 print_con_traceback() { 107 local dep indent 108 indent=${1:-!} 109 (($+verify_install[$1])) || return 110 for dep in ${(f)verify_install[$1]}; do 111 - warn_msg $indent conflict with ${(qqq)1} is caused by ${(qqq)dep} 112 - print_dep_traceback $dep "$indent >" 113 done 114 } 115 116 extend_install_list() { 117 [[ $# == 2 ]] || die "exactly two arguments required" 118 verify_install[$1]+=$2$'\n' 119 if (( ${conflicting_patches[(I)$1]} )); then 120 - err_msg ERROR cannot install patch $1 as it conflicts with current database 121 - print_dep_traceback $1 122 exit 3 123 elif (( $+verify_conflict[$1] )); then 124 - err_msg ERROR cannot install patch $1 as it conflicts with another patch 125 - print_con_traceback $1 126 - print_dep_traceback $1 127 exit 3 128 fi 129 } 130 131 extend_conflict_list() { 132 [[ $# == 2 ]] || die "exactly two arguments required" 133 verify_conflict[$1]+=$2$'\n' 134 if (( ${installed_patches[(I)$1]} )); then 135 - err_msg ERROR cannot install patch $2 as it creates conflict with $1 in the current database 136 - print_con_traceback $1 137 exit 3 138 elif (( $+verify_install[$1] )); then 139 - err_msg ERROR cannot install patch $2 as it creates conflict with another patch 140 - print_con_traceback $1 141 - print_dep_traceback $1 142 exit 3 143 fi 144 } 145 146 verify_patch() { 147 [[ $# == 2 ]] || die "exactly two arguments required" 148 # (( ${installed_patches[(I)$1]} )) && return 0 149 (( $+verify_install[$1] )) && return 0 150 local -a deps cons 151 local dep con 152 extend_install_list $1 $2 153 for dep in ${(ps:\0:)patch_deps[$1]}; do 154 - verify_patch $dep $1 155 done 156 for con in ${(ps:\0:)patch_cons[$1]}; do 157 - extend_conflict_list $con $1 158 done 159 } 160 161 # --- 162 163 install_patch_set() { 164 local patch 165 for patch in "$@"; do 166 - install_patch $patch 167 done 168 } 169 170 install_patch() { 171 (( ${installed_patches[(I)$1]} )) && return 0 172 if [[ $1 == *[\\\']* ]]; then 173 printf >&2 'ERROR: disallowed character in patch name: %s\n' $1 174 exit 1 175 fi 176 177 local dep 178 for dep in ${(ps:\0:)patch_deps[$1]}; do 179 - install_patch $dep 180 done 181 182 printf '%s\n' \ 183 '\set ON_ERROR_STOP' \ 184 "select _v.register_patch('$1', NULL, NULL);" \ 185 "\i $1" \ 186 | psql -1 -e -f - || die ERROR: "installing patch ${(qqq)1} failed with exitcode $#" 187 installed_patches+=$1 188 } 189 190 # --- 191 192 main "$@"