WebHook broker that accepts notifications from multiple platforms and performs simple actions in response
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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 services
  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. repos = dict((name, [ssh, clone]) for name, ssh, clone in services["gitea"].get_repos())
  23. jobs = list(services["jenkins"].get_jobs())
  24. app = Flask(__name__)
  25. @app.route("/")
  26. def handle_index():
  27. return app.send_static_file("index.html")
  28. @app.route("/hooks/gitea/<path:repo>/<hash>", methods=["POST"])
  29. @authenticate
  30. def handle_hook_gitea(repo):
  31. app.logger.info(f"Received hook for repo {repo}")
  32. if repo not in repos:
  33. app.logger.info(f"Repository not found. Known repos: {repos.keys()}")
  34. abort(404)
  35. if request.headers.get("X-Gitea-Event") == "push":
  36. urls = repos[repo]
  37. for name, spec, url in jobs:
  38. if url in urls:
  39. # TODO: Check branches
  40. app.logger.info(f"Found matching job: {name} with URL {url}")
  41. services["jenkins"].build_job(name)
  42. data = request.get_json()
  43. if not data["repository"]["private"]:
  44. repo = data["repository"]["full_name"]
  45. commits = len(data["commits"])
  46. compare = data["compare_url"]
  47. pusher = data["pusher"]["login"]
  48. services["reportbot"].announce(
  49. f"\002[git]\002 {pusher} pushed {commits} commit{'s' if commits != 1 else ''} to {repo}: {compare}"
  50. )
  51. for commit in data["commits"][:3]:
  52. services["reportbot"].announce(
  53. f"\002[git]\002 {commit['id'][:10]}: {commit['message'][:100]}"
  54. )
  55. return "", 204
  56. @app.route("/hooks/docker/registry/<hash>", methods=["GET", "POST"])
  57. @authenticate
  58. def handle_docker_registry():
  59. for event in request.get_json()["events"]:
  60. if (
  61. event["action"] == "push"
  62. and "vnd.docker.distribution.manifest" in event["target"]["mediaType"]
  63. and "tag" in event["target"]
  64. ):
  65. repo = event["target"]["repository"]
  66. tag = event["target"]["tag"]
  67. host = event["request"]["host"]
  68. user = event["actor"]["name"]
  69. services["reportbot"].announce(
  70. f"\002[registry]\002 New manifest pushed to {host}/{repo}:{tag} by {user}"
  71. )
  72. return "", 204
  73. if __name__ == "__main__":
  74. app.run("0.0.0.0")