#!/usr/bin/env bash # --- # This file is automatically generated from gitea.md - DO NOT EDIT # --- # MIT License # # Copyright (c) 2017 PJ Eby # # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software # is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #!/usr/bin/env bash realpath.location(){ realpath.follow "$1"; realpath.absolute "$REPLY" ".."; } realpath.resolved(){ realpath.follow "$1"; realpath.absolute "$REPLY"; } realpath.dirname() { if [[ $1 =~ /+[^/]+/*$ ]]; then REPLY="${1%${BASH_REMATCH[0]}}"; REPLY=${REPLY:-/}; else REPLY=.; fi } realpath.basename(){ if [[ $1 =~ /*([^/]+)/*$ ]]; then REPLY="${BASH_REMATCH[1]}"; else REPLY=/; fi } realpath.follow() { local target while [[ -L "$1" ]] && target=$(readlink -- "$1"); do realpath.dirname "$1" # Resolve relative to symlink's directory [[ $REPLY != . && $target != /* ]] && REPLY=$REPLY/$target || REPLY=$target # Break out if we found a symlink loop for target; do [[ $REPLY == "$target" ]] && break 2; done # Add to the loop-detect list and tail-recurse set -- "$REPLY" "$@" done REPLY="$1" } realpath.absolute() { REPLY=$PWD; local eg=extglob; ! shopt -q $eg || eg=; ${eg:+shopt -s $eg} while (($#)); do case $1 in /*) REPLY=/; set -- "${1##+(/)}" "${@:2}" ;; */*) set -- "${1%%/*}" "${1##${1%%/*}+(/)}" "${@:2}" ;; ''|.) shift ;; ..) realpath.dirname "$REPLY"; shift ;; *) REPLY="${REPLY%/}/$1"; shift ;; esac; done; ${eg:+shopt -u $eg} } realpath.canonical() { realpath.follow "$1"; set -- "$REPLY" # $1 is now resolved realpath.basename "$1"; set -- "$1" "$REPLY" # $2 = basename $1 realpath.dirname "$1" [[ $REPLY != "$1" ]] && realpath.canonical "$REPLY"; # recurse unless root realpath.absolute "$REPLY" "$2"; # combine canon parent w/basename } realpath.relative() { local target="" realpath.absolute "$1"; set -- "$REPLY" "${@:2}"; realpath.absolute "${2-$PWD}" X while realpath.dirname "$REPLY"; [[ "$1" != "$REPLY" && "$1" == "${1#${REPLY%/}/}" ]]; do target=../$target done [[ $1 == "$REPLY" ]] && REPLY=${target%/} || REPLY="$target${1#${REPLY%/}/}" REPLY=${REPLY:-.} } # For documentation, see https://github.com/bashup/loco set -euo pipefail fn_exists() { declare -F -- "$1"; } >/dev/null fn_copy() { REPLY="$(declare -f "$1")"; eval "$2 ${REPLY#$1}"; } findup() { walkup "${1:-$PWD}" reply_if_exists "${@:2}"; } reply_if_exists() { local pat dir=$1 IFS= ; shift for pat; do for REPLY in ${dir%/}/$pat; do [[ -f "$REPLY" ]] && return 0; done done return 1 } walkup() { realpath.absolute "$1" until set -- "$REPLY" "${@:2}"; "$2" "$1" "${@:3}"; do [[ "$1" != "/" ]] || return 1; realpath.dirname "$1" done } _loco_usage() { loco_error "Usage: $LOCO_COMMAND command args..."; } _loco_error() { echo "$@" >&2; exit 64; } _loco_cmd() { REPLY="$LOCO_NAME.$1"; } _loco_exec() { loco_error "Unrecognized command: $1"; } _loco_exists() { type -t "$1"; } >/dev/null _loco_do() { [[ "${1-}" ]] || loco_usage # No command given, exit w/usage REPLY=""; loco_cmd "$1"; local cmd="$REPLY" [[ "$cmd" ]] || loco_usage # Unrecognized command, exit w/usage if loco_exists "$cmd"; then # Command, alias, function, or builtin exists shift; "$cmd" "$@" else # Invoke the default command interpreter loco_exec "$@" fi } _loco_findproject() { # shellcheck disable=SC2015 # plain var assign can't be false findup "$LOCO_PWD" "${LOCO_FILE[@]}" && LOCO_PROJECT=$REPLY || loco_error "Can't find $LOCO_FILE here"; } _loco_preconfig() { true; } _loco_postconfig() { true; } _loco_findroot() { realpath.dirname "$LOCO_PROJECT"; LOCO_ROOT=$REPLY; } _loco_loadproject() { cd "$LOCO_ROOT"; $LOCO_LOAD "$1"; } _loco_site_config() { source "$1"; } _loco_user_config() { source "$1"; } # Find our configuration, exposing relevant paths and defaults # shellcheck disable=SC2034 # some vars are only used by extending scripts _loco_config() { LOCO_ARGS=("$@") loco_preconfig "$@" ${LOCO_COMMAND:+:} realpath.basename "$LOCO_SCRIPT"; LOCO_COMMAND="${LOCO_COMMAND-$REPLY}" LOCO_NAME="${LOCO_NAME-${LOCO_COMMAND}}" LOCO_PWD="${LOCO_PWD-$PWD}" LOCO_SITE_CONFIG="${LOCO_SITE_CONFIG-/etc/$LOCO_NAME/config}" [ -f "$LOCO_SITE_CONFIG" ] && loco_site_config "$LOCO_SITE_CONFIG" LOCO_RC="${LOCO_RC-.${LOCO_NAME}rc}" LOCO_USER_CONFIG="${LOCO_USER_CONFIG-$HOME/$LOCO_RC}" [ -f "$LOCO_USER_CONFIG" ] && loco_user_config "$LOCO_USER_CONFIG" [[ ${LOCO_FILE-} ]] || LOCO_FILE=(".$LOCO_NAME") LOCO_LOAD="${LOCO_LOAD-source}" loco_postconfig "$@" } _loco_main() { loco_config "$@" fn_exists "$LOCO_NAME" || eval "$LOCO_NAME() { loco_do \"\$@\"; }" ${LOCO_PROJECT:+:} loco_findproject "$@" ${LOCO_ROOT:+:} loco_findroot "$@" loco_loadproject "$LOCO_PROJECT" loco_do "$@" } # Initialize default function implementations for f in $(compgen -A function _loco_); do fn_exists "${f#_}" || fn_copy "$f" "${f#_}" done # Clear all LOCO_* variables before beginning for lv in ${!LOCO_@}; do unset "$lv"; done LOCO_SCRIPT=$0 GITEA_CREATE=() gitea.--with() { local GITEA_CREATE=(${GITEA_CREATE[@]+"${GITEA_CREATE[@]}"} "${@:1:2}") gitea "${@:3}" } gitea.--description() { gitea --with description "$1" "${@:2}"; } gitea.--desc() { gitea --description "$@"; } gitea.-d() { gitea --description "$@"; } gitea.--public() { gitea --with private= false "$@"; } gitea.-p() { gitea --public "$@"; } gitea.--private() { gitea --with private= true "$@"; } gitea.-P() { gitea --private "$@"; } gitea.--repo() { split_repo "$1"; local PROJECT_ORG="${REPLY[1]}" PROJECT_NAME="${REPLY[2]}"; gitea "${@:2}" } gitea.-r() { gitea --repo "$@"; } gitea.--tag() { local PROJECT_TAG="$1"; gitea "${@:2}" ; } gitea.-t() { gitea --tag "$@"; } gitea.exists() { split_repo "$1" && auth api 200 404 "repos/$REPLY" ; } gitea.delete() { split_repo "$1" && auth api 204 "" "/repos/$REPLY" -X DELETE; } gitea.deploy-key() { split_repo "$1" jmap title "$2" key "$3" read_only= "${4-true}" | json auth api 201 "" /repos/$REPLY/keys } gitea.new() { split_repo "$1"; local org="${REPLY[1]}" repo="${REPLY[2]}" if [[ $org == "$GITEA_USER" ]]; then org=user; else org="org/$org"; fi jmap name "$repo" ${GITEA_CREATE[@]+"${GITEA_CREATE[@]}"} "${@:2}" | json api "200|201" "" "$org/repos?token=$GITEA_API_TOKEN" [[ ! "${GITEA_DEPLOY_KEY-}" ]] || gitea deploy-key "$1" "${GITEA_DEPLOY_KEY_TITLE:-default}" \ "$GITEA_DEPLOY_KEY" "${GITEA_DEPLOY_READONLY:-true}" } gitea.vendor-merge() { :; } branch-exists() { git rev-parse --verify "$1" &>/dev/null; } gitea.vendor() { [[ ! -d .git ]] || loco_error ".git repo must not exist here"; [[ -n "${PROJECT_ORG-}" ]] || PROJECT_ORG=$GITEA_USER [[ -n "${PROJECT_NAME-}" ]] || loco_error "PROJECT_NAME not set" local GITEA_GIT_URL=${GITEA_GIT_URL-$GITEA_URL} [[ $GITEA_GIT_URL == *: ]] || GITEA_GIT_URL="${GITEA_GIT_URL%/}/"; local GIT_REPO="$GITEA_GIT_URL$PROJECT_ORG/$PROJECT_NAME.git" if gitea exists "$PROJECT_ORG/$PROJECT_NAME"; then [[ -n "${PROJECT_TAG-}" ]] || loco_error "PROJECT_TAG not set" local MESSAGE="Vendor update to $PROJECT_TAG" git clone --bare -b vendor "$GIT_REPO" .git || git clone --bare "$GIT_REPO" .git # handle missing-branch case else local MESSAGE="Initial import" gitea new "$PROJECT_ORG/$PROJECT_NAME" "$@" git clone --bare "$GIT_REPO" .git fi git config --local --bool core.bare false git config --local user.name "${GITEA_VENDOR_NAME:-Vendor}" git config --local user.email "${GITEA_VENDOR_EMAIL:-vendor@example.com}" git add .; git commit -m "$MESSAGE" # commit to master or vendor branch-exists vendor || git checkout -b vendor # split off vendor branch if needed git push --all [[ -z "${PROJECT_TAG-}" ]] || { git tag "vendor-$PROJECT_TAG"; git push --tags; } git checkout master gitea vendor-merge } split_repo() { [[ "$1" == */* ]] || set -- "$GITEA_USER/$1"; REPLY=("$1" "${1%/*}" "${1##*/}") } jmap() { local filter='{}' opts=(-n) arg=1 while (($#)); do if [[ $1 == *= ]]; then filter+=" | .$1 $2" else filter+=" | .$1=\$__$arg" opts+=(--arg "__$arg" "$2") ((arg++)) fi shift 2 done jq "${opts[@]}" "$filter" } json() { "$@" -X POST -H "Content-Type: application/json" -d @-; } auth() { "$@" -H "Authorization: token $GITEA_API_TOKEN"; } api() { if ! shopt -q extglob; then # extglob is needed for pattern matching local r=0; shopt -s extglob; api "$@" || r=$?; shopt -u extglob; return $r fi read-curl --silent --write-out '%{http_code}' --output /dev/null "${@:4}" "${GITEA_URL%/}/api/v1/${3#/}" local true="@($1)" false="@($2)" # shellcheck disable=2053 # glob matching is what we want! if [[ $REPLY == $true ]]; then return 0; elif [[ $2 && $REPLY == $false ]]; then return 1 else case $REPLY in 000) fail "Invalid server response: check GITEA_URL" 78 ;; # EX_PROTOCOL 401) fail "Unauthorized: check GITEA_USER and GITEA_API_TOKEN" 77 ;; # EX_NOPERM 404) fail "Server returned 404 Not Found" 69 ;; # EX_UNAVAILABLE *) fail "Failure: HTTP code $REPLY (expected $1${2:+ or $2})" 70 ;; # EX_SOFTWARE esac fi } read-curl() { REPLY=$(curl "$@"); } fail() { echo "$1" >&2; return "${2-64}"; } loco_preconfig() { LOCO_SCRIPT=$BASH_SOURCE LOCO_SITE_CONFIG=/etc/gitea-cli/gitearc LOCO_USER_CONFIG=$HOME/.config/gitearc LOCO_NAME=gitea LOCO_FILE=(.gitearc) PROJECT_NAME="${PROJECT_NAME-$(basename "$PWD")}" } loco_findproject() { findup "$LOCO_PWD" "${LOCO_FILE[@]}" && LOCO_PROJECT=$REPLY || LOCO_PROJECT=/dev/null } loco_findroot() { LOCO_ROOT=$LOCO_PWD; } if [[ $0 == "${BASH_SOURCE-}" ]]; then loco_main "$@"; fi