需求
假如現在想對使用到的一些Github上的開源組件進行備份,采用自建Gitlab服務器的方式進行備份保存,並且組件需定時保持與Github更新。
總體步驟
組件備份- 整體步驟如下:
    a.搭建gitlab服務器,參考CentOS7 搭建gitlab服務器 
    b.登錄賬戶,創建一個group,如kdv-opensource(方便統一管理,不創建也一樣)
    c.在kdv-opensource組里需創建相應的repo,可設置訪問權限為public。不過不手動創建,則gitlab默認創建為private。
    d.腳本執行git clone --mirror github-url,克隆鏡像
       或git remote update,進行更新
    e.添加要推送到遠端repo 的git地址
    git remote add gitlab gitlab-url
    f.推送到gitlab服務器上
        git push gitlab --all
        git push gitlab --tags
    g.python腳本加入crontab定期執行即可 
        搭建Gitlab服務器
如前所述,略。
創建group
  登錄賬戶,菜單欄選擇Groups,右側New group,填寫組名即描述信息即可。如創建一個kdv-opensource組:
創建項目repo
需要在kdv-opensource組內創建好所有要備份組件項目,且名字需與Github上開源項目名字保持一致(為了方便),可設定訪問權限為public,如下:

如果不顯示創建repo,則創建的項目repo默認為private。
腳本功能
我們使用python實現一個腳本從Github上鏡像克隆(git clone --mirror)相應的項目和鏡像更新(git remote update)。
python可以直接調用系統命令,使用os.system(command),即可執行相應的命令。當然,操作系統需預先安裝git及一些軟件才能使用。
修改配置文件
前面一步的git clone --mirror下載好相應的github repo,需導入到我們自建的Gitlab服務器中,這樣就需要修改推送的git 地址,即pushurl,僅需修改git項目下相應的配置文件。
git remote add gitlab git@ip:kdv-opensource/repo-name.git
ip為gitlab服務的相應ip地址,kdv-opensource為分組名,如果不分組則為用戶名,粗體gitlab為分支名。
推送到gitlab服務器上
我們知道,如果要進行push操作是需要進行鑒權的,也即需要輸入賬戶密碼,而在腳本執行過程中無法一一手工輸入密碼。有兩種方法可以解決這個問題:
方法一
在pushurl地址中指定username:password,如下:
pushurl = 'http://username:password@ip:port/kdv-opensource/{repo}.git' 
        方法二
通過SSH登錄,更為方便和安全,也是現在比較流行的方式。(推薦)

導入本機生成的ssh公鑰即可,公鑰可通過ssh-keygen生成(默認即可),因此需要先安裝SSH。

這樣,在腳本執行過程中就能自動地向gitlab服務器上推送repo內容。
定期更新
在linux下可將腳本加入到crontab列表,使之定期自動執行即可。
實現腳本
組件列表sample
文件以“項目名,github地址”的格式填入list.txt文件即可。
haproxy,https://github.com/haproxy/haproxy.git libevent,https://github.com/libevent/libevent.git openresty,https://github.com/openresty/openresty.git
python腳本
完整代碼如下,由於實際工作需要,增加了日記記錄功能,腳本執行在控制台上看不到數據,輸出會保存到日志文件中,如不需要,去掉重定向到文件即可。
應該比較清晰了。
#!/usr/bin/env python # -*- encoding: utf-8 -*- import os import time # 以下參數可自由指定 # 修改此處ip地址為gitlab服務器ip地址 giturl = 'git@172.16.6.105:kdv-opensource/{}.git' # 需修改ip為相應的gitlab服務器地址 opensource_list = 'list.txt' # 組件列表文件名 log_dirname = 'sync-logs' # 日志記錄目錄名 opensource_dirname = 'opensource' # 組件列表存放目錄名 # 以下參數程序自動構建 dicts = {} # 存放組件列表,組件-組件鏈接地址 base_path = '' # 程序執行根目錄 log_file = '' # 日志文件 opensource_path = '' def read_file(file): """ 從文件中讀取開源組件列表,填入到dicts中,以組件名為key,組件url為value :param file: 組件文件 :return: None """ with open(file, 'r') as fr: for line in fr: if line.strip(): line = line.strip() # 逗號分隔的組件名和組件URL,存放到字典dicts中 name, url = line.split(',') dicts[name.strip()] = url.strip() def initialize(): """ 做一些初始化工作 :return: None """ global base_path, log_file, opensource_path # 獲取程序當前執行路徑為根路徑:base_path base_path = os.path.dirname(os.path.realpath(__file__)) msg = 'Program path {}'.format(base_path) print(msg) # 組件目錄 opensource_path = os.path.join(base_path, opensource_dirname) # 判斷程序是否是初始化運行,確定組件目錄是否存在 if not os.path.exists(opensource_path): mkdir_command = 'mkdir {}'.format(opensource_path) os.system(mkdir_command) # 日志目錄 log_path = os.path.join(base_path, log_dirname) # 判斷程序是否是初始化運行,確定日志文件目錄是否存在 if not os.path.exists(log_path): mkdir_command = 'mkdir {}'.format(log_path) os.system(mkdir_command) # 以當前時間構造日志文件名稱,存放到log_dirname目錄中 xtime = time.strftime("%Y-%m-%d-%H.%M.%S") file_name = '{}.{}'.format(xtime, 'txt') log_file = os.path.join(log_path, file_name) print(log_file) # write basic info to log file msg = '[*]Program base path : {}'.format(base_path) append_2_file(log_file, msg) msg = '[*]Program opensource path : {}'.format(opensource_path) append_2_file(log_file, msg) msg = '[*]Program log path : {}'.format(log_path) append_2_file(log_file, msg) def append_2_file(file, msg): """ append sth to file :param file: actually, the log file :param msg: sth you want to log :return: None """ with open(file, 'a') as fw: fw.write(msg) fw.write('\n') pass if __name__ == '__main__': # 初始化參數 initialize() # 讀取組件文件列表 read_file(opensource_list) # 組件目錄opensource下已經存在的文件列表 num = len(dicts) # 總的組件個數 files = os.listdir(opensource_path) # 獲取當前路徑下的所有文件 msg = '[*]Existing opensource-files in {} are : {}'.format(opensource_path, files) append_2_file(log_file, msg) index = 0 os.chdir(opensource_path) # 切換到組件路徑下,方便操作 print() for name, url in dicts.items(): # 組件名字&git repo的url地址 # progress information index += 1 msg = '\n\n[{}/{}] - {}'.format(index, num, name) print(msg) append_2_file(log_file, msg) # save github information(origin) to log file msg = 'github(origin): {} - {}'.format(name, url) print(msg) append_2_file(log_file, msg) # save gitlab information(new) to log file name_git = '{}.git'.format(name) gitlab_url = giturl.format(name) msg = 'gitlab(new): {} - {}'.format(name, gitlab_url) print(msg) append_2_file(log_file, msg) if name_git in files: # 如果是已有的repo,update更新即可 try: print('-----------------------------------') # update os.chdir(name_git) command = 'git remote update' print(command) append_2_file(log_file, command)
os.system(command) # push all branches command_push_all = 'git push gitlab --all 2>>{}'.format(log_file) print(command_push_all) append_2_file(log_file, '\n') # for better presentation append_2_file(log_file, command_push_all) # log command execution os.system(command_push_all) # push all tags command_push_tags = 'git push gitlab --tags 2>>{}'.format(log_file) print(command_push_tags) append_2_file(log_file, '\n') # for better presentation append_2_file(log_file, command_push_tags) # log command execution os.system(command_push_tags) except: print('sth bad happen...') continue finally: os.chdir('..') else: try: print('-----------------------------------') # 以鏡像的形式clone repo command = 'git clone --mirror {} --progress 2>> {}'.format(url, log_file) print(command) os.system(command) # 創建遠端分支 os.chdir(name_git) pushurl = 'git remote add gitlab {}'.format(giturl.format(name)) print(pushurl) append_2_file(log_file, pushurl) os.system(pushurl) # push all branches command_push_all = 'git push gitlab --all 2>>{}'.format(log_file) append_2_file(log_file, '\n') # for better presentation append_2_file(log_file, command_push_all) # log command execution os.system(command_push_all) # push all tags command_push_tags = 'git push gitlab --tags 2>>{}'.format(log_file) append_2_file(log_file, '\n') # for better presentation append_2_file(log_file, command_push_tags) # log command execution os.system(command_push_tags) os.chdir('..') except: print('sth bad happen...') continue finally: pass os.chdir('..') end_line = '\n==============================================' print(end_line) append_2_file(log_file, end_line) append_2_file(log_file, "This is the End") pass
效果圖
再補兩張效果圖吧,如備份的組件curl,項目頁 ==>

  分支情況 ==>
提交記錄 ==>

Question
zeroPaddedFilemode
在push過程中(push --mirror),如果有文件出現錯誤:"zeroPaddedFilemode: contains zero-padded file modes",可參考如下文章。
Pushing fails if local repo has problems with zero-padded file modes
Zero-padded file modes in repository
remote: fatal: fsck error in packed object when pushing oh-my-zsh repo to new remote
不過好像對我不起作用,什么.git/config和~/.gitconfig文件都改爛了,還是不行。難受...
解決方法:修改配置文件
# vim /etc/gitlab/gitlab.rb ... omnibus_gitconfig['system'] = { 'fsck' => ['zeroPaddedFilemode = ignore'], # 這個是上面某個鏈接里說的方法,但不起作用,於是試了下-下面那行 "receive" => ["fsckObjects = false"], }
重新配置: gitlab-ctl reconfigure 重啟服務: gitlab-ctl restart
