Last active
February 10, 2023 00:47
-
-
Save ryran/072409b1b7efd5018683a8c45e019652 to your computer and use it in GitHub Desktop.
OCP4: leverage api.openshift.com/api/upgrades_info to inspect OCP versions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ocp4-chk-upgrade-channel v1.4 last mod 2022/11/16 | |
# https://gist.github.com/ryran/072409b1b7efd5018683a8c45e019652 | |
# Copyright 2020, 2021, 2022 Ryan Sawhill Aroha <[email protected]> | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
# General Public License <gnu.org/licenses/gpl.html> for more details. | |
export LC_ALL=en_US.UTF-8 | |
# Get version from line #2 | |
version=$(sed '2q;d' $0) | |
# Colors | |
[[ $OCP4_CHK_UPGRADE_NO_COLORS == 1 || $BASH_VERSINFO -lt 4 ]] || | |
declare -A c=( | |
[z]='\033[0;0m' # zero (reset) | |
[Q]='\033[0;0m\033[1;1m' # bold | |
[dim]='\033[0;0m\033[2m' # dim | |
[rdim]='\033[2;31m' [r]='\033[0;31m' [R]='\033[1;31m' # red | |
[gdim]='\033[2;32m' [g]='\033[0;32m' [G]='\033[1;32m' # green | |
[ydim]='\033[2;33m' [y]='\033[0;33m' [Y]='\033[1;33m' # yellow | |
[bdim]='\033[2;34m' [b]='\033[0;34m' [B]='\033[1;34m' # blue | |
[mdim]='\033[2;35m' [m]='\033[0;35m' [M]='\033[1;35m' # maroon | |
[cdim]='\033[2;36m' [c]='\033[0;36m' [C]='\033[1;36m' # cyan | |
) | |
# Return codes | |
declare -A RCs=( | |
[majorvers_chan_missing]=9 | |
[release_in_no_channels]=8 | |
[release_has_no_upgrades]=7 | |
[release_has_no_releasetxt]=6 | |
[release_highest_channel_is_candidate]=5 | |
[release_highest_channel_is_fast]=4 | |
) | |
# Fancy versions of upgrade channel-types | |
declare -A fancyChan=( | |
[eus]="${c[G]}🌲 ✔ eus 🌲 ✔ " | |
[stable]="${c[G]}✔✔✔ stable ✔✔✔" | |
[fast]="${c[Y]}⚠️ ⚠️ fast ⚠️ ⚠️ " | |
[candidate]="${c[R]}⛔ ☢️ candidate ⛔ ☢️ " | |
[prerelease]="${c[R]}⛔ ☢️ prerelease ⛔ ☢️ " | |
[null]="${c[R]}🛑 🚫 U N K N O W N 🛑 🚫 " | |
) | |
# Main stderr printing function | |
print() { | |
# only print if in verbose mode | |
if [[ $1 == --verbose ]]; then | |
[[ $verbose ]] || return | |
shift | |
fi | |
local echo_opts= | |
while [[ $1 =~ ^- ]]; do | |
echo_opts+="$1 " | |
shift | |
done | |
echo -e $echo_opts "$@" >&2 | |
} | |
get_releasetxt_url() { | |
echo https://mirror.openshift.com/pub/openshift-v4/clients/ocp/${1}/release.txt | |
} | |
# Whoami | |
me=${0##*/} | |
# This is the API we talk to | |
api=https://api.openshift.com/api/upgrades_info/v1 | |
# Just for help page | |
releaseTxtDummyUrl=$(get_releasetxt_url X.Y.Z) | |
# Default exit | |
rc=0 | |
# Whether we found a release.txt | |
releasetxtPublished= | |
# Blah | |
dimGreenCheck="${c[gdim]}✔${c[z]}" | |
dimYellowCheck="${c[ydim]}✔${c[z]}" | |
dimRedX="${c[rdim]}✘${c[z]}" | |
# Opts for getopt | |
opts_short=hiec:a:v | |
opts_long=help,ignore-releasetxt,allow-non-stable,chan:,arch:,verbose | |
# Populated from args/opts | |
ignoreMissingReleaseTxt= allowNonStable= channel= arch= verbose= | |
opMode= releaseMajor= releaseMinor= release= major= | |
# ██╗ ██╗███████╗██╗ ██████╗ | |
# ██║ ██║██╔════╝██║ ██╔══██╗ | |
# ███████║█████╗ ██║ ██████╔╝ | |
# ██╔══██║██╔══╝ ██║ ██╔═══╝ | |
# ██║ ██║███████╗███████╗██║ | |
# ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ | |
usage() { | |
print "$me: $@" | |
exit 1 | |
} | |
halp() { | |
local mode1_usage mode2_usage global_usage operating_mode mode1_line mode2_line halp_short halp halp_addendum | |
mode1_usage="$me [OPTS] [--allow-non-stable] ${c[B]}X.Y.Z${c[z]}" | |
mode1_line="Determine highest channel in which the X.Y.Z release can be found" | |
mode2_usage="$me [OPTS] [-c CHANNEL] ${c[C]}X.Y${c[z]}" | |
mode2_line="Determine newest release of X.Y version found in a channel" | |
mode3_usage="$me [OPTS] [-c CHANNEL] ${c[M]}X.Y.Z X.Y${c[z]}" | |
mode3_line="Determine available upgrade paths from X.Y.Z release to a different X.Y version" | |
operating_mode="" | |
halp_short=$(cat <<-EOF | |
${c[Q]}Usage: $mode1_usage | |
$mode1_line | |
${c[Q]}Usage: $mode2_usage | |
$mode2_line | |
${c[Q]}Usage: $mode3_usage | |
$mode3_line | |
EOF | |
) | |
halp=$(cat <<-EOF | |
${c[Q]}Usage: $me ${c[B]}X.Y.Z${c[Q]} | ${c[C]}X.Y${c[Q]} | ${c[M]}X.Y.Z X.Y${c[z]} | |
${c[Q]}Leverage ${api#https://}/openapi to inspect OCP versions${c[z]} | |
Operating mode is chosen based on presence of ${c[b]}X.Y.Z${c[z]} or ${c[c]}X.Y${c[z]} or ${c[m]}both${c[z]}. | |
${c[B]}Mode 1: $mode1_usage | |
$mode1_line | |
${c[b]}X.Y.Z${c[z]} (e.g. "4.6.12") | |
Searches for \$ARCH release of X.Y.Z release in channels: stable, fast, candidate | |
Prints to stdout the *highest* channel in which that release is tagged (e.g. "stable") | |
Exit codes: | |
• ${c[R]}${RCs[release_in_no_channels]}${c[z]} = release in NO channels | |
• ${c[r]}${RCs[release_has_no_releasetxt]}${c[z]} = release has no published release.txt (see "-i") | |
• ${c[m]}${RCs[release_highest_channel_is_candidate]}${c[z]} = highest channel: candidate (or prerelease) | |
• ${c[y]}${RCs[release_highest_channel_is_fast]}${c[z]} = highest channel: fast | |
• ${c[g]}0${c[z]} = in stable channel | |
${c[b]}-e, --allow-non-stable${c[z]} | |
Prevent throwing exit code ${c[m]}${RCs[release_highest_channel_is_candidate]}${c[z]} or ${c[y]}${RCs[release_highest_channel_is_fast]}${c[z]} when release is only in candidate or fast channel | |
Note that a missing release.txt could still trigger exit code ${c[r]}${RCs[release_has_no_releasetxt]}${c[z]} unless "-i" is also used | |
${c[C]}Mode 2: $mode2_usage | |
$mode2_line | |
${c[c]}X.Y${c[z]} (e.g. "4.6") | |
Searches for newest \$ARCH release of X.Y version in \$CHANNEL | |
Prints exact X.Y.Z release to stdout (e.g. "4.6.12") | |
Exit codes: | |
• ${c[R]}${RCs[majorvers_chan_missing]}${c[z]} = \$CHANNEL-X.Y doesn't exist for \$ARCH | |
• ${c[r]}${RCs[release_has_no_releasetxt]}${c[z]} = the latest release has no published release.txt (see "-i") | |
• ${c[g]}0${c[z]} = found latest good release | |
${c[c]}-c, --chan CHANNEL${c[z]} (upgrade channel type) | |
Allow selecting alternative to the default of "stable" | |
Other choices: eus, fast, candidate (or alias "cand"), prerelease | |
${c[M]}Mode 3: $mode3_usage | |
$mode3_line | |
${c[m]}X.Y.Z X.Y${c[z]} | |
Lists all \$ARCH X.Y releases in a channel that allow upgrading from X.Y.Z | |
By default, search starts in stable; if no results, then fast; then candidate | |
Prints to stdout a list of release numbers in ascending order | |
Exit codes: | |
• ${c[R]}${RCs[majorvers_chan_missing]}${c[z]} = none of the *-X.Y channels exist for \$ARCH | |
with -c/--chan: \$CHANNEL-X.Y doesn't exist for \$ARCH | |
• ${c[r]}${RCs[release_has_no_upgrades]}${c[z]} = none of the *-X.Y channels have valid upgrade paths for \$ARCH | |
with -c/--chan: no valid upgrade paths in \$CHANNEL-X.Y for \$ARCH | |
• ${c[g]}0${c[z]} = found valid upgrade paths in one of the *-X.Y channels for \$ARCH | |
with -c/--chan: found valid upgrade paths in \$CHANNEL-X.Y for \$ARCH | |
${c[m]}-c, --chan CHANNEL${c[z]} (upgrade channel type) | |
Look for upgrade paths only in \$CHANNEL (instead of searching through stable, fast, candidate) | |
Choices: eus, stable, fast, candidate (or alias "cand"), prerelease | |
${c[Q]}Mode-independent options${c[z]} | |
-i, --ignore-releasetxt | |
Allow version-specific release.txt to NOT be present at: | |
• ${releaseTxtDummyUrl#https://} | |
Otherwise, if it's missing, exit code ${c[r]}${RCs[release_has_no_releasetxt]}${c[z]} will be thrown, even if: | |
• ${c[b]}[mode 1]${c[z]} X.Y.Z release is tagged into one of the 3 main channels | |
• ${c[c]}[mode 2]${c[z]} the latest X.Y release was found | |
-a, --arch ARCH (architecture) | |
Allow selecting alternative to default of "amd64" | |
-v, --verbose | |
Print extra detail to stderr, including more about what's happening + full json release details | |
${c[c]}[mode 2]${c[z]} Also print list of all releases in \$CHANNEL | |
${c[m]}[mode 3]${c[z]} Also print full json details of each release | |
EOF | |
) | |
halp_addendum=$(cat <<-EOF | |
${c[Q]}Notes${c[z]} | |
• In all operating modes, the simple answer (if found) is printed to stdout; fancier explanatory | |
output will always be printed to stderr. | |
• On eus channels: one could argue that modes 1 & 3 should check & treat "eus" channels as | |
*higher* than "stable" ... rsaw is ambivalent about this. Feel free to contact him. | |
• On prerelease channels: as of 2021-01, these were only used in 4.1 & 4.2, so are ignored. | |
• Export ${c[r]}OCP4_CHK_UPGRADE_NO_COLORS=1${c[z]} to disable colors. | |
• You're looking at the full help page, as shown when executed with "--help" | |
To see the same without Notes & Requirements, execute with "-h" | |
For the most succinct usage summary, execute with no args | |
${c[Q]}Requirements${c[z]} | |
• curl | |
• jq (https://github.com/stedolan/jq/releases) | |
• getopt (GNU) | |
EOF | |
) | |
local rc=0 | |
if [[ $1 == --long ]]; then | |
print "$halp\n\n$halp_addendum\n" | |
print "${c[dim]}${version:2}${c[z]}" | |
print "${c[dim]}https://gist.github.com/ryran/072409b1b7efd5018683a8c45e019652${c[z]}" | |
elif [[ $1 == --mid ]]; then | |
print "$halp\n" | |
print "${c[dim]}Help: run with ${c[z]}--help${c[dim]} to see extra notes; for simple usage, run w/ no args${c[z]}" | |
print "${c[dim]}Disable colors: export ${c[z]}OCP4_CHK_UPGRADE_NO_COLORS=1" | |
print "\n${c[dim]}${version:2}${c[z]}" | |
print "${c[dim]}https://gist.github.com/ryran/072409b1b7efd5018683a8c45e019652${c[z]}" | |
else | |
if [[ $1 != --short ]]; then | |
print "${c[R]}$@${c[z]}\n" | |
rc=1 | |
fi | |
print "$halp_short\n" | |
print "${c[dim]}See help: run with ${c[z]}-h" | |
print "${c[dim]}Disable colors: export ${c[z]}OCP4_CHK_UPGRADE_NO_COLORS=1" | |
fi | |
exit $rc | |
} | |
validate_url() { | |
local errs | |
if errs=$(curl --fail -o /dev/null -LSsI $1 2>&1); then | |
[[ $verbose ]] && print "$dimGreenCheck" | |
return | |
else | |
print "\n$indent${c[R]}✘ Failed to get $1${c[z]}${c[r]}\n$indent $errs${c[z]}" | |
return 2 | |
fi | |
} | |
validate_releasetxt() { | |
local url=$(get_releasetxt_url $1) failtxt= | |
if [[ $ignoreMissingReleaseTxt ]]; then | |
local color=${c[ydim]} COLOR=${c[y]} | |
else | |
local color=${c[r]} COLOR=${c[R]} | |
fi | |
if [[ $verbose ]]; then | |
print -n "${c[dim]}Checking for ${url} ...${c[z]} " | |
failtxt="${COLOR}✘${c[z]}\n ${COLOR}Failed to get release.txt" | |
else | |
failtxt="${COLOR}✘ Failed to get $url" | |
fi | |
failtxt+="${c[z]}\n ${color}This would be bad for a normal public release${c[z]}\n ${color}" | |
[[ $ignoreMissingReleaseTxt ]] || failtxt+="You can use ${c[Q]}-i/--ignore-releasetxt${c[z]}${color} to prevent this failure from throwing rc ${RCs[release_has_no_releasetxt]}${c[z]}\n ${color}" | |
if failtxt+=$(curl --fail -o /dev/null -LSsI $url 2>&1); then | |
[[ $verbose ]] && print "$dimGreenCheck" | |
return 0 | |
else | |
print "${failtxt}${c[z]}" | |
# If this is set, return success; otherwise failure | |
[[ $ignoreMissingReleaseTxt ]] | |
return | |
fi | |
} | |
# ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗ | |
# ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║ | |
# ██║ ███╗██████╔╝███████║██████╔╝███████║ | |
# ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║ | |
# ╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║ | |
# ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ | |
graph_channel_upgrades_info() { | |
local channel=$1 major=$2 minor=$3 Arch=${arch% } | |
local chan=$channel-$major | |
local url="$api/graph?channel=$chan&arch=${Arch:-amd64}" | |
local out= numReleases= err= | |
out=$(curl -sSLH "Accept: application/json" "$url") | |
numReleases=$(jq -e '.nodes | length' <<<$out) || numReleases=0 | |
if (( numReleases == 0)); then | |
[[ $verbose ]] && print -n "${c[r]}✘${c[z]}\n ${c[r]}" || print -n "${c[r]}✘ " | |
print "API gave no results for ${arch}$chan${c[z]}${c[rdim]}\n Failed to get $url${c[z]}" | |
return 2 | |
fi | |
if [[ $minor ]]; then | |
if jq -Me --arg vers "$major.$minor" '.nodes[] | select(.version == $vers)' &>/dev/null <<<$out; then | |
print --verbose "$dimGreenCheck" | |
elif jq -Me --arg vers "$major.$minor" '.nodes[] | select(.version |startswith($vers))' >&2 <<<$out; then | |
print --verbose "$dimYellowCheck" | |
else | |
err=1 | |
print --verbose "$dimRedX" | |
fi | |
else | |
print --verbose "$dimGreenCheck" | |
fi | |
# if [[ $DEBUG ]]; then | |
# print -n "${c[dim]}" | |
# jq -M >&2 <<<$out | |
# print -n "${c[z]}" | |
# fi | |
echo "$out" | |
return ${err:-0} | |
} | |
# ███╗ ███╗ ██████╗ ██████╗ ███████╗ ██╗ | |
# ████╗ ████║██╔═══██╗██╔══██╗██╔════╝ ███║ | |
# ██╔████╔██║██║ ██║██║ ██║█████╗ ╚██║ | |
# ██║╚██╔╝██║██║ ██║██║ ██║██╔══╝ ██║ | |
# ██║ ╚═╝ ██║╚██████╔╝██████╔╝███████╗ ██║ | |
# ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ | |
main_mode1() { | |
local graph= | |
print "${c[Q]}OCP ${arch}v$releaseMajor release $releaseMinor${c[z]}" | |
# Make sure release exists | |
validate_releasetxt $release && releasetxtPublished=1 | |
# Check upgrade channel | |
for channel in stable fast candidate; do | |
print --verbose -n "${c[dim]}Querying upgrades_info API: scanning ${arch}releases in $channel-$releaseMajor for $release ...${c[z]} " | |
graph=$(graph_channel_upgrades_info $channel $releaseMajor $releaseMinor) && break | |
done || channel=null | |
channelMsg="${fancyChan[$channel]}" | |
warnNonStable="\n${c[y]}⚠️ WARNING: Release has not been tagged into the stable channel" | |
case $channel in | |
fast) | |
[[ $allowNonStable ]] || rc=${RCs[release_highest_channel_is_fast]} | |
channelMsg+=$warnNonStable ;; | |
candidate|prerelease) | |
[[ $allowNonStable ]] || rc=${RCs[release_highest_channel_is_candidate]} | |
channelMsg+=$warnNonStable ;; | |
null) | |
print "\n${c[R]}🛑 ERROR: Failed to find release in any of the standard upgrade channels!${c[z]}" | |
exit ${RCs[release_in_no_channels]} ;; | |
esac | |
[[ $verbose ]] && jq -Ce --arg vers $release '.nodes[] | select(.version == $vers)' <<<$graph >&2 | |
print "${c[Q]}Highest channel: $channelMsg${c[z]}" | |
echo $channel | |
} | |
# ███╗ ███╗ ██████╗ ██████╗ ███████╗ ██████╗ | |
# ████╗ ████║██╔═══██╗██╔══██╗██╔════╝ ╚════██╗ | |
# ██╔████╔██║██║ ██║██║ ██║█████╗ █████╔╝ | |
# ██║╚██╔╝██║██║ ██║██║ ██║██╔══╝ ██╔═══╝ | |
# ██║ ╚═╝ ██║╚██████╔╝██████╔╝███████╗ ███████╗ | |
# ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ | |
main_mode2() { | |
local graph= | |
print "${c[Q]}OCP ${arch}v$major releases in channel ${fancyChan[$channel]}${c[z]}" | |
print --verbose -n "${c[dim]}Querying upgrades_info API: getting ${arch}releases from $channel-$major ...${c[z]} " | |
graph=$(graph_channel_upgrades_info $channel $major) || exit ${RCs[majorvers_chan_missing]} | |
if [[ $verbose ]]; then | |
jq -C --arg vers ${major} '[ .nodes[].version | select( . | startswith($vers) ) ] | sort_by( . | [splits("[-.]")] | map(tonumber? // .) )' >&2 <<<$graph | |
fi | |
newest=$(jq -rMe --arg vers ${major} '[ .nodes[].version | select( . | startswith($vers) ) ] | sort_by( . | [splits("[-.]")] | map(tonumber? // .) )[-1]' <<<$graph) | |
print "${c[Q]}Newest release: $newest${c[z]}" | |
# Make sure release exists | |
validate_releasetxt $newest && releasetxtPublished=1 | |
if [[ $verbose ]]; then | |
jq -Ce --arg vers $newest '.nodes[] | select(.version == $vers)' >&2 <<<$graph | |
fi | |
echo "$newest" | |
} | |
# ███╗ ███╗ ██████╗ ██████╗ ███████╗ ██████╗ | |
# ████╗ ████║██╔═══██╗██╔══██╗██╔════╝ ╚════██╗ | |
# ██╔████╔██║██║ ██║██║ ██║█████╗ █████╔╝ | |
# ██║╚██╔╝██║██║ ██║██║ ██║██╔══╝ ╚═══██╗ | |
# ██║ ╚═╝ ██║╚██████╔╝██████╔╝███████╗ ██████╔╝ | |
# ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ | |
main_mode3() { | |
local channels= chan= done=0 graph= out= sortedOnlyMajor= newest= num= s allowedTxt gotgood= | |
if [[ $channel ]]; then | |
channels=$channel | |
else | |
# Only check eus in 4.6+ | |
[[ ${major#*.} -ge 6 ]] && channels="eus stable fast candidate" || channels="stable fast candidate" | |
fi | |
for chan in $channels; do | |
# Print a separator if we've already done some channels | |
(( done > 0 )) && print "${c[dim]}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c[z]}\n" | |
# Increment number of channels we've done | |
(( done++ )) | |
print --verbose -n "${c[dim]}Querying upgrades_info API: getting ${arch}releases from $chan-$major ...${c[z]} " | |
if ! graph=$(graph_channel_upgrades_info $chan $major); then | |
rc=${RCs[majorvers_chan_missing]} | |
[[ $channel ]] && { print "\n${c[c]}Try omitting -c/--chan or selecting a different major release${c[z]}"; exit $rc; } | |
[[ $chan == $channels ]] && { print "\n${c[y]}Try selecting a different major release${c[z]}"; exit $rc; } | |
continue | |
fi | |
# Got the initial logic from https://openshift.tips/upgrades/ | |
out=$(jq -M --arg vers $release '. as $graph | $graph.nodes | map(.version == $vers) | index(true) as $orig | $graph.edges | map(select(.[0] == $orig)[1]) | map($graph.nodes[.])' <<<$graph) | |
# Now let's sort by version number ascending and prune the ones that aren't the right major version | |
if sortedOnlyMajor=$(jq -Me --arg major $major 'sort_by( .version | [splits("[-.]")] | map(tonumber? // .) )[] | select( .version | startswith($major) )' <<<$out); then | |
gotgood=1 | |
num=$(jq -s 'length' <<<$sortedOnlyMajor) | |
(( num == 1 )) && s= || s=s | |
print "${c[gdim]}✔ API lists $num allowed upgrade$s for v$release in ${arch}$chan-$major${c[z]}" | |
print "${c[Q]}OCP ${arch}v${releaseMajor} release $releaseMinor valid upgrade paths in $major channel ${fancyChan[$chan]}${c[z]}" | |
if [[ $verbose ]]; then | |
jq -Cs <<<$sortedOnlyMajor >&2 | |
fi | |
jq -Mr '.version' <<<$sortedOnlyMajor | |
else | |
print "${c[ydim]}✘ API lists 0 allowed upgrades for v$release in ${arch}$chan-$major${c[z]}" | |
rc=${RCs[release_has_no_upgrades]} | |
[[ $channel ]] && { print "\n${c[r]}🛑 ERROR: Failed to find an upgrade path for release in requested channel!${c[z]}\n ${c[c]}Try omitting -c/--chan or selecting a different major release${c[z]}"; exit $rc; } | |
[[ $chan == $channels ]] && { print "\n${c[r]}🛑 ERROR: Failed to find an upgrade path for release in any of the standard channels!${c[z]}\n ${c[c]}Try selecting a different major release${c[z]}"; exit $rc; } | |
continue | |
fi | |
done | |
[[ $gotgood ]] && rc=0 | |
} | |
# ██████╗ ███████╗ ██████╗ ██╗ ██╗██╗██████╗ ███████╗███████╗ | |
# ██╔══██╗██╔════╝██╔═══██╗██║ ██║██║██╔══██╗██╔════╝██╔════╝ | |
# ██████╔╝█████╗ ██║ ██║██║ ██║██║██████╔╝█████╗ ███████╗ | |
# ██╔══██╗██╔══╝ ██║▄▄ ██║██║ ██║██║██╔══██╗██╔══╝ ╚════██║ | |
# ██║ ██║███████╗╚██████╔╝╚██████╔╝██║██║ ██║███████╗███████║ | |
# ╚═╝ ╚═╝╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ | |
[[ $# -eq 0 ]] && halp --short | |
jq_help=$(jq -h) | |
if [[ $? -gt 0 || ! $jq_help =~ "jq - commandline JSON processor" ]]; then | |
print "${c[R]}✘ Unexpected error running 'jq --help' command -- need jq from https://github.com/stedolan/jq/releases${c[z]}" | |
exit 127 | |
fi | |
getopt -T | |
if [[ $? != 4 ]]; then | |
print "${c[R]}✘ Missing GNU getopt command (Linux: usually comes in util-linux; Mac OS X: can install from MacPorts)${c[z]}" | |
exit 127 | |
fi | |
# █████╗ ██████╗ ██████╗ ███████╗ | |
# ██╔══██╗██╔══██╗██╔════╝ ██╔════╝ | |
# ███████║██████╔╝██║ ███╗███████╗ | |
# ██╔══██║██╔══██╗██║ ██║╚════██║ | |
# ██║ ██║██║ ██║╚██████╔╝███████║ | |
# ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝ | |
# Use getopt to validate args while allowing combined short-opts and long-opts with equals sign | |
getopt="getopt --name=$me -o $opts_short -l $opts_long" | |
if ! errs=$($getopt -- "$@" 2>&1 >/dev/null); then | |
errs=${errs//$me/✘ ERROR} | |
errs=${errs//invalid option -- \'/unrecognized option \'-} | |
if command -v sed >/dev/null; then | |
errs=$(sed -r "s|(option) (requires an argument) -- '(.)'|\1 '-\3' \2|" <<<${errs}) | |
else | |
errs=${errs//option requires an argument -- \'/option requires an argument: \'-} | |
fi | |
halp "$errs" | |
fi | |
args=$($getopt -- "$@") | |
eval set -- "$args" | |
until [[ $1 == -- ]]; do | |
case $1 in | |
-h) halp --mid | |
;; | |
--help) halp --long | |
;; | |
-i|--ignore-releasetxt) ignoreMissingReleaseTxt=1 | |
;; | |
-e|--allow-non-stable) allowNonStable=1 | |
;; | |
-c|--chan) shift; channel=$1 | |
[[ $channel == cand ]] && channel=candidate | |
[[ $channel =~ ^(eus|stable|fast|candidate|prerelease)$ ]] || halp "✘ ERROR: invalid CHANNEL argument supplied for '-c'" | |
;; | |
-a|--arch) shift; arch=$1 | |
;; | |
-v|--verbose) verbose=1 | |
;; | |
-u|--list-updates) listUpdates=1 | |
;; | |
-m|--desired-major) shift; desiredMajor=$1 | |
;; | |
*) halp "✘ ERROR: invalid option '$1'" | |
esac | |
shift | |
done | |
shift | |
[[ $# == 0 ]] && halp "✘ ERROR: required argument (X.Y.Z or X.Y) is missing" | |
# Parse remaining non-option args | |
while [[ $# -gt 0 ]]; do | |
# $1 is X.Y.Z | |
if [[ $1 =~ ^4\.[0-9]+\.[0-9]+([-.].+)?$ ]]; then | |
[[ $release ]] && halp "✘ ERROR: cannot specify X.Y.Z more than once" | |
releaseMajor=$(cut -d. -f1-2 <<<$1) | |
releaseMinor=$(cut -d. -f3- <<<$1) | |
release=$1 | |
# $1 is X.Y | |
elif [[ $1 =~ ^4\.[0-9]+$ ]]; then | |
[[ $major ]] && halp "✘ ERROR: cannot specify X.Y more than once" | |
major=$1 | |
else | |
halp "✘ ERROR: invalid args ('$1') -- expecting nothing more than X.Y.Z and/or X.Y" | |
fi | |
shift | |
continue | |
done | |
if [[ $major ]]; then | |
[[ $release ]] && opMode=3 || opMode=2 | |
else | |
opMode=1 | |
fi | |
[[ $channel && $opMode == 1 ]] && halp "✘ ERROR: improper use of -c/--chan option; only has meaning with X.Y" | |
[[ $allowNonStable && $opMode != 1 ]] && halp "✘ ERROR: improper use of -e/--allow-non-stable option with X.Y; only has meaning in X.Y.Z mode" | |
# Now set a default for channel in mode1 & mode2 | |
[[ $opMode != 3 && -z $channel ]] && channel=stable | |
# Clear arch if it's set to amd64; otherwise add a space | |
if [[ $arch == amd64 ]]; then | |
arch= | |
elif [[ $arch ]]; then | |
arch+=" " | |
fi | |
# ███╗ ███╗ █████╗ ██╗███╗ ██╗ | |
# ████╗ ████║██╔══██╗██║████╗ ██║ | |
# ██╔████╔██║███████║██║██╔██╗ ██║ | |
# ██║╚██╔╝██║██╔══██║██║██║╚██╗██║ | |
# ██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ | |
# ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ | |
case $opMode in | |
1) main_mode1 ;; | |
2) main_mode2 ;; | |
# Don't care about releasetxt in mode3 | |
3) main_mode3; releasetxtPublished=1 ;; | |
esac | |
# If release.txt is missing and -i/--ignore-releasetxt wasn't used... | |
[[ $releasetxtPublished ]] || exit ${RCs[release_has_no_releasetxt]} | |
# Otherwise, we just exit with $rc which was set above | |
exit $rc |
Pushed a minor cosmetic update that has no functional changes -- I only changed the usage/help pages to (hopefully) improve clarity, replacing all instances of OCP_VERSION
with simply 4.x.y
and replacing all instances of OCP_MAJOR_VERSION
with 4.x
. It's an improvement.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Random update: I just started running this on a Fedora35 system which had a
jq
rpm installed, so I didn't need to download jq manually like I have in the past (from github.com/stedolan/jq/releases). However, I was horrified to find that jq's sorting wasn't working correctly. See:That's with:
So I went and grabbed the binary direct from the latest (Nov 01, 2018) release on github and put that in my
~/bin/
and now all is well with jq's sorting:So let this serve as a warning against using distro-provided packages. Sigh.