{"action":"created","comment":{"url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/comments/37361447","id":37361447,"diff_hunk":"@@ -1,325 +1,504 @@\n #!/usr/bin/env python\n \n-import json, urlparse, sys, os, signal, socket, re\n-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer\n-from subprocess import call\n-from threading import Timer\n-\n-class GitAutoDeploy(BaseHTTPRequestHandler):\n-\n-\tCONFIG_FILEPATH = './GitAutoDeploy.conf.json'\n-\tconfig = None\n-\tdebug = True\n-\tquiet = False\n-\tdaemon = False\n-\n-\t@classmethod\n-\tdef getConfig(myClass):\n-\t\tif(myClass.config == None):\n-\t\t\ttry:\n-\t\t\t\tconfigString = open(myClass.CONFIG_FILEPATH).read()\n-\t\t\texcept:\n-\t\t\t\tprint \"Could not load %s file\" % myClass.CONFIG_FILEPATH\n-\t\t\t\tsys.exit(2)\n-\n-\t\t\ttry:\n-\t\t\t\tmyClass.config = json.loads(configString)\n-\t\t\texcept:\n-\t\t\t\tprint \"%s file is not valid JSON\" % myClass.CONFIG_FILEPATH\n-\t\t\t\tsys.exit(2)\n-\n-\t\t\tfor repository in myClass.config['repositories']:\n-\t\t\t\tif(not os.path.isdir(repository['path'])):\n-\t\t\t\t\tprint \"Directory %s not found\" % repository['path']\n-\t\t\t\t\tcall(['git clone --recursive '+repository['url']+' '+repository['path']], shell=True)\n-\t\t\t\t\tif(not os.path.isdir(repository['path'])):\n-\t\t\t\t\t\tprint \"Unable to clone repository %s\" % repository['url']\n-\t\t\t\t\t\tsys.exit(2)\n-\t\t\t\t\telse:\n-\t\t\t\t\t\tprint \"Repository %s successfully cloned\" % repository['url']\n-\t\t\t\tif(not os.path.isdir(repository['path'] + '/.git')):\n-\t\t\t\t\tprint \"Directory %s is not a Git repository\" % repository['path']\n-\t\t\t\t\tsys.exit(2)\n-\t\t\t\tmyClass.clearLock(repository['path'])\n-\n-\t\treturn myClass.config\n-\n-\tdef do_POST(self):\n-\t\turls = self.parseRequest()\n-\t\tself.respond()\n-\t\tTimer(1.0, self.do_process, [urls]).start()\n-\n-\tdef do_process(self, urls):\n-\t\tfor url in urls:\n-\t\t\trepos = self.getMatchingPaths(url)\n-\t\t\tfor repo in repos:\n-\t\t\t\tif self.lock(repo['path']):\n-\t\t\t\t\ttry:\n-\t\t\t\t\t\tn = 4\n-\t\t\t\t\t\twhile 0 < n and 0 != self.pull(repo['path'], repo['branch']):\n-\t\t\t\t\t\t\t--n\n-\t\t\t\t\t\tif 0 < n:\n-\t\t\t\t\t\t\tself.deploy(repo['path'])\n-\t\t\t\t\texcept:\n-\t\t\t\t\t\tcall(['echo \"Error during \\'pull\\' or \\'deploy\\' operation on path: ' + repo['path'] + '\"'], shell=True)\n-\t\t\t\t\tfinally:\n-\t\t\t\t\t\tself.unlock(repo['path'])\n-\n-\tdef parseRequest(self):\n-\t\tcontenttype = self.headers.getheader('content-type')\n-\t\tlength = int(self.headers.getheader('content-length'))\n-\t\tbody = self.rfile.read(length)\n-\n-\t\titems = []\n-\t\t\n-\t\ttry:\n-\t\t\tif contenttype == \"application/json\" or contenttype == \"application/x-www-form-urlencoded\":\n-\t\t\t\tpost = urlparse.parse_qs(body)\n-\n-\t\t\t# If payload is missing, we assume gitlab syntax.\n-\t\t\tif contenttype == \"application/json\" and \"payload\" not in post:\n-\t\t\t\tmode = \"github\"\n-\t\t\t# If x-www-form-urlencoded, we assume bitbucket syntax.\n-\t\t\telif contenttype == \"application/x-www-form-urlencoded\":\n-\t\t\t\tmode = \"bitbucket\"\n-\t\t\t# Oh Gitlab, dear Gitlab...\n-\t\t\telse:\n-\t\t\t\tmode = \"gitlab\"\n-\n-\t\t\t\n-\t\t\tif mode == \"github\":\n-\t\t\t\tresponse = json.loads(body)\n-\t\t\t\titems.append(response['repository']['url'])\n-\t\t\t\t\n-\t\t\telif mode == \"bitbucket\":\n-\t\t\t\tfor itemString in post['payload']:\n-\t\t\t\t\titem = json.loads(itemString)\n-\t\t\t\t\titems.append(\"ssh://hg@bitbucket.org\" + item['repository']['absolute_url'][0:-1])\n-\n-\t\t\t# Otherwise, we assume github/bitbucket syntax.\n-\t\t\telif mode == \"gitlab\":\n-\t\t\t\tfor itemString in post['payload']:\n-\t\t\t\t\titem = json.loads(itemString)\n-\t\t\t\t\titems.append(item['repository']['url'])\n-\t\t\t\n-\t\t\t# WTF?!\n-\t\t\telse:\n-\t\t\t\tpass\n-\t\texcept Exception:\n-\t\t\tpass\n-\n-\t\treturn items\n-\n-\tdef getMatchingPaths(self, repoUrl):\n-\t\tres = []\n-\t\tconfig = self.getConfig()\n-\t\tfor repository in config['repositories']:\n-\t\t\tif(repository['url'] == repoUrl):\n-\t\t\t\tres.append({\n-\t\t\t\t\t'path': repository['path'],\n-\t\t\t\t\t'branch': ('branch' in repository) and repository['branch'] or 'master'\n-\t\t\t\t})\n-\t\treturn res\n-\n-\tdef respond(self):\n-\t\tself.send_response(200)\n-\t\tself.send_header('Content-type', 'text/plain')\n-\t\tself.end_headers()\n-\n-\tdef lock(self, path):\n-\t\treturn 0 == call(['sh lock.sh \"' + path + '\"'], shell=True)\n-\n-\tdef unlock(self, path):\n-\t\tcall(['sh unlock.sh \"' + path + '\"'], shell=True)\n-\n-\t@classmethod\n-\tdef clearLock(myClass, path):\n-\t\tcall(['sh clear_lock.sh \"' + path + '\"'], shell=True)\n-\n-\tdef pull(self, path, branch):\n-\t\tif(not self.quiet):\n-\t\t\tprint \"\\nPost push request received\"\n-\t\t\tprint 'Updating ' + path\n-\t\tres = call(['sleep 5; cd \"' + path + '\" && unset GIT_DIR && git fetch origin && git update-index --refresh && git reset --hard origin/' + branch + ' && git submodule init && git submodule update'], shell=True)\n-\t\tcall(['echo \"Pull result: ' + str(res) + '\"'], shell=True)\n-\t\treturn res\n-\n-\tdef deploy(self, path):\n-\t\tconfig = self.getConfig()\n-\t\tfor repository in config['repositories']:\n-\t\t\tif(repository['path'] == path):\n-\t\t\t\tcmds = []\n-\t\t\t\tif 'deploy' in repository:\n-\t\t\t\t\tcmds.append(repository['deploy'])\n-\n-\t\t\t\tgd = config['global_deploy']\n-\t\t\t\tif len(gd[0]) is not 0:\n-\t\t\t\t\tcmds.insert(0, gd[0])\n-\t\t\t\tif len(gd[1]) is not 0:\n-\t\t\t\t\tcmds.append(gd[1])\n-\n-\t\t\t\tif(not self.quiet):\n-\t\t\t\t\tprint 'Executing deploy command(s)'\n-\t\t\t\tfor cmd in cmds:\n-\t\t\t\t\tcall(['cd \"' + path + '\" && ' + cmd], shell=True)\n-\n-\t\t\t\tbreak\n-\n-\n-class GitAutoDeployMain:\n-\n-\tserver = None\n-\n-\tdef run(self):\n-\t\tfor arg in sys.argv:\n-\t\t\tif(arg == '-d' or arg == '--daemon-mode'):\n-\t\t\t\tGitAutoDeploy.daemon = True\n-\t\t\t\tGitAutoDeploy.quiet = True\n-\t\t\tif(arg == '-q' or arg == '--quiet'):\n-\t\t\t\tGitAutoDeploy.quiet = True\n-\t\t\tif(arg == '--ssh-keyscan'):\n-\t\t\t\tprint 'Scanning repository hosts for ssh keys...'\n-\t\t\t\tself.ssh_key_scan()\n-\t\t\tif(arg == '--force'):\n-\t\t\t\tprint '[KILLER MODE] Warning: The --force option will try to kill any process ' \\\n-\t\t\t\t\t'using %s port. USE AT YOUR OWN RISK' %GitAutoDeploy.getConfig()['port']\n-\t\t\t\tself.kill_them_all()\n-\n-\t\tif(GitAutoDeploy.daemon):\n-\t\t\tpid = os.fork()\n-\t\t\tif(pid > 0):\n-\t\t\t\tsys.exit(0)\n-\t\t\tos.setsid()\n-\n-\t\tself.create_pidfile()\n-\n-\t\tif(not GitAutoDeploy.quiet):\n-\t\t\tprint 'Github & Gitlab Autodeploy Service v 0.1 started'\n-\t\telse:\n-\t\t\tprint 'Github & Gitlab Autodeploy Service v 0.1 started in daemon mode'\n-\n-\t\ttry:\n-\t\t\tself.server = HTTPServer((GitAutoDeploy.getConfig()['host'], GitAutoDeploy.getConfig()['port']), GitAutoDeploy)\n-\t\t\tsa = self.server.socket.getsockname()\n-\t\t\tprint \"Listeing on\", sa[0], \"port\", sa[1]\n-\t\t\tself.server.serve_forever()\n-\t\texcept socket.error, e:\n-\t\t\tif(not GitAutoDeploy.quiet and not GitAutoDeploy.daemon):\n-\t\t\t\tprint \"Error on socket: %s\" % e\n-\t\t\t\tself.debug_diagnosis()\n-\t\t\tsys.exit(1)\n-\n-\tdef ssh_key_scan(self):\n-\t\tfor repository in GitAutoDeploy.getConfig()['repositories']:\n-\t\t\turl = repository['url']\n-\t\t\tprint \"Scanning repository: %s\" %  url\n-\t\t\tm = re.match('.*@(.*?):', url)\n-\t\t\tif(m != None):\n-\t\t\t\tport = repository['port']\n-\t\t\t\tport = '' if port == None else ('-p' + port)\n-\t\t\t\tcall(['ssh-keyscan -t ecdsa,rsa ' + port + ' ' + m.group(1) + ' >> $HOME/.ssh/known_hosts'], shell=True)\n-\t\t\telse:\n-\t\t\t\tprint 'Could not find regexp match in path: %s' % url\n-\n-\tdef kill_them_all(self):\n-\t\tpid = self.get_pid_on_port(GitAutoDeploy.getConfig()['port'])\n-\t\tif pid == False:\n-\t\t\tprint '[KILLER MODE] I don\\'t know the number of pid that is using my configured port\\n ' \\\n-\t\t\t\t'[KILLER MODE] Maybe no one? Please, use --force option carefully'\n-\t\t\treturn False\n-\n-\t\tos.kill(pid, signal.SIGKILL)\n-\t\treturn True\n- \n-\tdef create_pidfile(self):\n-\t\twith open(GitAutoDeploy.getConfig()['pidfilepath'], 'w') as f:\n-\t\t\tf.write(str(os.getpid()))\n-\n-\tdef read_pidfile(self):\n-\t\twith open(GitAutoDeploy.getConfig()['pidfilepath'],'r') as f:\n-\t\t\treturn f.readlines()\n-\n-\tdef remove_pidfile(self):\n-\t\tos.remove(GitAutoDeploy.getConfig()['pidfilepath'])\n-\n-\tdef debug_diagnosis(self):\n-\t\tif GitAutoDeploy.debug == False:\n-\t\t\treturn\n-\n-\t\tport = GitAutoDeploy.getConfig()['port']\n-\t\tpid = self.get_pid_on_port(port)\n-\t\tif pid == False:\n-\t\t\tprint 'I don\\'t know the number of pid that is using my configured port'\n-\t\t\treturn\n-\t\t\n-\t\tprint 'Process with pid number %s is using port %s' % (pid, port)\n-\t\twith open(\"/proc/%s/cmdline\" % pid) as f:\n-\t\t\tcmdline = f.readlines()\n-\t\t\tprint 'cmdline ->', cmdline[0].replace('\\x00', ' ')\n-\n-\tdef get_pid_on_port(self,port):\n-\t\twith open(\"/proc/net/tcp\",'r') as f:\n-\t\t\tfilecontent = f.readlines()[1:]\n-\n-\t\tpids = [int(x) for x in os.listdir('/proc') if x.isdigit()]\n-\t\tconf_port = str(GitAutoDeploy.getConfig()['port'])\n-\t\tmpid = False\n-\n-\t\tfor line in filecontent:\n-\t\t\tif mpid != False:\n-\t\t\t\tbreak\n-\n-\t\t\t_, laddr, _, _, _, _, _, _, _, inode = line.split()[:10]\n-\t\t\tdecport = str(int(laddr.split(':')[1], 16))\n-\n-\t\t\tif decport != conf_port:\n-\t\t\t\tcontinue\n-\n-\t\t\tfor pid in pids:\n-\t\t\t\ttry:\n-\t\t\t\t\tpath = \"/proc/%s/fd\" % pid\n-\t\t\t\t\tif os.access(path, os.R_OK) is False:\n-\t\t\t\t\t\tcontinue\n-\n-\t\t\t\t\tfor fd in os.listdir(path):\n-\t\t\t\t\t\tcinode = os.readlink(\"/proc/%s/fd/%s\" % (pid, fd))\n-\t\t\t\t\t\tminode = cinode.split(\":\")\n-\n-\t\t\t\t\t\tif len(minode) == 2 and minode[1][1:-1] == inode:\n-\t\t\t\t\t\t\tmpid = pid\n-\t\t\t\texcept Exception as e:\n-\t\t\t\t\tpass\n-\t\treturn mpid\n-\n-\n-\tdef stop(self):\n-\t\tif(self.server is not None):\n-\t\t\tself.server.socket.close()\n-\n-\tdef exit(self):\n-\t\tif(not GitAutoDeploy.quiet):\n-\t\t\tprint '\\nGoodbye'\n-\t\tself.remove_pidfile()\n-\t\tsys.exit(0)\n-\n-\tdef signal_handler(self, signum, frame):\n-\t\tself.stop()\n-\t\tif(signum == 1):\n-\t\t\tself.run()\n-\t\t\treturn\n-\t\telif(signum == 2):\n-\t\t\tprint '\\nKeyboard Interrupt!!!'\n-\t\telif(signum == 6):\n-\t\t\tprint 'Requested close by SIGABRT (process abort signal). Code 6.'\n-\n-\t\tself.exit()\n+\n+class Lock():\n+    \"\"\"Simple implementation of a mutex lock using the file systems. Works on *nix systems.\"\"\"\n+\n+    path = None\n+    _has_lock = False\n+\n+    def __init__(self, path):\n+        self.path = path\n+\n+    def obtain(self):\n+        import os\n+        try:\n+            os.open(self.path, os.O_CREAT | os.O_EXCL | os.O_WRONLY)\n+            self._has_lock = True\n+            print \"Successfully obtained lock: %s\" % self.path\n+        except OSError:\n+            return False\n+        else:\n+            return True\n+\n+    def release(self):\n+        import os\n+        if not self._has_lock:\n+            raise Exception(\"Unable to release lock that is owned by another process\")\n+        try:\n+            os.remove(self.path)\n+            print \"Successfully released lock: %s\" % self.path\n+        finally:\n+            self._has_lock = False\n+\n+    def has_lock(self):\n+        return self._has_lock\n+\n+    def clear(self):\n+        import os\n+        try:\n+            os.remove(self.path)\n+        except OSError:\n+            pass\n+        finally:\n+            print \"Successfully cleared lock: %s\" % self.path\n+            self._has_lock = False\n+\n+\n+class GitWrapper():\n+    \"\"\"Wraps the git client. Currently uses git through shell command invocations.\"\"\"\n+\n+    def __init__(self):\n+        pass\n+\n+    @staticmethod\n+    def pull(repo_config):\n+        \"\"\"Pulls the latest version of the repo from the git server\"\"\"\n+        from subprocess import call\n+\n+        branch = ('branch' in repo_config) and repo_config['branch'] or 'master'\n+\n+        print \"\\nPost push request received\"\n+        print 'Updating ' + repo_config['path']\n+\n+        res = call(['sleep 5; cd \"' +\n+                    repo_config['path'] +\n+                    '\" && unset GIT_DIR && git fetch origin && git update-index --refresh && git reset --hard origin/' +\n+                    branch + ' && git submodule init && git submodule update'], shell=True)\n+        call(['echo \"Pull result: ' + str(res) + '\"'], shell=True)\n+        return res\n+\n+    @staticmethod\n+    def clone(url, path):\n+        from subprocess import call\n+        call(['git clone --recursive %s %s' % (url, path)], shell=True)\n+\n+\n+    @staticmethod\n+    def deploy(repo_config):\n+        \"\"\"Executes any supplied post-pull deploy command\"\"\"\n+        from subprocess import call\n+\n+        path = repo_config['path']\n+\n+        cmds = []\n+        if 'deploy' in repo_config:\n+            cmds.append(repo_config['deploy'])\n+\n+        gd = GitAutoDeploy().get_config()['global_deploy']\n+        if len(gd[0]) is not 0:\n+            cmds.insert(0, gd[0])\n+        if len(gd[1]) is not 0:\n+            cmds.append(gd[1])\n+\n+        print 'Executing deploy command(s)'\n+\n+        for cmd in cmds:\n+            call(['cd \"' + path + '\" && ' + cmd], shell=True)\n+\n+\n+from BaseHTTPServer import BaseHTTPRequestHandler\n+\n+\n+class WebhookRequestHandler(BaseHTTPRequestHandler):\n+    \"\"\"Extends the BaseHTTPRequestHandler class and handles the incoming HTTP requests.\"\"\"\n+\n+    def do_POST(self):\n+        \"\"\"Invoked on incoming POST requests\"\"\"\n+        from threading import Timer\n+\n+        # Extract repository URL(s) from incoming request body\n+        repo_urls = self.get_repo_urls_from_request()\n+\n+        self.send_response(200)\n+        self.send_header('Content-type', 'text/plain')\n+        self.end_headers()\n+\n+        # Wait one second before we do git pull (why?)\n+        Timer(1.0, GitAutoDeploy.process_repo_urls, [repo_urls]).start()\n+\n+    def get_repo_urls_from_request(self):\n+        \"\"\"Parses the incoming request and extracts all possible URLs to the repository in question. Since repos can\n+        have both ssh://, git:// and https:// URIs, and we don't know which of them is specified in the config, we need\n+        to collect and compare them all.\"\"\"\n+        import json\n+\n+        #content_type = self.headers.getheader('content-type')\n+        length = int(self.headers.getheader('content-length'))\n+        body = self.rfile.read(length)\n+\n+        data = json.loads(body)\n+\n+        repo_urls = []\n+\n+        gitlab_event = self.headers.getheader('X-Gitlab-Event')\n+        github_event = self.headers.getheader('X-GitHub-Event')\n+        user_agent = self.headers.getheader('User-Agent')\n+\n+        if not 'repository' in data:\n+            print \"ERROR - Unable to recognize data format\"\n+            return repo_urls\n+\n+        # Assume GitLab if the X-Gitlab-Event HTTP header is set\n+        if gitlab_event:\n+\n+            print \"Received '%s' event from GitLab\" % gitlab_event\n+\n+            # One repository may posses multiple URLs for different protocols\n+            for k in ['url', 'git_http_url', 'git_ssh_url']:\n+                if k in data['repository']:\n+                    repo_urls.append(data['repository'][k])\n+\n+        # Assume GitHub if the X-GitHub-Event HTTP header is set\n+        elif github_event:\n+\n+            print \"Received '%s' event from GitHub\" % github_event\n+\n+            # One repository may posses multiple URLs for different protocols\n+            for k in ['url', 'git_url', 'clone_url', 'ssh_url']:\n+                if k in data['repository']:\n+                    repo_urls.append(data['repository'][k])\n+\n+        # Assume BitBucket if the User-Agent HTTP header is set to 'Bitbucket-Webhooks/2.0' (or something similar)\n+        elif user_agent.lower().find('bitbucket') != -1:\n+\n+            print \"Received event from BitBucket\"\n+\n+            # One repository may posses multiple URLs for different protocols\n+            for k in ['url', 'git_url', 'clone_url', 'ssh_url']:\n+                if k in data['repository']:\n+                    repo_urls.append(data['repository'][k])\n+\n+            if 'full_name' in data['repository']:\n+                repo_urls.append('git@bitbucket.org:%s.git' % data['repository']['full_name'])\n+                repo_urls.append('https://oliverpoignant@bitbucket.org/%s.git' % data['repository']['full_name'])","path":"GitAutoDeploy.py","position":487,"original_position":487,"commit_id":"043b64e93824053db1a785fc981fb58d32f57a2c","original_commit_id":"043b64e93824053db1a785fc981fb58d32f57a2c","user":{"login":"alexandernst","id":89727,"avatar_url":"https://avatars.githubusercontent.com/u/89727?v=3","gravatar_id":"","url":"https://api.github.com/users/alexandernst","html_url":"https://github.com/alexandernst","followers_url":"https://api.github.com/users/alexandernst/followers","following_url":"https://api.github.com/users/alexandernst/following{/other_user}","gists_url":"https://api.github.com/users/alexandernst/gists{/gist_id}","starred_url":"https://api.github.com/users/alexandernst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexandernst/subscriptions","organizations_url":"https://api.github.com/users/alexandernst/orgs","repos_url":"https://api.github.com/users/alexandernst/repos","events_url":"https://api.github.com/users/alexandernst/events{/privacy}","received_events_url":"https://api.github.com/users/alexandernst/received_events","type":"User","site_admin":false},"body":"Not very sure what this does. Maybe you commited something ment for local-only?","created_at":"2015-08-18T22:25:43Z","updated_at":"2015-08-18T22:25:43Z","html_url":"https://github.com/olipo186/Git-Auto-Deploy/pull/33#discussion_r37361447","pull_request_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33","_links":{"self":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/comments/37361447"},"html":{"href":"https://github.com/olipo186/Git-Auto-Deploy/pull/33#discussion_r37361447"},"pull_request":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33"}}},"pull_request":{"url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33","id":42756196,"html_url":"https://github.com/olipo186/Git-Auto-Deploy/pull/33","diff_url":"https://github.com/olipo186/Git-Auto-Deploy/pull/33.diff","patch_url":"https://github.com/olipo186/Git-Auto-Deploy/pull/33.patch","issue_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/33","number":33,"state":"closed","locked":false,"title":"Dev","user":{"login":"olipo186","id":1056476,"avatar_url":"https://avatars.githubusercontent.com/u/1056476?v=3","gravatar_id":"","url":"https://api.github.com/users/olipo186","html_url":"https://github.com/olipo186","followers_url":"https://api.github.com/users/olipo186/followers","following_url":"https://api.github.com/users/olipo186/following{/other_user}","gists_url":"https://api.github.com/users/olipo186/gists{/gist_id}","starred_url":"https://api.github.com/users/olipo186/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/olipo186/subscriptions","organizations_url":"https://api.github.com/users/olipo186/orgs","repos_url":"https://api.github.com/users/olipo186/repos","events_url":"https://api.github.com/users/olipo186/events{/privacy}","received_events_url":"https://api.github.com/users/olipo186/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-08-18T22:21:27Z","updated_at":"2015-08-18T22:25:43Z","closed_at":"2015-08-18T22:22:14Z","merged_at":"2015-08-18T22:22:14Z","merge_commit_sha":"930be729ff42b0423d2bfd5e233b55fd77f0a8ca","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33/commits","review_comments_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33/comments","review_comment_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/33/comments","statuses_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/statuses/043b64e93824053db1a785fc981fb58d32f57a2c","head":{"label":"olipo186:dev","ref":"dev","sha":"043b64e93824053db1a785fc981fb58d32f57a2c","user":{"login":"olipo186","id":1056476,"avatar_url":"https://avatars.githubusercontent.com/u/1056476?v=3","gravatar_id":"","url":"https://api.github.com/users/olipo186","html_url":"https://github.com/olipo186","followers_url":"https://api.github.com/users/olipo186/followers","following_url":"https://api.github.com/users/olipo186/following{/other_user}","gists_url":"https://api.github.com/users/olipo186/gists{/gist_id}","starred_url":"https://api.github.com/users/olipo186/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/olipo186/subscriptions","organizations_url":"https://api.github.com/users/olipo186/orgs","repos_url":"https://api.github.com/users/olipo186/repos","events_url":"https://api.github.com/users/olipo186/events{/privacy}","received_events_url":"https://api.github.com/users/olipo186/received_events","type":"User","site_admin":false},"repo":{"id":10534595,"name":"Git-Auto-Deploy","full_name":"olipo186/Git-Auto-Deploy","owner":{"login":"olipo186","id":1056476,"avatar_url":"https://avatars.githubusercontent.com/u/1056476?v=3","gravatar_id":"","url":"https://api.github.com/users/olipo186","html_url":"https://github.com/olipo186","followers_url":"https://api.github.com/users/olipo186/followers","following_url":"https://api.github.com/users/olipo186/following{/other_user}","gists_url":"https://api.github.com/users/olipo186/gists{/gist_id}","starred_url":"https://api.github.com/users/olipo186/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/olipo186/subscriptions","organizations_url":"https://api.github.com/users/olipo186/orgs","repos_url":"https://api.github.com/users/olipo186/repos","events_url":"https://api.github.com/users/olipo186/events{/privacy}","received_events_url":"https://api.github.com/users/olipo186/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/olipo186/Git-Auto-Deploy","description":"Deploy your GitHub, GitLab or Bitbucket projects automatically on Git push events or webhooks using this small HTTP server written in Python. Continuous deployment in it's most simple form.","fork":true,"url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy","forks_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/forks","keys_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/teams","hooks_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/hooks","issue_events_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/events{/number}","events_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/events","assignees_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/assignees{/user}","branches_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/branches{/branch}","tags_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/tags","blobs_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/refs{/sha}","trees_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/statuses/{sha}","languages_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/languages","stargazers_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/stargazers","contributors_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/contributors","subscribers_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/subscribers","subscription_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/subscription","commits_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/commits{/sha}","git_commits_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/commits{/sha}","comments_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/comments{/number}","issue_comment_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/comments{/number}","contents_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/contents/{+path}","compare_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/merges","archive_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/downloads","issues_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues{/number}","pulls_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls{/number}","milestones_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/milestones{/number}","notifications_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/labels{/name}","releases_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/releases{/id}","created_at":"2013-06-06T19:25:38Z","updated_at":"2015-08-18T22:02:52Z","pushed_at":"2015-08-18T22:24:21Z","git_url":"git://github.com/olipo186/Git-Auto-Deploy.git","ssh_url":"git@github.com:olipo186/Git-Auto-Deploy.git","clone_url":"https://github.com/olipo186/Git-Auto-Deploy.git","svn_url":"https://github.com/olipo186/Git-Auto-Deploy","homepage":"http://olipo186.github.io/Github-Gitlab-Auto-Deploy/","size":392,"stargazers_count":72,"watchers_count":72,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":24,"mirror_url":null,"open_issues_count":4,"forks":24,"open_issues":4,"watchers":72,"default_branch":"master"}},"base":{"label":"olipo186:master","ref":"master","sha":"b0a71491637beee7b5a4e1c3de2305952a633746","user":{"login":"olipo186","id":1056476,"avatar_url":"https://avatars.githubusercontent.com/u/1056476?v=3","gravatar_id":"","url":"https://api.github.com/users/olipo186","html_url":"https://github.com/olipo186","followers_url":"https://api.github.com/users/olipo186/followers","following_url":"https://api.github.com/users/olipo186/following{/other_user}","gists_url":"https://api.github.com/users/olipo186/gists{/gist_id}","starred_url":"https://api.github.com/users/olipo186/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/olipo186/subscriptions","organizations_url":"https://api.github.com/users/olipo186/orgs","repos_url":"https://api.github.com/users/olipo186/repos","events_url":"https://api.github.com/users/olipo186/events{/privacy}","received_events_url":"https://api.github.com/users/olipo186/received_events","type":"User","site_admin":false},"repo":{"id":10534595,"name":"Git-Auto-Deploy","full_name":"olipo186/Git-Auto-Deploy","owner":{"login":"olipo186","id":1056476,"avatar_url":"https://avatars.githubusercontent.com/u/1056476?v=3","gravatar_id":"","url":"https://api.github.com/users/olipo186","html_url":"https://github.com/olipo186","followers_url":"https://api.github.com/users/olipo186/followers","following_url":"https://api.github.com/users/olipo186/following{/other_user}","gists_url":"https://api.github.com/users/olipo186/gists{/gist_id}","starred_url":"https://api.github.com/users/olipo186/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/olipo186/subscriptions","organizations_url":"https://api.github.com/users/olipo186/orgs","repos_url":"https://api.github.com/users/olipo186/repos","events_url":"https://api.github.com/users/olipo186/events{/privacy}","received_events_url":"https://api.github.com/users/olipo186/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/olipo186/Git-Auto-Deploy","description":"Deploy your GitHub, GitLab or Bitbucket projects automatically on Git push events or webhooks using this small HTTP server written in Python. Continuous deployment in it's most simple form.","fork":true,"url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy","forks_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/forks","keys_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/teams","hooks_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/hooks","issue_events_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/events{/number}","events_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/events","assignees_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/assignees{/user}","branches_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/branches{/branch}","tags_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/tags","blobs_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/refs{/sha}","trees_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/statuses/{sha}","languages_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/languages","stargazers_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/stargazers","contributors_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/contributors","subscribers_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/subscribers","subscription_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/subscription","commits_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/commits{/sha}","git_commits_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/commits{/sha}","comments_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/comments{/number}","issue_comment_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/comments{/number}","contents_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/contents/{+path}","compare_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/merges","archive_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/downloads","issues_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues{/number}","pulls_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls{/number}","milestones_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/milestones{/number}","notifications_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/labels{/name}","releases_url":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/releases{/id}","created_at":"2013-06-06T19:25:38Z","updated_at":"2015-08-18T22:02:52Z","pushed_at":"2015-08-18T22:24:21Z","git_url":"git://github.com/olipo186/Git-Auto-Deploy.git","ssh_url":"git@github.com:olipo186/Git-Auto-Deploy.git","clone_url":"https://github.com/olipo186/Git-Auto-Deploy.git","svn_url":"https://github.com/olipo186/Git-Auto-Deploy","homepage":"http://olipo186.github.io/Github-Gitlab-Auto-Deploy/","size":392,"stargazers_count":72,"watchers_count":72,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":24,"mirror_url":null,"open_issues_count":4,"forks":24,"open_issues":4,"watchers":72,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33"},"html":{"href":"https://github.com/olipo186/Git-Auto-Deploy/pull/33"},"issue":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/33"},"comments":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/33/comments"},"review_comments":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33/comments"},"review_comment":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls/33/commits"},"statuses":{"href":"https://api.github.com/repos/olipo186/Git-Auto-Deploy/statuses/043b64e93824053db1a785fc981fb58d32f57a2c"}}}}