github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/build/teamcity-support.sh (about)

     1  # Common helpers for teamcity-*.sh scripts.
     2  
     3  # root is the absolute path to the root directory of the repository.
     4  root=$(cd "$(dirname "$0")/.." && pwd)
     5  
     6  # maybe_ccache turns on ccache to speed up compilation, but only for PR builds.
     7  # This speeds up the CI cycle for developers while preventing ccache from
     8  # corrupting a release build.
     9  maybe_ccache() {
    10    if tc_release_branch; then
    11      echo "On release branch ($TC_BUILD_BRANCH), so not enabling ccache."
    12    else
    13      echo "Building PR (#$TC_BUILD_BRANCH), so enabling ccache."
    14      definitely_ccache
    15    fi
    16  }
    17  
    18  definitely_ccache() {
    19    run export COCKROACH_BUILDER_CCACHE=1
    20  }
    21  
    22  run() {
    23    echo "$@"
    24    "$@"
    25  }
    26  
    27  run_counter=-1
    28  
    29  # Takes args that produce `go test -json` output. It filters stdout to contain
    30  # only test output related to failing tests and run/pass/skip events for the
    31  # other tests (no output). It writes artifacts/failures.txt containing text
    32  # output for the failing tests.
    33  # It's valid to call this multiple times; all output artifacts will be
    34  # preserved.
    35  function run_json_test() {
    36    run_counter=$((run_counter+1))
    37    tc_start_block "prep"
    38    # TODO(tbg): better to go through builder for all of this.
    39    go install github.com/cockroachdb/cockroach/pkg/cmd/testfilter
    40    go install github.com/cockroachdb/cockroach/pkg/cmd/github-post
    41    mkdir -p artifacts
    42    tmpfile="artifacts/raw.${run_counter}.json.txt"
    43    tc_end_block "prep"
    44  
    45    tc_start_block "run"
    46    set +e
    47    run "$@" 2>&1 \
    48      | tee "${tmpfile}" \
    49      | testfilter -mode=strip \
    50      | tee artifacts/stripped.txt
    51    status=$?
    52    set -e
    53    tc_end_block "run"
    54  
    55    # Post issues, if on a release branch. Note that we're feeding github-post all
    56    # of the build output; it also does some slow test analysis.
    57    if tc_release_branch; then
    58      if [ -z "${GITHUB_API_TOKEN-}" ]; then
    59        # GITHUB_API_TOKEN must be in the env or github-post will barf if it's
    60        # ever asked to post, so enforce that on all runs.
    61        # The way this env var is made available here is quite tricky. The build
    62        # calling this method is usually a build that is invoked from PRs, so it
    63        # can't have secrets available to it (for the PR could modify
    64        # build/teamcity-* to leak the secret). Instead, we provide the secrets
    65        # to a higher-level job (Publish Bleeding Edge) and use TeamCity magic to
    66        # pass that env var through when it's there. This means we won't have the
    67        # env var on PR builds, but we'll have it for builds that are triggered
    68        # from the release branches.
    69        echo "GITHUB_API_TOKEN must be set"
    70        # TODO(tbg): let this bake for a few days and if all looks good make it
    71        # an error to not have the token specified when it's needed.
    72        # exit 1
    73      else
    74        tc_start_block "post issues"
    75        github-post < "${tmpfile}"
    76        tc_end_block "post issues"
    77      fi
    78    fi
    79  
    80    tc_start_block "artifacts"
    81    # Create (or append to) failures.txt artifact and delete stripped.txt.
    82    testfilter -mode=omit < artifacts/stripped.txt | testfilter -mode convert >> artifacts/failures.txt
    83  
    84    if [ $status -ne 0 ]; then
    85      # Keep the debug file around for failed builds. Compress it to avoid
    86      # clogging the agents with stuff we'll hopefully rarely ever need to
    87      # look at.
    88      # If the process failed, also save the full human-readable output. This is
    89      # helpful in cases in which tests timed out, where it's difficult to blame
    90      # the failure on any particular test. It's also a good alternative to poking
    91      # around in $tmpfile itself when anything else we don't handle well happens,
    92      # whatever that may be.
    93      fullfile=artifacts/full_output.txt
    94      testfilter -mode convert < "${tmpfile}" >> "${fullfile}"
    95      tar --strip-components 1 -czf "${tmpfile}.tgz" "${tmpfile}" "${fullfile}"
    96      rm -f "${fullfile}"
    97    fi
    98    rm -f "${tmpfile}" artifacts/stripped.txt
    99    tc_end_block "artifacts"
   100  
   101    # Make it easier to figure out whether we're exiting because of a test failure
   102    # or because of some auxiliary failure.
   103    tc_start_block "exit status"
   104    echo "test run finished with exit status $status"
   105    tc_end_block "exit status"
   106    return $status
   107  }
   108  
   109  # Takes a package name and remaining args that produce `go test` text output
   110  # for the given package.
   111  function run_text_test() {
   112    pkg=$1
   113    shift
   114    echo "# ${pkg}"
   115    echo "$@"
   116    "$@" 2>&1 | go tool test2json -t -p "${pkg}" | run_json_test cat
   117  }
   118  
   119  function maybe_stress() {
   120    # Don't stressrace on the release branches; we only want that to happen on the
   121    # PRs. There's no need in making master flakier than it needs to be; nightly
   122    # stress will weed out the flaky tests.
   123    # NB: as a consequence of the above, this code doesn't know about posting
   124    # Github issues.
   125    if tc_release_branch; then
   126      return 0
   127    fi
   128  
   129    target=$1
   130    shift
   131  
   132    block="Maybe ${target} pull request"
   133    tc_start_block "${block}"
   134    run build/builder.sh go install ./pkg/cmd/github-pull-request-make
   135    run_json_test build/builder.sh env BUILD_VCS_NUMBER="$BUILD_VCS_NUMBER" TARGET="${target}" github-pull-request-make
   136    tc_end_block "${block}"
   137  }
   138  
   139  # Returns the list of release branches from origin (origin/release-*), ordered
   140  # by version (higher version numbers first).
   141  get_release_branches() {
   142    # We sort by the minor version first, followed by a stable sort on the major
   143    # version.
   144    git branch -r --format='%(refname)' \
   145      | sed 's/^refs\/remotes\///' \
   146      | grep '^origin\/release-*' \
   147      | sort -t. -k2 -n -r \
   148      | sort -t- -k2 -n -r -s
   149  }
   150  
   151  # Returns the number of commits in the curent branch that are not shared with
   152  # the given branch.
   153  get_branch_distance() {
   154    git rev-list --count $1..HEAD
   155  }
   156  
   157  # Returns the branch among origin/master, origin/release-* which is the
   158  # closest to the current HEAD.
   159  #
   160  # Suppose the origin looks like this:
   161  #
   162  #                e (master)
   163  #                |
   164  #                d       w (release-19.2)
   165  #                |       |
   166  #                c       u
   167  #                 \     /
   168  #                  \   /
   169  #                   \ /
   170  #                    b
   171  #                    |
   172  #                    a
   173  #
   174  # Example 1. PR on master on top of d:
   175  #
   176  #      e (master)   pr
   177  #             \     /
   178  #              \   /
   179  #               \ /
   180  #                d       w (release-19.2)
   181  #                |       |
   182  #                c       u
   183  #                 \     /
   184  #                  \   /
   185  #                   \ /
   186  #                    b
   187  #                    |
   188  #                    a
   189  #
   190  # The pr commit has distance 1 from master and distance 3 from release-19.2
   191  # (commits c, d, and pr); so we deduce that the upstream branch is master.
   192  #
   193  # Example 2. PR on release-19.2 on top of u:
   194  #
   195  #                e (master)
   196  #                |
   197  #                d   w (release-19.2)
   198  #                |     \
   199  #                |      \   pr
   200  #                |       \ /
   201  #                c       u
   202  #                 \     /
   203  #                  \   /
   204  #                   \ /
   205  #                    b
   206  #                    |
   207  #                    a
   208  #
   209  # The pr commit has distance 2 from master (commits u and w) and distance 1 from
   210  # release-19.2; so we deduce that the upstream branch is release-19.2.
   211  #
   212  # If the PR is on top of the fork point (b in the example above), we return the
   213  # release-19.2 branch.
   214  #
   215  # Example 3. PR on even older release:
   216  #
   217  #                e (master)
   218  #                |
   219  #                d    w (release-19.2)
   220  #                |       |
   221  #                |       |
   222  #                |       |        pr
   223  #                c       u       /
   224  #                 \     /       y (release-19.1)
   225  #                  \   /       /
   226  #                   \ /       /
   227  #                    b       x
   228  #                     \     /
   229  #                      \   /
   230  #                       \ /
   231  #                        a
   232  #
   233  # The pr commit has distance 3 from both master and release-19.2 (commits x, y,
   234  # pr) and distance 1 from release-19.1. In general, the distance w.r.t. all
   235  # newer releases than the correct one will be equal; specifically, it is the
   236  # number of commits since the fork point of the correct release (the fork point
   237  # in this example is commit a).
   238  #
   239  get_upstream_branch() {
   240    local UPSTREAM DISTANCE D
   241  
   242    UPSTREAM="origin/master"
   243    DISTANCE=$(get_branch_distance origin/master)
   244  
   245    # Check if we're closer to any release branches. The branches are ordered
   246    # new-to-old, so stop as soon as the distance starts to increase.
   247    for branch in $(get_release_branches); do
   248      D=$(get_branch_distance $branch)
   249      # It is important to continue the loop if the distance is the same; see
   250      # example 3 above.
   251      if [ $D -gt $DISTANCE ]; then
   252        break
   253      fi
   254      UPSTREAM=$branch
   255      DISTANCE=$D
   256    done
   257  
   258    echo "$UPSTREAM"
   259  }
   260  
   261  changed_go_pkgs() {
   262    git fetch --quiet origin
   263    upstream_branch=$(get_upstream_branch)
   264    # Find changed packages, minus those that have been removed entirely. Note
   265    # that the three-dot notation means we are diffing against the merge-base of
   266    # the two branches, not against the tip of the upstream branch.
   267    git diff --name-only "$upstream_branch..." -- "pkg/**/*.go" ":!*/testdata/*" \
   268      | xargs -rn1 dirname \
   269      | sort -u \
   270      | { while read path; do if ls "$path"/*.go &>/dev/null; then echo -n "./$path "; fi; done; }
   271  }
   272  
   273  tc_release_branch() {
   274    [[ "$TC_BUILD_BRANCH" == master || "$TC_BUILD_BRANCH" == release-* || "$TC_BUILD_BRANCH" == provisional_* ]]
   275  }
   276  
   277  tc_start_block() {
   278    echo "##teamcity[blockOpened name='$1']"
   279  }
   280  
   281  if_tc() {
   282    if [[ "${TC_BUILD_ID-}" ]]; then
   283      "$@"
   284    fi
   285  }
   286  
   287  tc_end_block() {
   288    echo "##teamcity[blockClosed name='$1']"
   289  }
   290  
   291  tc_prepare() {
   292    tc_start_block "Prepare environment"
   293    run export BUILDER_HIDE_GOPATH_SRC=1
   294    run mkdir -p artifacts
   295    maybe_ccache
   296    tc_end_block "Prepare environment"
   297  }