diff --git a/lock-files.sh b/lock-files.sh new file mode 100755 index 0000000..5930a3f --- /dev/null +++ b/lock-files.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# +# Create and manipulate the lock files. +# +# We use lock files `Cargo-recent.lock` and `Cargo-minimal.lock` to pin +# dependencies and check specific versions in CI. +# +# Shellcheck can't search dynamic paths +# shellcheck source=/dev/null + +set -u + +main() { + set_globals + assert_cmds + handle_command_line_args "$@" +} + +set_globals() { + # The directory of this script. + script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + + # Used below by `say`. + script=${0##*/} + + # Used below by `verbose_say`. + flag_verbose=false + + # Sourcing this file requires `flag_verbose` to be set. + . "$script_dir/stdlib.sh" + + # Environment sanity checks + assert_nz "$HOME" "\$HOME is undefined" + assert_nz "$0" "\$0 is undefined" + + # Make all cargo invocations verbose. + export CARGO_TERM_VERBOSE=true + + recent="Cargo-recent.lock" + minimal="Cargo-minimal.lock" + + msrv="1.63.0" +} + +handle_command_line_args() { + local _no_args=false + local _help=false + local _create=false + local _msrv="" + local _update=false + + if [ $# -eq 0 ]; then + _no_args=true + fi + + local _arg + for _arg in "$@"; do + case "${_arg%%=*}" in + create ) + _create=true + ;; + + update ) + _update=true + ;; + + --msrv ) + if is_value_arg "$_arg" "--msrv"; then + _msrv="$(get_value_arg "$_arg")" + else + say_err "the --msrv option requires a toolchain version argument" + print_help + exit 1 + fi + + ;; + + -h | --help ) + _help=true + ;; + + --verbose) + # verbose is a global flag + flag_verbose=true + ;; + + *) + echo "Unknown argument '$_arg', displaying usage:" + echo "${_arg%%=*}" + _help=true + ;; + + esac + + done + + if [ "$_create" = true ]; then + if [ -z "$_msrv" ]; then + msrv="$_msrv" + fi + + verbose_say "Creating lock files, MSRV: $msrv" + create + fi + + if [ "$_update" = true ]; then + update + verbose_say "Your git index will now be dirty if lock file update is required" + fi + + if [ "$_help" = true ]; then + print_help + exit 0 + fi + + if [ "$_no_args" = true ]; then + verbose_say "no option supplied, defaulting to update" + update + fi +} + +is_value_arg() { + local _arg="$1" + local _name="$2" + + echo "$_arg" | grep -q -- "$_name=" + return $? +} + +get_value_arg() { + local _arg="$1" + + echo "$_arg" | cut -f2 -d= +} + +# Creates the minimal and recent lock files. +# +# If this function fails you may want to be lazy and just duplicate `Cargo.lock` +# as the minimal and recent lock files. +create() { + # Attempt to create a minimal lock file. + rm --force Cargo.lock > /dev/null + cargo +nightly check --all-features -Z minimal-versions + need_ok "failed to build with -Z minimial-versions, you might have to use a recent lock file for minimal" + + # We only want to create the minimal lock file if we can build with current MSRV. + cargo "+$msrv" --locked check --all-features + need_ok "failed to build with minimal lock file and MSRV $_msrv" + cp Cargo.lock "$minimal" + + # If that worked we can create a recent lock file. + cargo update + cp Cargo.lock "$recent" +} + +# Updates the minimal and recent lock files. +update() { + for file in "$minimal" "$recent"; do + cp --force "$file" Cargo.lock + cargo check + cp --force Cargo.lock "$file" + done +} + +assert_cmds() { + need_cmd cat + need_cmd cp + need_cmd cargo +} + +say() { + echo "$script: $1" +} + +say_err() { + say "$1" >&2 +} + +verbose_say() { + if [ "$flag_verbose" = true ]; then + say "$1" + fi +} + +print_help() { + cat <&2 + exit 1 +} + +need_cmd() { + if ! command -v "$1" > /dev/null 2>&1 + then err "need '$1' (command not found)" + fi +} + +need_ok() { + if [ $? != 0 ]; then err "$1"; fi +} + +assert_nz() { + if [ -z "$1" ]; then err "assert_nz $2"; fi +} + +# Run a command that should never fail. If the command fails execution +# will immediately terminate with an error showing the failing +# command. +ensure() { + "$@" + need_ok "command failed: $*" +} + +# This is just for indicating that commands' results are being +# intentionally ignored. Usually, because it's being executed +# as part of error handling. +ignore() { + run "$@" +} + +# Assert that we have a nightly Rust toolchain installed. +need_nightly() { + cargo_ver=$(cargo --version) + if echo "$cargo_ver" | grep -q -v nightly; then + err "Need a nightly compiler; have $(cargo --version) (use RUSTUP_TOOLCHAIN=+nightly cmd)" + fi +} diff --git a/template.sh b/template.sh new file mode 100755 index 0000000..e70814c --- /dev/null +++ b/template.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +# +# Bash shell script template. +# +# Shellcheck can't search dynamic paths +# shellcheck source=/dev/null + +# Note we don't use `set -x` because error handling is done manually. +# If you use pipes you may want to use `set -euxo pipefail` instead. +set -u + +main() { + set_globals + assert_cmds + handle_command_line_args "$@" +} + +set_globals() { + # The git repository where script is run from. + # repo_dir=$(git rev-parse --show-toplevel) + + # The directory of this script. + script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + + # Used below by `say`. + script=${0##*/} + + # Used below by `verbose_say`. + flag_verbose=false + + # Sourcing this file requires `flag_verbose` to be set. + . "$script_dir/stdlib.sh" + + # Environment sanity checks + assert_nz "$HOME" "\$HOME is undefined" + assert_nz "$0" "\$0 is undefined" + + # Make all cargo invocations verbose. + export CARGO_TERM_VERBOSE=true +} + +handle_command_line_args() { + local _help=false + + local _arg + for _arg in "$@"; do + case "${_arg%%=*}" in + -h | --help ) + _help=true + ;; + + --verbose) + # verbose is a global flag + flag_verbose=true + ;; + + *) + echo "Unknown argument '$_arg', displaying usage:" + echo "${_arg%%=*}" + _help=true + ;; + + esac + + done + + if [ "$_help" = true ]; then + print_help + exit 0 + fi + + verbose_say "Enabled verbose output" +} + +assert_cmds() { + need_cmd cat +} + +say() { + echo "$script: $1" +} + +say_err() { + say "$1" >&2 +} + +verbose_say() { + if [ "$flag_verbose" = true ]; then + say "$1" + fi +} + +print_help() { + cat <