123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- #!/usr/bin/env bash
- SCRIPT_NAME=$(basename "$0")
- function usage()
- {
- if [ "$TOR_PUSH_SAME" ]; then
- CURRENT_PUSH_SAME="push"
- else
- CURRENT_PUSH_SAME="skip"
- fi
- echo "$SCRIPT_NAME [-h] [-r <remote-name> [-t <test-branch-prefix>]] [-s]"
- # The next line looks misaligned, but it lines up in the output
- echo " [-- [-n] [--no-atomic] <git push options>]"
- echo
- echo " arguments:"
- echo " -h: show this help text"
- echo " -n: dry run mode"
- echo " (default: run commands)"
- echo " -r: push to remote-name, rather than the default upstream remote."
- echo " (default: $DEFAULT_UPSTREAM_REMOTE, current: $UPSTREAM_REMOTE)"
- echo " -t: test branch mode: push test branches to remote-name. Pushes"
- echo " branches prefix_035, prefix_040, ... , prefix_master."
- echo " (default: push maint-*, release-*, and master)"
- echo " -s: push branches whose tips match upstream maint, release, or"
- echo " master branches. The default is to skip these branches,"
- echo " because they do not contain any new code. Use -s to test for"
- echo " CI environment failures, using code that previously passed CI."
- echo " (default: skip; current: $CURRENT_PUSH_SAME matching branches)"
- echo " --: pass further arguments to git push."
- echo " All unrecognised arguments are passed to git push, but complex"
- echo " arguments before -- may be mangled by getopt."
- echo " (default: git push --atomic, current: $GIT_PUSH)"
- echo
- echo " env vars:"
- echo " optional:"
- echo " TOR_GIT_PUSH_PATH: change to this directory before pushing."
- echo " (default: if \$TOR_FULL_GIT_PATH is set,"
- echo " use \$TOR_FULL_GIT_PATH/\$TOR_MASTER;"
- echo " Otherwise, use the current directory for pushes;"
- echo " current: $TOR_GIT_PUSH_PATH)"
- echo " TOR_FULL_GIT_PATH: where the git repository directories reside."
- echo " We recommend using \$HOME/git/."
- echo " (default: use the current directory for pushes;"
- echo " current: $TOR_FULL_GIT_PATH)"
- echo " TOR_MASTER: the name of the directory containing the tor.git clone"
- echo " The tor master git directory is \$GIT_PATH/\$TOR_MASTER"
- echo " (default: tor; current: $TOR_MASTER_NAME)"
- echo
- echo " TOR_UPSTREAM_REMOTE_NAME: the default upstream remote."
- echo " Overridden by -r."
- echo " (default: upstream; current: $UPSTREAM_REMOTE)"
- echo " TOR_GIT_PUSH: the git push command and default arguments."
- echo " Overridden by <git push options> after --."
- echo " (default: git push --atomic; current: $GIT_PUSH)"
- echo " TOR_PUSH_SAME: push branches whose tips match upstream maint,"
- echo " release, or master branches. Inverted by -s."
- echo " (default: skip; current: $CURRENT_PUSH_SAME matching branches)"
- echo " TOR_PUSH_DELAY: pushes the master and maint branches separately,"
- echo " so that CI runs in a sensible order."
- echo " (default: push all branches immediately; current: $PUSH_DELAY)"
- echo " we recommend that you set these env vars in your ~/.profile"
- }
- set -e
- #################
- # Configuration #
- #################
- # Don't change this configuration - set the env vars in your .profile
- #
- # The tor master git repository directory from which all the worktree have
- # been created.
- TOR_MASTER_NAME=${TOR_MASTER_NAME:-"tor"}
- # Which directory do we push from?
- if [ "$TOR_FULL_GIT_PATH" ]; then
- TOR_GIT_PUSH_PATH=${TOR_GIT_PUSH_PATH:-"$TOR_FULL_GIT_PATH/$TOR_MASTER_NAME"}
- fi
- # git push command and default arguments
- GIT_PUSH=${TOR_GIT_PUSH:-"git push --atomic"}
- # The upstream remote which git.torproject.org/tor.git points to.
- DEFAULT_UPSTREAM_REMOTE=${TOR_UPSTREAM_REMOTE_NAME:-"upstream"}
- # Push to a different upstream remote using -r <remote-name>
- UPSTREAM_REMOTE=${DEFAULT_UPSTREAM_REMOTE}
- # Add a delay between pushes, so CI runs on the most important branches first
- PUSH_DELAY=${TOR_PUSH_DELAY:-0}
- # Push (1) or skip (0) test branches that are the same as an upstream
- # maint/master branch. Push if you are testing that the CI environment still
- # works on old code, skip if you are testing new code in the branch.
- # Default: skip unchanged branches.
- # Inverted by the -s option.
- PUSH_SAME=${TOR_PUSH_SAME:-0}
- #######################
- # Argument processing #
- #######################
- # Controlled by the -t <test-branch-prefix> option. The test branch prefix
- # option makes git-merge-forward.sh create new test branches:
- # <tbp>_035, <tbp>_040, ... , <tbp>_master, and merge each branch forward into
- # the next one.
- TEST_BRANCH_PREFIX=
- while getopts ":hr:st:" opt; do
- case "$opt" in
- h) usage
- exit 0
- ;;
- r) UPSTREAM_REMOTE="$OPTARG"
- echo " *** PUSHING TO REMOTE: ${UPSTREAM_REMOTE} ***"
- shift
- shift
- OPTIND=$((OPTIND - 2))
- ;;
- s) PUSH_SAME=$((! PUSH_SAME))
- if [ "$PUSH_SAME" -eq 0 ]; then
- echo " *** SKIPPING UNCHANGED TEST BRANCHES ***"
- else
- echo " *** PUSHING UNCHANGED TEST BRANCHES ***"
- fi
- shift
- OPTIND=$((OPTIND - 1))
- ;;
- t) TEST_BRANCH_PREFIX="$OPTARG"
- echo " *** PUSHING TEST BRANCHES: ${TEST_BRANCH_PREFIX}_nnn ***"
- shift
- shift
- OPTIND=$((OPTIND - 2))
- ;;
- *)
- # Make git push handle the option
- # This might mangle options with spaces, use -- for complex options
- GIT_PUSH="$GIT_PUSH $1"
- shift
- OPTIND=$((OPTIND - 1))
- ;;
- esac
- done
- # getopts doesn't allow "-" as an option character,
- # so we have to handle -- manually
- if [ "$1" = "--" ]; then
- shift
- fi
- if [ "$TEST_BRANCH_PREFIX" ]; then
- if [ "$UPSTREAM_REMOTE" = "$DEFAULT_UPSTREAM_REMOTE" ]; then
- echo "Pushing test branches ${TEST_BRANCH_PREFIX}_nnn to " \
- "the default remote $DEFAULT_UPSTREAM_REMOTE is not allowed."
- echo
- usage
- exit 1
- fi
- fi
- if [ "$TOR_GIT_PUSH_PATH" ]; then
- echo "Changing to $TOR_GIT_PUSH_PATH before pushing"
- cd "$TOR_GIT_PUSH_PATH"
- else
- echo "Pushing from the current directory"
- fi
- echo "Calling $GIT_PUSH" "$@" "<branches>"
- ################################
- # Git upstream remote branches #
- ################################
- set -e
- DEFAULT_UPSTREAM_BRANCHES=
- if [ "$DEFAULT_UPSTREAM_REMOTE" != "$UPSTREAM_REMOTE" ]; then
- for br in $(git-list-tor-branches.sh -l); do
- DEFAULT_UPSTREAM_BRANCHES="${DEFAULT_UPSTREAM_BRANCHES} ${DEFAULT_UPSTREAM_REMOTE}/${br}"
- done
- fi
- UPSTREAM_BRANCHES=
- for br in $(git-list-tor-branches.sh -l); do
- UPSTREAM_BRANCHES="${UPSTREAM_BRANCHES} ${UPSTREAM_REMOTE}/${br}"
- done
- ########################
- # Git branches to push #
- ########################
- if [ -z "$TEST_BRANCH_PREFIX" ]; then
- # maint/release push mode: push all branches.
- #
- # List of branches to push. Ordering is not important.
- PUSH_BRANCHES="$(git-list-tor-branches.sh -l)"
- else
- # Test branch push mode: push test branches, based on each maint branch.
- #
- # List of branches to push. Ordering is not important.
- PUSH_BRANCHES=""
- for suffix in $(git-list-tor-branches.sh -s -R); do
- PUSH_BRANCHES="${PUSH_BRANCHES} ${TEST_BRANCH_PREFIX}${suffix}"
- done
- fi
- set +e
- ###############
- # Entry point #
- ###############
- if [ "$TEST_BRANCH_PREFIX" ]; then
- # Skip the test branches that are the same as the default or current
- # upstream branches (they have already been tested)
- UPSTREAM_SKIP_SAME_AS="$UPSTREAM_BRANCHES $DEFAULT_UPSTREAM_BRANCHES"
- else
- # Skip the local maint-*, release-*, master branches that are the same as the
- # current upstream branches, but ignore the default upstream
- # (we want to update a non-default remote, even if it matches the default)
- UPSTREAM_SKIP_SAME_AS="$UPSTREAM_BRANCHES"
- fi
- # Skip branches that match the relevant upstream(s)
- if [ "$PUSH_SAME" -eq 0 ]; then
- NEW_PUSH_BRANCHES=
- for b in $PUSH_BRANCHES; do
- PUSH_COMMIT=$(git rev-parse "$b")
- SKIP_UPSTREAM=
- for u in $UPSTREAM_SKIP_SAME_AS; do
- # Skip the branch check on error
- UPSTREAM_COMMIT=$(git rev-parse "$u" 2>/dev/null) || continue
- if [ "$PUSH_COMMIT" = "$UPSTREAM_COMMIT" ]; then
- SKIP_UPSTREAM="$u"
- fi
- done
- if [ "$SKIP_UPSTREAM" ]; then
- printf "Skipping unchanged: %s matching remote: %s\\n" \
- "$b" "$SKIP_UPSTREAM"
- else
- if [ "$NEW_PUSH_BRANCHES" ]; then
- NEW_PUSH_BRANCHES="${NEW_PUSH_BRANCHES} ${b}"
- else
- NEW_PUSH_BRANCHES="${b}"
- fi
- fi
- done
- PUSH_BRANCHES=${NEW_PUSH_BRANCHES}
- fi
- if [ ! "$PUSH_BRANCHES" ]; then
- echo "No branches to push!"
- # We expect the rest of the script to run without errors, even if there
- # are no branches
- fi
- if [ "$PUSH_DELAY" -le 0 ]; then
- echo "Pushing $PUSH_BRANCHES"
- # We know that there are no spaces in any branch within $PUSH_BRANCHES, so
- # it is safe to use it unquoted. (This also applies to the other shellcheck
- # exceptions below.)
- #
- # Push all the branches at the same time
- # shellcheck disable=SC2086
- $GIT_PUSH "$@" "$UPSTREAM_REMOTE" $PUSH_BRANCHES
- else
- # Push the branches in optimal CI order, with a delay between each push
- PUSH_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | sort -V)
- MASTER_BRANCH=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep master) \
- || true # Skipped master branch
- if [ -z "$TEST_BRANCH_PREFIX" ]; then
- MAINT_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep maint) \
- || true # Skipped all maint branches
- RELEASE_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep release | \
- tr "\\n" " ") || true # Skipped all release branches
- else
- # Actually test branches based on maint branches
- MAINT_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep -v master) \
- || true # Skipped all maint test branches
- # No release branches
- RELEASE_BRANCHES=
- fi
- if [ "$MASTER_BRANCH" ] || [ "$MAINT_BRANCHES" ] \
- || [ "$RELEASE_BRANCHES" ]; then
- printf "Pushing with %ss delays, so CI runs in this order:\\n" \
- "$PUSH_DELAY"
- if [ "$MASTER_BRANCH" ]; then
- printf "%s\\n" "$MASTER_BRANCH"
- fi
- if [ "$MAINT_BRANCHES" ]; then
- printf "%s\\n" "$MAINT_BRANCHES"
- fi
- if [ "$RELEASE_BRANCHES" ]; then
- printf "%s\\n" "$RELEASE_BRANCHES"
- fi
- fi
- # shellcheck disable=SC2086
- for b in $MASTER_BRANCH $MAINT_BRANCHES; do
- $GIT_PUSH "$@" "$UPSTREAM_REMOTE" "$b"
- # If we are pushing more than one branch, delay.
- # In the unlikely scenario where we are pushing maint without master,
- # or maint without release, there may be an extra delay
- if [ "$MAINT_BRANCHES" ] || [ "$RELEASE_BRANCHES" ]; then
- sleep "$PUSH_DELAY"
- fi
- done
- if [ "$RELEASE_BRANCHES" ]; then
- # shellcheck disable=SC2086
- $GIT_PUSH "$@" "$UPSTREAM_REMOTE" $RELEASE_BRANCHES
- fi
- fi
|