团队的代码托管管理平台之前一直用Gitee的企业版本,但除了代码pull/push操作外,基本不用平台上其它功能,除了要新建一个仓库要打开下网页版,其它时间基本不会访问网页版本,所以经过半天的调研,从GitLab/Gogs/Gitea中选择了Gitea,把迁移过程记录如下。
安装Gitea
因为服务器上刚好装有docker,按照官方文档选择了最简单的docker安装。
1 | docker pull gitea/gitea:latest |
安装完成后遇到了页面有三个静态文件(css/js)加载不成功,导致页面排版混乱,F12查看控制台报错net::ERR_CONTENT_LENGTH_MISMATCH,google之,找到这篇文章
Nginx 做代理时浏览器报错 net::ERR_CONTENT_LENGTH_MISMATCH,按照此方法解决。
配置Nginx
在服务器Nginx上配置反向代理
vi /etc/nginx/conf.d/gitea.conf
1 | upstream gitea { |
重新加载配置
1 | sudo nginx -s reload |
域名解析
git.i.example.com解析到当前服务器ip,并把服务器防火墙入方向的10022 tcp端口打开,以便使用ssh方式clone仓库时使用。
Gitea初始化
打开http://.i.example.com,进入初始化界面(如果没进随便点注册或登录就会进),除了数据库根据需要配置,几个域名和网址要修改下,邮箱和其它选项按需配置。以后如果想修改配置,可以直接修改/data/gitea/gitea/conf/app.ini文件配置说明,修改完成后重启下gitea即可生效。
仓库迁移
因为我迁移的是团队项目,所以先通过Gitea提供的API把所有仓库以镜像方式(镜像方式同步过来仓库对成员为只读,并且可以设置间隔时间,默认8小时,定时从原始地址Gitee同步最新代码)同步过来[操作1],然后为每个项目配置好协作者/团队/权限等设置,在这期间,团队成员还是往Gitee上提交代码,待全部设置完成后取消告知团队成员不要往Gitee提交代码,并调用Giea api把所有仓库从Gitee上同步一下最新代码[操作2],然后每个仓库从镜像仓库转为普通仓库,并让团队的所有在自己仓库根目录执行修改本地仓库Git远程仓库地址替换操作[操作3]
[操作1]:登录Gitea后,界面右上角有一个加号,点开了后有一个迁移外部仓库的功能,只要填入外部仓库URL,授权验证信息等信息就可以一键把外部仓库的所有代码(包括所有branch和commit)迁移到Gitea,如果要迁移的仓库比较多,可以使用Gitea提供的Api来操作。对应此迁移操作的api是
1 | POST /repos/migrate?access_token=<your gitea admin access token> |
注:
access_token 请在有管理员权限的账号的设置>应用中创建;
Request body 中的uid即管理后台>账户管理/组织管理中的ID列值;
找了Gitee没找到可以获取账户下所有仓库信息的API,所以只好手写了一个Gitee仓库地址的文件,类似
vi gitee-url.txt
1 | https://gitee.com/example/project_a.git |
使用shell脚本逐行读取url,并调用Gitea api迁移仓库。
1 | !/bin/bash |
[操作2]:从Gitee上同步最新代码
1 | for line in $(<gitee-url.txt); |
注:owner为项目拥有者用户名/组织名
[操作3]:原本地仓库Git远程仓库地址替换
1 | // http地址 |
如果之前是用http地址进行克隆的仓库的话,现在就是在进行pull和push操作时,把账户密码换成Gitea的就可以了;
如果以前是用ssh克隆的仓库的话,现在在Gitea的设置>SSH / GPG 密钥里添加一下公钥就可以进行git pull/git push等操作了;
仓库备份
Gitea有自己的备份与恢复功能备份与恢复,这个备份比较全面,数据/代码/日志都可以备份,正是因为这样,如果仓库比较多这个备份的文件肯定会有点大,而且每次都是全量备份,所以频率肯定不能太高,而我只是想对仓库代码做一个高频率备份,所以写了一个Python3脚本调用Gitea api和 Git命令来进行所有仓库的所有分支代码备份,因为这个备份基于Git机制,所以虽然频率高,但备份始终只有一份。脚本如下:
backup.py
如果使用python2运行,分支名里有中文的话,请自行处理字符编码问题。
** python1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78#!/usr/bin/python3
import os
import platform
import requests
current_dir = os.path.abspath(os.path.dirname(__file__))
access_token = "<your access token>"
repos_url = 'http://git.i.example.com/api/v1/repos/search?access_token={}&page={}&limit={}'
branches_url = 'http://git.i.example.com/api/v1/repos/{}/branches?access_token={}'
repo_key_url = 'http://git.i.example.com/api/v1/repos/{}/{}/keys?access_token={}'
def repos():
page = 1
limit = 50
has_next = True
while has_next:
r = requests.get(repos_url.format(access_token, page, limit))
for repo in r.json()['data']:
yield repo
page += 1
has_next = len(r.json()['data']) == limit
"""拉取项目所有分支代码到本地"""
def sync_repo():
repo_index = 0
for repo in repos():
repo_index += 1
# 克隆仓库
os.chdir(current_dir)
print('克隆第 {} 个仓库 {} '.format(repo_index, repo['name']))
os.system("git clone {}".format(repo['ssh_url']))
os.chdir(os.path.join(current_dir, repo['name']))
# 更新仓库
print('同步 {} 仓库所有分支'.format(repo['name']))
os.system('git fetch --all')
# if platform.system() == 'Windows':
# Windows
branches = requests.get(branches_url.format(
repo['full_name'], access_token)).json()
for branch in branches:
branch_name = branch['name']
os.system('git branch --track {} origin/{}'.format(branch_name, branch_name))
# 用reset而不用pull是因为如果分支被强推了pull下来会有合并冲突,用rest就不会有冲突问题
os.system('git checkout {} && git reset --hard origin/{}'.format(branch_name, branch_name))
# else:
# # Linux/macOS
# # git branch -r | grep -v '\->' | while read remote; do git branch --track ${remote#origin/} $remote; done && git fetch --all && git pull --all
# # os.system("git branch -r | grep -v '\->' | while read remote; do git branch --track ${remote#origin/} $remote; done && git fetch --all && git pull --all")
# # # 用reset而不用pull是因为如果分支被强推了pull下来会有合并冲突,用rest就不会有冲突问题
# os.system("git branch -r | grep -v '\->' | while read remote; do git branch --track ${remote#origin/} $remote; git checkout ${remote#origin/}; git reset --hard $remote; done")
"""设置项目部署公钥"""
def set_pub_key():
repo_index = 0
body = {
"key": "ssh-rsa aabbcc",
"read_only": True,
"title": "SandBox"
}
for repo in repos():
repo_index += 1
print('==={}. {}==='.format(repo_index, repo['name']))
r = requests.post(repo_key_url.format(
repo['owner']['username'], repo['name'], access_token), data=body)
print(r.json())
if __name__ == '__main__':
sync_repo()
# set_pub_key()
可以把脚本放在本地,使用cron(Linux/macOS)/计划任务(Windows)定时运行python backup.py