02gitea / Dockerfile
Deep-sea's picture
Update Dockerfile
0c7efde verified
Raw
History Blame Contribute Delete
8.68 kB
FROM gitea/gitea:1.24.6
# 以 root 用户设置权限(Spaces 允许构建时 root)
USER root
# 安装 Python 和 pip
RUN apk update && \
apk add python3 py3-pip && \
pip3 install --break-system-packages --upgrade pip
# 安装 Python 包
RUN pip3 install --break-system-packages watchdog huggingface_hub
# 确保数据目录权限,使用默认 git 用户 (UID/GID 1000)
RUN mkdir -p /data/gitea /data/git /data/ssh && \
chown -R git:git /data && \
chmod -R 750 /data
# 设置工作目录
WORKDIR /data
# 暴露 Spaces 默认端口(文档用途,实际由 $PORT 控制)
EXPOSE 7860
# Gitea 环境变量:适配 Spaces 端口,禁用 SSH
ENV GITEA__database__DB_TYPE=sqlite3 \
GITEA__database__PATH=/data/gitea/gitea.db \
GITEA__server__ROOT_URL=https://deep-sea-02gitea.hf.space/ \
GITEA__server__HTTP_ADDR=0.0.0.0 \
GITEA__server__HTTP_PORT=7860 \
GITEA__server__DISABLE_SSH=true \
USER=git
# 切换到 git 用户(非 root)
USER git
COPY --chown=git:git <<'EOF' /uploadhf.py
import os
import time
import logging
import threading
import subprocess
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from huggingface_hub import HfApi
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class DataDirectoryHandler(FileSystemEventHandler):
"""处理 /data 目录文件变化的监控器"""
def __init__(self, repo_id, hf_token, data_directory="/data"):
self.repo_id = repo_id
self.hf_token = hf_token
self.data_directory = data_directory
self.api = HfApi(token=hf_token)
self.last_commit_time = 0
self.commit_delay = 30 # 防抖延迟:30秒内多次变动只提交一次
logger.info(f"初始化监控器,监控目录: {data_directory},目标仓库: {repo_id}")
def on_modified(self, event):
"""文件被修改时触发"""
if not event.is_directory:
logger.info(f"检测到文件修改: {event.src_path}")
self.schedule_commit("文件修改")
def on_created(self, event):
"""文件被创建时触发"""
if not event.is_directory:
logger.info(f"检测到新文件: {event.src_path}")
self.schedule_commit("文件创建")
def on_deleted(self, event):
"""文件被删除时触发"""
if not event.is_directory:
logger.info(f"检测到文件删除: {event.src_path}")
self.schedule_commit("文件删除")
def on_moved(self, event):
"""文件被移动或重命名时触发"""
logger.info(f"检测到文件移动: {event.src_path} -> {event.dest_path}")
self.schedule_commit("文件移动")
def schedule_commit(self, change_type):
"""安排提交任务,带有防抖机制"""
current_time = time.time()
if current_time - self.last_commit_time > self.commit_delay:
self.last_commit_time = current_time
# 使用线程执行提交,避免阻塞监控
thread = threading.Thread(target=self.commit_changes, args=(change_type,))
thread.daemon = True
thread.start()
def commit_changes(self, change_type):
"""提交变更到 Hugging Face Hub"""
try:
commit_message = f"自动提交: {change_type} - {time.strftime('%Y-%m-%d %H:%M:%S')}"
# 上传整个目录
self.api.upload_folder(
folder_path=self.data_directory,
repo_id=self.repo_id,
repo_type="dataset",
commit_message=commit_message,
ignore_patterns=["*.tmp", "*.log", "*.temp", ".git/*"] # 忽略临时文件
)
logger.info(f"✅ 成功提交变更到 Hugging Face Hub: {commit_message}")
except Exception as e:
logger.error(f"❌ 提交失败: {e}")
def start_directory_monitoring(data_directory="/data", repo_id=None, hf_token=None):
"""启动目录监控服务"""
# 参数验证
if not hf_token:
hf_token = os.getenv('HF_TOKEN')
if not hf_token:
raise ValueError("HF_TOKEN 环境变量未设置")
if not repo_id:
raise ValueError("必须提供 repo_id 参数")
# 检查监控目录是否存在
if not os.path.exists(data_directory):
logger.warning(f"监控目录 {data_directory} 不存在,正在创建...")
os.makedirs(data_directory, exist_ok=True)
# 创建事件处理器和观察器
event_handler = DataDirectoryHandler(
repo_id=repo_id,
hf_token=hf_token,
data_directory=data_directory
)
observer = Observer()
observer.schedule(event_handler, data_directory, recursive=True)
# 启动监控
observer.start()
logger.info(f"🎯 目录监控服务已启动: {data_directory}")
return observer
def start_gitea_server(port=7860):
"""启动 Gitea 服务器[1,3](@ref)"""
def run_gitea():
try:
# 使用 subprocess.Popen 启动 Gitea 服务[1,9](@ref)
gitea_process = subprocess.Popen(
['gitea', 'web', '--port', str(port)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
)
logger.info(f"🚀 Gitea 服务器已启动,端口: {port}")
logger.info(f"📊 访问地址: http://localhost:{port}")
# 实时输出 Gitea 日志
while True:
output = gitea_process.stdout.readline()
if output == '' and gitea_process.poll() is not None:
break
if output:
logger.info(f"Gitea: {output.strip()}")
# 检查进程退出状态
return_code = gitea_process.poll()
if return_code != 0:
error_output = gitea_process.stderr.read()
logger.error(f"❌ Gitea 服务器异常退出,返回码: {return_code}")
logger.error(f"错误信息: {error_output}")
else:
logger.info("✅ Gitea 服务器正常退出")
except FileNotFoundError:
logger.error("❌ 未找到 gitea 命令,请确保 Gitea 已正确安装")
except Exception as e:
logger.error(f"❌ 启动 Gitea 服务器时发生错误: {e}")
# 在新线程中运行 Gitea[2,6](@ref)
gitea_thread = threading.Thread(target=run_gitea)
gitea_thread.daemon = True # 设置为守护线程,主程序退出时自动结束
gitea_thread.start()
return gitea_thread
def main():
"""主函数 - 同时启动目录监控和 Gitea 服务器"""
# 配置参数
CONFIG = {
"data_directory": "/data",
"repo_id": "02engine/02gitea",
"hf_token": os.getenv('HF_TOKEN'),
"gitea_port": 7860
}
# 验证必要参数
if not CONFIG["hf_token"]:
logger.error("❌ 请设置 HF_TOKEN 环境变量")
return
logger.info("🚀 启动集成服务...")
try:
# 启动 Gitea 服务器[1,3](@ref)
gitea_thread = start_gitea_server(CONFIG["gitea_port"])
# 启动目录监控服务
observer = start_directory_monitoring(
data_directory=CONFIG["data_directory"],
repo_id=CONFIG["repo_id"],
hf_token=CONFIG["hf_token"]
)
logger.info("✅ 所有服务已启动完成!")
logger.info("调试: HF_TOKEN = %s", CONFIG["hf_token"])
logger.info("📁 目录监控: /data → Hugging Face Hub")
logger.info("🌐 Gitea 服务: http://localhost:7860")
logger.info("🛑 按 Ctrl+C 停止所有服务")
# 主循环,保持程序运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
logger.info("正在停止所有服务...")
except Exception as e:
logger.error(f"❌ 启动服务时发生错误: {e}")
finally:
# 清理资源
if 'observer' in locals():
observer.stop()
observer.join()
logger.info("所有服务已停止")
if __name__ == "__main__":
main()
EOF
# 运行 Gitea,显式指定 Spaces 默认端口
RUN cat /uploadhf.py
CMD ["python3", "/uploadhf.py"]