Upload 4 files
Browse files- Dockerfile +19 -29
- README.md +26 -6
- supervisord.conf +26 -0
- sync_to_dataset.py +46 -0
Dockerfile
CHANGED
|
@@ -3,51 +3,41 @@ FROM ubuntu:22.04
|
|
| 3 |
ENV DEBIAN_FRONTEND=noninteractive
|
| 4 |
ENV OPENCODE_DATA_DIR=/data
|
| 5 |
|
| 6 |
-
# 安装系统依赖 +
|
| 7 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
curl \
|
| 9 |
ca-certificates \
|
| 10 |
git \
|
| 11 |
-
openssh-client \
|
| 12 |
-
sudo \
|
| 13 |
python3 \
|
| 14 |
python3-pip \
|
|
|
|
| 15 |
&& rm -rf /var/lib/apt/lists/*
|
| 16 |
|
| 17 |
-
#
|
| 18 |
-
RUN
|
| 19 |
-
echo "user ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/user && \
|
| 20 |
-
chmod 0440 /etc/sudoers.d/user
|
| 21 |
-
|
| 22 |
-
# 创建数据持久化目录并设置权限
|
| 23 |
-
RUN mkdir -p /data && chown -R user:user /data
|
| 24 |
|
| 25 |
-
# 创建
|
| 26 |
-
RUN
|
|
|
|
| 27 |
|
| 28 |
-
# 切换到非 root 用户
|
| 29 |
USER user
|
| 30 |
WORKDIR /home/user
|
| 31 |
|
| 32 |
-
#
|
| 33 |
-
RUN
|
| 34 |
-
ssh-keyscan -T 5 github.com 2>/dev/null >> /home/user/.ssh/known_hosts || true
|
| 35 |
|
| 36 |
-
|
| 37 |
-
RUN curl --retry 3 --retry-delay 2 --max-time 60 -fsSL \
|
| 38 |
-
https://raw.githubusercontent.com/opencode-ai/opencode/refs/heads/main/install | bash
|
| 39 |
|
| 40 |
-
#
|
| 41 |
-
|
| 42 |
|
| 43 |
-
# 复制
|
| 44 |
-
COPY --chown=user
|
|
|
|
| 45 |
|
| 46 |
-
#
|
| 47 |
-
RUN
|
| 48 |
|
| 49 |
-
|
| 50 |
-
WORKDIR /workspace
|
| 51 |
|
| 52 |
-
|
| 53 |
-
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
| 3 |
ENV DEBIAN_FRONTEND=noninteractive
|
| 4 |
ENV OPENCODE_DATA_DIR=/data
|
| 5 |
|
| 6 |
+
# 安装系统依赖 + supervisor + Python3
|
| 7 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
curl \
|
| 9 |
ca-certificates \
|
| 10 |
git \
|
|
|
|
|
|
|
| 11 |
python3 \
|
| 12 |
python3-pip \
|
| 13 |
+
supervisor \
|
| 14 |
&& rm -rf /var/lib/apt/lists/*
|
| 15 |
|
| 16 |
+
# 安装 huggingface_hub(用于上传数据集)
|
| 17 |
+
RUN pip3 install --no-cache-dir huggingface_hub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
# 创建非 root 用户 (UID 1000)
|
| 20 |
+
RUN useradd -m -u 1000 -s /bin/bash user && \
|
| 21 |
+
mkdir -p /data && chown -R user:user /data
|
| 22 |
|
|
|
|
| 23 |
USER user
|
| 24 |
WORKDIR /home/user
|
| 25 |
|
| 26 |
+
# 安装 opencode(官方脚本)
|
| 27 |
+
RUN curl -fsSL https://opencode.ai/install | bash
|
|
|
|
| 28 |
|
| 29 |
+
ENV PATH="/home/user/.opencode/bin:${PATH}"
|
|
|
|
|
|
|
| 30 |
|
| 31 |
+
# 可选:复制 opencode.json 配置文件(如果你有的话,去掉下面一行的注释)
|
| 32 |
+
# COPY --chown=user opencode.json /home/user/.config/opencode/opencode.json
|
| 33 |
|
| 34 |
+
# 复制同步脚本和 supervisor 配置
|
| 35 |
+
COPY --chown=user sync_to_dataset.py /home/user/sync_to_dataset.py
|
| 36 |
+
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
| 37 |
|
| 38 |
+
# 确保脚本可执行
|
| 39 |
+
RUN chmod +x /home/user/sync_to_dataset.py
|
| 40 |
|
| 41 |
+
EXPOSE 7860
|
|
|
|
| 42 |
|
| 43 |
+
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf"]
|
|
|
README.md
CHANGED
|
@@ -1,11 +1,31 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
|
|
|
| 7 |
pinned: false
|
| 8 |
-
license: apache-2.0
|
| 9 |
---
|
| 10 |
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: OpenCode
|
| 3 |
+
emoji: ⚡
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: gray
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
pinned: false
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# OpenCode on Hugging Face Spaces
|
| 12 |
+
|
| 13 |
+
本 Space 使用 Docker 部署 [OpenCode](https://opencode.ai/) 的 HTTP 服务,并配置了:
|
| 14 |
+
|
| 15 |
+
- **Storage Bucket** 挂载到 `/data`,用于实时读写(会话、配置等)
|
| 16 |
+
- **每 120 秒自动同步** `/data` 到私有数据集 `jamesw853/opencode-data`(需要设置 `HF_TOKEN` 和 `OPENCODE_DATASET_REPO` 环境变量)
|
| 17 |
+
|
| 18 |
+
## 使用方式
|
| 19 |
+
|
| 20 |
+
- **API 文档**:访问 `/docs`(OpenCode serve 自带)
|
| 21 |
+
- **健康检查**:`GET /health`
|
| 22 |
+
|
| 23 |
+
## 环境变量(Secrets)
|
| 24 |
+
|
| 25 |
+
- `HF_TOKEN`:你的 Hugging Face 访问令牌(需 write 权限)
|
| 26 |
+
- `OPENCODE_DATASET_REPO`:数据集 ID,例如 `jamesw853/opencode-data`
|
| 27 |
+
|
| 28 |
+
## 持久化
|
| 29 |
+
|
| 30 |
+
- 主存储:Storage Bucket(挂载到 `/data`)
|
| 31 |
+
- 备份:私有数据集(每 120 秒同步)
|
supervisord.conf
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[supervisord]
|
| 2 |
+
nodaemon=true
|
| 3 |
+
logfile=/dev/null
|
| 4 |
+
pidfile=/tmp/supervisord.pid
|
| 5 |
+
|
| 6 |
+
[program:opencode]
|
| 7 |
+
command=/home/user/.opencode/bin/opencode serve --host 0.0.0.0 --port 7860
|
| 8 |
+
directory=/home/user
|
| 9 |
+
autostart=true
|
| 10 |
+
autorestart=true
|
| 11 |
+
startsecs=3
|
| 12 |
+
stdout_logfile=/dev/stdout
|
| 13 |
+
stdout_logfile_maxbytes=0
|
| 14 |
+
stderr_logfile=/dev/stderr
|
| 15 |
+
stderr_logfile_maxbytes=0
|
| 16 |
+
|
| 17 |
+
[program:sync]
|
| 18 |
+
command=python3 /home/user/sync_to_dataset.py
|
| 19 |
+
directory=/home/user
|
| 20 |
+
autostart=true
|
| 21 |
+
autorestart=true
|
| 22 |
+
startsecs=5
|
| 23 |
+
stdout_logfile=/dev/stdout
|
| 24 |
+
stdout_logfile_maxbytes=0
|
| 25 |
+
stderr_logfile=/dev/stderr
|
| 26 |
+
stderr_logfile_maxbytes=0
|
sync_to_dataset.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import os
|
| 3 |
+
import time
|
| 4 |
+
import logging
|
| 5 |
+
from huggingface_hub import HfApi, upload_folder
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 11 |
+
DATASET_REPO = os.environ.get("OPENCODE_DATASET_REPO")
|
| 12 |
+
SYNC_INTERVAL = 120 # 秒
|
| 13 |
+
|
| 14 |
+
if not HF_TOKEN:
|
| 15 |
+
logger.error("HF_TOKEN environment variable not set. Sync will not work.")
|
| 16 |
+
exit(1)
|
| 17 |
+
if not DATASET_REPO:
|
| 18 |
+
logger.error("OPENCODE_DATASET_REPO environment variable not set. Sync will not work.")
|
| 19 |
+
exit(1)
|
| 20 |
+
|
| 21 |
+
api = HfApi(token=HF_TOKEN)
|
| 22 |
+
|
| 23 |
+
def sync_data():
|
| 24 |
+
try:
|
| 25 |
+
if not os.path.isdir("/data"):
|
| 26 |
+
logger.warning("/data does not exist or is not a directory")
|
| 27 |
+
return
|
| 28 |
+
upload_folder(
|
| 29 |
+
repo_id=DATASET_REPO,
|
| 30 |
+
folder_path="/data",
|
| 31 |
+
path_in_repo="",
|
| 32 |
+
repo_type="dataset",
|
| 33 |
+
commit_message=f"Auto sync at {time.strftime('%Y-%m-%d %H:%M:%S')}"
|
| 34 |
+
)
|
| 35 |
+
logger.info(f"Successfully synced /data to {DATASET_REPO}")
|
| 36 |
+
except Exception as e:
|
| 37 |
+
logger.error(f"Sync failed: {e}")
|
| 38 |
+
|
| 39 |
+
if __name__ == "__main__":
|
| 40 |
+
logger.info(f"Starting sync daemon, interval={SYNC_INTERVAL}s, dataset={DATASET_REPO}")
|
| 41 |
+
while True:
|
| 42 |
+
try:
|
| 43 |
+
sync_data()
|
| 44 |
+
except Exception as e:
|
| 45 |
+
logger.error(f"Unexpected error in sync loop: {e}")
|
| 46 |
+
time.sleep(SYNC_INTERVAL)
|