|
2 | 2 | # Determines the base branch for the current PR (if we are running in a PR). |
3 | 3 | set -euo pipefail |
4 | 4 |
|
| 5 | +if [[ -n "${CI_COMMIT_BRANCH:-}" ]]; then |
| 6 | + echo "CI_COMMIT_BRANCH is set to $CI_COMMIT_BRANCH" >&2 |
| 7 | +else |
| 8 | + echo "CI_COMMIT_BRANCH is not set, skipping base ref detection" >&2 |
| 9 | + exit 1 |
| 10 | +fi |
| 11 | + |
| 12 | +if [[ $CI_COMMIT_BRANCH =~ ^(master|release/.*)$ ]]; then |
| 13 | + echo "CI_COMMIT_BRANCH is a master or release branch, skipping base ref detection" >&2 |
| 14 | + exit 1 |
| 15 | +fi |
| 16 | + |
5 | 17 | CURRENT_HEAD_SHA="$(git rev-parse HEAD)" |
6 | 18 | if [[ -z "${CURRENT_HEAD_SHA:-}" ]]; then |
7 | 19 | echo "Failed to determine current HEAD SHA" >&2 |
@@ -33,81 +45,61 @@ if [[ -f $CACHE_PATH ]]; then |
33 | 45 | fi |
34 | 46 |
|
35 | 47 | # Happy path: if we're just one commit away from master, base ref is master. |
36 | | -if [[ $(git log --pretty=oneline origin/master..HEAD | wc -l) -eq 1 ]]; then |
| 48 | +if [[ $(git rev-list --count origin/master..HEAD) -eq 1 ]]; then |
37 | 49 | echo "We are just one commit away from master, base ref is master" >&2 |
38 | 50 | save_cache "master" "$CURRENT_HEAD_SHA" |
39 | 51 | echo "master" |
40 | 52 | exit 0 |
41 | 53 | fi |
42 | 54 |
|
43 | | -# In GitLab: we have no reference to the base branch or even the PR number. |
44 | | -# We have to find it from the current branch name, which is defined in |
45 | | -# CI_COMMIT_REF_NAME. |
46 | | -if [[ -z "${CI_COMMIT_REF_NAME}" ]]; then |
47 | | - echo "CI_COMMIT_REF_NAME is not set, not running in GitLab CI?" >&2 |
48 | | - exit 1 |
49 | | -fi |
| 55 | +get_distance_from_merge_base() { |
| 56 | + local candidate_base="$1" |
| 57 | + local merge_base_sha |
| 58 | + local distance |
| 59 | + merge_base_sha=$(git merge-base "$candidate_base" HEAD) |
| 60 | + distance=$(git rev-list --count "$merge_base_sha".."$CURRENT_HEAD_SHA") |
| 61 | + echo "Distance from $candidate_base is $distance" >&2 |
| 62 | + echo "$distance" |
| 63 | +} |
50 | 64 |
|
51 | | -# In GitLab, CI_PROJECT_NAME is set, otherwise, set it for testing. |
52 | | -export CI_PROJECT_NAME="${CI_PROJECT_NAME:-dd-trace-java}" |
| 65 | +# Find the best base ref: the master/release branch whose merge base is closest to HEAD. |
| 66 | +# If there are multiple candidates (e.g. immediately after a release branch is created), we cannot |
| 67 | +# disambiguate and return an error. |
| 68 | +# NOTE: GitHub API is more robust for this task, but we hit rate limits. |
| 69 | +BEST_CANDIDATES=(origin/master) |
| 70 | +BEST_DISTANCE=$(get_distance_from_merge_base origin/master) |
53 | 71 |
|
54 | | -if [[ -z "${GITHUB_TOKEN:-}" ]]; then |
55 | | - echo "GITHUB_TOKEN is not set, fetching from AWS SSM" >&2 |
56 | | - if ! command -v aws >/dev/null 2>&1; then |
57 | | - echo "aws is not installed, please install it" >&2 |
58 | | - exit 1 |
59 | | - fi |
60 | | - set +e |
61 | | - GITHUB_TOKEN=$(aws ssm get-parameter --name "ci.$CI_PROJECT_NAME.gh_release_token" --with-decryption --query "Parameter.Value" --output text) |
62 | | - set -e |
63 | | - if [[ -z "${GITHUB_TOKEN:-}" ]]; then |
64 | | - echo "Failed to fetch GITHUB_TOKEN from AWS SSM" >&2 |
65 | | - exit 1 |
66 | | - fi |
67 | | - export GITHUB_TOKEN |
| 72 | +# If the current branch is not a project/ branch, project/ branches are candidates. |
| 73 | +# This accounts for the case when the project/ branch is being merged to master. |
| 74 | +if [[ ! "$CI_COMMIT_BRANCH" =~ ^project/.*$ ]]; then |
| 75 | + mapfile -t CANDIDATE_BASES < <(git branch -a --sort=committerdate --format='%(refname:short)' --list 'origin/release/v*' --list 'origin/project/*' | tac) |
| 76 | +else |
| 77 | + mapfile -t CANDIDATE_BASES < <(git branch -a --sort=committerdate --format='%(refname:short)' --list 'origin/release/v*' | tac) |
68 | 78 | fi |
69 | 79 |
|
70 | | -if ! command -v curl >/dev/null 2>&1; then |
71 | | - echo "curl is not installed, please install it" >&2 |
72 | | - exit 1 |
73 | | -fi |
| 80 | +for candidate_base in "${CANDIDATE_BASES[@]}"; do |
| 81 | + distance=$(get_distance_from_merge_base "$candidate_base") |
| 82 | + if [[ $distance -lt $BEST_DISTANCE ]]; then |
| 83 | + BEST_DISTANCE=$distance |
| 84 | + BEST_CANDIDATES=("$candidate_base") |
| 85 | + elif [[ $distance -eq $BEST_DISTANCE ]]; then |
| 86 | + BEST_CANDIDATES+=("$candidate_base") |
| 87 | + fi |
| 88 | +done |
74 | 89 |
|
75 | | -if ! command -v jq >/dev/null 2>&1; then |
76 | | - echo "jq is not installed, please install it" >&2 |
77 | | - exit 1 |
| 90 | +if [[ ${#BEST_CANDIDATES[@]} -eq 1 ]]; then |
| 91 | + # Remote the origin/ prefix |
| 92 | + base_ref="${BEST_CANDIDATES[0]#origin/}" |
| 93 | + echo "Base ref is ${base_ref}" >&2 |
| 94 | + save_cache "${base_ref}" "$CURRENT_HEAD_SHA" |
| 95 | + echo "${base_ref}" |
| 96 | + exit 0 |
78 | 97 | fi |
79 | 98 |
|
80 | | -while true; do |
81 | | - set +e |
82 | | - PR_DATA=$(curl \ |
83 | | - -XGET \ |
84 | | - --silent \ |
85 | | - --include \ |
86 | | - --fail-with-body \ |
87 | | - -H 'Accept: application/vnd.github+json' \ |
88 | | - -H "Authorization: Bearer ${GITHUB_TOKEN}" \ |
89 | | - -H "X-GitHub-Api-Version: 2022-11-28" \ |
90 | | - "https://api.github.com/repos/datadog/dd-trace-java/pulls?head=DataDog:${CI_COMMIT_REF_NAME}&sort=updated&direction=desc") |
91 | | - exit_code=$? |
92 | | - set -e |
93 | | - if [[ ${exit_code} -eq 0 ]]; then |
94 | | - PR_NUMBER=$(echo "$PR_DATA" | sed '1,/^[[:space:]]*$/d' | jq -r '.[].number') |
95 | | - PR_BASE_REF=$(echo "$PR_DATA" | sed '1,/^[[:space:]]*$/d' | jq -r '.[].base.ref') |
96 | | - if [[ -n "${PR_BASE_REF:-}" ]]; then |
97 | | - echo "PR is https://github.com/datadog/dd-trace-java/pull/${PR_NUMBER} and base ref is ${PR_BASE_REF}">&2 |
98 | | - save_cache "${PR_BASE_REF}" "$CURRENT_HEAD_SHA" |
99 | | - echo "${PR_BASE_REF}" |
100 | | - exit 0 |
101 | | - fi |
102 | | - fi |
103 | | - if echo "$PR_DATA" | grep -q "^x-ratelimit-reset:"; then |
104 | | - reset_timestamp=$(echo -n "$PR_DATA" | grep "^x-ratelimit-reset:" | sed -e 's/^x-ratelimit-reset: //' -e 's/\r//') |
105 | | - now=$(date +%s) |
106 | | - sleep_time=$((reset_timestamp - now + 1)) |
107 | | - echo "GitHub rate limit exceeded, sleeping for ${sleep_time} seconds" >&2 |
108 | | - sleep "${sleep_time}" |
109 | | - continue |
110 | | - fi |
111 | | - echo -e "GitHub request failed for an unknown reason:\n$(echo "$PR_DATA" | sed '/^$/q')" >&2 |
112 | | - exit 1 |
113 | | -done |
| 99 | +# If base ref is ambiguous, we cannot determine the correct one. |
| 100 | +# Example: a release branch is created, and a PR is opened starting from the |
| 101 | +# commit where the release branch was created. The distance to the merge base |
| 102 | +# for both master and the release branch is the same. In this case, we bail |
| 103 | +# out, and make no assumption on which is the correct base ref. |
| 104 | +echo "Base ref is ambiguous, candidates are: ${BEST_CANDIDATES[*]}" >&2 |
| 105 | +exit 1 |
0 commit comments