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.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import hashlib
  2. import socket
  3. import jenkins
  4. import requests
  5. import os
  6. from bs4 import BeautifulSoup
  7. from flask import Flask, abort
  8. BASE_URL = os.environ["LAS_BASE_URL"]
  9. SECRET = os.environ["LAS_SECRET"]
  10. jenkins_server = jenkins.Jenkins(
  11. os.environ["LAS_JENKINS_URL"],
  12. username=os.environ["LAS_JENKINS_USER"],
  13. password=os.environ["LAS_JENKINS_PASSWORD"],
  14. )
  15. def get_hook_key(service, identifier):
  16. nonce = (service + SECRET + identifier).encode("ascii")
  17. return hashlib.sha256(nonce).hexdigest()
  18. def get_hook_url(service, identifier):
  19. return f"{BASE_URL}hooks/{service}/{identifier}/{get_hook_key(service, identifier)}"
  20. def get_jenkins_jobs():
  21. for job in jenkins_server.get_all_jobs():
  22. config = BeautifulSoup(
  23. jenkins_server.get_job_config(job["fullname"]), features="xml"
  24. )
  25. for git_config in config.find_all("scm", class_="hudson.plugins.git.GitSCM"):
  26. branch_spec = git_config.find("branches").find("name").text
  27. yield job["fullname"], branch_spec, git_config.find("url").text
  28. def gitea_request(method, api_path, **kwargs):
  29. if "params" not in kwargs:
  30. kwargs["params"] = {}
  31. kwargs["params"]["access_token"] = os.environ["LAS_GITEA_TOKEN"]
  32. return requests.request(
  33. method, f"{os.environ['LAS_GITEA_URL']}api/v1/{api_path}", **kwargs
  34. )
  35. def maybe_install_gitea_hook(project):
  36. hook_url = get_hook_url("gitea", project)
  37. path = f"repos/{project}/hooks"
  38. hooks = gitea_request("get", path).json()
  39. if hook_url not in [hook["config"]["url"] for hook in hooks]:
  40. body = {
  41. "active": True,
  42. "config": {"content_type": "json", "url": hook_url},
  43. "events": [
  44. "create",
  45. "delete",
  46. "fork",
  47. "push",
  48. "issues",
  49. "issue_comment",
  50. "pull_request",
  51. "repository",
  52. "release",
  53. ],
  54. "type": "gitea",
  55. }
  56. gitea_request("post", path, json=body).json()
  57. def get_gitea_repos():
  58. repos = gitea_request("get", f"user/repos").json()
  59. for repo in repos:
  60. maybe_install_gitea_hook(repo["full_name"])
  61. yield repo["full_name"], repo["ssh_url"], repo["clone_url"]
  62. def reportbot_announce(message):
  63. try:
  64. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
  65. host = os.environ["LAS_REPORTBOT_ADDRESS"].split(":")
  66. sock.connect((host[0], int(host[1])))
  67. sock.sendall(
  68. f"{os.environ['LAS_REPORTBOT_PREFIX']} {message}\n".encode("utf-8")
  69. )
  70. app.logger.info(f"Report bot response: {sock.recv(512)}")
  71. except Exception:
  72. app.logger.exception("Unable to send report bot message")
  73. repos = dict((name, [ssh, clone]) for name, ssh, clone in get_gitea_repos())
  74. jobs = list(get_jenkins_jobs())
  75. app = Flask(__name__)
  76. @app.route("/")
  77. def handle_index():
  78. return app.send_static_file("index.html")
  79. @app.route("/hooks/gitea/<path:repo>/<hash>", methods=["POST"])
  80. def handle_hook_gitea(repo, hash):
  81. app.logger.info(f"Received hook for repo {repo} with has {hash}")
  82. expected_hash = get_hook_key("gitea", repo)
  83. if hash != expected_hash:
  84. app.logger.info(f"Hash mismatch: expected {expected_hash}")
  85. abort(403)
  86. if repo not in repos:
  87. app.logger.info(f"Repository not found. Known repos: {repos.keys()}")
  88. abort(404)
  89. urls = repos[repo]
  90. for name, spec, url in jobs:
  91. if url in urls:
  92. # TODO: Check branches
  93. app.logger.info(f"Found matching job: {name} with URL {url}")
  94. jenkins_server.build_job(name)
  95. return "", 204
  96. app.run("0.0.0.0")