#!/bin/bash
# Common helpers shared by the docker-random case scripts
# (all.sh, tidb.sh, cdc.sh, replication.sh, upgrade.sh).
#
# This file is meant to be sourced, not executed. It does not enable or
# disable any shell options so callers keep full control of `set -e` etc.

# Default number of attempts for `git_pull_retry`.
GIT_PULL_RETRY_MAX=${GIT_PULL_RETRY_MAX:-5}
# Base seconds to sleep between attempts; doubled each retry (exponential
# backoff), capped at 60s.
GIT_PULL_RETRY_BASE_INTERVAL=${GIT_PULL_RETRY_BASE_INTERVAL:-5}

# Run `git pull` with retry, intended to absorb transient failures
# (DNS hiccups, short-lived disk pressure, proxy "no healthy upstream",
# index-pack write errors, ...).
#
# Behavior:
#   - Makes GIT_PULL_RETRY_MAX attempts in total (default 5).
#   - Sleeps with exponential backoff between attempts.
#   - On success, returns 0.
#   - Gives up immediately, without further retries, on failures that
#     retrying cannot fix (local or untracked files blocking checkout,
#     SSH/auth failures, missing repository, not inside a git repo, ...).
#   - If every attempt fails, prints a warning to stderr and returns 1 so
#     the caller can skip downstream steps that depend on a fresh pull
#     (e.g. `git merge`). When invoked from an `if` condition the non-zero
#     status does NOT trigger `set -e`, so the calling script still continues
#     with the current local checkout instead of aborting the whole run.
#
# Globals read (overridable via environment):
#   GIT_PULL_RETRY_MAX           - total number of attempts (default 5)
#   GIT_PULL_RETRY_BASE_INTERVAL - initial sleep seconds (default 5)
git_pull_retry() {
    local max="${GIT_PULL_RETRY_MAX}"
    local interval="${GIT_PULL_RETRY_BASE_INTERVAL}"
    local attempt=1
    local output

    while [ "$attempt" -le "$max" ]; do
        # NOTE: do not combine `local` with the command substitution below;
        # `local` would mask `git pull`'s exit status.
        if output=$(git pull 2>&1); then
            printf '%s\n' "$output"
            return 0
        fi
        printf '%s\n' "$output" >&2

        # Failures that retrying cannot fix; give up right away instead of
        # burning the full retry budget every day.
        if printf '%s\n' "$output" | grep -qiE \
            'would be overwritten|Permission denied|Authentication failed|Repository not found|not a git repository|does not appear to be a git repository'; then
            echo "[git_pull_retry] ERROR: non-transient failure; giving up and continuing with the current checkout." >&2
            return 1
        fi

        if [ "$attempt" -lt "$max" ]; then
            echo "[git_pull_retry] attempt $attempt/$max failed; retrying in ${interval}s..." >&2
            sleep "$interval"
            # Exponential backoff, capped at one minute between attempts.
            if [ "$interval" -lt 60 ]; then
                interval=$((interval * 2))
                if [ "$interval" -gt 60 ]; then
                    interval=60
                fi
            fi
        fi
        attempt=$((attempt + 1))
    done

    echo "[git_pull_retry] WARNING: git pull failed $max times; continuing with the current checkout." >&2
    return 1
}
