Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,11 @@ default:

.gitlab_base_ref_params: &gitlab_base_ref_params
- |
# FIXME: Disabled until we find a way to not hit GitHub API rate limit
if false && [[ ! $CI_COMMIT_BRANCH =~ ^(master|release/.*)$ ]]; then
export GIT_BASE_REF=$(.gitlab/find-gh-base-ref.sh)
if [[ -n "$GIT_BASE_REF" ]]; then
export GRADLE_PARAMS="$GRADLE_PARAMS -PgitBaseRef=origin/$GIT_BASE_REF"
else
echo "Failed to find base ref for PR" >&2
fi
export GIT_BASE_REF=$(.gitlab/find-gh-base-ref.sh)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also moved some logic here to the script. If it's master or release, it will produce no base ref. If CI_COMMIT_BRANCH is not defined, it will also skip.

if [[ -n "$GIT_BASE_REF" ]]; then
export GRADLE_PARAMS="$GRADLE_PARAMS -PgitBaseRef=origin/$GIT_BASE_REF"
else
echo "Failed to find base ref for PR" >&2
fi

.gradle_build: &gradle_build
Expand Down
122 changes: 57 additions & 65 deletions .gitlab/find-gh-base-ref.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
# Determines the base branch for the current PR (if we are running in a PR).
set -euo pipefail

if [[ -n "${CI_COMMIT_BRANCH:-}" ]]; then
echo "CI_COMMIT_BRANCH is set to $CI_COMMIT_BRANCH" >&2
else
echo "CI_COMMIT_BRANCH is not set, skipping base ref detection" >&2
exit 1
fi

if [[ $CI_COMMIT_BRANCH =~ ^(master|release/.*)$ ]]; then
echo "CI_COMMIT_BRANCH is a master or release branch, skipping base ref detection" >&2
exit 1
fi

CURRENT_HEAD_SHA="$(git rev-parse HEAD)"
if [[ -z "${CURRENT_HEAD_SHA:-}" ]]; then
echo "Failed to determine current HEAD SHA" >&2
Expand Down Expand Up @@ -33,81 +45,61 @@ if [[ -f $CACHE_PATH ]]; then
fi

# Happy path: if we're just one commit away from master, base ref is master.
if [[ $(git log --pretty=oneline origin/master..HEAD | wc -l) -eq 1 ]]; then
if [[ $(git rev-list --count origin/master..HEAD) -eq 1 ]]; then
echo "We are just one commit away from master, base ref is master" >&2
save_cache "master" "$CURRENT_HEAD_SHA"
echo "master"
exit 0
fi

# In GitLab: we have no reference to the base branch or even the PR number.
# We have to find it from the current branch name, which is defined in
# CI_COMMIT_REF_NAME.
if [[ -z "${CI_COMMIT_REF_NAME}" ]]; then
echo "CI_COMMIT_REF_NAME is not set, not running in GitLab CI?" >&2
exit 1
fi
get_distance_from_merge_base() {
local candidate_base="$1"
local merge_base_sha
local distance
merge_base_sha=$(git merge-base "$candidate_base" HEAD)
distance=$(git rev-list --count "$merge_base_sha".."$CURRENT_HEAD_SHA")
echo "Distance from $candidate_base is $distance" >&2
echo "$distance"
}

# In GitLab, CI_PROJECT_NAME is set, otherwise, set it for testing.
export CI_PROJECT_NAME="${CI_PROJECT_NAME:-dd-trace-java}"
# Find the best base ref: the master/release branch whose merge base is closest to HEAD.
# If there are multiple candidates (e.g. immediately after a release branch is created), we cannot
# disambiguate and return an error.
# NOTE: GitHub API is more robust for this task, but we hit rate limits.
BEST_CANDIDATES=(origin/master)
BEST_DISTANCE=$(get_distance_from_merge_base origin/master)

if [[ -z "${GITHUB_TOKEN:-}" ]]; then
echo "GITHUB_TOKEN is not set, fetching from AWS SSM" >&2
if ! command -v aws >/dev/null 2>&1; then
echo "aws is not installed, please install it" >&2
exit 1
fi
set +e
GITHUB_TOKEN=$(aws ssm get-parameter --name "ci.$CI_PROJECT_NAME.gh_release_token" --with-decryption --query "Parameter.Value" --output text)
set -e
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
echo "Failed to fetch GITHUB_TOKEN from AWS SSM" >&2
exit 1
fi
export GITHUB_TOKEN
# If the current branch is not a project/ branch, project/ branches are candidates.
# This accounts for the case when the project/ branch is being merged to master.
if [[ ! "$CI_COMMIT_BRANCH" =~ ^project/.*$ ]]; then
mapfile -t CANDIDATE_BASES < <(git branch -a --sort=committerdate --format='%(refname:short)' --list 'origin/release/v*' --list 'origin/project/*' | tac)
else
mapfile -t CANDIDATE_BASES < <(git branch -a --sort=committerdate --format='%(refname:short)' --list 'origin/release/v*' | tac)
fi

if ! command -v curl >/dev/null 2>&1; then
echo "curl is not installed, please install it" >&2
exit 1
fi
for candidate_base in "${CANDIDATE_BASES[@]}"; do
distance=$(get_distance_from_merge_base "$candidate_base")
if [[ $distance -lt $BEST_DISTANCE ]]; then
BEST_DISTANCE=$distance
BEST_CANDIDATES=("$candidate_base")
elif [[ $distance -eq $BEST_DISTANCE ]]; then
BEST_CANDIDATES+=("$candidate_base")
fi
done

if ! command -v jq >/dev/null 2>&1; then
echo "jq is not installed, please install it" >&2
exit 1
if [[ ${#BEST_CANDIDATES[@]} -eq 1 ]]; then
# Remote the origin/ prefix
base_ref="${BEST_CANDIDATES[0]#origin/}"
echo "Base ref is ${base_ref}" >&2
save_cache "${base_ref}" "$CURRENT_HEAD_SHA"
echo "${base_ref}"
exit 0
fi

while true; do
set +e
PR_DATA=$(curl \
-XGET \
--silent \
--include \
--fail-with-body \
-H 'Accept: application/vnd.github+json' \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/datadog/dd-trace-java/pulls?head=DataDog:${CI_COMMIT_REF_NAME}&sort=updated&direction=desc")
exit_code=$?
set -e
if [[ ${exit_code} -eq 0 ]]; then
PR_NUMBER=$(echo "$PR_DATA" | sed '1,/^[[:space:]]*$/d' | jq -r '.[].number')
PR_BASE_REF=$(echo "$PR_DATA" | sed '1,/^[[:space:]]*$/d' | jq -r '.[].base.ref')
if [[ -n "${PR_BASE_REF:-}" ]]; then
echo "PR is https://github.com/datadog/dd-trace-java/pull/${PR_NUMBER} and base ref is ${PR_BASE_REF}">&2
save_cache "${PR_BASE_REF}" "$CURRENT_HEAD_SHA"
echo "${PR_BASE_REF}"
exit 0
fi
fi
if echo "$PR_DATA" | grep -q "^x-ratelimit-reset:"; then
reset_timestamp=$(echo -n "$PR_DATA" | grep "^x-ratelimit-reset:" | sed -e 's/^x-ratelimit-reset: //' -e 's/\r//')
now=$(date +%s)
sleep_time=$((reset_timestamp - now + 1))
echo "GitHub rate limit exceeded, sleeping for ${sleep_time} seconds" >&2
sleep "${sleep_time}"
continue
fi
echo -e "GitHub request failed for an unknown reason:\n$(echo "$PR_DATA" | sed '/^$/q')" >&2
exit 1
done
# If base ref is ambiguous, we cannot determine the correct one.
# Example: a release branch is created, and a PR is opened starting from the
# commit where the release branch was created. The distance to the merge base
# for both master and the release branch is the same. In this case, we bail
# out, and make no assumption on which is the correct base ref.
echo "Base ref is ambiguous, candidates are: ${BEST_CANDIDATES[*]}" >&2
exit 1