configuration.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. #! /usr/bin/env python
  2. # -*- coding: utf8 -*-
  3. #
  4. # gitlab irc sender
  5. # Copyright (C) 2016-2017 Andrei Karas (4144)
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 3 of the License, or
  10. # any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. import copy
  20. import logging
  21. import logging.handlers
  22. import yaml
  23. from collections import OrderedDict
  24. from code.dictconfig import DictConfig
  25. from code.project import ProjectClass
  26. class Configuration:
  27. # log file
  28. log_file = "./hooks.log"
  29. # maximum log size
  30. log_max_size = 104857600
  31. # log level
  32. log_level = logging.INFO
  33. # web server name and version
  34. http_web_server_name = "nginx"
  35. # gitlab compare commits url
  36. gitlab_compare_path = "{homepage}/compare/{commit1}...{commit2}"
  37. # gitlab commit url
  38. gitlab_commit_path = "{homepage}/commit/{commit}"
  39. # gitlab commits group or branch
  40. gitlab_commits_path = "{homepage}/commits/{name}"
  41. # gitlab build url
  42. gitlab_build_path = "{homepage}/builds/{build}"
  43. # gitlab pipeline url
  44. gitlab_pipeline_path = "{homepage}/pipelines/{pipeline}"
  45. # commit size in letters in compare gitlab link
  46. gitlab_compare_commit_size = 6
  47. # number of letters showed from commit id
  48. commit_size = 7
  49. # pause after each irc message
  50. irc_message_pause = 2
  51. # pause in gitlab api thread
  52. api_sleep_time = 3
  53. # first irc message about push
  54. irc_push_header_message = "[\x0302{project}\x0f] \x0307{author}\x0f pushed {count} commits \x0303{branch}\x0f {url}"
  55. # main push commit message, if comment to big, it continue in next line
  56. irc_push_commit_message = "[\x0302{project}\x0f] \x0307{author}\x0f \x0303{shortid}\x0f - {message}"
  57. # continue push commit message
  58. irc_push_commit_message_continue = "{message}"
  59. # irc message about pushed tag
  60. irc_push_add_tag_message = "[\x0302{project}\x0f] \x0307{author}\x0f pushed tag \x0303{tag}\x0f on commit \x0303{shortid}\x0f {url}"
  61. # irc message about removed tag
  62. irc_push_remove_tag_message = "[\x0302{project}\x0f] \x0307{author}\x0f deleted tag \x0303{tag}\x0f"
  63. # irc message about created branch
  64. irc_push_create_branch_message = "[\x0302{project}\x0f] \x0307{author}\x0f created/changed branch \x0303{branch}\x0f on commit \x0303{shortid}\x0f {url}"
  65. # irc message about deleted branch
  66. irc_push_delete_branch_message = "[\x0302{project}\x0f] \x0307{author}\x0f deleted branch \x0303{branch}\x0f"
  67. # irc message about new note
  68. irc_push_add_note_message = "[\x0302{project}\x0f] \x0307{author}\x0f add note on commit \x0303{shortid}\x0f {url}"
  69. # irc message about failed build
  70. irc_build_failed_message = "[\x0302{project}\x0f] Build \x0307#{build} {name}\x0f for commit \x0303{shortid}\x0f from \x0303{branch}\x0f failed {url}"
  71. # irc message about success build
  72. irc_build_success_message = "[\x0302{project}\x0f] Build \x0307#{build}\x0f for commit \x0303{shortid}\x0f from \x0303{branch}\x0f success {url}"
  73. # irc message about failure stage
  74. irc_stage_failed_message = "[\x0302{project}\x0f] Build \x0307#{build}\x0f for commit \x0303{shortid}\x0f from \x0303{branch}\x0f failed {url}"
  75. # irc message about success pipeline
  76. irc_pipeline_success_message = "[\x0302{project}\x0f] Pipeline \x0307#{pipeline}\x0f for commit \x0303{shortid}\x0f from \x0303{branch}\x0f success {url}"
  77. # irc message about failed pipeline
  78. irc_pipeline_failed_message = "[\x0302{project}\x0f] Pipeline \x0307#{pipeline}\x0f for commit \x0303{shortid}\x0f from \x0303{branch}\x0f failed {url}"
  79. # irc message about opened merge request
  80. irc_merge_request_opened_message = "[\x0302{project}\x0f] \x0307{author}\x0f opened merge request \x0303!{id}\x0f: {title} {url}"
  81. # irc message about closed merge request
  82. irc_merge_request_closed_message = "[\x0302{project}\x0f] \x0307{author}\x0f closed merge request \x0303!{id}\x0f: {title} {url}"
  83. # irc message about reopened merge request
  84. irc_merge_request_reopened_message = "[\x0302{project}\x0f] \x0307{author}\x0f reopened merge request \x0303!{id}\x0f: {title} {url}"
  85. # irc message about updated merge request
  86. irc_merge_request_updated_message = "[\x0302{project}\x0f] \x0307{author}\x0f updated merge request \x0303!{id}\x0f: {title} {url}"
  87. # irc message about merged merge request
  88. irc_merge_request_merged_message = "[\x0302{project}\x0f] \x0307{author}\x0f merged merge request \x0303!{id}\x0f: {title} {url}"
  89. # irc message about note in merge request
  90. irc_merge_request_add_note_message = "[\x0302{project}\x0f] \x0307{author}\x0f add note into merge request \x0303!{id}\x0f (\x0303{source_namespace}:{source_branch}\x0f to \x0303{branch}\x0f) {url}"
  91. # irc message about note in issue
  92. irc_issue_add_note_message = "[\x0302{project}\x0f] \x0307{author}\x0f add note into issue \x0303#{id}\x0f {url}"
  93. # irc message about note in snippet
  94. irc_snippet_add_note_message = "[\x0302{project}\x0f] \x0307{author}\x0f add note into snippet \x0303${id}\x0f {url}"
  95. # irc message about opened issue
  96. irc_issue_opened_message = "[\x0302{project}\x0f] \x0307{author}\x0f opened issue \x0303#{id}\x0f: {title} {url}"
  97. # irc message about closed issue
  98. irc_issue_closed_message = "[\x0302{project}\x0f] \x0307{author}\x0f closed issue \x0303#{id}\x0f: {title} {url}"
  99. # irc message about reopened issue
  100. irc_issue_reopened_message = "[\x0302{project}\x0f] \x0307{author}\x0f reopened issue \x0303#{id}\x0f: {title} {url}"
  101. # irc message about updated issue
  102. irc_issue_updated_message = "[\x0302{project}\x0f] \x0307{author}\x0f updated issue \x0303#{id}\x0f: {title} {url}"
  103. web_hook_default_service = "gitlab"
  104. # codeship messages
  105. # irc message about failed build
  106. irc_codeship_build_failed_message = "[\x0302{project}\x0f] Build for commit \x0303{shortid}\x0f from \x0303{branch}\x0f failed {url}"
  107. # irc message about success build
  108. irc_codeship_build_success_message = "[\x0302{project}\x0f] Build for commit \x0303{shortid}\x0f from \x0303{branch}\x0f success {url}"
  109. # github messages
  110. # irc message about failed build
  111. irc_github_build_failed_message = "[\x0302{project}\x0f] {description} for commit \x0303{shortid}\x0f from \x0303{branch}\x0f {url}"
  112. # irc message about success build
  113. irc_github_build_success_message = "[\x0302{project}\x0f] {description} for commit \x0303{shortid}\x0f from \x0303{branch}\x0f {url}"
  114. # irc message about opened issue
  115. irc_github_issues_opened_message = "[\x0302{project}\x0f] \x0307{author}\x0f opened issue \x0303#{id}\x0f: {title} {url}"
  116. # irc message about edited issue
  117. irc_github_issues_edited_message = "[\x0302{project}\x0f] \x0307{author}\x0f edited issue \x0303#{id}\x0f: {title} {url}"
  118. # irc message about closed issue
  119. irc_github_issues_closed_message = "[\x0302{project}\x0f] \x0307{author}\x0f closed issue \x0303#{id}\x0f: {title} {url}"
  120. # irc message about reopened issue
  121. irc_github_issues_reopened_message = "[\x0302{project}\x0f] \x0307{author}\x0f reopened issue \x0303#{id}\x0f: {title} {url}"
  122. # irc message about created issue comment
  123. irc_github_issue_comment_created_message = "[\x0302{project}\x0f] \x0307{author}\x0f add comment to issue \x0303#{id}\x0f: {title} {url}"
  124. # irc message about edited issue comment
  125. irc_github_issue_comment_edited_message = "[\x0302{project}\x0f] \x0307{author}\x0f edited comment in issue \x0303#{id}\x0f: {title} {url}"
  126. # irc message about deleted issue comment
  127. irc_github_issue_comment_deleted_message = "[\x0302{project}\x0f] \x0307{author}\x0f deleted comment from issue \x0303#{id}\x0f: {title} {url}"
  128. def __init__(self):
  129. self.global_options = DictConfig()
  130. self.projects = dict()
  131. self.show_info = True
  132. def addDefaultOption(self, name, value):
  133. if name not in self.global_options:
  134. self.global_options[name] = value
  135. setattr(self.global_options, name, value)
  136. def load_yaml(self, name):
  137. class OrderedLoader(yaml.SafeLoader):
  138. pass
  139. def construct_mapping(loader, node):
  140. loader.flatten_mapping(node)
  141. return OrderedDict(loader.construct_pairs(node))
  142. OrderedLoader.add_constructor(
  143. yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
  144. construct_mapping)
  145. with open(name, "r") as r:
  146. return yaml.load(r, OrderedLoader)
  147. def loadDefaultOptions(self):
  148. self.addDefaultOption("show_commits", True)
  149. self.addDefaultOption("show_commit_messages", True)
  150. # number of lines with commit message per commit
  151. self.addDefaultOption("max_message_lines_per_commit", 3)
  152. # if build failed, show error message in irc
  153. self.addDefaultOption("show_build_failures", True)
  154. # if build success messages for "allowed to fail"
  155. self.addDefaultOption("show_build_success_for_allowed_to_fail", False)
  156. # if build failure messages for "allowed to fail"
  157. self.addDefaultOption("show_build_failures_for_allowed_to_fail", False)
  158. # if pipeline success, show success message in irc
  159. self.addDefaultOption("show_pipeline_success", True)
  160. # if pipeline failed, show error message in irc
  161. self.addDefaultOption("show_pipeline_failures", True)
  162. # build stage name what will trigger build success message
  163. self.addDefaultOption("last_build_stage", "")
  164. # build stage name what will trigger build failed message
  165. self.addDefaultOption("last_build_fail_stage", "")
  166. # custom irc message after build failed message
  167. self.addDefaultOption("build_fail_custom_message", "")
  168. # directory where located files with channels created by ii bot
  169. self.addDefaultOption("ii_server_directory", "irc.freenode.net")
  170. # listen host / name
  171. self.addDefaultOption("listen_host", "0.0.0.0")
  172. # listen port
  173. self.addDefaultOption("listen_port", 8000)
  174. # if set to true, return error for any incorrect/not allowed request
  175. self.addDefaultOption("http_return_error_if_not_supported", True)
  176. # true, "all", "any" - save all incoming json data into log.
  177. # false - not save
  178. # unsupported - only if unsupported
  179. self.addDefaultOption("http_save_json", "unsupported")
  180. # if set to True, will follow build_accept_allow_failure attributes
  181. self.addDefaultOption("build_accept_allow_failure", True)
  182. # web hook service. Supported values: gitlab, codeship, github
  183. self.addDefaultOption("service", self.web_hook_default_service)
  184. # if enabled it reverse commits order in first push due gitlab bug.
  185. self.addDefaultOption("push_first_push_fix", True)
  186. # custom irc message after pipeline failed message
  187. self.addDefaultOption("pipeline_fail_custom_message", "")
  188. # gitlab api token for access gitlab api
  189. self.addDefaultOption("gitlab_api_token", "")
  190. # gitlab api url for access gitlab api
  191. self.addDefaultOption("gitlab_api_url", "https://gitlab.com/api")
  192. # gitlab api retry count
  193. self.addDefaultOption("gitlab_api_retry_count", 3)
  194. # gitlab build retry count
  195. self.addDefaultOption("build_retry_count", 3)
  196. # build retry flag
  197. self.addDefaultOption("build_retry", "never")
  198. self.global_options.update_fields()
  199. def loadProjectConfiguration(self, projectConf, name):
  200. if self.show_info == True:
  201. debugChannels = set()
  202. url = projectConf["url"]
  203. project = ProjectClass()
  204. project.name = name
  205. project.url = url
  206. actionsDict = dict()
  207. channelsDict = dict()
  208. allowBranches = []
  209. denyBranches = []
  210. options = copy.copy(self.global_options)
  211. if "options" in projectConf:
  212. options.update(projectConf["options"])
  213. options.update_fields()
  214. if "channels" in projectConf:
  215. channels = projectConf["channels"]
  216. for channelobj in channels:
  217. channel = channelobj
  218. channelName = "#" + channel
  219. channelsDict[channelName] = copy.copy(options)
  220. if "actions" in channels[channel]:
  221. objects = channels[channel]["actions"]
  222. else:
  223. objects = None
  224. if "options" in channels[channel]:
  225. channelsDict[channelName].update(channels[channel]["options"])
  226. channelsDict[channelName].update_fields()
  227. if objects is not None:
  228. for obj in objects:
  229. if obj not in actionsDict:
  230. actionsDict[obj] = []
  231. actionsDict[obj].append(channelName)
  232. if self.show_info == True:
  233. debugChannels.add(channelName)
  234. if self.show_info == True:
  235. data = ""
  236. for channel in debugChannels:
  237. data = data + channel + " "
  238. print("{0}: {1}".format(name, data))
  239. if "branches" in projectConf:
  240. branches = projectConf["branches"]
  241. if "allow" in branches:
  242. allowBranches = branches["allow"]
  243. if "deny" in branches:
  244. denyBranches = branches["deny"]
  245. if len(allowBranches) == 0 and "*" not in denyBranches:
  246. allowBranches.append("*")
  247. elif len(denyBranches) == 0 and "*" not in allowBranches:
  248. denyBranches.append("*")
  249. project.actions = actionsDict
  250. project.channels = channelsDict
  251. project.options = options
  252. project.allow_branches = allowBranches
  253. project.deny_branches = denyBranches
  254. self.projects[url] = project
  255. def loadConfiguration(self, name):
  256. doc = self.load_yaml(name)
  257. #print doc
  258. self.loadDefaultOptions()
  259. if "options" in doc:
  260. self.global_options.update(doc["options"])
  261. self.global_options.update_fields()
  262. projectsList = doc["projects"]
  263. for project in projectsList:
  264. if type(project) is str:
  265. self.loadProjectConfiguration(projectsList[project], project)
  266. else:
  267. name = next(iter(project))
  268. self.loadProjectConfiguration(project[name], name)