#!/usr/bin/env bash USAGE="Publish a new release of a tokio crate USAGE: $(basename "$0") [OPTIONS] [CRATE] [VERSION] OPTIONS: -v, --verbose Use verbose Cargo output -d, --dry-run Perform a dry run (do not publish or tag the release) -h, --help Show this help text and exit" set -euo pipefail cd "$(dirname "$0")"/.. DRY_RUN="" VERBOSE="" err() { echo -e "\e[31m\e[1merror:\e[0m" "$@" 1>&2; } status() { WIDTH=12 printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2" } verify() { status "Verifying" "if $CRATE v$VERSION can be released" ACTUAL=$(cargo pkgid | sed -n 's/.*#\(.*\)/\1/p') if [ "$ACTUAL" != "$VERSION" ]; then err "expected to release version $VERSION, but Cargo.toml contained $ACTUAL" exit 1 fi if ! cargo --list | grep -q "hack"; then err "missing cargo-hack executable" read -r -p "install it? [Y/n] " INPUT case "$INPUT" in [yY][eE][sS]|[yY]) status "Installing" "cargo-hack" cargo install cargo-hack ;; [nN][oO]|[nN]) echo "okay, exiting" exit 1 ;; *) err "invalid input $INPUT" exit 1 ;; esac fi status "Checking" "if $CRATE builds across feature combinations" CARGO_HACK=(cargo hack check --feature-powerset --no-dev-deps) if [[ "$VERBOSE" ]]; then CARGO_HACK+=("$VERBOSE") fi case "$CRATE" in tracing-subscriber) # for tracing-subscriber, don't test a complete powerset because # there are lots of feature flags INCLUDE_FEATURES=(fmt ansi json registry env-filter) "${CARGO_HACK[@]}" --include-features "${INCLUDE_FEATURES[*]}" CARGO_HACK_STATUS="$?" ;; tracing) # checking the full feature powerset for `tracing` will take # *forever* because of the `max_level_XXX` and # `release_max_level_XXX` features EXCLUDE_FEATURES=( max_level_off max_level_error max_level_warn max_level_info max_level_debug max_level_trace release_max_level_off release_max_level_error release_max_level_warn release_max_level_info release_max_level_debug release_max_level_trace ) "${CARGO_HACK[@]}" --exclude-features "${EXCLUDE_FEATURES[*]}" CARGO_HACK_STATUS="$?" ;; *) "${CARGO_HACK[@]}" CARGO_HACK_STATUS="$?" ;; esac if [[ "$CARGO_HACK_STATUS" != "0" ]] ; then err "$CRATE did not build with all feature combinations (cargo hack exited with $CARGO_HACK_STATUS)!" exit 1 fi if git tag -l | grep -Fxq "$TAG" ; then err "git tag \`$TAG\` already exists" exit 1 fi } release() { status "Releasing" "$CRATE v$VERSION" local CARGO_PACKAGE=(cargo package) local CARGO_PUBLISH=(cargo publish) if [[ "$VERBOSE" ]]; then CARGO_PACKAGE+=("$VERBOSE") CARGO_PUBLISH+=("$VERBOSE") fi if [[ "$DRY_RUN" ]]; then CARGO_PUBLISH+=("$DRY_RUN") fi "${CARGO_PACKAGE[@]}" "${CARGO_PUBLISH[@]}" status "Tagging" "$TAG" if [[ "$DRY_RUN" ]]; then echo "# git tag $TAG && git push --tags" else git tag "$TAG" && git push --tags fi } while [[ $# -gt 0 ]] do case "$1" in -v|--verbose) VERBOSE="--verbose" set -x shift ;; -d|--dry-run) DRY_RUN="--dry-run" shift ;; -*) err "unknown flag \"$1\"" echo "$USAGE" exit 1 ;; *) # crate or version if [[ -z "${CRATE+crate}" ]]; then CRATE="$1" elif [[ -z "${VERSION+version}" ]]; then VERSION="$1" else err "unknown positional argument \"$1\"" echo "$USAGE" exit 1 fi shift ;; esac done # set -- "${POSITIONAL[@]}" if [[ -z "${VERSION+version}" ]]; then err "no version specified!" HELP=1 fi if [[ "${CRATE+crate}" ]]; then TAG="$CRATE-$VERSION" else err "no crate specified!" HELP=1 fi if [[ "${HELP+help}" ]]; then echo "$USAGE" exit 1 fi if [ -d "$CRATE" ]; then (cd "$CRATE" && verify && release ) else err "no such crate \"$CRATE\"" exit 1 fi