Spaces:
Build error
Build error
New UI to register/unregister spaces on demand
Browse files- app.py +39 -43
- database.py +75 -0
- gradio_webhooks.py +4 -16
- ui.py +84 -0
app.py
CHANGED
@@ -2,7 +2,7 @@ import os
|
|
2 |
from pathlib import Path
|
3 |
from typing import Literal
|
4 |
|
5 |
-
from fastapi import BackgroundTasks, HTTPException
|
6 |
from huggingface_hub import (
|
7 |
CommitOperationAdd,
|
8 |
CommitOperationDelete,
|
@@ -16,42 +16,47 @@ from huggingface_hub import (
|
|
16 |
)
|
17 |
from huggingface_hub.repocard import RepoCard
|
18 |
from requests import HTTPError
|
19 |
-
|
20 |
from gradio_webhooks import GradioWebhookApp, WebhookPayload
|
|
|
|
|
|
|
21 |
|
22 |
-
|
23 |
|
|
|
24 |
|
25 |
-
app = GradioWebhookApp()
|
26 |
|
27 |
|
28 |
@app.add_webhook("/webhook")
|
29 |
async def post_webhook(payload: WebhookPayload, task_queue: BackgroundTasks):
|
30 |
if payload.repo.type != "space":
|
31 |
-
print("HTTP 400: not a space")
|
32 |
raise HTTPException(400, f"Must be a Space, not {payload.repo.type}")
|
33 |
|
34 |
space_id = payload.repo.name
|
|
|
|
|
35 |
|
|
|
36 |
if (
|
|
|
37 |
payload.event.scope.startswith("discussion")
|
38 |
and payload.event.action == "create"
|
39 |
and payload.discussion is not None
|
40 |
and payload.discussion.isPullRequest
|
41 |
and payload.discussion.status == "open"
|
42 |
):
|
43 |
-
# New PR!
|
44 |
if not is_pr_synced(space_id=space_id, pr_num=payload.discussion.num):
|
|
|
45 |
task_queue.add_task(
|
46 |
sync_ci_space,
|
47 |
space_id=space_id,
|
48 |
pr_num=payload.discussion.num,
|
49 |
private=payload.repo.private,
|
50 |
)
|
51 |
-
|
52 |
-
else:
|
53 |
-
print("New comment on PR but CI space already synced")
|
54 |
elif (
|
|
|
55 |
payload.event.scope.startswith("discussion")
|
56 |
and payload.event.action == "update"
|
57 |
and payload.discussion is not None
|
@@ -61,47 +66,44 @@ async def post_webhook(payload: WebhookPayload, task_queue: BackgroundTasks):
|
|
61 |
or payload.discussion.status == "closed"
|
62 |
)
|
63 |
):
|
64 |
-
# PR merged or closed!
|
65 |
task_queue.add_task(
|
66 |
delete_ci_space,
|
67 |
space_id=space_id,
|
68 |
pr_num=payload.discussion.num,
|
69 |
)
|
70 |
-
|
71 |
elif (
|
|
|
72 |
payload.event.scope.startswith("repo.content")
|
73 |
and payload.event.action == "update"
|
74 |
):
|
75 |
# New repo change. Is it a commit on a PR?
|
76 |
# => loop through all PRs and check if new changes happened
|
77 |
-
|
78 |
-
for discussion in get_repo_discussions(
|
79 |
-
repo_id=space_id, repo_type="space", token=HF_TOKEN
|
80 |
-
):
|
81 |
if discussion.is_pull_request and discussion.status == "open":
|
82 |
if not is_pr_synced(space_id=space_id, pr_num=discussion.num):
|
|
|
83 |
task_queue.add_task(
|
84 |
sync_ci_space,
|
85 |
space_id=space_id,
|
86 |
pr_num=discussion.num,
|
87 |
private=payload.repo.private,
|
88 |
)
|
89 |
-
|
90 |
-
print(f"Done looping over PRs.")
|
91 |
-
else:
|
92 |
-
print(f"Webhook ignored.")
|
93 |
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
96 |
|
97 |
|
98 |
def is_pr_synced(space_id: str, pr_num: int) -> bool:
|
99 |
# What is the last synced commit for this PR?
|
100 |
ci_space_id = _get_ci_space_id(space_id=space_id, pr_num=pr_num)
|
101 |
try:
|
102 |
-
card = RepoCard.load(
|
103 |
-
repo_id_or_path=ci_space_id, repo_type="space", token=HF_TOKEN
|
104 |
-
)
|
105 |
last_synced_sha = getattr(card.data, "synced_sha", None)
|
106 |
except HTTPError:
|
107 |
last_synced_sha = None
|
@@ -124,7 +126,6 @@ def sync_ci_space(space_id: str, pr_num: int, private: bool) -> None:
|
|
124 |
repo_type="space",
|
125 |
space_sdk="docker",
|
126 |
private=private,
|
127 |
-
token=HF_TOKEN,
|
128 |
)
|
129 |
is_new = True
|
130 |
except HTTPError as err:
|
@@ -136,10 +137,7 @@ def sync_ci_space(space_id: str, pr_num: int, private: bool) -> None:
|
|
136 |
# Download space codebase from PR revision
|
137 |
snapshot_path = Path(
|
138 |
snapshot_download(
|
139 |
-
repo_id=space_id,
|
140 |
-
revision=f"refs/pr/{pr_num}",
|
141 |
-
repo_type="space",
|
142 |
-
token=HF_TOKEN,
|
143 |
)
|
144 |
)
|
145 |
|
@@ -170,7 +168,6 @@ def sync_ci_space(space_id: str, pr_num: int, private: bool) -> None:
|
|
170 |
repo_type="space",
|
171 |
operations=operations,
|
172 |
commit_message=f"Sync CI Space with PR {pr_num}.",
|
173 |
-
token=HF_TOKEN,
|
174 |
)
|
175 |
|
176 |
# Post a comment on the PR
|
@@ -180,7 +177,7 @@ def sync_ci_space(space_id: str, pr_num: int, private: bool) -> None:
|
|
180 |
def delete_ci_space(space_id: str, pr_num: int) -> None:
|
181 |
# Delete
|
182 |
ci_space_id = _get_ci_space_id(space_id=space_id, pr_num=pr_num)
|
183 |
-
delete_repo(repo_id=ci_space_id, repo_type="space"
|
184 |
|
185 |
# Notify about deletion
|
186 |
notify_pr(space_id=space_id, pr_num=pr_num, action="delete")
|
@@ -200,38 +197,37 @@ def notify_pr(
|
|
200 |
raise ValueError(f"Status {action} not handled.")
|
201 |
|
202 |
comment_discussion(
|
203 |
-
repo_id=space_id,
|
204 |
-
repo_type="space",
|
205 |
-
discussion_num=pr_num,
|
206 |
-
comment=comment,
|
207 |
-
token=HF_TOKEN,
|
208 |
)
|
209 |
|
210 |
|
211 |
def _get_ci_space_id(space_id: str, pr_num: int) -> str:
|
212 |
-
return f"{space_id}-ci-pr-{pr_num}"
|
213 |
|
214 |
|
215 |
NOTIFICATION_TEMPLATE_CREATE = """\
|
216 |
Hey there!
|
217 |
-
Following the creation of this PR,
|
218 |
Any changes pushed to this PR will be synced with the test Space.
|
219 |
|
220 |
-
(
|
|
|
|
|
|
|
221 |
"""
|
222 |
|
223 |
NOTIFICATION_TEMPLATE_UPDATE = """\
|
224 |
Hey there!
|
225 |
-
Following new commits that happened in this PR, the
|
226 |
|
227 |
-
(This is an automated message)
|
228 |
"""
|
229 |
|
230 |
NOTIFICATION_TEMPLATE_DELETE = """\
|
231 |
Hey there!
|
232 |
-
PR is now merged/closed. The
|
233 |
|
234 |
-
(This is an automated message)
|
235 |
"""
|
236 |
|
237 |
|
|
|
2 |
from pathlib import Path
|
3 |
from typing import Literal
|
4 |
|
5 |
+
from fastapi import BackgroundTasks, HTTPException, Response, status
|
6 |
from huggingface_hub import (
|
7 |
CommitOperationAdd,
|
8 |
CommitOperationDelete,
|
|
|
16 |
)
|
17 |
from huggingface_hub.repocard import RepoCard
|
18 |
from requests import HTTPError
|
|
|
19 |
from gradio_webhooks import GradioWebhookApp, WebhookPayload
|
20 |
+
from huggingface_hub import login
|
21 |
+
from ui import generate_ui
|
22 |
+
from database import is_space_registered
|
23 |
|
24 |
+
login(token=os.getenv("HF_TOKEN"))
|
25 |
|
26 |
+
CI_BOT_NAME = "spaces-ci-bot"
|
27 |
|
28 |
+
app = GradioWebhookApp(ui=generate_ui())
|
29 |
|
30 |
|
31 |
@app.add_webhook("/webhook")
|
32 |
async def post_webhook(payload: WebhookPayload, task_queue: BackgroundTasks):
|
33 |
if payload.repo.type != "space":
|
|
|
34 |
raise HTTPException(400, f"Must be a Space, not {payload.repo.type}")
|
35 |
|
36 |
space_id = payload.repo.name
|
37 |
+
if not is_space_registered(space_id):
|
38 |
+
return "Space not in the watchlist."
|
39 |
|
40 |
+
has_task = False
|
41 |
if (
|
42 |
+
# Means "a new PR has been opened"
|
43 |
payload.event.scope.startswith("discussion")
|
44 |
and payload.event.action == "create"
|
45 |
and payload.discussion is not None
|
46 |
and payload.discussion.isPullRequest
|
47 |
and payload.discussion.status == "open"
|
48 |
):
|
|
|
49 |
if not is_pr_synced(space_id=space_id, pr_num=payload.discussion.num):
|
50 |
+
# New PR! Sync task scheduled
|
51 |
task_queue.add_task(
|
52 |
sync_ci_space,
|
53 |
space_id=space_id,
|
54 |
pr_num=payload.discussion.num,
|
55 |
private=payload.repo.private,
|
56 |
)
|
57 |
+
has_task = True
|
|
|
|
|
58 |
elif (
|
59 |
+
# Means "a PR has been merged or closed"
|
60 |
payload.event.scope.startswith("discussion")
|
61 |
and payload.event.action == "update"
|
62 |
and payload.discussion is not None
|
|
|
66 |
or payload.discussion.status == "closed"
|
67 |
)
|
68 |
):
|
|
|
69 |
task_queue.add_task(
|
70 |
delete_ci_space,
|
71 |
space_id=space_id,
|
72 |
pr_num=payload.discussion.num,
|
73 |
)
|
74 |
+
has_task = True
|
75 |
elif (
|
76 |
+
# Means "some content has been pushed to the Space" (any branch)
|
77 |
payload.event.scope.startswith("repo.content")
|
78 |
and payload.event.action == "update"
|
79 |
):
|
80 |
# New repo change. Is it a commit on a PR?
|
81 |
# => loop through all PRs and check if new changes happened
|
82 |
+
for discussion in get_repo_discussions(repo_id=space_id, repo_type="space"):
|
|
|
|
|
|
|
83 |
if discussion.is_pull_request and discussion.status == "open":
|
84 |
if not is_pr_synced(space_id=space_id, pr_num=discussion.num):
|
85 |
+
# Found a PR that is not yet synced
|
86 |
task_queue.add_task(
|
87 |
sync_ci_space,
|
88 |
space_id=space_id,
|
89 |
pr_num=discussion.num,
|
90 |
private=payload.repo.private,
|
91 |
)
|
92 |
+
has_task = True
|
|
|
|
|
|
|
93 |
|
94 |
+
if has_task:
|
95 |
+
return Response(
|
96 |
+
"Task scheduled to sync/delete Space", status_code=status.HTTP_202_ACCEPTED
|
97 |
+
)
|
98 |
+
else:
|
99 |
+
return Response("No task scheduled", status_code=status.HTTP_202_ACCEPTED)
|
100 |
|
101 |
|
102 |
def is_pr_synced(space_id: str, pr_num: int) -> bool:
|
103 |
# What is the last synced commit for this PR?
|
104 |
ci_space_id = _get_ci_space_id(space_id=space_id, pr_num=pr_num)
|
105 |
try:
|
106 |
+
card = RepoCard.load(repo_id_or_path=ci_space_id, repo_type="space")
|
|
|
|
|
107 |
last_synced_sha = getattr(card.data, "synced_sha", None)
|
108 |
except HTTPError:
|
109 |
last_synced_sha = None
|
|
|
126 |
repo_type="space",
|
127 |
space_sdk="docker",
|
128 |
private=private,
|
|
|
129 |
)
|
130 |
is_new = True
|
131 |
except HTTPError as err:
|
|
|
137 |
# Download space codebase from PR revision
|
138 |
snapshot_path = Path(
|
139 |
snapshot_download(
|
140 |
+
repo_id=space_id, revision=f"refs/pr/{pr_num}", repo_type="space"
|
|
|
|
|
|
|
141 |
)
|
142 |
)
|
143 |
|
|
|
168 |
repo_type="space",
|
169 |
operations=operations,
|
170 |
commit_message=f"Sync CI Space with PR {pr_num}.",
|
|
|
171 |
)
|
172 |
|
173 |
# Post a comment on the PR
|
|
|
177 |
def delete_ci_space(space_id: str, pr_num: int) -> None:
|
178 |
# Delete
|
179 |
ci_space_id = _get_ci_space_id(space_id=space_id, pr_num=pr_num)
|
180 |
+
delete_repo(repo_id=ci_space_id, repo_type="space")
|
181 |
|
182 |
# Notify about deletion
|
183 |
notify_pr(space_id=space_id, pr_num=pr_num, action="delete")
|
|
|
197 |
raise ValueError(f"Status {action} not handled.")
|
198 |
|
199 |
comment_discussion(
|
200 |
+
repo_id=space_id, repo_type="space", discussion_num=pr_num, comment=comment
|
|
|
|
|
|
|
|
|
201 |
)
|
202 |
|
203 |
|
204 |
def _get_ci_space_id(space_id: str, pr_num: int) -> str:
|
205 |
+
return f"{CI_BOT_NAME}-{space_id.replace('/', '-')}-ci-pr-{pr_num}"
|
206 |
|
207 |
|
208 |
NOTIFICATION_TEMPLATE_CREATE = """\
|
209 |
Hey there!
|
210 |
+
Following the creation of this PR, an ephemeral Space [{ci_space_id}](https://huggingface.co/spaces/{ci_space_id}) has been launched.
|
211 |
Any changes pushed to this PR will be synced with the test Space.
|
212 |
|
213 |
+
If your Space needs configuration (secrets or upgraded hardware), you must duplicate this ephemeral Space to your account and configure the settings by yourself.
|
214 |
+
You are responsible of making sure that the changes introduced in the PR are not harmful (leak secret, run malicious scripts,...)
|
215 |
+
|
216 |
+
(This is an automated message. To disable the Spaces CI Bot, please unregister using [this form](https://huggingface.co/spaces/spaces-ci-bot/webhook))
|
217 |
"""
|
218 |
|
219 |
NOTIFICATION_TEMPLATE_UPDATE = """\
|
220 |
Hey there!
|
221 |
+
Following new commits that happened in this PR, the ephemeral Space [{ci_space_id}](https://huggingface.co/spaces/{ci_space_id}) has been updated.
|
222 |
|
223 |
+
(This is an automated message. To disable the Spaces CI Bot, please unregister using [this form](https://huggingface.co/spaces/spaces-ci-bot/webhook))
|
224 |
"""
|
225 |
|
226 |
NOTIFICATION_TEMPLATE_DELETE = """\
|
227 |
Hey there!
|
228 |
+
PR is now merged/closed. The ephemeral Space has been deleted.
|
229 |
|
230 |
+
(This is an automated message. To disable the Spaces CI Bot, please unregister using [this form](https://huggingface.co/spaces/spaces-ci-bot/webhook))
|
231 |
"""
|
232 |
|
233 |
|
database.py
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from huggingface_hub import hf_hub_download, upload_file, space_info
|
2 |
+
from huggingface_hub.utils import HfHubHTTPError
|
3 |
+
from pathlib import Path
|
4 |
+
from typing import Set
|
5 |
+
|
6 |
+
DATABASE_REPO_ID = "spaces-ci-bot/webhook"
|
7 |
+
DATABASE_FILE = "registered_spaces.txt"
|
8 |
+
|
9 |
+
MAX_NB_ATTEMPTS = 10
|
10 |
+
|
11 |
+
|
12 |
+
def is_space_existing(space_id: str) -> bool:
|
13 |
+
try:
|
14 |
+
space_info(repo_id=space_id)
|
15 |
+
return True
|
16 |
+
except HfHubHTTPError:
|
17 |
+
return False
|
18 |
+
|
19 |
+
|
20 |
+
def is_space_registered(space_id: str) -> bool:
|
21 |
+
return space_id in get_registered_spaces()
|
22 |
+
|
23 |
+
|
24 |
+
def get_registered_spaces() -> Set[str]:
|
25 |
+
return _read_database(_get_latest_file())
|
26 |
+
|
27 |
+
|
28 |
+
def update_status(space_id: str, should_watch: bool) -> None:
|
29 |
+
nb_attempts = 0
|
30 |
+
while True:
|
31 |
+
# Get registered spaces
|
32 |
+
filepath = _get_latest_file()
|
33 |
+
registered_spaces = _read_database(filepath)
|
34 |
+
|
35 |
+
# Do nothing if:
|
36 |
+
# - need to register and already registered
|
37 |
+
# - need to unregister and already not registered
|
38 |
+
if (should_watch and space_id in registered_spaces) or (
|
39 |
+
not should_watch and space_id not in registered_spaces
|
40 |
+
):
|
41 |
+
return
|
42 |
+
|
43 |
+
# Else, (un)register new space
|
44 |
+
latest_revision = filepath.parent.name
|
45 |
+
if should_watch:
|
46 |
+
registered_spaces.add(space_id)
|
47 |
+
else:
|
48 |
+
registered_spaces.remove(space_id)
|
49 |
+
|
50 |
+
# Re-upload database and ensure no concurrent call happened
|
51 |
+
try:
|
52 |
+
nb_attempts += 1
|
53 |
+
upload_file(
|
54 |
+
path_in_repo=DATABASE_FILE,
|
55 |
+
repo_id=DATABASE_REPO_ID,
|
56 |
+
repo_type="dataset",
|
57 |
+
path_or_fileobj="\n".join(sorted(registered_spaces)).encode(),
|
58 |
+
commit_message=(
|
59 |
+
f"Register {space_id}" if should_watch else f"Unregister {space_id}"
|
60 |
+
),
|
61 |
+
parent_commit=latest_revision, # ensure consistency
|
62 |
+
)
|
63 |
+
return
|
64 |
+
except HfHubHTTPError:
|
65 |
+
# Retry X times before giving up (in case multiple registrations at the same time)
|
66 |
+
if nb_attempts == MAX_NB_ATTEMPTS:
|
67 |
+
raise
|
68 |
+
|
69 |
+
|
70 |
+
def _read_database(filepath: Path) -> Set[str]:
|
71 |
+
return set(filepath.read_text().split())
|
72 |
+
|
73 |
+
|
74 |
+
def _get_latest_file() -> Path:
|
75 |
+
return Path(hf_hub_download(DATABASE_REPO_ID, DATABASE_FILE, repo_type="dataset"))
|
gradio_webhooks.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
import os
|
2 |
-
from
|
3 |
-
from typing import Literal, Optional, Set, Union
|
4 |
|
5 |
import gradio as gr
|
6 |
from fastapi import Request
|
@@ -27,25 +26,14 @@ class GradioWebhookApp:
|
|
27 |
|
28 |
def __init__(
|
29 |
self,
|
30 |
-
|
31 |
webhook_secret: Optional[str] = None,
|
32 |
) -> None:
|
33 |
-
# Use README.md as landing page or provide any markdown file
|
34 |
-
landing_path = Path(landing_path)
|
35 |
-
landing_content = landing_path.read_text()
|
36 |
-
if landing_path.name == "README.md":
|
37 |
-
landing_content = landing_content.split("---")[-1].strip()
|
38 |
-
|
39 |
-
# Simple gradio app with landing content
|
40 |
-
block = gr.Blocks()
|
41 |
-
with block:
|
42 |
-
gr.Markdown(landing_content)
|
43 |
-
|
44 |
# Launch gradio app:
|
45 |
# - as non-blocking so that webhooks can be added afterwards
|
46 |
# - as shared if launch locally (to receive webhooks)
|
47 |
-
app, _, _ =
|
48 |
-
self.gradio_app =
|
49 |
self.fastapi_app = app
|
50 |
self.webhook_paths: Set[str] = set()
|
51 |
|
|
|
1 |
import os
|
2 |
+
from typing import Literal, Optional, Set
|
|
|
3 |
|
4 |
import gradio as gr
|
5 |
from fastapi import Request
|
|
|
26 |
|
27 |
def __init__(
|
28 |
self,
|
29 |
+
ui: gr.Blocks,
|
30 |
webhook_secret: Optional[str] = None,
|
31 |
) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
# Launch gradio app:
|
33 |
# - as non-blocking so that webhooks can be added afterwards
|
34 |
# - as shared if launch locally (to receive webhooks)
|
35 |
+
app, _, _ = ui.launch(prevent_thread_lock=True, share=not ui.is_space)
|
36 |
+
self.gradio_app = ui
|
37 |
self.fastapi_app = app
|
38 |
self.webhook_paths: Set[str] = set()
|
39 |
|
ui.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from enum import Enum
|
3 |
+
from database import is_space_existing, is_space_registered, update_status
|
4 |
+
|
5 |
+
|
6 |
+
TITLE = "⚙️ Spaces CI Bot ⚙️"
|
7 |
+
DESCRIPTION = """
|
8 |
+
This app lets you register your Space with the Spaces CI Bot.
|
9 |
+
|
10 |
+
Once your repository is watched, any PR opened on your Space will be deployed as a temporary Space to test the changes
|
11 |
+
on your demo. Any changes pushed to the PRs will trigger a re-deployment. Once the PR is merged, the temporary Space is
|
12 |
+
deleted.
|
13 |
+
|
14 |
+
If your app needs some secrets to run or a specific hardware, you will need to duplicate the temporary Space and to
|
15 |
+
setup your environment.
|
16 |
+
"""
|
17 |
+
|
18 |
+
|
19 |
+
class Action(Enum):
|
20 |
+
REGISTER = "Enable CI Bot"
|
21 |
+
UNREGISTER = "Disable CI Bot"
|
22 |
+
CHECK_STATUS = "Check status"
|
23 |
+
|
24 |
+
|
25 |
+
def gradio_fn(space_id: str, action: str) -> str:
|
26 |
+
if not is_space_existing(space_id):
|
27 |
+
return f"""## Error
|
28 |
+
Could not find Space '**{space_id}**' on the Hub.
|
29 |
+
Please make sure you are trying to register a public repository.
|
30 |
+
"""
|
31 |
+
|
32 |
+
registered = is_space_registered(space_id)
|
33 |
+
if action == Action.REGISTER.value:
|
34 |
+
if registered:
|
35 |
+
return f"""## Did nothing
|
36 |
+
The Space '**{space_id}**' is already in the watchlist. Any PR opened on
|
37 |
+
this repository will trigger an ephemeral Space.
|
38 |
+
"""
|
39 |
+
else:
|
40 |
+
update_status(space_id, should_watch=True)
|
41 |
+
return f"""## Success
|
42 |
+
The Space '**{space_id}**' has been added to the watchlist. Any PR opened on
|
43 |
+
this repository will trigger an ephemeral Space.
|
44 |
+
"""
|
45 |
+
elif action == Action.UNREGISTER.value:
|
46 |
+
if not registered:
|
47 |
+
return f"""## Did nothing
|
48 |
+
The Space '**{space_id}**' is currently not in the watchlist.
|
49 |
+
"""
|
50 |
+
else:
|
51 |
+
update_status(space_id, should_watch=False)
|
52 |
+
return f"""## Success
|
53 |
+
The Space '**{space_id}**' has been removed from the watchlist.
|
54 |
+
"""
|
55 |
+
elif action == Action.CHECK_STATUS.value:
|
56 |
+
if registered:
|
57 |
+
return f"""## Watched
|
58 |
+
The Space '**{space_id}**' is already in the watchlist. Any PR opened on
|
59 |
+
this repository will trigger an ephemeral Space.
|
60 |
+
"""
|
61 |
+
else:
|
62 |
+
return f"""## Not watched
|
63 |
+
The Space '**{space_id}**' is currently not in the watchlist.
|
64 |
+
"""
|
65 |
+
else:
|
66 |
+
return f"**Error:** action {action} not implemented."
|
67 |
+
|
68 |
+
|
69 |
+
def generate_ui() -> gr.Blocks:
|
70 |
+
return gr.Interface(
|
71 |
+
fn=gradio_fn,
|
72 |
+
inputs=[
|
73 |
+
gr.Textbox(lines=1, placeholder="username/my_cool_space", label="Space ID"),
|
74 |
+
gr.Radio(
|
75 |
+
[action.value for action in Action],
|
76 |
+
value=Action.REGISTER.value,
|
77 |
+
label="What should I do?",
|
78 |
+
),
|
79 |
+
],
|
80 |
+
outputs=[gr.Markdown()],
|
81 |
+
title=TITLE,
|
82 |
+
description=DESCRIPTION,
|
83 |
+
allow_flagging="never",
|
84 |
+
)
|