Skip to content

Commit 4acd1c7

Browse files
authored
Find PR base branch without GitHub API and enable test skipping (#9080)
1 parent bf5baad commit 4acd1c7

File tree

2 files changed

+62
-73
lines changed

2 files changed

+62
-73
lines changed

.gitlab-ci.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,11 @@ default:
119119

120120
.gitlab_base_ref_params: &gitlab_base_ref_params
121121
- |
122-
# FIXME: Disabled until we find a way to not hit GitHub API rate limit
123-
if false && [[ ! $CI_COMMIT_BRANCH =~ ^(master|release/.*)$ ]]; then
124-
export GIT_BASE_REF=$(.gitlab/find-gh-base-ref.sh)
125-
if [[ -n "$GIT_BASE_REF" ]]; then
126-
export GRADLE_PARAMS="$GRADLE_PARAMS -PgitBaseRef=origin/$GIT_BASE_REF"
127-
else
128-
echo "Failed to find base ref for PR" >&2
129-
fi
122+
export GIT_BASE_REF=$(.gitlab/find-gh-base-ref.sh)
123+
if [[ -n "$GIT_BASE_REF" ]]; then
124+
export GRADLE_PARAMS="$GRADLE_PARAMS -PgitBaseRef=origin/$GIT_BASE_REF"
125+
else
126+
echo "Failed to find base ref for PR" >&2
130127
fi
131128
132129
.gradle_build: &gradle_build

.gitlab/find-gh-base-ref.sh

Lines changed: 57 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
# Determines the base branch for the current PR (if we are running in a PR).
33
set -euo pipefail
44

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+
517
CURRENT_HEAD_SHA="$(git rev-parse HEAD)"
618
if [[ -z "${CURRENT_HEAD_SHA:-}" ]]; then
719
echo "Failed to determine current HEAD SHA" >&2
@@ -33,81 +45,61 @@ if [[ -f $CACHE_PATH ]]; then
3345
fi
3446

3547
# 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
3749
echo "We are just one commit away from master, base ref is master" >&2
3850
save_cache "master" "$CURRENT_HEAD_SHA"
3951
echo "master"
4052
exit 0
4153
fi
4254

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+
}
5064

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)
5371

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)
6878
fi
6979

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
7489

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
7897
fi
7998

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

Comments
 (0)