File size: 4,050 Bytes
b7bd3ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
106
107
108
109
110
111
112
from __future__ import annotations
import shutil, tempfile, textwrap, time
from pathlib import Path
from typing import Dict
import git
from huggingface_hub import HfApi
from huggingface_hub.errors import HfHubHTTPError
from .exceptions import (
    DockerfileMissingError,
    RepoCloneError,
    SpaceBuildTimeoutError,
    SpaceCreationError,
    SpaceDeployError,
)

__all__ = [
    "deploy_space",
    "SpaceDeployError",
]

from .utils import _chmod_and_retry


def deploy_space(*, hf_token: str, git_repo_url: str, deploy_path: str, space_name: str, space_port: int, description: str, env_vars: Dict[str, str], private: bool) -> str:
    deploy_path = deploy_path.strip(".").strip("/")
    api = HfApi(token=hf_token)
    try:
        username = api.whoami().get("name", None)
        repo_id = f"{username}/{space_name}"
        api.create_repo(
            repo_id=repo_id,
            repo_type="space",
            space_sdk="docker",
            private=private,
            # space_variables=[{}],
            space_secrets=[{"key": k, "value": v} for k, v in env_vars.items()],
            exist_ok=False,
        )
    except HfHubHTTPError as exc:
        raise SpaceCreationError(exc) from exc

    tmp = tempfile.mkdtemp(prefix="hf_space_")
    try:
        try:
            if not deploy_path:
                git.Repo.clone_from(
                    git_repo_url,
                    tmp,
                    depth=1,
                    multi_options=["--single-branch"]
                )
            else:
                repo = git.Repo.clone_from(
                    git_repo_url, tmp,
                    depth=1,
                    no_checkout=True,
                    filter=["tree:0"],
                    sparse=True,
                    multi_options=["--single-branch"],
                )
                repo.git.sparse_checkout("init", "--no-cone")
                repo.git.sparse_checkout("set", "--no-cone", f"/{deploy_path}/**")
                repo.git.checkout()

                # 平移文件到根目录
                src = Path(tmp, deploy_path)
                for item in src.iterdir():
                    shutil.move(item, tmp)  # 根目录目前是空的
                shutil.rmtree(src)
        except git.GitCommandError as exc:
            raise RepoCloneError(str(exc)) from exc

        git_dir = Path(tmp, ".git")
        if git_dir.exists() and git_dir.is_dir():
            shutil.rmtree(git_dir, onerror=_chmod_and_retry)

        if not Path(tmp, "Dockerfile").exists():
            raise DockerfileMissingError()

        readme = Path(tmp, "README.md")
        header = (
            f"---\n"
            f"title: \"{space_name}\"\n"
            f"emoji: \"🚀\"\n"
            f"colorFrom: blue\n"
            f"colorTo: green\n"
            f"sdk: docker\n"
            f"app_port: {space_port}\n"
            f"---\n"
        )
        badge = (
            f"### 🚀 一键部署\n"
            f"[![Deploy with HFSpaceDeploy](https://img.shields.io/badge/Deploy_with-HFSpaceDeploy-green?style=social&logo=rocket)](https://github.com/kfcx/HFSpaceDeploy)\n\n"
            f"本项目由[HFSpaceDeploy](https://github.com/kfcx/HFSpaceDeploy)一键部署\n"
        )
        readme.write_text(f"{header}\n{badge}\n{description}\n", encoding="utf-8")
        api.upload_folder(folder_path=tmp, repo_id=repo_id, repo_type="space", ignore_patterns=[".git"])
    finally:
        shutil.rmtree(tmp, ignore_errors=True)

    deadline = time.time() + 15 * 60
    while time.time() < deadline:
        stage = api.get_space_runtime(repo_id).stage
        if stage == "RUNNING":
            return f"https://huggingface.co/spaces/{repo_id}"
        if stage in ("BUILD_ERROR", "CONFIG_ERROR", "RUNTIME_ERROR",):
            raise SpaceDeployError("Space failed during config/build/runtime")
        if stage in ("NO_APP_FILE", "STOPPED", "PAUSED", "DELETING", ):
            raise SpaceDeployError("Space is currently unavailable")
        time.sleep(5)
    raise SpaceBuildTimeoutError()