jamesw853 commited on
Commit
98936b8
·
verified ·
1 Parent(s): e58ec4f

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +19 -29
  2. README.md +26 -6
  3. supervisord.conf +26 -0
  4. 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
- # 安装系统依赖 + Python3 pip
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
- # 创建非 root
18
- RUN useradd -m -u 1000 -s /bin/bash user && \
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
- # 创建工作目录(在切换用户之前以 root 身份创建)
26
- RUN mkdir -p /workspace && chown -R user:user /workspace
 
27
 
28
- # 切换到非 root 用户
29
  USER user
30
  WORKDIR /home/user
31
 
32
- # 配置 SSH known_hosts
33
- RUN mkdir -p /home/user/.ssh && \
34
- ssh-keyscan -T 5 github.com 2>/dev/null >> /home/user/.ssh/known_hosts || true
35
 
36
- # 安装 OpenCode
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
- # 设置 PATH包含 opencode 和 Python 用户二进制目录
41
- ENV PATH="/home/user/.opencode/bin:/home/user/.local/bin:${PATH}"
42
 
43
- # 复制 Web 服务代码到工作目录(此时 user 用户有写入权限)
44
- COPY --chown=user requirements.txt app.py /workspace/
 
45
 
46
- # 安装 Python 依赖
47
- RUN pip3 install --user --no-cache-dir -r /workspace/requirements.txt
48
 
49
- # 切换到工作目录
50
- WORKDIR /workspace
51
 
52
- # 启动 FastAPI 服务(监听 7860 端口)
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: Opencode
3
- emoji: 🐢
4
- colorFrom: yellow
5
- colorTo: pink
6
  sdk: docker
 
7
  pinned: false
8
- license: apache-2.0
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)