Projet Migration Subversion vers Git

Ce projet visait à réaliser la migration des sources hébergées sur Subversion pour les anicennes applications d’APF France handicap vers un outil basé sur GIT comme Atlassian Bitbucket.

Conditions de migration

Afin de réaliser cette migration, un environnement spécifique a été mis en place en utilisant :

  • Machine virtuelle contenant Docker
  • Conteneur Docker contenant BitBucket
  • Conteneur Docker contenant les outils de migration (dont le client Google SDK pour accéder à la machine virtuelle Subversion) dans le but de faire le lien entre la machine virtuelle Subversion et le Conteneur Bitbucket

Environnement de Développement

Conteneur BitBucket

Création de l’instance Bitbucket :

docker volume create --name bitbucketVolume docker run -v bitbucketVolume:/var/atlassian/application-data/bitbucket --name="bitbucket" -d -p 7990:7990 -p 7999:7999 atlassian/bitbucket

Accès à l’instance : http://localhost:7990/

Conteneur GitSVN

Création du conteneur

# Lancement du conteneur Ubuntu
docker run -it --rm ubuntu /bin/bash

Installation GitSVN

apt update
apt-get install -y git-svn

Installation Google Cloud SDK

apt update && apt-get install -y apt-transport-https ca-certificates gnupg curl sudo && echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg  add - && apt-get update -y && apt-get install google-cloud-cli -y

Paramétrage Google Cloud SDK

Création du fichier de login :

gcloud iam service-accounts keys create key.json --iam-account=nom-prenom@projetgcloud.iam.gserviceaccount.com cat key.json

Récupération du contenu de la clé :

{
  "type": "service_account",
  "project_id": "123456789",
  "private_key_id": "660 KEY 66e",
  "private_key": "-----BEGIN PRIVATE KEY----- KEY -----END PRIVATE KEY-----\n",
  "client_email": "account-compute@developer.gserviceaccount.com",
  "client_id": "123456789",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/123456789-compute%40developer.gserviceaccount.com",
  "universe_domain": "googleapis.com"
}
gcloud auth activate-service-account --key-file=/root/key.json --project=projetgcloud
gcloud compute config-ssh --quiet
gcloud compute ssh userssh@server-subversion --zone=europe-zone --command="pwd"

Montage du dossier Subversion

mkdir /mnt/server-subversion

sshfs -o IdentityFile=~/.ssh/google_compute_engine \
userssh@server-subversion.europe-zone.projetgcloud:/home \
/mnt/server-subversion

Utilisation Git-SVN

Créer un répertoire de travail sur le poste local (avec Docker et Docker Compose installés)

Docker

Création d’une image Docker (Fichier Dockerfile) :

FROM ubuntu

ENV LC_ALL C.UTF-8

ARG GIT_USERNAME
ARG GIT_USEREMAIL

# Installation Google Cloud SDK Cli
RUN apt-get update -y && \
    apt-get install -y apt-transport-https ca-certificates gnupg curl sudo && \
    echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg  add - && \
    apt-get update -y && \
    apt-get install google-cloud-cli -y

# Installation GitSVN
RUN apt-get update && apt-get install --yes --no-install-recommends \
    git \
    subversion \
    git-svn \
    sshfs \
    && rm -rf /var/lib/apt/lists/*

# Paramétrage GCloud SDK
COPY ./key.json /root/key.json
RUN gcloud auth activate-service-account --key-file=/root/key.json --project=projetgcloud
RUN gcloud compute config-ssh --quiet
RUN gcloud compute ssh userssh@server-subversion --zone=europe-zone --command="pwd"

# Paramétrage Git
RUN mkdir /git
RUN git config --global user.name "$GIT_USERNAME"
RUN git config --global user.email "$GIT_USEREMAIL"
COPY ./users-backup.txt /git/users.txt
ENV SVN_AUTHORS="/git/users.txt"
ENV GIT_PATH="/git/projects"
VOLUME /git

# Installation du script de migration
COPY ./migrate.sh /usr/bin/svn2git

# Variables d'environnements
ENV SVN_BRANCHES="branches"
ENV SVN_TAGS="tags"
ENV SVN_TRUNK="trunk"
ENV SVN_BASE="svn+ssh://userssh@server-subversion.europe-zone.projetgcloud:/home/developer/svn/subversion/"
ENV GIT_PROJECT_NAME=""
ENV GIT_URL=""
WORKDIR /git

Script

Création du script de migration (Fichier migration.sh) :

#!/bin/bash

#STYLE_COLOR
RED='\033[0;31m';
LIGHT_GREEN='\e[1;32m';
NC='\033[0m' # No Color

echo "Liste des projets :"

function select_project () {
  projects=()
  
  for line in $(gcloud compute ssh userssh@server-subversion --command "ls -a ~/svn/subversion/"); do
      if [ "$line" != "." ] && [ "$line" != ".." ] ; then
        num=$((num+1))
        echo "[$num] $line"
        # projects[$num]="$line"
        # projects+=( "ROMAIN" )
        # projects+=( "$line" "ROMAIN" )
        projects+=( "$line" )
      fi
  done
  printf "Sélectionner un projet : " 
  read GIT_PROJECT
  GIT_PROJECT_NAME="${projects[($GIT_PROJECT-1)]}"
  GIT_URL="$GIT_URL$GIT_PROJECT_NAME"
  SVN_BASE="$SVN_BASE$GIT_PROJECT_NAME"
}

if [ "$GIT_PROJECT_NAME" == "" ]; then
  select_project
fi

echo "PROJET : $GIT_PROJECT_NAME"
echo "URL GIT : $GIT_URL"
echo "URL SVN : $SVN_BASE"

# Branchs Configuration
SVN_BRANCHES_URL=$SVN_BASE/$SVN_BRANCHES
SVN_TAGS_URL=$SVN_BASE/$SVN_TAGS
SVN_TRUNK_URL=$SVN_BASE/$SVN_TRUNK
GIT_PATH=$GIT_PATH/$GIT_PROJECT_NAME

# START
echo -e "${LIGHT_GREEN} [LOG] Starting migration of ${NC}" $SVN_TRUNK
echo -e "${LIGHT_GREEN} [LOG]   Using: ${NC}" $(git --version)
echo -e "${LIGHT_GREEN} [LOG]   Using: ${NC}"  $(svn --version | grep svn,)

# ETAPE 1 : Directories
echo
echo -e "${LIGHT_GREEN} [LOG] Step 01/08 Create Directories ${NC}" $GIT_PATH
echo -e "${LIGHT_GREEN} [LOG]   Create $GIT_PATH directory ${NC}"
if [ -d "$GIT_PATH" ]; then rm -Rf $GIT_PATH; fi
mkdir -p $GIT_PATH
cd $GIT_PATH

# ETAPE 2 : Authors
echo
echo -e "${LIGHT_GREEN} [LOG] Step 02/08 Getting authors ${NC}"
if [ ! -s "${SVN_AUTHORS}" ]; then
    echo -e "${LIGHT_GREEN} [LOG] Create $SVN_AUTHORS file from SVN Repository ${NC}"
    svn log -q $SVN_BASE | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2"@"$EMAIL">"}' | sort -u >> $SVN_AUTHORS
else
    echo -e "${LIGHT_GREEN} [LOG] Use $SVN_AUTHORS file ${NC}"
fi

# ETAPE 3 : GitSVN Clone
echo
echo -e "${LIGHT_GREEN} [RUN] Step 03/08"
echo 'git svn clone --authors-file='$SVN_AUTHORS' --trunk='$SVN_TRUNK' --branches='$SVN_BRANCHES' --tags='$SVN_TAGS $SVN_BASE $GIT_PATH
echo -e "${NC}"
git svn clone --authors-file=$SVN_AUTHORS --trunk=$SVN_TRUNK --branches=$SVN_BRANCHES --tags=$SVN_TAGS $SVN_BASE $GIT_PATH
echo
echo -e "${LIGHT_GREEN} [LOG] Step 04/08  Getting first revision ${NC}"
FIRST_REVISION=$( svn log -r 1:HEAD --limit 1 $SVN_BASE | awk -F '|' '/^r/ {sub("^ ", "", $1); sub(" $", "", $1); print $1}' )
echo
echo -e "${LIGHT_GREEN} [RUN] Step 05/08 ${NC}"
echo 'git svn fetch -'$FIRST_REVISION':HEAD'
git svn fetch -$FIRST_REVISION:HEAD
echo
echo -e "${LIGHT_GREEN} [RUN] Step 06/08 ${NC}"
echo 'git remote add origin '$GIT_URL
echo -e "${LIGHT_GREEN} [LOG] SKIP (Use local git) ${NC}"
git remote add origin $GIT_URL
echo
echo -e "${LIGHT_GREEN} [RUN] Step 07/08 ${NC}"
echo 'svn ls '$SVN_BRANCHES_URL
for BRANCH in $(svn ls $SVN_BRANCHES_URL); do
    echo git branch ${SVN_BRANCH%/} remotes/svn/${SVN_BRANCH%/}
    git branch ${SVN_BRANCH%/} remotes/svn/${SVN_BRANCH%/}
done
git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/origin/tags | grep -v "@" | cut -d / -f 3- |
while read ref
do
  echo git tag -a $ref -m 'import tag from svn'
  git tag -a $ref -m 'import tag from svn'
done
git for-each-ref --format="%(refname:short)" refs/remotes/origin/tags | cut -d / -f 1- |
while read ref
do
  git branch -rd $ref
done
echo
echo -e "${LIGHT_GREEN} [RUN] Step 08/08 [RUN] git push ${NC}"
echo -e "${LIGHT_GREEN} [LOG] SKIP (Use local git) ${NC}"
git push origin --all --force
git push origin --tags
echo 'Sucessufull.'

Mapping des utilisateurs

Création d’un fichier de mapping des utilisateurs extrait des dépôts actuels Subversion (Fichier users-backup.txt) :

user_git1 = user_subversion1
user_git2 = user_subversion2

Authentification Google Cloud

Création d’un fichier de clé pour l’authentification (Fichier key.json) :

  1. Aller sur la console d’administration Google Cloud (en étant authentifié avec les accès au projet apf_developpement) : https://console.cloud.google.com/home/dashboard
  2. Aller sur le menu de gestion des comptes de service : https://console.cloud.google.com/iam-admin/serviceaccounts
  3. Sélectionner un compte ou en créer un pour l’accès
  4. Créer une nouvelle clé pour ce compte
  5. Créer la clé au format JSON
  6. La clé est téléchargée sur le navigateur
  7. Copier le fichier dans le répertoire de travail et la renommer key.json

Utilisation du script

Variables (exemple d’utilisation) :

  • Projet Subversion à migrer : apf_projet1
  • URL Git de destination pour la migration : http://bitbucket:7990/scm/aw/apf_projet1.git
  • Volume local pour les projets GIT : ./git_new
  • Nom de l’utilisateur GIT : Romain BANAT
  • Email de l’utilisateur GIT : romain.banat@email.apf

Construction de l’image Docker

Lancer la commande :

docker image build -t svn2git:latest --build-arg="GIT_USERNAME=Romain BANAT" --build-arg="GIT_USEREMAIL=romain.banat@email.apf" .

Utilisation avec un fichier .env

Créer un fichier .env dans le répertoire de travail :

SVN_BASE="svn+ssh://userssh@server-subversion.europe-zone.projetgcloud/home/developer/svn/subversion/apf_projet1"
GIT_PROJECT_NAME="apf_projet1"
GIT_URL="http://bitbucket:7990/scm/aw/apf_projet1.git"

Lancer la commande :

docker run --rm -ti --env-file=./.env ` --mount type=bind,source="$(pwd)/users-backup.txt",target="/git/users.txt",readonly ` --volume="$(pwd)/git_new:/git/projects" ` svn2git svn2git

Utilisation avec sélection du projet (sans fichier .env)

Lancer la commande :

docker run --rm -ti ` --env GIT_URL="http://bitbucket:7990/scm/aw/" ` --mount type=bind,source="$(pwd)/users-backup.txt",target="/git/users.txt",readonly ` --volume="$(pwd)/git_new:/git/projects" ` svn2git svn2git

Instructions :

  • Saisir le numéro correspondant au projet Subversion (Exemple pour apf_projet1: [1])
  • Les variables deviennent :
    • SVN_BASE = svn+ssh://developer@instance-archive/home/developer/svn/subversion/apf_projet1
    • GIT_PROJECT_NAME = apf_projet1
    • GIT_URL = http://bitbucket:7990/scm/aw/apf_projet1.git

Environnement de test

Dans l’archive se trouve un fichier docker-compose.yml qui permet de lancer une instance Bitbucket pour pousser le dépôt GIT distant.

Pour le lancer il suffit de lancer la commande :

docker-compose up -d

Il faut ensuite se connecter à l’interface d’administration (création d’un accès à la version d’évaluation avec le compte Atlassian) : http://localhost:7990/

Il faut ensuite créer un projet puis un dépôt. Pour accéder à ce dépôt depuis le conteneur docker, il faut modifier la commande d’exécution en ajoutant le paramètre --link bitbucket:bitbucket comme suit :

docker run --rm -ti --link bitbucket:bitbucket ` --env GIT_URL="http://bitbucket:7990/scm/aw/" ` --mount type=bind,source="$(pwd)/users-backup.txt",target="/git/users.txt",readonly ` --mount type=bind,source="$(pwd)/migrate.sh",target="/usr/bin/svn2git" ` --volume="$(pwd)/git_new:/git/projects" ` svn2git svn2git