293 lines
7.5 KiB
Bash
293 lines
7.5 KiB
Bash
#!/hint/bash
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
[[ -z ${DEVTOOLS_INCLUDE_API_GITLAB_SH:-} ]] || return 0
|
|
DEVTOOLS_INCLUDE_API_GITLAB_SH=1
|
|
|
|
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
|
|
# shellcheck source=src/lib/common.sh
|
|
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
|
|
# shellcheck source=src/lib/config.sh
|
|
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/config.sh
|
|
|
|
set -e
|
|
|
|
graphql_api_call() {
|
|
local outfile=$1
|
|
local request=$2
|
|
local node_type=$3
|
|
local data=$4
|
|
local hasNextPage cursor
|
|
|
|
# empty token
|
|
if [[ -z "${GITLAB_TOKEN}" ]]; then
|
|
msg_error " api call failed: No token provided"
|
|
return 1
|
|
fi
|
|
|
|
[[ -z ${WORKDIR:-} ]] && setup_workdir
|
|
api_workdir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
|
|
|
|
# normalize graphql data and prepare query
|
|
data="${data//\"/\\\"}"
|
|
data='{
|
|
"query": "'"${data}"'"
|
|
}'
|
|
data="${data//$'\t'/ }"
|
|
data="${data//$'\n'/}"
|
|
|
|
cursor=""
|
|
hasNextPage=true
|
|
while [[ ${hasNextPage} == true ]]; do
|
|
data=$(sed -E 's|after: \\"[a-zA-Z0-9]*\\"|after: \\"'"${cursor}"'\\"|' <<< "${data}")
|
|
result="${api_workdir}/result.${cursor}"
|
|
|
|
if ! curl --request "${request}" \
|
|
--url "https://${GITLAB_HOST}/api/graphql" \
|
|
--header "Authorization: Bearer ${GITLAB_TOKEN}" \
|
|
--header "Content-Type: application/json" \
|
|
--data "${data}" \
|
|
--output "${result}" \
|
|
--silent; then
|
|
msg_error " api call failed: $(cat "${outfile}")"
|
|
return 1
|
|
fi
|
|
|
|
hasNextPage=$(jq --raw-output ".data | .${node_type} | .pageInfo | .hasNextPage" < "${result}")
|
|
cursor=$(jq --raw-output ".data | .${node_type} | .pageInfo | .endCursor" < "${result}")
|
|
|
|
cp "${result}" "${api_workdir}/tmp"
|
|
jq ".data.${node_type}.nodes" "${api_workdir}/tmp" > "${result}"
|
|
done
|
|
|
|
jq --slurp add "${api_workdir}"/result.* > "${outfile}"
|
|
return 0
|
|
}
|
|
|
|
gitlab_api_call() {
|
|
local outfile=$1
|
|
local request=$2
|
|
local endpoint=$3
|
|
local data=${4:-}
|
|
|
|
# empty token
|
|
if [[ -z "${GITLAB_TOKEN}" ]]; then
|
|
msg_error " api call failed: No token provided"
|
|
return 1
|
|
fi
|
|
|
|
if ! curl --request "${request}" \
|
|
--url "https://${GITLAB_HOST}/api/v4/${endpoint}" \
|
|
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
|
|
--header "Content-Type: application/json" \
|
|
--data "${data}" \
|
|
--output "${outfile}" \
|
|
--silent; then
|
|
msg_error " api call failed: $(cat "${outfile}")"
|
|
return 1
|
|
fi
|
|
|
|
if ! gitlab_check_api_errors "${outfile}"; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
gitlab_api_call_paged() {
|
|
local outfile=$1
|
|
local request=$2
|
|
local endpoint=$3
|
|
local data=${4:-}
|
|
local result header
|
|
|
|
# empty token
|
|
if [[ -z "${GITLAB_TOKEN}" ]]; then
|
|
msg_error " api call failed: No token provided"
|
|
return 1
|
|
fi
|
|
|
|
[[ -z ${WORKDIR:-} ]] && setup_workdir
|
|
api_workdir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
|
|
|
|
next_page=1
|
|
while [[ -n "${next_page}" ]]; do
|
|
result="${api_workdir}/result.${next_page}"
|
|
header="${api_workdir}/header"
|
|
if ! curl --request "${request}" \
|
|
--get \
|
|
--url "https://${GITLAB_HOST}/api/v4/${endpoint}&per_page=100&page=${next_page}" \
|
|
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
|
|
--header "Content-Type: application/json" \
|
|
--data-urlencode "${data}" \
|
|
--dump-header "${header}" \
|
|
--output "${result}" \
|
|
--silent; then
|
|
msg_error " api call failed: $(cat "${result}")"
|
|
return 1
|
|
fi
|
|
|
|
if ! gitlab_check_api_errors "${result}"; then
|
|
return 1
|
|
fi
|
|
|
|
next_page=$(grep "x-next-page" "${header}" | tr -d '\r' | awk '{ print $2 }')
|
|
done
|
|
|
|
jq --slurp add "${api_workdir}"/result.* > "${outfile}"
|
|
return 0
|
|
}
|
|
|
|
gitlab_check_api_errors() {
|
|
local file=$1
|
|
local error
|
|
|
|
# search API only returns an array, no errors
|
|
if [[ $(jq --raw-output 'type' < "${file}") == "array" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# check for general purpose api error
|
|
if error=$(jq --raw-output --exit-status '.error' < "${file}"); then
|
|
msg_error " api call failed: ${error}"
|
|
return 1
|
|
fi
|
|
|
|
# check for api specific error messages
|
|
if ! jq --raw-output --exit-status '.id' < "${file}" >/dev/null; then
|
|
if jq --raw-output --exit-status '.message | keys[]' < "${file}" &>/dev/null; then
|
|
while read -r error; do
|
|
msg_error " api call failed: ${error}"
|
|
done < <(jq --raw-output --exit-status '.message|to_entries|map("\(.key) \(.value[])")[]' < "${file}")
|
|
elif error=$(jq --raw-output --exit-status '.message' < "${file}"); then
|
|
msg_error " api call failed: ${error}"
|
|
fi
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
graphql_check_api_errors() {
|
|
local file=$1
|
|
local error
|
|
|
|
# early exit if we do not have errors
|
|
if ! jq --raw-output --exit-status '.errors[]' < "${file}" &>/dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
# check for api specific error messages
|
|
while read -r error; do
|
|
msg_error " api call failed: ${error}"
|
|
done < <(jq --raw-output --exit-status '.errors[].message' < "${file}")
|
|
return 1
|
|
}
|
|
|
|
gitlab_api_get_user() {
|
|
local outfile username
|
|
|
|
[[ -z ${WORKDIR:-} ]] && setup_workdir
|
|
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
|
|
|
|
# query user details
|
|
if ! gitlab_api_call "${outfile}" GET "user/"; then
|
|
msg_warn " Invalid token provided?"
|
|
exit 1
|
|
fi
|
|
|
|
# extract username from details
|
|
if ! username=$(jq --raw-output --exit-status '.username' < "${outfile}"); then
|
|
msg_error " failed to query username: $(cat "${outfile}")"
|
|
return 1
|
|
fi
|
|
|
|
printf "%s" "${username}"
|
|
return 0
|
|
}
|
|
|
|
gitlab_api_get_project_name_mapping() {
|
|
local query=$1
|
|
local outfile
|
|
|
|
[[ -z ${WORKDIR:-} ]] && setup_workdir
|
|
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
|
|
|
|
# query user details
|
|
if ! graphql_api_call "${outfile}" POST projects "${query}"; then
|
|
msg_warn " Invalid token provided?"
|
|
exit 1
|
|
fi
|
|
|
|
cat "${outfile}"
|
|
return 0
|
|
}
|
|
|
|
# Convert arbitrary project names to GitLab valid path names.
|
|
#
|
|
# GitLab has several limitations on project and group names and also maintains
|
|
# a list of reserved keywords as documented on their docs.
|
|
# https://docs.gitlab.com/ee/user/reserved_names.html
|
|
#
|
|
# 1. replace single '+' between word boundaries with '-'
|
|
# 2. replace any other '+' with literal 'plus'
|
|
# 3. replace any special chars other than '_', '-' and '.' with '-'
|
|
# 4. replace consecutive '_-' chars with a single '-'
|
|
# 5. replace 'tree' with 'unix-tree' due to GitLab reserved keyword
|
|
gitlab_project_name_to_path() {
|
|
local name=$1
|
|
printf "%s" "${name}" \
|
|
| sed -E 's/([a-zA-Z0-9]+)\+([a-zA-Z]+)/\1-\2/g' \
|
|
| sed -E 's/\+/plus/g' \
|
|
| sed -E 's/[^a-zA-Z0-9_\-\.]/-/g' \
|
|
| sed -E 's/[_\-]{2,}/-/g' \
|
|
| sed -E 's/^tree$/unix-tree/g'
|
|
}
|
|
|
|
gitlab_api_create_project() {
|
|
local pkgbase=$1
|
|
local outfile data path project_path
|
|
|
|
[[ -z ${WORKDIR:-} ]] && setup_workdir
|
|
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
|
|
|
|
project_path=$(gitlab_project_name_to_path "${pkgbase}")
|
|
|
|
# create GitLab project
|
|
data='{
|
|
"name": "'"${pkgbase}"'",
|
|
"path": "'"${project_path}"'",
|
|
"namespace_id": "'"${GIT_PACKAGING_NAMESPACE_ID}"'",
|
|
"request_access_enabled": "false"
|
|
}'
|
|
if ! gitlab_api_call "${outfile}" POST "projects/" "${data}"; then
|
|
return 1
|
|
fi
|
|
|
|
if ! path=$(jq --raw-output --exit-status '.path' < "${outfile}"); then
|
|
msg_error " failed to query path: $(cat "${outfile}")"
|
|
return 1
|
|
fi
|
|
|
|
printf "%s" "${path}"
|
|
return 0
|
|
}
|
|
|
|
# TODO: parallelize
|
|
# https://docs.gitlab.com/ee/api/search.html#scope-blobs
|
|
gitlab_api_search() {
|
|
local search=$1
|
|
local outfile
|
|
|
|
[[ -z ${WORKDIR:-} ]] && setup_workdir
|
|
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
|
|
|
|
if ! gitlab_api_call_paged "${outfile}" GET "/groups/archlinux%2fpackaging%2fpackages/search?scope=blobs" "search=${search}"; then
|
|
return 1
|
|
fi
|
|
|
|
cat "${outfile}"
|
|
|
|
return 0
|
|
}
|