انتقل إلى المحتوى
في هذه الصفحة

Fetch باستخدام Gitlab API

Fetch باستخدام Gitlab API

الدافع

أستخدم gitlab، ولدي العديد من المستودعات التي أحتاج إلى تتبعها يومياً، أو حتى أكثر من مرة في اليوم. وجدت ذلك متعباً جداً أولاً لاستنساخ جميع تلك المستودعات عندما أغير جهاز العمل، ثم الحفاظ على جميع هذه المستودعات محدّثة. لهذا السبب بدأت في كتابة سكربت لأتمتة المهام المملة عن كتفي.

العملية

بدأت بسكربت bash بسيط، الذي يمر بلوب for ويقوم بالعمل، حيث يجب أن تكون مسارات المستودعات مكتوبة بالصلب.

#!/bin/bash

current_dir=$(pwd)
REPOS=(
	# list of all repos names
)

function doit() {
	for repo in ${REPOS[@]}; do
		if [ ! -d "$repo" ]; then
			echo " - cloning $repo ..."
				# git clone $repo
		else
			cd $current_dir/$repo
			echo " - fetching $repo ..."
			git fetch
			cd $current_dir
		fi
	done
  echo "Done"
}

doit

كان هذا سريعاً وعمل لفترة من الوقت، لكن عندما بدأت العمل مع مشاريع متعددة، وقمت زملائي بإضافة/حذف/إعادة تسمية مستودع، كان لا يزال عملاً يدوياً لتحديث السكربت، ومضلماً كما أنا، لم يعجبني، وبحثت عن طريقة كسولة لأتمتة العملية، وفتحت توثيق API الخاص بـ Gitlab، حيث بدأ المرح.

فكرة الحل

سكربت Python للتحدث مع API gitlab واستخدام نقطة النهاية /projects للوصول إلى المستودعات.

المتطلبات:

  • رمز Gitlab : أنشئ أولاً رمزاً خاصاً لاستخدامه للوصول إلى API كما هو مشرح في التوثيق.
  • وصول افتراضي إلى المستودعات (مثلاً: gitlab.com)

اختياري:

  • Pipenv لتثبيت تبعيات التطوير:
brew install pipenv

المحاولة الأولى: argparse

كانت المحاولة الأولى تمرير الخيارات كمعاملات للسكربت:

#!/usr/local/bin/python3.7
"""
Clone/fetch projects from Gitlab using the private token
"""

import argparse
from os import path
import json
import subprocess
import shlex
from urllib.request import urlopen

IGNORE_LIST = [
    'test',
    'example',
    'tour'
]


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--token', '-t', help='Gitlab private Token')
    parser.add_argument('--url', '-u', help='Gitlab URL')
    args = parser.parse_args()

    if not args.token or not args.url:
        parser.print_help()
        exit(1)

    gitlab_token = args.token
    gitlab_url = args.url

    projects = urlopen(
        f'https://{gitlab_url}/api/v4/projects?membership=1&order_by=path&per_page=1000&private_token={gitlab_token}')
    all_projects = json.loads(projects.read().decode())

    for project in all_projects:
        try:
            url = project.get('ssh_url_to_repo')
            if any([x in url for x in IGNORE_LIST]):
                continue
            name = project.get('name').replace(' ', '-').replace('.', '-')
            repo_path = path.join(path.dirname(path.realpath(__file__)), name)
            if path.isdir(repo_path):
                print(f'Fetching {name}')
                command = shlex.split(f'git -C {repo_path} fetch')
                process = subprocess.Popen(command)
                process.communicate(input=None)
            else:
                print(f'Cloning {name}')
                command = shlex.split(f'git clone {url} {name}')
                process = subprocess.Popen(command)
                process.communicate(input=None)
        except Exception as unexpected_exception:
            print(f"Error on {url}: {str(unexpected_exception)}")
    print('Done')


if __name__ == '__main__':
    main()

الاستخدام:

usage: main.py [-h] [--token TOKEN] [--url URL]

optional arguments:
  -h, --help            show this help message and exit
  --token TOKEN, -t TOKEN
                        Gitlab private Token
  --url URL, -u URL     Gitlab URL<Paste>

المحاولة الثانية: JSON

ثم فكرت أن الإعدادات يجب أن تكون أكثر مرونة، لذا كان استخدام ملف إعدادات منفصل منطقياً.

يمكن العثور على مستودع المشروع الكامل هنا

#!/usr/local/bin/python3.7
"""
Clone/fetch projects from Gitlab using the private token
NOTE:
The gitlab url, token should be provided in a config.json
file, which should exist in the same direcotry as this script.
"""

import json
import shlex
import subprocess
import sys
from os import path
from urllib.request import urlopen

ROOT = path.dirname(path.abspath(__file__))


def read_configs():
    configs = {}
    try:
        with open(f'{ROOT}/config.json', 'r') as config_file:
            configs = json.loads(config_file.read())
    except FileNotFoundError:
        print('config.json cannot be found')
    except json.decoder.JSONDecodeError:
        print('Please provide gitlab configs in json format')
    return configs


def shell(command):
    process = subprocess.Popen(command)
    process.communicate(input=None)


def main():
    configs = read_configs()
    gitlab_url = configs.get('gitlab_url')
    gitlab_token = configs.get('gitlab_token')
    if not(gitlab_url and gitlab_token):
        print('Please provide gitlab configs in your config.json')
        sys.exit(1)
    ignore_list = configs.get('ignore_list')

    projects = urlopen(
        f'https://{gitlab_url}/api/v4/projects?membership=1&order_by=path&per_page=1000&private_token={gitlab_token}')
    all_projects = json.loads(projects.read().decode())

    for project in all_projects:
        try:
            url = project.get('ssh_url_to_repo')
            if any([x in url for x in ignore_list]):
                continue
            name = project.get('name').replace(' ', '-').replace('.', '-')
            repo_path = path.join(path.dirname(path.realpath(__file__)), name)
            if path.isdir(repo_path):
                print(f'Fetching {name}')
                command = shlex.split(f'git -C {repo_path} fetch')
                shell(command)
            else:
                print(f'Cloning {name}')
                command = shlex.split(f'git clone {url} {name}')
                shell(command)
        except Exception as unexpected_exception:
            print(f"Error on {url}: {str(unexpected_exception)}")
    print('Done')
    sys.exit(0)


if __name__ == '__main__':
    main()

الاستخدام

python main.py

العودة للأعلى