WebHook broker that accepts notifications from multiple platforms and performs simple actions in response
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.py 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import re
  2. from functools import wraps
  3. from flask import Flask, abort, request, Response
  4. from lineandsinker.common import get_hook_key
  5. from lineandsinker.services import gitea_factory, jenkins_factory, reportbot_factory
  6. url_pattern = re.compile(
  7. "^/hooks/(?P<service>[^/]+)/(?P<identifier>.*)/(?P<key>[^/]+)$"
  8. )
  9. def authenticate(f):
  10. @wraps(f)
  11. def wrapper(*args, **kwargs):
  12. path = request.path
  13. match = url_pattern.match(path)
  14. if not match:
  15. return Response("Bad request", 400)
  16. expected_key = get_hook_key(match.group("service"), match.group("identifier"))
  17. if expected_key != match.group("key"):
  18. app.logger.info(f"Bad request to {path}: expected key {expected_key}")
  19. return Response("Invalid key", 403)
  20. return f(*args, **kwargs)
  21. return wrapper
  22. jenkins_service = jenkins_factory()
  23. gitea_service = gitea_factory()
  24. reportbot_service = reportbot_factory()
  25. repos = dict((name, [ssh, clone]) for name, ssh, clone in gitea_service.get_repos())
  26. jobs = list(jenkins_service.get_jobs())
  27. app = Flask(__name__)
  28. @app.route("/")
  29. def handle_index():
  30. return app.send_static_file("index.html")
  31. @app.route("/hooks/gitea/<path:repo>/<hash>", methods=["POST"])
  32. @authenticate
  33. def handle_hook_gitea(repo):
  34. app.logger.info(f"Received hook for repo {repo}")
  35. if repo not in repos:
  36. app.logger.info(f"Repository not found. Known repos: {repos.keys()}")
  37. abort(404)
  38. if request.headers.get("X-Gitea-Event") == "push":
  39. urls = repos[repo]
  40. for name, spec, url in jobs:
  41. if url in urls:
  42. # TODO: Check branches
  43. app.logger.info(f"Found matching job: {name} with URL {url}")
  44. jenkins_service.build_job(name)
  45. data = request.get_json()
  46. if not data["repository"]["private"]:
  47. repo = data["repository"]["full_name"]
  48. commits = len(data["commits"])
  49. compare = data["compare_url"]
  50. pusher = data["pusher"]["login"]
  51. reportbot_service.announce(
  52. f"\002[git]\002 {pusher} pushed {commits} commit{'s' if commits != 1 else ''} to {repo}: {compare}"
  53. )
  54. for commit in data["commits"][:3]:
  55. reportbot_service.announce(
  56. f"\002[git]\002 {commit['id'][:10]}: {commit['message'][:100]}"
  57. )
  58. return "", 204
  59. @app.route("/hooks/docker/registry/<hash>", methods=["GET", "POST"])
  60. @authenticate
  61. def handle_docker_registry():
  62. for event in request.get_json()["events"]:
  63. if (
  64. event["action"] == "push"
  65. and "vnd.docker.distribution.manifest" in event["target"]["mediaType"]
  66. and "tag" in event["target"]
  67. ):
  68. repo = event["target"]["repository"]
  69. tag = event["target"]["tag"]
  70. host = event["request"]["host"]
  71. user = event["actor"]["name"]
  72. reportbot_service.announce(
  73. f"\002[registry]\002 New manifest pushed to {host}/{repo}:{tag} by {user}"
  74. )
  75. return "", 204
  76. if __name__ == "__main__":
  77. app.run("0.0.0.0")