小心滥用GitHub被封,尽量使用GitLab

使用 git config --global credential.helper store 只用第一次验证后便永久记住GitHub账号, 适合Linux上使用HTTPS链接GitHub库, Windows有自己的密码管理器不用他 使用 git config --global --unset credential.helper 移除设置, 删除持久化密码

脚本与 AList 联合使用

subprocess 会把列表里的每个部分用 ““/’’ 引号 包起来, 所以路径有空格时用 subprocess 无需特殊处理 subprocess 默认将输出打印到控制台 ; 可以更改方法中 stdout= 参数控制输出 subprocess.run 是阻塞的,会一直卡着等待程序运行 subprocess.Popen 后台执行,不阻塞

1
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2024/2/4 15:59
# @Author : lowo
# @File : 用GitHub API实现备份自己GitHub上所有仓库的代码.py
# @Description :

from github import Github
# Authentication is defined via github.Auth
from github import Auth
import os
import shutil
import time
import subprocess

"""
/etc/crontab 中添加定时任务
51 1 ? * 2 root python "/root/py_script/用GitHub API实现备份自己GitHub上所有仓库的代码.py" >/dev/null 2>&1
每周二 1:51 执行脚本
"""

# git config --global credential.helper store
# git设置为登陆一次后记住 用户名与账户密码 ( 先登陆一次, 再执行这个脚本 )
# 若开启双重验证 则密码填写 个人访问令牌

# 替换为你的 GitHub 个人访问令牌
access_token = "your_github_access_token"
# 替换为你的 GitHub 用户名
user_name = "jlower"

# using an access token
auth = Auth.Token(access_token)

# 创建一个Public Web Github对象
g = Github(auth=auth)

# 获取当前用户
user = g.get_user()

# 获取用户的所有仓库
repos = user.get_repos()

# 备份每个仓库
backup_folder = "/root/Git Repository backup"
zip_output_folder = "/etc/alist/storage/_Git Repository backup"

os.makedirs(backup_folder, exist_ok=True)

repo_list = [] # 储存 存在哪些 远程git仓库
repo_skip_list = ["JavaStart-save"] # 要跳过不备份的远程git仓库
for repo in repos:
if repo.name in repo_skip_list:
continue
repo_path = os.path.join(backup_folder, repo.name)
repo_list.append(repo.name)
print("正在处理: ", repo.name)
if os.path.exists(repo_path):
# 如果本地仓库已存在,执行git pull
os.chdir(repo_path)
subprocess.run(["git", "pull"])
else:
# 如果本地仓库不存在,设定文件夹路径并进行克隆
subprocess.run(["git", "clone", repo.clone_url, repo_path])
time.sleep(0.5)

# 删除 backup_folder文件夹 中不在 repo_list列表 中的 过时文件夹(可能是由于远程仓库改名字导致的)
for dir_name in os.listdir(backup_folder):
dir_path = os.path.join(backup_folder, dir_name)
if dir_name not in repo_list:
if os.path.isdir(dir_path): # 如果是文件夹
shutil.rmtree(dir_path)

# 删除 zip_output_folder文件夹 中的文件
for filename in os.listdir(zip_output_folder):
file_path = os.path.join(zip_output_folder, filename)
os.remove(file_path)

# 将每个文件夹分别压缩并输出到 zip_output_folder 文件夹 (AList挂载的本地网盘文件夹)
files = os.listdir(backup_folder) # 获取路径下的子文件 (夹) 列表
zip_list = [] # 储存生成了哪些 zip 压缩包
for file in files:
file_path = os.path.join(backup_folder, file)
if os.path.isdir(file_path): # 如果是文件夹
zip_output_path = os.path.join(zip_output_folder, file)
zip_output_path = zip_output_path + ".zip"
zip_list.append(file + ".zip")
# zip 中 "-o" 是将压缩文件内的所有文件的最新变动时间设为压缩的时间(输出位置有同名压缩包时会追加变化而不会冲突, 体积将越来越大)
subprocess.run(["zip", "-r", "-q", zip_output_path, file_path])

# # 删除 zip_output_folder文件夹 中不在 zip_list列表 中的 过时文件(可能是由于远程仓库改名字导致的)
# for filename in os.listdir(zip_output_folder):
# file_path = os.path.join(zip_output_folder, filename)
# if filename not in zip_list:
# os.remove(file_path)

print("所有仓库已备份或更新到本地文件夹!")

# 删除第一层级的空文件夹
# files = os.listdir(backup_folder) # 获取路径下的子文件 (夹) 列表
# for file in files:
# file_path = os.path.join(backup_folder, file)
# if os.path.isdir(file_path): # 如果是文件夹
# if not os.listdir(file_path): # 如果子文件为空
# os.rmdir(file_path) # 删除这个空文件夹
# print('Remove: ', file)