Last active
June 9, 2019 06:25
-
-
Save passcod/e438c1ee035abe7b3413e26880ea47d1 to your computer and use it in GitHub Desktop.
A tool to find out what the earliest version that your project supports is!
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
#!/usr/bin/env bash | |
# With revisions from @parasyte for macOS compatibility | |
# Requires bash >= 5 (mapfile) | |
if [[ "$1" == "--keep-versions" ]]; then | |
keep_versions="1" | |
fi | |
purple="\e[0;35m" | |
reset="\e[0m" | |
releases_url="https://raw.githubusercontent.com/rust-lang/rust/master/RELEASES.md" | |
mapfile -t versions < <(curl -s "$releases_url" \ | |
| grep -v alpha \ | |
| egrep -o '^Version [1-9][0-9.]+' \ | |
| cut -d\ -f2) | |
# Before that, we have to use `cargo build` | |
checkindex=$(echo ${versions[@]/1.15.0//} | cut -d/ -f1 | wc -w | tr -d ' ') | |
n_versions=${#versions[@]} | |
echo -e "${purple}We know of ${reset}${n_versions}${purple} stable Rust versions." | |
mapfile -t priors < <(rustup toolchain list \ | |
| egrep -o '^[0-9.]+') | |
echo -e "There are ${reset}${#priors[@]}${purple} past versions explicitly installed here." | |
current=$(rustc --version | cut -d\ -f2) | |
curindex=$(echo ${versions[@]/$current//} | cut -d/ -f1 | wc -w | tr -d ' ') | |
if [[ $curindex == 0 ]]; then | |
echo -e "You are on the latest stable: ${reset}${current}${purple}." | |
elif [[ $curindex == $n_versions ]]; then | |
echo -e "You are not currently on a stable: ${reset}${current}${purple}" \ | |
"(latest is ${reset}${versions[0]}${purple})." | |
else | |
echo -e "You are not on the latest stable: ${reset}${current}${purple}" \ | |
"(latest is ${reset}${versions[0]}${purple})." | |
fi | |
# Exaggerated (+2) from theoretical given observations | |
est_steps=$(echo "l($n_versions - 1)/l(2) + 3" | bc -l | cut -d. -f1) | |
echo | |
echo "Now going to binary search over Rust versions to find the earliest one" \ | |
"which compiles your project. We do this by repeatedly installing and checking" \ | |
$(if [[ -z "$keep_versions" ]]; then | |
echo -e "then uninstalling Rust versions through rustup. Versions that are" \ | |
"already installed will not be removed (at the end, your system should" \ | |
"look the same). If you want to keep the versions this script downloads" \ | |
"instead, quit and run again with the ${reset}--keep-versions${purple} flag." | |
else | |
echo -e "Rust versions through rustup. As you've passed the" \ | |
"${reset}--keep-versions${purple} flag, versions this script downloads will" \ | |
"be kept instead of being cleaned up immediately." | |
fi) \ | |
| fold -s | |
echo | |
echo "You'll want a fast, unmetered internet connection. If you're running Windows," \ | |
"it will take forever due to https://github.com/rust-lang/rustup.rs/issues/1540." \ | |
| fold -s | |
if [[ -z "$keep_versions" ]]; then | |
echo | |
echo "To pause on Linux/macOS, prefer Ctrl-Z / fg." \ | |
| fold -s | |
fi | |
echo | |
echo -ne "If you still want to proceed (estimated ${reset}${est_steps}${purple} iterations)," \ | |
"press enter now. Otherwise, abort with Ctrl-C.\n" \ | |
"\n${reset}=> " \ | |
| fold -s | |
read | |
echo -e "${purple}" | |
# absolute smallest | |
absmin="$((n_versions - 1))" | |
# smallest unsupported | |
trymin="$absmin" | |
# smallest supported | |
trymax="0" | |
# what we're checking now | |
target="$trymin" | |
# default variable values, needed for cleanup | |
a_priori= | |
target_v="${versions[$target]}" | |
function cleanup { | |
if [[ -z "$a_priori" ]] && [[ -z "$keep_versions" ]]; then | |
echo -e "${reset}" | |
rustup uninstall "$target_v" | |
echo -e "${purple}" | |
fi | |
} | |
# remove the last installed version when the script is interrupted | |
trap 'cleanup ; echo -e "${reset}" ; exit 1' SIGINT | |
while true; do | |
#echo "target: $target trymin: $trymin trymax: $trymax" | |
# the above are indexes, this is the version string | |
target_v="${versions[$target]}" | |
echo -ne "Going to check against version ${reset}${target_v}${purple}" | |
a_priori= | |
if [[ " ${priors[@]} " =~ " ${target_v} " ]]; then | |
a_priori=1 | |
echo -e ", which already exists locally.${reset}" | |
else | |
echo -e ".${reset}" | |
rustup install "$target_v" | |
fi | |
echo | |
if if [[ $target > $checkindex ]]; then | |
rustup run "$target_v" cargo build | |
else | |
rustup run "$target_v" cargo check | |
fi; then | |
echo -e "\n${purple}Version ${reset}${target_v}${purple} is supported!" | |
trymax="$target" | |
target=$(((trymin - trymax) / 2 + trymax)) | |
if [[ $trymax == $target ]] || [[ $target_v == "1.0.0" ]]; then | |
echo "That's the earliest! We're done!" | |
cleanup | |
break | |
fi | |
else | |
echo -e "\n${purple}Version ${reset}${target_v}${purple} is not supported! See above for why." | |
trymin="$target" | |
target=$(((trymin - trymax) / 2 + trymax)) | |
fi | |
cleanup | |
done | |
echo -e "${reset}" |
Here's a patch to get this script working on macOS. (NOTE: I updated bash to version 5 to get mapfile
, but it could also be replaced with a read loop.)
--- rustup-bisect.txt 2019-02-12 14:27:47.000000000 -0800
+++ rustup-bisect 2019-02-12 14:31:22.000000000 -0800
@@ -11,7 +11,7 @@ releases_url="https://raw.githubusercont
mapfile -t versions < <(curl -s "$releases_url" \
| grep -v alpha \
- | grep -oP '^Version [1-9][\d.]+' \
+ | egrep -o '^Version [1-9][0-9.]+' \
| cut -d\ -f2)
# Before that, we have to use `cargo build`
@@ -21,7 +21,7 @@ n_versions=${#versions[@]}
echo -e "${purple}We know of ${reset}${n_versions}${purple} stable Rust versions."
mapfile -t priors < <(rustup toolchain list \
- | grep -oP '^[\d.]+')
+ | egrep -o '^[\d.]+')
echo -e "There are ${reset}${#priors[@]}${purple} past versions explicitly installed here."
@@ -129,3 +129,5 @@ while true; do
echo -e "${purple}"
fi
done
+
+echo -e "${reset}"
@parasyte I merged those in, thank you!
@passcod The latest change missed one of the required fixes to the regular expression for matching versions. egrep
doesn't support \d
inside character ranges. But you can still use 0-9
.
I also decided to fix the toolchain uninstall when the script is interrupted. These changes are in my fork of the gist, making it a bit easier to contribute. :)
Thank you!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Origin and demo: https://twitter.com/passcod/status/1088728138509479936