Commit aff5ae4f authored by xuwang's avatar xuwang
Browse files

refactor(renovate): route renovate-bot API calls through gitlab.sh helpers



Replace raw curl with OTICA's gitlab.sh helpers (gitlab_get/post/post_stdin/put/
delete) for all operator-token calls — drops the hdr array, -X, and full URLs, and
matches the house style. ci-set upserts via delete-then-create and add-groups via
response-body checks (no HTTP-status branching). Only two curls remain by design:
the post-create "whoami" (new token) and SA self-rotate (bot's own token). Token
resolution and behavior unchanged; create/rotate/revoke re-validated on gitlab.med.

Changelog: changed
Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 2868d0d9
Loading
Loading
Loading
Loading
+41 −35
Original line number Diff line number Diff line
@@ -63,25 +63,26 @@ NAME="${RENOVATE_BOT_NAME:-renovate-bot}"
LEVEL="${RENOVATE_BOT_ACCESS_LEVEL:-40}"          # 40=Maintainer (default), 30=Developer
EXPIRES="${RENOVATE_BOT_EXPIRES:-$(date -v+365d +%F 2>/dev/null || date -d '+365 days' +%F)}"
TOKEN_FILE="${RENOVATE_BOT_TOKEN_FILE:-${HOME}/.otica-tokens/${NAME}.token}"
hdr=(--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}")

default_repo() {
    local url; url="$(_remote_url)"; url="${url%.git}"
    case "$url" in *://*) echo "${url#*://*/}";; *:*) echo "${url#*:}";; *) echo "$url";; esac
}

# Resolve the access-tokens API base for the chosen scope.
# GitLab calls go through gitlab.sh's gitlab_get/post/put/delete helpers (they add
# the PRIVATE-TOKEN header + ${GITLAB_API} base, using the operator token resolved
# above). TOKENS_PATH is the access-tokens API path for the chosen scope.
if [ -n "${RENOVATE_BOT_GROUP:-}" ]; then
    KIND="group"; TARGET="${RENOVATE_BOT_GROUP}"
    api="${GITLAB_API}/groups/$(urlencode "${TARGET}")/access_tokens"
    TOKENS_PATH="groups/$(urlencode "${TARGET}")/access_tokens"
else
    KIND="project"; TARGET="${RENOVATE_BOT_REPO:-$(default_repo)}"
    api="${GITLAB_API}/projects/$(get_project_id "${TARGET}")/access_tokens"
    TOKENS_PATH="projects/$(get_project_id "${TARGET}")/access_tokens"
fi

cmd_create() {
    local resp tok user
    resp=$(curl -s "${hdr[@]}" -X POST "${api}" \
    resp=$(gitlab_post "${TOKENS_PATH}" \
        --data "name=${NAME}" \
        --data "scopes[]=api" --data "scopes[]=write_repository" \
        --data "access_level=${LEVEL}" --data "expires_at=${EXPIRES}")
@@ -96,7 +97,7 @@ cmd_create() {
}

cmd_list() {
    local resp; resp=$(curl -s "${hdr[@]}" "${api}")
    local resp; resp=$(gitlab_get "${TOKENS_PATH}")
    echo "${resp}" | jq -e 'type=="array"' >/dev/null 2>&1 \
        || err "API error on ${KIND} ${TARGET}: $(echo "${resp}" | jq -r '.message // .error // .' 2>/dev/null || echo "${resp}") — check the token's server/permissions (RENOVATE_BOT_ADMIN_TOKEN[_FILE])."
    echo "${NAME} tokens on ${KIND} ${TARGET}:"
@@ -104,28 +105,32 @@ cmd_list() {
}

cmd_revoke() {
    local id
    id=$(curl -s "${hdr[@]}" "${api}" | jq -r --arg n "${NAME}" '.[]? | select(.name==$n and .active==true) | .id' | head -1)
    [ -n "${id}" ] && curl -s "${hdr[@]}" -X DELETE "${api}/${id}" -o /dev/null -w "  revoked ${NAME} (id=${id}): HTTP %{http_code}\n" \
        || echo "  (no active ${NAME} token on ${KIND} ${TARGET})"
    local id out
    id=$(gitlab_get "${TOKENS_PATH}" | jq -r --arg n "${NAME}" '.[]? | select(.name==$n and .active==true) | .id' | head -1)
    if [ -n "${id}" ]; then
        out=$(gitlab_delete "${TOKENS_PATH}/${id}")       # 204 No Content -> empty body on success
        [ -z "${out}" ] && echo "  revoked ${NAME} (id=${id})" \
            || echo "  revoke ${NAME} (id=${id}) error: $(echo "${out}" | jq -r '.message // .error // .')"
    else
        echo "  (no active ${NAME} token on ${KIND} ${TARGET})"
    fi
    [ -f "${TOKEN_FILE}" ] && { rm -f "${TOKEN_FILE}"; echo "  removed ${TOKEN_FILE}"; } || true
}

# Set the masked RENOVATE_TOKEN CI/CD variable on a project from the stored token.
cmd_ci_set() {
    local repo="${1:-${RENOVATE_BOT_REPO:-$(default_repo)}}" pid tok vapi code
    local repo="${1:-${RENOVATE_BOT_REPO:-$(default_repo)}}" pid tok vars resp
    [ -f "${TOKEN_FILE}" ] || err "No bot token at ${TOKEN_FILE} — run 'renovate-bot.sh create' first."
    tok="$(cat "${TOKEN_FILE}")"
    pid="$(get_project_id "${repo}")"
    vapi="${GITLAB_API}/projects/${pid}/variables"
    # Upsert: create, else update.
    code=$(curl -s "${hdr[@]}" -X POST "${vapi}" \
    vars="projects/${pid}/variables"
    # Upsert as delete-then-create (idempotent; avoids status-code branching).
    gitlab_delete "${vars}/RENOVATE_TOKEN" >/dev/null 2>&1 || true
    resp=$(gitlab_post "${vars}" \
        --data "key=RENOVATE_TOKEN" --data-urlencode "value=${tok}" \
        --data "masked=true" --data "protected=false" -o /dev/null -w '%{http_code}')
    if [ "${code}" = "400" ]; then
        curl -s "${hdr[@]}" -X PUT "${vapi}/RENOVATE_TOKEN" \
            --data-urlencode "value=${tok}" --data "masked=true" --data "protected=false" -o /dev/null
    fi
        --data "masked=true" --data "protected=false")
    echo "${resp}" | jq -e '.key? // empty' >/dev/null 2>&1 \
        || err "ci-set failed on ${repo}: $(echo "${resp}" | jq -r '.message // .error // .')"
    echo "Set masked RENOVATE_TOKEN CI/CD variable on ${repo}."
}

@@ -138,13 +143,13 @@ cmd_ci_set() {
# INSTANCE ADMIN here — the service-accounts API is admin-only.
cmd_sa_create() {
    local resp sid suser tok
    resp=$(curl -s "${hdr[@]}" -X POST "${GITLAB_API}/service_accounts" \
        --data-urlencode "name=${RENOVATE_BOT_SA_NAME:-Renovate Bot}" \
        --data "username=${RENOVATE_BOT_USER:-${NAME}}")
    # JSON body (via _stdin helper) so the display name may contain spaces.
    resp=$(jq -n --arg n "${RENOVATE_BOT_SA_NAME:-Renovate Bot}" --arg u "${RENOVATE_BOT_USER:-${NAME}}" \
        '{name: $n, username: $u}' | gitlab_post_stdin "service_accounts")
    sid=$(echo "${resp}" | jq -r '.id // empty')
    suser=$(echo "${resp}" | jq -r '.username // empty')
    [ -n "${sid}" ] || err "service-account create failed (needs an INSTANCE-ADMIN token via RENOVATE_BOT_ADMIN_TOKEN[_FILE]): $(echo "${resp}" | jq -r '.message // .error // .')"
    resp=$(curl -s "${hdr[@]}" -X POST "${GITLAB_API}/service_accounts/${sid}/personal_access_tokens" \
    resp=$(gitlab_post "service_accounts/${sid}/personal_access_tokens" \
        --data "name=${NAME}" --data "scopes[]=api" --data "scopes[]=write_repository" --data "expires_at=${EXPIRES}")
    tok=$(echo "${resp}" | jq -r '.token // empty')
    [ -n "${tok}" ] || err "SA token create failed: $(echo "${resp}" | jq -r '.message // .error // .')"
@@ -165,24 +170,25 @@ groups_from_config() {
# (RENOVATE_BOT_GROUPS, else derived from the admin config). Runnable by a group
# Owner — this is the part you don't need an instance admin for.
cmd_add_groups() {
    local user="${RENOVATE_BOT_USER:-}" uid groups g gid lvl code
    local user="${RENOVATE_BOT_USER:-}" uid groups g gid lvl resp
    [ -n "${user}" ] || err "set RENOVATE_BOT_USER to the bot username."
    lvl="${RENOVATE_BOT_ACCESS_LEVEL:-40}"
    uid=$(curl -s "${hdr[@]}" "${GITLAB_API}/users?username=${user}" | jq -r '.[0].id // empty')
    uid=$(gitlab_get "users?username=${user}" | jq -r '.[0].id // empty')
    [ -n "${uid}" ] || err "user '${user}' not found on ${GITLAB_SERVER}."
    groups="${RENOVATE_BOT_GROUPS:-$(groups_from_config || true)}"
    [ -n "${groups}" ] || err "no groups: set RENOVATE_BOT_GROUPS or provide ${RENOVATE_ADMIN_CONFIG:-renovate-admin.json}."
    echo "Adding ${user} (id=${uid}) as member (access ${lvl}) of:"
    for g in ${groups}; do
        gid=$(urlencode "${g}")
        code=$(curl -s "${hdr[@]}" -X POST "${GITLAB_API}/groups/${gid}/members" \
            --data "user_id=${uid}" --data "access_level=${lvl}" -o /dev/null -w '%{http_code}')
        case "${code}" in
            201) echo "  ${g}: added";;
            409) curl -s "${hdr[@]}" -X PUT "${GITLAB_API}/groups/${gid}/members/${uid}" --data "access_level=${lvl}" -o /dev/null
                 echo "  ${g}: already a member (level set ${lvl})";;
            *)   echo "  ${g}: HTTP ${code} (need Owner on the group?)";;
        esac
        resp=$(gitlab_post "groups/${gid}/members" --data "user_id=${uid}" --data "access_level=${lvl}")
        if echo "${resp}" | jq -e '.id? // empty' >/dev/null 2>&1; then
            echo "  ${g}: added"
        elif echo "${resp}" | jq -e '(.message // "" | tostring) | test("already";"i")' >/dev/null 2>&1; then
            gitlab_put "groups/${gid}/members/${uid}" --data "access_level=${lvl}" >/dev/null
            echo "  ${g}: already a member (level set ${lvl})"
        else
            echo "  ${g}: $(echo "${resp}" | jq -r '.message // .error // .') (need Owner on the group?)"
        fi
    done
}

@@ -225,10 +231,10 @@ cmd_rotate() {
        echo "Rotated service-account token for ${RENOVATE_BOT_USER} (old revoked)."
        _store_token "${new}"
    else
        id=$(curl -s "${hdr[@]}" "${api}" | jq -r --arg n "${NAME}" \
        id=$(gitlab_get "${TOKENS_PATH}" | jq -r --arg n "${NAME}" \
            '[.[]? | select(.name==$n and .active==true)] | sort_by(.expires_at) | last | .id // empty')
        [ -n "${id}" ] || err "no active ${NAME} token on ${KIND} ${TARGET} to rotate (create one first)."
        resp=$(curl -s "${hdr[@]}" -X POST "${api}/${id}/rotate" --data "expires_at=${EXPIRES}")
        resp=$(gitlab_post "${TOKENS_PATH}/${id}/rotate" --data "expires_at=${EXPIRES}")
        new=$(echo "${resp}" | jq -r '.token // empty')
        [ -n "${new}" ] || err "rotate failed on ${KIND} ${TARGET}: $(echo "${resp}" | jq -r '.message // .error // .')"
        echo "Rotated ${NAME} on ${KIND} ${TARGET} (old revoked)."