diff --git a/.gitignore b/.gitignore index 11a595e..0f5bd52 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /runme.sh +/init.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1462c4 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +Migrate to gitea + +Usage: + +With key over ssh +``` +./migrate.sh \ + --admin gitea_user \ + --url http://gitea.example.com \ + --token TOKEN_VALUE \ + --pushurl ssh://git@gitea.example.com:3022 \ + "ARG 1" ... "ARG N" +``` +With password over http +``` +./migrate.sh \ + --admin gitea_user \ + --url http://gitea.example.com \ + --token TOKEN_VALUE \ + --password PASSWORD_VALUE \ + "ARG 1" ... "ARG N" +``` +ARG format: ``"/path/to/repo:ORG_NAME:REPO_NAME"`` + +examples: +``` + # only path to repo - owner will be $GITEA_ADMIN_USERNAME + # name will be result of $(basename "/path/to/repo" ".git") + /path/to/repo + + # org only + /path/to/repo:ORG_NAME + + # org and name + /path/to/repo:ORG_NAME:REPO_NAME + + # name only - owner will be $GITEA_ADMIN_USERNAME + /path/to/repo::REPO_NAME +``` +configuration can be done with file ``./init.sh`` +``` +GITEA_ADMIN_USERNAME="$USER" +GITEA_SERVER_PUSH_URL="ssh://git@gitea.example.com:3022" +GITEA_ADMIN_TOKEN="TOKEN_VALUE" +GITEA_SERVER_URL="http://gitea.example.com" +``` + +Required commands: +``` +git +curl +``` \ No newline at end of file diff --git a/bitbucket/.gitignore b/bitbucket/.gitignore new file mode 100644 index 0000000..fa85c00 --- /dev/null +++ b/bitbucket/.gitignore @@ -0,0 +1 @@ +/init.sh \ No newline at end of file diff --git a/bitbucket/migrate_to_gitea.sh b/bitbucket/migrate_to_gitea.sh new file mode 100755 index 0000000..3c3980b --- /dev/null +++ b/bitbucket/migrate_to_gitea.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -euf -o pipefail + +curp="$(cd "$(dirname "$0")" && pwd)" + +bb_projects() { + # get all projects + curl --silent \ + -u ${BB_ADMIN_USERNAME}:${SRC_TOKEN} \ + "${BB_SERVER_URL}/rest/api/1.0/projects/" | + jq -r '.values[] | .key' | + tr '[:upper:]' '[:lower:]' +} +bb_repos() { + [ -z "$1" ] && return + # by project + curl --silent \ + -u ${BB_ADMIN_USERNAME}:${SRC_TOKEN} \ + "${BB_SERVER_URL}/rest/api/1.0/projects/$1/repos/" | + jq -r '.values[].name' | + tr '[:upper:]' '[:lower:]' +} + +# unset all GITEA_* variables +unset "${!GITEA_@}" + +[ -f "$curp/init.sh" ] && source "$curp/init.sh" + +[ -z "${SRC_TOKEN}" ] && { + echo "SRC_TOKEN is empty" 1>&2 + exit 1 +} + +: ${BB_ADMIN_USERNAME:=$USER} +: ${BB_ADMIN_PASSWORD:=} + +ARGS=() +pos_arg_flag=0 +while [[ $# -gt 0 ]]; do + if [ "${pos_arg_flag}" -eq "1" ]; then + ARGS+=("$1") + shift 1 + continue + fi + case "$1" in + --url | -u) + BB_SERVER_URL="$2" + shift 2 + ;; + --token | -t) + SRC_TOKEN="$2" + shift 2 + ;; + --admin | -a) + BB_ADMIN_USERNAME="$2" + shift 2 + ;; + --password | -p) + BB_ADMIN_PASSWORD="$2" + shift 2 + ;; + --pushurl | -s) + BB_SERVER_PUSH_URL="$2" + shift 2 + ;; + --) + pos_arg_flag=1 + shift 1 + ;; + --*=*) + set -- "$(cat - <<<"$1" | cut -d '=' -f1)" "$(cat - <<<"$1" | cut -d '=' -f2-)" "${@:3}" + ;; + *) + ARGS+=("$1") + shift 1 + ;; + esac +done + +[ -z "${BB_SERVER_PUSH_URL}" ] && + BB_SERVER_PUSH_URL="$(echo "${BB_SERVER_URL}" | perl -pe "s#(https?://)#\${1}${BB_ADMIN_USERNAME}:${BB_ADMIN_PASSWORD}@#")/scm/" + +# mirror all remote repos and get changes +for prj in $(bb projects); do + for repo in $(bb repo "$prj"); do + if [ -d "$curp/$prj/$repo" ]; then + ( + cd "$curp/$prj/$repo" + git remote update + ) + else + git clone --mirror "${BB_SERVER_PUSH_URL}/$prj/$repo" "$prj/$repo" + fi + done +done + +# migrate all repos to gitea +"$curp/../migrate.sh" "$@" diff --git a/include/bitbucket.sh b/include/bitbucket.sh new file mode 100644 index 0000000..7d21de2 --- /dev/null +++ b/include/bitbucket.sh @@ -0,0 +1,21 @@ +bitbucket_projects() { + url="$1" + username="$2" + token="$3" + # get all projects + curl --silent \ + -u ${username}:${token} \ + "${url}/rest/api/1.0/projects/" | + jq -r '.values[] | .key' | + tr '[:upper:]' '[:lower:]' +} +bitbucket_repos() { + url="$1" + username="$2" + token="$3" + curl --silent \ + -u ${username}:${token} \ + "${url}/rest/api/1.0/projects/$1/repos/" | + jq -r '.values[].name' | + tr '[:upper:]' '[:lower:]' +} \ No newline at end of file diff --git a/include/gitea.sh b/include/gitea.sh new file mode 100644 index 0000000..7f214d5 --- /dev/null +++ b/include/gitea.sh @@ -0,0 +1,85 @@ +gitea_endpoint() { + UTYPE="$1" + if [ "${UTYPE}" == "org" ]; then + API_ENDPOINT="orgs" + elif [ "${UTYPE}" == "user" ]; then + API_ENDPOINT="admin/users" + else + echo "unknown type '${UTYPE}'" 1>&2 + exit 1 + fi + echo -n "${API_ENDPOINT}" +} + +gitea_users() { + GITEA_SERVER_API_URL="${GITEA_SERVER_URL}/api/v1" + curl --silent \ + -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \ + -H "accept: application/json" \ + -H "Content-Type: application/json" \ + "${GITEA_SERVER_API_URL}/$(gitea_endpoint "${UTYPE:-user}")" \ + "$@" +} + +gitea_repos() { + GITEA_SERVER_API_URL="${GITEA_SERVER_URL}/api/v1" + owner="$1" + curl --silent \ + -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \ + -H "accept: application/json" \ + -H "Content-Type: application/json" \ + "${GITEA_SERVER_API_URL}/$(gitea_endpoint "${UTYPE:-user}")/${owner}/repos" \ + "${@:2}" +} + +gitea_create_org() { + name="$1" + id=$(UTYPE=org gitea_users -X POST -d "{ \"username\": \"$name\", \"visibility\": \"limited\"}" | jq -r '.id') + [[ "$id" != "null" ]] && sleep 1 || true +} +gitea_create_user() { + name="$1" + id=$(gitea_users -X POST -d "{ \"username\": \"$name\", \"visibility\": \"limited\"}" | jq -r '.id') + [[ "$id" != "null" ]] && sleep 1 || true +} + +gitea_create_repo() { + owner="$1" + repo="$2" + repoid=$( + UTYPE=$3 gitea_repos \ + "$owner" \ + -X POST \ + -d "{\"auto_init\": false, \"private\": true, \"name\": \"$repo\"}" | + jq -r '.id' + ) + [[ "$repoid" != "null" ]] && sleep 5 || true +} + +gitea_create_repo_user() { + gitea_create_repo "$1" "$2" "user" +} + +gitea_create_repo_org() { + gitea_create_repo "$1" "$2" "opg" +} + +gitea_add_key_ssh() { + GITEA_SERVER_API_URL="${GITEA_SERVER_URL}/api/v1" + owner="$1" + pubkey_val="$(cat "${2}" | cut -d ' ' -f1,2 | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))')" + pubkey_name="$(cat "${2}" | cut -d ' ' -f3)" + curl --silent -X POST "${GITEA_SERVER_API_URL}/admin/users/${owner}/keys" \ + -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \ + -H "accept: application/json" -H "Content-Type: application/json" \ + -d "{ \"key\": ${pubkey_val}, \"read_only\": false ,\"title\":\"${pubkey_name}\"}" + +} +gitea_add_key_gpg() { + GITEA_SERVER_API_URL="${GITEA_SERVER_URL}/api/v1" + gpg_key="$(gpg --export --armor "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))')" + curl --silent -X POST "${GITEA_SERVER_API_URL}/user/gpg_keys" \ + -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \ + -H "accept: application/json" -H "Content-Type: application/json" \ + -d "{ \"armored_public_key\": ${gpg_key}" +} \ No newline at end of file diff --git a/migrate.sh b/migrate.sh index 98e6792..5f67a04 100755 --- a/migrate.sh +++ b/migrate.sh @@ -4,23 +4,11 @@ set -euf -o pipefail curp="$(cd "$(dirname "$0")" && pwd)" -create_org() { - orgid=$(curl --silent -X POST "${GITEA_SERVER_API_URL}/orgs" \ - -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \ - -H "accept: application/json" \ - -H "Content-Type: application/json" \ - -d "{ \"username\": \"$1\", \"visibility\": \"limited\"}" | jq -r '.id') - [[ "$orgid" != "null" ]] && sleep 1 || true -} +# unset all GITEA_* variables +unset "${!GITEA_@}" -create_repo() { - repoid=$(curl --silent -X POST "${GITEA_SERVER_API_URL}$2" \ - -H "Authorization: token ${GITEA_ADMIN_TOKEN}" \ - -H "accept: application/json" \ - -H "Content-Type: application/json" \ - -d "{\"auto_init\": false, \"private\": true, \"name\": \"$1\"}" | jq -r '.id') - [[ "$repoid" != "null" ]] && sleep 5 || true -} +[ -f "$curp/init.sh" ] && source "$curp/init.sh" +source "${curp}/include/gitea.sh" # defaults : ${GITEA_ADMIN_USERNAME:=$USER} @@ -76,12 +64,6 @@ done GITEA_SERVER_API_URL="${GITEA_SERVER_URL}/api/v1" -# repo examples -# /path/to/repo:orgname ( orgname only ) -# /path/to/repo::reponame ( repo name only ) -# /path/to/repo:orgname:reponame ( all options passed ) -# /path/to/repo ( use name from path, no org ) - # init repos for repo in "${ARGS[@]}"; do repoPath="$(echo "$repo" | cut -d ':' -f1 | tr -d '[:space:]')" @@ -91,14 +73,16 @@ for repo in "${ARGS[@]}"; do if [ -z "$repoOrg" ]; then repoAPIEP="/user/repos" repoOwner="${GITEA_ADMIN_USERNAME}" + UTYPE=user else - create_org "$repoOrg" + gitea_create_org "$repoOrg" repoAPIEP="/org/$repoOrg/repos" repoOwner="$repoOrg" + UTYPE=org fi - create_repo "${repoName}" "${repoAPIEP}" 1>&2 + gitea_create_repo "${repoOwner}" "${repoName}" "${UTYPE}" 1>&2 ( cd "$repoPath" - git push "${GITEA_SERVER_PUSH_URL}/${repoOwner}/${repoName}.git" master + git push "${GITEA_SERVER_PUSH_URL}/${repoOwner}/${repoName}.git" --mirror ) done