From: Matthias Kruk Date: Wed, 24 Mar 2021 23:22:35 +0000 (+0900) Subject: include/gitlab: Add functions to call Gitlab API from the shell X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=7bf48265a846c16195ea36f968dd5d433046164a;p=toolbox include/gitlab: Add functions to call Gitlab API from the shell Gitlab can be integrated with other software through Gitlab's REST API. This commit adds functions that allow using various Gitlab API functions from the shell, such as forking repositories, managing branches, and creating merge requests. --- diff --git a/include/gitlab.sh b/include/gitlab.sh new file mode 100755 index 0000000..7fa6ac0 --- /dev/null +++ b/include/gitlab.sh @@ -0,0 +1,465 @@ +#!/bin/bash + +# +# gitlab_api.sh - Bash functions to access Gitlab API +# Author: Matthias Kruk +# + +__init() { + if ! include "log" "json"; then + return 1 + fi + + return 0 +} + +_gitlab_urlencode() { + local str + + str="$1" + + echo "${str//\//%2F}" +} + +_gitlab_get() { + local token + local url + + token="$1" + url="$2" + + if ! curl --silent --location -X GET \ + --header "Private-Token: $token" "$url"; then + return 1 + fi + + return 0 +} + +_gitlab_post() { + local token + local url + local data + + token="$1" + url="$2" + data="$3" + + if ! curl --silent --location -X POST \ + --header "Private-Token: $token" \ + --header "Content-Type: application/json" \ + --data "$data" "$url"; then + return 1 + fi + + return 0 +} + +gitlab_import_status() { + local host + local token + local project + + local url + local res + + host="$1" + token="$2" + project="$3" + + id=$(_gitlab_urlencode "$project") + url="$host/api/v4/projects/$id" + + if ! res=$(_gitlab_get "$token" "$url"); then + return 1 + fi + + echo "$res" | jq -r ".import_status" + return 0 +} + +gitlab_download_file() { + local host + local token + local project + local branch + local file + + local url + + host="$1" + token="$2" + project="$3" + branch="$4" + file="$5" + + project=$(_gitlab_urlencode "$project") + file=$(_gitlab_urlencode "$file") + url="$host/api/v4/projects/$project/repository/files/$file/raw?ref=$branch" + + if ! _gitlab_get "$token" "$url"; then + return 1 + fi + + return 0 +} + +gitlab_get_users() { + local host + local token + + local url + + host="$1" + token="$2" + + url="$host/api/v4/users?per_page=512" + + if ! _gitlab_get "$token" "$url"; then + return 1 + fi + + return 0 +} + +gitlab_user_list() { + local host + local token + + local resp + + host="$1" + token="$2" + + if ! resp=$(gitlab_get_users "$host" "$token"); then + return 1 + fi + + echo "$resp" | jq -r ".[] | \"\(.id) \(.username) \(.name)\"" + return 0 +} + +gitlab_get_user_id() { + local host + local token + local user + + local resp + local uid + local username + local fullname + + host="$1" + token="$2" + user="$3" + + if ! resp=$(gitlab_user_list "$host" "$token"); then + return 1 + fi + + while read -r uid username fullname; do + if [[ "$username" == "$user" ]]; then + echo "$uid" + return 0 + fi + done <<< "$resp" + + return 1 +} + +gitlab_fork() { + local host + local token + local project + + local url + local id + local data + local resp + + host="$1" + token="$2" + project="$3" + + id=$(_gitlab_urlencode "$project") + url="$host/api/v4/projects/$id/fork" + data=$(json_make "id" "$id") + + if ! _gitlab_post "$token" "$url" "$data"; then + return 1 + fi + + return 0 +} + +gitlab_fork_sync() { + local host + local token + local project + + local resp + local fork_id + + host="$1" + token="$2" + project="$3" + + if ! resp=$(gitlab_fork "$host" "$token" "$project"); then + echo "Could not fork project" 1>&2 + return 1 + fi + + if ! fork_id=$(echo "$resp" | jq ".id"); then + echo "Could not get id of fork" 1>&2 + return 1 + fi + + # Gitlab's fork API call returns before the fork completes, but we want + # to make sure the fork is complete by the time we return to the caller + + while true; do + local import_status + + if ! import_status=$(gitlab_import_status "$host" \ + "$token" \ + "$fork_id"); then + echo "Could not get import status of fork" 1>&2 + return 1 + fi + + if [[ "$import_status" == "none" ]] || + [[ "$import_status" == "finished" ]]; then + break + fi + + sleep 5 + done + + return 0 +} + +gitlab_create_branch() { + local host + local token + local project + local branch + local ref + + local id + local url + + host="$1" + token="$2" + project="$3" + branch="$4" + ref="$5" + + id=$(_gitlab_urlencode "$project") + data=$(json_make "id" "$id" "branch" "$branch" "ref" "$ref") + url="$host/api/v4/projects/$id/repository/branches" + + if ! _gitlab_post "$token" "$url" "$data"; then + return 1 + fi + + return 0 +} + +gitlab_project_get_branches() { + local host + local token + local project + + local url + local resp + + host="$1" + token="$2" + project="$3" + + project=$(_gitlab_urlencode "$project") + url="$host/api/v4/projects/$project/repository/branches" + + if ! resp=$(_gitlab_get "$token" "$url"); then + return 1 + fi + + if ! echo "$resp" | jq -r ".[].name"; then + return 1 + fi + + return 0 +} + +gitlab_get_project_id() { + local host + local token + local project + + local url + local resp + + host="$1" + token="$2" + project="$3" + + project=$(_gitlab_urlencode "$project") + url="$host/api/v4/projects/$project" + + if ! resp=$(_gitlab_get "$token" "$url"); then + return 1 + fi + + echo "$resp" | jq ".id" + return 0 +} + +gitlab_list_projects_page() { + local host + local token + local perpage + local page + + local url + local results + + host="$1" + token="$2" + perpage="$3" + page="$4" + + url="$host/api/v4/projects?simple=true&per_page=$perpage&page=$page" + + if ! results=$(_gitlab_get "$token" "$url"); then + return 1 + fi + + echo "$results" | jq -r ".[] | \"\(.id) \(.path_with_namespace)\"" + + return 0 +} + +gitlab_list_projects() { + local host + local token + + local page + local perpage + + host="$1" + token="$2" + + page=1 + perpage=50 + + while true; do + local projects + local num + + if ! projects=$(gitlab_list_projects_page "$host" \ + "$token" \ + "$perpage" \ + "$page"); then + return 1 + fi + + num=$(echo "$projects" | wc -l) + echo "$projects" + + if ((num < perpage)); then + break + fi + + ((page++)) + done + + return 0 +} + +# +# gitlab_merge_request - Create a new merge request +# +# SYNOPSIS +# gitlab_merge_request "$host" "$token" "$source" "$destination" +# "$title" "$assignee" "$description" +# +# DESCRIPTION +# The gitlab_merge_request function creates a new merge request from the +# repository:branch identified by $source to the repository:branch identified +# by $destination. The title, assignee, and description of the merge request +# will be set according to the $title, $assignee, and $description arguments, +# respectively. +# +gitlab_merge_request() { + local host + local token + local source + local destination + local title + local assignee + local description + + local source_name + local destination_name + local source_id + local destination_id + local source_branch + local destination_branch + local assignee_id + local url + + host="$1" + token="$2" + source="$3" + destination="$4" + title="$5" + assignee="$6" + description="$7" + + source_name="${source%:*}" + destination_name="${destination%:*}" + source_branch="${source##*:}" + destination_branch="${destination##*:}" + + if ! assignee_id=$(gitlab_get_user_id "$host" \ + "$token" \ + "$assignee"); then + echo "Invalid user: $assignee" 1>&2 + return 1 + fi + + if [ -z "$source_branch" ]; then + echo "Invalid source branch" 1>&2 + return 1 + fi + + if [ -z "$destination_branch" ]; then + echo "Invalid destination branch" 1>&2 + return 1 + fi + + if ! source_id=$(gitlab_get_project_id "$host" "$token" "$source_name"); then + echo "Could not get project id for $source_name" 1>&2 + return 1 + fi + + if ! destination_id=$(gitlab_get_project_id "$host" "$token" "$destination_name"); then + echo "Could not get project id for $destination_name" 1>&2 + return 1 + fi + + data=$(json_make "id" "$source_id" \ + "target_project_id" "$destination_id" \ + "source_branch" "$source_branch" \ + "target_branch" "$destination_branch" \ + "title" "$title" \ + "assignee_id" "$assignee_id" \ + "description" "$description") + url="$host/api/v4/projects/$source_id/merge_requests" + + if ! _gitlab_post "$token" "$url" "$data"; then + return 1 + fi + + return 0 +}