feat: initial commit
This commit is contained in:
commit
1f2425b9d6
3 changed files with 103 additions and 0 deletions
16
LICENSE
Normal file
16
LICENSE
Normal file
|
@ -0,0 +1,16 @@
|
|||
Copyright 2023 s3lph
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
43
README.md
Normal file
43
README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Semaphore Webhook Server
|
||||
|
||||
A small Python webservice to extend the API of [Semaphore UI][semui] with a "webhook" enpoint to start tasks.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Put the script onto your Semaphore server.
|
||||
1. Run it e.g. as a systemd service.
|
||||
1. Configure the reverse proxy to proxy a path of your choice to `http://localhost:3042/webhook` rather than Semaphore.
|
||||
- Apache example:
|
||||
|
||||
ProxyPass /.well-known !
|
||||
# Webhook server - must be before semaphre
|
||||
ProxyPass /webhook http://localhost:3042/webhook
|
||||
ProxyPassReverse /webhook http://localhost:3042/webhook
|
||||
# Semaphore
|
||||
ProxyPass /api/ws ws://localhost:3000/api/ws
|
||||
ProxyPass / http://localhost:3000/
|
||||
ProxyPassReverse / http://localhost:3000/
|
||||
|
||||
## Usage
|
||||
|
||||
The easiest to use this webhook endpoint, simply GET or POST the `/webhook` endpoint with the `project_id` and `template_id` parameters:
|
||||
|
||||
GET /webhook?project_id=1&template_id=1 HTTP/1.1
|
||||
Host: semaphore.example.org
|
||||
Authorization: Bearer ...
|
||||
|
||||
These two parameters are required. The total set of supporter parameters are:
|
||||
|
||||
- `project_id` (**required**): int
|
||||
- `template_id` (**required**): int
|
||||
- `debug`: bool, `true` or `false`
|
||||
- `dry_run`: bool
|
||||
- `diff`: bool
|
||||
|
||||
Each request to the webhook server is transformed into a request to the Semaphore API's [`POST /project/{project_id}/tasks` endpoint][endpoint].
|
||||
|
||||
The authorization header is passed on to Semaphore unmodified.
|
||||
|
||||
|
||||
[semui]: https://www.semui.co/
|
||||
[endpoint]: https://www.semui.co/api-docs/#/project/post_project__project_id__tasks
|
44
semaphore-webhook-server.py
Executable file
44
semaphore-webhook-server.py
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
import bottle
|
||||
|
||||
|
||||
@bottle.get('/webhook')
|
||||
@bottle.post('/webhook')
|
||||
def webhook():
|
||||
project_id = int(bottle.request.query.project_id)
|
||||
payload = {
|
||||
'project_id': project_id,
|
||||
'environment': '{}',
|
||||
}
|
||||
if 'template_id' in bottle.request.query:
|
||||
payload['template_id'] = int(bottle.request.query.template_id)
|
||||
if 'debug' in bottle.request.query:
|
||||
payload['debug'] = bottle.request.query.debug == 'true'
|
||||
if 'dry_run' in bottle.request.query:
|
||||
payload['dry_run'] = bottle.request.query.dry_run == 'true'
|
||||
if 'diff' in bottle.request.query:
|
||||
payload['diff'] = bottle.request.query.diff == 'true'
|
||||
baseurl = f'https://{bottle.request.urlparts.netloc}'
|
||||
url = os.path.join(baseurl, 'api/project', str(project_id), 'tasks')
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': bottle.request.headers['Authorization'],
|
||||
'X-Forwarded-For': bottle.request.headers['X-Forwarded-For'],
|
||||
'X-Forwarded-Host': bottle.request.headers['X-Forwarded-Host'],
|
||||
'X-Forwarded-Server': bottle.request.headers['X-Forwarded-Server'],
|
||||
}
|
||||
req = urllib.request.Request(url, data=json.dumps(payload).encode(), method='POST', headers=headers)
|
||||
try:
|
||||
resp = urllib.request.urlopen(req)
|
||||
except urllib.error.HTTPError as e:
|
||||
bottle.abort(e.code, e.reason)
|
||||
bottle.abort(204, 'No Content')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bottle.run(host='::1', port=3042)
|
Loading…
Reference in a new issue