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 }