tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

git-push-all.sh (10900B)


      1 #!/usr/bin/env bash
      2 
      3 SCRIPT_NAME=$(basename "$0")
      4 
      5 function usage()
      6 {
      7  if [ "$TOR_PUSH_SAME" ]; then
      8    CURRENT_PUSH_SAME="push"
      9  else
     10    CURRENT_PUSH_SAME="skip"
     11  fi
     12 
     13  echo "$SCRIPT_NAME [-h] [-r <remote-name> [-t <test-branch-prefix>]] [-s]"
     14  # The next line looks misaligned, but it lines up in the output
     15  echo "                [-- [-n] [--no-atomic] <git push options>]"
     16  echo
     17  echo "  arguments:"
     18  echo "   -h: show this help text"
     19  echo "   -n: dry run mode"
     20  echo "       (default: run commands)"
     21  echo "   -r: push to remote-name, rather than the default upstream remote."
     22  echo "       (default: $DEFAULT_UPSTREAM_REMOTE, current: $UPSTREAM_REMOTE)"
     23  echo "   -t: test branch mode: push test branches to remote-name. Pushes"
     24  echo "       branches prefix_035, prefix_040,  ... , prefix_main."
     25  echo "       (default: push maint-*, release-*, and main)"
     26  echo "   -s: push branches whose tips match upstream maint, release, or"
     27  echo "       main branches. The default is to skip these branches,"
     28  echo "       because they do not contain any new code. Use -s to test for"
     29  echo "       CI environment failures, using code that previously passed CI."
     30  echo "       (default: skip; current: $CURRENT_PUSH_SAME matching branches)"
     31  echo "   --: pass further arguments to git push."
     32  echo "       All unrecognised arguments are passed to git push, but complex"
     33  echo "       arguments before -- may be mangled by getopt."
     34  echo "       (default: git push --atomic, current: $GIT_PUSH)"
     35  echo
     36  echo " env vars:"
     37  echo "   optional:"
     38  echo "   TOR_GIT_PUSH_PATH: change to this directory before pushing."
     39  echo "       (default: if \$TOR_FULL_GIT_PATH is set,"
     40  echo "       use \$TOR_FULL_GIT_PATH/\$TOR_MASTER;"
     41  echo "       Otherwise, use the current directory for pushes;"
     42  echo "       current: $TOR_GIT_PUSH_PATH)"
     43  echo "   TOR_FULL_GIT_PATH: where the git repository directories reside."
     44  echo "       We recommend using \$HOME/git/."
     45  echo "       (default: use the current directory for pushes;"
     46  echo "       current: $TOR_FULL_GIT_PATH)"
     47  echo "   TOR_MASTER: the name of the directory containing the tor.git clone"
     48  echo "       The primary tor git directory is \$GIT_PATH/\$TOR_MASTER"
     49  echo "       (default: tor; current: $TOR_MASTER_NAME)"
     50  echo
     51  echo "   TOR_UPSTREAM_REMOTE_NAME: the default upstream remote."
     52  echo "       Overridden by -r."
     53  echo "       (default: upstream; current: $UPSTREAM_REMOTE)"
     54  echo "   TOR_GIT_PUSH: the git push command and default arguments."
     55  echo "       Overridden by <git push options> after --."
     56  echo "       (default: git push --atomic; current: $GIT_PUSH)"
     57  echo "   TOR_PUSH_SAME: push branches whose tips match upstream maint,"
     58  echo "       release, or main branches. Inverted by -s."
     59  echo "       (default: skip; current: $CURRENT_PUSH_SAME matching branches)"
     60  echo "   TOR_PUSH_DELAY: pushes the main and maint branches separately,"
     61  echo "       so that CI runs in a sensible order."
     62  echo "       (default: push all branches immediately; current: $PUSH_DELAY)"
     63  echo "   we recommend that you set these env vars in your ~/.profile"
     64 }
     65 
     66 set -e
     67 
     68 #################
     69 # Configuration #
     70 #################
     71 
     72 # Don't change this configuration - set the env vars in your .profile
     73 #
     74 # The primary tor git repository directory from which all the worktree have
     75 # been created.
     76 TOR_MASTER_NAME=${TOR_MASTER_NAME:-"tor"}
     77 # Which directory do we push from?
     78 if [ "$TOR_FULL_GIT_PATH" ]; then
     79  TOR_GIT_PUSH_PATH=${TOR_GIT_PUSH_PATH:-"$TOR_FULL_GIT_PATH/$TOR_MASTER_NAME"}
     80 fi
     81 # git push command and default arguments
     82 GIT_PUSH=${TOR_GIT_PUSH:-"git push --atomic"}
     83 # The upstream remote which gitlab.torproject.org/tpo/core/tor.git points to.
     84 DEFAULT_UPSTREAM_REMOTE=${TOR_UPSTREAM_REMOTE_NAME:-"upstream"}
     85 # Push to a different upstream remote using -r <remote-name>
     86 UPSTREAM_REMOTE=${DEFAULT_UPSTREAM_REMOTE}
     87 # Add a delay between pushes, so CI runs on the most important branches first
     88 PUSH_DELAY=${TOR_PUSH_DELAY:-0}
     89 # Push (1) or skip (0) test branches that are the same as an upstream
     90 # maint/main branch. Push if you are testing that the CI environment still
     91 # works on old code, skip if you are testing new code in the branch.
     92 # Default: skip unchanged branches.
     93 # Inverted by the -s option.
     94 PUSH_SAME=${TOR_PUSH_SAME:-0}
     95 
     96 #######################
     97 # Argument processing #
     98 #######################
     99 
    100 # Controlled by the -t <test-branch-prefix> option. The test branch prefix
    101 # option makes git-merge-forward.sh create new test branches:
    102 # <tbp>_035, <tbp>_040, ... , <tbp>_main, and merge each branch forward into
    103 # the next one.
    104 TEST_BRANCH_PREFIX=
    105 
    106 while getopts ":hr:st:" opt; do
    107  case "$opt" in
    108    h) usage
    109       exit 0
    110       ;;
    111    r) UPSTREAM_REMOTE="$OPTARG"
    112       echo "    *** PUSHING TO REMOTE: ${UPSTREAM_REMOTE} ***"
    113       shift
    114       shift
    115       OPTIND=$((OPTIND - 2))
    116       ;;
    117    s) PUSH_SAME=$((! PUSH_SAME))
    118       if [ "$PUSH_SAME" -eq 0 ]; then
    119         echo "    *** SKIPPING UNCHANGED TEST BRANCHES ***"
    120       else
    121         echo "    *** PUSHING UNCHANGED TEST BRANCHES ***"
    122       fi
    123       shift
    124       OPTIND=$((OPTIND - 1))
    125       ;;
    126    t) TEST_BRANCH_PREFIX="$OPTARG"
    127       echo "    *** PUSHING TEST BRANCHES: ${TEST_BRANCH_PREFIX}_nnn ***"
    128       shift
    129       shift
    130       OPTIND=$((OPTIND - 2))
    131       ;;
    132    *)
    133       # Make git push handle the option
    134       # This might mangle options with spaces, use -- for complex options
    135       GIT_PUSH="$GIT_PUSH $1"
    136       shift
    137       OPTIND=$((OPTIND - 1))
    138       ;;
    139  esac
    140 done
    141 
    142 # getopts doesn't allow "-" as an option character,
    143 # so we have to handle -- manually
    144 if [ "$1" = "--" ]; then
    145  shift
    146 fi
    147 
    148 if [ "$TEST_BRANCH_PREFIX" ]; then
    149  if [ "$UPSTREAM_REMOTE" = "$DEFAULT_UPSTREAM_REMOTE" ]; then
    150    echo "Pushing test branches ${TEST_BRANCH_PREFIX}_nnn to " \
    151      "the default remote $DEFAULT_UPSTREAM_REMOTE is not allowed."
    152    echo
    153    usage
    154    exit 1
    155  fi
    156 fi
    157 
    158 if [ "$TOR_GIT_PUSH_PATH" ]; then
    159  echo "Changing to $TOR_GIT_PUSH_PATH before pushing"
    160  cd "$TOR_GIT_PUSH_PATH"
    161 else
    162  echo "Pushing from the current directory"
    163 fi
    164 
    165 echo "Calling $GIT_PUSH" "$@" "<branches>"
    166 
    167 ################################
    168 # Git upstream remote branches #
    169 ################################
    170 
    171 set -e
    172 DEFAULT_UPSTREAM_BRANCHES=
    173 if [ "$DEFAULT_UPSTREAM_REMOTE" != "$UPSTREAM_REMOTE" ]; then
    174    for br in $(git-list-tor-branches.sh -l); do
    175        DEFAULT_UPSTREAM_BRANCHES="${DEFAULT_UPSTREAM_BRANCHES} ${DEFAULT_UPSTREAM_REMOTE}/${br}"
    176    done
    177 fi
    178 
    179 UPSTREAM_BRANCHES=
    180 for br in $(git-list-tor-branches.sh -l); do
    181    UPSTREAM_BRANCHES="${UPSTREAM_BRANCHES} ${UPSTREAM_REMOTE}/${br}"
    182 done
    183 
    184 ########################
    185 # Git branches to push #
    186 ########################
    187 
    188 if [ -z "$TEST_BRANCH_PREFIX" ]; then
    189 
    190  # maint/release push mode: push all branches.
    191  #
    192  # List of branches to push. Ordering is not important.
    193  PUSH_BRANCHES="$(git-list-tor-branches.sh -l)"
    194 else
    195 
    196  # Test branch push mode: push test branches, based on each maint branch.
    197  #
    198  # List of branches to push. Ordering is not important.
    199  PUSH_BRANCHES=""
    200  for suffix in $(git-list-tor-branches.sh -s -R); do
    201      PUSH_BRANCHES="${PUSH_BRANCHES} ${TEST_BRANCH_PREFIX}${suffix}"
    202  done
    203 fi
    204 
    205 set +e
    206 
    207 ###############
    208 # Entry point #
    209 ###############
    210 
    211 if [ "$TEST_BRANCH_PREFIX" ]; then
    212  # Skip the test branches that are the same as the default or current
    213  # upstream branches (they have already been tested)
    214  UPSTREAM_SKIP_SAME_AS="$UPSTREAM_BRANCHES $DEFAULT_UPSTREAM_BRANCHES"
    215 else
    216  # Skip the local maint-*, release-*, main branches that are the same as the
    217  # current upstream branches, but ignore the default upstream
    218  # (we want to update a non-default remote, even if it matches the default)
    219  UPSTREAM_SKIP_SAME_AS="$UPSTREAM_BRANCHES"
    220 fi
    221 
    222 # Skip branches that match the relevant upstream(s)
    223 if [ "$PUSH_SAME" -eq 0 ]; then
    224  NEW_PUSH_BRANCHES=
    225  for b in $PUSH_BRANCHES; do
    226    PUSH_COMMIT=$(git rev-parse "$b")
    227    SKIP_UPSTREAM=
    228    for u in $UPSTREAM_SKIP_SAME_AS; do
    229      # Skip the branch check on error
    230      UPSTREAM_COMMIT=$(git rev-parse "$u" 2>/dev/null) || continue
    231      if [ "$PUSH_COMMIT" = "$UPSTREAM_COMMIT" ]; then
    232        SKIP_UPSTREAM="$u"
    233      fi
    234    done
    235    if [ "$SKIP_UPSTREAM" ]; then
    236      printf "Skipping unchanged: %s matching remote: %s\\n" \
    237        "$b" "$SKIP_UPSTREAM"
    238    else
    239      if [ "$NEW_PUSH_BRANCHES" ]; then
    240        NEW_PUSH_BRANCHES="${NEW_PUSH_BRANCHES} ${b}"
    241      else
    242        NEW_PUSH_BRANCHES="${b}"
    243      fi
    244    fi
    245  done
    246  PUSH_BRANCHES=${NEW_PUSH_BRANCHES}
    247 fi
    248 
    249 if [ ! "$PUSH_BRANCHES" ]; then
    250  echo "No branches to push!"
    251  # We expect the rest of the script to run without errors, even if there
    252  # are no branches
    253 fi
    254 
    255 if [ "$PUSH_DELAY" -le 0 ]; then
    256  echo "Pushing $PUSH_BRANCHES"
    257  # We know that there are no spaces in any branch within $PUSH_BRANCHES, so
    258  # it is safe to use it unquoted.  (This also applies to the other shellcheck
    259  # exceptions below.)
    260  #
    261  # Push all the branches at the same time
    262  # shellcheck disable=SC2086
    263  $GIT_PUSH "$@" "$UPSTREAM_REMOTE" $PUSH_BRANCHES
    264 else
    265  # Push the branches in optimal CI order, with a delay between each push
    266  PUSH_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | sort -V)
    267  MASTER_BRANCH=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep main$) \
    268      || true # Skipped main branch
    269  if [ -z "$TEST_BRANCH_PREFIX" ]; then
    270    MAINT_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep maint) \
    271        || true # Skipped all maint branches
    272    RELEASE_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep release | \
    273      tr "\\n" " ") || true # Skipped all release branches
    274  else
    275    # Actually test branches based on maint branches
    276    MAINT_BRANCHES=$(echo "$PUSH_BRANCHES" | tr " " "\\n" | grep -v main$) \
    277        || true # Skipped all maint test branches
    278    # No release branches
    279    RELEASE_BRANCHES=
    280  fi
    281  if [ "$MASTER_BRANCH" ] || [ "$MAINT_BRANCHES" ] \
    282      || [ "$RELEASE_BRANCHES" ]; then
    283    printf "Pushing with %ss delays, so CI runs in this order:\\n" \
    284           "$PUSH_DELAY"
    285    if [ "$MASTER_BRANCH" ]; then
    286      printf "%s\\n" "$MASTER_BRANCH"
    287    fi
    288    if [ "$MAINT_BRANCHES" ]; then
    289      printf "%s\\n" "$MAINT_BRANCHES"
    290    fi
    291    if [ "$RELEASE_BRANCHES" ]; then
    292      printf "%s\\n" "$RELEASE_BRANCHES"
    293    fi
    294  fi
    295  # shellcheck disable=SC2086
    296  for b in $MASTER_BRANCH $MAINT_BRANCHES; do
    297    $GIT_PUSH "$@" "$UPSTREAM_REMOTE" "$b"
    298    # If we are pushing more than one branch, delay.  In the unlikely scenario
    299    # where we are pushing maint branches without the main branch, or maint
    300    # without release, there may be an extra delay
    301    if [ "$MAINT_BRANCHES" ] || [ "$RELEASE_BRANCHES" ]; then
    302      sleep "$PUSH_DELAY"
    303    fi
    304  done
    305  if [ "$RELEASE_BRANCHES" ]; then
    306    # shellcheck disable=SC2086
    307    $GIT_PUSH "$@" "$UPSTREAM_REMOTE" $RELEASE_BRANCHES
    308  fi
    309 fi