Site icon

Gitlab vers Github – Importer ses projets Gitlab dans Github

Je vous présente notre projet gitlab-to-github et surtout je vous explique comment je l’ai construit.

Vous trouverez le code du projet ICI.

A quoi sert le projet ?

Tout simplement pour envoyer tous ses projets gitlab sur son compte Github.

Pourquoi ?

Si comme moi tout le monde te demande ton compte Github pour voir tes projets mais tous tes repos sont sur ton gitlab 😢

Comment concevoir le plan d’action

De quoi aura-t-on besoin ?

Comment le programme fonctionne ?

Pour push tous mes projets Gitlab vers github, mon script va devoir suivre ces étapes :

  1. Récupérer mes repositories gitlab
  2. Pour chaque repos gitlab :
    1. via l’API de Github créer un projet avec le même nom que le repo gitlab
    2. cloner le repo gitlab dans un dossier temporaire puis le push vers le nouveau projet github

Bonus : Si la variable import_private_repo est True alors on import aussi les projets privés

Requête API

Le programme est basé principalement sur des requêtes API.

Pour avoir le droit de lancer des requête API sur nos comptes il faut alors qu’on crée une clé API pour chaque compte avec les scopes (périmètres) qui vont bien.

Création clé API Gitlab

Une fois connecté sur ton compte gitlab, aller sur https://gitlab.com/-/profile/personal_access_tokens

Comme sur l’impression d’écran ci-dessus :

  1. Donnes un nom à ton token
  2. Une date d’expiration (pas obligé)
  3. coches read_api
  4. Clique sur Create personnal access token

En retour Gitlab te fournit la clé de ton Token, garde le de côté on en aura besoin très bientôt.

Création clé API Github

Une fois connecté à Github aller sur https://github.com/settings/tokens.

Tu arriveras ici :

Cliques sur Generate new token.

Tu seras dirigé vers la page suivante :

  1. Dans Note tu peux donner une description au token
  2. Coche repo et project
  3. Generate Token

La clé est alors généré, garde la de côté.

Conception du projet

Composition

Variables

Cacher mes petits secrets

Durant ma phase de test je stockais les tokens dans les fichiers .token_gitlab et .token_github.

Je les chargeais dans config.py comme ça :

token_file = open(".token_gitlab")
token_gitlab = token_file.read()

token_file = open(".token_github")
token_github = token_file.read()

Puis une fois la phase de dev terminé, j’ai modifié le fichier config.py pour récupérer mes variables sensible via des variables d’environnements.

import os

try:
    token_file = open(".token_gitlab")
    token_gitlab = token_file.read()
except:
    if os.environ.get('token_gitlab') is not None:
        token_gitlab = os.environ.get('token_gitlab')
    else:
        print(".Token_gitlab not found")
        raise SystemExit

En gros ce bout de code vérifie si le fichier .token_gitlab est présent, sinon si la variable d’environnement token_gitlab existe il le charge dans ma variable token_gitlab.

Et si ni le fichier .token_gitlab ni la variable d’env token_gitlab ne sont trouvés alors le programme s’arrête.

Mes amis les fonctions

Le fichier fonctions.py entier :

from config import token_gitlab, token_github, gitlab_url, github_url, github_username, gitlab_username
import requests
from requests.auth import HTTPBasicAuth
import unidecode
import tempfile
from git import Repo



def get_repos_gitlab():
    header = {"PRIVATE-TOKEN": token_gitlab}
    r = requests.get(gitlab_url,headers=header)
    return r.json()

def get_repos_public_gitlab():
    projects = get_repos_gitlab()
    repo_public = list()
    for project in projects:
        if project['visibility'] == "public":
            repo_public.append(project)
    return repo_public

def get_repos_private_gitlab():
    projects = get_repos_gitlab()
    repo_private = list()
    for project in projects:
        if project['visibility'] == "private":
            repo_private.append(project)
    return repo_private

def get_repos_github():
    request = requests.get(github_url,auth = HTTPBasicAuth(github_username, token_github)).json()
    return request

def create_repo_github(nom_repo):
    request = requests.post(f"https://api.github.com/user/repos",auth = HTTPBasicAuth(github_username, token_github),json={"name":unidecode.unidecode(nom_repo)}).json()
    return request

def create_private_repo_github(nom_repo):
    request = requests.post(f"https://api.github.com/user/repos",auth = HTTPBasicAuth(github_username, token_github),json={"name":unidecode.unidecode(nom_repo),"private":"True"}).json()
    return request


def delete_repo_github(nom_repo):#not used
    delete_repo = requests.delete(f"https://api.github.com/repos/babidi34/{nom_repo}",auth = HTTPBasicAuth(github_username, token_github)).json()

def cloneGitlab_and_pushGithub(repo):
    with tempfile.TemporaryDirectory() as tmpdirname:
        print('created temporary directory', tmpdirname)
        if repo['visibility'] == 'private':
            cloned_repo = Repo.clone_from(f"https://{gitlab_username}:{token_gitlab}@gitlab.com/{gitlab_username}/{unidecode.unidecode(repo['path'])}.git", tmpdirname)
        else:
            cloned_repo = Repo.clone_from(repo['web_url'], tmpdirname)
        remote = cloned_repo.create_remote("new", url=f"https://{github_username}:{token_github}@github.com/{github_username}/{unidecode.unidecode(repo['path'])}.git")
        remote.push(refspec='{}:{}'.format(repo['default_branch'], repo['default_branch']))

On va déchiffrer un peu tout ça :

get_repos_gitlab()

Retourne tout les repositories de ton Gitlab.

def get_repos_gitlab():
    header = {"PRIVATE-TOKEN": token_gitlab}
    r = requests.get(gitlab_url,headers=header)
    return r.json()

get_repos_public_gitlab()

Retourne les repos public gitlab.

def get_repos_public_gitlab():
    projects = get_repos_gitlab()
    repo_public = list()
    for project in projects:
        if project['visibility'] == "public":
            repo_public.append(project)
    return repo_public

get_repos_private_gitlab()

Retourne les repos privées gitlab.

def get_repos_private_gitlab():
    projects = get_repos_gitlab()
    repo_private = list()
    for project in projects:
        if project['visibility'] == "private":
            repo_private.append(project)
    return repo_private

get_repos_github()

Récupère les repos github.

def get_repos_github():
    request = requests.get(github_url,auth = HTTPBasicAuth(github_username, token_github)).json()
    return request

create_repo_github(nom_repo)

Créer un repo public sur son compte Github.

def create_repo_github(nom_repo):
    request = requests.post(f"https://api.github.com/user/repos",auth = HTTPBasicAuth(github_username, token_github),json={"name":unidecode.unidecode(nom_repo)}).json()
    return request

create_private_repo_github(nom_repo)

Créer un repo privé sur son Github.

def create_private_repo_github(nom_repo):
    request = requests.post(f"https://api.github.com/user/repos",auth = HTTPBasicAuth(github_username, token_github),json={"name":unidecode.unidecode(nom_repo),"private":"True"}).json()
    return request

delete_repo_github(nom_repo)

Supprime un repo github.

def delete_repo_github(nom_repo):#not used
    delete_repo = requests.delete(f"https://api.github.com/repos/babidi34/{nom_repo}",auth = HTTPBasicAuth(github_username, token_github)).json()

cloneGitlab_and_pushGithub(repo)

Cette fonction prend en input un objet repo, un JSON et va le push sur le compte Github.

Pour ce faire la fonction suit 3 étapes :

  1. Création d’un dossier temporaire
  2. git clone du repo dans notre dossier temporaire
  3. On ajoute une nouvelle URL vers son github au git remote
  4. git push
def cloneGitlab_and_pushGithub(repo):
    with tempfile.TemporaryDirectory() as tmpdirname:
        print('created temporary directory', tmpdirname)
        if repo['visibility'] == 'private':
            cloned_repo = Repo.clone_from(f"https://{gitlab_username}:{token_gitlab}@gitlab.com/{gitlab_username}/{unidecode.unidecode(repo['path'])}.git", tmpdirname)
        else:
            cloned_repo = Repo.clone_from(repo['web_url'], tmpdirname)
        remote = cloned_repo.create_remote("new", url=f"https://{github_username}:{token_github}@github.com/{github_username}/{unidecode.unidecode(repo['path'])}.git")
        remote.push(refspec='{}:{}'.format(repo['default_branch'], repo['default_branch']))
Explication du fonctionnement de la fonction cloneGitlab_and_pushGithub(repo) :
with tempfile.TemporaryDirectory() as tmpdirname:
cloned_repo = Repo.clone_from(f"https://{gitlab_username}:{token_gitlab}@gitlab.com/{gitlab_username}/{unidecode.unidecode(repo['path'])}.git", tmpdirname)
remote = cloned_repo.create_remote("new", url=f"https://{github_username}:{token_github}@github.com/{github_username}/{unidecode.unidecode(repo['path'])}.git")
 remote.push(refspec='{}:{}'.format(repo['default_branch'], repo['default_branch']))

Toutes les fonctions ne sont pas forcément utilisées.

Certaines ont été crées pour faciliter les tests durant la phase de test comme la fonction delete_repo_github(nom_repo).

Décollage

C’est le fichier main.py qui pilote tout ça.

main.py :

import fonctions, config

public_repo_gitlab = fonctions.get_repos_public_gitlab()
private_repo_gitlab = fonctions.get_repos_private_gitlab()

for repo in public_repo_gitlab:
    fonctions.create_repo_github(repo['name'])
    fonctions.cloneGitlab_and_pushGithub(repo)

if config.import_private_repo:
    for repo in private_repo_gitlab:
        fonctions.create_private_repo_github(repo['name'])
        fonctions.cloneGitlab_and_pushGithub(repo)
  1. Il récupère dans des variables les repos gitlab public et privés
  2. Pour chaque repositories public il crée un repo du même nom sur Github et lance l’importation du repo de gitlab vers github
  3. Si la variable import_private_repo est True alors il fait pareil pour les projets privées

Pipeline CI CD

Voici mon fichier .gitlab-ci pour créer une pipeline CI CD :

image: python:latest

stages:

  - push_to_github

before_script:
  - pip install -r requirements.txt


push_to_github:
  stage: push_to_github
  script: python main.py
  rules:
    - if: '$token_gitlab'
    - if: '$token_github'
    - if: '$gitlab_username'

On utilise ici une image docker Python3.

Avant de lancer le stage principal pip installe toutes les librairies requises.

Et j’ai fait en sorte que la pipeline ne soit joué que si les variables $token_gitlab, $token_github et $gitlab_username existe :

rules:
    - if: '$token_gitlab'
    - if: '$token_github'
    - if: '$gitlab_username'

Pipeline planifié

De plus pour que mes comtes Gitlab et Github soient assez souvent synchronisé j’ai créer un scheduler pour jouer la pipeline tous les vendredis à 20h.

Pour ce faire :

  1. Dans le projet gitlab, dans la partie CI/CD / Schedules
  2. New schedule

Une fois le scheduler sauvegardé, la pipeline se déclenchera automatiquement tous les vendredi à 20h.

Suggestion ?

Si tu as des idées pour améliorer le code ou pour arriver au même objectif différemment n’hésite pas à donner des tips en commentaires plus bas pour en faire profiter à la communauté. ♥️

Besoin d’aide ?

Tu galères sur un script, une pipeline ou autres ? Contactes nous ICI ou poses ta question en commentaire.

Le partage nous aide à nous faire connaître, merci pour ton partage 🫀

Quitter la version mobile