Миграция с Gitolite на GitLab с помощью Shell скрипта

от автора

Процесс миграции нередко представляет собой трудную задачу, особенно, когда объем информации, который необходимо перенести, настолько велик, что выгоднее становится его автоматизировать. Именно необходимость миграции с Gitolite на GitLab и побудила меня написать статью о моем опыте в данном вопросе.

Для миграции репозиториев я буду использовать компьютер с установленной операционной системой CentOS 7. На ней необходимо установить следующий набор приложений: git, ssh-agent, curl, jq, xargs.

Для начала работы нам необходимо получить ключ доступа к API GitLab. В веб-интерфейсе заходим в раздел настройки пользователя. Далее выбираем пункт меню «Access Tokens». В открывшейся форме необходимо указать наименование получаемого ключа, а также можно указать срок, до которого ключ будет активен. Ниже ставим галочку напротив пункта api и нажимаем кнопку «Create personal access token». Этот ключ будет применяться при отправке запросов в API GitLab.

  1. Подготовка рабочей среды

    Для того чтобы мы могли загружать код в репозиторий GitLab по SSH, необходимо сгенерировать пару приватного и публичного ключей, выполнить регистрацию публичного ключа в GitLab. Для регистрации публичного ключа в панели администратора GitLab «Admin area» заходим в раздел «Deploy keys» и нажимаем кнопку «New deploy key». В открывшейся форме необходимо указать наименование загружаемого ключа, а также сам публичный ключ. После добавления ключа, нам необходимо определить идентификатор данного ключа в системе GitLab. Чтобы определить его мы можем выполнить следующий команду в bash:

    curl -k -X GET --header "PRIVATE-TOKEN: <your private token>" <gitlab url>/api/v4/deploy_keys

    Примечание: Чтобы не вводить пароль от приватного SSH ключа при выполнении действий git с сервером, необходимо добавить данный ключ в ssh-agent. Для этого выполним следующие команды:

    eval `ssh-agent`

    ssh-add ~/.ssh/id_rsa

  2. Создание репозитория в GitLab с использованием API

    Чтобы создать репозиторий в GitLab, необходимо отправить POST запрос в API GitLab, передав параметры, согласно документации. Подробнее о Projects API GitLab читайте в документации на сайте GitLab.

    # $1 - gitlab remote url # $2 - api access token # $3 - remote repository name # $4 - remote repository namespace identifier initRemoteRepository() {     local response=`curl -s -k -X POST --header "Private-Token: $2" --data "name=$3&namespace_id=$4&visibility=private&description=$3" $1/api/v4/projects`     local id=`echo $response | jq -r ".id"`     echo "$id" }

    Функция initRemoteRepository возвращает идентификатор созданного репозитория. В дальнейшем идентификатор репозитория будет использоваться для установки прав доступа.
    Примечание: ключ –k необходим, если серверный сертификат GitLab является самозаверенным.

  3. Предоставление доступа на запись и чтение репозитория с использованием API

    Для того, чтобы выполнить запись в созданный репозиторий, необходимо выдать соответствующее право. Подробнее о Deploy Keys API GitLab читайте в документации на сайте GitLab.

    # $1 - remote url # $2 - api token # $3 - repository identifier # $4 - deploy key identifier setRepositoryWriteAccess() {     local response=`curl -s -k -X PUT --header "PRIVATE-TOKEN: $2" --header "Content-Type: application/json" --data '{"can_push": true}' $1/api/v4/projects/$3/deploy_keys/$4`     echo "$response" }

    Функция enableRepositoryDeployKey позволяет добавить публичный ключ с соответствующим идентификатором в список ключей соответствующего рапозитория.

    # $1 - remote url # $2 - api token # $3 - repository identifier # $4 - deploy key identifier enableRepositoryDeployKey() {     local response=`curl -s -k -X POST --header "Private-Token: $2" $1/api/v4/projects/$3/deploy_keys/$4/enable`     echo "$response" }

    Функция setRepositoryWriteAccess позволяет установить доступ на запись в конкретный репозиторий.

  4. Мигрирация репозиториев включая все ветки

    Основной скрипт перебирает список репозиториев, указанных в заранее заданном файле. Создается соответствующий репозиторий в GitLab, а также выдаются привелегии для чтения и записи. После подготовительных действий выполняется запрос веток данного репозитория. Далее выполняется переключение на очередную ветку репозитория и отправка в GitLab.

    # $1 - branch # $2 – separator getCanonicalBranchName() {     local branch=""     IFS=$2 read -r -a array <<< "$1"     length=${#array[@]}     for index in "${!array[@]}"     do         if [ $index -gt 0 ]; then             if [ $index -eq 1 ]; then                 local branch="${array[index]}"             else                 local branch="$branch/${array[index]}"             fi         fi     done     echo "$branch" } PSWD="$(dirname "$0")" cd $PSWD while IFS= read -r REPOSITORY do     echo "====================  <$REPOSITORY>  ===================="     GITOLITE_REPO=$GITOLITE$REPOSITORY     echo "Run cloning remote repository $GITOLITE_REPO"     git clone $GITOLITE_REPO     cd $REPOSITORY     GITLAB_REPO=$GITLAB$REPOSITORY.git     echo "Initialize remote gitgab repository $GITLAB_REPO"     PROJECT=$(initRemoteRepository $GITLAB_URL $API_TOKEN $REPOSITORY)     echo "Remote repository initialized identifier $PROJECT"     echo "Add deploy key and enable write access to remote repository $REPOSITORY ($PROJECT)"     response=$(enableRepositoryDeployKey $GITLAB_URL $API_TOKEN $PROJECT $DEPLOY_KEY_ID)     response =$(setRepositoryWriteAccess $GITLAB_URL $API_TOKEN $PROJECT $DEPLOY_KEY_ID)     echo "Add new remote url for repository $GITLAB_REPO"     git remote add gitlab $GITLAB_REPO     # push all branches     IFS_BACK=$IFS     IFS=$'\n'     branches=$(git branch -r)     for branchName in $branches; do         trimBranchName=`echo $branchName | xargs`         canonicalBranchName=$(getCanonicalBranchName $trimBranchName '/')         echo "$trimBranchName ($canonicalBranchName) init push"         git checkout -b $canonicalBranchName remotes/origin/$canonicalBranchName         git push -f gitlab $canonicalBranchName     done     IFS=$IFS_BACK     cd ..     rm -r -f $REPOSITORY     echo "====================  <$REPOSITORY>  ===================="     echo "" done < "$REPOSITORIES"

    Сохраняем файл скрипта и запускаем, при необходимости изменив права доступа. Пример вывода работы данного скрипта можно посмотреть ниже.

Полный текст скрипта

REPOSITORIES="<repositories_file>" GITOLITE="<gitolite_ssh_url>" GITLAB_URL="<gitlab_api_url>" GITLAB="<gitolite_ssh_url>" API_TOKEN="<api_access_token>" DEPLOY_KEY_ID="<deploy_key_identifier>" # $1 - gitlab remote url # $2 - api access token # $3 - remote repository name # $4 - remote repository namespace identifier initRemoteRepository() {         local response=`curl -s -k -X POST --header "Private-Token: $2" --data "name=$3&namespace_id=$4&visibility=private&description=$3" $1/api/v4/projects`         local id=`echo $response | jq -r ".id"`         echo "$id" } # $1 - remote url # $2 - api token # $3 - repository identifier # $4 - deploy key identifier setRepositoryWriteAccess() {     local response=`curl -s -k -X PUT --header "PRIVATE-TOKEN: $2" --header "Content-Type: application/json" --data '{"can_push": true}' $1/api/v4/projects/$3/deploy_keys/$4`     echo "$response" } # $1 - remote url # $2 - api token # $3 - repository identifier # $4 - deploy key identifier enableRepositoryDeployKey() {     local response=`curl -s -k -X POST --header "Private-Token: $2" $1/api/v4/projects/$3/deploy_keys/$4/enable`     echo "$response" } # $1 - branch # $2 – separator getCanonicalBranchName() {     local branch=""     IFS=$2 read -r -a array <<< "$1"     length=${#array[@]}     for index in "${!array[@]}"     do         if [ $index -gt 0 ]; then             if [ $index -eq 1 ]; then                 local branch="${array[index]}"             else                 local branch="$branch/${array[index]}"             fi         fi     done     echo "$branch" } PSWD="$(dirname "$0")" cd $PSWD while IFS= read -r REPOSITORY do     echo "====================  <$REPOSITORY>  ===================="     GITOLITE_REPO=$GITOLITE$REPOSITORY     echo "Run cloning remote repository $GITOLITE_REPO"     git clone $GITOLITE_REPO     cd $REPOSITORY     GITLAB_REPO=$GITLAB$REPOSITORY.git     echo "Initialize remote gitgab repository $GITLAB_REPO"     PROJECT=$(initRemoteRepository $GITLAB_URL $API_TOKEN $REPOSITORY)     echo "Remote repository initialized identifier $PROJECT"     echo "Add deploy key and enable write access to remote repository $REPOSITORY ($PROJECT)"     response=$(enableRepositoryDeployKey $GITLAB_URL $API_TOKEN $PROJECT $DEPLOY_KEY_ID)     response=$(setRepositoryWriteAccess $GITLAB_URL $API_TOKEN $PROJECT $DEPLOY_KEY_ID)     echo "Add new remote url for repository $GITLAB_REPO"     git remote add gitlab $GITLAB_REPO     # push all branches     IFS_BACK=$IFS     IFS=$'\n'     branches=$(git branch -r)     for branchName in $branches; do         trimBranchName=`echo $branchName | xargs`         canonicalBranchName=$(getCanonicalBranchName $trimBranchName '/')         echo "$trimBranchName ($canonicalBranchName) init push"         git checkout -b $canonicalBranchName remotes/origin/$canonicalBranchName         git push -f gitlab $canonicalBranchName     done     IFS=$IFS_BACK     cd ..     rm -r -f $REPOSITORY     echo "====================  <$REPOSITORY>  ===================="     echo "" done < "$REPOSITORIES"

Пример вывода в консоль

====================  <project>  ==================== Run cloning remote repository git@skynet-uro.bank.srv:project Cloning into 'project'... remote: Counting objects: 62, done. remote: Compressing objects: 100% (61/61), done. remote: Total 62 (delta 21), reused 0 (delta 0) Receiving objects: 100% (62/62), 15.57 KiB | 0 bytes/s, done. Resolving deltas: 100% (21/21), done. Current folder path: /app/migration/project Initialize remote gitgab repository git@127.0.0.1:project.git Remote repository initialized identifier 222 Enable deploy key write access to remote repository project (222) Set write access to remote repository project (222) Add new remote url for repository git@127.0.0.1:project.git Push to new remote gitlab repository git@127.0.0.1:project.git Counting objects: 55, done. Delta compression using up to 2 threads. Compressing objects: 100% (38/38), done. Writing objects: 100% (55/55), 14.65 KiB | 0 bytes/s, done. Total 55 (delta 18), reused 50 (delta 16) To git@127.0.0.1:project.git  * [new branch]      master -> master <--- gitlab/master (master) init push fatal: A branch named 'master' already exists. Everything up-to-date ---> gitlab/master (master) success pushed <--- origin/v0.0.0 (v0.0.0) init push Branch v0.0.0 set up to track remote branch v0.0.0 from origin. Switched to a new branch 'v0.0.0' Total 0 (delta 0), reused 0 (delta 0) remote: remote: To create a merge request for v0.0.0, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.0 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.0 -> v0.0.0 ---> origin/v0.0.0 (v0.0.0) success pushed <--- origin/v0.0.0-13 (v0.0.0-13) init push Branch v0.0.0-13 set up to track remote branch v0.0.0-13 from origin. Switched to a new branch 'v0.0.0-13' Total 0 (delta 0), reused 0 (delta 0) remote: remote: To create a merge request for v0.0.0-13, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.0-13 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.0-13 -> v0.0.0-13 ---> origin/v0.0.0-13 (v0.0.0-13) success pushed <--- origin/v0.0.0-15 (v0.0.0-15) init push Branch v0.0.0-15 set up to track remote branch v0.0.0-15 from origin. Switched to a new branch 'v0.0.0-15' Total 0 (delta 0), reused 0 (delta 0) remote: remote: To create a merge request for v0.0.0-15, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.0-15 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.0-15 -> v0.0.0-15 ---> origin/v0.0.0-15 (v0.0.0-15) success pushed <--- origin/v0.0.1 (v0.0.1) init push Branch v0.0.1 set up to track remote branch v0.0.1 from origin. Switched to a new branch 'v0.0.1' Total 0 (delta 0), reused 0 (delta 0) remote: remote: To create a merge request for v0.0.1, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.1 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.1 -> v0.0.1 ---> origin/v0.0.1 (v0.0.1) success pushed <--- origin/v0.0.1-11 (v0.0.1-11) init push Branch v0.0.1-11 set up to track remote branch v0.0.1-11 from origin. Switched to a new branch 'v0.0.1-11' Total 0 (delta 0), reused 0 (delta 0) remote: remote: To create a merge request for v0.0.1-11, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.1-11 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.1-11 -> v0.0.1-11 ---> origin/v0.0.1-11 (v0.0.1-11) success pushed <--- origin/v0.0.2 (v0.0.2) init push Branch v0.0.2 set up to track remote branch v0.0.2 from origin. Switched to a new branch 'v0.0.2' Total 0 (delta 0), reused 0 (delta 0) remote: remote: To create a merge request for v0.0.2, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.2 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.2 -> v0.0.2 ---> origin/v0.0.2 (v0.0.2) success pushed <--- origin/v0.0.3 (v0.0.3) init push Branch v0.0.3 set up to track remote branch v0.0.3 from origin. Switched to a new branch 'v0.0.3' Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 592 bytes | 0 bytes/s, done. Total 4 (delta 3), reused 1 (delta 0) remote: remote: To create a merge request for v0.0.3, visit: remote:   https://127.0.0.1:8082/project/merge_requests/new?merge_request%5Bsource_branch%5D=v0.0.3 remote: To git@127.0.0.1:project.git  * [new branch]      v0.0.3 -> v0.0.3 ---> origin/v0.0.3 (v0.0.3) success pushed ====================  <project>  ====================

ссылка на оригинал статьи https://habr.com/ru/post/492122/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *