From 47efae4e6cc9062f899bc6b5e65d4489706d8063 Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Sun, 4 Apr 2021 15:40:09 +0200 Subject: [PATCH 1/8] Keep a fresh copy of apps.json Ref #132 --- abra | 58 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/abra b/abra index 4928bf3..f069a02 100755 --- a/abra +++ b/abra @@ -6,6 +6,7 @@ ABRA_VERSION="0.6.0" ABRA_BACKUP_DIR="${ABRA_BACKUP_DIR:-$ABRA_DIR/backups}" ABRA_VENDOR_DIR="$ABRA_DIR/vendor" YQ="$ABRA_VENDOR_DIR/yq" +ABRA_APPS_JSON="${ABRA_DIR}/apps.json" ####################################### # Global help @@ -434,7 +435,29 @@ require_consent_for_update() { fi } -require_plugin (){ +require_apps_json() { + # Ensure we have the latest copy of apps.json + apps_url="https://abra-apps.cloud.autonomic.zone" + if [ -f "$ABRA_APPS_JSON" ]; then + modified=$(curl --silent --head $apps_url | \ + awk '/^Last-Modified/{print $0}' | \ + sed 's/^Last-Modified: //') + remote_ctime=$(date --date="$modified" +%s) + local_ctime=$(stat -c %Z "$ABRA_APPS_JSON") + + if [ "$local_ctime" -lt "$remote_ctime" ]; then + info "Downloading new apps.json" + wget -qO "$ABRA_APPS_JSON" $apps_url + else + debug "No apps.json update needed" + fi + else + info "Downloading apps.json" + wget -qO "$ABRA_APPS_JSON" $apps_url + fi +} + +require_plugin() { PLUGIN="$1" BRANCH="${abra___branch:-master}" @@ -501,32 +524,42 @@ require_app_latest() { fi } -require_yq() { +vendor_binary() { require_vendor_dir require_binary wget - YQ_VERSION="4.6.1" - YQ_BINARY="yq_linux_amd64" - YQ_RELEASE_URL="https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/${YQ_BINARY}" + local REPO="$1" + local VERSION="$2" + local FILE="$3" + local BINARY="${REPO##*/}" + local RELEASE_URL="$REPO/releases/download/${VERSION}/${FILE}" - if [ -f "$YQ" ]; then - debug "yq is already vendored" + if [ -f "$ABRA_DIR/vendor/$BINARY" ]; then + debug "$BINARY is already vendored" return fi case $(uname -m) in x86_64) - warning "Attempting to download the yq binary into $ABRA_VENDOR_DIR" - wget -q "$YQ_RELEASE_URL" && chmod +x "$YQ_BINARY" && mv "$YQ_BINARY" "$YQ" - success "yq is now vendored ☮" + warning "Attempting to download the $BINARY binary from $RELEASE_URL into $ABRA_VENDOR_DIR" + wget -qO "$ABRA_VENDOR_DIR/$BINARY" "$RELEASE_URL" && chmod +x "$ABRA_VENDOR_DIR/$BINARY" + success "$BINARY is now vendored ☮" ;; *) - error "Unable to automatically vendor yq, you'll have to manually manage this\n - Please see https://mikefarah.gitbook.io/yq/#install and place the yq binary in the $YQ path" + error "Unable to automatically vendor $BINARY, you'll have to manually manage this.\n + Please see $REPO and place the $BINARY binary in $ABRA_VENDOR_DIR." ;; esac } +require_jq() { + vendor_binary "https://github.com/stedolan/jq/" "jq-1.6" "jq-linux64" +} + +require_yq() { + vendor_binary "https://github.com/mikefarah/yq" "v4.6.1" "yq_linux_amd64" +} + require_docker_version (){ get_servers @@ -2039,6 +2072,7 @@ abra() { if [ -n "$abra__app_" ]; then load_instance load_instance_env + require_apps_json fi load_abra_sh From 8b60ece3d46a081d00c2cfea3ba35947ccc49b29 Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Sun, 4 Apr 2021 16:55:18 +0200 Subject: [PATCH 2/8] Add "recipe" commands, rejig vendoring, vendor jq --- abra | 329 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 205 insertions(+), 124 deletions(-) diff --git a/abra b/abra index f069a02..6345ae7 100755 --- a/abra +++ b/abra @@ -5,7 +5,6 @@ ABRA_DIR="${ABRA_DIR:-$HOME/.abra}" ABRA_VERSION="0.6.0" ABRA_BACKUP_DIR="${ABRA_BACKUP_DIR:-$ABRA_DIR/backups}" ABRA_VENDOR_DIR="$ABRA_DIR/vendor" -YQ="$ABRA_VENDOR_DIR/yq" ABRA_APPS_JSON="${ABRA_DIR}/apps.json" ####################################### @@ -36,6 +35,8 @@ Usage: abra [options] app secret (rm|delete) (|--all) [--pass] abra [options] app undeploy abra [options] app [...] + abra [options] recipe ls + abra [options] recipe versions abra [options] server add [] [] abra [options] server new abra [options] server (list|ls) @@ -161,15 +162,15 @@ eval "var_$1+=($value)"; else eval "var_$1=$value"; fi; return 0; fi; done return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() { printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() { [[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() { -printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2162} -usage=${DOC:40:1553}; digest=b3855 -shorts=(-n -b -s -v -h -C -U -d -e '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') -longs=(--no-prompt --branch --stack --verbose --help --skip-check --skip-update --debug --env --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) -argcounts=(0 1 1 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ -switch __no_prompt 0; }; node_1(){ value __branch 1; }; node_2(){ -value __stack 2; }; node_3(){ switch __verbose 3; }; node_4(){ switch __help 4 -}; node_5(){ switch __skip_check 5; }; node_6(){ switch __skip_update 6; } -node_7(){ switch __debug 7; }; node_8(){ value __env 8; }; node_9(){ +printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2231} +usage=${DOC:40:1622}; digest=22c5b +shorts=(-h -b -n -s -e -d -C -v -U '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') +longs=(--help --branch --no-prompt --stack --env --debug --skip-check --verbose --skip-update --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) +argcounts=(0 1 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ +switch __help 0; }; node_1(){ value __branch 1; }; node_2(){ +switch __no_prompt 2; }; node_3(){ value __stack 3; }; node_4(){ value __env 4 +}; node_5(){ switch __debug 5; }; node_6(){ switch __skip_check 6; }; node_7(){ +switch __verbose 7; }; node_8(){ switch __skip_update 8; }; node_9(){ switch __status 9; }; node_10(){ value __server 10; }; node_11(){ value __type 11; }; node_12(){ value __domain 12; }; node_13(){ value __app_name 13; }; node_14(){ switch __pass 14; }; node_15(){ @@ -182,68 +183,71 @@ value _service_ a; }; node_27(){ value _src_ a; }; node_28(){ value _dst_ a; } node_29(){ value _backup_file_ a; }; node_30(){ value _args_ a true; } node_31(){ value _secret_ a; }; node_32(){ value _version_ a; }; node_33(){ value _cmd_ a; }; node_34(){ value _data_ a; }; node_35(){ value _command_ a; } -node_36(){ value _host_ a; }; node_37(){ value _user_ a; }; node_38(){ -value _port_ a; }; node_39(){ value _provider_ a; }; node_40(){ -value _subcommands_ a true; }; node_41(){ _command app; }; node_42(){ -_command list; }; node_43(){ _command ls; }; node_44(){ _command new; } -node_45(){ _command backup; }; node_46(){ _command deploy; }; node_47(){ -_command check; }; node_48(){ _command version; }; node_49(){ _command config; } -node_50(){ _command cp; }; node_51(){ _command logs; }; node_52(){ _command ps -}; node_53(){ _command restore; }; node_54(){ _command rm; }; node_55(){ -_command delete; }; node_56(){ _command run; }; node_57(){ _command rollback; } -node_58(){ _command secret; }; node_59(){ _command generate; }; node_60(){ -_command insert; }; node_61(){ _command undeploy; }; node_62(){ _command server -}; node_63(){ _command add; }; node_64(){ _command init; }; node_65(){ -_command apps; }; node_66(){ _command upgrade; }; node_67(){ _command doctor; } -node_68(){ _command help; }; node_69(){ optional 0 1 2 3 4 5 6 7 8; } -node_70(){ optional 69; }; node_71(){ either 42 43; }; node_72(){ required 71; } -node_73(){ optional 9; }; node_74(){ optional 10; }; node_75(){ optional 11; } -node_76(){ required 70 41 72 73 74 75; }; node_77(){ optional 12; }; node_78(){ -optional 13; }; node_79(){ optional 14; }; node_80(){ optional 15; }; node_81(){ -required 70 41 44 74 77 78 79 80 24; }; node_82(){ either 26 16; }; node_83(){ -required 82; }; node_84(){ required 70 41 25 45 83; }; node_85(){ optional 17; } -node_86(){ optional 18; }; node_87(){ optional 19; }; node_88(){ -required 70 41 25 46 85 86 87; }; node_89(){ required 70 41 25 47; }; node_90(){ -required 70 41 25 48; }; node_91(){ required 70 41 25 49; }; node_92(){ -required 70 41 25 50 27 28; }; node_93(){ optional 26; }; node_94(){ -required 70 41 25 51 93; }; node_95(){ required 70 41 25 52; }; node_96(){ -required 70 41 25 53 83; }; node_97(){ either 54 55; }; node_98(){ required 97 -}; node_99(){ optional 20; }; node_100(){ required 70 41 25 98 99 80; } -node_101(){ optional 29; }; node_102(){ required 70 41 25 53 26 101; } -node_103(){ optional 21; }; node_104(){ optional 22; }; node_105(){ oneormore 30 -}; node_106(){ required 70 41 25 56 103 104 26 105; }; node_107(){ -required 70 41 25 57 26; }; node_108(){ required 31 32; }; node_109(){ -either 108 16; }; node_110(){ required 109; }; node_111(){ optional 33; } -node_112(){ required 70 41 25 58 59 110 111 79; }; node_113(){ -required 70 41 25 58 60 31 32 34 79; }; node_114(){ either 31 16; }; node_115(){ -required 114; }; node_116(){ required 70 41 25 58 98 115 79; }; node_117(){ -required 70 41 25 61; }; node_118(){ optional 105; }; node_119(){ -required 70 41 25 35 118; }; node_120(){ optional 37; }; node_121(){ optional 38 -}; node_122(){ required 70 62 63 36 120 121; }; node_123(){ required 70 62 44 39 -}; node_124(){ required 70 62 72; }; node_125(){ required 70 62 36 54; } -node_126(){ required 70 62 36 64; }; node_127(){ required 70 62 36 65 73; } -node_128(){ optional 23; }; node_129(){ required 70 66 128; }; node_130(){ -required 70 48; }; node_131(){ required 70 67; }; node_132(){ oneormore 40; } -node_133(){ optional 132; }; node_134(){ required 70 68 133; }; node_135(){ -required 70; }; node_136(){ -either 76 81 84 88 89 90 91 92 94 95 96 100 102 106 107 112 113 116 117 119 122 123 124 125 126 127 129 130 131 134 135 -}; node_137(){ required 136; }; cat <<<' docopt_exit() { -[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1553}" >&2 -exit 1; }'; unset var___no_prompt var___branch var___stack var___verbose \ -var___help var___skip_check var___skip_update var___debug var___env \ +node_36(){ value _recipe_ a; }; node_37(){ value _host_ a; }; node_38(){ +value _user_ a; }; node_39(){ value _port_ a; }; node_40(){ value _provider_ a +}; node_41(){ value _subcommands_ a true; }; node_42(){ _command app; } +node_43(){ _command list; }; node_44(){ _command ls; }; node_45(){ _command new +}; node_46(){ _command backup; }; node_47(){ _command deploy; }; node_48(){ +_command check; }; node_49(){ _command version; }; node_50(){ _command config; } +node_51(){ _command cp; }; node_52(){ _command logs; }; node_53(){ _command ps +}; node_54(){ _command restore; }; node_55(){ _command rm; }; node_56(){ +_command delete; }; node_57(){ _command run; }; node_58(){ _command rollback; } +node_59(){ _command secret; }; node_60(){ _command generate; }; node_61(){ +_command insert; }; node_62(){ _command undeploy; }; node_63(){ _command recipe +}; node_64(){ _command versions; }; node_65(){ _command server; }; node_66(){ +_command add; }; node_67(){ _command init; }; node_68(){ _command apps; } +node_69(){ _command upgrade; }; node_70(){ _command doctor; }; node_71(){ +_command help; }; node_72(){ optional 0 1 2 3 4 5 6 7 8; }; node_73(){ +optional 72; }; node_74(){ either 43 44; }; node_75(){ required 74; } +node_76(){ optional 9; }; node_77(){ optional 10; }; node_78(){ optional 11; } +node_79(){ required 73 42 75 76 77 78; }; node_80(){ optional 12; }; node_81(){ +optional 13; }; node_82(){ optional 14; }; node_83(){ optional 15; }; node_84(){ +required 73 42 45 77 80 81 82 83 24; }; node_85(){ either 26 16; }; node_86(){ +required 85; }; node_87(){ required 73 42 25 46 86; }; node_88(){ optional 17; } +node_89(){ optional 18; }; node_90(){ optional 19; }; node_91(){ +required 73 42 25 47 88 89 90; }; node_92(){ required 73 42 25 48; }; node_93(){ +required 73 42 25 49; }; node_94(){ required 73 42 25 50; }; node_95(){ +required 73 42 25 51 27 28; }; node_96(){ optional 26; }; node_97(){ +required 73 42 25 52 96; }; node_98(){ required 73 42 25 53; }; node_99(){ +required 73 42 25 54 86; }; node_100(){ either 55 56; }; node_101(){ +required 100; }; node_102(){ optional 20; }; node_103(){ +required 73 42 25 101 102 83; }; node_104(){ optional 29; }; node_105(){ +required 73 42 25 54 26 104; }; node_106(){ optional 21; }; node_107(){ +optional 22; }; node_108(){ oneormore 30; }; node_109(){ +required 73 42 25 57 106 107 26 108; }; node_110(){ required 73 42 25 58 26; } +node_111(){ required 31 32; }; node_112(){ either 111 16; }; node_113(){ +required 112; }; node_114(){ optional 33; }; node_115(){ +required 73 42 25 59 60 113 114 82; }; node_116(){ +required 73 42 25 59 61 31 32 34 82; }; node_117(){ either 31 16; }; node_118(){ +required 117; }; node_119(){ required 73 42 25 59 101 118 82; }; node_120(){ +required 73 42 25 62; }; node_121(){ optional 108; }; node_122(){ +required 73 42 25 35 121; }; node_123(){ required 73 63 44; }; node_124(){ +required 73 63 36 64; }; node_125(){ optional 38; }; node_126(){ optional 39; } +node_127(){ required 73 65 66 37 125 126; }; node_128(){ required 73 65 45 40; } +node_129(){ required 73 65 75; }; node_130(){ required 73 65 37 55; } +node_131(){ required 73 65 37 67; }; node_132(){ required 73 65 37 68 76; } +node_133(){ optional 23; }; node_134(){ required 73 69 133; }; node_135(){ +required 73 49; }; node_136(){ required 73 70; }; node_137(){ oneormore 41; } +node_138(){ optional 137; }; node_139(){ required 73 71 138; }; node_140(){ +required 73; }; node_141(){ +either 79 84 87 91 92 93 94 95 97 98 99 103 105 109 110 115 116 119 120 122 123 124 127 128 129 130 131 132 134 135 136 139 140 +}; node_142(){ required 141; }; cat <<<' docopt_exit() { +[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1622}" >&2 +exit 1; }'; unset var___help var___branch var___no_prompt var___stack \ +var___env var___debug var___skip_check var___verbose var___skip_update \ var___status var___server var___type var___domain var___app_name var___pass \ var___secrets var___all var___update var___force var___skip_version_check \ var___volumes var___no_tty var___user var___dev var__type_ var__app_ \ var__service_ var__src_ var__dst_ var__backup_file_ var__args_ var__secret_ \ -var__version_ var__cmd_ var__data_ var__command_ var__host_ var__user_ \ -var__port_ var__provider_ var__subcommands_ var_app var_list var_ls var_new \ -var_backup var_deploy var_check var_version var_config var_cp var_logs var_ps \ -var_restore var_rm var_delete var_run var_rollback var_secret var_generate \ -var_insert var_undeploy var_server var_add var_init var_apps var_upgrade \ -var_doctor var_help; parse 137 "$@"; local prefix=${DOCOPT_PREFIX:-''} -unset "${prefix}__no_prompt" "${prefix}__branch" "${prefix}__stack" \ -"${prefix}__verbose" "${prefix}__help" "${prefix}__skip_check" \ -"${prefix}__skip_update" "${prefix}__debug" "${prefix}__env" \ +var__version_ var__cmd_ var__data_ var__command_ var__recipe_ var__host_ \ +var__user_ var__port_ var__provider_ var__subcommands_ var_app var_list var_ls \ +var_new var_backup var_deploy var_check var_version var_config var_cp var_logs \ +var_ps var_restore var_rm var_delete var_run var_rollback var_secret \ +var_generate var_insert var_undeploy var_recipe var_versions var_server \ +var_add var_init var_apps var_upgrade var_doctor var_help; parse 142 "$@" +local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" "${prefix}__branch" \ +"${prefix}__no_prompt" "${prefix}__stack" "${prefix}__env" "${prefix}__debug" \ +"${prefix}__skip_check" "${prefix}__verbose" "${prefix}__skip_update" \ "${prefix}__status" "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ "${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ @@ -251,24 +255,24 @@ unset "${prefix}__no_prompt" "${prefix}__branch" "${prefix}__stack" \ "${prefix}_type_" "${prefix}_app_" "${prefix}_service_" "${prefix}_src_" \ "${prefix}_dst_" "${prefix}_backup_file_" "${prefix}_args_" \ "${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \ -"${prefix}_command_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \ -"${prefix}_provider_" "${prefix}_subcommands_" "${prefix}app" "${prefix}list" \ -"${prefix}ls" "${prefix}new" "${prefix}backup" "${prefix}deploy" \ -"${prefix}check" "${prefix}version" "${prefix}config" "${prefix}cp" \ -"${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ +"${prefix}_command_" "${prefix}_recipe_" "${prefix}_host_" "${prefix}_user_" \ +"${prefix}_port_" "${prefix}_provider_" "${prefix}_subcommands_" \ +"${prefix}app" "${prefix}list" "${prefix}ls" "${prefix}new" "${prefix}backup" \ +"${prefix}deploy" "${prefix}check" "${prefix}version" "${prefix}config" \ +"${prefix}cp" "${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ "${prefix}delete" "${prefix}run" "${prefix}rollback" "${prefix}secret" \ -"${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}server" \ -"${prefix}add" "${prefix}init" "${prefix}apps" "${prefix}upgrade" \ -"${prefix}doctor" "${prefix}help" -eval "${prefix}"'__no_prompt=${var___no_prompt:-false}' -eval "${prefix}"'__branch=${var___branch:-}' -eval "${prefix}"'__stack=${var___stack:-}' -eval "${prefix}"'__verbose=${var___verbose:-false}' +"${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ +"${prefix}versions" "${prefix}server" "${prefix}add" "${prefix}init" \ +"${prefix}apps" "${prefix}upgrade" "${prefix}doctor" "${prefix}help" eval "${prefix}"'__help=${var___help:-false}' -eval "${prefix}"'__skip_check=${var___skip_check:-false}' -eval "${prefix}"'__skip_update=${var___skip_update:-false}' -eval "${prefix}"'__debug=${var___debug:-false}' +eval "${prefix}"'__branch=${var___branch:-}' +eval "${prefix}"'__no_prompt=${var___no_prompt:-false}' +eval "${prefix}"'__stack=${var___stack:-}' eval "${prefix}"'__env=${var___env:-}' +eval "${prefix}"'__debug=${var___debug:-false}' +eval "${prefix}"'__skip_check=${var___skip_check:-false}' +eval "${prefix}"'__verbose=${var___verbose:-false}' +eval "${prefix}"'__skip_update=${var___skip_update:-false}' eval "${prefix}"'__status=${var___status:-false}' eval "${prefix}"'__server=${var___server:-}' eval "${prefix}"'__type=${var___type:-}' @@ -294,6 +298,7 @@ fi; eval "${prefix}"'_secret_=${var__secret_:-}' eval "${prefix}"'_version_=${var__version_:-}' eval "${prefix}"'_cmd_=${var__cmd_:-}'; eval "${prefix}"'_data_=${var__data_:-}' eval "${prefix}"'_command_=${var__command_:-}' +eval "${prefix}"'_recipe_=${var__recipe_:-}' eval "${prefix}"'_host_=${var__host_:-}' eval "${prefix}"'_user_=${var__user_:-}' eval "${prefix}"'_port_=${var__port_:-}' @@ -319,6 +324,8 @@ eval "${prefix}"'secret=${var_secret:-false}' eval "${prefix}"'generate=${var_generate:-false}' eval "${prefix}"'insert=${var_insert:-false}' eval "${prefix}"'undeploy=${var_undeploy:-false}' +eval "${prefix}"'recipe=${var_recipe:-false}' +eval "${prefix}"'versions=${var_versions:-false}' eval "${prefix}"'server=${var_server:-false}' eval "${prefix}"'add=${var_add:-false}' eval "${prefix}"'init=${var_init:-false}' @@ -327,25 +334,25 @@ eval "${prefix}"'upgrade=${var_upgrade:-false}' eval "${prefix}"'doctor=${var_doctor:-false}' eval "${prefix}"'help=${var_help:-false}'; local docopt_i=1 [[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do -declare -p "${prefix}__no_prompt" "${prefix}__branch" "${prefix}__stack" \ -"${prefix}__verbose" "${prefix}__help" "${prefix}__skip_check" \ -"${prefix}__skip_update" "${prefix}__debug" "${prefix}__env" \ -"${prefix}__status" "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ +declare -p "${prefix}__help" "${prefix}__branch" "${prefix}__no_prompt" \ +"${prefix}__stack" "${prefix}__env" "${prefix}__debug" "${prefix}__skip_check" \ +"${prefix}__verbose" "${prefix}__skip_update" "${prefix}__status" \ +"${prefix}__server" "${prefix}__type" "${prefix}__domain" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ "${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ "${prefix}__volumes" "${prefix}__no_tty" "${prefix}__user" "${prefix}__dev" \ "${prefix}_type_" "${prefix}_app_" "${prefix}_service_" "${prefix}_src_" \ "${prefix}_dst_" "${prefix}_backup_file_" "${prefix}_args_" \ "${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \ -"${prefix}_command_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \ -"${prefix}_provider_" "${prefix}_subcommands_" "${prefix}app" "${prefix}list" \ -"${prefix}ls" "${prefix}new" "${prefix}backup" "${prefix}deploy" \ -"${prefix}check" "${prefix}version" "${prefix}config" "${prefix}cp" \ -"${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ +"${prefix}_command_" "${prefix}_recipe_" "${prefix}_host_" "${prefix}_user_" \ +"${prefix}_port_" "${prefix}_provider_" "${prefix}_subcommands_" \ +"${prefix}app" "${prefix}list" "${prefix}ls" "${prefix}new" "${prefix}backup" \ +"${prefix}deploy" "${prefix}check" "${prefix}version" "${prefix}config" \ +"${prefix}cp" "${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ "${prefix}delete" "${prefix}run" "${prefix}rollback" "${prefix}secret" \ -"${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}server" \ -"${prefix}add" "${prefix}init" "${prefix}apps" "${prefix}upgrade" \ -"${prefix}doctor" "${prefix}help"; done; } +"${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ +"${prefix}versions" "${prefix}server" "${prefix}add" "${prefix}init" \ +"${prefix}apps" "${prefix}upgrade" "${prefix}doctor" "${prefix}help"; done; } # docopt parser above, complete command for generating this parser is `docopt.sh abra` PROGRAM_NAME=$(basename "$0") @@ -435,6 +442,30 @@ require_consent_for_update() { fi } +require_docker_version (){ + get_servers + + MIN_DOCKER_VERSION=19 + + SERVERS+=("default") + + for SERVER in "${SERVERS[@]}"; do + SERVER="${SERVER##*/}" # basename + host=$(docker context inspect "$SERVER" -f "{{.Endpoints.docker.Host}}" 2>/dev/null) + if [[ -n "$host" ]]; then + major_version=$(DOCKER_CONTEXT="$SERVER" docker version --format "{{.Server.Version}}" | cut -d'.' -f1 2>/dev/null) + if [[ "$major_version" -lt "$MIN_DOCKER_VERSION" ]]; then + error "This tool requires Docker v${MIN_DOCKER_VERSION} or greater. Please upgrade your Docker installation on $SERVER" + exit 1 + else + debug "Docker version on $SERVER is sufficient (v${major_version})" + fi + fi + done +} + +###### Download and update data + require_apps_json() { # Ensure we have the latest copy of apps.json apps_url="https://abra-apps.cloud.autonomic.zone" @@ -534,6 +565,10 @@ vendor_binary() { local BINARY="${REPO##*/}" local RELEASE_URL="$REPO/releases/download/${VERSION}/${FILE}" + # Make the path to the binary available as a similarly-named variable, e.g. + # yq -> $YQ + export "${BINARY^^}=$ABRA_VENDOR_DIR/$BINARY" + if [ -f "$ABRA_DIR/vendor/$BINARY" ]; then debug "$BINARY is already vendored" return @@ -542,51 +577,44 @@ vendor_binary() { case $(uname -m) in x86_64) warning "Attempting to download the $BINARY binary from $RELEASE_URL into $ABRA_VENDOR_DIR" - wget -qO "$ABRA_VENDOR_DIR/$BINARY" "$RELEASE_URL" && chmod +x "$ABRA_VENDOR_DIR/$BINARY" - success "$BINARY is now vendored ☮" ;; *) error "Unable to automatically vendor $BINARY, you'll have to manually manage this.\n Please see $REPO and place the $BINARY binary in $ABRA_VENDOR_DIR." ;; esac + + wget -qO "$ABRA_VENDOR_DIR/$BINARY" "$RELEASE_URL" && chmod +x "$ABRA_VENDOR_DIR/$BINARY" + success "$BINARY is now vendored ☮" } require_jq() { - vendor_binary "https://github.com/stedolan/jq/" "jq-1.6" "jq-linux64" + vendor_binary "https://github.com/stedolan/jq" "jq-1.6" "jq-linux64" } require_yq() { vendor_binary "https://github.com/mikefarah/yq" "v4.6.1" "yq_linux_amd64" } -require_docker_version (){ - get_servers - - MIN_DOCKER_VERSION=19 - - SERVERS+=("default") - - for SERVER in "${SERVERS[@]}"; do - SERVER="${SERVER##*/}" # basename - host=$(docker context inspect "$SERVER" -f "{{.Endpoints.docker.Host}}" 2>/dev/null) - if [[ -n "$host" ]]; then - major_version=$(DOCKER_CONTEXT="$SERVER" docker version --format "{{.Server.Version}}" | cut -d'.' -f1 2>/dev/null) - if [[ "$major_version" -lt "$MIN_DOCKER_VERSION" ]]; then - error "This tool requires Docker v${MIN_DOCKER_VERSION} or greater. Please upgrade your Docker installation on $SERVER" - exit 1 - else - debug "Docker version on $SERVER is sufficient (v${major_version})" - fi - fi - done -} - # FIXME 3wc: update or remove if [ -z "$ABRA_ENV" ] && [ -f .env ] && type direnv > /dev/null 2>&1 && ! direnv status | grep -q 'Found RC allowed true'; then error "direnv is blocked, run direnv allow" fi +###### Parse apps.json + +get_recipes() { + require_jq + + mapfile -t RECIPES < <($JQ -r ". | keys | .[]" "$ABRA_APPS_JSON" | sort) +} + +get_recipe_versions() { + require_jq + + mapfile -t RECIPE_VERSIONS < <($JQ -r ".${abra__recipe_}.versions | keys | .[]" "$ABRA_APPS_JSON" | sort) +} + ###### Run-time loading load_abra_sh() { @@ -1196,13 +1224,14 @@ _abra_backup_mysql() { ###### .. app deploy help_app_deploy (){ - echo "abra [options] app deploy [--update] + echo "abra [options] app deploy [--update] [--force] [--skip-version-check] Deploy app to the configured server. OPTIONS - --update Consent to deploying an updated app version - --force Force a deployment regardless of state + --update Consent to deploying an updated app version + --force Force a deployment regardless of state + --skip-version-check Don't try and detect deployed version POWERED BY docker stack deploy -c compose.yml " @@ -1759,6 +1788,58 @@ sub_app_cp() { docker cp ${CP_ARGS} } +####################################### +# abra recipe .. +####################################### + +###### .. recipe ls +help_recipe_ls (){ + help_recipe_list +} + +sub_recipe_ls() { + sub_recipe_list +} + +help_recipe_list() { + echo "abra [options] recipe (list|ls) + +List all available recipes." +} + +sub_recipe_list() { + get_recipes + + printf "%s delicious recipes:\n" "${#RECIPES[@]}" + printf '%s\n' "${RECIPES[@]}" +} + +###### .. recipe versions + +help_recipe_versions() { + echo "abra [options] recipe versions + +Show all available versions of ." +} + +sub_recipe_versions() { + get_recipe_versions "$abra__recipe_" + + printf "%s thrilling versions of $abra__recipe_:\n" "${#RECIPE_VERSIONS[@]}" + + for version in "${RECIPE_VERSIONS[@]}"; do + recipe_version_data=$($JQ -r ".${abra__recipe_}.versions.\"${version}\"" "$ABRA_APPS_JSON") + mapfile -t services < <(echo "$recipe_version_data" | $JQ -r ". | keys | .[]" -) + printf '%s:\n' "$version" + for service in "${services[@]}"; do + image=$(echo "$recipe_version_data" | $JQ -r ".$service.image" -) + tag=$(echo "$recipe_version_data" | $JQ -r ".$service.tag" -) + digest=$(echo "$recipe_version_data" | $JQ -r ".$service.digest" -) + printf ' - %s (%s:%s, %s)\n' "$service" "$image" "$tag" "$digest" + done + done +} + ####################################### # abra server .. ####################################### @@ -2046,7 +2127,7 @@ abra() { abra___skip_check abra__backup_file_ abra___verbose abra___debug \ abra___help abra___branch abra___volumes abra__provider_ abra___type \ abra___dev abra___update abra___no_prompt abra___force \ - abra___skip_version_check + abra___skip_version_check abra__recipe_ if ! type tput > /dev/null 2>&1; then tput() { From 33280f90b33eec49f74c4dc26be6bcfe87426ec7 Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Sun, 4 Apr 2021 20:20:54 +0200 Subject: [PATCH 3/8] Rejig rollback, add to deploy Ref #132 --- abra | 271 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 155 insertions(+), 116 deletions(-) diff --git a/abra b/abra index 6345ae7..f42cd6c 100755 --- a/abra +++ b/abra @@ -18,7 +18,7 @@ Usage: abra [options] app (list|ls) [--status] [--server=] [--type=] abra [options] app new [--server=] [--domain=] [--app-name=] [--pass] [--secrets] abra [options] app backup (|--all) - abra [options] app deploy [--update] [--force] [--skip-version-check] + abra [options] app deploy [--update] [--force] [--skip-version-check] [] abra [options] app check abra [options] app version abra [options] app config @@ -29,7 +29,7 @@ Usage: abra [options] app (rm|delete) [--volumes] [--secrets] abra [options] app restore [] abra [options] app run [--no-tty] [--user=] ... - abra [options] app rollback + abra [options] app rollback [] abra [options] app secret generate ( |--all) [] [--pass] abra [options] app secret insert [--pass] abra [options] app secret (rm|delete) (|--all) [--pass] @@ -162,15 +162,15 @@ eval "var_$1+=($value)"; else eval "var_$1=$value"; fi; return 0; fi; done return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() { printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() { [[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() { -printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2231} -usage=${DOC:40:1622}; digest=22c5b -shorts=(-h -b -n -s -e -d -C -v -U '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') -longs=(--help --branch --no-prompt --stack --env --debug --skip-check --verbose --skip-update --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) -argcounts=(0 1 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ -switch __help 0; }; node_1(){ value __branch 1; }; node_2(){ -switch __no_prompt 2; }; node_3(){ value __stack 3; }; node_4(){ value __env 4 -}; node_5(){ switch __debug 5; }; node_6(){ switch __skip_check 6; }; node_7(){ -switch __verbose 7; }; node_8(){ switch __skip_update 8; }; node_9(){ +printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2245} +usage=${DOC:40:1636}; digest=ba0b1 +shorts=(-C -d -s -h -v -U -e -b -n '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') +longs=(--skip-check --debug --stack --help --verbose --skip-update --env --branch --no-prompt --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) +argcounts=(0 0 1 0 0 0 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ +switch __skip_check 0; }; node_1(){ switch __debug 1; }; node_2(){ +value __stack 2; }; node_3(){ switch __help 3; }; node_4(){ switch __verbose 4 +}; node_5(){ switch __skip_update 5; }; node_6(){ value __env 6; }; node_7(){ +value __branch 7; }; node_8(){ switch __no_prompt 8; }; node_9(){ switch __status 9; }; node_10(){ value __server 10; }; node_11(){ value __type 11; }; node_12(){ value __domain 12; }; node_13(){ value __app_name 13; }; node_14(){ switch __pass 14; }; node_15(){ @@ -179,15 +179,15 @@ switch __update 17; }; node_18(){ switch __force 18; }; node_19(){ switch __skip_version_check 19; }; node_20(){ switch __volumes 20; }; node_21(){ switch __no_tty 21; }; node_22(){ value __user 22; }; node_23(){ switch __dev 23 }; node_24(){ value _type_ a; }; node_25(){ value _app_ a; }; node_26(){ -value _service_ a; }; node_27(){ value _src_ a; }; node_28(){ value _dst_ a; } -node_29(){ value _backup_file_ a; }; node_30(){ value _args_ a true; } -node_31(){ value _secret_ a; }; node_32(){ value _version_ a; }; node_33(){ -value _cmd_ a; }; node_34(){ value _data_ a; }; node_35(){ value _command_ a; } -node_36(){ value _recipe_ a; }; node_37(){ value _host_ a; }; node_38(){ -value _user_ a; }; node_39(){ value _port_ a; }; node_40(){ value _provider_ a -}; node_41(){ value _subcommands_ a true; }; node_42(){ _command app; } -node_43(){ _command list; }; node_44(){ _command ls; }; node_45(){ _command new -}; node_46(){ _command backup; }; node_47(){ _command deploy; }; node_48(){ +value _service_ a; }; node_27(){ value _version_ a; }; node_28(){ value _src_ a +}; node_29(){ value _dst_ a; }; node_30(){ value _backup_file_ a; }; node_31(){ +value _args_ a true; }; node_32(){ value _secret_ a; }; node_33(){ value _cmd_ a +}; node_34(){ value _data_ a; }; node_35(){ value _command_ a; }; node_36(){ +value _recipe_ a; }; node_37(){ value _host_ a; }; node_38(){ value _user_ a; } +node_39(){ value _port_ a; }; node_40(){ value _provider_ a; }; node_41(){ +value _subcommands_ a true; }; node_42(){ _command app; }; node_43(){ +_command list; }; node_44(){ _command ls; }; node_45(){ _command new; } +node_46(){ _command backup; }; node_47(){ _command deploy; }; node_48(){ _command check; }; node_49(){ _command version; }; node_50(){ _command config; } node_51(){ _command cp; }; node_52(){ _command logs; }; node_53(){ _command ps }; node_54(){ _command restore; }; node_55(){ _command rm; }; node_56(){ @@ -204,75 +204,76 @@ node_79(){ required 73 42 75 76 77 78; }; node_80(){ optional 12; }; node_81(){ optional 13; }; node_82(){ optional 14; }; node_83(){ optional 15; }; node_84(){ required 73 42 45 77 80 81 82 83 24; }; node_85(){ either 26 16; }; node_86(){ required 85; }; node_87(){ required 73 42 25 46 86; }; node_88(){ optional 17; } -node_89(){ optional 18; }; node_90(){ optional 19; }; node_91(){ -required 73 42 25 47 88 89 90; }; node_92(){ required 73 42 25 48; }; node_93(){ -required 73 42 25 49; }; node_94(){ required 73 42 25 50; }; node_95(){ -required 73 42 25 51 27 28; }; node_96(){ optional 26; }; node_97(){ -required 73 42 25 52 96; }; node_98(){ required 73 42 25 53; }; node_99(){ -required 73 42 25 54 86; }; node_100(){ either 55 56; }; node_101(){ -required 100; }; node_102(){ optional 20; }; node_103(){ -required 73 42 25 101 102 83; }; node_104(){ optional 29; }; node_105(){ -required 73 42 25 54 26 104; }; node_106(){ optional 21; }; node_107(){ -optional 22; }; node_108(){ oneormore 30; }; node_109(){ -required 73 42 25 57 106 107 26 108; }; node_110(){ required 73 42 25 58 26; } -node_111(){ required 31 32; }; node_112(){ either 111 16; }; node_113(){ -required 112; }; node_114(){ optional 33; }; node_115(){ -required 73 42 25 59 60 113 114 82; }; node_116(){ -required 73 42 25 59 61 31 32 34 82; }; node_117(){ either 31 16; }; node_118(){ -required 117; }; node_119(){ required 73 42 25 59 101 118 82; }; node_120(){ -required 73 42 25 62; }; node_121(){ optional 108; }; node_122(){ -required 73 42 25 35 121; }; node_123(){ required 73 63 44; }; node_124(){ -required 73 63 36 64; }; node_125(){ optional 38; }; node_126(){ optional 39; } -node_127(){ required 73 65 66 37 125 126; }; node_128(){ required 73 65 45 40; } -node_129(){ required 73 65 75; }; node_130(){ required 73 65 37 55; } -node_131(){ required 73 65 37 67; }; node_132(){ required 73 65 37 68 76; } -node_133(){ optional 23; }; node_134(){ required 73 69 133; }; node_135(){ -required 73 49; }; node_136(){ required 73 70; }; node_137(){ oneormore 41; } -node_138(){ optional 137; }; node_139(){ required 73 71 138; }; node_140(){ -required 73; }; node_141(){ -either 79 84 87 91 92 93 94 95 97 98 99 103 105 109 110 115 116 119 120 122 123 124 127 128 129 130 131 132 134 135 136 139 140 -}; node_142(){ required 141; }; cat <<<' docopt_exit() { -[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1622}" >&2 -exit 1; }'; unset var___help var___branch var___no_prompt var___stack \ -var___env var___debug var___skip_check var___verbose var___skip_update \ +node_89(){ optional 18; }; node_90(){ optional 19; }; node_91(){ optional 27; } +node_92(){ required 73 42 25 47 88 89 90 91; }; node_93(){ required 73 42 25 48 +}; node_94(){ required 73 42 25 49; }; node_95(){ required 73 42 25 50; } +node_96(){ required 73 42 25 51 28 29; }; node_97(){ optional 26; }; node_98(){ +required 73 42 25 52 97; }; node_99(){ required 73 42 25 53; }; node_100(){ +required 73 42 25 54 86; }; node_101(){ either 55 56; }; node_102(){ +required 101; }; node_103(){ optional 20; }; node_104(){ +required 73 42 25 102 103 83; }; node_105(){ optional 30; }; node_106(){ +required 73 42 25 54 26 105; }; node_107(){ optional 21; }; node_108(){ +optional 22; }; node_109(){ oneormore 31; }; node_110(){ +required 73 42 25 57 107 108 26 109; }; node_111(){ required 73 42 25 58 91; } +node_112(){ required 32 27; }; node_113(){ either 112 16; }; node_114(){ +required 113; }; node_115(){ optional 33; }; node_116(){ +required 73 42 25 59 60 114 115 82; }; node_117(){ +required 73 42 25 59 61 32 27 34 82; }; node_118(){ either 32 16; }; node_119(){ +required 118; }; node_120(){ required 73 42 25 59 102 119 82; }; node_121(){ +required 73 42 25 62; }; node_122(){ optional 109; }; node_123(){ +required 73 42 25 35 122; }; node_124(){ required 73 63 44; }; node_125(){ +required 73 63 36 64; }; node_126(){ optional 38; }; node_127(){ optional 39; } +node_128(){ required 73 65 66 37 126 127; }; node_129(){ required 73 65 45 40; } +node_130(){ required 73 65 75; }; node_131(){ required 73 65 37 55; } +node_132(){ required 73 65 37 67; }; node_133(){ required 73 65 37 68 76; } +node_134(){ optional 23; }; node_135(){ required 73 69 134; }; node_136(){ +required 73 49; }; node_137(){ required 73 70; }; node_138(){ oneormore 41; } +node_139(){ optional 138; }; node_140(){ required 73 71 139; }; node_141(){ +required 73; }; node_142(){ +either 79 84 87 92 93 94 95 96 98 99 100 104 106 110 111 116 117 120 121 123 124 125 128 129 130 131 132 133 135 136 137 140 141 +}; node_143(){ required 142; }; cat <<<' docopt_exit() { +[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1636}" >&2 +exit 1; }'; unset var___skip_check var___debug var___stack var___help \ +var___verbose var___skip_update var___env var___branch var___no_prompt \ var___status var___server var___type var___domain var___app_name var___pass \ var___secrets var___all var___update var___force var___skip_version_check \ var___volumes var___no_tty var___user var___dev var__type_ var__app_ \ -var__service_ var__src_ var__dst_ var__backup_file_ var__args_ var__secret_ \ -var__version_ var__cmd_ var__data_ var__command_ var__recipe_ var__host_ \ +var__service_ var__version_ var__src_ var__dst_ var__backup_file_ var__args_ \ +var__secret_ var__cmd_ var__data_ var__command_ var__recipe_ var__host_ \ var__user_ var__port_ var__provider_ var__subcommands_ var_app var_list var_ls \ var_new var_backup var_deploy var_check var_version var_config var_cp var_logs \ var_ps var_restore var_rm var_delete var_run var_rollback var_secret \ var_generate var_insert var_undeploy var_recipe var_versions var_server \ -var_add var_init var_apps var_upgrade var_doctor var_help; parse 142 "$@" -local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" "${prefix}__branch" \ -"${prefix}__no_prompt" "${prefix}__stack" "${prefix}__env" "${prefix}__debug" \ -"${prefix}__skip_check" "${prefix}__verbose" "${prefix}__skip_update" \ -"${prefix}__status" "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ -"${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ -"${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ -"${prefix}__volumes" "${prefix}__no_tty" "${prefix}__user" "${prefix}__dev" \ -"${prefix}_type_" "${prefix}_app_" "${prefix}_service_" "${prefix}_src_" \ -"${prefix}_dst_" "${prefix}_backup_file_" "${prefix}_args_" \ -"${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \ -"${prefix}_command_" "${prefix}_recipe_" "${prefix}_host_" "${prefix}_user_" \ -"${prefix}_port_" "${prefix}_provider_" "${prefix}_subcommands_" \ -"${prefix}app" "${prefix}list" "${prefix}ls" "${prefix}new" "${prefix}backup" \ -"${prefix}deploy" "${prefix}check" "${prefix}version" "${prefix}config" \ -"${prefix}cp" "${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ -"${prefix}delete" "${prefix}run" "${prefix}rollback" "${prefix}secret" \ -"${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ -"${prefix}versions" "${prefix}server" "${prefix}add" "${prefix}init" \ -"${prefix}apps" "${prefix}upgrade" "${prefix}doctor" "${prefix}help" -eval "${prefix}"'__help=${var___help:-false}' -eval "${prefix}"'__branch=${var___branch:-}' -eval "${prefix}"'__no_prompt=${var___no_prompt:-false}' -eval "${prefix}"'__stack=${var___stack:-}' -eval "${prefix}"'__env=${var___env:-}' -eval "${prefix}"'__debug=${var___debug:-false}' +var_add var_init var_apps var_upgrade var_doctor var_help; parse 143 "$@" +local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__skip_check" \ +"${prefix}__debug" "${prefix}__stack" "${prefix}__help" "${prefix}__verbose" \ +"${prefix}__skip_update" "${prefix}__env" "${prefix}__branch" \ +"${prefix}__no_prompt" "${prefix}__status" "${prefix}__server" \ +"${prefix}__type" "${prefix}__domain" "${prefix}__app_name" "${prefix}__pass" \ +"${prefix}__secrets" "${prefix}__all" "${prefix}__update" "${prefix}__force" \ +"${prefix}__skip_version_check" "${prefix}__volumes" "${prefix}__no_tty" \ +"${prefix}__user" "${prefix}__dev" "${prefix}_type_" "${prefix}_app_" \ +"${prefix}_service_" "${prefix}_version_" "${prefix}_src_" "${prefix}_dst_" \ +"${prefix}_backup_file_" "${prefix}_args_" "${prefix}_secret_" \ +"${prefix}_cmd_" "${prefix}_data_" "${prefix}_command_" "${prefix}_recipe_" \ +"${prefix}_host_" "${prefix}_user_" "${prefix}_port_" "${prefix}_provider_" \ +"${prefix}_subcommands_" "${prefix}app" "${prefix}list" "${prefix}ls" \ +"${prefix}new" "${prefix}backup" "${prefix}deploy" "${prefix}check" \ +"${prefix}version" "${prefix}config" "${prefix}cp" "${prefix}logs" \ +"${prefix}ps" "${prefix}restore" "${prefix}rm" "${prefix}delete" \ +"${prefix}run" "${prefix}rollback" "${prefix}secret" "${prefix}generate" \ +"${prefix}insert" "${prefix}undeploy" "${prefix}recipe" "${prefix}versions" \ +"${prefix}server" "${prefix}add" "${prefix}init" "${prefix}apps" \ +"${prefix}upgrade" "${prefix}doctor" "${prefix}help" eval "${prefix}"'__skip_check=${var___skip_check:-false}' +eval "${prefix}"'__debug=${var___debug:-false}' +eval "${prefix}"'__stack=${var___stack:-}' +eval "${prefix}"'__help=${var___help:-false}' eval "${prefix}"'__verbose=${var___verbose:-false}' eval "${prefix}"'__skip_update=${var___skip_update:-false}' +eval "${prefix}"'__env=${var___env:-}' +eval "${prefix}"'__branch=${var___branch:-}' +eval "${prefix}"'__no_prompt=${var___no_prompt:-false}' eval "${prefix}"'__status=${var___status:-false}' eval "${prefix}"'__server=${var___server:-}' eval "${prefix}"'__type=${var___type:-}' @@ -290,12 +291,12 @@ eval "${prefix}"'__user=${var___user:-}' eval "${prefix}"'__dev=${var___dev:-false}' eval "${prefix}"'_type_=${var__type_:-}'; eval "${prefix}"'_app_=${var__app_:-}' eval "${prefix}"'_service_=${var__service_:-}' +eval "${prefix}"'_version_=${var__version_:-}' eval "${prefix}"'_src_=${var__src_:-}'; eval "${prefix}"'_dst_=${var__dst_:-}' eval "${prefix}"'_backup_file_=${var__backup_file_:-}' if declare -p var__args_ >/dev/null 2>&1; then eval "${prefix}"'_args_=("${var__args_[@]}")'; else eval "${prefix}"'_args_=()' fi; eval "${prefix}"'_secret_=${var__secret_:-}' -eval "${prefix}"'_version_=${var__version_:-}' eval "${prefix}"'_cmd_=${var__cmd_:-}'; eval "${prefix}"'_data_=${var__data_:-}' eval "${prefix}"'_command_=${var__command_:-}' eval "${prefix}"'_recipe_=${var__recipe_:-}' @@ -334,21 +335,21 @@ eval "${prefix}"'upgrade=${var_upgrade:-false}' eval "${prefix}"'doctor=${var_doctor:-false}' eval "${prefix}"'help=${var_help:-false}'; local docopt_i=1 [[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do -declare -p "${prefix}__help" "${prefix}__branch" "${prefix}__no_prompt" \ -"${prefix}__stack" "${prefix}__env" "${prefix}__debug" "${prefix}__skip_check" \ -"${prefix}__verbose" "${prefix}__skip_update" "${prefix}__status" \ -"${prefix}__server" "${prefix}__type" "${prefix}__domain" \ +declare -p "${prefix}__skip_check" "${prefix}__debug" "${prefix}__stack" \ +"${prefix}__help" "${prefix}__verbose" "${prefix}__skip_update" \ +"${prefix}__env" "${prefix}__branch" "${prefix}__no_prompt" \ +"${prefix}__status" "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ "${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ "${prefix}__volumes" "${prefix}__no_tty" "${prefix}__user" "${prefix}__dev" \ -"${prefix}_type_" "${prefix}_app_" "${prefix}_service_" "${prefix}_src_" \ -"${prefix}_dst_" "${prefix}_backup_file_" "${prefix}_args_" \ -"${prefix}_secret_" "${prefix}_version_" "${prefix}_cmd_" "${prefix}_data_" \ -"${prefix}_command_" "${prefix}_recipe_" "${prefix}_host_" "${prefix}_user_" \ -"${prefix}_port_" "${prefix}_provider_" "${prefix}_subcommands_" \ -"${prefix}app" "${prefix}list" "${prefix}ls" "${prefix}new" "${prefix}backup" \ -"${prefix}deploy" "${prefix}check" "${prefix}version" "${prefix}config" \ -"${prefix}cp" "${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ +"${prefix}_type_" "${prefix}_app_" "${prefix}_service_" "${prefix}_version_" \ +"${prefix}_src_" "${prefix}_dst_" "${prefix}_backup_file_" "${prefix}_args_" \ +"${prefix}_secret_" "${prefix}_cmd_" "${prefix}_data_" "${prefix}_command_" \ +"${prefix}_recipe_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \ +"${prefix}_provider_" "${prefix}_subcommands_" "${prefix}app" "${prefix}list" \ +"${prefix}ls" "${prefix}new" "${prefix}backup" "${prefix}deploy" \ +"${prefix}check" "${prefix}version" "${prefix}config" "${prefix}cp" \ +"${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ "${prefix}delete" "${prefix}run" "${prefix}rollback" "${prefix}secret" \ "${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ "${prefix}versions" "${prefix}server" "${prefix}add" "${prefix}init" \ @@ -468,7 +469,13 @@ require_docker_version (){ require_apps_json() { # Ensure we have the latest copy of apps.json + + if [ "$abra___skip_update" = "true" ]; then + return + fi + apps_url="https://abra-apps.cloud.autonomic.zone" + if [ -f "$ABRA_APPS_JSON" ]; then modified=$(curl --silent --head $apps_url | \ awk '/^Last-Modified/{print $0}' | \ @@ -537,8 +544,9 @@ require_app (){ success "Fetched app configuration via Git" } -require_app_latest() { +require_app_version() { APP="$1" + VERSION="$2" APP_DIR="$ABRA_DIR/apps/$APP" debug "Checking for type '$APP'" @@ -548,11 +556,13 @@ require_app_latest() { fi debug "Using $APP_DIR" + cd "$APP_DIR" || error "Can't find app dir '$APP_DIR'" - if [ "$abra___skip_update" = "false" ]; then - debug "Pulling latest '$APP' definition via git" - (cd "$APP_DIR" && git pull > /dev/null 2>&1) + if ! git tag -l | grep -q "$VERSION"; then + git fetch -q --all fi + + git checkout -q "$VERSION" || error "Can't find version $VERSION" } vendor_binary() { @@ -584,7 +594,7 @@ vendor_binary() { ;; esac - wget -qO "$ABRA_VENDOR_DIR/$BINARY" "$RELEASE_URL" && chmod +x "$ABRA_VENDOR_DIR/$BINARY" + wget -qO "$ABRA_VENDOR_DIR/$BINARY" "$RELEASE_URL" && chmod +x "$ABRA_VENDOR_DIR/$BINARY" success "$BINARY is now vendored ☮" } @@ -612,7 +622,9 @@ get_recipes() { get_recipe_versions() { require_jq - mapfile -t RECIPE_VERSIONS < <($JQ -r ".${abra__recipe_}.versions | keys | .[]" "$ABRA_APPS_JSON" | sort) + recipe="${1?Recipe not set}" + + mapfile -t RECIPE_VERSIONS < <($JQ -r ".${recipe}.versions | keys | .[]" "$ABRA_APPS_JSON" | sort) } ###### Run-time loading @@ -624,10 +636,6 @@ load_abra_sh() { info "Loading abra.sh" fi - if [ -n "$abra__app_" ]; then - require_app_latest "$TYPE" - fi - if [ -f "$APP_DIR/abra.sh" ]; then info "Loading $APP_DIR/abra.sh" # shellcheck disable=SC1090,SC1091 @@ -683,7 +691,7 @@ output_version_summary() { if [ "$live_version" != "$service_tag" ] || [ "$live_digest" != "$service_digest" ]; then IS_AN_UPDATE="true" fi - echo " to be deployed: $(tput setaf 1)$service_version ($service_digest)$(tput sgr0)" + echo " to be deployed: $(tput setaf 1)$service_tag ($service_digest)$(tput sgr0)" fi else if [[ $UNDEPLOYED_STATE == "true" ]]; then @@ -1050,7 +1058,10 @@ sub_app_new (){ DOMAIN=$abra___domain APP_NAME=$abra___app_name - require_app_latest "$TYPE" + get_recipe_versions "$TYPE" + VERSION="${RECIPE_VERSIONS[-1]}" + + require_app_version "$TYPE" "$VERSION" if [ -z "$SERVER" ]; then echo "Where would you like to put $TYPE?" @@ -1126,8 +1137,6 @@ sub_app_new (){ ###### .. app backup sub_app_backup (){ - require_app_latest "$TYPE" - # Add _ if it's defined FUNCTION="abra_backup${abra__service_:+_}$abra__service_" @@ -1143,8 +1152,6 @@ sub_app_backup (){ ###### .. app restore sub_app_restore (){ - require_app_latest "$TYPE" - FUNCTION="abra_restore_$abra__service_" if ! type "$FUNCTION" > /dev/null 2>&1; then @@ -1239,11 +1246,24 @@ POWERED BY sub_app_deploy (){ require_yq - require_app_latest "$TYPE" NON_INTERACTIVE=$abra___no_prompt SKIP_VERSION_CHECK=$abra___skip_version_check + if [ -n "$abra__version_" ]; then + VERSION="$abra__version_" + if ! printf '%s\0' "${RECIPE_VERSIONS[@]}" | grep -Fqxz -- "$VERSION"; then + error "'$version' doesn't appear to be a valid version of $TYPE" + fi + else + get_recipe_versions "$TYPE" + VERSION="${RECIPE_VERSIONS[-1]}" + fi + + info "Chose version $VERSION" + + require_app_version "$TYPE" "$VERSION" + echo "Deployment overview:" echo " Server: $(tput setaf 4)${SERVER}$(tput sgr0)" @@ -1671,22 +1691,41 @@ sub_app_run(){ ###### .. app rollback help_app_rollback (){ - echo "abra [options] app rollback + echo "abra [options] app rollback [] -Rollback a deployed app service to a previous version. +Roll back a deployed app to a previous version. + +You can specify a particular ; see \`abra recipe version\` for +the list of options. + +Otherwise, we'll roll back to the second-most-recent available version. EXAMPLES - abra app wordpress rollback app + abra app wordpress rollback POWERED BY - CONTAINER_ID=\$(docker container ls -f ...) - docker service rollback \$CONTAINER_ID ..." + abra app deploy --update" } sub_app_rollback(){ - SERVICE="${abra__service_}" + version="${abra__version_}" - docker service rollback "${STACK_NAME}_${SERVICE}" + get_recipe_versions "$TYPE" + if [ "${#RECIPE_VERSIONS[@]}" -lt 2 ]; then + error "Can't roll back; need 2 versions, ${#RECIPE_VERSIONS[@]} available" + fi + + if [ -z "$version" ]; then + version="${RECIPE_VERSIONS[-2]}" + info "Guessed version $version" + fi + + # FIXME 3wc: check if $version is actually older than what's deployed + + abra__version_="$version" + abra___update="true" + + sub_app_deploy } ###### .. app logs From e2640fac08a1029f39e2b782d90733f4d9d70972 Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Sun, 4 Apr 2021 22:00:42 +0200 Subject: [PATCH 4/8] Add `.. recipe .. release` subcommand Closes #134 --- abra | 316 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 218 insertions(+), 98 deletions(-) diff --git a/abra b/abra index f42cd6c..0ce6491 100755 --- a/abra +++ b/abra @@ -36,6 +36,7 @@ Usage: abra [options] app undeploy abra [options] app [...] abra [options] recipe ls + abra [options] recipe release abra [options] recipe versions abra [options] server add [] [] abra [options] server new @@ -162,29 +163,30 @@ eval "var_$1+=($value)"; else eval "var_$1=$value"; fi; return 0; fi; done return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() { printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() { [[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() { -printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2245} -usage=${DOC:40:1636}; digest=ba0b1 -shorts=(-C -d -s -h -v -U -e -b -n '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') -longs=(--skip-check --debug --stack --help --verbose --skip-update --env --branch --no-prompt --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) -argcounts=(0 0 1 0 0 0 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ -switch __skip_check 0; }; node_1(){ switch __debug 1; }; node_2(){ -value __stack 2; }; node_3(){ switch __help 3; }; node_4(){ switch __verbose 4 -}; node_5(){ switch __skip_update 5; }; node_6(){ value __env 6; }; node_7(){ -value __branch 7; }; node_8(){ switch __no_prompt 8; }; node_9(){ -switch __status 9; }; node_10(){ value __server 10; }; node_11(){ -value __type 11; }; node_12(){ value __domain 12; }; node_13(){ -value __app_name 13; }; node_14(){ switch __pass 14; }; node_15(){ -switch __secrets 15; }; node_16(){ switch __all 16; }; node_17(){ -switch __update 17; }; node_18(){ switch __force 18; }; node_19(){ -switch __skip_version_check 19; }; node_20(){ switch __volumes 20; }; node_21(){ -switch __no_tty 21; }; node_22(){ value __user 22; }; node_23(){ switch __dev 23 -}; node_24(){ value _type_ a; }; node_25(){ value _app_ a; }; node_26(){ -value _service_ a; }; node_27(){ value _version_ a; }; node_28(){ value _src_ a -}; node_29(){ value _dst_ a; }; node_30(){ value _backup_file_ a; }; node_31(){ -value _args_ a true; }; node_32(){ value _secret_ a; }; node_33(){ value _cmd_ a -}; node_34(){ value _data_ a; }; node_35(){ value _command_ a; }; node_36(){ -value _recipe_ a; }; node_37(){ value _host_ a; }; node_38(){ value _user_ a; } -node_39(){ value _port_ a; }; node_40(){ value _provider_ a; }; node_41(){ +printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2286} +usage=${DOC:40:1677}; digest=07a57 +shorts=(-h -s -U -e -b -v -C -d -n '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') +longs=(--help --stack --skip-update --env --branch --verbose --skip-check --debug --no-prompt --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) +argcounts=(0 1 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ +switch __help 0; }; node_1(){ value __stack 1; }; node_2(){ +switch __skip_update 2; }; node_3(){ value __env 3; }; node_4(){ +value __branch 4; }; node_5(){ switch __verbose 5; }; node_6(){ +switch __skip_check 6; }; node_7(){ switch __debug 7; }; node_8(){ +switch __no_prompt 8; }; node_9(){ switch __status 9; }; node_10(){ +value __server 10; }; node_11(){ value __type 11; }; node_12(){ +value __domain 12; }; node_13(){ value __app_name 13; }; node_14(){ +switch __pass 14; }; node_15(){ switch __secrets 15; }; node_16(){ +switch __all 16; }; node_17(){ switch __update 17; }; node_18(){ +switch __force 18; }; node_19(){ switch __skip_version_check 19; }; node_20(){ +switch __volumes 20; }; node_21(){ switch __no_tty 21; }; node_22(){ +value __user 22; }; node_23(){ switch __dev 23; }; node_24(){ value _type_ a; } +node_25(){ value _app_ a; }; node_26(){ value _service_ a; }; node_27(){ +value _version_ a; }; node_28(){ value _src_ a; }; node_29(){ value _dst_ a; } +node_30(){ value _backup_file_ a; }; node_31(){ value _args_ a true; } +node_32(){ value _secret_ a; }; node_33(){ value _cmd_ a; }; node_34(){ +value _data_ a; }; node_35(){ value _command_ a; }; node_36(){ value _recipe_ a +}; node_37(){ value _host_ a; }; node_38(){ value _user_ a; }; node_39(){ +value _port_ a; }; node_40(){ value _provider_ a; }; node_41(){ value _subcommands_ a true; }; node_42(){ _command app; }; node_43(){ _command list; }; node_44(){ _command ls; }; node_45(){ _command new; } node_46(){ _command backup; }; node_47(){ _command deploy; }; node_48(){ @@ -194,47 +196,49 @@ node_51(){ _command cp; }; node_52(){ _command logs; }; node_53(){ _command ps _command delete; }; node_57(){ _command run; }; node_58(){ _command rollback; } node_59(){ _command secret; }; node_60(){ _command generate; }; node_61(){ _command insert; }; node_62(){ _command undeploy; }; node_63(){ _command recipe -}; node_64(){ _command versions; }; node_65(){ _command server; }; node_66(){ -_command add; }; node_67(){ _command init; }; node_68(){ _command apps; } -node_69(){ _command upgrade; }; node_70(){ _command doctor; }; node_71(){ -_command help; }; node_72(){ optional 0 1 2 3 4 5 6 7 8; }; node_73(){ -optional 72; }; node_74(){ either 43 44; }; node_75(){ required 74; } -node_76(){ optional 9; }; node_77(){ optional 10; }; node_78(){ optional 11; } -node_79(){ required 73 42 75 76 77 78; }; node_80(){ optional 12; }; node_81(){ -optional 13; }; node_82(){ optional 14; }; node_83(){ optional 15; }; node_84(){ -required 73 42 45 77 80 81 82 83 24; }; node_85(){ either 26 16; }; node_86(){ -required 85; }; node_87(){ required 73 42 25 46 86; }; node_88(){ optional 17; } -node_89(){ optional 18; }; node_90(){ optional 19; }; node_91(){ optional 27; } -node_92(){ required 73 42 25 47 88 89 90 91; }; node_93(){ required 73 42 25 48 -}; node_94(){ required 73 42 25 49; }; node_95(){ required 73 42 25 50; } -node_96(){ required 73 42 25 51 28 29; }; node_97(){ optional 26; }; node_98(){ -required 73 42 25 52 97; }; node_99(){ required 73 42 25 53; }; node_100(){ -required 73 42 25 54 86; }; node_101(){ either 55 56; }; node_102(){ -required 101; }; node_103(){ optional 20; }; node_104(){ -required 73 42 25 102 103 83; }; node_105(){ optional 30; }; node_106(){ -required 73 42 25 54 26 105; }; node_107(){ optional 21; }; node_108(){ -optional 22; }; node_109(){ oneormore 31; }; node_110(){ -required 73 42 25 57 107 108 26 109; }; node_111(){ required 73 42 25 58 91; } -node_112(){ required 32 27; }; node_113(){ either 112 16; }; node_114(){ -required 113; }; node_115(){ optional 33; }; node_116(){ -required 73 42 25 59 60 114 115 82; }; node_117(){ -required 73 42 25 59 61 32 27 34 82; }; node_118(){ either 32 16; }; node_119(){ -required 118; }; node_120(){ required 73 42 25 59 102 119 82; }; node_121(){ -required 73 42 25 62; }; node_122(){ optional 109; }; node_123(){ -required 73 42 25 35 122; }; node_124(){ required 73 63 44; }; node_125(){ -required 73 63 36 64; }; node_126(){ optional 38; }; node_127(){ optional 39; } -node_128(){ required 73 65 66 37 126 127; }; node_129(){ required 73 65 45 40; } -node_130(){ required 73 65 75; }; node_131(){ required 73 65 37 55; } -node_132(){ required 73 65 37 67; }; node_133(){ required 73 65 37 68 76; } -node_134(){ optional 23; }; node_135(){ required 73 69 134; }; node_136(){ -required 73 49; }; node_137(){ required 73 70; }; node_138(){ oneormore 41; } -node_139(){ optional 138; }; node_140(){ required 73 71 139; }; node_141(){ -required 73; }; node_142(){ -either 79 84 87 92 93 94 95 96 98 99 100 104 106 110 111 116 117 120 121 123 124 125 128 129 130 131 132 133 135 136 137 140 141 -}; node_143(){ required 142; }; cat <<<' docopt_exit() { -[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1636}" >&2 -exit 1; }'; unset var___skip_check var___debug var___stack var___help \ -var___verbose var___skip_update var___env var___branch var___no_prompt \ +}; node_64(){ _command release; }; node_65(){ _command versions; }; node_66(){ +_command server; }; node_67(){ _command add; }; node_68(){ _command init; } +node_69(){ _command apps; }; node_70(){ _command upgrade; }; node_71(){ +_command doctor; }; node_72(){ _command help; }; node_73(){ +optional 0 1 2 3 4 5 6 7 8; }; node_74(){ optional 73; }; node_75(){ +either 43 44; }; node_76(){ required 75; }; node_77(){ optional 9; }; node_78(){ +optional 10; }; node_79(){ optional 11; }; node_80(){ required 74 42 76 77 78 79 +}; node_81(){ optional 12; }; node_82(){ optional 13; }; node_83(){ optional 14 +}; node_84(){ optional 15; }; node_85(){ required 74 42 45 78 81 82 83 84 24; } +node_86(){ either 26 16; }; node_87(){ required 86; }; node_88(){ +required 74 42 25 46 87; }; node_89(){ optional 17; }; node_90(){ optional 18; } +node_91(){ optional 19; }; node_92(){ optional 27; }; node_93(){ +required 74 42 25 47 89 90 91 92; }; node_94(){ required 74 42 25 48; } +node_95(){ required 74 42 25 49; }; node_96(){ required 74 42 25 50; } +node_97(){ required 74 42 25 51 28 29; }; node_98(){ optional 26; }; node_99(){ +required 74 42 25 52 98; }; node_100(){ required 74 42 25 53; }; node_101(){ +required 74 42 25 54 87; }; node_102(){ either 55 56; }; node_103(){ +required 102; }; node_104(){ optional 20; }; node_105(){ +required 74 42 25 103 104 84; }; node_106(){ optional 30; }; node_107(){ +required 74 42 25 54 26 106; }; node_108(){ optional 21; }; node_109(){ +optional 22; }; node_110(){ oneormore 31; }; node_111(){ +required 74 42 25 57 108 109 26 110; }; node_112(){ required 74 42 25 58 92; } +node_113(){ required 32 27; }; node_114(){ either 113 16; }; node_115(){ +required 114; }; node_116(){ optional 33; }; node_117(){ +required 74 42 25 59 60 115 116 83; }; node_118(){ +required 74 42 25 59 61 32 27 34 83; }; node_119(){ either 32 16; }; node_120(){ +required 119; }; node_121(){ required 74 42 25 59 103 120 83; }; node_122(){ +required 74 42 25 62; }; node_123(){ optional 110; }; node_124(){ +required 74 42 25 35 123; }; node_125(){ required 74 63 44; }; node_126(){ +required 74 63 36 64; }; node_127(){ required 74 63 36 65; }; node_128(){ +optional 38; }; node_129(){ optional 39; }; node_130(){ +required 74 66 67 37 128 129; }; node_131(){ required 74 66 45 40; } +node_132(){ required 74 66 76; }; node_133(){ required 74 66 37 55; } +node_134(){ required 74 66 37 68; }; node_135(){ required 74 66 37 69 77; } +node_136(){ optional 23; }; node_137(){ required 74 70 136; }; node_138(){ +required 74 49; }; node_139(){ required 74 71; }; node_140(){ oneormore 41; } +node_141(){ optional 140; }; node_142(){ required 74 72 141; }; node_143(){ +required 74; }; node_144(){ +either 80 85 88 93 94 95 96 97 99 100 101 105 107 111 112 117 118 121 122 124 125 126 127 130 131 132 133 134 135 137 138 139 142 143 +}; node_145(){ required 144; }; cat <<<' docopt_exit() { +[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1677}" >&2 +exit 1; }'; unset var___help var___stack var___skip_update var___env \ +var___branch var___verbose var___skip_check var___debug var___no_prompt \ var___status var___server var___type var___domain var___app_name var___pass \ var___secrets var___all var___update var___force var___skip_version_check \ var___volumes var___no_tty var___user var___dev var__type_ var__app_ \ @@ -243,36 +247,36 @@ var__secret_ var__cmd_ var__data_ var__command_ var__recipe_ var__host_ \ var__user_ var__port_ var__provider_ var__subcommands_ var_app var_list var_ls \ var_new var_backup var_deploy var_check var_version var_config var_cp var_logs \ var_ps var_restore var_rm var_delete var_run var_rollback var_secret \ -var_generate var_insert var_undeploy var_recipe var_versions var_server \ -var_add var_init var_apps var_upgrade var_doctor var_help; parse 143 "$@" -local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__skip_check" \ -"${prefix}__debug" "${prefix}__stack" "${prefix}__help" "${prefix}__verbose" \ -"${prefix}__skip_update" "${prefix}__env" "${prefix}__branch" \ -"${prefix}__no_prompt" "${prefix}__status" "${prefix}__server" \ -"${prefix}__type" "${prefix}__domain" "${prefix}__app_name" "${prefix}__pass" \ -"${prefix}__secrets" "${prefix}__all" "${prefix}__update" "${prefix}__force" \ -"${prefix}__skip_version_check" "${prefix}__volumes" "${prefix}__no_tty" \ -"${prefix}__user" "${prefix}__dev" "${prefix}_type_" "${prefix}_app_" \ -"${prefix}_service_" "${prefix}_version_" "${prefix}_src_" "${prefix}_dst_" \ -"${prefix}_backup_file_" "${prefix}_args_" "${prefix}_secret_" \ -"${prefix}_cmd_" "${prefix}_data_" "${prefix}_command_" "${prefix}_recipe_" \ -"${prefix}_host_" "${prefix}_user_" "${prefix}_port_" "${prefix}_provider_" \ -"${prefix}_subcommands_" "${prefix}app" "${prefix}list" "${prefix}ls" \ -"${prefix}new" "${prefix}backup" "${prefix}deploy" "${prefix}check" \ -"${prefix}version" "${prefix}config" "${prefix}cp" "${prefix}logs" \ -"${prefix}ps" "${prefix}restore" "${prefix}rm" "${prefix}delete" \ -"${prefix}run" "${prefix}rollback" "${prefix}secret" "${prefix}generate" \ -"${prefix}insert" "${prefix}undeploy" "${prefix}recipe" "${prefix}versions" \ -"${prefix}server" "${prefix}add" "${prefix}init" "${prefix}apps" \ -"${prefix}upgrade" "${prefix}doctor" "${prefix}help" -eval "${prefix}"'__skip_check=${var___skip_check:-false}' -eval "${prefix}"'__debug=${var___debug:-false}' +var_generate var_insert var_undeploy var_recipe var_release var_versions \ +var_server var_add var_init var_apps var_upgrade var_doctor var_help +parse 145 "$@"; local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" \ +"${prefix}__stack" "${prefix}__skip_update" "${prefix}__env" \ +"${prefix}__branch" "${prefix}__verbose" "${prefix}__skip_check" \ +"${prefix}__debug" "${prefix}__no_prompt" "${prefix}__status" \ +"${prefix}__server" "${prefix}__type" "${prefix}__domain" \ +"${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ +"${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ +"${prefix}__volumes" "${prefix}__no_tty" "${prefix}__user" "${prefix}__dev" \ +"${prefix}_type_" "${prefix}_app_" "${prefix}_service_" "${prefix}_version_" \ +"${prefix}_src_" "${prefix}_dst_" "${prefix}_backup_file_" "${prefix}_args_" \ +"${prefix}_secret_" "${prefix}_cmd_" "${prefix}_data_" "${prefix}_command_" \ +"${prefix}_recipe_" "${prefix}_host_" "${prefix}_user_" "${prefix}_port_" \ +"${prefix}_provider_" "${prefix}_subcommands_" "${prefix}app" "${prefix}list" \ +"${prefix}ls" "${prefix}new" "${prefix}backup" "${prefix}deploy" \ +"${prefix}check" "${prefix}version" "${prefix}config" "${prefix}cp" \ +"${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ +"${prefix}delete" "${prefix}run" "${prefix}rollback" "${prefix}secret" \ +"${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ +"${prefix}release" "${prefix}versions" "${prefix}server" "${prefix}add" \ +"${prefix}init" "${prefix}apps" "${prefix}upgrade" "${prefix}doctor" \ +"${prefix}help"; eval "${prefix}"'__help=${var___help:-false}' eval "${prefix}"'__stack=${var___stack:-}' -eval "${prefix}"'__help=${var___help:-false}' -eval "${prefix}"'__verbose=${var___verbose:-false}' eval "${prefix}"'__skip_update=${var___skip_update:-false}' eval "${prefix}"'__env=${var___env:-}' eval "${prefix}"'__branch=${var___branch:-}' +eval "${prefix}"'__verbose=${var___verbose:-false}' +eval "${prefix}"'__skip_check=${var___skip_check:-false}' +eval "${prefix}"'__debug=${var___debug:-false}' eval "${prefix}"'__no_prompt=${var___no_prompt:-false}' eval "${prefix}"'__status=${var___status:-false}' eval "${prefix}"'__server=${var___server:-}' @@ -326,6 +330,7 @@ eval "${prefix}"'generate=${var_generate:-false}' eval "${prefix}"'insert=${var_insert:-false}' eval "${prefix}"'undeploy=${var_undeploy:-false}' eval "${prefix}"'recipe=${var_recipe:-false}' +eval "${prefix}"'release=${var_release:-false}' eval "${prefix}"'versions=${var_versions:-false}' eval "${prefix}"'server=${var_server:-false}' eval "${prefix}"'add=${var_add:-false}' @@ -335,9 +340,9 @@ eval "${prefix}"'upgrade=${var_upgrade:-false}' eval "${prefix}"'doctor=${var_doctor:-false}' eval "${prefix}"'help=${var_help:-false}'; local docopt_i=1 [[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do -declare -p "${prefix}__skip_check" "${prefix}__debug" "${prefix}__stack" \ -"${prefix}__help" "${prefix}__verbose" "${prefix}__skip_update" \ -"${prefix}__env" "${prefix}__branch" "${prefix}__no_prompt" \ +declare -p "${prefix}__help" "${prefix}__stack" "${prefix}__skip_update" \ +"${prefix}__env" "${prefix}__branch" "${prefix}__verbose" \ +"${prefix}__skip_check" "${prefix}__debug" "${prefix}__no_prompt" \ "${prefix}__status" "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ "${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ @@ -352,8 +357,9 @@ declare -p "${prefix}__skip_check" "${prefix}__debug" "${prefix}__stack" \ "${prefix}logs" "${prefix}ps" "${prefix}restore" "${prefix}rm" \ "${prefix}delete" "${prefix}run" "${prefix}rollback" "${prefix}secret" \ "${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ -"${prefix}versions" "${prefix}server" "${prefix}add" "${prefix}init" \ -"${prefix}apps" "${prefix}upgrade" "${prefix}doctor" "${prefix}help"; done; } +"${prefix}release" "${prefix}versions" "${prefix}server" "${prefix}add" \ +"${prefix}init" "${prefix}apps" "${prefix}upgrade" "${prefix}doctor" \ +"${prefix}help"; done; } # docopt parser above, complete command for generating this parser is `docopt.sh abra` PROGRAM_NAME=$(basename "$0") @@ -633,11 +639,11 @@ load_abra_sh() { if [ -f abra.sh ]; then # shellcheck disable=SC1091 source abra.sh - info "Loading abra.sh" + debug "Loading ./abra.sh" fi if [ -f "$APP_DIR/abra.sh" ]; then - info "Loading $APP_DIR/abra.sh" + debug "Loading $APP_DIR/abra.sh" # shellcheck disable=SC1090,SC1091 source "$APP_DIR/abra.sh" fi @@ -1854,7 +1860,6 @@ sub_recipe_list() { } ###### .. recipe versions - help_recipe_versions() { echo "abra [options] recipe versions @@ -1879,6 +1884,121 @@ sub_recipe_versions() { done } +###### .. recipe release +help_recipe_release() { + echo "abra [options] recipe release + +(For app maintainers) + +Make sure the service labels and git tags for are in sync with the +specified image tags. + +Run this after you have (or comrade \`renovate-bot\` has) bumped the version of +any of the images in ." +} + +sub_recipe_release() { + require_binary skopeo + require_yq + + recipe="$abra__recipe_" + recipe_dir="$ABRA_DIR/apps/$recipe" + + get_recipe_versions "$recipe" + latest_version="${RECIPE_VERSIONS[-1]}" + + cd "$recipe_dir" || error "Can't find recipe dir '$recipe_dir'" + + current_tag=$(git tag --points-at HEAD) + if [ -n "$current_tag" ]; then + error "$recipe is already on $current_tag, no release needed" + fi + + mapfile -t extra_compose_files < <(ls -- compose.*.yml || true) + + compose_files=("compose.yml" "${extra_compose_files[@]}") + + new_version="false" + + for compose_file in "${compose_files[@]}"; do + mapfile -t services < <($YQ e -N '.services | keys | .[]' "$compose_file" | sort -u) + + for service in "${services[@]}"; do + # 3wc: skip the "app" service unless we're in compose.yml; this service is + # often repeated in other compose.*.yml files to extend options, but we only + # want to add the deploy.label in one definition + # TODO 3wc: make this smarter, what if a separate compose file extends + # other services too? + if [ "$compose_file" != "compose.yml" ] && [ "$service" = "app" ]; then + debug "skipping '$service'" + continue + fi + debug "processing '$service'" + + service_image=$($YQ e ".services.$service.image" "$compose_file") + service_tag="${service_image##*:}" + + latest_data=$($JQ ".$recipe.versions.\"$latest_version\".\"$service\"" "$ABRA_APPS_JSON") + latest_tag="$(echo "$latest_data" | $JQ -r ".tag" -)" + + if [ "$service_tag" != "$latest_tag" ]; then + if [ "$service" = "app" ]; then + new_version="$service_tag" + fi + info "fetching $service_image metadata from Docker Hub" + service_data=$(skopeo inspect "docker://$service_image") + service_digest=$(echo "$service_data" | jq -r '.Digest' | cut -d':' -f2 | cut -c-8) + + label="coop-cloud.\${STACK_NAME}.$service.version=${service_tag}-${service_digest}" + + debug "Replacing version label on $service with $label" + + # delete old label, if one exists + $YQ eval -i "del(.services.$service.deploy.labels.[] | select(. == \"coop*\"))" "$compose_file" + # add new label + $YQ eval -i ".services.$service.deploy.labels += [\"$label\"]" "$compose_file" + else + debug "no updates for $service" + fi + done + done + + if [ "$new_version" = "false" ]; then + # `app` tag hasn't changed, just bump release + if echo "$latest_version" | grep -q '_'; then + latest_version_minor="${latest_version##*_}" + else + latest_version_minor=0 + fi + new_version_minor="$((latest_version_minor + 1))" + new_version="${latest_version%%*_}_$new_version_minor" + fi + + debug "Calculated new version $new_version" + + if [ "$new_version" = "$latest_version" ]; then + error "Hmm, something went wrong generating a new version number.." + fi + + success "All compose files updated; new version is $new_version" + + read -rp "Commit your changes to git? (y/[n])? " choice + + if [ "${choice,,}" != "y" ]; then + return + fi + + git commit -av || exit + + read -rp "Tag this as \`$new_version\`? (y/[n])? " choice + + if [ "${choice,,}" != "y" ]; then + return + fi + + git tag -a "$new_version" +} + ####################################### # abra server .. ####################################### From a261114bbc7519195a420e8f0a246265ad10bc72 Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Sun, 4 Apr 2021 23:35:13 +0200 Subject: [PATCH 5/8] Add --force to `recipe .. release` --- abra | 128 +++++++++++++++++++++++++-------------------- bin/app-version.sh | 76 --------------------------- 2 files changed, 70 insertions(+), 134 deletions(-) delete mode 100755 bin/app-version.sh diff --git a/abra b/abra index 0ce6491..39fd37a 100755 --- a/abra +++ b/abra @@ -36,7 +36,7 @@ Usage: abra [options] app undeploy abra [options] app [...] abra [options] recipe ls - abra [options] recipe release + abra [options] recipe release [--force] abra [options] recipe versions abra [options] server add [] [] abra [options] server new @@ -163,30 +163,29 @@ eval "var_$1+=($value)"; else eval "var_$1=$value"; fi; return 0; fi; done return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() { printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() { [[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() { -printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2286} -usage=${DOC:40:1677}; digest=07a57 -shorts=(-h -s -U -e -b -v -C -d -n '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') -longs=(--help --stack --skip-update --env --branch --verbose --skip-check --debug --no-prompt --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) -argcounts=(0 1 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ -switch __help 0; }; node_1(){ value __stack 1; }; node_2(){ -switch __skip_update 2; }; node_3(){ value __env 3; }; node_4(){ -value __branch 4; }; node_5(){ switch __verbose 5; }; node_6(){ -switch __skip_check 6; }; node_7(){ switch __debug 7; }; node_8(){ -switch __no_prompt 8; }; node_9(){ switch __status 9; }; node_10(){ -value __server 10; }; node_11(){ value __type 11; }; node_12(){ -value __domain 12; }; node_13(){ value __app_name 13; }; node_14(){ -switch __pass 14; }; node_15(){ switch __secrets 15; }; node_16(){ -switch __all 16; }; node_17(){ switch __update 17; }; node_18(){ -switch __force 18; }; node_19(){ switch __skip_version_check 19; }; node_20(){ -switch __volumes 20; }; node_21(){ switch __no_tty 21; }; node_22(){ -value __user 22; }; node_23(){ switch __dev 23; }; node_24(){ value _type_ a; } -node_25(){ value _app_ a; }; node_26(){ value _service_ a; }; node_27(){ -value _version_ a; }; node_28(){ value _src_ a; }; node_29(){ value _dst_ a; } -node_30(){ value _backup_file_ a; }; node_31(){ value _args_ a true; } -node_32(){ value _secret_ a; }; node_33(){ value _cmd_ a; }; node_34(){ -value _data_ a; }; node_35(){ value _command_ a; }; node_36(){ value _recipe_ a -}; node_37(){ value _host_ a; }; node_38(){ value _user_ a; }; node_39(){ -value _port_ a; }; node_40(){ value _provider_ a; }; node_41(){ +printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:1:2296} +usage=${DOC:40:1687}; digest=4e473 +shorts=(-e -d -b -n -v -s -C -U -h '' '' '' '' '' '' '' '' '' '' '' '' '' '' '') +longs=(--env --debug --branch --no-prompt --verbose --stack --skip-check --skip-update --help --status --server --type --domain --app-name --pass --secrets --all --update --force --skip-version-check --volumes --no-tty --user --dev) +argcounts=(1 0 1 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0); node_0(){ +value __env 0; }; node_1(){ switch __debug 1; }; node_2(){ value __branch 2; } +node_3(){ switch __no_prompt 3; }; node_4(){ switch __verbose 4; }; node_5(){ +value __stack 5; }; node_6(){ switch __skip_check 6; }; node_7(){ +switch __skip_update 7; }; node_8(){ switch __help 8; }; node_9(){ +switch __status 9; }; node_10(){ value __server 10; }; node_11(){ +value __type 11; }; node_12(){ value __domain 12; }; node_13(){ +value __app_name 13; }; node_14(){ switch __pass 14; }; node_15(){ +switch __secrets 15; }; node_16(){ switch __all 16; }; node_17(){ +switch __update 17; }; node_18(){ switch __force 18; }; node_19(){ +switch __skip_version_check 19; }; node_20(){ switch __volumes 20; }; node_21(){ +switch __no_tty 21; }; node_22(){ value __user 22; }; node_23(){ switch __dev 23 +}; node_24(){ value _type_ a; }; node_25(){ value _app_ a; }; node_26(){ +value _service_ a; }; node_27(){ value _version_ a; }; node_28(){ value _src_ a +}; node_29(){ value _dst_ a; }; node_30(){ value _backup_file_ a; }; node_31(){ +value _args_ a true; }; node_32(){ value _secret_ a; }; node_33(){ value _cmd_ a +}; node_34(){ value _data_ a; }; node_35(){ value _command_ a; }; node_36(){ +value _recipe_ a; }; node_37(){ value _host_ a; }; node_38(){ value _user_ a; } +node_39(){ value _port_ a; }; node_40(){ value _provider_ a; }; node_41(){ value _subcommands_ a true; }; node_42(){ _command app; }; node_43(){ _command list; }; node_44(){ _command ls; }; node_45(){ _command new; } node_46(){ _command backup; }; node_47(){ _command deploy; }; node_48(){ @@ -225,7 +224,7 @@ required 74 42 25 59 61 32 27 34 83; }; node_119(){ either 32 16; }; node_120(){ required 119; }; node_121(){ required 74 42 25 59 103 120 83; }; node_122(){ required 74 42 25 62; }; node_123(){ optional 110; }; node_124(){ required 74 42 25 35 123; }; node_125(){ required 74 63 44; }; node_126(){ -required 74 63 36 64; }; node_127(){ required 74 63 36 65; }; node_128(){ +required 74 63 36 64 90; }; node_127(){ required 74 63 36 65; }; node_128(){ optional 38; }; node_129(){ optional 39; }; node_130(){ required 74 66 67 37 128 129; }; node_131(){ required 74 66 45 40; } node_132(){ required 74 66 76; }; node_133(){ required 74 66 37 55; } @@ -236,9 +235,9 @@ node_141(){ optional 140; }; node_142(){ required 74 72 141; }; node_143(){ required 74; }; node_144(){ either 80 85 88 93 94 95 96 97 99 100 101 105 107 111 112 117 118 121 122 124 125 126 127 130 131 132 133 134 135 137 138 139 142 143 }; node_145(){ required 144; }; cat <<<' docopt_exit() { -[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1677}" >&2 -exit 1; }'; unset var___help var___stack var___skip_update var___env \ -var___branch var___verbose var___skip_check var___debug var___no_prompt \ +[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:40:1687}" >&2 +exit 1; }'; unset var___env var___debug var___branch var___no_prompt \ +var___verbose var___stack var___skip_check var___skip_update var___help \ var___status var___server var___type var___domain var___app_name var___pass \ var___secrets var___all var___update var___force var___skip_version_check \ var___volumes var___no_tty var___user var___dev var__type_ var__app_ \ @@ -249,10 +248,10 @@ var_new var_backup var_deploy var_check var_version var_config var_cp var_logs \ var_ps var_restore var_rm var_delete var_run var_rollback var_secret \ var_generate var_insert var_undeploy var_recipe var_release var_versions \ var_server var_add var_init var_apps var_upgrade var_doctor var_help -parse 145 "$@"; local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" \ -"${prefix}__stack" "${prefix}__skip_update" "${prefix}__env" \ -"${prefix}__branch" "${prefix}__verbose" "${prefix}__skip_check" \ -"${prefix}__debug" "${prefix}__no_prompt" "${prefix}__status" \ +parse 145 "$@"; local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__env" \ +"${prefix}__debug" "${prefix}__branch" "${prefix}__no_prompt" \ +"${prefix}__verbose" "${prefix}__stack" "${prefix}__skip_check" \ +"${prefix}__skip_update" "${prefix}__help" "${prefix}__status" \ "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ "${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ @@ -269,15 +268,15 @@ parse 145 "$@"; local prefix=${DOCOPT_PREFIX:-''}; unset "${prefix}__help" \ "${prefix}generate" "${prefix}insert" "${prefix}undeploy" "${prefix}recipe" \ "${prefix}release" "${prefix}versions" "${prefix}server" "${prefix}add" \ "${prefix}init" "${prefix}apps" "${prefix}upgrade" "${prefix}doctor" \ -"${prefix}help"; eval "${prefix}"'__help=${var___help:-false}' -eval "${prefix}"'__stack=${var___stack:-}' -eval "${prefix}"'__skip_update=${var___skip_update:-false}' -eval "${prefix}"'__env=${var___env:-}' -eval "${prefix}"'__branch=${var___branch:-}' -eval "${prefix}"'__verbose=${var___verbose:-false}' -eval "${prefix}"'__skip_check=${var___skip_check:-false}' +"${prefix}help"; eval "${prefix}"'__env=${var___env:-}' eval "${prefix}"'__debug=${var___debug:-false}' +eval "${prefix}"'__branch=${var___branch:-}' eval "${prefix}"'__no_prompt=${var___no_prompt:-false}' +eval "${prefix}"'__verbose=${var___verbose:-false}' +eval "${prefix}"'__stack=${var___stack:-}' +eval "${prefix}"'__skip_check=${var___skip_check:-false}' +eval "${prefix}"'__skip_update=${var___skip_update:-false}' +eval "${prefix}"'__help=${var___help:-false}' eval "${prefix}"'__status=${var___status:-false}' eval "${prefix}"'__server=${var___server:-}' eval "${prefix}"'__type=${var___type:-}' @@ -340,9 +339,9 @@ eval "${prefix}"'upgrade=${var_upgrade:-false}' eval "${prefix}"'doctor=${var_doctor:-false}' eval "${prefix}"'help=${var_help:-false}'; local docopt_i=1 [[ $BASH_VERSION =~ ^4.3 ]] && docopt_i=2; for ((;docopt_i>0;docopt_i--)); do -declare -p "${prefix}__help" "${prefix}__stack" "${prefix}__skip_update" \ -"${prefix}__env" "${prefix}__branch" "${prefix}__verbose" \ -"${prefix}__skip_check" "${prefix}__debug" "${prefix}__no_prompt" \ +declare -p "${prefix}__env" "${prefix}__debug" "${prefix}__branch" \ +"${prefix}__no_prompt" "${prefix}__verbose" "${prefix}__stack" \ +"${prefix}__skip_check" "${prefix}__skip_update" "${prefix}__help" \ "${prefix}__status" "${prefix}__server" "${prefix}__type" "${prefix}__domain" \ "${prefix}__app_name" "${prefix}__pass" "${prefix}__secrets" "${prefix}__all" \ "${prefix}__update" "${prefix}__force" "${prefix}__skip_version_check" \ @@ -1902,19 +1901,29 @@ sub_recipe_release() { require_yq recipe="$abra__recipe_" + force="$abra___force" recipe_dir="$ABRA_DIR/apps/$recipe" - get_recipe_versions "$recipe" - latest_version="${RECIPE_VERSIONS[-1]}" - cd "$recipe_dir" || error "Can't find recipe dir '$recipe_dir'" + get_recipe_versions "$recipe" + + if [ "${#RECIPE_VERSIONS[@]}" -gt 0 ]; then + latest_version="${RECIPE_VERSIONS[-1]}" + latest_version_message=$(git tag -l "$latest_version" --format='%(contents)') + info "Latest available version: '$latest_version'" + else + latest_version="" + latest_version_message="" + info "No previous releases found" + fi + current_tag=$(git tag --points-at HEAD) - if [ -n "$current_tag" ]; then + if [ "$force" = "false" ] && [ -n "$current_tag" ]; then error "$recipe is already on $current_tag, no release needed" fi - mapfile -t extra_compose_files < <(ls -- compose.*.yml || true) + mapfile -t extra_compose_files < <(ls -- compose.*.yml 2> /dev/null || true) compose_files=("compose.yml" "${extra_compose_files[@]}") @@ -1930,22 +1939,24 @@ sub_recipe_release() { # TODO 3wc: make this smarter, what if a separate compose file extends # other services too? if [ "$compose_file" != "compose.yml" ] && [ "$service" = "app" ]; then - debug "skipping '$service'" + debug "Skipping '$service'" continue fi - debug "processing '$service'" + debug "Processing '$service'" service_image=$($YQ e ".services.$service.image" "$compose_file") service_tag="${service_image##*:}" - latest_data=$($JQ ".$recipe.versions.\"$latest_version\".\"$service\"" "$ABRA_APPS_JSON") - latest_tag="$(echo "$latest_data" | $JQ -r ".tag" -)" + if [ -n "$latest_version" ]; then + latest_data=$($JQ ".$recipe.versions.\"$latest_version\".\"$service\"" "$ABRA_APPS_JSON") + latest_tag="$(echo "$latest_data" | $JQ -r ".tag" -)" + fi - if [ "$service_tag" != "$latest_tag" ]; then + if [ -z "$latest_version" ] || [ "$force" = "true" ] || [ "$service_tag" != "$latest_tag" ]; then if [ "$service" = "app" ]; then new_version="$service_tag" fi - info "fetching $service_image metadata from Docker Hub" + info "Fetching $service_image metadata from Docker Hub" service_data=$(skopeo inspect "docker://$service_image") service_digest=$(echo "$service_data" | jq -r '.Digest' | cut -d':' -f2 | cut -c-8) @@ -1958,7 +1969,7 @@ sub_recipe_release() { # add new label $YQ eval -i ".services.$service.deploy.labels += [\"$label\"]" "$compose_file" else - debug "no updates for $service" + debug "no updates for '$service_image'" fi done done @@ -1976,7 +1987,7 @@ sub_recipe_release() { debug "Calculated new version $new_version" - if [ "$new_version" = "$latest_version" ]; then + if [ -n "$latest_version" ] && [ "$force" = "false" ] && [ "$new_version" = "$latest_version" ]; then error "Hmm, something went wrong generating a new version number.." fi @@ -1988,7 +1999,7 @@ sub_recipe_release() { return fi - git commit -av || exit + git commit -avem "Version $new_version; sync labels" || exit read -rp "Tag this as \`$new_version\`? (y/[n])? " choice @@ -1996,7 +2007,8 @@ sub_recipe_release() { return fi - git tag -a "$new_version" + test "$force" = "true" && git tag -d "$new_version" + git tag -aem "$latest_version_message" "$new_version" } ####################################### diff --git a/bin/app-version.sh b/bin/app-version.sh deleted file mode 100755 index 37b070d..0000000 --- a/bin/app-version.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -# Usage: ./app-version.sh app-directory -# -# Pull image tag versions and digests into each recipe's compose.yml file, and -# update git tags to point to the new ref - -YQ="$HOME/.abra/vendor/yq" - -function process_file() { - compose_file="${1?Compose file not set}" - - mapfile -t services < <($YQ e -N '.services | keys | .[]' "$compose_file" | sort -u) - - for service in "${services[@]}"; do - # 3wc: skip the "app" service unless we're in compose.yml; this service is - # often repeated in other compose.*.yml files to extend options, but we only - # want to add the deploy.label in one definition - # TODO 3wc: make this smarter, what if a separate compose file extends - # other services too? - if [ "$compose_file" != "compose.yml" ] && [ "$service" = "app" ]; then - echo "debug: skipping $service" - continue - fi - - echo "debug: processing $service" - - service_data=$(jq ".$app.versions.\"$latest_version\".$service" ~/.abra/apps.json) - service_tag=$(echo "$service_data" | jq -r '.tag') - service_digest=$(echo "$service_data" | jq -r '.digest') - - label="coop-cloud.\${STACK_NAME}.$service.version=${service_tag}-${service_digest}" - - # delete old label, if one exists - $YQ eval -i "del(.services.$service.deploy.labels.[] | select(. == \"coop*\"))" "$compose_file" - # add new label - $YQ eval -i ".services.$service.deploy.labels += [\"$label\"]" "$compose_file" - - done -} - -stack_dir="${ABRA_DIR:-$HOME/.abra}/apps/" - -app="${1?No app provided}" - -cd "$stack_dir/$app" || exit - -mapfile -t extra_compose_files < <(ls -- compose.*.yml || true) - -compose_files=("compose.yml" "${extra_compose_files[@]}") - -mapfile -t git_tags < <(git tag -l) -latest_version="${git_tags[-1]}" -latest_version_message=$(git tag -l "$latest_version" --format='%(contents)') - -echo "debug: latest version: $latest_version" - -for compose_file in "${compose_files[@]}"; do - process_file "$compose_file" -done - -read -rp "On the next screen, you'll see the proposed changes; press 'q' to exit when you're done. Hit 'return' / 'enter' to continue now" choice - -git diff - -read -rp "Commit this change and replace the '$latest_version' tag? (y/[n])? " choice - -if [ "${choice,,}" != "y" ]; then - exit -fi - -git commit -a -m "Auto-add service labels" - -git tag -d "$latest_version" - -git tag -a "$latest_version" -m "$latest_version_message" From 103a4941c7053033dc1b1b9d6bc473d69471703b Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Mon, 5 Apr 2021 11:26:07 +0200 Subject: [PATCH 6/8] Reliability improvements to `recipe .. release` --- abra | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/abra b/abra index 39fd37a..c0ff426 100755 --- a/abra +++ b/abra @@ -448,7 +448,7 @@ require_consent_for_update() { fi } -require_docker_version (){ +require_docker_version() { get_servers MIN_DOCKER_VERSION=19 @@ -541,9 +541,11 @@ require_app (){ error "Could not retrieve app type '$APP', this app type doesn't exist?" fi + cd "$APP_DIR" && checkout_main_or_master + if [[ $(cd "$ABRA_DIR/apps/$APP" && git branch --list | wc -l) == "0" ]]; then debug "Failed to clone default branch, guessing alternative is 'main'" - (cd "$ABRA_DIR/apps/$APP" && git checkout main > /dev/null 2>&1) + (cd "$ABRA_DIR/apps/$APP" && git checkout main) fi success "Fetched app configuration via Git" @@ -611,6 +613,10 @@ require_yq() { vendor_binary "https://github.com/mikefarah/yq" "v4.6.1" "yq_linux_amd64" } +checkout_main_or_master() { + git checkout main > /dev/null 2>&1 || git checkout master > /dev/null 2>&1 +} + # FIXME 3wc: update or remove if [ -z "$ABRA_ENV" ] && [ -f .env ] && type direnv > /dev/null 2>&1 && ! direnv status | grep -q 'Found RC allowed true'; then error "direnv is blocked, run direnv allow" @@ -629,7 +635,7 @@ get_recipe_versions() { recipe="${1?Recipe not set}" - mapfile -t RECIPE_VERSIONS < <($JQ -r ".${recipe}.versions | keys | .[]" "$ABRA_APPS_JSON" | sort) + mapfile -t RECIPE_VERSIONS < <($JQ -r ".\"${recipe}\".versions | keys | .[]" "$ABRA_APPS_JSON" | sort) } ###### Run-time loading @@ -1730,7 +1736,7 @@ sub_app_rollback(){ abra__version_="$version" abra___update="true" - sub_app_deploy + sub_app_deploy } ###### .. app logs @@ -1871,7 +1877,7 @@ sub_recipe_versions() { printf "%s thrilling versions of $abra__recipe_:\n" "${#RECIPE_VERSIONS[@]}" for version in "${RECIPE_VERSIONS[@]}"; do - recipe_version_data=$($JQ -r ".${abra__recipe_}.versions.\"${version}\"" "$ABRA_APPS_JSON") + recipe_version_data=$($JQ -r ".\"${abra__recipe_}\".versions.\"${version}\"" "$ABRA_APPS_JSON") mapfile -t services < <(echo "$recipe_version_data" | $JQ -r ". | keys | .[]" -) printf '%s:\n' "$version" for service in "${services[@]}"; do @@ -1902,7 +1908,7 @@ sub_recipe_release() { recipe="$abra__recipe_" force="$abra___force" - recipe_dir="$ABRA_DIR/apps/$recipe" + recipe_dir="$ABRA_DIR/apps/$recipe" cd "$recipe_dir" || error "Can't find recipe dir '$recipe_dir'" @@ -1914,7 +1920,7 @@ sub_recipe_release() { info "Latest available version: '$latest_version'" else latest_version="" - latest_version_message="" + latest_version_message="Initial tagged release" info "No previous releases found" fi @@ -1923,6 +1929,14 @@ sub_recipe_release() { error "$recipe is already on $current_tag, no release needed" fi + if [ "$(git rev-parse --abbrev-ref --symbolic-full-name HEAD)" = "HEAD" ]; then + warning "It looks like $recipe_dir is in 'detached HEAD' state" + read -rp "Check out main/master branch first? [Y/n] " + if [ "${choice,,}" != "n" ]; then + checkout_main_or_master + fi + fi + mapfile -t extra_compose_files < <(ls -- compose.*.yml 2> /dev/null || true) compose_files=("compose.yml" "${extra_compose_files[@]}") @@ -1948,7 +1962,7 @@ sub_recipe_release() { service_tag="${service_image##*:}" if [ -n "$latest_version" ]; then - latest_data=$($JQ ".$recipe.versions.\"$latest_version\".\"$service\"" "$ABRA_APPS_JSON") + latest_data=$($JQ ".\"$recipe\".versions.\"$latest_version\".\"$service\"" "$ABRA_APPS_JSON") latest_tag="$(echo "$latest_data" | $JQ -r ".tag" -)" fi @@ -1975,6 +1989,7 @@ sub_recipe_release() { done if [ "$new_version" = "false" ]; then + # `app` tag hasn't changed, just bump release if echo "$latest_version" | grep -q '_'; then latest_version_minor="${latest_version##*_}" From 028c7dbde558ebaf375914d5d590b8a9ff984ab5 Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Mon, 5 Apr 2021 12:31:28 +0200 Subject: [PATCH 7/8] Tweak `recipe .. release` docs --- abra | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/abra b/abra index c0ff426..015b58a 100755 --- a/abra +++ b/abra @@ -1893,13 +1893,22 @@ sub_recipe_versions() { help_recipe_release() { echo "abra [options] recipe release -(For app maintainers) +(For recipe maintainers) Make sure the service labels and git tags for are in sync with the specified image tags. -Run this after you have (or comrade \`renovate-bot\` has) bumped the version of -any of the images in ." +Run this after you or comrade \`renovate-bot\` have bumped the version of +any of the images in . + +OPTIONS + --force Over-write existing tag; use this if you have a git tag for the + recipe version already, to make sure labels are in sync. + +POWERED BY + skopeo inspect docker://image:tag + git commit + git tag" } sub_recipe_release() { From a26a0d27d7ac3e5d620ef408b6cd5377c5c4881f Mon Sep 17 00:00:00 2001 From: 3wc <3wc.git@doesthisthing.work> Date: Mon, 5 Apr 2021 12:31:45 +0200 Subject: [PATCH 8/8] Reduce noise from app-json.py --- bin/app-json.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bin/app-json.py b/bin/app-json.py index 0fbf1ac..32bc853 100755 --- a/bin/app-json.py +++ b/bin/app-json.py @@ -13,7 +13,7 @@ from os.path import basename, exists, expanduser from pathlib import Path from re import findall, search from shlex import split -from subprocess import check_output +from subprocess import check_output, DEVNULL from sys import exit from requests import get @@ -31,6 +31,8 @@ REPOS_TO_SKIP = ( "organising", "pyabra", "stack-ssh-deploy", + "radicle-seed-node", + "coturn" ) log = getLogger(__name__) @@ -38,10 +40,9 @@ basicConfig() log.setLevel(DEBUG) -def _run_cmd(cmd, shell=False): +def _run_cmd(cmd, shell=False, **kwargs): """Run a shell command.""" args = [split(cmd)] - kwargs = {} if shell: args = [cmd] @@ -187,7 +188,8 @@ def get_app_versions(app_path, cached_apps_json): existing_tags = [] for tag in tags: - _run_cmd(f"git checkout {tag}") + _run_cmd(f"git checkout {tag}", + stderr=DEVNULL) services_cmd = f"{YQ_PATH} e '.services | keys | .[]' compose*.yml" services = _run_cmd(services_cmd, shell=True).split()