File size: 4,943 Bytes
7403ab9
713516b
7403ab9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754e7df
7403ab9
 
 
 
 
 
b23c183
713516b
 
 
ee2d982
713516b
 
7403ab9
 
a0f6a59
8e54bfd
7403ab9
 
 
 
6a32b53
7403ab9
 
 
 
 
 
 
 
 
 
 
8332b48
7403ab9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0f7b0a
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
113
114
115
116
117
118
119
import re
import time

import gradio as gr
from huggingface_hub import CommitOperationAdd, HfApi, RepoUrl, SpaceCard

IMPORTS_REGEX = re.compile(r"^(from|import) .*$", re.MULTILINE)

PR_DESCRIPTION = """
This PR enables Space CI on your Space. **Gradio Space CI is a tool to create ephemeral Spaces for each PR opened on your Space repo.** The goal is to improve developer experience by making the review process as lean as possible.

### ⚙️ How it works:

- Listens to pull requests events:
  - If PR is opened => starts an ephemeral Space
  - If PR is updated => updates the Space
  - If PR is closed => deleted the Space
- Checks PR author:
  - If trusted author => ephemeral Space is configured with variables, secrets and hardware.
  - If not a trusted author => ephemeral Space is started without configuration.
  - Space owners are trusted by default. Additional "trusted authors" can be configuration manually.

### ⚠️ Before merging:

1. Check that the configuration is correct. By default the Space is configured to run ephemeral Spaces on a (free) CPU instance without any secrets.
2. You must set `HF_TOKEN` as a secret in your Space settings. Token must have 'write' permission. You can create a new one in your [User settings](https://huggingface.co/settings/token).

---
This is an automated PR created with https://huggingface.co/spaces/Wauplin/gradio-space-ci.
For more details about Space CI, checkout [this page](https://huggingface.co/spaces/Wauplin/gradio-space-ci/blob/main/README.md).
If you find any issues, please report here: https://huggingface.co/spaces/Wauplin/gradio-space-ci/discussions

Feel free to ignore this PR.
"""


def open_pr(space_id_or_url: str, oauth_token: gr.OAuthToken | None) -> str:
    if oauth_token is None:
        raise gr.Error("You must be logged in to open a PR. Click on 'Sign in with Huggingface' first.")
    token = oauth_token.token
    if oauth_token.expires_at < time.time():
        raise gr.Error("Token expired. Logout and try again.")

    api = HfApi(token=token)

    if not space_id_or_url:
        raise gr.Error("You must input a Space ID or URL.")
    space_id = (
        RepoUrl(space_id_or_url).repo_id if "https://huggingface.co/spaces/" in space_id_or_url else space_id_or_url
    )

    # 0. Check repo existence
    if not api.repo_exists(space_id, repo_type="space"):
        raise gr.Error(f"Space does not exist: 'https://huggingface.co/spaces/{space_id}'.")

    # 1. Add to requirements.txt
    if api.file_exists(repo_id=space_id, repo_type="space", filename="requirements.txt"):
        requirements_file = api.hf_hub_download(repo_id=space_id, repo_type="space", filename="requirements.txt")
        with open(requirements_file) as f:
            requirements = f.read()
    else:
        requirements = ""
    if "gradio-space-ci" not in requirements:
        requirements += "\ngradio-space-ci @ git+https://huggingface.co/spaces/Wauplin/gradio-space-ci@0.2.2\n"

    # 2. Configure CI in README.md
    card = SpaceCard.load(api.hf_hub_download(repo_id=space_id, repo_type="space", filename="README.md"))
    card.data["space_ci"] = {
        "trusted_authors": [],
        "secrets": [],
        "hardware": "cpu-basic",
        "storage": None,
    }
    if card.data.sdk != "gradio":
        raise gr.Error(f"Space must be a Gradio Space, not '{card.data.sdk}'.")
    app_file = card.data.app_file
    if app_file is None:
        raise gr.Error("Space must have an app_file defined their README.")
    if not api.file_exists(repo_id=space_id, repo_type="space", filename=app_file):
        raise gr.Error(f"Could not find app file '{app_file}' in Space repo.")

    # 3. Enable CI in app.py
    with open(api.hf_hub_download(repo_id=space_id, repo_type="space", filename=app_file)) as f:
        app = f.read()
    if "enable_space_ci()" in app:
        raise gr.Error("Space CI is already enabled.")

    all_imports = list(IMPORTS_REGEX.finditer(app))
    if len(all_imports) == 0:
        raise gr.Error("Could not find any imports in app.py.")
    last_import = all_imports[-1]

    app = (
        app[: last_import.end()]
        + "\n\n"
        + "from gradio_space_ci import enable_space_ci"
        + "\n\n"
        + "enable_space_ci()"
        + "\n\n"
        + app[last_import.end() :]
    )

    # 4. Push changes as a PR
    commit = api.create_commit(
        repo_id=space_id,
        repo_type="space",
        operations=[
            CommitOperationAdd(path_in_repo="README.md", path_or_fileobj=str(card).encode()),
            CommitOperationAdd(path_in_repo="requirements.txt", path_or_fileobj=requirements.encode()),
            CommitOperationAdd(path_in_repo=app_file, path_or_fileobj=app.encode()),
        ],
        commit_message="Enable Space CI",
        commit_description=PR_DESCRIPTION,
        create_pr=True,
    )
    assert commit.pr_url is not None  # since `create_pr=True`

    return commit.pr_url