{"id":13936941,"url":"https://github.com/jayfk/octohook","last_synced_at":"2025-03-17T09:30:56.936Z","repository":{"id":73239045,"uuid":"52008254","full_name":"jayfk/octohook","owner":"jayfk","description":null,"archived":false,"fork":false,"pushed_at":"2023-05-01T19:25:30.000Z","size":27,"stargazers_count":85,"open_issues_count":6,"forks_count":5,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-02-27T22:00:37.481Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jayfk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-18T13:16:18.000Z","updated_at":"2023-03-03T16:57:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"efcb7cbb-0cfa-44d0-9a27-eca7c8e02180","html_url":"https://github.com/jayfk/octohook","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayfk%2Foctohook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayfk%2Foctohook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayfk%2Foctohook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayfk%2Foctohook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jayfk","download_url":"https://codeload.github.com/jayfk/octohook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243858929,"owners_count":20359260,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-07T23:03:08.277Z","updated_at":"2025-03-17T09:30:56.617Z","avatar_url":"https://github.com/jayfk.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"[![Updates](https://pyup.io/repos/github/jayfk/octohook/shield.svg)](https://pyup.io/repos/github/pyupio/octohook/) [![Build Status](https://travis-ci.org/jayfk/octohook.svg?branch=master)](https://travis-ci.org/pyupio/octohook) [![codecov.io](https://codecov.io/github/jayfk/octohook/coverage.svg?branch=master)](https://codecov.io/github/pyupio/octohook?branch=master)\n\n## About\n\nOctohook is a server that listens for incoming webhooks, validates them and routes them to your code. The idea was to make it easier to write and deploy code that runs when something happens on your repo. Be it a new issue, a reverted commit or someone starring it.\n\n### How it works\n\nOctohook uses flask to serve incoming requests and to establish routes. When the server starts, it checks for files in the `repos/` folder, imports them and establishes a route to them by using the filename. Once a POST request hits the URL, the view function calls the appropiate function for the event in that module.\n\n\nFor example, if you create a file `repos/myrepo.py`, the server will listen for incoming webhooks at `/myrepo/`. When a webhook for the event `fork` hits `/myrepo/`, the `fork` function in `repo/myrepo.py` is called.\n\nOr, if you have a repo called `foo` and you want to run some code on `pull_request` events you create a file `repos/foo.py` and implement a `pull_request(data)` in it. Your server now listens on `/foo/` and waits for hooks to come by.\n\n## Get started (quick)\n\n1. Clone the repo\n\n        git clone https://github.com/pyupio/octohook.git\n\n2. Add your code in `repos/whateveryouwant.py`.\n  \n3. Start the server\n\n\t**With vanilla python**\n\n        mkvirtualenv octohook\n        pip install -r requirements.txt\n        export DEBUG=True\n        python hook/hook.py\n    \n    **Or with docker**\n\n        docker-compose -f dev.yml up \n    \n4. Use ngrok during development to forward request to your local machine. The server listens on port `5000`. \n\n\n\n## Get started\n\nFirst, clone the repo by running\n\n    git clone https://github.com/pyupio/octohook.git\n   \nNow, take a look at the `repos/` folder. This is where incoming webhooks will be routed to. There's a file called `example.py` in that folder. Every function you see in there maps an event sent by Github. \n\nMove the file and name it eg. `myrepo.py`\n\n    mv repos/example.py repos/myrepo.py\n    \nAs an easy example to begin with we are going to listen to the `watch` event and print a message to the terminal once someone stars our repo. \n\nOpen `myrepo.py` and delete all functions except for the `watch` function at the end of the file. We are going to add a simple print statement to the function that tells us who has starred the repo and how much stars the repo has in total.\n\n    def watch(data):\n        \"\"\"Any time a User stars a Repository.\"\"\"\n        print(\"{user} just starred {repo_name}. The repo now has a total of {stars} stars\".format(\n            user=data[\"sender\"][\"login\"],\n            repo_name=data[\"repository\"][\"name\"],\n            stars=data[\"repository\"][\"watchers_count\"]\n        ))\n\nNow we need to run the server. Octohook comes with built in docker support using docker-compose, and of course vanilla python. \n\n**Vanilla python**\n\n    mkvirtualenv octohook\n    pip install -r requirements.txt\n    export DEBUG=True\n    python hook/hook.py\n\n**Or with docker**\n\n\tdocker-compose -f dev.yml up    \n\nYou should see a warning telling you that you are running in `DEBUG` mode. That's because the `DEBUG` environment variable is set. There's no signature verification on `DEBUG`, take a look at [Security](###security) for more on that.\n\nSince we are probably running on a local machine that isn't visible to Githubs servers, install [ngrok](https://ngrok.com/) that helps us to tunnel incoming webhooks to our dev machine.\n\nOpen a new terminal and run:\n\n    ngrok 127.0.0.1:5000\n    \n*If you are using docker on OSX/Windows, replace `127.0.0.1` with the IP of your docker deamon. Run `docker-machine ip default` to get it.*\n\nCreate a new repo or use an existing one and go to click on `Settings` \u003e `Webhooks \u0026 services` \u003e `Add webhook`. \n\nTake a look at the terminal where you started `ngrok`, copy the forwarding URL into the Payload URL field and add `/myrepo/` to it, so that it looks like `http://783yk8fae.ngrok.com/myrepo/`. Leave the secret empty and make sure to click on *Send me everything*.\n\nNow get back to your github repo. To trigger the `watch` event, click on the star button. (You might need to click twice if you already starred your repo).\n\nThe octohook server should now print\n\n    jayfk just starred test-repo. The repo now has a total of 1 stars\n    \nAnd ngrok should tell you that it forwarded the request sucessfully\n\n    POST /myrepo/                 200 OK\n    \n    \nNot what you are seeing? Go to the `Webhooks \u0026 services` page again. And click on the webhook you just created. Check the `Recent Deliveries` Pane and check the `Response` tab to see the error.\n\n## Security\n\nGithub signs the payload if you set a secret token during the creation of the webhook on the web interface. That's generally  a very good thing, because otherwise everyone could POST funny payloads to your server. \n\nOctohook verifies the signature by default and won't continue to process the request if it doesn't match. In fact, octohook will even refuse to start when the secret for a repo is not set.\n\nTo tell octohook the secret for your repo, you need set the environment variable `REPONAME_SECRET`. For example, if you have a file `repos/foo.py` you'll need to set the environment variable `FOO_SECRET`. If you have file `repos/bar.py`, you'll need to set `BAR_SECRET`.\n\nThe only exception where octohook won't verify incoming payloads, is when you set the `DEBUG` environment variable. That makes it easier during development, because you don't have to sign your payloads if you are developing.\n\n## Deploy\n\nWhen you are done with testing and you want to run that thing on a real server there's good news: Octohook comes with a docker-compose configuration that makes it easy to deploy your code to a live server.\n\nThe configuration uses nginx with a self signed certificate (that is auto generated during the build) and a gunicorn server that runs the code.\n\nYou can use whatever provider you prefer. For simplicity, we are going to use Digital Ocean. Make sure to check docker-machine`s [provider list](https://github.com/docker/machine/blob/master/docs/AVAILABLE_DRIVER_PLUGINS.md) if you want to use something else.  \n\n    docker-machine create --driver digitalocean --digitalocean-access-token=\u003cYOUR_DO_API_TOKEN\u003e octohook\n\nThis creates a digital ocean droplet with 512mb for us, it takes a couple of minutes to run. \n\nIn the meantime, open your `docker-compose.yml`. \n\nWe need to set a secret for octohook to verify incoming webhooks. This is done through environment variables. The name of the environment variable depends on how you called the file you added. If you've added `myrepo.py`, you'll need to set `MYREPO_SECRET`. There's already a environment variable called `SOMEREPO_SECRET` defined. Replace that with yours.\n\nOnce docker-machine is ready, run:\n\n    docker-machine ip octohook\n    \u003e 123.236.197.123\n    \nand copy the address, we need the it later to create the webhook. \n    \nNow that we have the IP, switch the environment docker-machine is pointing to by running:\n\n    eval $(docker-machine env octohook)\n    \nTo push and build the stack on the server, run:\n\n    docker-compose build\n    \n\nWe are now ready to run the application on the virtual machine, type: \n\n    docker-compose up -d\n    \nto start the server in detached mode.\n\nTo check the logs, run:\n\n    docker-compose logs\n    \nYou should get an output similar to this\n\n    hook_1  | [2016-02-19 08:56:12 +0000] [1] [INFO] Starting gunicorn 19.4.5\n    hook_1  | [2016-02-19 08:56:12 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)\n    hook_1  | [2016-02-19 08:56:12 +0000] [1] [INFO] Using worker: sync\n    hook_1  | [2016-02-19 08:56:12 +0000] [8] [INFO] Booting worker with pid: 8\n    hook_1  | [2016-02-19 08:56:12 +0000] [9] [INFO] Booting worker with pid: 9\n    hook_1  | [2016-02-19 08:56:12 +0000] [10] [INFO] Booting worker with pid: 10\n    hook_1  | [2016-02-19 08:56:13 +0000] [11] [INFO] Booting worker with pid: 11\n    \nIf you see an `AssertionError` popping up telling you that you don't have set the secret key, make sure you have set that correctly. Hit `CTRL+C`, change the secret key and run `docker-compose build` and `docker-compose up -d` again to rebuild and restart the server.    \n    \nNow, head over to your github repo and click on `Settings` \u003e `Webhooks \u0026 services` \u003e `Add webhook`\n\n - Payload URL is `https://\u003cyour_ip\u003e/\u003cyour_repo\u003e/`, for example `https://1.2.3.4/myrepo/`\n - Content type is `application/json`\n - Secret is the value of the secret key you set for that repo.  \n\nMake sure to click `Disable SSL verification`. We need to do that because we are using a self signed certificate and Github isn't trusting our CA. That's perfectly fine since we just want that Github sends us all the payload over an encrypted connection.\n\nSelect which events you would like to be send to octohook and click on `Add webhook`.\n\nMake sure that everything works by triggering an event you selected. Take a look at your logs, you should see an output similar to this:\n\n    nginx_1 | 192.30.252.46 - - [19/Feb/2016:08:45:12 +0000] \"POST /myrepo/ HTTP/1.1\" 200 2 \"-\" \"GitHub-Hookshot/21f57ba\" \"-\"\n\n \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjayfk%2Foctohook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjayfk%2Foctohook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjayfk%2Foctohook/lists"}