Spaces:
				
			
			
	
			
			
		Build error
		
	
	
	
			
			
	
	
	
	
		
		
		Build error
		
	
		testbot
		
	commited on
		
		
					Commit 
							
							·
						
						a5e8053
	
1
								Parent(s):
							
							8b1b383
								
Use huggingface_hub.WebhooksServer
Browse files- app.py +5 -5
 - gradio_webhooks.py +0 -110
 - requirements.txt +2 -2
 - simple.py +0 -12
 - ui.py +1 -1
 
    	
        app.py
    CHANGED
    
    | 
         @@ -16,8 +16,8 @@ from huggingface_hub import ( 
     | 
|
| 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 huggingface_hub.utils import RepositoryNotFoundError
         
     | 
| 22 | 
         
             
            from ui import generate_ui
         
     | 
| 23 | 
         | 
| 
         @@ -25,11 +25,11 @@ login(token=os.getenv("HF_TOKEN")) 
     | 
|
| 25 | 
         | 
| 26 | 
         
             
            CI_BOT_NAME = "spaces-ci-bot"
         
     | 
| 27 | 
         | 
| 28 | 
         
            -
            app =  
     | 
| 29 | 
         | 
| 30 | 
         | 
| 31 | 
         
            -
            @app.add_webhook 
     | 
| 32 | 
         
            -
            async def  
     | 
| 33 | 
         
             
                if payload.repo.type != "space":
         
     | 
| 34 | 
         
             
                    raise HTTPException(400, f"Must be a Space, not {payload.repo.type}")
         
     | 
| 35 | 
         | 
| 
         @@ -232,4 +232,4 @@ _(This is an automated message.)_ 
     | 
|
| 232 | 
         
             
            """
         
     | 
| 233 | 
         | 
| 234 | 
         | 
| 235 | 
         
            -
            app. 
     | 
| 
         | 
|
| 16 | 
         
             
            )
         
     | 
| 17 | 
         
             
            from huggingface_hub.repocard import RepoCard
         
     | 
| 18 | 
         
             
            from requests import HTTPError
         
     | 
| 
         | 
|
| 19 | 
         
             
            from huggingface_hub import login
         
     | 
| 20 | 
         
            +
            from huggingface_hub import WebhooksServer, WebhookPayload
         
     | 
| 21 | 
         
             
            from huggingface_hub.utils import RepositoryNotFoundError
         
     | 
| 22 | 
         
             
            from ui import generate_ui
         
     | 
| 23 | 
         | 
| 
         | 
|
| 25 | 
         | 
| 26 | 
         
             
            CI_BOT_NAME = "spaces-ci-bot"
         
     | 
| 27 | 
         | 
| 28 | 
         
            +
            app = WebhooksServer(ui=generate_ui())
         
     | 
| 29 | 
         | 
| 30 | 
         | 
| 31 | 
         
            +
            @app.add_webhook
         
     | 
| 32 | 
         
            +
            async def trigger_ci_on_pr(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 | 
         | 
| 
         | 
|
| 232 | 
         
             
            """
         
     | 
| 233 | 
         | 
| 234 | 
         | 
| 235 | 
         
            +
            app.run()
         
     | 
    	
        gradio_webhooks.py
    DELETED
    
    | 
         @@ -1,110 +0,0 @@ 
     | 
|
| 1 | 
         
            -
            import os
         
     | 
| 2 | 
         
            -
            from typing import Literal, Optional, Set
         
     | 
| 3 | 
         
            -
             
     | 
| 4 | 
         
            -
            import gradio as gr
         
     | 
| 5 | 
         
            -
            from fastapi import Request
         
     | 
| 6 | 
         
            -
            from fastapi.responses import JSONResponse
         
     | 
| 7 | 
         
            -
            from pydantic import BaseModel
         
     | 
| 8 | 
         
            -
             
     | 
| 9 | 
         
            -
             
     | 
| 10 | 
         
            -
            class GradioWebhookApp:
         
     | 
| 11 | 
         
            -
                """
         
     | 
| 12 | 
         
            -
                ```py
         
     | 
| 13 | 
         
            -
                from gradio_webhooks import GradioWebhookApp
         
     | 
| 14 | 
         
            -
             
     | 
| 15 | 
         
            -
                app = GradioWebhookApp()
         
     | 
| 16 | 
         
            -
             
     | 
| 17 | 
         
            -
             
     | 
| 18 | 
         
            -
                @app.add_webhook("/test_webhook")
         
     | 
| 19 | 
         
            -
                async def hello():
         
     | 
| 20 | 
         
            -
                    return {"in_gradio": True}
         
     | 
| 21 | 
         
            -
             
     | 
| 22 | 
         
            -
             
     | 
| 23 | 
         
            -
                app.ready()
         
     | 
| 24 | 
         
            -
                ```
         
     | 
| 25 | 
         
            -
                """
         
     | 
| 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 | 
         
            -
             
     | 
| 40 | 
         
            -
                    # Add auth middleware to check the "X-Webhook-Secret" header
         
     | 
| 41 | 
         
            -
                    self._webhook_secret = webhook_secret or os.getenv("WEBHOOK_SECRET")
         
     | 
| 42 | 
         
            -
                    if self._webhook_secret is None:
         
     | 
| 43 | 
         
            -
                        print(
         
     | 
| 44 | 
         
            -
                            "\nWebhook secret is not defined. This means your webhook endpoints will be open to everyone."
         
     | 
| 45 | 
         
            -
                        )
         
     | 
| 46 | 
         
            -
                        print(
         
     | 
| 47 | 
         
            -
                            "To add a secret, set `WEBHOOK_SECRET` as environment variable or pass it at initialization: "
         
     | 
| 48 | 
         
            -
                            "\n\t`app = GradioWebhookApp(webhook_secret='my_secret', ...)`"
         
     | 
| 49 | 
         
            -
                        )
         
     | 
| 50 | 
         
            -
                        print(
         
     | 
| 51 | 
         
            -
                            "For more details about Webhook secrets, please refer to https://huggingface.co/docs/hub/webhooks#webhook-secret."
         
     | 
| 52 | 
         
            -
                        )
         
     | 
| 53 | 
         
            -
                    else:
         
     | 
| 54 | 
         
            -
                        print("\nWebhook secret is correctly defined.")
         
     | 
| 55 | 
         
            -
                    app.middleware("http")(self._webhook_secret_middleware)
         
     | 
| 56 | 
         
            -
             
     | 
| 57 | 
         
            -
                def add_webhook(self, path: str):
         
     | 
| 58 | 
         
            -
                    """Decorator to add a webhook to the server app."""
         
     | 
| 59 | 
         
            -
                    self.webhook_paths.add(path)
         
     | 
| 60 | 
         
            -
                    return self.fastapi_app.post(path)
         
     | 
| 61 | 
         
            -
             
     | 
| 62 | 
         
            -
                def ready(self) -> None:
         
     | 
| 63 | 
         
            -
                    """Set the app as "ready" and block main thread to keep it running."""
         
     | 
| 64 | 
         
            -
                    url = (
         
     | 
| 65 | 
         
            -
                        self.gradio_app.share_url
         
     | 
| 66 | 
         
            -
                        if self.gradio_app.share_url is not None
         
     | 
| 67 | 
         
            -
                        else self.gradio_app.local_url
         
     | 
| 68 | 
         
            -
                    ).strip("/")
         
     | 
| 69 | 
         
            -
                    print("\nWebhooks are correctly setup and ready to use:")
         
     | 
| 70 | 
         
            -
                    print("\n".join(f"  - POST {url}{webhook}" for webhook in self.webhook_paths))
         
     | 
| 71 | 
         
            -
                    print("Go to https://huggingface.co/settings/webhooks to setup your webhooks.")
         
     | 
| 72 | 
         
            -
                    self.gradio_app.block_thread()
         
     | 
| 73 | 
         
            -
             
     | 
| 74 | 
         
            -
                async def _webhook_secret_middleware(self, request: Request, call_next) -> None:
         
     | 
| 75 | 
         
            -
                    """Middleware to check "X-Webhook-Secret" header on every webhook request."""
         
     | 
| 76 | 
         
            -
                    if request.url.path in self.webhook_paths:
         
     | 
| 77 | 
         
            -
                        if self._webhook_secret is not None:
         
     | 
| 78 | 
         
            -
                            request_secret = request.headers.get("x-webhook-secret")
         
     | 
| 79 | 
         
            -
                            if request_secret is None:
         
     | 
| 80 | 
         
            -
                                return JSONResponse(
         
     | 
| 81 | 
         
            -
                                    {"error": "x-webhook-secret header not set."}, status_code=401
         
     | 
| 82 | 
         
            -
                                )
         
     | 
| 83 | 
         
            -
                            if request_secret != self._webhook_secret:
         
     | 
| 84 | 
         
            -
                                return JSONResponse(
         
     | 
| 85 | 
         
            -
                                    {"error": "Invalid webhook secret."}, status_code=403
         
     | 
| 86 | 
         
            -
                                )
         
     | 
| 87 | 
         
            -
                    return await call_next(request)
         
     | 
| 88 | 
         
            -
             
     | 
| 89 | 
         
            -
             
     | 
| 90 | 
         
            -
            class WebhookPayloadEvent(BaseModel):
         
     | 
| 91 | 
         
            -
                action: Literal["create", "update", "delete"]
         
     | 
| 92 | 
         
            -
                scope: str
         
     | 
| 93 | 
         
            -
             
     | 
| 94 | 
         
            -
             
     | 
| 95 | 
         
            -
            class WebhookPayloadRepo(BaseModel):
         
     | 
| 96 | 
         
            -
                type: Literal["dataset", "model", "space"]
         
     | 
| 97 | 
         
            -
                name: str
         
     | 
| 98 | 
         
            -
                private: bool
         
     | 
| 99 | 
         
            -
             
     | 
| 100 | 
         
            -
             
     | 
| 101 | 
         
            -
            class WebhookPayloadDiscussion(BaseModel):
         
     | 
| 102 | 
         
            -
                num: int
         
     | 
| 103 | 
         
            -
                isPullRequest: bool
         
     | 
| 104 | 
         
            -
                status: Literal["open", "closed", "merged"]
         
     | 
| 105 | 
         
            -
             
     | 
| 106 | 
         
            -
             
     | 
| 107 | 
         
            -
            class WebhookPayload(BaseModel):
         
     | 
| 108 | 
         
            -
                event: WebhookPayloadEvent
         
     | 
| 109 | 
         
            -
                repo: WebhookPayloadRepo
         
     | 
| 110 | 
         
            -
                discussion: Optional[WebhookPayloadDiscussion]
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
    	
        requirements.txt
    CHANGED
    
    | 
         @@ -1,2 +1,2 @@ 
     | 
|
| 1 | 
         
            -
             
     | 
| 2 | 
         
            -
            huggingface_hub 
     | 
| 
         | 
|
| 1 | 
         
            +
            gradio
         
     | 
| 2 | 
         
            +
            git+https://github.com/huggingface/huggingface_hub@feat-webhook-app#egg=huggingface_hub
         
     | 
    	
        simple.py
    DELETED
    
    | 
         @@ -1,12 +0,0 @@ 
     | 
|
| 1 | 
         
            -
            from gradio_webhooks import GradioWebhookApp, WebhookPayload
         
     | 
| 2 | 
         
            -
             
     | 
| 3 | 
         
            -
            app = GradioWebhookApp(webhook_secret="my_dummy_secret")
         
     | 
| 4 | 
         
            -
             
     | 
| 5 | 
         
            -
             
     | 
| 6 | 
         
            -
            @app.add_webhook("/my_webhook")
         
     | 
| 7 | 
         
            -
            async def hello(payload: WebhookPayload):
         
     | 
| 8 | 
         
            -
                print(f"Received webhook for repo {payload.repo.name}")
         
     | 
| 9 | 
         
            -
                return {"processed": True}
         
     | 
| 10 | 
         
            -
             
     | 
| 11 | 
         
            -
             
     | 
| 12 | 
         
            -
            app.ready()
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
    	
        ui.py
    CHANGED
    
    | 
         @@ -21,7 +21,7 @@ You are a few clicks away from having the CI Bot configured on your Spaces. No c 
     | 
|
| 21 | 
         
             
            #### 1. Go to your [settings page](https://huggingface.co/settings/webhooks) > "Add a new webhook"
         
     | 
| 22 | 
         
             
            #### 2. Configure your webhook (see image below)
         
     | 
| 23 | 
         
             
            1. Filter which user, organization or Space you want to watch. It can be a Space you own or not. In this example, all Spaces from `Wauplin` are watched.
         
     | 
| 24 | 
         
            -
            2. Set the webhook URL to <https://spaces-ci-bot-webhook.hf.space/ 
     | 
| 25 | 
         
             
            3. Make sure to watch both "repo changes" and "community"
         
     | 
| 26 | 
         | 
| 27 | 
         
             
            
         
     | 
| 
         | 
|
| 21 | 
         
             
            #### 1. Go to your [settings page](https://huggingface.co/settings/webhooks) > "Add a new webhook"
         
     | 
| 22 | 
         
             
            #### 2. Configure your webhook (see image below)
         
     | 
| 23 | 
         
             
            1. Filter which user, organization or Space you want to watch. It can be a Space you own or not. In this example, all Spaces from `Wauplin` are watched.
         
     | 
| 24 | 
         
            +
            2. Set the webhook URL to <https://spaces-ci-bot-webhook.hf.space/webhooks/trigger_ci_on_pr>.
         
     | 
| 25 | 
         
             
            3. Make sure to watch both "repo changes" and "community"
         
     | 
| 26 | 
         | 
| 27 | 
         
             
            
         
     |