-
Notifications
You must be signed in to change notification settings - Fork 29
/
gitlab-webhook-receiver.py
executable file
·205 lines (182 loc) · 7.19 KB
/
gitlab-webhook-receiver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/python -tt
#
# Copyright (C) 2012 Shawn Sterling <[email protected]>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# gitlab-webhook-receiver: a script for gitlab & puppet integration
#
# The latest version of this code will be found on my github page:
# https://github.com/shawn-sterling/gitlab-webhook-receiver
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import json
import logging
import logging.handlers
import os
import re
import shutil
import subprocess
############################################################
##### You will likely need to change some of the below #####
# log file for this script
log_file = '/var/lib/puppet/gitlab-webhook-receiver/webhook.log'
# where the puppet base git dir is
git_dir = "/etc/puppet/environments"
# the puppet master environment
git_master_dir = "/etc/puppet/environments/master"
# this is the name of the gitlab project name
git_project = "newpuppet"
# this is the git ssh account
git_ssh = "[email protected]"
log_max_size = 25165824 # 24 MB
log_level = logging.INFO
#log_level = logging.DEBUG # DEBUG is quite verbose
listen_port = 8000
##### You should stop changing things unless you know what you are doing #####
##############################################################################
log = logging.getLogger('log')
log.setLevel(log_level)
log_handler = logging.handlers.RotatingFileHandler(log_file,
maxBytes=log_max_size,
backupCount=4)
f = logging.Formatter("%(asctime)s %(filename)s %(levelname)s %(message)s",
"%B %d %H:%M:%S")
log_handler.setFormatter(f)
log.addHandler(log_handler)
class webhookReceiver(BaseHTTPRequestHandler):
def run_it(self, cmd):
"""
runs a command
"""
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
log.debug('running:%s' % cmd)
p.wait()
if p.returncode != 0:
log.critical("Non zero exit code:%s executing: %s" % (p.returncode,
cmd))
return p.stdout
def git_cleanup(self):
"""
cleans up the master, does a prune origin
"""
log.debug('git_cleanup begins')
os.chdir(git_master_dir)
cmd = "git reset --hard HEAD"
self.run_it(cmd)
cmd = "git pull"
self.run_it(cmd)
cmd = "git remote prune origin"
self.run_it(cmd)
log.debug('git_cleanup ends')
def git_handle_branches(self):
"""
gets a list of branches, does a reset then pull for each branch.
clones any non existing branches.
"""
log.debug('git_handle_branches begins')
current_branches = []
cmd = "git branch -a"
output = self.run_it(cmd)
for branch in output:
branch = re.sub("\*", "", branch)
branch = re.sub("\s+", "", branch)
if not re.search("HEAD", branch):
if re.search("/", branch):
short_name = branch.split('/')[2]
if not short_name == "master":
current_branches.append(short_name)
fwd = os.path.join(git_dir, short_name)
if os.path.isdir(fwd):
os.chdir(fwd)
log.debug('cwd:%s' % fwd)
cmd = "git reset --hard HEAD"
output = self.run_it(cmd)
cmd = "git pull"
output = self.run_it(cmd)
else:
os.chdir(git_dir)
cmd = "git clone -b %s %s:%s %s" % (
short_name, git_ssh, git_project, short_name)
output = self.run_it(cmd)
log.debug('git_handle_branches ends')
return current_branches
def git_remove_stale_branches(self, current_branches):
"""
removes any directories that don't have a branch name (ignores
production dir/symlink)
"""
log.debug('remove stale branches begins')
# remove stale directories (deleted branches)
if len(current_branches) > 1:
current_directories = os.listdir(git_dir)
for branch in current_branches:
current_directories.remove(branch)
# a production symlink must exist for puppet, ignore this dir
if "production" in current_directories:
current_directories.remove("production")
if len(current_directories) > 0:
for branch in current_directories:
fwd = os.path.join(git_dir, branch)
log.debug("trying to remove:%s" % fwd)
try:
shutil.rmtree(fwd)
except (IOError, OSError) as e:
log.critical('Exception:%s removing dir:%s' % (fwd, e))
log.debug('remove stale branches ends')
def do_POST(self):
"""
receives post, handles it
"""
log.debug('got post')
message = 'OK'
self.rfile._sock.settimeout(5)
data_string = self.rfile.read(int(self.headers['Content-Length']))
self.send_response(200)
self.send_header("Content-type", "text")
self.send_header("Content-length", str(len(message)))
self.end_headers()
self.wfile.write(message)
log.debug('gitlab connection should be closed now.')
# parse data
text = json.loads(data_string)
text = json.dumps(text, indent=2)
if git_project in text:
log.debug('project is in text')
self.git_cleanup()
current_branches = self.git_handle_branches()
self.git_remove_stale_branches(current_branches)
log.debug('post complete')
else:
log.debug('project name not in text, ignoring post')
def log_message(self, formate, *args):
"""
disable printing to stdout/stderr for every post
"""
return
def main():
"""
the main event.
"""
try:
server = HTTPServer(('', listen_port), webhookReceiver)
log.info('started web server...')
server.serve_forever()
except KeyboardInterrupt:
log.info('ctrl-c pressed, shutting down.')
server.socket.close()
if __name__ == '__main__':
main()